ardour icon indicating copy to clipboard operation
ardour copied to clipboard

no resolve for MidiPort::realtime_locate at loop end

Open jsundqvist opened this issue 9 months ago • 13 comments

fixes midi notes being cut short at loop start due to resolve_notes call after realtime_relocate from loop end.

tested and verified working with hardware on 8.12.

jsundqvist avatar Mar 26 '25 10:03 jsundqvist

@x42 @pauldavisthefirst

jsundqvist avatar May 23 '25 20:05 jsundqvist

Could you say more about this? Your commit means that there are now cases that will not resolve on-notes, which will lead to them being stuck. How does this change avoid that?

pauldavisthefirst avatar May 23 '25 22:05 pauldavisthefirst

or in other words: Why should loop-end not resolve note?

x42 avatar May 23 '25 22:05 x42

It turns out that note resolve at loop end is already handled (correctly) by MidiNoteTracker::resolve_notes using MIDI_CMD_NOTE_OFF (0x80) as opposed to the MIDI_CTL_ALL_NOTES_OFF (0x7B) sent by MidiPort::resolve_notes.

This is what's causing the choking of my TD-27 drum module on the first bar of the loop, and is an incorrect way to resolve notes unless we are also stopping playback, which is probably why the parameter for_loop_end exists in the first place.

I verified using Simple Synth that a note extending outside of the loop is resolved at loop end just as before my change.

jsundqvist avatar May 25 '25 21:05 jsundqvist

Do you have some citation regarding the use of ALL_NOTES_OFF being "incorrect" ?

pauldavisthefirst avatar May 25 '25 21:05 pauldavisthefirst

Gemini:

What are some common side effects of the MIDI Mode Message Control Change #123 "All Notes Off"?

The MIDI Mode Message Control Change #⁠123, "All Notes Off," is designed to immediately stop all sounding notes on a particular MIDI channel. While useful for preventing "stuck notes," it can sometimes lead to unexpected behavior or side effects, especially with older or less robust MIDI implementations.

Here are some common side effects and issues associated with MIDI CC #⁠123:

  • Cutting off Sustain/Release: If notes are being sustained via a sustain pedal (MIDI CC #⁠64) or have long release times, an "All Notes Off" message can abruptly cut off these sounds, leading to an unnatural or truncated sound. Some devices may not release notes until the hold pedal is turned off, even if CC #⁠123 is received.

  • Interference with intended note durations: In some cases, a device might send CC #⁠123 automatically when the last key is released, which can be problematic if you intend for notes to continue sounding or if you're using a sequencer that already handles note-offs. This can cause notes to be unexpectedly cut short.

  • Inconsistent Implementation Across Devices: Not all MIDI devices implement CC #⁠123 consistently or even at all. The MIDI specification states that recognition of "All Notes Off" is not required. This means some instruments or software might ignore the message, leading to stuck notes persisting, while others might respond in a way that's not ideal for the musical context.

  • Affecting unintended channels: If a device is in "Omni On" state, it might ignore the channel-specific nature of the "All Notes Off" message and apply it across all channels, potentially cutting off notes on other tracks that shouldn't be affected.

  • Conflicts with local keyboard playback: If a device cannot distinguish between notes played via MIDI input and notes played on its local keyboard, it might prematurely turn off notes the musician is actively playing.

  • Difficulty in filtering/mapping: Some DAWs or MIDI software might treat CC #⁠123 as a reserved message, making it difficult to filter out or remap for other purposes. Users have reported issues with DAWs not allowing CC #⁠123 to be mapped to controls.

  • Unnecessary transmission: While useful for panic situations (stuck notes), sending "All Notes Off" periodically as part of normal operation is generally discouraged by the MIDI specification. It should primarily be used when the entire MIDI system is "at rest" (e.g., when a sequence has stopped).

Workarounds and Solutions:

  • MIDI Filtering: Many users resort to MIDI filtering tools (software or hardware) to block CC #⁠123 messages from problematic controllers or to allow only specific "All Notes Off" messages to pass through.

  • Using individual Note Off messages: The most robust way to turn off notes is to send individual MIDI Note Off messages (8n kk vv) for each active note. While this generates more MIDI data, it's a more reliable method as it's universally understood by MIDI devices.

  • "All Sound Off" (CC #⁠120): In some cases, the "All Sound Off" message (CC #⁠120) might be a more appropriate or effective solution if the goal is to silence all sound rather than just notes that were explicitly turned on.

  • Checking device manuals and settings: Always consult the manual for your MIDI devices to understand how they handle CC #⁠123 and if there are any settings to modify its behavior.

  • DAW MIDI settings: Many DAWs have settings related to MIDI reset messages that might allow you to control how CC #⁠123 is sent or received.

In summary, while "All Notes Off" (CC #⁠123) serves a critical purpose in preventing stuck notes, its common side effects stem from inconsistent implementation and its broad nature, which can sometimes lead to unintended silencing of sounds.

jsundqvist avatar May 25 '25 22:05 jsundqvist

While some of the points made are fair, I do not accept citations from LLMs as evidence.

We could potentially add an option to never send All Notes Off (and also All Sound Off).

pauldavisthefirst avatar May 25 '25 22:05 pauldavisthefirst

I can't believe I have to wrestle you to get things fixed. Anyway, here's some more LLM jibberish for you:


The notion of "unnecessary transmission" for MIDI CC #⁠123 (All Notes Off) stems from the original intent and limitations of the MIDI specification, as well as practical issues observed by musicians and developers over time. Here's a breakdown of the sources and reasoning behind this bullet point:

MIDI Specification and Intended Use:

  • The MIDI 1.0 specification, while defining CC #⁠123, implies it's a "mode message" meant for channel-wide operational changes rather than continuous control.

  • Many MIDI sources (like midi.teragonaudio.com/tech/midispec/ntnoff.htm) explicitly state that "All Notes Off" is for notes turned on by received Note On messages and that it's not supposed to turn off notes played on the local keyboard if a device can't distinguish. This highlights its role as a "panic" or reset function for MIDI input rather than a general-purpose note termination.

  • The MIDI specification itself, in its "Channel Mode Messages" section, groups CC #⁠123 with other reset/mode-altering messages like "All Sound Off" (CC #⁠120) and "Reset All Controllers" (CC #⁠121). This categorization suggests they are intended for exceptional situations or system resets, not continuous musical performance.

Historical Implementation and "Stuck Notes":

  • In the early days of MIDI, "stuck notes" were a common problem due to dropped Note Off messages, MIDI buffer overflows, or unstable hardware. CC #⁠123 was a crucial "panic button" to resolve this.

  • However, some older keyboards (like the Roland D-50, as discussed in various forum threads on Cantabile Community and Reddit) were designed to send "All Notes Off" automatically when the last key was released. This was a manufacturer-specific behavior, not a general MIDI requirement, and it caused the very side effects mentioned (cutting off releases, interfering with DAWs). This automatic, constant sending is what's considered "unnecessary transmission" in a modern context.

Modern DAW/Sequencer Behavior and Best Practices:

  • Modern DAWs and sequencers typically manage Note On/Off messages individually for precise control. They generally don't send CC #⁠123 constantly unless explicitly instructed to do so or as a "stop" or "panic" function.

  • As noted in discussions on forums like Ableton and Squarp, DAWs often send CC #⁠120 (All Sound Off) or CC #⁠123 (All Notes Off) when transport is stopped. This is considered acceptable as a reset, but continuous or frequent sending during playback is generally avoided to prevent unintended musical artifacts.

  • The Ableton Forum thread "Live 12 - Midi CC 123 not usable" and the Squarp Forum thread "Sending CC higher than 119?" highlight that many DAWs and MIDI controllers treat CCs 120-127 as "reserved for Channel Mode Messages" and often prevent users from mapping them to arbitrary controls. This implies they are not meant for general, continuous data streaming. The squarp forum specifically mentions that "it [CC123] is not really designed to be automated".

  • A MuseScore forum discussion ("All notes off overkill?") directly criticizes the practice of sending "All Notes Off" (and even all 128 Note Off messages) on every channel at the start and end of playback, calling it "overkill" and suggesting it can cause "feedback loop circuit breaker" issues in MIDI drivers due to the sudden flood of messages. This reinforces the idea that excessive transmission, even for a "reset" message, can be problematic.

In essence, "unnecessary transmission" refers to situations where CC #⁠123 is sent more frequently or in contexts beyond its original "panic" or "reset" intent, often by older hardware designs or by software implementations that don't fully adhere to best practices for modern MIDI sequencing. The ideal is to only send it when genuinely needed to clear stuck notes, rather than as a routine part of a MIDI stream.

jsundqvist avatar May 25 '25 23:05 jsundqvist

Or, let's put it like this: I have a hardware device from a well-known (albeit not perfect) manufacturer that is behaving in an unwanted manner because of Ardour.

What "evidence" do you have that the current implementation is correct, and is not causing unwanted side-effects?

jsundqvist avatar May 25 '25 23:05 jsundqvist

Here is the actual MIDI Manufacturer's Association specification for MIDI 1.0

"Messages 123 through 127 also function as All Notes Off messages. They will turn off all voices controlled by the assigned Basic Channel. These messages should not be sent periodically but only for a specific purpose. In no case shall they be used in lieu of Note Off commands to turn off notes which have previously been turned on. Any All Note Off command (123-127) may be ignored by a receiver with no possibility of notes staying on, since any Note On command must have a corresponding specific Note Off command".

MIDI 1.0 Detailed Specification 4.2, page 20

So, Ardour's MidiStateTracker takes care of sending the Note Off commands required, but Ardour additionally sends an All Notes Off command as a "belt and braces" approach. One can certainly make the argument that this is redundant, but as usual the MMA spec was not tightly worded enough to get completely consistent behavior on the part of either senders or receivers. Your TD-27 module is also in the gray zone, failing to ignore the All Notes Off command effectively, even though a Note Off is sent for every Note On.

There are hardware modules that fail to turn all notes off based on individual Note Off commands under a variety of circumstances (e.g. following MIDI Clock from the sender, and receiving Note Off commands after an apparent locate, or before the apparent locate, or both; or because there are too many Note Off command with the same timestamp (clearly, not in compliance with the spec, but they exist). Hence Ardour's "belt and braces" approach: send all the required individual Note Off commands and an All Notes Off command to make sure that what we intend to happen definitely happens.

I favor making the All Notes Off command a preference item, though a complete implementation would do this on a per-track or even per-port basis, depending on what is connected to the port and/or what plugin(s) are in the track.

pauldavisthefirst avatar May 25 '25 23:05 pauldavisthefirst

Also, re: "I can't believe I have to wrestle you to get things fixed."

I don't know why you'd be certain that this is so cut-and-dried obvious that no discussion is necessary.

I'd like to see this issue (i..e "certain synth modules misbehave when Ardour sends an All Notes Off command at loop end") get fixed, but experience over 25 years suggests that such issues frequently have more dimensions to them that is immediately apparent to the eye, and I would prefer not to break other "certain synth modules" in an attempt to fix it.

pauldavisthefirst avatar May 25 '25 23:05 pauldavisthefirst

I favor making the All Notes Off command a preference item, though a complete implementation would do this on a per-track or even per-port basis, depending on what is connected to the port and/or what plugin(s) are in the track.

I was going to suggest that (global pref, which can be overridden at track level), or initially just a per-track option, available from the track header (we already have related MIDI Patch Restore option there, to help with some synths).

What I don't understand is how this change is made on a per-port level, and not in the signal flow itself. The proposed change would only work around the issue with a specific hardware synth connected a MIDI track's MIDI output.

Surely it would be better handled at the Source, here MidiRegion::_read_at when looping (loop range is passed to that function).

x42 avatar May 26 '25 14:05 x42

@x42 the parameter was introduced by @pauldavisthefirst in Dec, 2019 (https://github.com/Ardour/ardour/commit/febaa1ff2d1f5150d30902f7ccc8b5dfcfb3c913) but only used in DiskReader:

fix unconditional note resolution during DiskReader::realtime_locate()

When looping, we do not want to resolve notes at the end of the loop via ::realtime_locate() -
::get_midi_playback() has already taken care of this. But when not looping, we need this. So,
add an argument to tell all interested parties whether the locate is for a loop end or not

In Jan, 2020 Plugin was changed to take this parameter into account, also by @pauldavisthefirst (https://github.com/Ardour/ardour/commit/5948d140124e1c6a644708b52c76edb39f60b01d):

plugins should NOT resolve MIDI notes at loopend

Their data will come from (1) disk, in which case the DiskReader will do the resolve (2) live input
in which case the player/user will do the resolve

I think it's great, but I don't know any better. Why would you rather handle it in MidiRegion::_read_at?

jsundqvist avatar May 27 '25 08:05 jsundqvist