13
0

triggerbox: lots of design changes and implementation tweaks. Gate launch style now works

This commit is contained in:
Paul Davis 2021-07-19 09:57:31 -06:00
parent 50d68a6a34
commit 8eed420ad4
2 changed files with 223 additions and 64 deletions

View File

@ -19,6 +19,7 @@
#ifndef __ardour_triggerbox_h__ #ifndef __ardour_triggerbox_h__
#define __ardour_triggerbox_h__ #define __ardour_triggerbox_h__
#include <map>
#include <vector> #include <vector>
#include <string> #include <string>
#include <exception> #include <exception>
@ -42,10 +43,12 @@ class TriggerBox;
class LIBARDOUR_API Trigger { class LIBARDOUR_API Trigger {
public: public:
Trigger(); Trigger (boost::shared_ptr<Region>);
virtual ~Trigger() {} virtual ~Trigger() {}
virtual void bang (TriggerBox&, Temporal::Beats const &, samplepos_t) = 0; 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; } bool running() const { return _running; }
enum LaunchStyle { enum LaunchStyle {
@ -72,10 +75,17 @@ class LIBARDOUR_API Trigger {
FollowAction follow_action() const { return _follow_action; } FollowAction follow_action() const { return _follow_action; }
void set_follow_action (FollowAction); void set_follow_action (FollowAction);
virtual int set_region (boost::shared_ptr<Region>) = 0;
boost::shared_ptr<Region> region() const { return _region; }
protected: protected:
bool _running; bool _running;
bool _stop_requested;
LaunchStyle _launch_style; LaunchStyle _launch_style;
FollowAction _follow_action; FollowAction _follow_action;
boost::shared_ptr<Region> _region;
void set_region_internal (boost::shared_ptr<Region>);
}; };
class LIBARDOUR_API AudioTrigger : public Trigger { class LIBARDOUR_API AudioTrigger : public Trigger {
@ -84,13 +94,19 @@ class LIBARDOUR_API AudioTrigger : public Trigger {
~AudioTrigger (); ~AudioTrigger ();
void bang (TriggerBox&, Temporal::Beats const & , samplepos_t); 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); 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<Region>);
private: private:
boost::shared_ptr<AudioRegion> region;
std::vector<Sample*> data; std::vector<Sample*> data;
std::vector<samplecnt_t> read_index; std::vector<samplecnt_t> read_index;
samplecnt_t length; samplecnt_t length;
void drop_data ();
int load_data (boost::shared_ptr<AudioRegion>);
}; };
class LIBARDOUR_API TriggerBox : public Processor 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 can_support_io_configuration (const ChanCount& in, ChanCount& out);
bool configure_io (ChanCount in, ChanCount out); bool configure_io (ChanCount in, ChanCount out);
typedef std::vector<Trigger*> Triggers;
Trigger* trigger (Triggers::size_type);
bool queue_trigger (Trigger*); bool queue_trigger (Trigger*);
void add_trigger (Trigger*); void add_trigger (Trigger*);
@ -112,19 +132,18 @@ class LIBARDOUR_API TriggerBox : public Processor
private: private:
PBD::RingBuffer<Trigger*> _trigger_queue; PBD::RingBuffer<Trigger*> _trigger_queue;
typedef std::vector<Trigger*> Triggers;
Triggers active_triggers; Triggers active_triggers;
Glib::Threads::Mutex trigger_lock; Glib::Threads::RWLock trigger_lock;
Triggers all_triggers; 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_on (int note_number, int velocity);
void note_off (int note_number, int velocity); void note_off (int note_number, int velocity);
/* XXX for initial testing only */ typedef std::map<uint8_t,Triggers::size_type> MidiTriggerMap;
MidiTriggerMap midi_trigger_map;
boost::shared_ptr<Source> the_source;
boost::shared_ptr<AudioRegion> the_region;
AudioTrigger* the_trigger;
}; };
} // namespace ARDOUR } // namespace ARDOUR

View File

@ -1,5 +1,7 @@
#include <iostream> #include <iostream>
#include "pbd/failed_constructor.h"
#include "ardour/audioregion.h" #include "ardour/audioregion.h"
#include "ardour/audio_buffer.h" #include "ardour/audio_buffer.h"
#include "ardour/midi_buffer.h" #include "ardour/midi_buffer.h"
@ -17,9 +19,16 @@ TriggerBox::TriggerBox (Session& s)
: Processor (s, _("TriggerBox"), Temporal::BeatTime) : Processor (s, _("TriggerBox"), Temporal::BeatTime)
, _trigger_queue (1024) , _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<Source> 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::start, 0);
plist.add (Properties::length, the_source->length ()); plist.add (Properties::length, the_source->length ());
@ -27,18 +36,39 @@ TriggerBox::TriggerBox (Session& s)
plist.add (Properties::layer, 0); plist.add (Properties::layer, 0);
plist.add (Properties::layering_index, 0); plist.add (Properties::layering_index, 0);
boost::shared_ptr<Region> r = RegionFactory::create (the_source, plist, false); boost::shared_ptr<Region> the_region (RegionFactory::create (the_source, plist, false));
the_region = boost::dynamic_pointer_cast<AudioRegion> (r);
/* XXX the_region/trigger will be looked up in a all_triggers[0] = new AudioTrigger (boost::dynamic_pointer_cast<AudioRegion> (the_region));
std::map<MIDI::byte,Trigger>
*/
the_trigger = new AudioTrigger (the_region);
add_trigger (the_trigger);
} }
TriggerBox::~TriggerBox () 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 bool
@ -56,16 +86,14 @@ TriggerBox::can_support_io_configuration (const ChanCount& in, ChanCount& out)
bool bool
TriggerBox::configure_io (ChanCount in, ChanCount out) TriggerBox::configure_io (ChanCount in, ChanCount out)
{ {
cerr << "TB: " << in << " / " << out << endl;
return Processor::configure_io (in, out); return Processor::configure_io (in, out);
} }
void void
TriggerBox::add_trigger (Trigger* trigger) TriggerBox::add_trigger (Trigger* trigger)
{ {
Glib::Threads::Mutex::Lock lm (trigger_lock); Glib::Threads::RWLock::WriterLock lm (trigger_lock);
all_triggers.push_back (trigger); all_triggers.push_back (trigger);
cerr << "Now have " << all_triggers.size() << " of all possible triggers\n";
} }
bool bool
@ -74,29 +102,86 @@ TriggerBox::queue_trigger (Trigger* trigger)
return _trigger_queue.write (&trigger, 1) == 1; 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<Trigger*>::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 void
TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, double speed, pframes_t nframes, bool result_required) 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; samplepos_t next_beat = 0;
Temporal::Beats beats_now; Temporal::Beats beats_now;
process_trigger_requests (beats_now, start_sample);
/* check MIDI port input buffers for triggers */ /* check MIDI port input buffers for triggers */
for (BufferSet::midi_iterator mi = bufs.midi_begin(); mi != bufs.midi_end(); ++mi) { for (BufferSet::midi_iterator mi = bufs.midi_begin(); mi != bufs.midi_end(); ++mi) {
MidiBuffer& mb (*mi); MidiBuffer& mb (*mi);
for (MidiBuffer::iterator ev = mb.begin(); ev != mb.end(); ++ev) { 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 ((*ev).is_note_on()) {
if (!the_trigger->running()) { if (!t->running()) {
active_triggers.push_back (the_trigger); 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 */ /* get tempo map */
/* find offset to next bar * and beat start /* 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; //run_beats = true;
//} //}
/* if there are any triggers queued, make them active
*/
RingBuffer<Trigger*>::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; bool err = false;
const size_t nchans = the_region->n_channels ();
bool need_butler = false; bool need_butler = false;
size_t max_chans = 0;
for (uint32_t chan = 0; chan < nchans; ++chan) {
AudioBuffer& buf = bufs.get_audio (chan);
buf.silence (nframes, 0);
}
for (Triggers::iterator t = active_triggers.begin(); !err && t != active_triggers.end(); ++t) { 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; continue;
} }
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (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) { for (uint32_t chan = 0; !err && chan < nchans; ++chan) {
AudioBuffer& buf = bufs.get_audio (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) { if (!data) {
/* XXX need to delete the trigger/put it back in the pool */ /* XXX need to delete the trigger/put it back in the pool */
cerr << "trigger complete\n";
t = active_triggers.erase (t); t = active_triggers.erase (t);
err = true; err = true;
} else { } 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()); cc.set_midi (bufs.count().n_midi());
bufs.set_count (cc); bufs.set_count (cc);
} }
@ -192,33 +255,46 @@ TriggerBox::set_state (const XMLNode&, int version)
return 0; return 0;
} }
/*--------------------*/ /*--------------------*/
Trigger::Trigger () Trigger::Trigger (boost::shared_ptr<Region> r)
: _running (false) : _running (false)
, _launch_style (Loop) , _stop_requested (false)
, _launch_style (Gate)
, _follow_action (Stop) , _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<Region> r)
{
_region = r;
}
/*--------------------*/ /*--------------------*/
AudioTrigger::AudioTrigger (boost::shared_ptr<AudioRegion> r) AudioTrigger::AudioTrigger (boost::shared_ptr<AudioRegion> r)
: region (r) : Trigger (r)
, data (0) , data (0)
, length (0) , length (0)
{ {
/* XXX catch region going away */ /* XXX catch region going away */
const uint32_t nchans = region->n_channels(); if (load_data (r)) {
throw failed_constructor ();
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);
} }
} }
@ -229,6 +305,56 @@ AudioTrigger::~AudioTrigger ()
} }
} }
int
AudioTrigger::set_region (boost::shared_ptr<Region> r)
{
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (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<AudioRegion> 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 void
AudioTrigger::bang (TriggerBox& /*proc*/, Temporal::Beats const &, samplepos_t) AudioTrigger::bang (TriggerBox& /*proc*/, Temporal::Beats const &, samplepos_t)
{ {
@ -243,6 +369,12 @@ AudioTrigger::bang (TriggerBox& /*proc*/, Temporal::Beats const &, samplepos_t)
_running = true; _running = true;
} }
void
AudioTrigger::unbang (TriggerBox& /*proc*/, Temporal::Beats const &, samplepos_t)
{
_stop_requested = true;
}
Sample* Sample*
AudioTrigger::run (uint32_t channel, pframes_t& nframes, samplepos_t /*start_sample*/, samplepos_t /*end_sample*/, bool& /* need_butler */) 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) { if (read_index[channel] >= length) {
_running = false;
return 0;
}
if (_stop_requested) {
/* XXX need fade out machinery */
_running = false;
_stop_requested = false;
return 0; return 0;
} }