diff --git a/Sources/VLPListDocument.mm b/Sources/VLPListDocument.mm index 0d4036a..7aa64ba 100644 --- a/Sources/VLPListDocument.mm +++ b/Sources/VLPListDocument.mm @@ -115,7 +115,7 @@ void VLPlistVisitor::VisitMeasure(size_t m, VLProperties & p, VLMeasure & meas) [NSNumber numberWithInt:volta], @"volta", nil] forKey: @"end-ending"]; - if (fSong->fGoToCoda == m) + if (fSong->fGoToCoda == m+1) [md setObject:[NSNumber numberWithBool:YES] forKey:@"tocoda"]; if (fSong->fCoda == m) [md setObject:[NSNumber numberWithBool:YES] forKey:@"coda"]; @@ -275,8 +275,8 @@ advanceAt: ) { VLChord chord; chord.fDuration = - VLFraction([[ndict objectForKey:@"durNum"] intValue], - [[ndict objectForKey:@"durDenom"] intValue], + VLFraction([[cdict objectForKey:@"durNum"] intValue], + [[cdict objectForKey:@"durDenom"] intValue], true); chord.fPitch = [[cdict objectForKey:@"pitch"] intValue]; chord.fRootPitch = [[cdict objectForKey:@"root"] intValue]; @@ -304,7 +304,7 @@ advanceAt: [self readChords:[mdict objectForKey:@"chords"] inMeasure:measNo]; if ([[mdict objectForKey:@"tocoda"] boolValue]) - song->fGoToCoda = measNo; + song->fGoToCoda = measNo+1; if ([[mdict objectForKey:@"coda"] boolValue]) song->fCoda = measNo; if (NSDictionary * beginRep = [mdict objectForKey:@"begin-repeat"]) { @@ -361,8 +361,8 @@ advanceAt: VLProperties prop; prop.fTime = - VLFraction([[ndict objectForKey:@"timeNum"] intValue], - [[ndict objectForKey:@"timeDenom"] intValue], + VLFraction([[pdict objectForKey:@"timeNum"] intValue], + [[pdict objectForKey:@"timeDenom"] intValue], false); prop.fKey = [[pdict objectForKey:@"key"] intValue]; prop.fMode = [[pdict objectForKey:@"mode"] intValue]; @@ -372,17 +372,27 @@ advanceAt: } } +- (void)setValueFromPlist:(id)plist plistKey:(NSString *)plistKey forKey:(NSString *)key +{ + id value = [plist objectForKey:plistKey]; + if (value) + [self setValue:value forKey:key]; +} + - (BOOL)readFromPlist:(id)plist error:(NSError **)outError { + NSUndoManager * undoMgr = [self undoManager]; + [undoMgr disableUndoRegistration]; song->clear(); - [self setValue:[plist objectForKey:@"title"] forKey:@"songTitle"]; - [self setValue:[plist objectForKey:@"composer"] forKey:@"songComposer"]; - [self setValue:[plist objectForKey:@"lyricist"] forKey:@"songLyricist"]; - [self setValue:[plist objectForKey:@"groove"] forKey:@"songGroove"]; - [self setValue:[plist objectForKey:@"tempo"] forKey:@"songTempo"]; + [self setValueFromPlist:plist plistKey:@"title" forKey:@"songTitle"]; + [self setValueFromPlist:plist plistKey:@"composer" forKey:@"songComposer"]; + [self setValueFromPlist:plist plistKey:@"lyricist" forKey:@"songLyricist"]; + [self setValueFromPlist:plist plistKey:@"groove" forKey:@"songGroove"]; + [self setValueFromPlist:plist plistKey:@"tempo" forKey:@"songTempo"]; [self readPropertiesFromPlist:[plist objectForKey:@"properties"]]; [self readMeasuresFromPlist:[plist objectForKey:@"measures"]]; + [undoMgr enableUndoRegistration]; return YES; } diff --git a/Sources/VLXMLDocument.mm b/Sources/VLXMLDocument.mm index c71c1fe..4af309a 100644 --- a/Sources/VLXMLDocument.mm +++ b/Sources/VLXMLDocument.mm @@ -9,432 +9,10 @@ // #import "VLXMLDocument.h" - -@interface NSXMLNode (VLConvenience) - -- (id) nodeForXPath:(NSString *)path error:(NSError **)outError; -- (NSString *)stringForXPath:(NSString *)path error:(NSError **)outError; -- (int)intForXPath:(NSString *)path error:(NSError **)outError; - -@end - -@implementation NSXMLNode (VLConvenience) - -- (id) nodeForXPath:(NSString *)path error:(NSError **)outError -{ - NSArray * nodes = [self nodesForXPath:path error:outError]; - return [nodes count] ? [nodes objectAtIndex:0] : nil; -} - -- (NSString *)stringForXPath:(NSString *)path error:(NSError **)outError -{ - return [[self nodeForXPath:path error:outError] stringValue]; -} - -- (int)intForXPath:(NSString *)path error:(NSError **)outError -{ - return [[[self nodeForXPath:path error:outError] stringValue] intValue]; -} - -@end +#import "VLPListDocument.h" @implementation VLDocument (XML) -- (NSXMLDTD *)partwiseDTD -{ - NSXMLDTD * dtd = [[[NSXMLDTD alloc] init] autorelease]; - [dtd setPublicID:@"-//Recordare//DTD MusicXML 1.1 Partwise//EN"]; - [dtd setSystemID:@"http://www.musicxml.org/dtds/partwise.dtd"]; - [dtd setName:@"score-partwise"]; - - return dtd; -} - -- (NSXMLElement *)scorePartWithID:(NSString *)id name:(NSString *)name -{ - NSXMLElement * part = [NSXMLNode elementWithName:@"score-part"]; - [part addAttribute: [NSXMLNode attributeWithName:@"id" - stringValue:id]]; - [part addChild: [NSXMLNode elementWithName:@"part-name" - stringValue:name]]; - - return part; -} - -- (NSXMLElement *)attributesWithProps:(VLProperties)props -{ - NSXMLElement * attr = [NSXMLNode elementWithName:@"attributes"]; - [attr addChild: [NSXMLNode elementWithName:@"divisions" - stringValue:[NSString stringWithFormat:@"%d", - props.fDivisions]]]; - - NSXMLElement * key = [NSXMLNode elementWithName:@"key"]; - [key addChild: [NSXMLNode elementWithName:@"fifths" - stringValue:[NSString stringWithFormat:@"%d", - props.fKey]]]; - [key addChild: [NSXMLNode elementWithName:@"mode" - stringValue: - props.fMode < 0 ? @"minor" : @"major"]]; - - NSXMLElement * time = [NSXMLNode elementWithName:@"time"]; - [time addChild: [NSXMLNode elementWithName:@"beats" - stringValue:[NSString stringWithFormat:@"%d", - props.fTime.fNum]]]; - [time addChild: [NSXMLNode elementWithName:@"beat-type" - stringValue:[NSString stringWithFormat:@"%d", - props.fTime.fDenom]]]; - - NSXMLElement * clef = [NSXMLNode elementWithName:@"clef"]; - [clef addChild: [NSXMLNode elementWithName:@"sign" - stringValue:@"G"]]; - [clef addChild: [NSXMLNode elementWithName:@"line" - stringValue:@"2"]]; - - - [attr addChild: key]; - [attr addChild: time]; - [attr addChild: clef]; - - return attr; -} - -const char * sSteps = "C DbD EbE F GbG AbA BbB "; - -- (NSXMLElement *)noteWithPitch:(int)pitch duration:(int)units useSharps:(BOOL)useSharps tied:(int)tied -{ - NSXMLElement * note = [NSXMLNode elementWithName:@"note"]; - if (pitch == VLNote::kNoPitch) { - [note addChild: [NSXMLNode elementWithName:@"rest"]]; - } else { - NSXMLElement * p = [NSXMLNode elementWithName:@"pitch"]; - int octave = pitch/12 - 1; - pitch = 2*(pitch % 12); - char step = sSteps[pitch]; - int alt = sSteps[pitch+1] == 'b'; - if (alt) - if (useSharps) { - step= step=='A' ? 'G' : step-1; // Db -> C# - alt = 1; - } else { - alt = -1; - } - [p addChild: - [NSXMLNode elementWithName:@"step" - stringValue: [NSString stringWithFormat:@"%c", - step]]]; - if (alt) - [p addChild: - [NSXMLNode elementWithName:@"alter" - stringValue: [NSString stringWithFormat:@"%d", - alt]]]; - [p addChild: - [NSXMLNode elementWithName:@"octave" - stringValue: [NSString stringWithFormat:@"%d", - octave]]]; - [note addChild:p]; - } - [note addChild: [NSXMLNode elementWithName:@"duration" - stringValue: [NSString stringWithFormat:@"%d", - units]]]; - if (tied & VLNote::kTiedWithPrev) { - NSXMLElement * tie = [NSXMLNode elementWithName:@"tie"]; - [tie addAttribute: [NSXMLNode attributeWithName:@"type" - stringValue:@"stop"]]; - [note addChild:tie]; - } - if (tied & VLNote::kTiedWithNext) { - NSXMLElement * tie = [NSXMLNode elementWithName:@"tie"]; - [tie addAttribute: [NSXMLNode attributeWithName:@"type" - stringValue:@"start"]]; - [note addChild:tie]; - } - [note addChild: [NSXMLNode elementWithName:@"voice" - stringValue: @"1"]]; - - return note; -} - -- (NSXMLElement *)syllable:(const VLSyllable *)syllable inStanza:(int)stanza -{ - NSString * syll; - switch (syllable->fKind) { - default: - case VLSyllable::kSingle: - syll = @"single"; - break; - case VLSyllable::kBegin: - syll = @"begin"; - break; - case VLSyllable::kEnd: - syll = @"end"; - break; - case VLSyllable::kMiddle: - syll = @"middle"; - break; - } - - NSString * text = [NSString stringWithUTF8String:syllable->fText.c_str()]; - NSXMLNode* num = [NSXMLNode attributeWithName:@"number" - stringValue:[NSString stringWithFormat:@"%d", - stanza]]; - return [NSXMLNode - elementWithName:@"lyric" - children: [NSArray arrayWithObjects: - [NSXMLNode elementWithName:@"syllabic" stringValue:syll], - [NSXMLNode elementWithName:@"text" stringValue:text], - nil] - attributes: [NSArray arrayWithObject:num]]; -} - -- (void)addNotes:(VLNoteList *)notes toMeasure:(NSXMLElement *)meas -{ - VLFraction resolution(1, song->fProperties.front().fDivisions*4); - bool useSharps = song->fProperties.front().fKey > 0; - for (VLNoteList::const_iterator note = notes->begin(); - note != notes->end(); - ++note - ) { - VLFraction u = note->fDuration / resolution; - int units = (u.fNum+u.fDenom/2)/u.fDenom; - NSXMLElement*n = - [self noteWithPitch:note->fPitch duration:units useSharps:useSharps - tied:note->fTied]; - for (size_t i=0; ifLyrics.size(); ++i) - if (note->fLyrics[i]) - [n addChild:[self syllable:¬e->fLyrics[i] inStanza:i+1]]; - - [meas addChild:n]; - } -} - -- (void)addChords:(VLChordList *)chords toMeasure:(NSXMLElement *)meas -{ - VLFraction resolution(1, song->fProperties.front().fDivisions*4); - bool useSharps = song->fProperties.front().fKey > 0; - for (VLChordList::const_iterator chord = chords->begin(); - chord != chords->end(); - ++chord - ) { - VLFraction u = chord->fDuration / resolution; - int units = (u.fNum+u.fDenom/2)/u.fDenom; - NSXMLElement* ch = nil; - if (chord->fPitch == VLNote::kNoPitch) { - [meas addChild:[self noteWithPitch:chord->fPitch duration:units useSharps:useSharps tied:0]]; - continue; - } - if (chord->fRootPitch != VLNote::kNoPitch) { - [meas addChild:[self noteWithPitch:chord->fRootPitch - duration:units useSharps:useSharps tied:0]]; - ch = [NSXMLNode elementWithName:@"chord"]; - } - for (int step=0; step<32; ++step) - if ((1 << step) > chord->fSteps) { - break; - } else if (chord->fSteps & (1 << step)) { - NSXMLElement * note = - [self noteWithPitch:chord->fPitch+step - duration:units useSharps:useSharps tied:0]; - [note insertChild:ch atIndex:0]; - [meas addChild: note]; - ch = [NSXMLNode elementWithName:@"chord"]; - } - } -} - -- (NSXMLElement *) identificationElement -{ - NSXMLElement * identification = [NSXMLNode elementWithName:@"identification"]; - NSXMLElement * composer = [NSXMLNode elementWithName:@"creator" - stringValue:songComposer]; - [composer addAttribute: [NSXMLNode attributeWithName:@"type" - stringValue:@"composer"]]; - [identification addChild:composer]; - NSXMLElement * poet = [NSXMLNode elementWithName:@"creator" - stringValue:songLyricist]; - [poet addAttribute: [NSXMLNode attributeWithName:@"type" - stringValue:@"poet"]]; - [identification addChild:poet]; - - NSXMLElement * encoding = [NSXMLNode elementWithName:@"encoding"]; - [encoding addChild: - [NSXMLNode elementWithName:@"encoding-date" - stringValue: - [[NSDate date] - descriptionWithCalendarFormat:@"%Y-%m-%d" - timeZone:nil locale:nil]]]; - [encoding addChild: - [NSXMLNode elementWithName:@"software" - stringValue: [NSString stringWithFormat:@"VocalEasel %@", - [[NSBundle mainBundle] - objectForInfoDictionaryKey:@"CFBundleVersion"]]]]; - [identification addChild:encoding]; - - return identification; -} - -- (NSXMLElement *) soundElt:(NSString *)title -{ - NSXMLElement * sound = [NSXMLNode elementWithName:@"sound"]; - [sound addAttribute: [NSXMLNode attributeWithName:title - stringValue:@"A"]]; - return sound; -} - -- (NSData *)XMLDataWithError:(NSError **)outError -{ - NSXMLElement * work = [NSXMLNode elementWithName:@"work"]; - [work addChild: [NSXMLNode elementWithName:@"work-title" - stringValue:songTitle]]; - - NSXMLElement * identification = [self identificationElement]; - - NSXMLElement * partList = [NSXMLNode elementWithName:@"part-list"]; - [partList addChild: [self scorePartWithID:@"HARM" name:@"Chords"]]; - [partList addChild: [self scorePartWithID:@"MELO" name:@"Melody"]]; - - NSXMLElement * chords = [NSXMLNode elementWithName:@"part"]; - [chords addAttribute: [NSXMLNode attributeWithName:@"id" - stringValue:@"HARM"]]; - - NSXMLElement * melody = [NSXMLNode elementWithName:@"part"]; - [melody addAttribute: [NSXMLNode attributeWithName:@"id" - stringValue:@"MELO"]]; - - size_t endMeasure = song->CountMeasures()-song->EmptyEnding(); - for (int measure = 0; measure < endMeasure; ++measure) { - NSXMLElement * melMeas = [NSXMLNode elementWithName:@"measure"]; - [melMeas addAttribute: - [NSXMLNode attributeWithName:@"number" - stringValue:[NSString stringWithFormat:@"%d", - measure+1]]]; - if (!measure) - [melMeas addChild: - [self attributesWithProps:song->fProperties.front()]]; - - NSXMLElement * harMeas = [melMeas copy]; - - size_t volta; - bool repeat; - int times; - if (song->DoesBeginRepeat(measure)) { - NSXMLElement * barline = [NSXMLNode elementWithName:@"barline"]; - [barline addAttribute: [NSXMLNode attributeWithName:@"location" - stringValue:@"left"]]; - NSString * style = @"heavy-light"; - if (song->DoesEndRepeat(measure) - || (song->DoesEndEnding(measure, &repeat) && repeat) - ) - style = @"heavy-heavy"; - [barline addChild: [NSXMLNode elementWithName:@"bar-style" - stringValue:style]]; - NSXMLElement * repeat = [NSXMLNode elementWithName:@"repeat"]; - [repeat addAttribute: [NSXMLNode attributeWithName:@"direction" - stringValue:@"forward"]]; - [barline addChild:repeat]; - [melMeas addChild:barline]; - } - if (song->DoesBeginEnding(measure, 0, &volta)) { - NSXMLElement * barline = [NSXMLNode elementWithName:@"barline"]; - [barline addAttribute: [NSXMLNode attributeWithName:@"location" - stringValue:@"left"]]; - NSXMLElement * ending = [NSXMLNode elementWithName:@"ending"]; - [ending addAttribute: [NSXMLNode attributeWithName:@"type" - stringValue:@"start"]]; - NSString * number = nil; - for (size_t i = 0; i<8; ++i) - if (volta & (1<DoesEndRepeat(measure+1, ×)) { - NSXMLElement * barline = [NSXMLNode elementWithName:@"barline"]; - [barline addAttribute: [NSXMLNode attributeWithName:@"location" - stringValue:@"right"]]; - NSString * style = @"light-heavy"; - if (song->DoesBeginRepeat(measure+1)) - style = @"heavy-heavy"; - [barline addChild: [NSXMLNode elementWithName:@"bar-style" - stringValue:style]]; - NSXMLElement * repeat = [NSXMLNode elementWithName:@"repeat"]; - [repeat addAttribute: [NSXMLNode attributeWithName:@"direction" - stringValue:@"backward"]]; - [repeat addAttribute: - [NSXMLNode attributeWithName:@"times" - stringValue:[NSString stringWithFormat:@"%d", times]]]; - [barline addChild:repeat]; - [melMeas addChild:barline]; - } - if (song->DoesEndEnding(measure+1, &repeat, &volta)) { - NSXMLElement * barline = [NSXMLNode elementWithName:@"barline"]; - [barline addAttribute: [NSXMLNode attributeWithName:@"location" - stringValue:@"right"]]; - if (repeat) { - NSString * style = @"light-heavy"; - if (song->DoesBeginRepeat(measure+1)) - style = @"heavy-heavy"; - [barline addChild: [NSXMLNode elementWithName:@"bar-style" - stringValue:style]]; - NSXMLElement * repeat = [NSXMLNode elementWithName:@"repeat"]; - [repeat addAttribute: [NSXMLNode attributeWithName:@"direction" - stringValue:@"backward"]]; - [barline addChild:repeat]; - } - NSXMLElement * ending = [NSXMLNode elementWithName:@"ending"]; - [ending addAttribute: - [NSXMLNode attributeWithName:@"type" - stringValue:repeat ? @"stop" : @"discontinue"]]; - NSString * number = nil; - for (size_t i = 0; i<8; ++i) - if (volta & (1<fCoda == measure) - [melMeas addChild:[self soundElt:@"coda"]]; - - [self addNotes:&song->fMeasures[measure].fMelody toMeasure:melMeas]; - [self addChords:&song->fMeasures[measure].fChords toMeasure:harMeas]; - - if (song->fGoToCoda == measure+1) - [melMeas addChild:[self soundElt:@"tocoda"]]; - - [melody addChild:melMeas]; - [chords addChild:harMeas]; - } - - NSXMLElement * score = - [NSXMLNode - elementWithName:@"score-partwise" - children:[NSArray arrayWithObjects: work, identification, - partList, chords, melody, nil] - attributes:[NSArray arrayWithObject: - [NSXMLNode attributeWithName:@"version" - stringValue:@"1.1"]]]; - NSXMLDocument * doc = [[[NSXMLDocument alloc] - initWithRootElement:score] - autorelease]; - [doc setVersion:@"1.0"]; - [doc setCharacterEncoding:@"UTF-8"]; - [doc setDTD:[self partwiseDTD]]; - [[doc DTD] setChildren: nil]; - - return [doc XMLDataWithOptions:NSXMLNodePrettyPrint|NSXMLNodeCompactEmptyElement]; -} - (NSArray *)propertyKeys { @@ -449,21 +27,19 @@ const char * sSteps = "C DbD EbE F GbG AbA BbB "; - (NSFileWrapper *)XMLFileWrapperWithError:(NSError **)outError flat:(BOOL)flat; { - NSData * contents = [self XMLDataWithError:outError]; + NSFileWrapper * contents = [self fileWrapperWithFilter:@"VLMusicXMLType" error:outError]; if (!contents) { return nil; } else if (flat) { - return [[[NSFileWrapper alloc] - initRegularFileWithContents:contents] - autorelease]; + return contents; } else { NSFileWrapper * wrap = [[[NSFileWrapper alloc] initDirectoryWithFileWrappers: [NSDictionary dictionary]] autorelease]; - [wrap addRegularFileWithContents:contents - preferredFilename:@"Song"]; + [contents setPreferredFilename:@"Song"]; + [wrap addFileWrapper:contents]; NSDictionary * prop = [self dictionaryWithValuesForKeys:[self propertyKeys]]; [wrap addRegularFileWithContents: @@ -478,262 +54,6 @@ const char * sSteps = "C DbD EbE F GbG AbA BbB "; } } -- (BOOL)readPropsFromAttributes:(NSXMLElement*)attr error:(NSError **)outError -{ - VLProperties & prop = song->fProperties.front(); - - prop.fDivisions = [attr intForXPath:@"./divisions" error:outError]; - prop.fKey = [attr intForXPath:@"./key/fifths" error:outError]; - prop.fMode = [[attr stringForXPath:@"./key/mode" error:outError] - isEqual:@"minor"] ? -1 : 1; - prop.fTime.fNum = [attr intForXPath:@"./time/beats" error:outError]; - prop.fTime.fDenom = [attr intForXPath:@"./time/beat-type" error:outError]; - - return YES; -} - -int8_t sStepToPitch[] = { - 9, 11, 0, 2, 4, 5, 7 -}; - -- (VLLyricsNote) readNote:(NSXMLElement*)note withUnit:(VLFraction)unit -{ - NSError * outError; - VLLyricsNote n; - - n.fTied = 0; - - if ([[note elementsForName:@"rest"] count]) { - n.fPitch = VLNote::kNoPitch; - } else { - n.fPitch = ([note intForXPath:@"./pitch/octave" error:&outError]+1)*12; - n.fPitch += - sStepToPitch[[[note stringForXPath:@"./pitch/step" error:&outError] - characterAtIndex:0] - 'A']; - if (NSXMLElement * alter = [note nodeForXPath:@"./pitch/alter" error:&outError]) - n.fPitch += [[alter stringValue] intValue]; - } - n.fDuration = VLFraction([note intForXPath:@"./duration" error:&outError])*unit; - if ([note nodeForXPath:@".//tie[@type=\"stop\"]" error:&outError]) - n.fTied |= VLNote::kTiedWithPrev; - NSEnumerator * e = [[note elementsForName:@"lyric"] objectEnumerator]; - for (NSXMLElement * lyric; lyric = [e nextObject]; ) { - int stanza = [[[lyric attributeForName:@"number"] - stringValue] intValue]; - if (stanza > n.fLyrics.size()) - n.fLyrics.resize(stanza); - --stanza; - NSString * kind = [lyric stringForXPath:@"./syllabic" error:&outError]; - if ([kind isEqual:@"begin"]) - n.fLyrics[stanza].fKind = VLSyllable::kBegin; - else if ([kind isEqual:@"end"]) - n.fLyrics[stanza].fKind = VLSyllable::kEnd; - else if ([kind isEqual:@"middle"]) - n.fLyrics[stanza].fKind = VLSyllable::kMiddle; - else - n.fLyrics[stanza].fKind = VLSyllable::kSingle; - n.fLyrics[stanza].fText = - [[lyric stringForXPath:@"./text" error:&outError] UTF8String]; - } - - return n; -} - -- (void) addRepeat:(VLRepeat *)repeat -{ - size_t end = repeat->fEndings.size() > 1 - ? repeat->fEndings[1].fBegin : repeat->fEndings[0].fEnd; - song->AddRepeat(repeat->fEndings[0].fBegin, end, repeat->fTimes); - for (size_t e = 1; efEndings.size(); ++e) - song->AddEnding(repeat->fEndings[e].fBegin, repeat->fEndings[e].fEnd, - repeat->fEndings[e].fVolta); -} - -- (void) readBarlines:(NSArray *)barlines measure:(int)m - repeat:(VLRepeat *)repeat inRepeat:(bool *)inRepeat - error:(NSError **)outError -{ - NSEnumerator * e = [barlines objectEnumerator]; - for (NSXMLElement * barline; barline = [e nextObject]; ) { - NSXMLElement * rep = [barline nodeForXPath:@"./repeat" error:outError]; - NSXMLElement * ending = [barline nodeForXPath:@"./ending" error:outError]; - NSString * direction = nil; - if (rep) - direction = [rep stringForXPath:@"./@direction" error:outError]; - NSString * endingType = nil; - size_t volta = 0; - int maxEnding = 0; - if (ending) { - endingType = [ending stringForXPath:@"./@type" error:outError]; - NSEnumerator * n= - [[[ending stringForXPath:@"./@number" error:outError] - componentsSeparatedByString:@","] objectEnumerator]; - for (NSString * num; num = [n nextObject]; ) { - int n = [num intValue]; - maxEnding = n; - volta |= 1<<(n-1); - } - } - - if ([direction isEqual:@"forward"]) { - // - // New repeat, add old one if there was one - // - if (*inRepeat) - [self addRepeat:repeat]; - *inRepeat = true; - repeat->fTimes = 0; - repeat->fEndings.clear(); - repeat->fEndings.push_back(VLRepeat::Ending(m, 0, 0)); - } else if ([endingType isEqual:@"start"]) { - repeat->fTimes = std::max(repeat->fTimes, maxEnding); - repeat->fEndings.push_back(VLRepeat::Ending(m, 0, volta)); - } else if (endingType) { - repeat->fEndings.back().fEnd = m+1; - repeat->fEndings[0].fEnd = m+1; - } else if (direction) { - repeat->fTimes = [rep intForXPath:@"./@times" error:outError]; - repeat->fEndings[0].fEnd = m+1; - } - } -} - -- (void) readMelody:(NSArray *)measures error:(NSError **)outError -{ - NSEnumerator * e = [measures objectEnumerator]; - - VLRepeat repeat; - bool inRepeat = false; - uint8_t prevKind[20]; - memset(prevKind, 0, 20); - - for (NSXMLElement * measure; measure = [e nextObject]; ) { - VLProperties & prop = song->fProperties.front(); - VLFraction unit(1, 4*prop.fDivisions); - VLFraction at(0); - int m = [[[measure attributeForName:@"number"] - stringValue] intValue]-1; - - [self readBarlines:[measure elementsForName:@"barline"] measure:m - repeat:&repeat inRepeat:&inRepeat error:outError]; - if ([measure nodeForXPath:@".//sound[@coda=\"A\"]" error:outError]) - song->fCoda = m; - if ([measure nodeForXPath:@".//sound[@tocoda=\"A\"]" error:outError]) - song->fGoToCoda = m+1; - NSEnumerator * n = [[measure elementsForName:@"note"] objectEnumerator]; - - for (NSXMLElement * note; note = [n nextObject]; ) { - VLLyricsNote n = [self readNote:note withUnit:unit]; - // - // Sanitize syllabic information which was corrupt in early - // versions. - // - for (size_t i = 0; iAddNote(n, m, at); - at += n.fDuration; - } - } - if (inRepeat) - [self addRepeat:&repeat]; -} - -- (void) readChords:(NSArray *)measures error:(NSError **)outError -{ - NSEnumerator * e = [measures objectEnumerator]; - - for (NSXMLElement * measure; measure = [e nextObject]; ) { - VLProperties & prop = song->fProperties.front(); - VLFraction unit(1, 4*prop.fDivisions); - VLFraction at(0); - VLFraction dur(0); - int m = [[[measure attributeForName:@"number"] - stringValue] intValue]-1; - VLChord chord; - - chord.fSteps = 0; - if (m >= song->CountMeasures()) - song->fMeasures.resize(m); - - NSEnumerator * n = [[measure elementsForName:@"note"] objectEnumerator]; - - for (NSXMLElement * note; note = [n nextObject]; ) { - VLNote n = [self readNote:note withUnit:unit]; - if (![[note elementsForName:@"chord"] count]) { - // - // Start of new chord - // - if (chord.fSteps) - song->AddChord(chord, m, at); - at += dur; - chord.fPitch = n.fPitch; - chord.fRootPitch= VLNote::kNoPitch; - chord.fDuration = n.fDuration; - chord.fSteps = n.fPitch == VLNote::kNoPitch ? 0 : VLChord::kmUnison; - dur = n.fDuration; - if (n.fPitch < VLNote::kMiddleC) { - chord.fPitch = VLNote::kNoPitch; - chord.fRootPitch= n.fPitch; - } - } else if (chord.fPitch == VLNote::kNoPitch) { - chord.fPitch = n.fPitch; - } else { - chord.fSteps |= 1 << (n.fPitch-chord.fPitch); - } - } - if (chord.fSteps) - song->AddChord(chord, m, at); - } -} - -- (BOOL)readFromXMLData:(NSData *)data error:(NSError **)outError -{ - song->clear(); - - NSXMLDocument * doc = [[NSXMLDocument alloc] initWithData:data - options:0 - error:outError]; - // - // For now, in gross violation of MusicXML spirit, we're only reading - // our own input. - // - songTitle = [[doc stringForXPath:@".//work-title" error:outError] retain]; - songComposer= [[doc stringForXPath:@".//creator[@type=\"composer\"]" - error: outError] retain]; - songLyricist= [[doc stringForXPath:@".//creator[@type=\"poet\"]" - error: outError] retain]; - id groove = [doc stringForXPath:@".//miscellaneous-field[@name=\"VocalEasel-groove\"]" - error: outError]; - if (groove) { - [songGroove autorelease]; - songGroove = [groove retain]; - } - - NSXMLElement * chords = [doc nodeForXPath:@".//part[@id=\"HARM\"]" - error:outError]; - NSXMLElement * melody = [doc nodeForXPath:@".//part[@id=\"MELO\"]" - error:outError]; - - if (!chords || !melody) - return NO; - if (![self readPropsFromAttributes: - [melody nodeForXPath:@".//attributes" error:outError] - error:outError] - ) - return NO; - - [self readMelody:[melody nodesForXPath:@"./measure" error:outError] - error:outError]; - [self readChords:[chords nodesForXPath:@"./measure" error:outError] - error:outError]; - - return YES; -} - - (BOOL)readFromXMLFileWrapper:(NSFileWrapper *)wrapper error:(NSError **)outError { NSDictionary * wrappers = [wrapper fileWrappers]; @@ -752,8 +72,7 @@ int8_t sStepToPitch[] = { format:nil errorDescription:nil]]; [undoMgr enableUndoRegistration]; } - return [self readFromXMLData: - [[wrappers objectForKey:@"Song"] regularFileContents] + return [self readFromFileWrapper:[wrappers objectForKey:@"Song"] withFilter:@"VLMusicXMLType" error:outError]; } diff --git a/VocalEasel.xcodeproj/project.pbxproj b/VocalEasel.xcodeproj/project.pbxproj index b1a5bf0..a935e63 100644 --- a/VocalEasel.xcodeproj/project.pbxproj +++ b/VocalEasel.xcodeproj/project.pbxproj @@ -47,6 +47,8 @@ 955E59610957C1400045FDA5 /* TVLChord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 955E59600957C1400045FDA5 /* TVLChord.cpp */; }; 955E59640957C15A0045FDA5 /* VLModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 955E58E4095658AB0045FDA5 /* VLModel.cpp */; }; 95784D870BFAD795009ABEA4 /* VLMirrorWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 95784D860BFAD795009ABEA4 /* VLMirrorWindow.mm */; }; + 95795CE60C88B25D00E4A21F /* vl.rb in Copy Filters */ = {isa = PBXBuildFile; fileRef = 95795CE40C88B25D00E4A21F /* vl.rb */; }; + 95795CE70C88B25D00E4A21F /* VLMusicXMLType.reader in Copy Filters */ = {isa = PBXBuildFile; fileRef = 95795CE50C88B25D00E4A21F /* VLMusicXMLType.reader */; }; 9588363C0C6F9C7D004B4162 /* VLPListDocument.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9588363B0C6F9C7D004B4162 /* VLPListDocument.mm */; }; 9588365C0C6FABBA004B4162 /* VLDebugFlags.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9588365A0C6FABBA004B4162 /* VLDebugFlags.cpp */; }; 95932B91096527700008B0DB /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 952CBBB3095FD34F00434E43 /* AudioUnit.framework */; }; @@ -134,6 +136,8 @@ dstPath = Filters; dstSubfolderSpec = 7; files = ( + 95795CE60C88B25D00E4A21F /* vl.rb in Copy Filters */, + 95795CE70C88B25D00E4A21F /* VLMusicXMLType.reader in Copy Filters */, 95EF921A0C786CB90093E5F4 /* plistReader.rb in Copy Filters */, 95EF921B0C786CB90093E5F4 /* plistWriter.rb in Copy Filters */, 95EF92290C78E94E0093E5F4 /* VLMusicXMLType.writer in Copy Filters */, @@ -206,6 +210,8 @@ 955E59600957C1400045FDA5 /* TVLChord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TVLChord.cpp; path = Tests/TVLChord.cpp; sourceTree = ""; }; 95784D850BFAD795009ABEA4 /* VLMirrorWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLMirrorWindow.h; path = Sources/VLMirrorWindow.h; sourceTree = ""; }; 95784D860BFAD795009ABEA4 /* VLMirrorWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = VLMirrorWindow.mm; path = Sources/VLMirrorWindow.mm; sourceTree = ""; }; + 95795CE40C88B25D00E4A21F /* vl.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; path = vl.rb; sourceTree = ""; }; + 95795CE50C88B25D00E4A21F /* VLMusicXMLType.reader */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; path = VLMusicXMLType.reader; sourceTree = ""; }; 9588363A0C6F9C7D004B4162 /* VLPListDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VLPListDocument.h; path = Sources/VLPListDocument.h; sourceTree = ""; }; 9588363B0C6F9C7D004B4162 /* VLPListDocument.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = VLPListDocument.mm; path = Sources/VLPListDocument.mm; sourceTree = ""; }; 9588365A0C6FABBA004B4162 /* VLDebugFlags.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = VLDebugFlags.cpp; path = Sources/VLDebugFlags.cpp; sourceTree = ""; }; @@ -530,6 +536,8 @@ 95EF92110C786B2C0093E5F4 /* Filters */ = { isa = PBXGroup; children = ( + 95795CE40C88B25D00E4A21F /* vl.rb */, + 95795CE50C88B25D00E4A21F /* VLMusicXMLType.reader */, 95EF92270C78E9390093E5F4 /* VLMusicXMLType.writer */, 95EF92120C786B2C0093E5F4 /* plistReader.rb */, 95EF92130C786B2C0093E5F4 /* plistWriter.rb */,