diff --git a/include/MidiClip.h b/include/MidiClip.h index b3ed0d84a20..c0e2d692d5b 100644 --- a/include/MidiClip.h +++ b/include/MidiClip.h @@ -82,6 +82,9 @@ class LMMS_EXPORT MidiClip : public Clip // Split the list of notes on the given position void splitNotes(const NoteVector& notes, TimePos pos); + // Split the list of notes along a line + void splitNotesAlongLine(TimePos pos1, int key1, TimePos pos2, int key2); + // clip-type stuff inline Type type() const { diff --git a/include/PianoRoll.h b/include/PianoRoll.h index e9939101c3f..a1d045ff452 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -456,9 +456,14 @@ protected slots: // did we start a mouseclick with shift pressed bool m_startedWithShift; - // Variable that holds the position in ticks for the knife action - int m_knifeTickPos; - void updateKnifePos(QMouseEvent* me); + // Variables that hold the start and end position for the knife line + TimePos m_knifeStartTickPos; + int m_knifeStartKey; + TimePos m_knifeEndTickPos; + int m_knifeEndKey; + bool m_knifeDown; + + void updateKnifePos(QMouseEvent* me, bool initial); friend class PianoRollWindow; diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index cbd68e7ff4f..dd79c7ebc47 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -1610,19 +1610,8 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) // -- Knife if (m_editMode == EditMode::Knife && me->button() == Qt::LeftButton) { - NoteVector n; - Note* note = noteUnderMouse(); - - if (note) - { - n.push_back(note); - - updateKnifePos(me); - - // Call splitNotes for the note - m_midiClip->splitNotes(n, TimePos(m_knifeTickPos)); - } - + updateKnifePos(me, true); + m_knifeDown = true; update(); return; } @@ -2140,6 +2129,7 @@ void PianoRoll::setKnifeAction() m_knifeMode = m_editMode; m_editMode = EditMode::Knife; m_action = Action::Knife; + m_knifeDown = false; setCursor(Qt::ArrowCursor); update(); } @@ -2149,6 +2139,7 @@ void PianoRoll::cancelKnifeAction() { m_editMode = m_knifeMode; m_action = Action::None; + m_knifeDown = false; update(); } @@ -2277,6 +2268,11 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me ) m_midiClip->rearrangeAllNotes(); } + else if (m_action == Action::Knife) + { + m_midiClip->splitNotesAlongLine(TimePos(m_knifeStartTickPos), m_knifeStartKey, TimePos(m_knifeEndTickPos), m_knifeEndKey); + m_knifeDown = false; + } if( m_action == Action::MoveNote || m_action == Action::ResizeNote ) { @@ -2380,7 +2376,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) // Update Knife position if we are on knife mode if (m_editMode == EditMode::Knife) { - updateKnifePos(me); + updateKnifePos(me, false); } if( me->y() > PR_TOP_MARGIN || m_action != Action::None ) @@ -2761,11 +2757,13 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) -void PianoRoll::updateKnifePos(QMouseEvent* me) +void PianoRoll::updateKnifePos(QMouseEvent* me, bool initial) { // Calculate the TimePos from the mouse - int mouseViewportPos = me->x() - m_whiteKeyWidth; - int mouseTickPos = mouseViewportPos * TimePos::ticksPerBar() / m_ppb + m_currentPosition; + int mouseViewportPosX = me->x() - m_whiteKeyWidth; + int mouseViewportPosY = keyAreaBottom() - 1 - me->y(); + int mouseTickPos = mouseViewportPosX * TimePos::ticksPerBar() / m_ppb + m_currentPosition; + int mouseKey = mouseViewportPosY / m_keyLineHeight + m_startKey - 1; // If ctrl is not pressed, quantize the position if (!(me->modifiers() & Qt::ControlModifier)) @@ -2773,7 +2771,13 @@ void PianoRoll::updateKnifePos(QMouseEvent* me) mouseTickPos = floor(mouseTickPos / quantization()) * quantization(); } - m_knifeTickPos = mouseTickPos; + if (initial) + { + m_knifeStartTickPos = mouseTickPos; + m_knifeStartKey = mouseKey; + } + m_knifeEndTickPos = mouseTickPos; + m_knifeEndKey = mouseKey; } @@ -3533,37 +3537,21 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) } // -- Knife tool (draw cut line) - if (m_action == Action::Knife) + if (m_action == Action::Knife && m_knifeDown) { auto xCoordOfTick = [this](int tick) { return m_whiteKeyWidth + ( (tick - m_currentPosition) * m_ppb / TimePos::ticksPerBar()); }; - Note* n = noteUnderMouse(); - if (n) - { - const int key = n->key() - m_startKey + 1; - int y = y_base - key * m_keyLineHeight; + int x1 = xCoordOfTick(m_knifeStartTickPos); + int y1 = y_base - (m_knifeStartKey - m_startKey + 1) * m_keyLineHeight; + int x2 = xCoordOfTick(m_knifeEndTickPos); + int y2 = y_base - (m_knifeEndKey - m_startKey + 1) * m_keyLineHeight; - int x = xCoordOfTick(m_knifeTickPos); + p.setPen(QPen(m_knifeCutLineColor, 1)); + p.drawLine(x1, y1, x2, y2); - if (x > xCoordOfTick(n->pos()) && - x < xCoordOfTick(n->pos() + n->length())) - { - p.setPen(QPen(m_knifeCutLineColor, 1)); - p.drawLine(x, y, x, y + m_keyLineHeight); - - setCursor(Qt::BlankCursor); - } - else - { - setCursor(Qt::ArrowCursor); - } - } - else - { - setCursor(Qt::ArrowCursor); - } + setCursor(Qt::BlankCursor); } // -- End knife tool diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index 409fb60aefe..87256943a8f 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -344,6 +344,36 @@ void MidiClip::splitNotes(const NoteVector& notes, TimePos pos) } } +void MidiClip::splitNotesAlongLine(TimePos pos1, int key1, TimePos pos2, int key2) +{ + if (m_notes.empty()) { return; } + // Don't split if the line is horitzontal + if (key1 == key2) { return; } + + addJournalCheckPoint(); + + NoteVector notesCopy = m_notes; + + float slope = 1.f * (pos2 - pos1) / (key2 - key1); + + for (const auto& note : notesCopy) + { + TimePos keyIntercept = slope * (note->key() - key1) + pos1; + if (note->pos() < keyIntercept && note->endPos() > keyIntercept) + { + Note newNote1 = Note(*note); + newNote1.setLength(keyIntercept - note->pos()); + addNote(newNote1, false); + + Note newNote2 = Note(*note); + newNote2.setPos(keyIntercept); + newNote2.setLength(note->endPos() - keyIntercept); + addNote(newNote2, false); + + removeNote(note); + } + } +}