Skip to content

Commit

Permalink
Real-time (manual) note entry mode
Browse files Browse the repository at this point in the history
  • Loading branch information
shoogle committed Aug 8, 2016
1 parent 2011942 commit 8371a25
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 28 deletions.
53 changes: 38 additions & 15 deletions libmscore/cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1779,12 +1779,21 @@ bool Score::processMidiInput()
if (MScore::debugMode)
qDebug("processMidiInput");

NoteEntryMethod entryMethod = _is.noteEntryMethod();
bool cmdActive = false;
while (!midiInputQueue()->empty()) {
MidiInputEvent ev = midiInputQueue()->dequeue();
for (auto itr = activeMidiPitches()->begin(); itr != activeMidiPitches()->end();) {
if ((*itr).pitch == ev.pitch)
itr = activeMidiPitches()->erase(itr);
else
++itr;
}
if (MScore::debugMode)
qDebug("<-- !noteentry dequeue %i", ev.pitch);
if (!noteEntryMode()) {
if (!noteEntryMode()
|| entryMethod == NoteEntryMethod::REALTIME_AUTO
|| entryMethod == NoteEntryMethod::REALTIME_MANUAL) {
int staffIdx = selection().staffStart();
Part* p;
if (staffIdx < 0 || staffIdx >= nstaves())
Expand All @@ -1802,25 +1811,39 @@ bool Score::processMidiInput()
0.0);
}
}
else {
if (ev.velocity == 0)
if (noteEntryMode()) {
if (ev.velocity == 0) {
// delete note in realtime mode
//Chord* chord = static_cast<Chord*>(_is.cr());
//std::vector<Note*> notes = chord->notes();
if (entryMethod == NoteEntryMethod::REALTIME_AUTO || entryMethod == NoteEntryMethod::REALTIME_MANUAL) {
if (_is.cr()->isChord()) {
Note* n = static_cast<Chord*>(_is.cr())->findNote(ev.pitch);
if (n) {
qDebug("Pitches match! Note %i, Pitch %i", n->pitch(), ev.pitch);
if (!cmdActive) {
startCmd();
cmdActive = true;
}
deleteItem(n->tieBack());
deleteItem(n);
}
}
}
continue;
}
if (!cmdActive) {
startCmd();
cmdActive = true;
}
NoteVal nval(ev.pitch);
Staff* st = staff(inputState().track() / VOICES);

// if transposing, interpret MIDI pitch as representing desired written pitch
// set pitch based on corresponding sounding pitch
if (!styleB(StyleIdx::concertPitch))
nval.pitch += st->part()->instrument(inputState().tick())->transpose().chromatic;
// let addPitch calculate tpc values from pitch
//Key key = st->key(inputState().tick());
//nval.tpc1 = pitch2tpc(nval.pitch, key, Prefer::NEAREST);

addPitch(nval, ev.chord);
if (activeMidiPitches()->empty())
ev.chord = false;
else
ev.chord = true;
// TODO: add shadow note instead of real note in realtime modes
// (note becomes real when realtime-advance triggered).
addMidiPitch(ev.pitch, ev.chord);
activeMidiPitches()->push_back(ev);
}
}
if (cmdActive) {
Expand Down
65 changes: 58 additions & 7 deletions libmscore/edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,11 @@ Note* Score::addNote(Chord* chord, NoteVal& noteVal)
setPlayNote(true);
setPlayChord(true);
select(note, SelectType::SINGLE, 0);
if (!chord->staff()->isTabStaff())
_is.moveToNextInputPos();
if (!chord->staff()->isTabStaff()) {
NoteEntryMethod entryMethod = _is.noteEntryMethod();
if (entryMethod != NoteEntryMethod::REALTIME_AUTO && entryMethod != NoteEntryMethod::REALTIME_MANUAL)
_is.moveToNextInputPos();
}
return note;
}

Expand Down Expand Up @@ -1030,6 +1033,47 @@ NoteVal Score::noteValForPosition(Position pos, bool &error)
return nval;
}

//---------------------------------------------------------
// addTiedMidiPitch
//---------------------------------------------------------

Note* Score::addTiedMidiPitch(int pitch, bool addFlag, Chord* prevChord)
{
if (prevChord->isChord()) {
Note* n = addMidiPitch(pitch, addFlag);
Note* nn = prevChord->findNote(n->pitch());
if (nn) {
Tie* tie = new Tie(this);
tie->setStartNote(nn);
tie->setEndNote(n);
tie->setTrack(n->track());
undoAddElement(tie);
return n;
}
undoRemoveElement(n);
}
return 0;
}

//---------------------------------------------------------
// addMidiPitch
//---------------------------------------------------------

Note* Score::addMidiPitch(int pitch, bool addFlag)
{
NoteVal nval(pitch);
Staff* st = staff(inputState().track() / VOICES);

// if transposing, interpret MIDI pitch as representing desired written pitch
// set pitch based on corresponding sounding pitch
if (!styleB(StyleIdx::concertPitch))
nval.pitch += st->part()->instrument(inputState().tick())->transpose().chromatic;
// let addPitch calculate tpc values from pitch
//Key key = st->key(inputState().tick());
//nval.tpc1 = pitch2tpc(nval.pitch, key, Prefer::NEAREST);
return addPitch(nval, addFlag);
}

//---------------------------------------------------------
// addPitch
//---------------------------------------------------------
Expand All @@ -1044,8 +1088,11 @@ Note* Score::addPitch(NoteVal& nval, bool addFlag)
return 0;
}
Note* note = addNote(c, nval);
if (_is.lastSegment() == _is.segment())
_is.moveToNextInputPos();
if (_is.lastSegment() == _is.segment()) {
NoteEntryMethod entryMethod = _is.noteEntryMethod();
if (entryMethod != NoteEntryMethod::REALTIME_AUTO && entryMethod != NoteEntryMethod::REALTIME_MANUAL)
_is.moveToNextInputPos();
}
return note;
}
expandVoice();
Expand Down Expand Up @@ -1172,8 +1219,11 @@ Note* Score::addPitch(NoteVal& nval, bool addFlag)
if (next)
_is.moveInputPos(next->segment());
}
else
_is.moveToNextInputPos();
else {
NoteEntryMethod entryMethod = _is.noteEntryMethod();
if (entryMethod != NoteEntryMethod::REALTIME_AUTO && entryMethod != NoteEntryMethod::REALTIME_MANUAL)
_is.moveToNextInputPos();
}
return note;
}

Expand Down Expand Up @@ -2725,7 +2775,8 @@ void Score::cmdEnterRest(const TDuration& d)
NoteVal nval;
setNoteRest(_is.segment(), track, nval, d.fraction(), Direction::AUTO);
_is.moveToNextInputPos();
_is.setRest(false); // continue with normal note entry
if (!noteEntryMode() || usingNoteEntryMethod(NoteEntryMethod::STEPTIME))
_is.setRest(false); // continue with normal note entry
endCmd();
}

Expand Down
12 changes: 9 additions & 3 deletions libmscore/score.h
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,8 @@ class Score : public QObject, public ScoreElement {

Note* addPitch(NoteVal&, bool addFlag);
void addPitch(int pitch, bool addFlag, bool insert);
Note* addTiedMidiPitch(int pitch, bool addFlag, Chord* prevChord);
Note* addMidiPitch(int pitch, bool addFlag);
Note* addNote(Chord*, NoteVal& noteVal);

NoteVal noteValForPosition(Position pos, bool &error);
Expand Down Expand Up @@ -1099,6 +1101,7 @@ class Score : public QObject, public ScoreElement {
virtual QVariant propertyDefault(P_ID) const override;

virtual inline QQueue<MidiInputEvent>* midiInputQueue();
virtual inline std::list<MidiInputEvent>* activeMidiPitches();

friend class ChangeSynthesizerState;
friend class Chord;
Expand All @@ -1124,7 +1127,8 @@ class MasterScore : public Score {

// bool _undoRedo; ///< true if in processing a undo/redo
int _midiPortCount { 0 }; // A count of JACK/ALSA midi out ports
QQueue<MidiInputEvent> _midiInputQueue;
QQueue<MidiInputEvent> _midiInputQueue; // MIDI events that have yet to be processed
std::list<MidiInputEvent> _activeMidiPitches; // MIDI keys currently being held down
QList<MidiMapping> _midiMapping;
bool isSimpleMidiMaping; // midi mapping is simple if all ports and channels
// don't decrease and don't have gaps
Expand Down Expand Up @@ -1152,7 +1156,8 @@ class MasterScore : public Score {
virtual RepeatList* repeatList() const override { return _repeatList; }
virtual QList<Excerpt*>& excerpts() override { return _excerpts; }
virtual const QList<Excerpt*>& excerpts() const override { return _excerpts; }
virtual QQueue<MidiInputEvent>* midiInputQueue() override { return &_midiInputQueue; }
virtual QQueue<MidiInputEvent>* midiInputQueue() override { return &_midiInputQueue; }
virtual std::list<MidiInputEvent>* activeMidiPitches() override { return &_activeMidiPitches; }

virtual void setUpdateAll() override { _cmdState.setUpdateMode(UpdateMode::UpdateAll); }
virtual void setLayoutAll() override { _cmdState.setUpdateMode(UpdateMode::LayoutAll); }
Expand Down Expand Up @@ -1210,7 +1215,8 @@ inline TempoMap* Score::tempomap() const { return _masterScore->te
inline TimeSigMap* Score::sigmap() const { return _masterScore->sigmap(); }
inline QList<Excerpt*>& Score::excerpts() { return _masterScore->excerpts(); }
inline const QList<Excerpt*>& Score::excerpts() const { return _masterScore->excerpts(); }
inline QQueue<MidiInputEvent>* Score::midiInputQueue() { return _masterScore->midiInputQueue(); }
inline QQueue<MidiInputEvent>* Score::midiInputQueue() { return _masterScore->midiInputQueue(); }
inline std::list<MidiInputEvent>* Score::activeMidiPitches() { return _masterScore->activeMidiPitches(); }
inline void Score::setUpdateAll() { _masterScore->setUpdateAll(); }
inline void Score::setLayoutAll() { _masterScore->setLayoutAll(); }
inline void Score::setLayout(int tick) { _masterScore->setLayout(tick); }
Expand Down
4 changes: 4 additions & 0 deletions mscore/data/shortcuts.xml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@
<key>rest</key>
<seq>0</seq>
</SC>
<SC>
<key>realtime-advance</key>
<seq>Enter</seq>
</SC>
<SC>
<key>add-staccato</key>
<seq>Shift+S</seq>
Expand Down
5 changes: 4 additions & 1 deletion mscore/musescore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4516,7 +4516,10 @@ void MuseScore::endCmd()
if (samePitch && !cs->selection().elements().empty())
e = cs->selection().elements()[0];

if (e && (cs->playNote() || cs->playChord())) {
NoteEntryMethod entryMethod = cs->noteEntryMethod();
if (e && (cs->playNote() || cs->playChord())
&& entryMethod != NoteEntryMethod::REALTIME_AUTO
&& entryMethod != NoteEntryMethod::REALTIME_MANUAL) {
if (cs->playChord() && preferences.playChordOnAddNote && e->type() == Element::Type::NOTE)
play(static_cast<Note*>(e)->chord());
else
Expand Down
41 changes: 39 additions & 2 deletions mscore/scoreview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3259,6 +3259,14 @@ void ScoreView::cmd(const QAction* a)
cmdCopyLyricsToClipboard();
}

// 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()));
}

// STATE_HARMONY_FIGBASS_EDIT actions

else if (cmd == "advance-longa") {
Expand Down Expand Up @@ -4322,7 +4330,10 @@ void ScoreView::cmdEnterRest(const TDuration& d)
qDebug("cmdEnterRest %s", qPrintable(d.name()));
if (!noteEntryMode())
sm->postEvent(new CommandEvent("note-input"));
_score->cmdEnterRest(d);
if (_score->usingNoteEntryMethod(NoteEntryMethod::RHYTHM))
_score->cmd(getAction("pad-rest"));
else
_score->cmdEnterRest(d);
#if 0
expandVoice();
if (_is.cr() == 0) {
Expand Down Expand Up @@ -5254,6 +5265,33 @@ qDebug("midiNoteReceived %d chord %d", pitch, chord);
cmd(0);
}

//---------------------------------------------------------
// cmdRealtimeAdvance
// move input forwards and extend current chord/rest.
//---------------------------------------------------------

void ScoreView::cmdRealtimeAdvance()
{
InputState& is = _score->inputState();
if (!is.noteEntryMode())
return;
_score->startCmd();
if (is.cr()->duration() != is.duration().fraction())
_score->setNoteRest(is.segment(), is.track(), NoteVal(), is.duration().fraction(), Direction::AUTO);
Chord* prevChord = static_cast<Chord*>(is.cr());
is.moveToNextInputPos();
if (_score->activeMidiPitches()->empty())
_score->setNoteRest(is.segment(), is.track(), NoteVal(), is.duration().fraction(), Direction::AUTO);
else {
bool partOfChord = false;
for (const MidiInputEvent &ev : *_score->activeMidiPitches()) {
_score->addTiedMidiPitch(ev.pitch, partOfChord, prevChord);
partOfChord = true;
}
}
_score->endCmd();
}

//---------------------------------------------------------
// cmdAddPitch
/// insert note or add note to chord
Expand Down Expand Up @@ -6233,4 +6271,3 @@ void ScoreView::updateShadowNotes()
}

}

1 change: 1 addition & 0 deletions mscore/scoreview.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ class ScoreView : public QWidget, public MuseScoreView {

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

public slots:
void setViewRect(const QRectF&);
Expand Down
8 changes: 8 additions & 0 deletions mscore/shortcut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,14 @@ Shortcut Shortcut::_sc[] = {
0,
Icons::quartrest_ICON
},
{
MsWidget::SCORE_TAB,
STATE_NOTE_ENTRY_METHOD_REALTIME_AUTO | STATE_NOTE_ENTRY_METHOD_REALTIME_MANUAL,
"realtime-advance",
QT_TRANSLATE_NOOP("action","Real-time advance"),
QT_TRANSLATE_NOOP("action","Move the cursor forward in real-time input mode"),
0,
},
{
MsWidget::SCORE_TAB,
STATE_NORMAL | STATE_NOTE_ENTRY,
Expand Down

0 comments on commit 8371a25

Please sign in to comment.