triggerbox: start redesign based on only 1 trigger running at a time
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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&
|
||||
|
||||
Reference in New Issue
Block a user