diff --git a/Sources/VLModel.cpp b/Sources/VLModel.cpp index c55d6bf..74c5c2a 100644 --- a/Sources/VLModel.cpp +++ b/Sources/VLModel.cpp @@ -818,6 +818,19 @@ void VLMeasure::MMAChords(std::string & chords, const VLProperties & prop, } } +bool VLMeasure::IsEmpty() const +{ + return fChords.size() == 1 && fMelody.size() == 1 + && fChords.front().fPitch == VLNote::kNoPitch + && fMelody.front().fPitch == VLNote::kNoPitch; +} + +bool VLMeasure::NoChords() const +{ + return fChords.size() == 1 + && fChords.front().fPitch == VLNote::kNoPitch; +} + VLSong::VLSong(bool initialize) { if (!initialize) @@ -827,21 +840,27 @@ VLSong::VLSong(bool initialize) VLProperties defaultProperties = {fourFour, 0, 1, 3}; fProperties.push_back(defaultProperties); - fMeasures.resize(32); // Leadin, AABA - - VLLyricsNote rest = VLLyricsNote(VLRest(1)); - VLChord rchord; - rchord.fDuration = 1; - - for (int i=0; i<32; ++i) { - fMeasures[i].fChords.push_back(rchord); - fMeasures[i].fMelody.push_back(rest); - } + + AddMeasure(); fGoToCoda = -1; fCoda = -1; } +void VLSong::AddMeasure() +{ + VLFraction dur = fProperties[0].fTime; + VLLyricsNote rest = VLLyricsNote(VLRest(dur)); + VLChord rchord; + rchord.fDuration = dur; + VLMeasure meas; + + meas.fChords.push_back(rchord); + meas.fMelody.push_back(rest); + + fMeasures.push_back(meas); +} + void VLSong::swap(VLSong & other) { fProperties.swap(other.fProperties); @@ -866,6 +885,12 @@ void VLSong::clear() // void VLSong::AddChord(VLChord chord, size_t measure, VLFraction at) { + // + // Always keep an empty measure in reserve + // + while (measure+1 >= fMeasures.size()) + AddMeasure(); + VLChordList::iterator i = fMeasures[measure].fChords.begin(); VLFraction t(0); @@ -925,6 +950,11 @@ void VLSong::DelChord(size_t measure, VLFraction at) t = tEnd; ++i; } + // + // Trim excess empty measures + // + if (measure == fMeasures.size()-2 && fMeasures[measure].IsEmpty()) + fMeasures.pop_back(); } uint8_t & FirstTie(VLMeasure & measure) @@ -945,6 +975,12 @@ uint8_t & LastTie(VLMeasure & measure) // void VLSong::AddNote(VLLyricsNote note, size_t measure, VLFraction at) { + // + // Always keep an empty measure in reserve + // + while (measure+1 >= fMeasures.size()) + AddMeasure(); + VLNoteList::iterator i = fMeasures[measure].fMelody.begin(); VLFraction t(0); @@ -1031,6 +1067,11 @@ void VLSong::DelNote(size_t measure, VLFraction at) t = tEnd; ++i; } + // + // Trim excess empty measures + // + if (measure == fMeasures.size()-2 && fMeasures[measure].IsEmpty()) + fMeasures.pop_back(); } void VLSong::ExtendNote(size_t measure, VLFraction at) @@ -1159,7 +1200,17 @@ void VLSong::Transpose(int semi) } } -size_t VLSong::CountStanzas() const +size_t VLSong::EmptyEnding() const +{ + size_t full = fMeasures.size(); + + while (full-- && fMeasures[full].IsEmpty()) + ; + + return fMeasures.size()-(full+1); +} + +size_t VLSong::CountStanzas() const { size_t stanzas = 0; @@ -1233,6 +1284,7 @@ void VLSong::LilypondNotes(std::string & notes) const std::string indent = ""; size_t seenEnding = 0; int numEndings = 0; + size_t endMeasure = fMeasures.size()-EmptyEnding(); for (size_t measure=0; measure(1, std::floor((sz.width-fClefKeyW) / fDisplayScale / fMeasureW)); - fNumSystems = (song->CountMeasures()+fMeasPerSystem-1)/fMeasPerSystem; + fNumSystems = std::max(2lu, (song->CountMeasures()+fMeasPerSystem-1)/fMeasPerSystem); sz.height = fNumSystems*kSystemH; NSSize boundsSz = {sz.width / fDisplayScale, sz.height / fDisplayScale}; @@ -289,6 +290,7 @@ VLMusicElement sSemi2Accidental[12][12] = { NSMakePoint(0.0, NSMaxY([dv frame])-NSHeight([cv bounds]))]; } + fLastMeasures = song->CountMeasures(); fNeedsRecalc = kNoRecalc; } @@ -518,7 +520,7 @@ VLMusicElement sSemi2Accidental[12][12] = { - (void)drawRect:(NSRect)rect { - if (fNeedsRecalc || [self inLiveResize]) { + if (fNeedsRecalc || [self inLiveResize] || [self song]->CountMeasures() != fLastMeasures) { [self recalculateDimensions]; rect = [self bounds]; } diff --git a/Sources/VLXMLDocument.mm b/Sources/VLXMLDocument.mm index fe4b976..449fab9 100644 --- a/Sources/VLXMLDocument.mm +++ b/Sources/VLXMLDocument.mm @@ -308,7 +308,8 @@ const char * sSteps = "C DbD EbE F GbG AbA BbB "; [melody addAttribute: [NSXMLNode attributeWithName:@"id" stringValue:@"MELO"]]; - for (int measure = 0; measure < song->CountMeasures(); ++measure) { + size_t endMeasure = song->CountMeasures()-song->EmptyEnding(); + for (int measure = 0; measure < endMeasure; ++measure) { NSXMLElement * melMeas = [NSXMLNode elementWithName:@"measure"]; [melMeas addAttribute: [NSXMLNode attributeWithName:@"number" @@ -601,18 +602,6 @@ int8_t sStepToPitch[] = { int m = [[[measure attributeForName:@"number"] stringValue] intValue]-1; - if (m >= song->CountMeasures()) { - size_t oldSz = song->fMeasures.size(); - song->fMeasures.resize(m+1); - VLLyricsNote rest = VLLyricsNote(VLRest(prop.fTime)); - VLChord rchord; - rchord.fDuration = prop.fTime; - for (size_t i=oldSz; i<=m; ++i) { - song->fMeasures[i].fChords.push_back(rchord); - song->fMeasures[i].fMelody.push_back(rest); - } - } - [self readBarlines:[measure elementsForName:@"barline"] measure:m repeat:&repeat inRepeat:&inRepeat error:outError]; if ([measure nodeForXPath:@".//sound[@coda=\"A\"]" error:outError])