Midi note hangs forever when switching to new track while current track is recieving midi input

I have run into an issue with the engine (I am on linux kubuntu 20.04), not sure if this issue occurs on other platforms). I am trying to switch input monitoring/arm recording to a new track, while the currently armed/input monitored track is receiving midi input. (Recording is not in process)

For the most part what I have works (I based it off the examples, but maybe the way I am doing it is incorrect). The problem arises when I hold down a midi key and play a note, while simultaneously selecting a new track to arm/enable input monitoring for. The note I am playing plays infinitely (until you switch back to that track and press that same note again). This problem happens in Waveform Free on linux as well. If you play a note then click on a new track with an instrument on it the note hangs forever. I am using the 4OSC instrument This makes me think it might be an issue with the engine/4osc. Seems to be since the midi note off event isnt sent it just keeps on going until you switch back and press and release the same note so it gets the note off command. Is there a good way to deal with this?

When my app starts up I enable the midi inputs and arm/setTarget for the currently selected track (Defaults to the first track):

// set initial midi devices
auto& deviceManager = edit.engine.getDeviceManager();
for (int i = 0; i < deviceManager.getNumMidiInDevices(); i++)
{
    if (auto midiInputDevice = deviceManager.getMidiInDevice(i))
    {
        midiInputDevice->setEndToEndEnabled (true);
        midiInputDevice->setEnabled(true);
        DBG("enabled midi device: " + midiInputDevice->getName());
    }
}


edit.getTransport().ensureContextAllocated();

for (auto instance : edit.getAllInputDevices())
{

    if (instance->getInputDevice().getDeviceType() == tracktion_engine::InputDevice::physicalMidiDevice)
    {

            instance->setTargetTrack(*getSelectedTrack(), 0, true);
            instance->setRecordingEnabled(*getSelectedTrack(), true);
    }

}

edit.restartPlayback();

When a user selects a different track I clear the input from the tracks and then set a new target track and enable recording for it. In order to get the switch to the new track input to occur I have to restart playback. Is that the correct way?:

for (auto instance : edit.getAllInputDevices())
{

    if (instance->getInputDevice().getDeviceType() == tracktion_engine::InputDevice::physicalMidiDevice)
    {
        instance->clearFromTracks();
        instance->setTargetTrack(*getSelectedTrack(), 0, true);
        instance->setRecordingEnabled(*getSelectedTrack(), true);
    }

    edit.restartPlayback();
}

Perhaps you can just test if a track has a note-on triggered when it is no longer the focused track and send a note-off to it? Is there a callback you can use to tell you when a track is no longer selected?

It seems logical to me that a note would stick forever if you never send a note-off message to the track playing the sound. And if you send that note off to a different track (because you switched tracks), that also seems like a logical reason for the note never turning off.

yes I mean it makes sense why this is happening. I have tried stopping and clearing the input from the tracks:

instance->stop();
instance->clearFromTracks();
instance->setTargetTrack(*getSelectedTrack(), 0, true);
instance->setRecordingEnabled(*getSelectedTrack(), true);

I have also tried removing the currently selected track as the target track for the input before the switch happens

instance->removeTargetTrack(*getSelectedTrack());

but the problem persists. Is this a regular occurrence in DAWs? The fact it occurred in waveform surprised me. It seems like the currently playing note is not a property of the track since its not been recorded or anything or at least I cant find anythin that would give access to it. It also seems like its not part of the inputDevice since clearing it and stopping the instance doesnt seem to stop it. I dont really know enough about the engine to know where to go to stop that note.

I tried injecting a live all notes off midi CC message to the current track before switching as well:

 juce::MidiMessage message = juce::MidiMessage::controllerEvent(1, 123, 127);
getSelectedTrack()->injectLiveMidiMessage(message, tracktion_engine::MidiMessageArray::notMPE);

The sent notes can’t be a property of a the track as there are lots of places a note could be injected in to the mix bus. Off the top of my head:
• From a physical MIDI input
• From the on-screen keyboard of a physical MIDI input
• From one of several physical MIDI inputs on a track
• From the piano roll when dragging a note
• From the piano roll when pressing a key in the UI
• From clicking a note in the piano roll
• From clicking a note in the step clip keyboard
• From clicking the “Test note” button in the step clip properties
• And so on…

Once messages have been injected in to the mix bus, you don’t really have any idea of what plugins do with then. Some may eat them, some may pass them on, some may generate new messages based on incoming messages.

The only real way to do this is to have a special flag in the processing pipeline (MidiMessageArray::isAllNotesOff) which gets set automatically if the playhead is stopped/started or jumps. This is then used in ExternalPlugin to add note-offs for all the note-ons that were started.

In Waveform, it’s rare that someone will switch inputs between tracks whilst holding a note. If they do, a quick stop/start will reset them.


You injection of a MIDI all-notes-off message might work, providing the plugin responds to it.
Also be sure to send it on all channels and you might also need to send hold and sustain pedal off messages or plugins might still be playing notes. This also assumes that the plugin list is simple and you don’t need to propagate these messages to multiple plugins.

Thanks for the reply dave. That makes sense. I think I will go the waveform route and just allow a stop/start if needed. How does one reset the engine in order to stop all sound like the panic button in waveform does?