summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2020-01-13 08:34:37 +0100
committerRobin Gareus <robin@gareus.org>2020-01-13 17:57:35 +0100
commitfd4c35d46a63b22795e526d1e0375a9b2d6d441c (patch)
tree6308b2f8f5156c606cb0f00f816f704bd00979f2
parent868d752b4f83dc31fa74ae9db171012304ded49a (diff)
Add rubberband Lua bindings to process ardour regions
-rw-r--r--libs/ardour/ardour/lua_api.h45
-rw-r--r--libs/ardour/lua_api.cc273
-rw-r--r--libs/ardour/luabindings.cc10
3 files changed, 328 insertions, 0 deletions
diff --git a/libs/ardour/ardour/lua_api.h b/libs/ardour/ardour/lua_api.h
index bed9db5aa2..4d3bd38080 100644
--- a/libs/ardour/ardour/lua_api.h
+++ b/libs/ardour/ardour/lua_api.h
@@ -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);
diff --git a/libs/ardour/lua_api.cc b/libs/ardour/lua_api.cc
index 7d6d54e5d6..f294cb3925 100644
--- a/libs/ardour/lua_api.cc
+++ b/libs/ardour/lua_api.cc
@@ -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;
+}
diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc
index 6c571dd6fd..f781530b4a 100644
--- a/libs/ardour/luabindings.cc
+++ b/libs/ardour/luabindings.cc
@@ -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