diff --git a/include/MidiClip.h b/include/MidiClip.h index 036f2771c..02b5fa7e8 100644 --- a/include/MidiClip.h +++ b/include/MidiClip.h @@ -72,6 +72,7 @@ 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/MidiClipView.h b/include/MidiClipView.h index 82e552a8c..52a2630c6 100644 --- a/include/MidiClipView.h +++ b/include/MidiClipView.h @@ -68,6 +68,7 @@ protected slots: void resetName(); void changeName(); + void transposeSelection(); protected: diff --git a/src/gui/clips/MidiClipView.cpp b/src/gui/clips/MidiClipView.cpp index 987d1fe14..9df77e797 100644 --- a/src/gui/clips/MidiClipView.cpp +++ b/src/gui/clips/MidiClipView.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -36,6 +37,7 @@ #include "MidiClip.h" #include "PianoRoll.h" #include "RenameDialog.h" +#include "TrackView.h" MidiClipView::MidiClipView( MidiClip* clip, TrackView* parent ) : ClipView( clip, parent ), @@ -140,8 +142,37 @@ void MidiClipView::changeName() +void MidiClipView::transposeSelection() +{ + int semitones = QInputDialog::getInt(this, tr("Transpose"), tr("Semitones to transpose:"), 0, -NumKeys, NumKeys, 1); + if (semitones == 0) { return; } + + // TODO make this not crash + // Engine::getSong()->addJournalCheckPoint(); + + QSet m_changedTracks; + + for (ClipView* clipview: getClickedClips()) + { + if (auto mcv = dynamic_cast(clipview)) + { + m_changedTracks.insert(clipview->getTrackView()->getTrack()); + mcv->getMidiClip()->transpose(semitones); + } + } + for (Track* track: m_changedTracks) + { + track->addJournalCheckPoint(); + } +} + + + + void MidiClipView::constructContextMenu( QMenu * _cm ) { + bool isBeat = m_clip->type() == MidiClip::BeatClip; + QAction * a = new QAction( embed::getIconPixmap( "piano" ), tr( "Open in piano-roll" ), _cm ); _cm->insertAction( _cm->actions()[0], a ); @@ -159,6 +190,10 @@ void MidiClipView::constructContextMenu( QMenu * _cm ) _cm->addAction( embed::getIconPixmap( "edit_erase" ), tr( "Clear all notes" ), m_clip, SLOT( clear() ) ); + if (!isBeat) + { + _cm->addAction(embed::getIconPixmap("scale"), tr("Transpose"), this, SLOT(transposeSelection())); + } _cm->addSeparator(); _cm->addAction( embed::getIconPixmap( "reload" ), tr( "Reset name" ), @@ -167,7 +202,7 @@ void MidiClipView::constructContextMenu( QMenu * _cm ) tr( "Change name" ), this, SLOT( changeName() ) ); - if ( m_clip->type() == MidiClip::BeatClip ) + if (isBeat) { _cm->addSeparator(); diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index 7a5fb657d..69f73886b 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -346,6 +346,18 @@ 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); + } +} + + + void MidiClip::setType( MidiClipTypes _new_clip_type ) {