triggerbox: more significant design clean-ups, and follow actions now work (basically)

This commit is contained in:
Paul Davis 2021-08-10 20:35:39 -06:00
parent 8cea19dd25
commit edbafd9f5b
3 changed files with 307 additions and 161 deletions

View file

@ -58,8 +58,9 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
Stopped = 1, Stopped = 1,
WaitingToStart = 2, WaitingToStart = 2,
Running = 3, Running = 3,
WaitingToStop = 4, WaitingForRetrigger = 4,
Stopping = 5 WaitingToStop = 5,
Stopping = 6
}; };
Trigger (size_t index, TriggerBox&); Trigger (size_t index, TriggerBox&);
@ -131,7 +132,17 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
ChangeTriggers = 0x8 ChangeTriggers = 0x8
}; };
bool maybe_compute_start_or_stop (Temporal::Beats const & start, Temporal::Beats const & end); enum RunType {
RunEnd,
RunStart,
RunAll,
RunNone,
};
RunType maybe_compute_next_transition (Temporal::Beats const & start, Temporal::Beats const & end);
void set_next_trigger (int n);
int next_trigger() const { return _next_trigger; }
protected: protected:
TriggerBox& _box; TriggerBox& _box;
@ -140,6 +151,7 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
std::atomic<int> _bang; std::atomic<int> _bang;
std::atomic<int> _unbang; std::atomic<int> _unbang;
size_t _index; size_t _index;
int _next_trigger;
LaunchStyle _launch_style; LaunchStyle _launch_style;
FollowAction _follow_action; FollowAction _follow_action;
boost::shared_ptr<Region> _region; boost::shared_ptr<Region> _region;
@ -155,7 +167,7 @@ class LIBARDOUR_API AudioTrigger : public Trigger {
AudioTrigger (size_t index, TriggerBox&); AudioTrigger (size_t index, TriggerBox&);
~AudioTrigger (); ~AudioTrigger ();
RunResult run (AudioBuffer&, uint32_t channel, pframes_t& nframes, pframes_t offset, bool first); int run (BufferSet&, pframes_t nframes, pframes_t offset, bool first);
void set_length (timecnt_t const &); void set_length (timecnt_t const &);
timecnt_t current_length() const; timecnt_t current_length() const;
@ -168,7 +180,7 @@ class LIBARDOUR_API AudioTrigger : public Trigger {
private: private:
std::vector<Sample*> data; std::vector<Sample*> data;
std::vector<samplecnt_t> read_index; samplecnt_t read_index;
samplecnt_t data_length; samplecnt_t data_length;
void drop_data (); void drop_data ();
@ -212,13 +224,11 @@ class LIBARDOUR_API TriggerBox : public Processor
Triggers all_triggers; Triggers all_triggers;
/* These three are accessed (read/write) only from process() context */ /* These three are accessed (read/write) only from process() context */
Triggers pending_on_triggers;
Triggers pending_off_triggers;
Triggers active_triggers;
void drop_triggers (); void drop_triggers ();
void process_ui_trigger_requests (); void process_ui_trigger_requests ();
void process_midi_trigger_requests (BufferSet&); void process_midi_trigger_requests (BufferSet&);
void set_next_trigger (size_t n);
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

@ -843,6 +843,7 @@ setup_enum_writer ()
REGISTER_CLASS_ENUM (Trigger, Stopped); REGISTER_CLASS_ENUM (Trigger, Stopped);
REGISTER_CLASS_ENUM (Trigger, WaitingToStart); REGISTER_CLASS_ENUM (Trigger, WaitingToStart);
REGISTER_CLASS_ENUM (Trigger, Running); REGISTER_CLASS_ENUM (Trigger, Running);
REGISTER_CLASS_ENUM (Trigger, WaitingForRetrigger);
REGISTER_CLASS_ENUM (Trigger, WaitingToStop); REGISTER_CLASS_ENUM (Trigger, WaitingToStop);
REGISTER_CLASS_ENUM (Trigger, Stopping); REGISTER_CLASS_ENUM (Trigger, Stopping);
REGISTER (_TriggerState); REGISTER (_TriggerState);

View file

@ -1,4 +1,5 @@
#include <iostream> #include <iostream>
#include <cstdlib>
#include <glibmm.h> #include <glibmm.h>
@ -39,8 +40,9 @@ Trigger::Trigger (size_t n, TriggerBox& b)
, _bang (0) , _bang (0)
, _unbang (0) , _unbang (0)
, _index (n) , _index (n)
, _launch_style (Loop) , _next_trigger (-1)
, _follow_action (Stop) , _launch_style (Toggle)
, _follow_action (NextTrigger)
, _quantization (Temporal::BBT_Offset (0, 1, 0)) , _quantization (Temporal::BBT_Offset (0, 1, 0))
{ {
} }
@ -49,7 +51,6 @@ void
Trigger::bang () Trigger::bang ()
{ {
_bang.fetch_add (1); _bang.fetch_add (1);
std::cerr << "trigger " << index() << " banged to " << _bang.load() << std::endl;
} }
void void
@ -123,9 +124,9 @@ Trigger::process_state_requests ()
{ {
State new_state = _requested_state.exchange (None); State new_state = _requested_state.exchange (None);
if (new_state == _state) { if (new_state != None && new_state != _state) {
return;
} DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 requested state %2\n", index(), enum_2_string (new_state)));
switch (new_state) { switch (new_state) {
case Stopped: case Stopped:
@ -139,6 +140,7 @@ Trigger::process_state_requests ()
default: default:
break; break;
} }
}
/* now check bangs/unbangs */ /* now check bangs/unbangs */
@ -148,6 +150,8 @@ Trigger::process_state_requests ()
_bang.fetch_sub (1); _bang.fetch_sub (1);
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 handling bang with state = %2\n", index(), enum_2_string (_state)));
switch (_state) { switch (_state) {
case None: case None:
abort (); abort ();
@ -156,14 +160,14 @@ Trigger::process_state_requests ()
case Running: case Running:
switch (launch_style()) { switch (launch_style()) {
case Loop: case Loop:
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 retrigger\n", index())); DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 -> %3\n", index(), enum_2_string (Running), enum_2_string (WaitingForRetrigger)));
retrigger (); _state = WaitingForRetrigger;
break; break;
case Gate: case Gate:
case Toggle: case Toggle:
case Repeat: case Repeat:
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 -> %3\n", index(), enum_2_string (Running), enum_2_string (Stopped))); DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 %2 -> %3\n", index(), enum_2_string (Running), enum_2_string (Stopped)));
_state = Stopped; _state = WaitingToStop;
} }
break; break;
@ -173,11 +177,8 @@ Trigger::process_state_requests ()
break; break;
case WaitingToStart: case WaitingToStart:
break;
case WaitingToStop: case WaitingToStop:
break; case WaitingForRetrigger:
case Stopping: case Stopping:
break; break;
} }
@ -194,34 +195,66 @@ Trigger::process_state_requests ()
} }
} }
bool Trigger::RunType
Trigger::maybe_compute_start_or_stop (Temporal::Beats const & start, Temporal::Beats const & end) Trigger::maybe_compute_next_transition (Temporal::Beats const & start, Temporal::Beats const & end)
{ {
/* In these states, we are not waiting for a transition */
switch (_state) {
case Stopped:
return RunNone;
case Running:
return RunAll;
case Stopping:
return RunAll;
default:
break;
}
timepos_t ev_time (Temporal::BeatTime); timepos_t ev_time (Temporal::BeatTime);
if (_quantization.bars == 0) { if (_quantization.bars == 0) {
ev_time = timepos_t (start.snap_to (Temporal::Beats (_quantization.beats, _quantization.ticks))); ev_time = timepos_t (start.snap_to (Temporal::Beats (_quantization.beats, _quantization.ticks)));
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 quantized with %5 start at %2, sb %3 eb %4\n", index(), ev_time, start, end, _quantization)); DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 quantized with %5 start at %2, sb %3 eb %4\n", index(), ev_time.beats(), start, end, _quantization));
} else { } else {
/* XXX not yet handled */ /* XXX not yet handled */
} }
if (ev_time.beats() >= start && ev_time < end) { if (ev_time.beats() >= start && ev_time < end) {
bang_samples = ev_time.samples(); bang_samples = ev_time.samples();
bang_beats = ev_time.beats (); bang_beats = ev_time.beats ();
if (_state == WaitingToStop) { if (_state == WaitingToStop) {
_state = Stopping; _state = Stopping;
} else { return RunEnd;
} else if (_state == WaitingToStart) {
retrigger (); retrigger ();
_state = Running; _state = Running;
return RunStart;
} else if (_state == WaitingForRetrigger) {
retrigger ();
_state = Running;
return RunAll;
}
} else {
if (_state == WaitingForRetrigger) {
/* retrigger time has not been reached, just continue
to play normally until then.
*/
return RunAll;
}
} }
return true; return RunNone;
} }
return false; void
Trigger::set_next_trigger (int n)
{
_next_trigger = n;
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 : set %2 as next\n", index(), n));
} }
/*--------------------*/ /*--------------------*/
@ -229,6 +262,7 @@ Trigger::maybe_compute_start_or_stop (Temporal::Beats const & start, Temporal::B
AudioTrigger::AudioTrigger (size_t n, TriggerBox& b) AudioTrigger::AudioTrigger (size_t n, TriggerBox& b)
: Trigger (n, b) : Trigger (n, b)
, data (0) , data (0)
, read_index (0)
, data_length (0) , data_length (0)
{ {
} }
@ -261,8 +295,6 @@ AudioTrigger::set_length (timecnt_t const & newlen)
return; return;
} }
std::cerr << " sl to " << newlen << " from " << _region->length() << endl;
/* offline stretch */ /* offline stretch */
/* study */ /* study */
@ -281,7 +313,6 @@ AudioTrigger::set_length (timecnt_t const & newlen)
} else { } else {
/* XXX what to use for position ??? */ /* XXX what to use for position ??? */
const timecnt_t dur = TempoMap::use()->convert_duration (newlen, timepos_t (0), AudioTime); const timecnt_t dur = TempoMap::use()->convert_duration (newlen, timepos_t (0), AudioTime);
std::cerr << "new dur = " << dur << " S " << dur.samples() << " vs " << data_length << endl;
new_ratio = (double) dur.samples() / data_length; new_ratio = (double) dur.samples() / data_length;
} }
@ -428,7 +459,6 @@ AudioTrigger::load_data (boost::shared_ptr<AudioRegion> ar)
try { try {
for (uint32_t n = 0; n < nchans; ++n) { for (uint32_t n = 0; n < nchans; ++n) {
data.push_back (new Sample[data_length]); data.push_back (new Sample[data_length]);
read_index.push_back (0);
ar->read (data[n], 0, data_length, n); ar->read (data[n], 0, data_length, n);
} }
} catch (...) { } catch (...) {
@ -442,63 +472,69 @@ AudioTrigger::load_data (boost::shared_ptr<AudioRegion> ar)
void void
AudioTrigger::retrigger () AudioTrigger::retrigger ()
{ {
for (std::vector<samplecnt_t>::iterator ri = read_index.begin(); ri != read_index.end(); ++ri) { read_index = 0;
(*ri) = 0;
}
} }
Trigger::RunResult int
AudioTrigger::run (AudioBuffer& buf, uint32_t channel, pframes_t& nframes, pframes_t dest_offset, bool first) AudioTrigger::run (BufferSet& bufs, pframes_t nframes, pframes_t dest_offset, bool first)
{ {
if (read_index[channel] >= data_length) { boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(_region);
return RemoveTrigger;
}
if (!active()) { assert (ar);
return RemoveTrigger; assert (active());
}
while (nframes) {
channel %= data.size(); pframes_t this_read = (pframes_t) std::min ((samplecnt_t) nframes, (data_length - read_index));
pframes_t nf = (pframes_t) std::min ((samplecnt_t) nframes, (data_length - read_index[channel])); for (size_t chn = 0; chn < ar->n_channels(); ++chn) {
Sample* src = data[channel] + read_index[channel];
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 running with nf %2\n", index(), nf)); size_t channel = chn % data.size();
Sample* src = data[channel] + read_index;
AudioBuffer& buf (bufs.get_audio (chn));
if (first) { if (first) {
buf.read_from (src, nf, dest_offset); buf.read_from (src, this_read, dest_offset);
} else { } else {
buf.accumulate_from (src, nf); buf.accumulate_from (src, this_read, dest_offset);
}
} }
read_index[channel] += nf; read_index += this_read;
if ((nframes - nf) != 0) { if (read_index >= data_length) {
/* did not get all samples, must have reached the end, figure out what do to */
nframes = nf;
return at_end ();
}
return Relax; /* We reached the end */
}
Trigger::RunResult if (_launch_style == Loop) {
AudioTrigger::at_end () nframes -= this_read;
{ dest_offset += this_read;
switch (launch_style()) { DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 was reached end, but set to loop, so retrigger\n", index()));
case Trigger::Loop:
retrigger (); retrigger ();
return ReadMore; /* and go around again */
default: continue;
} else {
if (this_read < nframes) {
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()));
buf.silence (nframes - this_read, dest_offset + this_read);
}
}
_state = Stopped;
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 was reached end, now stopped\n", index()));
break; break;
} }
if (follow_action() == Stop) {
return RunResult (RemoveTrigger|FillSilence);
} }
return RunResult (RemoveTrigger|ChangeTriggers|FillSilence); nframes -= this_read;
}
return 0;
} }
/**************/ /**************/
@ -590,8 +626,8 @@ TriggerBox::stop_all ()
{ {
/* XXX needs to be done with mutex or via thread-safe queue */ /* XXX needs to be done with mutex or via thread-safe queue */
for (Triggers::iterator t = active_triggers.begin(); t != active_triggers.end(); ++t) { for (size_t n = 0; n < all_triggers.size(); ++n) {
(*t)->stop (); all_triggers[n]->stop ();
} }
} }
@ -687,7 +723,6 @@ TriggerBox::process_midi_trigger_requests (BufferSet& bufs)
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)
{ {
if (start_sample < 0) { if (start_sample < 0) {
/* we can't do anything under these conditions (related to /* we can't do anything under these conditions (related to
latency compensation latency compensation
@ -697,7 +732,7 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
process_midi_trigger_requests (bufs); process_midi_trigger_requests (bufs);
size_t run_cnt = 0; std::vector<size_t> to_run;
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 ();
@ -710,14 +745,15 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
case Trigger::Running: case Trigger::Running:
case Trigger::WaitingToStart: case Trigger::WaitingToStart:
case Trigger::WaitingToStop: case Trigger::WaitingToStop:
case Trigger::WaitingForRetrigger:
case Trigger::Stopping: case Trigger::Stopping:
run_cnt++; to_run.push_back (n);
default: default:
break; break;
} }
} }
if (run_cnt == 0) { if (to_run.empty()) {
/* this saves us from the cost of the tempo map lookups. /* this saves us from the cost of the tempo map lookups.
XXX if these were passed in to ::run(), we could possibly XXX if these were passed in to ::run(), we could possibly
skip this condition. skip this condition.
@ -736,55 +772,37 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
Temporal::Beats start_beats (start.beats()); Temporal::Beats start_beats (start.beats());
Temporal::Beats end_beats (end.beats()); Temporal::Beats end_beats (end.beats());
Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use()); Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use());
bool will_start = false;
bool will_stop = false;
for (size_t n = 0; n < all_triggers.size(); ++n) {
}
size_t max_chans = 0; size_t max_chans = 0;
Trigger::RunResult rr; Trigger::RunResult rr;
bool first = false; bool first = false;
/* Now actually run all currently active triggers */ /* Now actually run all currently active triggers */
for (size_t n = 0; n < all_triggers.size(); ++n) { for (size_t n = 0; n < to_run.size(); ++n) {
Trigger& trigger (*all_triggers[n]); Trigger& trigger (*all_triggers[to_run[n]]);
if (trigger.state() < Trigger::WaitingToStart) { assert (trigger.state() >= Trigger::WaitingToStart);
continue;
}
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 might run, state %2\n", trigger.index(), enum_2_string (trigger.state())));
boost::shared_ptr<Region> r = trigger.region(); boost::shared_ptr<Region> r = trigger.region();
sampleoffset_t dest_offset; sampleoffset_t dest_offset;
pframes_t trigger_samples; pframes_t trigger_samples;
Trigger::RunType rt;
bool was_waiting_to_start = (trigger.state() == Trigger::WaitingToStart);
switch (trigger.state()) { switch (trigger.state()) {
case Trigger::None:
case Trigger::Stopped:
abort ();
break;
case Trigger::Running:
case Trigger::Stopping:
break;
case Trigger::WaitingToStop: case Trigger::WaitingToStop:
will_stop = trigger.maybe_compute_start_or_stop (start_beats, end_beats);
break;
case Trigger::WaitingToStart: case Trigger::WaitingToStart:
will_start = trigger.maybe_compute_start_or_stop (start_beats, end_beats); case Trigger::WaitingForRetrigger:
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 waiting to start, will start? %2\n", trigger.index(), will_start)); rt = trigger.maybe_compute_next_transition (start_beats, end_beats);
break; break;
default:
rt = Trigger::RunAll;
} }
if (will_stop) { 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
@ -794,7 +812,7 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
trigger_samples = nframes - (trigger.bang_samples - start_sample); trigger_samples = nframes - (trigger.bang_samples - start_sample);
dest_offset = 0; dest_offset = 0;
} else if (will_start) { } else if (rt == Trigger::RunStart) {
/* trigger will start somewhere within this process /* trigger will start somewhere within this process
* cycle. Compute the sample offset where any audio * cycle. Compute the sample offset where any audio
@ -804,7 +822,7 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
dest_offset = std::max (samplepos_t (0), trigger.bang_samples - start_sample); dest_offset = std::max (samplepos_t (0), trigger.bang_samples - start_sample);
trigger_samples = nframes - dest_offset; trigger_samples = nframes - dest_offset;
} else { } else if (rt == Trigger::RunAll) {
/* trigger is just running normally, and will fill /* trigger is just running normally, and will fill
* buffers entirely. * buffers entirely.
@ -812,6 +830,13 @@ 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) {
continue;
}
if (was_waiting_to_start) {
set_next_trigger (trigger.index());
} }
AudioTrigger* at = dynamic_cast<AudioTrigger*> (&trigger); AudioTrigger* at = dynamic_cast<AudioTrigger*> (&trigger);
@ -820,46 +845,34 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r); boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
const size_t nchans = ar->n_channels (); const size_t nchans = ar->n_channels ();
pframes_t nf = trigger_samples;
max_chans = std::max (max_chans, nchans); max_chans = std::max (max_chans, nchans);
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 run with ts %2 do %3\n", trigger.index(), trigger_samples, dest_offset)); at->run (bufs, trigger_samples, dest_offset, first);
read_more:
for (uint32_t chan = 0; chan < nchans; ++chan) {
/* we assume the result will be the same for all channels */
AudioBuffer& buf (bufs.get_audio (chan));
rr = at->run (buf, chan, nf, dest_offset, first);
/* nf is now the number of samples that were
* actually processed/generated/written
*/
if (rr & Trigger::FillSilence) {
buf.silence (trigger_samples - nf, nf + dest_offset);
}
}
first = false; first = false;
if (rr == Trigger::ReadMore) {
trigger_samples -= nf;
goto read_more;
}
} else { } else {
/* XXX MIDI triggers to be implemented */ /* XXX MIDI triggers to be implemented */
} }
if (rr & Trigger::ChangeTriggers) { if (trigger.state() == Trigger::Stopped) {
/* XXX do this! */
std::cerr << "Should change triggers!\n"; if (trigger.follow_action() != Trigger::Stop) {
int nxt = trigger.next_trigger();
if (nxt >= 0 && nxt < all_triggers.size() && !all_triggers[nxt]->active()) {
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 switching to %2\n", trigger.index(), nxt));
/* 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);
}
}
} }
} }
@ -868,6 +881,128 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
bufs.set_count (cc); bufs.set_count (cc);
} }
void
TriggerBox::set_next_trigger (size_t current)
{
size_t n;
size_t runnable;
for (size_t n = 0; n < all_triggers.size(); ++n) {
if (all_triggers[n]->region()) {
runnable++;
}
}
switch (all_triggers[current]->follow_action()) {
case Trigger::Stop:
return;
case Trigger::QueuedTrigger:
/* XXX implement me */
return;
default:
if (runnable == 1) {
all_triggers[current]->set_next_trigger (current);
return;
}
}
switch (all_triggers[current]->follow_action()) {
case Trigger::NextTrigger:
n = current;
while (true) {
++n;
if (n >= all_triggers.size()) {
n = 0;
}
if (n == current) {
break;
}
if (all_triggers[n]->region() && !all_triggers[n]->active()) {
all_triggers[current]->set_next_trigger (n);
return;
}
}
break;
case Trigger::PrevTrigger:
n = current;
while (true) {
if (n == 0) {
n = all_triggers.size() - 1;
} else {
n -= 1;
}
if (n == current) {
break;
}
if (all_triggers[n]->region() && !all_triggers[n]->active ()) {
all_triggers[current]->set_next_trigger (n);
return;
}
}
break;
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;
}
}
break;
case Trigger::LastTrigger:
for (n = all_triggers.size() - 1; n >= 0; --n) {
if (all_triggers[n]->region() && !all_triggers[n]->active ()) {
all_triggers[current]->set_next_trigger (n);
return;
}
}
break;
case Trigger::AnyTrigger:
while (true) {
n = random() % all_triggers.size();
if (!all_triggers[n]->region()) {
continue;
}
if (all_triggers[n]->active()) {
continue;
}
break;
}
all_triggers[current]->set_next_trigger (n);
return;
case Trigger::OtherTrigger:
while (true) {
n = random() % all_triggers.size();
if ((size_t) n == current) {
continue;
}
if (!all_triggers[n]->region()) {
continue;
}
if (all_triggers[n]->active()) {
continue;
}
break;
}
all_triggers[current]->set_next_trigger (n);
return;
/* NOTREACHED */
case Trigger::Stop:
case Trigger::QueuedTrigger:
break;
}
all_triggers[current]->set_next_trigger (current);
}
XMLNode& XMLNode&
TriggerBox::get_state (void) TriggerBox::get_state (void)