13
0

triggerbox: add RubberBandStretcher for static stretch of regions in slots

This commit is contained in:
Paul Davis 2021-08-06 13:03:28 -06:00
parent 46a64c2eef
commit 44176aa2e4
2 changed files with 133 additions and 17 deletions

View File

@ -46,7 +46,7 @@ class TriggerBox;
class LIBARDOUR_API Trigger : public PBD::Stateful {
public:
Trigger (size_t index);
Trigger (size_t index, TriggerBox&);
virtual ~Trigger() {}
virtual void bang (TriggerBox&) = 0;
@ -84,6 +84,10 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
Temporal::BBT_Offset quantization() const;
void set_quantization (Temporal::BBT_Offset const &);
virtual void set_length (timecnt_t const &) = 0;
virtual timecnt_t current_length() const = 0;
virtual timecnt_t natural_length() const = 0;
bool stop_requested() const { return _stop_requested; }
virtual void stop();
virtual void retrigger () {}
@ -98,6 +102,7 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
int set_state (const XMLNode&, int version);
protected:
TriggerBox& _box;
bool _running;
bool _stop_requested;
size_t _index;
@ -111,7 +116,7 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
class LIBARDOUR_API AudioTrigger : public Trigger {
public:
AudioTrigger (size_t index);
AudioTrigger (size_t index, TriggerBox&);
~AudioTrigger ();
void bang (TriggerBox&);
@ -119,13 +124,17 @@ class LIBARDOUR_API AudioTrigger : public Trigger {
Sample* run (uint32_t channel, pframes_t& nframes, bool& need_butler);
void set_length (timecnt_t const &);
timecnt_t current_length() const;
timecnt_t natural_length() const;
int set_region (boost::shared_ptr<Region>);
void retrigger ();
private:
std::vector<Sample*> data;
std::vector<samplecnt_t> read_index;
samplecnt_t length;
samplecnt_t data_length;
void drop_data ();
int load_data (boost::shared_ptr<AudioRegion>);

View File

@ -1,5 +1,7 @@
#include <iostream>
#include <rubberband/RubberBandStretcher.h>
#include "pbd/basename.h"
#include "pbd/failed_constructor.h"
@ -31,7 +33,7 @@ TriggerBox::TriggerBox (Session& s, DataType dt)
if (_data_type == DataType::AUDIO) {
for (size_t n = 0; n < 16; ++n) {
all_triggers.push_back (new AudioTrigger (n));
all_triggers.push_back (new AudioTrigger (n, *this));
}
}
@ -418,8 +420,9 @@ TriggerBox::set_state (const XMLNode&, int version)
/*--------------------*/
Trigger::Trigger (size_t n)
: _running (false)
Trigger::Trigger (size_t n, TriggerBox& b)
: _box (b)
, _running (false)
, _stop_requested (false)
, _index (n)
, _launch_style (Loop)
@ -478,10 +481,10 @@ Trigger::stop ()
/*--------------------*/
AudioTrigger::AudioTrigger (size_t n)
: Trigger (n)
AudioTrigger::AudioTrigger (size_t n, TriggerBox& b)
: Trigger (n, b)
, data (0)
, length (0)
, data_length (0)
{
}
@ -492,6 +495,110 @@ AudioTrigger::~AudioTrigger ()
}
}
void
AudioTrigger::set_length (timecnt_t const & newlen)
{
using namespace RubberBand;
using namespace Temporal;
if (!_region) {
return;
}
boost::shared_ptr<AudioRegion> ar (boost::dynamic_pointer_cast<AudioRegion> (_region));
/* load raw data */
load_data (ar);
if (newlen == _region->length()) {
/* no stretch required */
return;
}
/* offline stretch */
/* study */
const uint32_t nchans = ar->n_channels();
RubberBandStretcher::Options options = RubberBandStretcher::Option (RubberBandStretcher::OptionProcessRealTime|RubberBandStretcher::OptionStretchPrecise);
RubberBandStretcher stretcher (_box.session().sample_rate(), nchans, options, 1.0, 1.0);
/* Compute stretch ratio */
double new_ratio;
if (newlen.time_domain() == AudioTime) {
new_ratio = (double) newlen.samples() / data_length;
} else {
/* XXX what to use for position ??? */
const timecnt_t dur = TempoMap::use()->convert_duration (newlen, timepos_t (0), AudioTime);
new_ratio = (double) dur.samples() / data_length;
}
stretcher.setTimeRatio (new_ratio);
/* RB expects array-of-ptr-to-Sample, so set one up */
Sample* pdata[nchans];
for (uint32_t n = 0; n < nchans; ++n) {
pdata[n] = data[n];
}
/* study, then process */
stretcher.setMaxProcessSize (data_length);
stretcher.setExpectedInputDuration (data_length);
stretcher.study (pdata, _region->length_samples(), true);
stretcher.process (pdata, _region->length_samples(), true);
/* how many samples did we end up with? */
samplecnt_t plen = stretcher.available();
/* allocate new data buffers */
drop_data ();
for (uint32_t n = 0; n < nchans; ++n) {
data.push_back (new Sample[plen]);
}
/* reset pdata to point to newly allocated buffers */
for (uint32_t n = 0; n < nchans; ++n) {
pdata[n] = data[n];
}
/* fetch it all */
stretcher.retrieve (pdata, plen);
/* store length */
data_length = plen;
}
timecnt_t
AudioTrigger::current_length() const
{
if (_region) {
return timecnt_t (data_length);
}
return timecnt_t (Temporal::BeatTime);
}
timecnt_t
AudioTrigger::natural_length() const
{
if (_region) {
return _region->length();
}
return timecnt_t (Temporal::BeatTime);
}
int
AudioTrigger::set_region (boost::shared_ptr<Region> r)
{
@ -503,9 +610,9 @@ AudioTrigger::set_region (boost::shared_ptr<Region> r)
set_region_internal (r);
if (load_data (ar)) {
return -1;
}
/* this will load data, but won't stretch it for now */
set_length (r->length ());
PropertyChanged (ARDOUR::Properties::name);
@ -526,15 +633,15 @@ AudioTrigger::load_data (boost::shared_ptr<AudioRegion> ar)
{
const uint32_t nchans = ar->n_channels();
length = ar->length_samples();
data_length = ar->length_samples();
drop_data ();
try {
for (uint32_t n = 0; n < nchans; ++n) {
data.push_back (new Sample[length]);
data.push_back (new Sample[data_length]);
read_index.push_back (0);
ar->read (data[n], 0, length, n);
ar->read (data[n], 0, data_length, n);
}
} catch (...) {
drop_data ();
@ -607,7 +714,7 @@ AudioTrigger::run (uint32_t channel, pframes_t& nframes, bool& /* need_butler */
return 0;
}
if (read_index[channel] >= length) {
if (read_index[channel] >= data_length) {
_running = false;
return 0;
}
@ -621,7 +728,7 @@ AudioTrigger::run (uint32_t channel, pframes_t& nframes, bool& /* need_butler */
channel %= data.size();
nframes = (pframes_t) std::min ((samplecnt_t) nframes, (length - read_index[channel]));
nframes = (pframes_t) std::min ((samplecnt_t) nframes, (data_length - read_index[channel]));
read_index[channel] += nframes;
return data[channel] + read_index[channel];