13
0

Add rubberband Lua bindings to process ardour regions

This commit is contained in:
Robin Gareus 2020-01-13 08:34:37 +01:00
parent 868d752b4f
commit fd4c35d46a
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
3 changed files with 328 additions and 0 deletions

View File

@ -22,12 +22,15 @@
#include <string>
#include <lo/lo.h>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <rubberband/RubberBandStretcher.h>
#include <vamp-hostsdk/Plugin.h>
#include "evoral/Note.h"
#include "ardour/libardour_visibility.h"
#include "ardour/audioregion.h"
#include "ardour/midi_model.h"
#include "ardour/processor.h"
#include "ardour/session.h"
@ -306,6 +309,48 @@ namespace ARDOUR { namespace LuaAPI {
};
class Rubberband : public Readable , public boost::enable_shared_from_this<Rubberband>
{
public:
Rubberband (boost::shared_ptr<AudioRegion>, bool percussive);
~Rubberband ();
bool set_strech_and_pitch (double stretch_ratio, double pitch_ratio);
bool set_mapping (luabridge::LuaRef tbl);
boost::shared_ptr<AudioRegion> process (luabridge::LuaRef cb);
boost::shared_ptr<Readable> readable ();
/* readable API */
samplecnt_t readable_length () const { return _read_len; }
uint32_t n_channels () const { return _n_channels; }
samplecnt_t read (Sample*, samplepos_t pos, samplecnt_t cnt, int channel) const;
private:
Rubberband (Rubberband const&); // no copy construction
bool read_region (bool study);
bool retrieve (float**);
void cleanup (bool abort);
boost::shared_ptr<AudioRegion> finalize ();
boost::shared_ptr<AudioRegion> _region;
uint32_t _n_channels;
samplecnt_t _read_len;
samplecnt_t _read_start;
samplecnt_t _read_offset;
std::vector<boost::shared_ptr<AudioSource> > _asrc;
RubberBand::RubberBandStretcher _rbs;
std::map<size_t, size_t> _mapping;
double _stretch_ratio;
double _pitch_ratio;
luabridge::LuaRef* _cb;
boost::shared_ptr<Rubberband> _self;
static const samplecnt_t _bufsize;
};
boost::shared_ptr<Evoral::Note<Temporal::Beats> >
new_noteptr (uint8_t, Temporal::Beats, Temporal::Beats, uint8_t, uint8_t);

View File

@ -19,10 +19,14 @@
#include <cstring>
#include <vamp-hostsdk/PluginLoader.h>
#include "pbd/basename.h"
#include "pbd/compose.h"
#include "pbd/error.h"
#include "pbd/failed_constructor.h"
#include "ardour/analyser.h"
#include "ardour/audiofilesource.h"
#include "ardour/audiosource.h"
#include "ardour/lua_api.h"
#include "ardour/luaproc.h"
#include "ardour/luascripting.h"
@ -30,6 +34,8 @@
#include "ardour/plugin_insert.h"
#include "ardour/plugin_manager.h"
#include "ardour/readable.h"
#include "ardour/region_factory.h"
#include "ardour/source_factory.h"
#include "LuaBridge/LuaBridge.h"
@ -875,3 +881,270 @@ LuaAPI::note_list (boost::shared_ptr<MidiModel> mm)
}
return note_ptr_list;
}
/* ****************************************************************************/
const samplecnt_t LuaAPI::Rubberband::_bufsize = 256;
LuaAPI::Rubberband::Rubberband (boost::shared_ptr<AudioRegion> r, bool percussive)
: _region (r)
, _rbs (r->session().sample_rate(), r->n_channels(),
percussive ? RubberBand::RubberBandStretcher::DefaultOptions : RubberBand::RubberBandStretcher::PercussiveOptions,
r->stretch (), r->shift ())
, _stretch_ratio (r->stretch ())
, _pitch_ratio (r->shift ())
, _cb (0)
{
_n_channels = r->n_channels ();
_read_len = r->length () / (double)r->stretch ();
_read_start = r->ancestral_start () + samplecnt_t (r->start () / (double)r->stretch ());
_read_offset = _read_start - r->start () + r->position ();
}
LuaAPI::Rubberband::~Rubberband ()
{
}
bool
LuaAPI::Rubberband::set_strech_and_pitch (double stretch_ratio, double pitch_ratio)
{
if (stretch_ratio <= 0 || pitch_ratio <= 0) {
return false;
}
_stretch_ratio = stretch_ratio * _region->stretch ();
_pitch_ratio = pitch_ratio * _region->shift ();
return true;
}
bool
LuaAPI::Rubberband::set_mapping (luabridge::LuaRef tbl)
{
if (!tbl.isTable ()) {
return false;
}
_mapping.clear ();
for (luabridge::Iterator i (tbl); !i.isNil (); ++i) {
if (!i.key ().isNumber () || !i.value ().isNumber ()) {
continue;
}
size_t ss = i.key ().cast<double> ();
size_t ds = i.value ().cast<double> ();
printf ("ADD %ld %ld\n", ss, ds);
_mapping[ss] = ds;
}
return !_mapping.empty ();
}
samplecnt_t
LuaAPI::Rubberband::read (Sample* buf, samplepos_t pos, samplecnt_t cnt, int channel) const
{
return _region->master_read_at (buf, NULL, NULL, _read_offset + pos, cnt, channel);
}
static void null_deleter (LuaAPI::Rubberband*) {}
boost::shared_ptr<Readable>
LuaAPI::Rubberband::readable ()
{
if (!_self) {
_self = boost::shared_ptr<Rubberband> (this, &null_deleter);
}
return boost::dynamic_pointer_cast<Readable> (_self);
}
bool
LuaAPI::Rubberband::read_region (bool study)
{
samplepos_t pos = 0;
float** buffers = new float*[_n_channels];
for (uint32_t c = 0; c < _n_channels; ++c) {
buffers[c] = new float[_bufsize];
}
while (pos < _read_len) {
samplecnt_t n_read = 0;
for (uint32_t c = 0; c < _n_channels; ++c) {
samplepos_t to_read = std::min (_bufsize, _read_len - pos);
n_read = read (buffers[c], pos, to_read, c);
if (n_read != to_read) {
pos = 0;
goto errout;
}
}
pos += n_read;
assert (!_cb || _cb->type () == LUA_TFUNCTION);
if ((*_cb) (NULL, pos * .5 + (study ? 0 : _read_len / 2))) {
pos = 0;
goto errout;
}
if (study) {
_rbs.study (buffers, n_read, pos == _read_len);
continue;
}
assert (_asrc.size () == _n_channels);
_rbs.process (buffers, n_read, pos == _read_len);
if (!retrieve (buffers)) {
pos = 0;
goto errout;
}
}
if (!retrieve (buffers)) {
pos = 0;
}
errout:
if (buffers) {
for (uint32_t c = 0; c < _n_channels; ++c) {
delete[] buffers[c];
}
delete[] buffers;
}
return pos == _read_len;
}
bool
LuaAPI::Rubberband::retrieve (float** buffers)
{
samplecnt_t avail = 0;
while ((avail = _rbs.available ()) > 0) {
samplepos_t to_read = std::min (_bufsize, avail);
_rbs.retrieve (buffers, to_read);
for (uint32_t c = 0; c < _asrc.size (); ++c) {
if (_asrc[c]->write (buffers[c], to_read) != to_read) {
return false;
}
}
}
return true;
}
boost::shared_ptr<AudioRegion>
LuaAPI::Rubberband::process (luabridge::LuaRef cb)
{
boost::shared_ptr<AudioRegion> rv;
if (cb.type () == LUA_TFUNCTION) {
_cb = new luabridge::LuaRef (cb);
}
_rbs.reset ();
_rbs.setDebugLevel (1);
_rbs.setTimeRatio (_stretch_ratio);
_rbs.setPitchScale (_pitch_ratio);
_rbs.setExpectedInputDuration (_read_len);
/* compare to Filter::make_new_sources */
vector<string> names = _region->master_source_names ();
Session& session = _region->session ();
samplecnt_t sample_rate = session.sample_rate ();
for (uint32_t c = 0; c < _n_channels; ++c) {
string name = PBD::basename_nosuffix (names[c]) + "(rb)";
const string path = session.new_audio_source_path (name, _n_channels, c, false, false);
if (path.empty ()) {
cleanup (true);
return rv;
}
try {
_asrc.push_back (boost::dynamic_pointer_cast<AudioSource> (
SourceFactory::createWritable (
DataType::AUDIO, session,
path, false, sample_rate)));
} catch (failed_constructor& err) {
cleanup (true);
return rv;
}
}
/* study */
if (!read_region (true)) {
cleanup (true);
return rv;
}
if (!_mapping.empty ()) {
_rbs.setKeyFrameMap (_mapping);
}
/* process */
if (!read_region (false)) {
cleanup (true);
return rv;
}
rv = finalize ();
cleanup (false);
return rv;
}
boost::shared_ptr<AudioRegion>
LuaAPI::Rubberband::finalize ()
{
time_t xnow = time (NULL);
struct tm* now = localtime (&xnow);
/* this is the same as RBEffect::finish, Filter::finish */
SourceList sl;
for (std::vector<boost::shared_ptr<AudioSource>>::iterator i = _asrc.begin (); i != _asrc.end (); ++i) {
boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*i);
assert (afs);
afs->done_with_peakfile_writes ();
afs->update_header (_region->position (), *now, xnow);
afs->mark_immutable ();
Analyser::queue_source_for_analysis (*i, false);
sl.push_back (*i);
}
/* create a new region */
std::string region_name = RegionFactory::new_region_name (_region->name ());
PropertyList plist;
plist.add (Properties::start, 0);
plist.add (Properties::length, _region->length ());
plist.add (Properties::name, region_name);
plist.add (Properties::whole_file, true);
plist.add (Properties::position, _region->position ());
boost::shared_ptr<Region> r = RegionFactory::create (sl, plist);
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
ar->set_scale_amplitude (_region->scale_amplitude ());
ar->set_fade_in_active (_region->fade_in_active ());
ar->set_fade_in (_region->fade_in ());
ar->set_fade_out_active (_region->fade_out_active ());
ar->set_fade_out (_region->fade_out ());
*(ar->envelope ()) = *(_region->envelope ());
ar->set_ancestral_data (_read_start, _read_len, _stretch_ratio, _pitch_ratio);
ar->set_master_sources (_region->master_sources ());
ar->set_length (ar->length () * _stretch_ratio, 0); // XXX
if (_stretch_ratio != 1.0) {
// TODO: apply mapping
ar->envelope ()->x_scale (_stretch_ratio);
}
return ar;
}
void
LuaAPI::Rubberband::cleanup (bool abort)
{
if (abort) {
for (std::vector<boost::shared_ptr<AudioSource>>::iterator i = _asrc.begin (); i != _asrc.end (); ++i) {
(*i)->mark_for_remove ();
}
}
_asrc.clear ();
delete (_cb);
_cb = 0;
}

View File

@ -2446,6 +2446,16 @@ LuaBindings::common (lua_State* L)
.addFunction ("process", &ARDOUR::LuaAPI::Vamp::process)
.endClass ()
.beginClass <ARDOUR::LuaAPI::Rubberband> ("Rubberband")
.addConstructor <void (*) (boost::shared_ptr<AudioRegion>, bool)> ()
.addFunction ("set_strech_and_pitch", &ARDOUR::LuaAPI::Rubberband::set_strech_and_pitch)
.addFunction ("set_mapping", &ARDOUR::LuaAPI::Rubberband::set_mapping)
.addFunction ("process", &ARDOUR::LuaAPI::Rubberband::process)
.addFunction ("readable_length", &ARDOUR::LuaAPI::Rubberband::readable_length)
.addFunction ("n_channels", &ARDOUR::LuaAPI::Rubberband::n_channels)
.addFunction ("readable", &ARDOUR::LuaAPI::Rubberband::readable)
.endClass ()
.endNamespace () // end LuaAPI
.endNamespace ();// end ARDOUR