Adding / removing MidiNotes from a playing MidiSequence in a MidiClip without restarting playback

I’m working on a sequencer using Tracktion Engine, and I have the code below which creates a MIDI output and loops over 1 bar allowing the user to toggle notes on and off using an external controller. It all works, but when I modify the sequence, either adding notes, removing notes, clearing the sequence, etc. the playback starts from the beginning, sometimes after a small delay. How can I get it to continue playing and not restart when the MidiSequence is modified? I tried creating a separate sequence and clearing the current one then copying the sequence using addFrom() but that didn’t work.

        void createMidiOut()
        {
            track = tracktion_engine::getAudioTracks(*edit)[0];
            if (track)
            {
                track->getOutput().setOutputByName("IAC Driver Bus 2");
                const te::EditTimeRange editTimeRange(0, edit->tempoSequence.barsBeatsToTime({ 1, 0.0 }));
                track->insertMIDIClip("MIDI Clip", editTimeRange, nullptr);

                if (auto clip = dynamic_cast<te::MidiClip*> (track->getClips()[0]))
                {
                    midiClip = *clip;
                }
            }
        }

        void loop()
        {
            auto& transport = edit->getTransport();
            transport.setLoopRange({ 0.0, edit->getLength() });
            transport.looping = true;
            transport.play(false);
        }

        void toggleNote(int col, int row)
        {
            double beats = edit->tempoSequence.timeToBeats(col * .125);
            Note note = {
                    75 + output->getCols() - row,
                    beats,
                    .25,
                    127
            };

            auto& transport = edit->getTransport();
            double pos = transport.getCurrentPosition();

            auto& seq = midiClip->getSequence();
            if (notes.find(note) == notes.end())
            {
                auto midiNote = seq.addNote(note.pitch, note.beats, note.length, note.velocity, 0, nullptr);
                notes[note] = midiNote;
            }
            else
            {
                seq.removeNote(*notes[note], nullptr);
                notes.erase(note);
            }

            transport.setCurrentPosition(pos);
        }

I guess you want prevent this behavior by adding this line?

transport.setCurrentPosition(pos);

But I can not see, why your loop is starting at the beginning when you add a note. I can add/remove notes to a sequence but the transport keeps playing. There must be a reason why your transport starts again.

Yeah I’m not sure why it starts at the beginning but there will be some delay between reading a position from the message thread and then setting it again (i.e. the audio thread may have advanced in time during this slight gap). So setting the transport position may cause a slight jump.

As @baramgb says, just don’t call transport.setCurrentPosition when modifying the sequence and it should all work.

I forgot to mention that without the

transport.setCurrentPosition(pos);

this still happens. That was an attempt to try to prevent the loop restarting. @baramgb So the sequence should be modifiable while the loop plays without restarting. That is helpful to know. Any ideas on what else would cause the sequence to restart? Interestingly enough, the call to setCurrentPosition() doesn’t seem to do anything at all that I can tell as it doesn’t restore either the previous position or any positions close to it and the loop restarts with or without it. @dave96

Maybe put a breakpoint in TransportControl::performPositionChange to see what’s causing the change?

Actually, I’m wrong about what’s happening. It’s not repeating. It’s sending an all notes off and a reset all controllers message out, then resumes sending midi after a delay. TransportControl::performPositionChange is never called. @dave96

Are these getting inserted because a isAllNotesOff message is getting set on the MidiMessageArray?
I.e. line 1190 of ExternalPlugin::prepareIncomingMidiMessages?

That usually happens when the playhead jumps i.e. line 65 or 71 in PlayHeadState::update.
Are you sure PlayHead::setPosition isn’t getting called somewhere from your code?

No, prepareIncomingMidiMessages() is not being called and neither is setPosition(). The only time it is and it jumps is when the play/stop on the transport is pressed. Not sure exactly what is happening that’s causing this but when I add/remove notes, none of the above breakpoints are being hit. Maybe it’s some sort of blocking or threading issue or some configuration that is wrong? @dave96

Also, the all notes off / reset all controllers doesn’t always happen. If i turn a note on / off by repeatedly alternately calling addNote() and removeNote() when I press a button, then it basically gets delayed indefinitely and nothing plays. Same thing if I call just addNote() repeatedly. But usually only the first time I add a note will the all notes off / reset controllers messages be sent. It also seems to be sent after the call to addNote() / removeNote(). I’m continuing to try different things and ways of triggering these notes to see if I can find a way to avoid this.

Sorry, I’m not quite sure I follow what’s happening here.
If you do a similar thing in Waveform, i.e. add/remove a note from the MIDI editor, you’ll see playback just continues so I’m sure you must be doing something else that’s causing the playhead to jump around?