triggerbox: closing in on a one-at-a-time design

This commit is contained in:
Paul Davis 2021-09-04 23:19:47 -06:00
parent 2cd88a67f1
commit c2abde9199
2 changed files with 105 additions and 104 deletions

View file

@ -162,7 +162,7 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
void set_legato (bool yn); void set_legato (bool yn);
bool legato () const { return _legato; } bool legato () const { return _legato; }
void startup (); virtual void startup ();
protected: protected:
TriggerBox& _box; TriggerBox& _box;
@ -206,6 +206,7 @@ class LIBARDOUR_API AudioTrigger : public Trigger {
timepos_t natural_length() const; /* offset from start of data */ timepos_t natural_length() const; /* offset from start of data */
int set_region (boost::shared_ptr<Region>); int set_region (boost::shared_ptr<Region>);
void startup ();
XMLNode& get_state (void); XMLNode& get_state (void);
int set_state (const XMLNode&, int version); int set_state (const XMLNode&, int version);
@ -254,26 +255,28 @@ class LIBARDOUR_API TriggerBox : public Processor
DataType data_type() const { return _data_type; } DataType data_type() const { return _data_type; }
void stop_all (); void request_stop_all ();
/* only valid when called by Triggers from within ::process_state_requests() */ /* only valid when called by Triggers from within ::process_state_requests() */
size_t currently_running() const { return actually_running; } bool currently_running() const { return currently_playing; }
void set_next (size_t which); void set_next (size_t which);
void queue_explict (Trigger*); void queue_explict (Trigger*);
void queue_implicit (Trigger*); void queue_implicit (Trigger*);
Trigger* get_next_trigger (); Trigger* get_next_trigger ();
Trigger* peak_next_trigger ();
void prepare_next (size_t current); void prepare_next (size_t current);
private: private:
PBD::RingBuffer<Trigger*> _bang_queue; PBD::RingBuffer<Trigger*> _bang_queue;
PBD::RingBuffer<Trigger*> _unbang_queue; PBD::RingBuffer<Trigger*> _unbang_queue;
DataType _data_type; DataType _data_type;
size_t actually_running;
Glib::Threads::RWLock trigger_lock; /* protects all_triggers */ Glib::Threads::RWLock trigger_lock; /* protects all_triggers */
Triggers all_triggers; Triggers all_triggers;
PBD::RingBuffer<Trigger*> explicit_queue; /* user queued triggers */ PBD::RingBuffer<Trigger*> explicit_queue; /* user queued triggers */
PBD::RingBuffer<Trigger*> implicit_queue; /* follow-action queued triggers */ PBD::RingBuffer<Trigger*> implicit_queue; /* follow-action queued triggers */
Trigger* currently_playing;
std::atomic<bool> _stop_all;
PBD::PCGRand _pcg; PBD::PCGRand _pcg;
@ -283,6 +286,7 @@ class LIBARDOUR_API TriggerBox : public Processor
void process_ui_trigger_requests (); void process_ui_trigger_requests ();
void process_midi_trigger_requests (BufferSet&); void process_midi_trigger_requests (BufferSet&);
int determine_next_trigger (size_t n); int determine_next_trigger (size_t n);
void stop_all ();
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);

View file

@ -42,7 +42,6 @@ Trigger::Trigger (size_t n, TriggerBox& b)
, _bang (0) , _bang (0)
, _unbang (0) , _unbang (0)
, _index (n) , _index (n)
, _next_trigger (-1)
, _launch_style (Toggle) , _launch_style (Toggle)
, _follow_action { NextTrigger, Stop } , _follow_action { NextTrigger, Stop }
, _follow_action_probability (100) , _follow_action_probability (100)
@ -161,7 +160,6 @@ Trigger::quantization () const
void void
Trigger::stop (int next) Trigger::stop (int next)
{ {
_next_trigger = next;
request_state (Stopped); request_state (Stopped);
} }
@ -178,20 +176,6 @@ Trigger::startup()
PropertyChanged (ARDOUR::Properties::running); PropertyChanged (ARDOUR::Properties::running);
} }
void
Trigger::maybe_startup ()
{
if (_state != WaitingToStart) {
if (!_box.currently_running()) {
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 -> %3\n", index(), enum_2_string (_state), enum_2_string (WaitingToStart)));
startup ();
} else {
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 queue explicit3 from state request\n", index()));
_box.queue_explict (this);
}
}
}
void void
Trigger::process_state_requests () Trigger::process_state_requests ()
{ {
@ -210,7 +194,7 @@ Trigger::process_state_requests ()
} }
break; break;
case Running: case Running:
maybe_startup (); _box.queue_explict (this);
break; break;
default: default:
break; break;
@ -250,7 +234,7 @@ Trigger::process_state_requests ()
case Stopped: case Stopped:
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 -> %3\n", index(), enum_2_string (Stopped), enum_2_string (WaitingToStart))); DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 -> %3\n", index(), enum_2_string (Stopped), enum_2_string (WaitingToStart)));
maybe_startup (); _box.queue_explict (this);
break; break;
case WaitingToStart: case WaitingToStart:
@ -261,7 +245,6 @@ Trigger::process_state_requests ()
} }
} }
while ((x = _unbang.load ())) { while ((x = _unbang.load ())) {
_unbang.fetch_sub (1); _unbang.fetch_sub (1);
@ -342,14 +325,6 @@ Trigger::maybe_compute_next_transition (Temporal::Beats const & start, Temporal:
return RunNone; return RunNone;
} }
void
Trigger::set_next_trigger (int n)
{
_next_trigger = n;
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 : set %2 as next\n", index(), n));
}
/*--------------------*/ /*--------------------*/
AudioTrigger::AudioTrigger (size_t n, TriggerBox& b) AudioTrigger::AudioTrigger (size_t n, TriggerBox& b)
@ -371,6 +346,13 @@ AudioTrigger::~AudioTrigger ()
} }
} }
void
AudioTrigger::startup ()
{
Trigger::startup ();
retrigger ();
}
XMLNode& XMLNode&
AudioTrigger::get_state (void) AudioTrigger::get_state (void)
{ {
@ -673,6 +655,7 @@ void
AudioTrigger::retrigger () AudioTrigger::retrigger ()
{ {
read_index = _start_offset + _legato_offset; read_index = _start_offset + _legato_offset;
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 retriggered to %2\n", _index, read_index));
} }
int int
@ -708,7 +691,7 @@ AudioTrigger::run (BufferSet& bufs, pframes_t nframes, pframes_t dest_offset, bo
/* We reached the end */ /* We reached the end */
if ((_launch_style == Repeat) || ((_next_trigger > 0) && (size_t) _next_trigger == _index)) { /* self repeat */ if ((_launch_style == Repeat) || (_box.peak_next_trigger() == this)) { /* self repeat */
nframes -= this_read; nframes -= this_read;
dest_offset += this_read; dest_offset += this_read;
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached end, but set to loop, so retrigger\n", index())); DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached end, but set to loop, so retrigger\n", index()));
@ -723,7 +706,7 @@ AudioTrigger::run (BufferSet& bufs, pframes_t nframes, pframes_t dest_offset, bo
for (size_t chn = 0; chn < ar->n_channels(); ++chn) { for (size_t chn = 0; chn < ar->n_channels(); ++chn) {
size_t channel = chn % data.size(); size_t channel = chn % data.size();
AudioBuffer& buf (bufs.get_audio (channel)); AudioBuffer& buf (bufs.get_audio (channel));
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 short fill, do silent fill\n", index())); DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 short fill, ri %2 vs ls %3, do silent fill\n", index(), read_index, last_sample));
buf.silence (nframes - this_read, dest_offset + this_read); buf.silence (nframes - this_read, dest_offset + this_read);
} }
} }
@ -762,9 +745,10 @@ TriggerBox::TriggerBox (Session& s, DataType dt)
, _bang_queue (1024) , _bang_queue (1024)
, _unbang_queue (1024) , _unbang_queue (1024)
, _data_type (dt) , _data_type (dt)
, actually_running (0)
, explicit_queue (64) , explicit_queue (64)
, implicit_queue (64) , implicit_queue (64)
, currently_playing (0)
, _stop_all (false)
{ {
/* default number of possible triggers. call ::add_trigger() to increase */ /* default number of possible triggers. call ::add_trigger() to increase */
@ -790,20 +774,45 @@ TriggerBox::TriggerBox (Session& s, DataType dt)
void void
TriggerBox::queue_explict (Trigger* t) TriggerBox::queue_explict (Trigger* t)
{ {
cerr << "explQ " << t->index() << endl; assert (t);
DEBUG_TRACE (DEBUG::Triggers, string_compose ("explicit queue %1\n", t->index()));
explicit_queue.write (&t, 1); explicit_queue.write (&t, 1);
implicit_queue.reset (); implicit_queue.reset ();
if (currently_playing) {
currently_playing->unbang ();
}
} }
void void
TriggerBox::queue_implicit (Trigger* t) TriggerBox::queue_implicit (Trigger* t)
{ {
assert (t);
if (explicit_queue.read_space() == 0) { if (explicit_queue.read_space() == 0) {
cerr << "implQ " << t->index() << endl; DEBUG_TRACE (DEBUG::Triggers, string_compose ("implicit queue %1\n", t->index()));
implicit_queue.write (&t, 1); implicit_queue.write (&t, 1);
} }
} }
Trigger*
TriggerBox::peak_next_trigger ()
{
RingBuffer<Trigger*>::rw_vector rwv;
explicit_queue.get_read_vector (&rwv);
if (rwv.len[0] > 0) {
return *(rwv.buf[0]);
}
implicit_queue.get_read_vector (&rwv);
if (rwv.len[0] > 0) {
return *(rwv.buf[0]);
}
return 0;
}
Trigger* Trigger*
TriggerBox::get_next_trigger () TriggerBox::get_next_trigger ()
{ {
@ -819,7 +828,6 @@ TriggerBox::get_next_trigger ()
return r; return r;
} }
DEBUG_TRACE (DEBUG::Triggers, "no next trigger\n");
return 0; return 0;
} }
@ -876,6 +884,12 @@ TriggerBox::~TriggerBox ()
drop_triggers (); drop_triggers ();
} }
void
TriggerBox::request_stop_all ()
{
_stop_all = true;
}
void void
TriggerBox::stop_all () TriggerBox::stop_all ()
{ {
@ -884,6 +898,9 @@ TriggerBox::stop_all ()
for (size_t n = 0; n < all_triggers.size(); ++n) { for (size_t n = 0; n < all_triggers.size(); ++n) {
all_triggers[n]->stop (-1); all_triggers[n]->stop (-1);
} }
implicit_queue.reset ();
explicit_queue.reset ();
} }
void void
@ -989,25 +1006,6 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
process_midi_trigger_requests (bufs); process_midi_trigger_requests (bufs);
/* count how many triggers are actually running
* (Note that in most cases (i.e. following the usual Live/Bitwig
* model), this is always either zero or one, but we
* allow for the possibility of overlapping triggers.
*/
actually_running = 0;
for (size_t n = 0; n < all_triggers.size(); ++n) {
switch (all_triggers[n]->state()) {
case Trigger::Running:
case Trigger::WaitingToStart:
case Trigger::WaitingToStop:
actually_running++;
break;
default:
break;
}
}
/* now let each trigger handle any state changes */ /* now let each trigger handle any state changes */
@ -1015,28 +1013,15 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
for (size_t n = 0; n < all_triggers.size(); ++n) { for (size_t n = 0; n < all_triggers.size(); ++n) {
all_triggers[n]->process_state_requests (); all_triggers[n]->process_state_requests ();
}
/* now recheck all states so that we know if we have any if (!currently_playing) {
* active triggers. if ((currently_playing = get_next_trigger ()) != 0) {
*/ currently_playing->startup ();
switch (all_triggers[n]->state()) {
case Trigger::Running:
case Trigger::WaitingToStart:
case Trigger::WaitingToStop:
case Trigger::WaitingForRetrigger:
case Trigger::Stopping:
to_run.push_back (n);
default:
break;
} }
} }
if (to_run.empty()) { if (!currently_playing) {
/* this saves us from the cost of the tempo map lookups.
XXX if these were passed in to ::run(), we could possibly
skip this condition.
*/
return; return;
} }
@ -1046,6 +1031,11 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
_session.start_transport_from_processor (); _session.start_transport_from_processor ();
} }
if (_stop_all) {
stop_all ();
_stop_all = false;
}
timepos_t start (start_sample); timepos_t start (start_sample);
timepos_t end (end_sample); timepos_t end (end_sample);
Temporal::Beats start_beats (start.beats()); Temporal::Beats start_beats (start.beats());
@ -1054,40 +1044,42 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
size_t max_chans = 0; size_t max_chans = 0;
bool first = false; bool first = false;
/* Now actually run all currently active triggers */ while (currently_playing) {
for (size_t n = 0; n < to_run.size(); ++n) { assert (currently_playing->state() >= Trigger::WaitingToStart);
Trigger& trigger (*all_triggers[to_run[n]]);
assert (trigger.state() >= Trigger::WaitingToStart);
boost::shared_ptr<Region> r = trigger.region();
sampleoffset_t dest_offset;
pframes_t trigger_samples;
Trigger::RunType rt; Trigger::RunType rt;
bool was_waiting_to_start = (trigger.state() == Trigger::WaitingToStart);
switch (trigger.state()) { switch (currently_playing->state()) {
case Trigger::WaitingToStop: case Trigger::WaitingToStop:
case Trigger::WaitingToStart: case Trigger::WaitingToStart:
case Trigger::WaitingForRetrigger: case Trigger::WaitingForRetrigger:
rt = trigger.maybe_compute_next_transition (start_beats, end_beats); rt = currently_playing->maybe_compute_next_transition (start_beats, end_beats);
break; break;
default: default:
rt = Trigger::RunAll; rt = Trigger::RunAll;
} }
if (rt == Trigger::RunEnd) { if (rt == Trigger::RunNone) {
/* nothing to do at this time, still waiting to start */
return;
}
boost::shared_ptr<Region> r = currently_playing->region();
sampleoffset_t dest_offset;
pframes_t trigger_samples;
const bool was_waiting_to_start = (currently_playing->state() == Trigger::WaitingToStart);
if (rt == Trigger::RunEnd) {
/* trigger will reach it's end somewhere within this /* trigger will reach it's end somewhere within this
* process cycle, so compute the number of samples it * process cycle, so compute the number of samples it
* should generate. * should generate.
*/ */
trigger_samples = nframes - (trigger.bang_samples - start_sample); trigger_samples = nframes - (currently_playing->bang_samples - start_sample);
dest_offset = 0; dest_offset = 0;
} else if (rt == Trigger::RunStart) { } else if (rt == Trigger::RunStart) {
@ -1097,7 +1089,7 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
* should end up, and the number of samples it should generate. * should end up, and the number of samples it should generate.
*/ */
dest_offset = std::max (samplepos_t (0), trigger.bang_samples - start_sample); dest_offset = std::max (samplepos_t (0), currently_playing->bang_samples - start_sample);
trigger_samples = nframes - dest_offset; trigger_samples = nframes - dest_offset;
} else if (rt == Trigger::RunAll) { } else if (rt == Trigger::RunAll) {
@ -1109,15 +1101,15 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
dest_offset = 0; dest_offset = 0;
trigger_samples = nframes; trigger_samples = nframes;
} else if (rt == Trigger::RunNone) { } else {
continue; /* NOTREACHED */
} }
if (was_waiting_to_start) { if (was_waiting_to_start) {
determine_next_trigger (trigger.index()); determine_next_trigger (currently_playing->index());
} }
AudioTrigger* at = dynamic_cast<AudioTrigger*> (&trigger); AudioTrigger* at = dynamic_cast<AudioTrigger*> (currently_playing);
if (at) { if (at) {
@ -1136,22 +1128,29 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
} }
if (trigger.state() == Trigger::Stopped) { if (currently_playing->state() == Trigger::Stopped) {
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 did stop\n", currently_playing->index()));
Trigger* nxt = get_next_trigger (); Trigger* nxt = get_next_trigger ();
if (nxt && !nxt->active()) { if (nxt) {
cerr << "next trigger will be " << nxt->index() << endl;
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 switching to %2\n", trigger.index(), nxt->index())); DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 switching to %2\n", currently_playing->index(), nxt->index()));
if (nxt->legato()) { if (nxt->legato()) {
nxt->set_legato_offset (trigger.current_pos()); nxt->set_legato_offset (currently_playing->current_pos());
} }
/* start it up */ /* start it up */
nxt->startup(); nxt->startup();
/* make sure we run it this process cycle */ currently_playing = nxt;
to_run.push_back (nxt->index());
} else {
currently_playing = 0;
} }
} else {
/* done */
break;
} }
} }
@ -1165,8 +1164,6 @@ TriggerBox::prepare_next (size_t current)
{ {
int nxt = determine_next_trigger (current); int nxt = determine_next_trigger (current);
cerr << "prepare next for " << current << " => " << nxt << endl;
if (nxt >= 0) { if (nxt >= 0) {
queue_implicit (all_triggers[nxt]); queue_implicit (all_triggers[nxt]);
} }
@ -1200,7 +1197,7 @@ TriggerBox::determine_next_trigger (size_t current)
} }
/* first switch: deal with the "special" cases where we either do /* first switch: deal with the "special" cases where we either do
* nothing or just repeat the current trigger. * nothing or just repeat the current trigger
*/ */
switch (all_triggers[current]->follow_action (which_follow_action)) { switch (all_triggers[current]->follow_action (which_follow_action)) {