From 61478ba1e22d321612d41a2af13823934e0cb764 Mon Sep 17 00:00:00 2001 From: Juan Antonio Date: Thu, 5 Dec 2019 00:00:16 +0100 Subject: [PATCH] WIP Play along feature --- Makefile | 4 +-- mscore/drumroll.cpp | 2 +- mscore/musescore.cpp | 5 ++++ mscore/pianoroll/pianoroll.cpp | 1 + mscore/seq.cpp | 49 ++++++++++++++++++++++++++++++++++ mscore/seq.h | 8 +++++- mscore/shortcut.cpp | 12 ++++++++- share/workspaces/Advanced.xml | 1 + share/workspaces/Basic.xml | 3 ++- 9 files changed, 79 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index d302a6fd06c9b..3a798a3798b2f 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,7 @@ release: cd build.release; \ export PATH=${BINPATH}; \ cmake -DCMAKE_BUILD_TYPE=RELEASE \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=1 \ -DCMAKE_TOOLCHAIN_FILE="${CMAKE_TOOLCHAIN_FILE}" \ -DCMAKE_INSTALL_PREFIX="${PREFIX}" \ -DMSCORE_INSTALL_SUFFIX="${SUFFIX}" \ @@ -91,6 +92,7 @@ debug: cd build.debug; \ export PATH=${BINPATH}; \ cmake -DCMAKE_BUILD_TYPE=DEBUG \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=1 \ -DCMAKE_INSTALL_PREFIX="${PREFIX}" \ -DMSCORE_INSTALL_SUFFIX="${SUFFIX}" \ -DMUSESCORE_LABEL="${LABEL}" \ @@ -222,5 +224,3 @@ unix: zip: zip -q -r MuseScore-${VERSION}.zip * -x .git\* -x vtest/html\* - - diff --git a/mscore/drumroll.cpp b/mscore/drumroll.cpp index 4365ae90677c8..708caa36da6fd 100644 --- a/mscore/drumroll.cpp +++ b/mscore/drumroll.cpp @@ -66,6 +66,7 @@ DrumrollEditor::DrumrollEditor(QWidget* parent) tb->addSeparator(); #ifdef HAS_MIDI tb->addAction(getAction("midi-on")); + tb->addAction(getAction("play-along")); #endif QAction* a = getAction("follow"); a->setCheckable(true); @@ -427,4 +428,3 @@ void DrumrollEditor::cmd(QAction* a) gv->setStaff(staff, locator); } } - diff --git a/mscore/musescore.cpp b/mscore/musescore.cpp index c08a15ecd3daa..d5ceaad115495 100644 --- a/mscore/musescore.cpp +++ b/mscore/musescore.cpp @@ -284,6 +284,8 @@ const std::list MuseScore::_allPlaybackControlEntries { #ifdef HAS_MIDI "midi-on", "", + "play-along", + "", #endif "rewind", "play", @@ -6592,6 +6594,9 @@ void MuseScore::cmd(QAction* a, const QString& cmd) } } #endif + else if (cmd == "play-along") { + seq->setPlayAlong(a->isChecked()); + } else { if (cv) { //isAncestorOf is called to see if a widget from inspector has focus diff --git a/mscore/pianoroll/pianoroll.cpp b/mscore/pianoroll/pianoroll.cpp index 4c17a09c20ebc..f33884f2cea2d 100644 --- a/mscore/pianoroll/pianoroll.cpp +++ b/mscore/pianoroll/pianoroll.cpp @@ -73,6 +73,7 @@ PianorollEditor::PianorollEditor(QWidget* parent) tbMain->addSeparator(); #ifdef HAS_MIDI tbMain->addAction(getAction("midi-on")); + tbMain->addAction(getAction("play-along")); #endif tbMain->addSeparator(); diff --git a/mscore/seq.cpp b/mscore/seq.cpp index eca7c082fca34..43b006359acf8 100644 --- a/mscore/seq.cpp +++ b/mscore/seq.cpp @@ -147,6 +147,7 @@ static long ovTell(void* datasource) Seq::Seq() : midi(nullptr) { + playAlong = false; running = false; playlistChanged = false; cs = 0; @@ -617,6 +618,10 @@ void Seq::processMessages() break; case SeqMsgId::PLAY: putEvent(msg.event); + if (playAlong) { + qDebug("Event: %d %d %d", msg.event.type(), msg.event.pitch(), msg.event.channel()); + handlePlayAlong(msg.event); + } break; case SeqMsgId::SEEK: setPos(msg.intVal); @@ -630,6 +635,37 @@ void Seq::processMessages() } } +void Seq::handlePlayAlong(NPlayEvent &event) +{ + if (event.type() == ME_NOTEON && event.velo() != 0){ + pressed_notes.insert(event.pitch()); + } + + for (auto pending_note : pending_notes) { + bool note_found = false; + for (auto pressed_note : pressed_notes) { + if (pending_note->pitch() == pressed_note) { + pending_notes.erase(pending_note); + note_found = true; + break; + } + } + + if(note_found) { + break; + } + } + + if (event.velo() == 0 || event.type() == ME_NOTEOFF){ + for (auto pressed_note : pressed_notes){ + if (event.pitch() == pressed_note) { + pressed_notes.erase(pressed_note); + break; + } + } + } +} + //--------------------------------------------------------- // metronome //--------------------------------------------------------- @@ -787,6 +823,10 @@ void Seq::process(unsigned framesPerPeriod, float* buffer) processMessages(); if (state == Transport::PLAY) { + if (playAlong && !pending_notes.empty()) { + return; + } + if (!cs) return; @@ -889,7 +929,9 @@ void Seq::process(unsigned framesPerPeriod, float* buffer) } } const NPlayEvent& event = (*pPlayPos)->second; + playEvent(event, framePos); + if (event.type() == ME_TICK1) { tickRemain = tickLength; tickVolume = event.velo() ? qreal(event.value()) / 127.0 : 1.0; @@ -944,6 +986,7 @@ void Seq::process(unsigned framesPerPeriod, float* buffer) } } else { + pending_notes.clear(); // Outside of playback mode while (!liveEventQueue()->empty()) { const NPlayEvent& event = liveEventQueue()->dequeue(); @@ -1154,6 +1197,10 @@ void Seq::setRelTempo(double relTempo) guiToSeq(SeqMsg(SeqMsgId::TEMPO_CHANGE, relTempo)); } +void Seq::setPlayAlong(bool play) + { + playAlong = play; + } //--------------------------------------------------------- // setPos // seek @@ -1637,6 +1684,7 @@ void Seq::heartBeatTimeout() if (!se->isNote()) continue; Note* currentNote = toNote(se); + pending_notes.insert(currentNote); currentNote->setMark(true); markedNotes.append(currentNote); r |= currentNote->canvasBoundingRect(); @@ -1651,6 +1699,7 @@ void Seq::heartBeatTimeout() continue; Note* currentNote = toNote(se); currentNote->setMark(false); + currentNote->setColor(Qt::blue); r |= currentNote->canvasBoundingRect(); markedNotes.removeOne(currentNote); } diff --git a/mscore/seq.h b/mscore/seq.h index 3c39c95b5226d..71af75ea73797 100644 --- a/mscore/seq.h +++ b/mscore/seq.h @@ -25,6 +25,7 @@ #include "libmscore/fraction.h" #include "libmscore/fifo.h" #include "libmscore/tempo.h" +#include #include "audio/midi/event.h" #include "audio/drivers/driver.h" @@ -107,6 +108,9 @@ class Seq : public QObject, public Sequencer { Q_OBJECT mutable QMutex mutex; + std::set pending_notes; + std::set pressed_notes; + bool playAlong; MasterScore* cs; ScoreView* cv; @@ -205,6 +209,8 @@ class Seq : public QObject, public Sequencer { inline QQueue* liveEventQueue() { return &_liveEventQueue; } + void handlePlayAlong(NPlayEvent &event); + private slots: void seqMessage(int msg, int arg = 0); void heartBeatTimeout(); @@ -292,6 +298,7 @@ class Seq : public QObject, public Sequencer { unsigned getCurrentMillisecondTimestampWithLatency(unsigned framePos) const; void preferencesChanged() { cachedPrefs.update(); } + void setPlayAlong(bool); }; extern Seq* seq; @@ -300,4 +307,3 @@ extern bool initMidi(); } // namespace Ms #endif - diff --git a/mscore/shortcut.cpp b/mscore/shortcut.cpp index b76ff2be1e579..76d92fe9cf04b 100644 --- a/mscore/shortcut.cpp +++ b/mscore/shortcut.cpp @@ -1945,6 +1945,17 @@ Shortcut Shortcut::_sc[] = { Qt::WindowShortcut, ShortcutFlags::A_CHECKABLE }, + { + MsWidget::MAIN_WINDOW, + STATE_NORMAL | STATE_NOTE_ENTRY, + "play-along", + QT_TRANSLATE_NOOP("action","Play along"), + QT_TRANSLATE_NOOP("action","Toggle 'Play Along'"), + 0, + Icons::midiin_ICON, + Qt::WindowShortcut, + ShortcutFlags::A_CHECKABLE + }, { MsWidget::SCORE_TAB, STATE_NORMAL | STATE_NOTE_ENTRY, @@ -4779,4 +4790,3 @@ QKeySequence Shortcut::keySeqFromString(const QString& str, QKeySequence::Sequen return keySeq; } } - diff --git a/share/workspaces/Advanced.xml b/share/workspaces/Advanced.xml index 2ee474eaa1539..09c409a7762b9 100644 --- a/share/workspaces/Advanced.xml +++ b/share/workspaces/Advanced.xml @@ -3355,6 +3355,7 @@ repeat pan metronome + play-along diff --git a/share/workspaces/Basic.xml b/share/workspaces/Basic.xml index e83af516930b6..8c906203463b2 100644 --- a/share/workspaces/Basic.xml +++ b/share/workspaces/Basic.xml @@ -1,6 +1,6 @@ - + Basic @@ -923,6 +923,7 @@ repeat pan metronome + play-along