mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-10 08:36:32 +01:00
triggerbox: more significant design clean-ups, and follow actions now work (basically)
This commit is contained in:
parent
8cea19dd25
commit
edbafd9f5b
3 changed files with 307 additions and 161 deletions
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
@ -188,40 +189,72 @@ Trigger::process_state_requests ()
|
||||||
|
|
||||||
_unbang.fetch_sub (1);
|
_unbang.fetch_sub (1);
|
||||||
|
|
||||||
if (active() &&launch_style() == Trigger::Gate) {
|
if (active() && launch_style() == Trigger::Gate) {
|
||||||
_state = WaitingToStop;
|
_state = WaitingToStop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
/* We reached the end */
|
||||||
return at_end ();
|
|
||||||
|
if (_launch_style == Loop) {
|
||||||
|
nframes -= this_read;
|
||||||
|
dest_offset += this_read;
|
||||||
|
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 was reached end, but set to loop, so retrigger\n", index()));
|
||||||
|
retrigger ();
|
||||||
|
/* and go around again */
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return Relax;
|
_state = Stopped;
|
||||||
}
|
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 was reached end, now stopped\n", index()));
|
||||||
|
|
||||||
Trigger::RunResult
|
|
||||||
AudioTrigger::at_end ()
|
|
||||||
{
|
|
||||||
switch (launch_style()) {
|
|
||||||
case Trigger::Loop:
|
|
||||||
retrigger();
|
|
||||||
return ReadMore;
|
|
||||||
default:
|
|
||||||
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)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue