From 8b09becf1de50015a58dec86f5e98d22e2d68e93 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 19 Jun 2025 07:58:04 -0600 Subject: [PATCH] fix the way mute operates for MIDI tracks our policy is that "mute works on outputs", which means that mute should have no effect on what an instrument plugin does. However, MidiTrack::act_on_mute() used to inject sustain=0 messages into the data flow, which would affect the instrument plugin(s) in the track. now, MidiTrack::act_on_mute() simply notifies all Delivery objects in the track that a MIDI mute is needed via a channel mask. The Delivery objects notice this during their ::run() method, and deliver the required MIDI events to their output ports. There is still a potential issue that Amp objects which notice they have been muted also send a similar set of messages. This needs more investigation and possibly other changes. But this commit allows a sustained note to return after the track is muted midway through it. --- libs/ardour/ardour/delivery.h | 6 ++++ libs/ardour/delivery.cc | 52 +++++++++++++++++++++++++++++++++++ libs/ardour/internal_send.cc | 2 ++ libs/ardour/midi_track.cc | 24 ++++++---------- 4 files changed, 69 insertions(+), 15 deletions(-) diff --git a/libs/ardour/ardour/delivery.h b/libs/ardour/ardour/delivery.h index 1b6314fa6f..a684c112c9 100644 --- a/libs/ardour/ardour/delivery.h +++ b/libs/ardour/ardour/delivery.h @@ -30,6 +30,7 @@ #include "ardour/types.h" #include "ardour/chan_count.h" #include "ardour/io_processor.h" +#include "ardour/midi_buffer.h" #include "ardour/gain_control.h" namespace ARDOUR { @@ -89,6 +90,8 @@ public: void run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, double speed, pframes_t nframes, bool); + void set_midi_mute_mask (int); + /* supplemental method used with MIDI */ void flush_buffers (samplecnt_t nframes); @@ -157,6 +160,7 @@ protected: std::shared_ptr _amp; gain_t target_gain (); + void maybe_merge_midi_mute (BufferSet&); private: bool _no_outs_cuz_we_no_monitor; @@ -176,6 +180,8 @@ private: void output_changed (IOChange, void*); bool _no_panner_reset; + std::atomic _midi_mute_mask; + MidiBuffer _midi_mute_buffer; }; diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc index 4c84e960c8..76f5de50d0 100644 --- a/libs/ardour/delivery.cc +++ b/libs/ardour/delivery.cc @@ -65,6 +65,8 @@ Delivery::Delivery (Session& s, std::shared_ptr io, std::shared_ptr io, std::shared_ptrchanged.connect_same_thread (*this, std::bind (&Delivery::output_changed, this, _1, _2)); } @@ -90,6 +97,8 @@ Delivery::Delivery (Session& s, std::shared_ptr pannable, std::shared_ , _mute_master (mm) , _rta_active (false) , _no_panner_reset (false) + , _midi_mute_mask (0) + , _midi_mute_buffer (0) { if (pannable) { bool is_send = false; @@ -98,6 +107,10 @@ Delivery::Delivery (Session& s, std::shared_ptr pannable, std::shared_ } _display_to_user = false; + const size_t stamp_size = sizeof(samplepos_t); + const size_t etype_size = sizeof(Evoral::EventType); + const size_t mmb_size = 16 * (stamp_size + etype_size + 3); + _midi_mute_buffer.resize (mmb_size); if (_output) { _output->changed.connect_same_thread (*this, std::bind (&Delivery::output_changed, this, _1, _2)); @@ -267,6 +280,38 @@ Delivery::configure_io (ChanCount in, ChanCount out) return true; } +void +Delivery::maybe_merge_midi_mute (BufferSet& bufs) +{ + if (bufs.available().n_midi()) { + + int mask = _midi_mute_mask.load(); /* atomic */ + MidiBuffer& pmbuf (bufs.get_midi (0)); + + if (mask && (_current_gain < GAIN_COEFF_SMALL)) { + + /* mask set, and we have just been muted */ + + _midi_mute_buffer.clear (); + + for (uint8_t channel = 0; channel <= 0xF; channel++) { + + if ((1< ev (Evoral::MIDI_EVENT, 0, 3, buf); + _midi_mute_buffer.push_back (ev); + + /* Note we do not send MIDI_CTL_ALL_NOTES_OFF here, since this may + silence notes that came from another non-muted track. */ + } + } + pmbuf.merge_from (_midi_mute_buffer, 0, 0, 0); /* last 3 args do not matter for MIDI */ + _midi_mute_mask = 0; + } + } +} + void Delivery::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, double speed, pframes_t nframes, bool result_required) { @@ -343,6 +388,8 @@ Delivery::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample _amp->run (bufs, start_sample, end_sample, speed, nframes, true); } + maybe_merge_midi_mute (bufs); + RTABufferListPtr rtabuffers = _rtabuffers; if (_rta_active.load () && rtabuffers && !rtabuffers->empty ()) { uint32_t n_audio = bufs.count().n_audio(); @@ -721,3 +768,8 @@ Delivery::panner () const } } +void +Delivery::set_midi_mute_mask (int mask) +{ + _midi_mute_mask = mask; /* atomic */ +} diff --git a/libs/ardour/internal_send.cc b/libs/ardour/internal_send.cc index a8f0880049..2d5cc42d4b 100644 --- a/libs/ardour/internal_send.cc +++ b/libs/ardour/internal_send.cc @@ -333,6 +333,8 @@ InternalSend::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sa Amp::apply_simple_gain (mixbufs, nframes, tgain); } + maybe_merge_midi_mute (mixbufs); + /* apply fader gain automation */ _amp->set_gain_automation_buffer (_session.send_gain_automation_buffer ()); _amp->setup_gain_automation (start_sample + latency, end_sample + latency, nframes); diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index eb12205048..4a793c33d0 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -915,24 +915,18 @@ MidiTrack::act_on_mute () if (muted() || _mute_master->muted_by_others_soloing_at (MuteMaster::AllPoints)) { /* only send messages for channels we are using */ - - uint16_t mask = _playback_filter.get_channel_mask(); - - for (uint8_t channel = 0; channel <= 0xF; channel++) { - - if ((1< p) { + std::shared_ptr delivery = std::dynamic_pointer_cast (p.lock()); + if (delivery) { + delivery->set_midi_mute_mask (_playback_filter.get_channel_mask()); } - } + }); /* Resolve active notes. */ - _disk_reader->resolve_tracker (_immediate_events, 0); + + if (!the_instrument()) { + _disk_reader->resolve_tracker (_immediate_events, 0); + } } }