triggerbox: start redesign based on only 1 trigger running at a time

This commit is contained in:
Paul Davis
2021-09-04 10:37:36 -06:00
parent cc32201e9c
commit 2cd88a67f1
2 changed files with 166 additions and 62 deletions

View File

@@ -76,9 +76,7 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
void bang ();
void unbang ();
/* explicitly call for the trigger to stop */
virtual void stop();
/* explicitly call for the trigger to start */
virtual void start();
virtual void stop (int next_to_run);
virtual void set_start (timepos_t const &) = 0;
virtual void set_end (timepos_t const &) = 0;
@@ -164,6 +162,8 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
void set_legato (bool yn);
bool legato () const { return _legato; }
void startup ();
protected:
TriggerBox& _box;
State _state;
@@ -184,6 +184,7 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
void request_state (State s);
virtual void retrigger() = 0;
virtual void set_usable_length () = 0;
void maybe_startup ();
};
class LIBARDOUR_API AudioTrigger : public Trigger {
@@ -255,22 +256,33 @@ class LIBARDOUR_API TriggerBox : public Processor
void stop_all ();
/* only valid when called by Triggers from within ::process_state_requests() */
size_t currently_running() const { return actually_running; }
void set_next (size_t which);
void queue_explict (Trigger*);
void queue_implicit (Trigger*);
Trigger* get_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 */
PBD::PCGRand _pcg;
/* These three are accessed (read/write) only from process() context */
/* These four are accessed (read/write) only from process() context */
void drop_triggers ();
void process_ui_trigger_requests ();
void process_midi_trigger_requests (BufferSet&);
void set_next_trigger (size_t n);
int determine_next_trigger (size_t n);
void note_on (int note_number, int velocity);
void note_off (int note_number, int velocity);

View File

@@ -47,7 +47,7 @@ Trigger::Trigger (size_t n, TriggerBox& b)
, _follow_action { NextTrigger, Stop }
, _follow_action_probability (100)
, _quantization (Temporal::BBT_Offset (0, 1, 0))
, _legato (false)
, _legato (true)
{
}
@@ -159,24 +159,39 @@ Trigger::quantization () const
}
void
Trigger::stop ()
Trigger::stop (int next)
{
_next_trigger = -1;
_next_trigger = next;
request_state (Stopped);
}
void
Trigger::start ()
{
request_state (Running);
}
void
Trigger::request_state (State s)
{
_requested_state.store (s);
}
void
Trigger::startup()
{
_state = WaitingToStart;
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 ()
{
@@ -188,14 +203,14 @@ Trigger::process_state_requests ()
switch (new_state) {
case Stopped:
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 -> %3\n", index(), enum_2_string (_state), enum_2_string (WaitingToStop)));
_state = WaitingToStop;
PropertyChanged (ARDOUR::Properties::running);
if (_state != WaitingToStop) {
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 -> %3\n", index(), enum_2_string (_state), enum_2_string (WaitingToStop)));
_state = WaitingToStop;
PropertyChanged (ARDOUR::Properties::running);
}
break;
case Running:
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 -> %3\n", index(), enum_2_string (_state), enum_2_string (WaitingToStart)));
_state = WaitingToStart;
PropertyChanged (ARDOUR::Properties::running);
maybe_startup ();
break;
default:
break;
@@ -235,8 +250,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)));
_state = WaitingToStart;
PropertyChanged (ARDOUR::Properties::running);
maybe_startup ();
break;
case WaitingToStart:
@@ -306,11 +320,13 @@ Trigger::maybe_compute_next_transition (Temporal::Beats const & start, Temporal:
} else if (_state == WaitingToStart) {
retrigger ();
_state = Running;
_box.prepare_next (_index);
PropertyChanged (ARDOUR::Properties::running);
return RunStart;
} else if (_state == WaitingForRetrigger) {
retrigger ();
_state = Running;
_box.prepare_next (_index);
PropertyChanged (ARDOUR::Properties::running);
return RunAll;
}
@@ -672,8 +688,6 @@ AudioTrigger::run (BufferSet& bufs, pframes_t nframes, pframes_t dest_offset, bo
pframes_t this_read = (pframes_t) std::min ((samplecnt_t) nframes, (last_sample - read_index));
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 grab %2 @ %3 dest offset %4\n", index(), this_read, read_index, dest_offset));
for (size_t chn = 0; chn < ar->n_channels(); ++chn) {
size_t channel = chn % data.size();
@@ -748,6 +762,9 @@ TriggerBox::TriggerBox (Session& s, DataType dt)
, _bang_queue (1024)
, _unbang_queue (1024)
, _data_type (dt)
, actually_running (0)
, explicit_queue (64)
, implicit_queue (64)
{
/* default number of possible triggers. call ::add_trigger() to increase */
@@ -770,6 +787,42 @@ TriggerBox::TriggerBox (Session& s, DataType dt)
midi_trigger_map.insert (midi_trigger_map.end(), std::make_pair (uint8_t (69), 9));
}
void
TriggerBox::queue_explict (Trigger* t)
{
cerr << "explQ " << t->index() << endl;
explicit_queue.write (&t, 1);
implicit_queue.reset ();
}
void
TriggerBox::queue_implicit (Trigger* t)
{
if (explicit_queue.read_space() == 0) {
cerr << "implQ " << t->index() << endl;
implicit_queue.write (&t, 1);
}
}
Trigger*
TriggerBox::get_next_trigger ()
{
Trigger* r;
if (explicit_queue.read (&r, 1) == 1) {
DEBUG_TRACE (DEBUG::Triggers, string_compose ("next trigger from explicit queue = %1\n", r->index()));
return r;
}
if (implicit_queue.read (&r, 1) == 1) {
DEBUG_TRACE (DEBUG::Triggers, string_compose ("next trigger from implicit queue = %1\n", r->index()));
return r;
}
DEBUG_TRACE (DEBUG::Triggers, "no next trigger\n");
return 0;
}
int
TriggerBox::set_from_path (size_t slot, std::string const & path)
{
@@ -829,7 +882,7 @@ TriggerBox::stop_all ()
/* XXX needs to be done with mutex or via thread-safe queue */
for (size_t n = 0; n < all_triggers.size(); ++n) {
all_triggers[n]->stop ();
all_triggers[n]->stop (-1);
}
}
@@ -936,6 +989,28 @@ 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 */
std::vector<size_t> to_run;
for (size_t n = 0; n < all_triggers.size(); ++n) {
@@ -1039,7 +1114,7 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
}
if (was_waiting_to_start) {
set_next_trigger (trigger.index());
determine_next_trigger (trigger.index());
}
AudioTrigger* at = dynamic_cast<AudioTrigger*> (&trigger);
@@ -1063,23 +1138,19 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
if (trigger.state() == Trigger::Stopped) {
cerr << "stopped, check trigger " << trigger.next_trigger() << std::endl;
Trigger* nxt = get_next_trigger ();
if (trigger.next_trigger() != -1) {
if (nxt && !nxt->active()) {
cerr << "next trigger will be " << nxt->index() << endl;
int nxt = trigger.next_trigger();
if (nxt >= 0 && (size_t) nxt < all_triggers.size() && !all_triggers[nxt]->active()) {
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 switching to %2\n", trigger.index(), nxt));
if (all_triggers[nxt]->legato()) {
all_triggers[nxt]->set_legato_offset (trigger.current_pos());
}
/* start it up */
all_triggers[nxt]->bang ();
all_triggers[nxt]->process_state_requests ();
/* make sure we run it this process cycle */
to_run.push_back (nxt);
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 switching to %2\n", trigger.index(), nxt->index()));
if (nxt->legato()) {
nxt->set_legato_offset (trigger.current_pos());
}
/* start it up */
nxt->startup();
/* make sure we run it this process cycle */
to_run.push_back (nxt->index());
}
}
}
@@ -1090,17 +1161,35 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
}
void
TriggerBox::set_next_trigger (size_t current)
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]);
}
}
int
TriggerBox::determine_next_trigger (size_t current)
{
size_t n;
size_t runnable = 0;
/* count number of triggers that can actually be run (i.e. they have a region) */
for (size_t n = 0; n < all_triggers.size(); ++n) {
if (all_triggers[n]->region()) {
runnable++;
}
}
/* decide which of the two follow actions we're going to use (based on
* random number and the probability setting)
*/
int which_follow_action;
int r = _pcg.rand (100); // 0 .. 99
@@ -1110,27 +1199,33 @@ TriggerBox::set_next_trigger (size_t current)
which_follow_action = 1;
}
/* first switch: deal with the "special" cases where we either do
* nothing or just repeat the current trigger.
*/
switch (all_triggers[current]->follow_action (which_follow_action)) {
case Trigger::Stop:
all_triggers[current]->set_next_trigger (-1);
return;
return -1;
case Trigger::QueuedTrigger:
/* XXX implement me */
return;
return -1;
default:
if (runnable == 1) {
all_triggers[current]->set_next_trigger (current);
return;
/* there's only 1 runnable trigger, so the "next" one
is the same as the current one.
*/
return current;
}
}
/* second switch: handle the "real" follow actions */
switch (all_triggers[current]->follow_action (which_follow_action)) {
case Trigger::Again:
all_triggers[current]->set_next_trigger (current);
return;
return current;
case Trigger::NextTrigger:
n = current;
@@ -1146,8 +1241,7 @@ TriggerBox::set_next_trigger (size_t current)
}
if (all_triggers[n]->region() && !all_triggers[n]->active()) {
all_triggers[current]->set_next_trigger (n);
return;
return n;
}
}
break;
@@ -1165,8 +1259,7 @@ TriggerBox::set_next_trigger (size_t current)
}
if (all_triggers[n]->region() && !all_triggers[n]->active ()) {
all_triggers[current]->set_next_trigger (n);
return;
return n;
}
}
break;
@@ -1174,16 +1267,14 @@ TriggerBox::set_next_trigger (size_t current)
case Trigger::FirstTrigger:
for (n = 0; n < all_triggers.size(); ++n) {
if (all_triggers[n]->region() && !all_triggers[n]->active ()) {
all_triggers[current]->set_next_trigger (n);
return;
return n;
}
}
break;
case Trigger::LastTrigger:
for (int i = all_triggers.size() - 1; i >= 0; --i) {
if (all_triggers[i]->region() && !all_triggers[i]->active ()) {
all_triggers[current]->set_next_trigger (i);
return;
return i;
}
}
break;
@@ -1199,8 +1290,8 @@ TriggerBox::set_next_trigger (size_t current)
}
break;
}
all_triggers[current]->set_next_trigger (n);
return;
return n;
case Trigger::OtherTrigger:
while (true) {
@@ -1216,8 +1307,8 @@ TriggerBox::set_next_trigger (size_t current)
}
break;
}
all_triggers[current]->set_next_trigger (n);
return;
return n;
/* NOTREACHED */
case Trigger::Stop:
@@ -1225,7 +1316,8 @@ TriggerBox::set_next_trigger (size_t current)
break;
}
all_triggers[current]->set_next_trigger (current);
return current;
}
XMLNode&