Midi note-off messages not sent during editing

• Sep 24, 2019 - 02:01
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


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) calls Seq::startNoteTimer(), which then calls Seq::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 is JackAudio::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 (through jack_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.

In reply to 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);

Fix version
3.5.0