diff --git a/Sources/VLDocument.h b/Sources/VLDocument.h index 2b53a61..4f8eb98 100644 --- a/Sources/VLDocument.h +++ b/Sources/VLDocument.h @@ -55,9 +55,9 @@ enum { - (int) repeatVolta; - (bool) brandNew; -- (void) setKey:(int)key transpose:(BOOL)transpose; -- (void) setTimeNum:(int)num denom:(int)denom; -- (void) setDivisions:(int)divisions; +- (void) setKey:(int)key transpose:(BOOL)transpose inSections:(NSRange)sections; +- (void) setTimeNum:(int)num denom:(int)denom inSections:(NSRange)sections; +- (void) setDivisions:(int)divisions inSections:(NSRange)sections; - (void) setRepeatVolta:(int)repeatVolta; - (IBAction) showOutput:(id)sender; diff --git a/Sources/VLDocument.mm b/Sources/VLDocument.mm index 9c63b4d..3f4c1ec 100644 --- a/Sources/VLDocument.mm +++ b/Sources/VLDocument.mm @@ -203,11 +203,12 @@ return [NSNumber numberWithInt: (prop.fKey << 8) | (prop.fMode & 0xFF)]; } -- (void) setKey:(int)key transpose:(BOOL)transpose +- (void) setKey:(int)key transpose:(BOOL)transpose inSections:(NSRange)sections { [self willChangeSong]; [self willChangeValueForKey:@"songKey"]; - song->ChangeKey(key>>8, key & 0xFF, transpose); + while (sections.length-- > 0) + song->ChangeKey(sections.location++, key>>8, key & 0xFF, transpose); [self didChangeValueForKey:@"songKey"]; [self didChangeSong]; } @@ -219,11 +220,12 @@ return [NSNumber numberWithInt: (prop.fTime.fNum << 8) | prop.fTime.fDenom]; } -- (void) setTimeNum:(int)num denom:(int)denom +- (void) setTimeNum:(int)num denom:(int)denom inSections:(NSRange)sections { [self willChangeSong]; [self willChangeValueForKey:@"songTime"]; - song->ChangeTime(VLFraction(num, denom)); + while (sections.length-- > 0) + song->ChangeTime(sections.location++, VLFraction(num, denom)); [self didChangeValueForKey:@"songTime"]; [self didChangeSong]; } @@ -235,12 +237,13 @@ return [NSNumber numberWithInt: prop.fDivisions]; } -- (void) setDivisions:(int)divisions +- (void) setDivisions:(int)divisions inSections:(NSRange)sections { [self willChangeSong]; [self willChangeValueForKey:@"songDivisions"]; [self willChangeSong]; - song->ChangeDivisions(divisions); + while (sections.length-- > 0) + song->ChangeDivisions(sections.location++, divisions); [self didChangeValueForKey:@"songDivisions"]; [self didChangeSong]; } diff --git a/Sources/VLModel.cpp b/Sources/VLModel.cpp index 0ff86a6..5d2fdc7 100644 --- a/Sources/VLModel.cpp +++ b/Sources/VLModel.cpp @@ -896,9 +896,9 @@ static void TransposePinned(int8_t & pitch, int semi) pitch = octave+pitchInOctave; } -void VLSong::ChangeKey(int newKey, int newMode, bool transpose) +void VLSong::ChangeKey(int section, int newKey, int newMode, bool transpose) { - VLProperties & prop = fProperties.front(); + VLProperties & prop = fProperties[section]; int semi = 7*(newKey-prop.fKey) % 12; prop.fKey = newKey; prop.fMode = newMode; @@ -906,6 +906,9 @@ void VLSong::ChangeKey(int newKey, int newMode, bool transpose) return; for (size_t measure=0; measure= toProp.fTime) { + if (at < toProp.fTime) { + c.fDuration = toProp.fTime-at; + newChords.push_back(c); + } + break; + } else { + newChords.push_back(c); + at += c.fDuration; + } + } + + return newChords; + } +} + +static VLNoteList Realign(const VLNoteList & notes, + const VLProperties& fromProp, + const VLProperties& toProp) +{ + if (fromProp.fTime == toProp.fTime && fromProp.fDivisions == toProp.fDivisions) + return notes; + VLNoteList newNotes(notes); + if (fromProp.fTime < toProp.fTime) { + VLNote rest(toProp.fTime-fromProp.fTime); + newNotes.push_back(rest); + } else if (fromProp.fTime > toProp.fTime) { + VLNoteList::const_iterator i = notes.begin(); + VLNoteList::const_iterator e = notes.end(); + VLFraction at(0); + + for (; i!=e; ++i) { + VLNote n = *i; + if (at+n.fDuration >= toProp.fTime) { + if (at < toProp.fTime) { + n.fDuration = toProp.fTime-at; + newNotes.push_back(n); + } + break; + } else { + newNotes.push_back(n); + at += n.fDuration; + } + } + } + if (fromProp.fDivisions == toProp.fDivisions) { + VLRealigner realign(fromProp.fDivisions, toProp.fDivisions); + + VLNoteList alignedNotes; VLFraction at(0); VLFraction lastAt; - VLNoteList::iterator i = fMeasures[measure].fMelody.begin(); - VLNoteList::iterator e = fMeasures[measure].fMelody.end(); + VLNoteList::iterator i = newNotes.begin(); + VLNoteList::iterator e = newNotes.end(); for (; i!=e; ++i) { VLLyricsNote n = *i; VLFraction newAt = realign(at); - if (newMelody.empty()) { - newMelody.push_back(n); + if (alignedNotes.empty()) { + alignedNotes.push_back(n); lastAt = newAt; } else if (newAt != lastAt) { - newMelody.back().fDuration = newAt-lastAt; - newMelody.push_back(n); + alignedNotes.back().fDuration = newAt-lastAt; + alignedNotes.push_back(n); lastAt = newAt; } at += n.fDuration; } if (lastAt == at) - newMelody.pop_back(); + alignedNotes.pop_back(); else - newMelody.back().fDuration = at-lastAt; - fMeasures[measure].fMelody.swap(newMelody); - } - prop.fDivisions = newDivisions; + alignedNotes.back().fDuration = at-lastAt; + + return alignedNotes; + } else + return newNotes; } -void VLSong::ChangeTime(VLFraction newTime) +void VLSong::ChangeDivisions(int section, int newDivisions) { - VLProperties & prop = fProperties.front(); + VLProperties & prop = fProperties[section]; + if (prop.fDivisions == newDivisions) + return; // Unchanged + VLProperties newProp = prop; + newProp.fDivisions = newDivisions; + + // + // Only melody needs to be realigned, chords are already quarter notes + // + for (size_t measure=0; measure= newTime) { - if (at < newTime) { - c.fDuration = newTime-at; - newChords.push_back(c); - } - break; - } else { - newChords.push_back(c); - at += c.fDuration; - } - } - fMeasures[measure].fChords.swap(newChords); - } else - fMeasures[measure].fChords.push_back(rchord); - - if (newTime < prop.fTime) { - VLNoteList::iterator i = fMeasures[measure].fMelody.begin(); - VLNoteList::iterator e = fMeasures[measure].fMelody.end(); - VLFraction at(0); - VLNoteList newMelody; - - for (; i!=e; ++i) { - VLLyricsNote n = *i; - if (at+n.fDuration >= newTime) { - if (at < newTime) { - n.fDuration = newTime-at; - newMelody.push_back(n); - } - break; - } else { - newMelody.push_back(n); - at += n.fDuration; - } - } - fMeasures[measure].fMelody.swap(newMelody); - } else - fMeasures[measure].fMelody.push_back(rnote); - } - prop.fTime = newTime; + VLProperties newProp = prop; + newProp.fTime = newTime; + for (size_t measure=0; measure fEndings; }; +typedef std::vector VLPropertyList; +typedef std::vector VLMeasureList; +typedef std::vector VLRepeatList; + class VLSong { public: VLSong(bool initialize = true); void swap(VLSong & other); void clear(); - std::vector fProperties; - std::vector fMeasures; - std::vector fRepeats; - int8_t fGoToCoda; - int8_t fCoda; + VLPropertyList fProperties; + VLMeasureList fMeasures; + VLRepeatList fRepeats; + int8_t fGoToCoda; + int8_t fCoda; // // Iterate over measures in performance order @@ -340,9 +344,9 @@ public: bool DoesTieWithPrevRepeat(size_t measure) const; bool DoesTieWithNextRepeat(size_t measure) const; bool IsNonEmpty() const; - void ChangeKey(int newKey, int newMode, bool transpose); - void ChangeDivisions(int newDivisions); - void ChangeTime(VLFraction newTime); + void ChangeKey(int section, int newKey, int newMode, bool transpose); + void ChangeDivisions(int section, int newDivisions); + void ChangeTime(int section, VLFraction newTime); bool FindWord(size_t stanza, size_t & measure, VLFraction & at); bool PrevWord(size_t stanza, size_t & measure, VLFraction & at); diff --git a/Sources/VLSheetView.mm b/Sources/VLSheetView.mm index aa3abb8..cd706c0 100644 --- a/Sources/VLSheetView.mm +++ b/Sources/VLSheetView.mm @@ -591,7 +591,8 @@ const char * sBreak[3] = {"", "\xE2\xA4\xBE", "\xE2\x8E\x98"}; return; int key = [[sender selectedItem] tag]; - [[self document] setKey:key transpose:returnCode==NSAlertDefaultReturn]; + [[self document] setKey:key transpose:returnCode==NSAlertDefaultReturn + inSections:[self sectionsInSelection]]; fNeedsRecalc = kRecalc; [self setNeedsDisplay: YES]; } @@ -617,7 +618,8 @@ const char * sBreak[3] = {"", "\xE2\xA4\xBE", "\xE2\x8E\x98"}; { int time = [[sender selectedItem] tag]; - [[self document] setTimeNum: time >> 8 denom: time & 0xFF]; + [[self document] setTimeNum: time >> 8 denom: time & 0xFF + inSections:[self sectionsInSelection]]; fNeedsRecalc = kRecalc; [self setNeedsDisplay: YES]; } @@ -626,7 +628,7 @@ const char * sBreak[3] = {"", "\xE2\xA4\xBE", "\xE2\x8E\x98"}; { int div = [[sender selectedItem] tag]; - [[self document] setDivisions: div]; + [[self document] setDivisions: div inSections:[self sectionsInSelection]]; fNeedsRecalc = kRecalc; [self setNeedsDisplay: YES]; } diff --git a/Sources/VLSheetViewSelection.h b/Sources/VLSheetViewSelection.h index 644e6d7..ccc887e 100644 --- a/Sources/VLSheetViewSelection.h +++ b/Sources/VLSheetViewSelection.h @@ -12,6 +12,7 @@ - (void)editSelection; - (void)adjustSelection:(NSEvent *)event; +- (NSRange)sectionsInSelection; - (BOOL)validateUserInterfaceItem:(id)item; - (IBAction)cut:(id)sender; diff --git a/Sources/VLSheetViewSelection.mm b/Sources/VLSheetViewSelection.mm index 5de9b11..20b3f36 100644 --- a/Sources/VLSheetViewSelection.mm +++ b/Sources/VLSheetViewSelection.mm @@ -61,6 +61,27 @@ static VLSong sPasteboard; fCursorRegion = kRegionMeasure; } +- (NSRange)sectionsInSelection +{ + NSRange sections; + int firstSection; + int lastSection; + VLSong * song = [self song]; + + if (fSelEnd > -1) { + firstSection = song->fMeasures[fSelStart].fPropIdx; + lastSection = fSelEnd > fSelStart+1 ? + song->fMeasures[fSelEnd].fPropIdx : firstSection; + } else { + firstSection = 0; + lastSection = song->fMeasures.back().fPropIdx; + } + sections.location = firstSection; + sections.length = lastSection-firstSection+1; + + return sections; +} + - (BOOL)validateMenuItem:(id) item { SEL action = [item action];