mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-16 03:36:32 +01:00
triggerbox: further steps to seamless looped clips, and tiny beginnings for follow actions
This commit is contained in:
parent
d8d9ffaf3d
commit
029231b8ef
2 changed files with 91 additions and 35 deletions
|
|
@ -101,6 +101,14 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
|
||||||
XMLNode& get_state (void);
|
XMLNode& get_state (void);
|
||||||
int set_state (const XMLNode&, int version);
|
int set_state (const XMLNode&, int version);
|
||||||
|
|
||||||
|
enum RunResult {
|
||||||
|
Relax = 0,
|
||||||
|
RemoveTrigger = 0x1,
|
||||||
|
ReadMore = 0x2,
|
||||||
|
FillSilence = 0x4,
|
||||||
|
ChangeTriggers = 0x8
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TriggerBox& _box;
|
TriggerBox& _box;
|
||||||
bool _running;
|
bool _running;
|
||||||
|
|
@ -122,7 +130,7 @@ class LIBARDOUR_API AudioTrigger : public Trigger {
|
||||||
void bang (TriggerBox&);
|
void bang (TriggerBox&);
|
||||||
void unbang (TriggerBox&, Temporal::Beats const & , samplepos_t);
|
void unbang (TriggerBox&, Temporal::Beats const & , samplepos_t);
|
||||||
|
|
||||||
int run (AudioBuffer&, uint32_t channel, pframes_t nframes, pframes_t offset, bool first);
|
RunResult run (AudioBuffer&, uint32_t channel, 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;
|
||||||
|
|
@ -138,6 +146,7 @@ class LIBARDOUR_API AudioTrigger : public Trigger {
|
||||||
|
|
||||||
void drop_data ();
|
void drop_data ();
|
||||||
int load_data (boost::shared_ptr<AudioRegion>);
|
int load_data (boost::shared_ptr<AudioRegion>);
|
||||||
|
RunResult at_end ();
|
||||||
};
|
};
|
||||||
|
|
||||||
class LIBARDOUR_API TriggerBox : public Processor
|
class LIBARDOUR_API TriggerBox : public Processor
|
||||||
|
|
|
||||||
|
|
@ -346,6 +346,7 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t max_chans = 0;
|
size_t max_chans = 0;
|
||||||
|
Trigger::RunResult rr;
|
||||||
|
|
||||||
for (Triggers::iterator t = active_triggers.begin(); t != active_triggers.end(); ) {
|
for (Triggers::iterator t = active_triggers.begin(); t != active_triggers.end(); ) {
|
||||||
|
|
||||||
|
|
@ -376,33 +377,57 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioTrigger* at = dynamic_cast<AudioTrigger*> (trigger);
|
AudioTrigger* at = dynamic_cast<AudioTrigger*> (trigger);
|
||||||
bool err = false;
|
|
||||||
|
|
||||||
if (at) {
|
if (at) {
|
||||||
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
|
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
|
||||||
const bool first = (t == active_triggers.begin());
|
const bool first = (t == active_triggers.begin());
|
||||||
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);
|
||||||
|
|
||||||
|
read_more:
|
||||||
for (uint32_t chan = 0; chan < nchans; ++chan) {
|
for (uint32_t chan = 0; chan < nchans; ++chan) {
|
||||||
|
|
||||||
if (at->run (bufs.get_audio (chan), chan, trigger_samples, dest_offset, first)) {
|
/* we assume the result will be the same for all channels */
|
||||||
err = true;
|
|
||||||
break;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rr == Trigger::ReadMore) {
|
||||||
|
trigger_samples -= nf;
|
||||||
|
goto read_more;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
/* XXX to be written */
|
/* XXX MIDI triggers to be implemented */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
if (rr & Trigger::RemoveTrigger) {
|
||||||
t = active_triggers.erase (t);
|
t = active_triggers.erase (t);
|
||||||
} else {
|
continue;
|
||||||
++t;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++t;
|
||||||
|
|
||||||
|
if (rr & Trigger::ChangeTriggers) {
|
||||||
|
/* XXX do this! */
|
||||||
|
std::cerr << "Should change triggers!\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ChanCount cc (DataType::AUDIO, max_chans);
|
ChanCount cc (DataType::AUDIO, max_chans);
|
||||||
|
|
@ -560,7 +585,7 @@ AudioTrigger::set_length (timecnt_t const & newlen)
|
||||||
|
|
||||||
/* study, then process */
|
/* study, then process */
|
||||||
|
|
||||||
const samplecnt_t block_size = 8192;
|
const samplecnt_t block_size = 16384;
|
||||||
samplecnt_t read = 0;
|
samplecnt_t read = 0;
|
||||||
|
|
||||||
stretcher.setDebugLevel (0);
|
stretcher.setDebugLevel (0);
|
||||||
|
|
@ -754,58 +779,80 @@ AudioTrigger::unbang (TriggerBox& /*proc*/, Temporal::Beats const &, samplepos_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
Trigger::RunResult
|
||||||
AudioTrigger::run (AudioBuffer& buf, uint32_t channel, pframes_t nframes, pframes_t dest_offset, bool first)
|
AudioTrigger::run (AudioBuffer& buf, uint32_t channel, pframes_t& nframes, pframes_t dest_offset, bool first)
|
||||||
{
|
{
|
||||||
if (!_running) {
|
if (!_running) {
|
||||||
return -1;
|
return RemoveTrigger;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read_index[channel] >= data_length) {
|
if (read_index[channel] >= data_length) {
|
||||||
_running = false;
|
_running = false;
|
||||||
return -1;
|
return RemoveTrigger;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_stop_requested) {
|
if (_stop_requested) {
|
||||||
/* XXX need fade out machinery */
|
/* XXX need fade out machinery instead of immediate stop */
|
||||||
_running = false;
|
_running = false;
|
||||||
_stop_requested = false;
|
_stop_requested = false;
|
||||||
return 0;
|
return RemoveTrigger;
|
||||||
}
|
}
|
||||||
|
|
||||||
channel %= data.size();
|
channel %= data.size();
|
||||||
|
|
||||||
read_more:
|
|
||||||
pframes_t nf = (pframes_t) std::min ((samplecnt_t) nframes, (data_length - read_index[channel]));
|
pframes_t nf = (pframes_t) std::min ((samplecnt_t) nframes, (data_length - read_index[channel]));
|
||||||
Sample* src = data[channel] + read_index[channel];
|
Sample* src = data[channel] + read_index[channel];
|
||||||
|
|
||||||
if (first) {
|
if (first) {
|
||||||
buf.read_from (src, nf, dest_offset);
|
buf.read_from (src, nf, dest_offset);
|
||||||
if ((nf + dest_offset) < nframes) {
|
|
||||||
buf.silence (nframes - nf, nf + dest_offset);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
buf.accumulate_from (src, nf);
|
buf.accumulate_from (src, nf);
|
||||||
}
|
}
|
||||||
|
|
||||||
read_index[channel] += nf;
|
read_index[channel] += nf;
|
||||||
|
|
||||||
if (nf < nframes) {
|
nframes -= nf;
|
||||||
|
|
||||||
nframes -= nf;
|
if (nframes != 0) {
|
||||||
|
/* did not get all samples, must have reached the end, figure out what do to */
|
||||||
switch (launch_style()) {
|
return at_end ();
|
||||||
case Trigger::Loop:
|
|
||||||
retrigger();
|
|
||||||
goto read_more;
|
|
||||||
break;
|
|
||||||
case Trigger::Gate:
|
|
||||||
case Trigger::Toggle:
|
|
||||||
case Trigger::Repeat:
|
|
||||||
return -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return Relax;
|
||||||
|
}
|
||||||
|
|
||||||
|
Trigger::RunResult
|
||||||
|
AudioTrigger::at_end ()
|
||||||
|
{
|
||||||
|
switch (launch_style()) {
|
||||||
|
case Trigger::Loop:
|
||||||
|
retrigger();
|
||||||
|
return ReadMore; /* means keep reading */
|
||||||
|
|
||||||
|
case Trigger::Gate:
|
||||||
|
case Trigger::Toggle:
|
||||||
|
case Trigger::Repeat:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (follow_action()) {
|
||||||
|
case Stop:
|
||||||
|
return RunResult (RemoveTrigger|FillSilence);
|
||||||
|
case QueuedTrigger:
|
||||||
|
break;
|
||||||
|
case NextTrigger:
|
||||||
|
break;
|
||||||
|
case PrevTrigger:
|
||||||
|
break;
|
||||||
|
case FirstTrigger:
|
||||||
|
break;
|
||||||
|
case LastTrigger:
|
||||||
|
break;
|
||||||
|
case AnyTrigger:
|
||||||
|
break;
|
||||||
|
case OtherTrigger:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ChangeTriggers;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue