diff --git a/Sources/VLMMADocument.mm b/Sources/VLMMADocument.mm index 6400d93..447ca4f 100644 --- a/Sources/VLMMADocument.mm +++ b/Sources/VLMMADocument.mm @@ -29,8 +29,11 @@ mmaFile += '\n'; std::string mmas; - for (size_t m=0; mCountMeasures(); ++m) { - sprintf(buf, "%-5d", m+1); + size_t meas = 0; + VLSong::iterator end = song->end(); + for (VLSong::iterator i=song->begin(); i!=end; ++i) { + size_t m = *i; + sprintf(buf, "%-5d", ++meas); mmaFile += buf; song->fMeasures[m].MMAChords(mmas, prop); mmaFile += mmas; diff --git a/Sources/VLModel.cpp b/Sources/VLModel.cpp index ee11bea..64bbb4c 100644 --- a/Sources/VLModel.cpp +++ b/Sources/VLModel.cpp @@ -1043,12 +1043,50 @@ size_t VLSong::CountStanzas() const void VLSong::LilypondNotes(std::string & notes) const { - notes = ""; + notes = ""; + std::string indent = ""; + size_t seenEnding = 0; + int numEndings = 0; for (size_t measure=0; measureLilypondName(note, at, fProperties[fMeasures[measure].fPropIdx]); @@ -1452,11 +1490,15 @@ bool VLSong::CanBeEnding(size_t beginMeasure, size_t endMeasure, return false; } -bool VLSong::DoesBeginRepeat(size_t measure) const +bool VLSong::DoesBeginRepeat(size_t measure, int * times) const { for (size_t r=0; r= measure && fRepeats[r].fEndings.size() > 1 ) { - *volta = (1<= measure && fRepeats[r].fEndings.size() > 1 ) { - size_t volta = (1< fMeasures; std::vector fRepeats; + // + // Iterate over measures in performance order + // + class iterator { + public: + size_t operator*() { return fMeasure; } + iterator & operator++(); + bool operator==(const iterator & other) const { + return fMeasure==other.fMeasure && fStatus == other.fStatus; + } + bool operator!=(const iterator & other) const { + return fMeasure!=other.fMeasure || fStatus != other.fStatus; + } + protected: + friend class VLSong; + iterator(const VLSong & song, bool end); + private: + size_t fMeasure; + const VLSong & fSong; + struct Repeat { + Repeat(size_t begin, int times) + : fBegin(begin), fTimes(times), fVolta(0) {} + size_t fBegin; + int8_t fTimes; + int8_t fVolta; + + bool operator==(const Repeat & other) const { + return fBegin==other.fBegin && fVolta == other.fVolta; + } + bool operator!=(const Repeat & other) const { + return fBegin!=other.fBegin || fVolta != other.fVolta; + } + }; + std::vector fStatus; + + void AdjustStatus(); + }; + iterator begin() { return iterator(*this, false); } + iterator end() { return iterator(*this, true); } void AddChord(VLChord chord, size_t measure, VLFraction at); void AddNote(VLLyricsNote note, size_t measure, VLFraction at); @@ -276,10 +315,10 @@ struct VLSong { bool CanBeRepeat(size_t beginMeasure, size_t endMeasure, int * times = 0); bool CanBeEnding(size_t beginMeasure, size_t endMeasure, size_t * volta = 0, size_t * voltaOK = 0); - bool DoesBeginRepeat(size_t measure) const; - bool DoesEndRepeat(size_t measure, int * times) const; - bool DoesBeginEnding(size_t measure, size_t * volta) const; - bool DoesEndEnding(size_t measure, bool * repeat) const; + bool DoesBeginRepeat(size_t measure, int * times = 0) const; + bool DoesEndRepeat(size_t measure, int * times = 0) const; + bool DoesBeginEnding(size_t measure, bool * repeat = 0, size_t * volta = 0) const; + bool DoesEndEnding(size_t measure, bool * repeat = 0, size_t * volta = 0) const; void Transpose(int semitones); bool FindWord(size_t stanza, size_t & measure, VLFraction & at); diff --git a/Sources/VLSheetView.mm b/Sources/VLSheetView.mm index f44637a..fb7a252 100644 --- a/Sources/VLSheetView.mm +++ b/Sources/VLSheetView.mm @@ -332,10 +332,9 @@ VLMusicElement sSemi2Accidental[12][12] = { const float x = fClefKeyW+measure*fMeasureW; const float yy = kSystemY+4.0f*kLineH; bool repeat; - int times; size_t volta; bool dotsPrecede= measure != 0 && - (song->DoesEndRepeat(m, ×) + (song->DoesEndRepeat(m) || (song->DoesEndEnding(m, &repeat) && repeat)); bool dotsFollow = measureDoesBeginRepeat(m); if (!dotsPrecede && !dotsFollow) { @@ -384,7 +383,7 @@ VLMusicElement sSemi2Accidental[12][12] = { [bz removeAllPoints]; } if (measureDoesBeginEnding(m, &volta)) { + if (song->DoesBeginEnding(m, 0, &volta)) { [bz setLineWidth:kThin]; [bz moveToPoint: NSMakePoint(x+kDblLineOff, yy+0.5f*kLineH)]; [bz lineToPoint: NSMakePoint(x+kDblLineOff, yy+2.0f*kLineH)]; diff --git a/Sources/VLXMLDocument.mm b/Sources/VLXMLDocument.mm index 4fdb97b..6ff3762 100644 --- a/Sources/VLXMLDocument.mm +++ b/Sources/VLXMLDocument.mm @@ -295,6 +295,98 @@ const char * sSteps = "C DbD EbE F GbG AbA BbB "; 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<fMeasures[measure].fMelody toMeasure:melMeas]; [self addChords:&song->fMeasures[measure].fChords toMeasure:harMeas]; @@ -404,10 +496,72 @@ int8_t sStepToPitch[] = { 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; + for (NSXMLElement * measure; measure = [e nextObject]; ) { VLProperties & prop = song->fProperties.front(); VLFraction unit(1, 4*prop.fDivisions); @@ -418,6 +572,9 @@ int8_t sStepToPitch[] = { if (m >= song->CountMeasures()) song->fMeasures.resize(m); + [self readBarlines:[measure elementsForName:@"barline"] measure:m + repeat:&repeat inRepeat:&inRepeat error:outError]; + NSEnumerator * n = [[measure elementsForName:@"note"] objectEnumerator]; for (NSXMLElement * note; note = [n nextObject]; ) { @@ -427,6 +584,8 @@ int8_t sStepToPitch[] = { at += n.fDuration; } } + if (inRepeat) + [self addRepeat:&repeat]; } - (void) readChords:(NSArray *)measures error:(NSError **)outError