Feature: Glue notes (#5721)

Tool to glue selected notes together. Selected notes that are
adjacent to each other or overlapping are transformed into one note
stretching over the combined notes on/off times.

Key command: <Shift> + G

Partially fixes: #746 which is part of #4877

Co-authored-by: Hyunjin Song <tteu.ingog@gmail.com>
Co-authored-by: IanCaio <iancaio_dev@hotmail.com>
Co-authored-by: Spekular <Spekularr@gmail.com>
Co-authored-by: Kevin Zander <veratil@gmail.com>
Co-authored-by: Oskar Wallgren <oskar.wallgren13@gmail.com>
This commit is contained in:
Oskar Wallgren
2020-10-19 22:18:21 +02:00
committed by GitHub
parent 4c559b91f8
commit acd0e4d430
4 changed files with 84 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 B

View File

@@ -208,6 +208,7 @@ protected slots:
void selectRegionFromPixels( int xStart, int xEnd );
void clearGhostPattern();
void glueNotes();
signals:

View File

@@ -37,6 +37,7 @@
#include <QScrollBar>
#include <QStyleOption>
#include <QtMath>
#include <QToolButton>
#ifndef __USE_XOPEN
#define __USE_XOPEN
@@ -644,6 +645,73 @@ void PianoRoll::clearGhostPattern()
}
void PianoRoll::glueNotes()
{
if (hasValidPattern())
{
NoteVector selectedNotes = getSelectedNotes();
if (selectedNotes.empty())
{
TextFloat::displayMessage( tr( "Glue notes failed" ),
tr( "Please select notes to glue first." ),
embed::getIconPixmap( "glue", 24, 24 ),
3000 );
return;
}
// Make undo possible
m_pattern->addJournalCheckPoint();
// Sort notes on key and then pos.
std::sort(selectedNotes.begin(), selectedNotes.end(),
[](const Note * note, const Note * compareNote) -> bool
{
if (note->key() == compareNote->key())
{
return note->pos() < compareNote->pos();
}
return note->key() < compareNote->key();
});
QList<Note *> noteToRemove;
NoteVector::iterator note = selectedNotes.begin();
auto nextNote = note+1;
NoteVector::iterator end = selectedNotes.end();
while (note != end && nextNote != end)
{
// key and position match for glue. The notes are already
// sorted so we don't need to test that nextNote is the same
// position or next in sequence.
if ((*note)->key() == (*nextNote)->key()
&& (*nextNote)->pos() <= (*note)->pos()
+ qMax(MidiTime(0), (*note)->length()))
{
(*note)->setLength(qMax((*note)->length(),
MidiTime((*nextNote)->endPos() - (*note)->pos())));
noteToRemove.push_back(*nextNote);
++nextNote;
}
// key or position doesn't match
else
{
note = nextNote;
nextNote = note+1;
}
}
// Remove old notes
for (int i = 0; i < noteToRemove.count(); ++i)
{
m_pattern->removeNote(noteToRemove[i]);
}
update();
}
}
void PianoRoll::loadMarkedSemiTones(const QDomElement & de)
{
// clear marked semitones to prevent leftover marks
@@ -4361,6 +4429,21 @@ PianoRollWindow::PianoRollWindow() :
DropToolBar *timeLineToolBar = addDropToolBarToTop( tr( "Timeline controls" ) );
m_editor->m_timeLine->addToolButtons( timeLineToolBar );
// -- Note modifier tools
// Toolbar
QToolButton * noteToolsButton = new QToolButton(m_toolBar);
noteToolsButton->setIcon(embed::getIconPixmap("tool"));
noteToolsButton->setPopupMode(QToolButton::InstantPopup);
// Glue
QAction * glueAction = new QAction(embed::getIconPixmap("glue"),
tr("Glue"), noteToolsButton);
connect(glueAction, SIGNAL(triggered()), m_editor, SLOT(glueNotes()));
glueAction->setShortcut( Qt::SHIFT | Qt::Key_G );
noteToolsButton->addAction(glueAction);
notesActionsToolBar->addWidget(noteToolsButton);
addToolBarBreak();