diff --git a/Sources/VLMMADocument.mm b/Sources/VLMMADocument.mm index c97d31c..2d308e9 100644 --- a/Sources/VLMMADocument.mm +++ b/Sources/VLMMADocument.mm @@ -39,7 +39,7 @@ mmaFile += buf; song->fMeasures[m].MMAChords(mmas, prop); mmaFile += mmas; - song->fMeasures[m].MMANotes(mmas, prop); + song->fMeasures[m].MMANotes(mmas, prop, song->TiedDuration(m+1)); mmaFile += "\t{ " + mmas + " }\n"; } diff --git a/Sources/VLModel.cpp b/Sources/VLModel.cpp index 941318f..95e2839 100644 --- a/Sources/VLModel.cpp +++ b/Sources/VLModel.cpp @@ -191,10 +191,12 @@ void VLNote::LilypondName(std::string & name, VLFraction at, VLFraction prevDur, dur -= part; } for (size_t i=0; i= 0; name.clear(); VLFraction prevPart(0); - for (VLFraction dur = fDuration; dur.fNum; ) { + while (dur.fNum) { VLFraction part; bool grouped = dur==nextDur || (prevPart!=0 ? dur==prevPart : dur==prevDur); @@ -236,13 +246,16 @@ void VLNote::MMAName(std::string & name, VLFraction at, VLFraction prevDur, VLFr dur -= part; at += part; } - name += MMAPitchName(fPitch, useSharps); - if (fPitch != kNoPitch) { - for (int raise = (fPitch-kMiddleC)/kOctave; raise>0; --raise) + int pitch = fTied & kTiedWithPrev ? kNoPitch : fPitch; + name += MMAPitchName(pitch, useSharps); + if (pitch != kNoPitch) { + for (int raise = (pitch-kMiddleC)/kOctave; raise>0; --raise) name += '+'; - for (int lower = (kMiddleC-fPitch)/kOctave; lower>0; --lower) + for (int lower = (kMiddleC-pitch)/kOctave; lower>0; --lower) name += '-'; } + if (fTied & kTiedWithNext) + name += '~'; } struct VLChordModifier { @@ -755,7 +768,8 @@ VLMeasure::VLMeasure() { } -void VLMeasure::MMANotes(std::string & notes, const VLProperties & prop) const +void VLMeasure::MMANotes(std::string & notes, const VLProperties & prop, + VLFraction extra) const { VLFraction at(0); VLNoteList::const_iterator i = fMelody.begin(); @@ -766,15 +780,23 @@ void VLMeasure::MMANotes(std::string & notes, const VLProperties & prop) const for (; i!=e; ++i) { std::string note; VLFraction nextDur(0); + VLFraction dur(i->fDuration); VLNoteList::const_iterator n=i; if (++n != e) nextDur = n->fDuration; - i->MMAName(note, at, prevDur, nextDur, prop); - if (notes.size()) + else + dur += extra; + i->MMAName(note, at, dur, prevDur, nextDur, prop); + if (notes.size()>1) notes += ' '; - notes += note+';'; + if (note == "~") + notes += note; + else + notes += note+';'; at += i->fDuration; } + if (notes == "~") + notes += "<>;"; } void VLMeasure::MMAChords(std::string & chords, const VLProperties & prop) const @@ -934,8 +956,6 @@ void VLSong::AddNote(VLLyricsNote note, size_t measure, VLFraction at) // // Overlap, split current // - if (i->fTied & VLNote::kTiedWithPrev) - LastTie(fMeasures[measure-1]) &= ~VLNote::kTiedWithNext; note.fDuration = tEnd-at; i->fDuration = at-t; i = fMeasures[measure].fMelody.insert(++i, note); @@ -1035,7 +1055,7 @@ void VLSong::ExtendNote(size_t measure, VLFraction at) // k->fPitch = i->fPitch; k->fTied = VLNote::kTiedWithPrev; - i->fTied != VLNote::kTiedWithNext; + i->fTied |= VLNote::kTiedWithNext; k->fLyrics.clear(); if (wasTied) { // @@ -2007,3 +2027,19 @@ void VLSong::DeleteMeasures(size_t beginMeasure, size_t endMeasure) } } } + +VLFract VLSong::TiedDuration(size_t measure) +{ + VLFraction total(0); + + while (measure < fMeasures.size()) { + VLNote n = fMeasures[measure++].fMelody.front(); + + if (!(n.fTied & VLNote::kTiedWithPrev)) + break; + total += n.fDuration; + if (!(n.fTied & VLNote::kTiedWithNext)) + break; + } + return total; +} diff --git a/Sources/VLModel.h b/Sources/VLModel.h index 5f3155e..db382c8 100644 --- a/Sources/VLModel.h +++ b/Sources/VLModel.h @@ -140,7 +140,7 @@ struct VLNote { void Name(std::string & name, bool useSharps = false) const; void LilypondName(std::string & name, VLFraction at, VLFraction prevDur, VLFraction nextDur, bool & triplet, const VLProperties & prop) const; - void MMAName(std::string & name, VLFraction at, VLFraction prevDur, VLFraction nextDur, const VLProperties & prop) const; + void MMAName(std::string & name, VLFraction at, VLFraction dur, VLFraction prevDur, VLFraction nextDur, const VLProperties & prop) const; }; struct VLRest : VLNote { @@ -244,7 +244,7 @@ struct VLMeasure { VLMeasure(); - void MMANotes(std::string & notes, const VLProperties & prop) const; + void MMANotes(std::string & notes, const VLProperties & prop, VLFraction extra) const; void MMAChords(std::string & chords, const VLProperties & prop) const; }; @@ -354,6 +354,7 @@ struct VLSong { void LilypondNotes(std::string & notes) const; void LilypondChords(std::string & chords) const; void LilypondStanza(std::string & lyrics, size_t stanza) const; + VLFract TiedDuration(size_t measure); }; // Local Variables: diff --git a/Sources/VLSheetViewNotes.mm b/Sources/VLSheetViewNotes.mm index 591dce5..b7dfb92 100644 --- a/Sources/VLSheetViewNotes.mm +++ b/Sources/VLSheetViewNotes.mm @@ -299,7 +299,7 @@ VLNoteList::const_iterator next = note; if (++next != melody.end()) nextDur = next->fDuration; - BOOL first = !m || !note->fTied; + BOOL first = !m || !(note->fTied & VLNote::kTiedWithPrev); int pitch = note->fPitch; while (dur > 0) { VLFraction partialDur; // Actual value of note drawn diff --git a/Sources/VLXMLDocument.mm b/Sources/VLXMLDocument.mm index 6865230..ea6101b 100644 --- a/Sources/VLXMLDocument.mm +++ b/Sources/VLXMLDocument.mm @@ -104,7 +104,7 @@ const char * sSteps = "C DbD EbE F GbG AbA BbB "; -- (NSXMLElement *)noteWithPitch:(int)pitch duration:(int)units useSharps:(BOOL)useSharps +- (NSXMLElement *)noteWithPitch:(int)pitch duration:(int)units useSharps:(BOOL)useSharps tied:(int)tied { NSXMLElement * note = [NSXMLNode elementWithName:@"note"]; if (pitch == VLNote::kNoPitch) { @@ -140,6 +140,18 @@ const char * sSteps = "C DbD EbE F GbG AbA BbB "; [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"]]; @@ -189,7 +201,8 @@ const char * sSteps = "C DbD EbE F GbG AbA BbB "; VLFraction u = note->fDuration / resolution; int units = (u.fNum+u.fDenom/2)/u.fDenom; NSXMLElement*n = - [self noteWithPitch:note->fPitch duration:units useSharps:useSharps]; + [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]]; @@ -210,12 +223,12 @@ const char * sSteps = "C DbD EbE F GbG AbA BbB "; 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]]; + [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]]; + duration:units useSharps:useSharps tied:0]]; ch = [NSXMLNode elementWithName:@"chord"]; } for (int step=0; step<32; ++step) @@ -224,7 +237,7 @@ const char * sSteps = "C DbD EbE F GbG AbA BbB "; } else if (chord->fSteps & (1 << step)) { NSXMLElement * note = [self noteWithPitch:chord->fPitch+step - duration:units useSharps:useSharps]; + duration:units useSharps:useSharps tied:0]; [note insertChild:ch atIndex:0]; [meas addChild: note]; ch = [NSXMLNode elementWithName:@"chord"]; @@ -494,6 +507,8 @@ int8_t sStepToPitch[] = { 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"]