From 8eed420ad434431cb130f526c82d757c5eb69812 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Mon, 19 Jul 2021 09:57:31 -0600 Subject: [PATCH] triggerbox: lots of design changes and implementation tweaks. Gate launch style now works --- libs/ardour/ardour/triggerbox.h | 37 +++-- libs/ardour/triggerbox.cc | 250 +++++++++++++++++++++++++------- 2 files changed, 223 insertions(+), 64 deletions(-) diff --git a/libs/ardour/ardour/triggerbox.h b/libs/ardour/ardour/triggerbox.h index 2af3bcf91f..96e877370d 100644 --- a/libs/ardour/ardour/triggerbox.h +++ b/libs/ardour/ardour/triggerbox.h @@ -19,6 +19,7 @@ #ifndef __ardour_triggerbox_h__ #define __ardour_triggerbox_h__ +#include #include #include #include @@ -42,10 +43,12 @@ class TriggerBox; class LIBARDOUR_API Trigger { public: - Trigger(); + Trigger (boost::shared_ptr); virtual ~Trigger() {} virtual void bang (TriggerBox&, Temporal::Beats const &, samplepos_t) = 0; + virtual void unbang (TriggerBox&, Temporal::Beats const &, samplepos_t) = 0; + bool running() const { return _running; } enum LaunchStyle { @@ -72,10 +75,17 @@ class LIBARDOUR_API Trigger { FollowAction follow_action() const { return _follow_action; } void set_follow_action (FollowAction); + virtual int set_region (boost::shared_ptr) = 0; + boost::shared_ptr region() const { return _region; } + protected: bool _running; + bool _stop_requested; LaunchStyle _launch_style; FollowAction _follow_action; + boost::shared_ptr _region; + + void set_region_internal (boost::shared_ptr); }; class LIBARDOUR_API AudioTrigger : public Trigger { @@ -84,13 +94,19 @@ class LIBARDOUR_API AudioTrigger : public Trigger { ~AudioTrigger (); void bang (TriggerBox&, Temporal::Beats const & , samplepos_t); + void unbang (TriggerBox&, Temporal::Beats const & , samplepos_t); + Sample* run (uint32_t channel, pframes_t& nframes, samplepos_t start_frame, samplepos_t end_frame, bool& need_butler); + int set_region (boost::shared_ptr); + private: - boost::shared_ptr region; std::vector data; std::vector read_index; samplecnt_t length; + + void drop_data (); + int load_data (boost::shared_ptr); }; class LIBARDOUR_API TriggerBox : public Processor @@ -103,6 +119,10 @@ class LIBARDOUR_API TriggerBox : public Processor bool can_support_io_configuration (const ChanCount& in, ChanCount& out); bool configure_io (ChanCount in, ChanCount out); + typedef std::vector Triggers; + + Trigger* trigger (Triggers::size_type); + bool queue_trigger (Trigger*); void add_trigger (Trigger*); @@ -112,19 +132,18 @@ class LIBARDOUR_API TriggerBox : public Processor private: PBD::RingBuffer _trigger_queue; - typedef std::vector Triggers; Triggers active_triggers; - Glib::Threads::Mutex trigger_lock; + Glib::Threads::RWLock trigger_lock; Triggers all_triggers; + void drop_triggers (); + void process_trigger_requests (Temporal::Beats const &, samplepos_t); + void note_on (int note_number, int velocity); void note_off (int note_number, int velocity); - /* XXX for initial testing only */ - - boost::shared_ptr the_source; - boost::shared_ptr the_region; - AudioTrigger* the_trigger; + typedef std::map MidiTriggerMap; + MidiTriggerMap midi_trigger_map; }; } // namespace ARDOUR diff --git a/libs/ardour/triggerbox.cc b/libs/ardour/triggerbox.cc index b17582de70..17abc0ee4c 100644 --- a/libs/ardour/triggerbox.cc +++ b/libs/ardour/triggerbox.cc @@ -1,5 +1,7 @@ #include +#include "pbd/failed_constructor.h" + #include "ardour/audioregion.h" #include "ardour/audio_buffer.h" #include "ardour/midi_buffer.h" @@ -17,9 +19,16 @@ TriggerBox::TriggerBox (Session& s) : Processor (s, _("TriggerBox"), Temporal::BeatTime) , _trigger_queue (1024) { - PropertyList plist; - the_source.reset (new SndFileSource (_session, "/music/misc/La_Voz_Del_Rio.wav", 0, Source::Flag (0))); + /* default number of possible triggers. call ::add_trigger() to increase */ + + all_triggers.resize (16, 0); + + midi_trigger_map.insert (midi_trigger_map.end(), std::make_pair (uint8_t (60), 0)); + + boost::shared_ptr the_source (new SndFileSource (_session, "/music/misc/La_Voz_Del_Rio.wav", 0, Source::Flag (0))); + + PropertyList plist; plist.add (Properties::start, 0); plist.add (Properties::length, the_source->length ()); @@ -27,18 +36,39 @@ TriggerBox::TriggerBox (Session& s) plist.add (Properties::layer, 0); plist.add (Properties::layering_index, 0); - boost::shared_ptr r = RegionFactory::create (the_source, plist, false); - the_region = boost::dynamic_pointer_cast (r); + boost::shared_ptr the_region (RegionFactory::create (the_source, plist, false)); - /* XXX the_region/trigger will be looked up in a - std::map - */ - the_trigger = new AudioTrigger (the_region); - add_trigger (the_trigger); + all_triggers[0] = new AudioTrigger (boost::dynamic_pointer_cast (the_region)); } TriggerBox::~TriggerBox () { + drop_triggers (); +} + +void +TriggerBox::drop_triggers () +{ + Glib::Threads::RWLock::WriterLock lm (trigger_lock); + + for (Triggers::iterator t = all_triggers.begin(); t != all_triggers.end(); ++t) { + if (*t) { + delete *t; + (*t) = 0; + } + } +} + +Trigger* +TriggerBox::trigger (Triggers::size_type n) +{ + Glib::Threads::RWLock::ReaderLock lm (trigger_lock); + + if (n >= all_triggers.size()) { + return 0; + } + + return all_triggers[n]; } bool @@ -56,16 +86,14 @@ TriggerBox::can_support_io_configuration (const ChanCount& in, ChanCount& out) bool TriggerBox::configure_io (ChanCount in, ChanCount out) { - cerr << "TB: " << in << " / " << out << endl; return Processor::configure_io (in, out); } void TriggerBox::add_trigger (Trigger* trigger) { - Glib::Threads::Mutex::Lock lm (trigger_lock); + Glib::Threads::RWLock::WriterLock lm (trigger_lock); all_triggers.push_back (trigger); - cerr << "Now have " << all_triggers.size() << " of all possible triggers\n"; } bool @@ -74,29 +102,86 @@ TriggerBox::queue_trigger (Trigger* trigger) return _trigger_queue.write (&trigger, 1) == 1; } +void +TriggerBox::process_trigger_requests (Temporal::Beats const & beats_now, samplepos_t samples_now) +{ + /* if there are any triggers queued, make them active + */ + + RingBuffer::rw_vector vec; + _trigger_queue.get_read_vector (&vec); + + for (uint32_t n = 0; n < vec.len[0]; ++n) { + Trigger* t = vec.buf[0][n]; + t->bang (*this, beats_now, samples_now); + active_triggers.push_back (t); + } + + for (uint32_t n = 0; n < vec.len[1]; ++n) { + Trigger* t = vec.buf[1][n]; + t->bang (*this, beats_now, samples_now); + active_triggers.push_back (t); + } + + _trigger_queue.increment_read_idx (vec.len[0] + vec.len[1]); +} + void TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, double speed, pframes_t nframes, bool result_required) { samplepos_t next_beat = 0; Temporal::Beats beats_now; + process_trigger_requests (beats_now, start_sample); + /* check MIDI port input buffers for triggers */ for (BufferSet::midi_iterator mi = bufs.midi_begin(); mi != bufs.midi_end(); ++mi) { MidiBuffer& mb (*mi); for (MidiBuffer::iterator ev = mb.begin(); ev != mb.end(); ++ev) { + + if (!(*ev).is_note()) { + continue; + } + + MidiTriggerMap::iterator mt = midi_trigger_map.find ((*ev).note()); + Trigger* t = 0; + + if (mt != midi_trigger_map.end()) { + + assert (mt->second < all_triggers.size()); + + t = all_triggers[mt->second]; + + if (!t) { + continue; + } + } + if ((*ev).is_note_on()) { - if (!the_trigger->running()) { - active_triggers.push_back (the_trigger); + if (!t->running()) { + active_triggers.push_back (t); + } + + t->bang (*this, beats_now, start_sample); + + } else if ((*ev).is_note_off()) { + + if (t->running() && t->launch_style() == Trigger::Gate) { + t->unbang (*this, beats_now, start_sample); } - the_trigger->bang (*this, beats_now, start_sample); } } } + if (active_triggers.empty()) { + /* nothing to do */ + return; + } + /* get tempo map */ /* find offset to next bar * and beat start @@ -112,34 +197,9 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp //run_beats = true; //} - /* if there are any triggers queued, make them active - */ - - RingBuffer::rw_vector vec; - _trigger_queue.get_read_vector (&vec); - - for (uint32_t n = 0; n < vec.len[0]; ++n) { - Trigger* t = vec.buf[0][n]; - t->bang (*this, beats_now, start_sample); - active_triggers.push_back (t); - } - - for (uint32_t n = 0; n < vec.len[1]; ++n) { - Trigger* t = vec.buf[1][n]; - t->bang (*this, beats_now, start_sample); - active_triggers.push_back (t); - } - - _trigger_queue.increment_read_idx (vec.len[0] + vec.len[1]); - bool err = false; - const size_t nchans = the_region->n_channels (); bool need_butler = false; - - for (uint32_t chan = 0; chan < nchans; ++chan) { - AudioBuffer& buf = bufs.get_audio (chan); - buf.silence (nframes, 0); - } + size_t max_chans = 0; for (Triggers::iterator t = active_triggers.begin(); !err && t != active_triggers.end(); ++t) { @@ -149,6 +209,10 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp continue; } + boost::shared_ptr ar = boost::dynamic_pointer_cast (at->region()); + const size_t nchans = ar->n_channels (); + max_chans = std::max (max_chans, nchans); + for (uint32_t chan = 0; !err && chan < nchans; ++chan) { AudioBuffer& buf = bufs.get_audio (chan); @@ -158,7 +222,6 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp if (!data) { /* XXX need to delete the trigger/put it back in the pool */ - cerr << "trigger complete\n"; t = active_triggers.erase (t); err = true; } else { @@ -175,7 +238,7 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp } } - ChanCount cc (DataType::AUDIO, nchans); + ChanCount cc (DataType::AUDIO, max_chans); cc.set_midi (bufs.count().n_midi()); bufs.set_count (cc); } @@ -192,33 +255,46 @@ TriggerBox::set_state (const XMLNode&, int version) return 0; } - /*--------------------*/ -Trigger::Trigger () +Trigger::Trigger (boost::shared_ptr r) : _running (false) - , _launch_style (Loop) + , _stop_requested (false) + , _launch_style (Gate) , _follow_action (Stop) + , _region (r) { } +void +Trigger::set_follow_action (FollowAction f) +{ + _follow_action = f; +} + +void +Trigger::set_launch_style (LaunchStyle l) +{ + _launch_style = l; +} + +void +Trigger::set_region_internal (boost::shared_ptr r) +{ + _region = r; +} + /*--------------------*/ AudioTrigger::AudioTrigger (boost::shared_ptr r) - : region (r) + : Trigger (r) , data (0) , length (0) { /* XXX catch region going away */ - const uint32_t nchans = region->n_channels(); - - length = region->length_samples(); - - for (uint32_t n = 0; n < nchans; ++n) { - data.push_back (new Sample[length]);; - read_index.push_back (0); - region->read (data[n], 0, length, n); + if (load_data (r)) { + throw failed_constructor (); } } @@ -229,6 +305,56 @@ AudioTrigger::~AudioTrigger () } } +int +AudioTrigger::set_region (boost::shared_ptr r) +{ + boost::shared_ptr ar = boost::dynamic_pointer_cast (r); + + if (!ar) { + return -1; + } + + set_region_internal (r); + + if (load_data (ar)) { + return -1; + } + + return 0; +} + +void +AudioTrigger::drop_data () +{ + for (uint32_t n = 0; n < data.size(); ++n) { + delete [] data[n]; + } + data.clear (); +} + +int +AudioTrigger::load_data (boost::shared_ptr ar) +{ + const uint32_t nchans = ar->n_channels(); + + length = ar->length_samples(); + + drop_data (); + + try { + for (uint32_t n = 0; n < nchans; ++n) { + data.push_back (new Sample[length]); + read_index.push_back (0); + ar->read (data[n], 0, length, n); + } + } catch (...) { + drop_data (); + return -1; + } + + return 0; +} + void AudioTrigger::bang (TriggerBox& /*proc*/, Temporal::Beats const &, samplepos_t) { @@ -243,6 +369,12 @@ AudioTrigger::bang (TriggerBox& /*proc*/, Temporal::Beats const &, samplepos_t) _running = true; } +void +AudioTrigger::unbang (TriggerBox& /*proc*/, Temporal::Beats const &, samplepos_t) +{ + _stop_requested = true; +} + Sample* AudioTrigger::run (uint32_t channel, pframes_t& nframes, samplepos_t /*start_sample*/, samplepos_t /*end_sample*/, bool& /* need_butler */) { @@ -251,6 +383,14 @@ AudioTrigger::run (uint32_t channel, pframes_t& nframes, samplepos_t /*start_sam } if (read_index[channel] >= length) { + _running = false; + return 0; + } + + if (_stop_requested) { + /* XXX need fade out machinery */ + _running = false; + _stop_requested = false; return 0; }