Midi note-off messages not sent during editing
Reported version
3.2
Type
Functional
Frequency
Few
Severity
S4 - Minor
Reproducibility
Always
Status
closed
Regression
No
Workaround
No
Project
Steps to reproduce the behavior:
1) Play notes when editing should be set to "ON"
2) Midi output should be sending midi to any external synth engine of your choice (software or hardware will both have the same result)
3) Add notes to a staff
Resulting behavior:
When adding notes to the staff or editing existing notes, the notes will send note-on messages but do note send any note-off messages, resulting in any connected synth holding the notes forever as a note cluster
Expected behavior:
A midi note-off message should be sent after a period of time as specified in the setting "Default duration" (which is typically set to 300ms)
Please see an earlier post from someone else about this same issue:
https://musescore.org/en/node/288601
Fix version
3.5.0
Comments
I've been doing some digging on this issue, and this might be an issue with JACK, not with MuseScore.
When playing a single note, MuseScore is set to stop all notes from playing after a short period of time.
Seq::startNote(5 arg overload)
callsSeq::startNoteTimer()
, which then callsSeq::stopNotes()
.https://github.com/musescore/MuseScore/blob/3.4/mscore/seq.cpp#L1229
This then queues the
putEvent()
method of the currently running driver, which for JACK isJackAudio::putEvent()
.https://github.com/musescore/MuseScore/blob/3.4/mscore/jackaudio.cpp#L5…
The MIDI specification for stopping all notes is
bn
(n
being the midi channel)7B
,00
.https://www.midi.org/specifications-old/item/table-1-summary-of-midi-me…
The problem is, MuseScore does exactly what it's supposed to do,
JackAudio::putEvent()
gets called and sends the right events (throughjack_midi_event_reserve()
), yet for some reason, this specific message gets lost, but only sometimes. I've gotten the message to send correctly in Visual Studio, while debugging with breakpoints. Maybe an issue with QTimer?edit: Another problem is that
Seq::stopNotes()
should send stop to all channels when -1 is passed, yet it only stops channel 0 (1).edit2: Found the problem. It's not MuseScore, or JACK, it's that All Notes Off is not a well supported MIDI message, at least not with the applications I tried.
I was doing some reading, and it seems modern approach for some devices/applications is to turn off all MIDI notes in a channel iteratively, so I replaced the event in
stopNotes()
with a for-loop that turns off all 128 notes in a channel, and it works... it's not ideal, but at least it's functioning now. The only thing left to do is fix the -1 channels issue, when that's done I'll submit a PR.On a related note, I did some experimenting pairing PortMidi output with a virtual MIDI device, created by loopMIDI, and this seems like a much better alternative to JACK for Windows systems. It's easier to setup, and will probably work better with most applications.
https://github.com/musescore/MuseScore/pull/5645
Fixed in branch master, commit f58a3725c1
_ fix #294834, fix #294836: Midi note-off messages not sent during editing or when stopping playback_
Fixed in branch master, commit 93cf014e1d
_Merge pull request #5645 from ChrootDoot/294834-midi-sustain-fix
fix #294834 and #294836: Midi note-off messages not sent during editing, or when stopping playback_
Automatically closed -- issue fixed for 2 weeks with no activity.
In reply to Auto close by System Message
Should this be opened again? I came across this in researching the problem.
Looping over every single note in all MIDI ports/MIDI channels clobbers everything MIDI (16 MIDI ports16 midi channels128 notes). Internally, this might be handled by musescore OK, but externally I think this is too much.
Maybe it would work if the connection is software-only, and if synchronization isn't an issue, although I think this clobbers most MIDI buffers.
I tried compiling w/o looping over all 128 possible notes and it seems a lot better.
In troubleshooting MIDI messages, I'm not sure the normal ALL_NOTE_OFF is actually working:
I don't see any MIDI messages in jack midi monitor, although I think this is being called:
case SeqMsgId::ALL_NOTE_OFF:
_synti->allNotesOff(msg.intVal);
From here:
if (cachedPrefs.useAlsaAudio || cachedPrefs.useJackAudio || cachedPrefs.usePulseAudio || cachedPrefs.usePortAudio) {
guiToSeq(SeqMsg(SeqMsgId::ALL_NOTE_OFF, channel));
}
The simplest way with the least impact for musescore internals I think is just removing the line that I have commented below:
if (channel == -1) {
for(unsigned ch = 0; ch < cs->midiMapping().size(); ch++) {
send(NPlayEvent(ME_CONTROLLER, ch, CTRL_SUSTAIN, 0));
//turnAllNotesOff(ch);
If anything better open a new issue, any let this one here RIP