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);
bool legato () const { return _legato; }
void startup ();
virtual void startup ();
protected:
TriggerBox& _box;
@ -206,6 +206,7 @@ class LIBARDOUR_API AudioTrigger : public Trigger {
timepos_t natural_length() const; /* offset from start of data */
int set_region (boost::shared_ptr<Region>);
void startup ();
XMLNode& get_state (void);
int set_state (const XMLNode&, int version);
@ -254,26 +255,28 @@ class LIBARDOUR_API TriggerBox : public Processor
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() */
size_t currently_running() const { return actually_running; }
bool currently_running() const { return currently_playing; }
void set_next (size_t which);
void queue_explict (Trigger*);
void queue_implicit (Trigger*);
Trigger* get_next_trigger ();
Trigger* peak_next_trigger ();
void prepare_next (size_t current);
private:
PBD::RingBuffer<Trigger*> _bang_queue;
PBD::RingBuffer<Trigger*> _unbang_queue;
DataType _data_type;
size_t actually_running;
Glib::Threads::RWLock trigger_lock; /* protects all_triggers */
Triggers all_triggers;
PBD::RingBuffer<Trigger*> explicit_queue; /* user queued triggers */
PBD::RingBuffer<Trigger*> implicit_queue; /* follow-action queued triggers */
Trigger* currently_playing;
std::atomic<bool> _stop_all;
PBD::PCGRand _pcg;
@ -283,6 +286,7 @@ class LIBARDOUR_API TriggerBox : public Processor
void process_ui_trigger_requests ();
void process_midi_trigger_requests (BufferSet&);
int determine_next_trigger (size_t n);
void stop_all ();
void note_on (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)
, _unbang (0)
, _index (n)
, _next_trigger (-1)
, _launch_style (Toggle)
, _follow_action { NextTrigger, Stop }
, _follow_action_probability (100)
@ -161,7 +160,6 @@ Trigger::quantization () const
void
Trigger::stop (int next)
{
_next_trigger = next;
request_state (Stopped);
}
@ -178,20 +176,6 @@ Trigger::startup()
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
Trigger::process_state_requests ()
{
@ -210,7 +194,7 @@ Trigger::process_state_requests ()
}
break;
case Running:
maybe_startup ();
_box.queue_explict (this);
break;
default:
break;
@ -250,7 +234,7 @@ Trigger::process_state_requests ()
case Stopped:
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;
case WaitingToStart:
@ -261,7 +245,6 @@ Trigger::process_state_requests ()
}
}
while ((x = _unbang.load ())) {
_unbang.fetch_sub (1);
@ -342,14 +325,6 @@ Trigger::maybe_compute_next_transition (Temporal::Beats const & start, Temporal:
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)
@ -371,6 +346,13 @@ AudioTrigger::~AudioTrigger ()
}
}
void
AudioTrigger::startup ()
{
Trigger::startup ();
retrigger ();
}
XMLNode&
AudioTrigger::get_state (void)
{
@ -673,6 +655,7 @@ void
AudioTrigger::retrigger ()
{
read_index = _start_offset + _legato_offset;
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 retriggered to %2\n", _index, read_index));
}
int
@ -708,7 +691,7 @@ AudioTrigger::run (BufferSet& bufs, pframes_t nframes, pframes_t dest_offset, bo
/* 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;
dest_offset += this_read;
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) {
size_t channel = chn % data.size();
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);
}
}
@ -762,9 +745,10 @@ TriggerBox::TriggerBox (Session& s, DataType dt)
, _bang_queue (1024)
, _unbang_queue (1024)
, _data_type (dt)
, actually_running (0)
, explicit_queue (64)
, implicit_queue (64)
, currently_playing (0)
, _stop_all (false)
{
/* default number of possible triggers. call ::add_trigger() to increase */
@ -790,20 +774,45 @@ TriggerBox::TriggerBox (Session& s, DataType dt)
void
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);
implicit_queue.reset ();
if (currently_playing) {
currently_playing->unbang ();
}
}
void
TriggerBox::queue_implicit (Trigger* t)
{
assert (t);
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);
}
}
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*
TriggerBox::get_next_trigger ()
{
@ -819,7 +828,6 @@ TriggerBox::get_next_trigger ()
return r;
}
DEBUG_TRACE (DEBUG::Triggers, "no next trigger\n");
return 0;
}
@ -876,6 +884,12 @@ TriggerBox::~TriggerBox ()
drop_triggers ();
}
void
TriggerBox::request_stop_all ()
{
_stop_all = true;
}
void
TriggerBox::stop_all ()
{
@ -884,6 +898,9 @@ TriggerBox::stop_all ()
for (size_t n = 0; n < all_triggers.size(); ++n) {
all_triggers[n]->stop (-1);
}
implicit_queue.reset ();
explicit_queue.reset ();
}
void
@ -989,25 +1006,6 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
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 */
@ -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) {
all_triggers[n]->process_state_requests ();
}
/* now recheck all states so that we know if we have any
* active triggers.
*/
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 (!currently_playing) {
if ((currently_playing = get_next_trigger ()) != 0) {
currently_playing->startup ();
}
}
if (to_run.empty()) {
/* 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.
*/
if (!currently_playing) {
return;
}
@ -1046,6 +1031,11 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
_session.start_transport_from_processor ();
}
if (_stop_all) {
stop_all ();
_stop_all = false;
}
timepos_t start (start_sample);
timepos_t end (end_sample);
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;
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;
bool was_waiting_to_start = (trigger.state() == Trigger::WaitingToStart);
switch (trigger.state()) {
switch (currently_playing->state()) {
case Trigger::WaitingToStop:
case Trigger::WaitingToStart:
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;
default:
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
* process cycle, so compute the number of samples it
* should generate.
*/
trigger_samples = nframes - (trigger.bang_samples - start_sample);
trigger_samples = nframes - (currently_playing->bang_samples - start_sample);
dest_offset = 0;
} 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.
*/
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;
} else if (rt == Trigger::RunAll) {
@ -1109,15 +1101,15 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
dest_offset = 0;
trigger_samples = nframes;
} else if (rt == Trigger::RunNone) {
continue;
} else {
/* NOTREACHED */
}
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) {
@ -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 ();
if (nxt && !nxt->active()) {
cerr << "next trigger will be " << nxt->index() << endl;
if (nxt) {
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()) {
nxt->set_legato_offset (trigger.current_pos());
nxt->set_legato_offset (currently_playing->current_pos());
}
/* start it up */
nxt->startup();
/* make sure we run it this process cycle */
to_run.push_back (nxt->index());
currently_playing = nxt;
} else {
currently_playing = 0;
}
} else {
/* done */
break;
}
}
@ -1165,8 +1164,6 @@ TriggerBox::prepare_next (size_t current)
{
int nxt = determine_next_trigger (current);
cerr << "prepare next for " << current << " => " << nxt << endl;
if (nxt >= 0) {
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
* nothing or just repeat the current trigger.
* nothing or just repeat the current trigger
*/
switch (all_triggers[current]->follow_action (which_follow_action)) {