Skip to content

Commit

Permalink
Real-time (automatic) note entry mode
Browse files Browse the repository at this point in the history
  • Loading branch information
shoogle committed Aug 8, 2016
1 parent f4c4f84 commit 205d669
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 4 deletions.
82 changes: 78 additions & 4 deletions mscore/scoreview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,11 @@ ScoreView::ScoreView(QWidget* parent)
s->addTransition(new NoteEntryDragTransition(this)); // mouse drag
s->addTransition(new NoteEntryButtonTransition(this)); // mouse button
s->addTransition(new CommandTransition("play", states[ENTRY_PLAY])); // ->entryPlay
realtimeTimer = new QTimer(this);
connect(realtimeTimer, SIGNAL(timeout()), this, SLOT(triggerCmdRealtimeAdvance()));
extendNoteTimer = new QTimer(this);
connect(extendNoteTimer, SIGNAL(timeout()), this, SLOT(extendCurrentNote()));
extendNoteTimer->setSingleShot(true);

// setup normal drag canvas state
s = states[DRAG];
Expand Down Expand Up @@ -3262,9 +3267,7 @@ void ScoreView::cmd(const QAction* a)
// STATE_NOTE_ENTRY_REALTIME actions (auto or manual)

else if (cmd == "realtime-advance") {
// The user will want to press notes "on the beat" and not before the beat, so wait a
// little in case midi input event is received just after realtime-advance was called.
QTimer::singleShot(100, this, SLOT(cmdRealtimeAdvance()));
realtimeAdvance(true);
}

// STATE_HARMONY_FIGBASS_EDIT actions
Expand Down Expand Up @@ -5254,15 +5257,86 @@ void ScoreView::cmdTuplet(int n)

void ScoreView::midiNoteReceived(int pitch, bool chord, int velocity)
{
qDebug("midiNoteReceived %d chord %d", pitch, chord);

MidiInputEvent ev;
ev.pitch = pitch;
ev.chord = chord;
ev.velocity = velocity;

qDebug("midiNoteReceived %d chord %d", pitch, chord);
score()->masterScore()->enqueueMidiEvent(ev);

if (!score()->undoStack()->active())
cmd(0);

if (!chord && velocity && !realtimeTimer->isActive() && score()->usingNoteEntryMethod(NoteEntryMethod::REALTIME_AUTO)) {
// First note pressed in automatic real-time mode.
extendNoteTimer->start(preferences.realtimeDelay); // set timer to trigger repeatedly
triggerCmdRealtimeAdvance(); // also trigger once immediately
}

}

//---------------------------------------------------------
// extendCurrentNote
// Called after user has held down a midi key for a while.
// TODO: adapt to allow calling from StepTime mode.
//---------------------------------------------------------

void ScoreView::extendCurrentNote()
{
if (!noteEntryMode() || realtimeTimer->isActive())
return;

allowRealtimeRests = false;
realtimeTimer->start(preferences.realtimeDelay); // set timer to trigger repeatedly
triggerCmdRealtimeAdvance(); // also trigger once immediately
}

//---------------------------------------------------------
// realtimeAdvance
//---------------------------------------------------------

void ScoreView::realtimeAdvance(bool allowRests)
{
if (!noteEntryMode())
return;
InputState& is = score()->inputState();
switch (is.noteEntryMethod()) {
case NoteEntryMethod::REALTIME_MANUAL:
allowRealtimeRests = allowRests;
triggerCmdRealtimeAdvance();
break;
case NoteEntryMethod::REALTIME_AUTO:
if (realtimeTimer->isActive())
realtimeTimer->stop();
else {
allowRealtimeRests = allowRests;
realtimeTimer->start(preferences.realtimeDelay);
}
break;
default:
break;
}
}

//---------------------------------------------------------
// triggerCmdRealtimeAdvance
//---------------------------------------------------------

void ScoreView::triggerCmdRealtimeAdvance()
{
InputState& is = score()->inputState();
bool realtime = is.usingNoteEntryMethod(NoteEntryMethod::REALTIME_AUTO) || is.usingNoteEntryMethod(NoteEntryMethod::REALTIME_MANUAL);
if (!realtime || !noteEntryMode() || (!allowRealtimeRests && score()->activeMidiPitches()->empty())) {
if (realtimeTimer->isActive())
realtimeTimer->stop();
allowRealtimeRests = true;
return;
}
// The user will want to press notes "on the beat" and not before the beat, so wait a
// little in case midi input event is received just after realtime-advance was called.
QTimer::singleShot(100, this, SLOT(cmdRealtimeAdvance()));
}

//---------------------------------------------------------
Expand Down
8 changes: 8 additions & 0 deletions mscore/scoreview.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ class ScoreView : public QWidget, public MuseScoreView {
PositionCursor* _cursor;
ShadowNote* shadowNote;

// Realtime state: Note: always set allowRealtimeRests to desired value before starting a timer.
QTimer* realtimeTimer; // multi-shot timer for advancing in automatic realtime mode
QTimer* extendNoteTimer; // single-shot timer for initial advancement when a note is held
bool allowRealtimeRests; // Allow entering rests in realtime mode? (See note above)

// Loop In/Out marks in the score
PositionCursor* _curLoopIn;
PositionCursor* _curLoopOut;
Expand Down Expand Up @@ -234,6 +239,7 @@ class ScoreView : public QWidget, public MuseScoreView {
void figuredBassTab(bool meas, bool back);
void figuredBassTicksTab(int ticks);
void figuredBassEndEdit();
void realtimeAdvance(bool allowRests);
void cmdAddPitch(int note, bool addFlag, bool insert);
void cmdAddFret(int fret);
void cmdAddChordName();
Expand Down Expand Up @@ -279,7 +285,9 @@ class ScoreView : public QWidget, public MuseScoreView {

void posChanged(POS pos, unsigned tick);
void loopToggled(bool);
void triggerCmdRealtimeAdvance();
void cmdRealtimeAdvance();
void extendCurrentNote();

public slots:
void setViewRect(const QRectF&);
Expand Down

0 comments on commit 205d669

Please sign in to comment.