From d7895ab1d2d143eb506a57161e237e210a478564 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Sat, 19 Nov 2022 10:12:14 -0700 Subject: [PATCH] triggers: remove sidechain and use global port for trigger control --- libs/ardour/ardour/triggerbox.h | 24 ++- libs/ardour/route.cc | 4 - libs/ardour/session_state.cc | 3 + libs/ardour/track.cc | 1 - libs/ardour/triggerbox.cc | 293 +++++++------------------------- 5 files changed, 75 insertions(+), 250 deletions(-) diff --git a/libs/ardour/ardour/triggerbox.h b/libs/ardour/ardour/triggerbox.h index c6d49c0371..052c8cbb0e 100644 --- a/libs/ardour/ardour/triggerbox.h +++ b/libs/ardour/ardour/triggerbox.h @@ -71,6 +71,7 @@ class AudioRegion; class MidiRegion; class TriggerBox; class SideChain; +class MidiPort; typedef uint32_t color_t; @@ -786,9 +787,6 @@ class LIBARDOUR_API TriggerBox : public Processor TriggerPtr get_next_trigger (); TriggerPtr peek_next_trigger (); - void add_midi_sidechain (); - void update_sidechain_name (); - void request_reload (int32_t slot, void*); void set_region (uint32_t slot, boost::shared_ptr region); @@ -837,6 +835,7 @@ class LIBARDOUR_API TriggerBox : public Processor static void set_first_midi_note (int n); static void init (); + static void static_init (Session&); static void begin_process_cycle (); static TriggerBoxThread* worker; @@ -874,8 +873,6 @@ class LIBARDOUR_API TriggerBox : public Processor bool _cancel_locate_armed; bool _fast_forwarding; - boost::shared_ptr _sidechain; - PBD::PCGRand _pcg; /* These four are accessed (read/write) only from process() context */ @@ -888,13 +885,8 @@ class LIBARDOUR_API TriggerBox : public Processor void maybe_swap_pending (uint32_t); - int note_to_trigger (int node, int channel); - - void note_on (int note_number, int velocity); - void note_off (int note_number, int velocity); - - void reconnect_to_default (); void parameter_changed (std::string const &); + static void static_parameter_changed (std::string const &); static int _first_midi_note; static TriggerMidiMapMode _midi_map_mode; @@ -941,11 +933,15 @@ class LIBARDOUR_API TriggerBox : public Processor typedef std::map,std::pair > CustomMidiMap; static CustomMidiMap _custom_midi_map; - static void midi_learn_input_handler (MIDI::Parser&, MIDI::byte*, size_t, samplecnt_t); - static PBD::ScopedConnectionList midi_learn_connections; + static void midi_input_handler (MIDI::Parser&, MIDI::byte*, size_t, samplecnt_t); + static MIDI::Parser* input_parser; + static PBD::ScopedConnection midi_input_connection; + static void input_port_check (); + static PBD::ScopedConnectionList static_connections; + static boost::shared_ptr current_input; + static bool _learning; static std::pair learning_for; - static MIDI::Parser* learning_parser; static PBD::Signal0 TriggerMIDILearned; static void init_pool(); diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 399144d364..79fe4865dc 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -4630,10 +4630,6 @@ Route::set_name (const string& str) pi->update_sidechain_name (); } - if (_triggerbox) { - _triggerbox->update_sidechain_name (); - } - bool ret = (_input->set_name(newname) && _output->set_name(newname)); if (ret) { diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 05d7dbe7ab..076044f300 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -399,6 +399,9 @@ Session::post_engine_init () Port::set_connecting_blocked (false); + /* Can't do this until the trigger input MIDI port is set up */ + TriggerBox::static_init (*this); + set_clean (); /* Now, finally, we can [ask the butler to] fill the playback buffers */ diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 5b2efe6d90..62a49effcc 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -91,7 +91,6 @@ Track::init () if (!is_auditioner()) { _triggerbox = boost::shared_ptr (new TriggerBox (_session, data_type ())); _triggerbox->set_owner (this); - _triggerbox->add_midi_sidechain (); } if (Route::init ()) { diff --git a/libs/ardour/triggerbox.cc b/libs/ardour/triggerbox.cc index 3431a7dcd8..74901d71e7 100644 --- a/libs/ardour/triggerbox.cc +++ b/libs/ardour/triggerbox.cc @@ -3045,10 +3045,13 @@ bool TriggerBox::roll_requested = false; bool TriggerBox::_learning = false; TriggerBox::CustomMidiMap TriggerBox::_custom_midi_map; std::pair TriggerBox::learning_for; -PBD::ScopedConnectionList TriggerBox::midi_learn_connections; -MIDI::Parser* TriggerBox::learning_parser = 0; PBD::Signal0 TriggerBox::TriggerMIDILearned; +MIDI::Parser* TriggerBox::input_parser (new MIDI::Parser); /* leak */ +PBD::ScopedConnectionList TriggerBox::static_connections; +PBD::ScopedConnection TriggerBox::midi_input_connection; +boost::shared_ptr TriggerBox::current_input; + typedef std::map , boost::shared_ptr> RegionStateMap; RegionStateMap enqueued_state_map; @@ -3060,6 +3063,15 @@ TriggerBox::init () init_pool (); } +void +TriggerBox::static_init (Session & s) +{ + Config->ParameterChanged.connect_same_thread (static_connections, boost::bind (&TriggerBox::static_parameter_changed, _1)); + input_parser->any.connect_same_thread (midi_input_connection, boost::bind (&TriggerBox::midi_input_handler, _1, _2, _3, _4)); + boost::dynamic_pointer_cast (s.trigger_input_port())->set_trace (input_parser); + s.trigger_input_port()->connect (Config->get_default_trigger_input_port()); +} + TriggerBox::TriggerBox (Session& s, DataType dt) : Processor (s, _("TriggerBox"), Temporal::BeatTime) , tracker (dt == DataType::MIDI ? new MidiStateTracker : 0) @@ -3108,19 +3120,34 @@ TriggerBox::set_cue_recording (bool yn) } void -TriggerBox::parameter_changed (std::string const & param) +TriggerBox::input_port_check () +{ + if (Config->get_default_trigger_input_port().empty()) { + return; + } + + Session* session = AudioEngine::instance()->session(); + + if (!session) { + return; + } + + std::cerr << "Reconnect to " << Config->get_default_trigger_input_port() << std::endl; + session->trigger_input_port()->connect (Config->get_default_trigger_input_port()); +} + +void +TriggerBox::static_parameter_changed (std::string const & param) { if (param == X_("default-trigger-input-port")) { + input_port_check (); + } +} - if (!Config->get_default_trigger_input_port().empty()) { - if (!_sidechain) { - add_midi_sidechain (); - } else { - reconnect_to_default (); - } - } - - } else if (param == "cue-behavior") { +void +TriggerBox::parameter_changed (std::string const & param) +{ + if (param == "cue-behavior") { const bool follow = (_session.config.get_cue_behavior() & FollowCues); if (!follow) { cancel_locate_armed (); @@ -3751,36 +3778,6 @@ TriggerBox::trigger (Triggers::size_type n) return all_triggers[n]; } -void -TriggerBox::add_midi_sidechain () -{ - assert (owner()); - if (!_sidechain) { - _sidechain.reset (new SideChain (_session, string_compose ("%1/%2", owner()->name(), name ()))); - _sidechain->activate (); - _sidechain->input()->add_port ("", owner(), DataType::MIDI); // add a port, don't connect. - boost::shared_ptr p = _sidechain->input()->nth (0); - - if (p) { - if (!Config->get_default_trigger_input_port().empty ()) { - p->connect (Config->get_default_trigger_input_port()); - } - } else { - error << _("Could not create port for trigger side-chain") << endmsg; - } - } -} - -void -TriggerBox::update_sidechain_name () -{ - if (!_sidechain) { - return; - } - assert (owner()); - _sidechain->set_name (string_compose ("%1/%2", owner()->name(), name ())); -} - bool TriggerBox::can_support_io_configuration (const ChanCount& in, ChanCount& out) { @@ -3800,10 +3797,6 @@ TriggerBox::can_support_io_configuration (const ChanCount& in, ChanCount& out) bool TriggerBox::configure_io (ChanCount in, ChanCount out) { - if (_sidechain) { - _sidechain->configure_io (in, out + ChanCount (DataType::MIDI, 1)); - } - bool ret = Processor::configure_io (in, out); if (ret) { @@ -3833,59 +3826,6 @@ TriggerBox::set_first_midi_note (int n) _first_midi_note = n; } -int -TriggerBox::note_to_trigger (int midi_note, int channel) -{ - const int column = _order; - int first_note; - int top; - int x; - int y; - - switch (_midi_map_mode) { - - case AbletonPush: - /* the top row of pads generate MIDI note 92, 93, 94 and so on. - Each lower row generates notes 8 below the one above it. - */ - top = 92 + column; - for (int row = 0; row < 8; ++row) { - if (midi_note == top - (row * 8)) { - return row; - } - } - return -1; - break; - - case SequentialNote: - first_note = _first_midi_note + (column * all_triggers.size()); - return midi_note - first_note; /* direct access to row */ - - case ByMidiChannel: - first_note = 3; - break; - - case Custom: - if (!_learning) { - - std::vector msg { uint8_t (MIDI::on | channel), (uint8_t) midi_note }; - - if (lookup_custom_midi_binding (msg, x, y)) { - if (x == _order) { - return y; - } - } - } - return -1; - - default: - break; - - } - - return midi_note; -} - bool TriggerBox::lookup_custom_midi_binding (std::vector const & msg, int& x, int& y) { @@ -3902,18 +3842,32 @@ TriggerBox::lookup_custom_midi_binding (std::vector const & msg, int& x } void -TriggerBox::midi_learn_input_handler (MIDI::Parser&, MIDI::byte* ev, size_t sz, samplecnt_t) +TriggerBox::midi_input_handler (MIDI::Parser&, MIDI::byte* buf, size_t sz, samplecnt_t) { - if (!_learning) { + if (_learning) { + + if ((buf[0] & 0xf0) == MIDI::on) { + /* throw away velocity */ + std::vector msg { buf[0], buf[1] }; + add_custom_midi_binding (msg, learning_for.first, learning_for.second); + _learning = false; + TriggerMIDILearned (); /* EMIT SIGNAL */ + } + return; } - if ((ev[0] & 0xf0) == MIDI::on) { - /* throw away velocity */ - std::vector msg { ev[0], ev[1] }; - add_custom_midi_binding (msg, learning_for.first, learning_for.second); - TriggerMIDILearned (); /* emit signal */ - return; + Evoral::Event ev (Evoral::MIDI_EVENT, 0, sz, buf); + + if (ev.is_note_on()) { + + std::vector msg { uint8_t (MIDI::on | ev.channel()), (uint8_t) ev.note() }; + int x; + int y; + + if (lookup_custom_midi_binding (msg, x, y)) { + AudioEngine::instance()->session()->bang_trigger_at (x, y); + } } return; @@ -3922,41 +3876,15 @@ TriggerBox::midi_learn_input_handler (MIDI::Parser&, MIDI::byte* ev, size_t sz, void TriggerBox::begin_midi_learn (int index) { - if (!_sidechain) { - return; - } - learning_for.first = order(); /* x */ learning_for.second = index; /* y */ _learning = true; - - - boost::shared_ptr mp = boost::dynamic_pointer_cast (_sidechain->input()->nth(0)); - if (!mp) { - return; - } - - if (!learning_parser) { - learning_parser = new MIDI::Parser; - } - - TriggerMIDILearned.connect_same_thread (midi_learn_connections, boost::bind (&TriggerBox::stop_midi_learn, this)); - learning_parser->any.connect_same_thread (midi_learn_connections, boost::bind (&TriggerBox::midi_learn_input_handler, _1, _2, _3, _4)); - mp->set_trace (learning_parser); } void TriggerBox::stop_midi_learn () { - if (_learning) { - _learning = false; - midi_learn_connections.drop_connections (); - boost::shared_ptr mp = boost::dynamic_pointer_cast (_sidechain->input()->nth(0)); - - if (mp) { - mp->set_trace (0); - } - } + _learning = false; } void @@ -4085,59 +4013,6 @@ TriggerBox::remove_custom_midi_binding (int x, int y) } } -void -TriggerBox::process_midi_trigger_requests (BufferSet& bufs) -{ - /* check MIDI port input buffer for triggers. This is always the last - * MIDI buffer of the BufferSet - */ - - MidiBuffer& mb (bufs.get_midi (bufs.count().n_midi() - 1 /* due to zero-based index*/)); - - for (MidiBuffer::iterator ev = mb.begin(); ev != mb.end(); ++ev) { - - if (!(*ev).is_note()) { - continue; - } - - int trigger_number = note_to_trigger ((*ev).note(), (*ev).channel()); - - DEBUG_TRACE (DEBUG::Triggers, string_compose ("note %1 received on %2, translated to trigger num %3\n", (int) (*ev).note(), (int) (*ev).channel(), trigger_number)); - - if (trigger_number < 0) { - /* not for us */ - continue; - } - - if (trigger_number >= (int) all_triggers.size()) { - continue; - } - - TriggerPtr t = all_triggers[trigger_number]; - - if (!t) { - continue; - } - - if ((*ev).is_note_on()) { - - if (t->velocity_effect() != 0.0) { - /* if MVE is zero, MIDI velocity has no - impact on gain. If it is small, it - has a small effect on gain. As it - approaches 1.0, it has full control - over the trigger gain. - */ - t->set_velocity_gain (1.0 - (t->velocity_effect() * (*ev).velocity() / 127.f)); - } - t->bang (); - - } else if ((*ev).is_note_off()) { - t->unbang (); - } - } -} - void TriggerBox::maybe_request_roll (Session& s) { @@ -4232,18 +4107,6 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp #endif bool allstop = _requests.stop_all.exchange (false); - - - /* STEP THREE: triggers in audio tracks need a MIDI sidechain to be - * able to receive inbound MIDI for triggering etc. This needs to run - * before anything else, since we may need data just received to launch - * a trigger (or stop it) - */ - - if (_sidechain) { - _sidechain->run (bufs, start_sample, end_sample, speed, nframes, true); - } - bool was_recorded; int32_t cue_bang = _session.first_cue_within (start_sample, end_sample, was_recorded); @@ -4293,11 +4156,6 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp process_requests (bufs); - /* STEP FIVE: handle any incoming MIDI requests - */ - - process_midi_trigger_requests (bufs); - /* STEP SEVEN: let each slot process any individual state requests */ @@ -4754,10 +4612,6 @@ TriggerBox::get_state () const node.add_child_nocopy (*trigger_child); - if (_sidechain) { - node.add_child_nocopy (_sidechain->get_state ()); - } - return node; } @@ -4803,18 +4657,6 @@ TriggerBox::set_state (const XMLNode& node, int version) } } - /* sidechain is a Processor (IO) */ - XMLNode* scnode = node.child (Processor::state_node_name.c_str ()); - if (scnode) { - add_midi_sidechain (); - assert (_sidechain); - if (!regenerate_xml_or_string_ids ()) { - _sidechain->set_state (*scnode, version); - } else { - update_sidechain_name (); - } - } - /* Since _active_slots may have changed, we could consider sending * EmptyStatusChanged, but for now we don't consider ::set_state() to * be used except at session load. @@ -4823,17 +4665,6 @@ TriggerBox::set_state (const XMLNode& node, int version) return 0; } -void -TriggerBox::reconnect_to_default () -{ - if (!_sidechain) { - return; - } - - _sidechain->input()->nth (0)->disconnect_all (); - _sidechain->input()->nth (0)->connect (Config->get_default_trigger_input_port()); -} - MultiAllocSingleReleasePool* TriggerBox::Request::pool; void