diff --git a/include/MidiClip.h b/include/MidiClip.h index 02b5fa7e8..036f2771c 100644 --- a/include/MidiClip.h +++ b/include/MidiClip.h @@ -72,7 +72,6 @@ public: // Split the list of notes on the given position void splitNotes(NoteVector notes, TimePos pos); - void transpose(int semitones); // clip-type stuff inline MidiClipTypes type() const diff --git a/include/Note.h b/include/Note.h index 31ba6995e..b528b89f7 100644 --- a/include/Note.h +++ b/include/Note.h @@ -244,7 +244,14 @@ private: }; -typedef QVector NoteVector; + + +class NoteVector: public QVector +{ +public: + bool getBounds(TimePos& start, TimePos& end, int& lower, int& upper) const; + void transpose(int semitones) const; +}; #endif diff --git a/src/core/JournallingObject.cpp b/src/core/JournallingObject.cpp index 729303c67..f4d5f2451 100644 --- a/src/core/JournallingObject.cpp +++ b/src/core/JournallingObject.cpp @@ -58,6 +58,7 @@ void JournallingObject::addJournalCheckPoint() if( isJournalling() ) { Engine::projectJournal()->addJournalCheckPoint( this ); + // TODO Engine::getSong()->setModified() ? } } diff --git a/src/core/Note.cpp b/src/core/Note.cpp index cba02ee9e..b9ce91cf4 100644 --- a/src/core/Note.cpp +++ b/src/core/Note.cpp @@ -232,3 +232,59 @@ bool Note::withinRange(int tickStart, int tickEnd) const return pos().getTicks() >= tickStart && pos().getTicks() <= tickEnd && length().getTicks() != 0; } + + + + + + + + +/*! \brief Get the outer bounds of the notes + * + * \param start - Will be set to the first time position + * \param end - Will be set to the last end position + * \param lower - Will be set to the lowest key + * \param upper - Will be set to the highest key + * \return false if there are no notes + */ +bool NoteVector::getBounds(TimePos& start, TimePos& end, int& lower, int& upper) const +{ + if (empty()) { return false; } + + start = first()->pos(); + end = start; + lower = first()->key(); + upper = lower; + + for (const Note* note: *this) + { + // TODO should we assume that NoteVector is always sorted correctly, + // so first() always has the lowest time position? + start = std::min(start, note->pos()); + end = std::max(end, note->endPos()); + lower = std::min(lower, note->key()); + upper = std::max(upper, note->key()); + } + + return true; +} + + + + +/*! \brief Transpose all notes in the vector by X semitones + * + * Notes will be hard-clipped to the MIDI note range. To prevent this use getBounds() prior to transposing. + * + * \param semitones Semitones to transpose + */ +void NoteVector::transpose(int semitones) const +{ + if (empty() || !semitones) { return; } + + for (Note* note: *this) + { + note->setKey(note->key() + semitones); + } +} diff --git a/src/gui/clips/MidiClipView.cpp b/src/gui/clips/MidiClipView.cpp index f33fe0c4e..9909525fe 100644 --- a/src/gui/clips/MidiClipView.cpp +++ b/src/gui/clips/MidiClipView.cpp @@ -144,7 +144,28 @@ void MidiClipView::changeName() void MidiClipView::transposeSelection() { - int semitones = QInputDialog::getInt(this, tr("Transpose"), tr("Semitones to transpose:"), 0, -NumKeys, NumKeys, 1); + const auto selection = getClickedClips(); + + // Calculate the key boundries for all clips + int highest = 0; + int lowest = NumKeys - 1; + for (ClipView* clipview: selection) + { + if (auto mcv = dynamic_cast(clipview)) + { + TimePos start, end; + int low, high; + if (mcv->getMidiClip()->notes().getBounds(start, end, low, high)) + { + lowest = std::min(low, lowest); + highest = std::max(high, highest); + } + } + } + + int minStep = 0 - lowest; + int maxStep = NumKeys - 1 - highest; + int semitones = QInputDialog::getInt(this, tr("Transpose"), tr("Semitones to transpose:"), 0, minStep, maxStep, 1); if (semitones == 0) { return; } // TODO make this not crash @@ -161,9 +182,12 @@ void MidiClipView::transposeSelection() track->addJournalCheckPoint(); m_changedTracks.insert(track); } - mcv->getMidiClip()->transpose(semitones); + auto clip = mcv->getMidiClip(); + clip->notes().transpose(semitones); + emit clip->dataChanged(); } } + Engine::getSong()->setModified(); } diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index 69f73886b..ce2e9d0be 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -346,15 +346,8 @@ void MidiClip::splitNotes(NoteVector notes, TimePos pos) -void MidiClip::transpose(int semitones) -{ - if (notes().empty()) { return; } - for (Note* note: notes()) - { - note->setKey(note->key() + semitones); - } -} +