I’m making a synthesiser and was working on the assumption that MidiNoteOn messages would always be paired with a MidiNoteOff message since the note on message doesn’t say how long the note lasts for. It was my expectation that stopping the transport would generate a MidiNoteOff message for all my plugins, but that doesn’t seem to be the case.
Should stopping the transport do this? If not, what’s the typical strategy for stopping playback on a synth/instrument/sampler/whatever when you can’t rely on there being a MidiNoteOff message?
When the transport stops, you can check MidiMessageArray::isAllNotesOff in the PluginRenderContext to see if you should stop all your notes. Does that help?
That does help, I’m not seeing the message so now I know where to start problem solving. Thanks!
Is this a feature of the transport that should just be baked into Tracktion Engine, or is there something I should be doing on my end to ensure that this massage is being sent?
I can see that the jumped flag is being set to true, doesn’t result in getting an AllNotesOff message in my PluginRenderContext though. I guess the next question is how is “How is the playheadJumped flag used”. I’ll investigate the flag and the building of the PluginRenderContext and will follow up if I turn up anything interesting.
Are you sure? Have a look in PluginNode and see if the isAllNotesOff = true; is getting set.
That should call Plugin::applyToBufferWithAutomation directly afterwards with that MidiMessageArray::isAllNotesOff set to true.
Though it looks like the isAllNotesOff flag is being set within PluginNode::process(), by the time I arrive in Plugin::applyToBufferWithAutoMation() there are no midi messages at all in my PluginRenderContext ala:
void Plugin::applyToBufferWithAutomation (const PluginRenderContext& pc)
{
SCOPED_REALTIME_CHECK
std::vector<std::string> midiMessages;
for (auto element : *pc.bufferForMidiMessages)
{
midiMessages.push_back(element.getDescription().toStdString());
if (element.isAllNotesOff())
int breakpoint = 8888;
}
Oh my, I understand. The answer to my question is “No”.
What happens is when you stop the transport a data member called isAllNotesOff is set to true. There is not a “AllNotesOff” midi message, so if you call isAllNotesOff() you’ll get false.
While not incorrect, this is incredibly confusing.
Yes, sorry I thought MidiMessageArray::isAllNotesOff was clear
This used to be a special MIDI message but it didn’t really work correctly as latency gets applied through the graph and note events could get inserted before/after it etc.
It should probably be called something like MidiMessageArray::shouldResetFlag but it is closer to what it replaced and naming is hard… Sorry for any confusion.
No you were perfectly clear, it’s just that in my position as a learner it took me a few passes to catch the distinction.
The context of a midi message not being possible here is the detail that clears up the confusion for someone like myself coming in with the assumption that all communication will come through midi. There’s the overlapping names, there’s the fact that the flag is on the MidiMessageArray so you’re still looking for the flag in the context of looking for midi messages generally, and there’s the hidden context that answers “Why?”.
If a rename isn’t in the cards can we at least get a comment providing some context? Something like:
// The isAllNotesOff flag is set when sending a AllNotesOff midi message
// might not be practical for issues of latency.
bool isAllNotesOff = false;
/** Checks whether this message is an all-notes-off message.
You may want to check MidiMessageArray::isAllNotesOff in scenarios
where a all-notes-off message is not present due to latency issues.
@see allNotesOff
*/
bool isAllNotesOff() const noexcept;