Add Vamp-plugin Lua bindings (work in progress)
authorRobin Gareus <robin@gareus.org>
Mon, 3 Oct 2016 01:51:53 +0000 (03:51 +0200)
committerRobin Gareus <robin@gareus.org>
Mon, 3 Oct 2016 01:52:51 +0000 (03:52 +0200)
libs/ardour/ardour/lua_api.h
libs/ardour/lua_api.cc
libs/ardour/luabindings.cc

index bfe2e16283b290ce3f3d0889b7156156dbcbfa55..85fe92f28c8aead2a6e50936b0047ff1f03d8e0b 100644 (file)
 #include <string>
 #include <lo/lo.h>
 #include <boost/shared_ptr.hpp>
+#include <vamp-sdk/Plugin.h>
 
 #include "ardour/libardour_visibility.h"
 
 #include "ardour/processor.h"
 #include "ardour/session.h"
 
+namespace ARDOUR {
+       class Readable;
+}
+
 namespace ARDOUR { namespace LuaAPI {
 
        /** convenience constructor for DataType::NIL with managed lifetime
@@ -157,6 +162,66 @@ namespace ARDOUR { namespace LuaAPI {
         */
        int build_filename (lua_State *lua);
 
+       class Vamp {
+       /** Vamp Plugin Interface
+        *
+        * Vamp is an audio processing plugin system for plugins that extract descriptive information
+        * from audio data - typically referred to as audio analysis plugins or audio feature
+        * extraction plugins.
+        *
+        * This interface allows to load a plugins and directly access it using the Vamp Plugin API.
+        *
+        * A convenience method is provided to analyze Ardour::Readable objects (Regions).
+        */
+               public:
+                       Vamp (const std::string&, float sample_rate);
+                       ~Vamp ();
+                       ::Vamp::Plugin* plugin () { return _plugin; }
+
+                       /* high-level abstraction to process a single channel of the given Readable.
+                        *
+                        * If the plugin is not yet initialized, initialize() is called.
+                        *
+                        * if @cb is not nil, it is called with the immediate
+                        * Vamp::Plugin::Features on every process call.
+                        *
+                        * @r readable
+                        * @channel channel to process
+                        * @cb lua callback function
+                        * @return 0 on success
+                        */
+                       int analyze (boost::shared_ptr<ARDOUR::Readable>, uint32_t channel, luabridge::LuaRef fn);
+
+                       /** call plugin():reset() and clear intialization flag */
+                       void reset ();
+
+                       /** initialize the plugin for use with analyze().
+                        *
+                        * This is equivalent to plugin():initialise (1, 8192, 8192)
+                        * and prepares a plugin for analyze.
+                        *
+                        * Manual initialization is only required to set plugin-parameters
+                        * which depend on prior initialization of the plugin.
+                        *
+                        * @code
+                        * vamp:reset ()
+                        * vamp:initialize ()
+                        * vamp:plugin():setParameter (0, 1.5)
+                        * vamp:analyze (r, 0)
+                        * @endcode
+                        */
+                       bool initialize ();
+
+                       bool initialized () const { return _initialized; }
+
+               private:
+                       ::Vamp::Plugin* _plugin;
+                       float           _sample_rate;
+                       framecnt_t      _bufsize;
+                       bool            _initialized;
+
+       };
+
 } } /* namespace */
 
 namespace ARDOUR { namespace LuaOSC {
index 201ca7de0e5b33335b9d6f406ee67b46509566a3..2f6c9958519c197e01bca93cbc014a8cc25eef9a 100644 (file)
  *
  */
 #include <cstring>
+#include <vamp-hostsdk/PluginLoader.h>
 
-#include "pbd/error.h"
 #include "pbd/compose.h"
+#include "pbd/error.h"
+#include "pbd/failed_constructor.h"
 
 #include "ardour/lua_api.h"
 #include "ardour/luaproc.h"
@@ -27,6 +29,7 @@
 #include "ardour/plugin.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/plugin_manager.h"
+#include "ardour/readable.h"
 
 #include "LuaBridge/LuaBridge.h"
 
@@ -535,3 +538,96 @@ void LuaTableRef::assign (luabridge::LuaRef* rv, T key, const LuaTableEntry& s)
                        break;
        }
 }
+
+
+LuaAPI::Vamp::Vamp (const std::string& key, float sample_rate)
+       : _plugin (0)
+       , _sample_rate (sample_rate)
+       , _bufsize (8192)
+       , _initialized (false)
+{
+       using namespace ::Vamp::HostExt;
+
+       PluginLoader* loader (PluginLoader::getInstance());
+       _plugin = loader->loadPlugin (key, _sample_rate, PluginLoader::ADAPT_ALL_SAFE);
+
+       if (!_plugin) {
+               PBD::error << string_compose (_("VAMP Plugin \"%1\" could not be loaded"), key) << endmsg;
+               throw failed_constructor ();
+       }
+}
+
+LuaAPI::Vamp::~Vamp ()
+{
+       delete _plugin;
+}
+
+void
+LuaAPI::Vamp::reset ()
+{
+       _initialized = false;
+       if (_plugin) {
+               _plugin->reset ();
+       }
+}
+
+bool
+LuaAPI::Vamp::initialize ()
+{
+       if (!_plugin || _plugin->getMinChannelCount() > 1) {
+               return false;
+       }
+       if (!_plugin->initialise (1, _bufsize, _bufsize)) {
+               return false;
+       }
+       _initialized = true;
+       return true;
+}
+
+int
+LuaAPI::Vamp::analyze (boost::shared_ptr<ARDOUR::Readable> r, uint32_t channel, luabridge::LuaRef cb)
+{
+       if (!_initialized) {
+               if (!initialize ()) {
+                       return -1;
+               }
+       }
+       assert (_initialized);
+
+       ::Vamp::Plugin::FeatureSet features;
+       float* data = new float[_bufsize];
+       float* bufs[1] = { data };
+
+       framecnt_t len = r->readable_length();
+       framepos_t pos = 0;
+
+       int rv = 0;
+       while (1) {
+               framecnt_t to_read = std::min ((len - pos), _bufsize);
+               if (r->read (data, pos, to_read, channel) != to_read) {
+                       rv = -1;
+                       break;
+               }
+               if (to_read != _bufsize) {
+                       memset (data + to_read, 0, (_bufsize - to_read) * sizeof (float));
+               }
+
+               features = _plugin->process (bufs, ::Vamp::RealTime::fromSeconds ((double) pos / _sample_rate));
+
+               if (cb.type () == LUA_TFUNCTION) {
+                       /* TODO existing "features" binding fails here
+                        * std::map<int, std::vector<_VampHost::Vamp::Plugin::Feature> >
+                        */
+                       // cb (features, pos); // XXX
+               }
+
+               pos += to_read;
+
+               if (pos >= len) {
+                       break;
+               }
+       }
+
+       delete [] data;
+       return rv;
+}
index 096c0647aeaaee49428fe141b6ec68565477e98f..59e64d3a865af5bff8f24787f2d5681249edb34f 100644 (file)
@@ -32,6 +32,7 @@
 #include "ardour/audio_buffer.h"
 #include "ardour/audio_port.h"
 #include "ardour/audio_track.h"
+#include "ardour/audioplaylist.h"
 #include "ardour/buffer_set.h"
 #include "ardour/chan_mapping.h"
 #include "ardour/dB.h"
@@ -239,6 +240,10 @@ LuaBindings::stddef (lua_State* L)
                .beginStdVector <std::string> ("StringVector")
                .endClass ()
 
+       // std::vector<float>
+               .beginStdVector <float> ("FloatVector")
+               .endClass ()
+
        // register float array (uint8_t*)
                .registerArray <uint8_t> ("ByteArray")
 
@@ -437,6 +442,103 @@ LuaBindings::common (lua_State* L)
 
                .endNamespace () // Evoral
 
+               .beginNamespace ("Vamp")
+
+               .beginClass<Vamp::RealTime> ("RealTime")
+               .addConstructor <void (*) (int, int)> ()
+               .addFunction ("usec", &Vamp::RealTime::usec)
+               .addFunction ("msec", &Vamp::RealTime::msec)
+               .addFunction ("toString", &Vamp::RealTime::toString)
+               .endClass ()
+
+               .beginClass<Vamp::PluginBase> ("PluginBase")
+               .addFunction ("getIdentifier", &Vamp::PluginBase::getIdentifier)
+               .addFunction ("getName", &Vamp::PluginBase::getName)
+               .addFunction ("getDescription", &Vamp::PluginBase::getDescription)
+               .addFunction ("getMaker", &Vamp::PluginBase::getMaker)
+               .addFunction ("getCopyright", &Vamp::PluginBase::getCopyright)
+               .addFunction ("getPluginVersion", &Vamp::PluginBase::getPluginVersion)
+               .addFunction ("getParameterDescriptors", &Vamp::PluginBase::getParameterDescriptors)
+               .addFunction ("getParameter", &Vamp::PluginBase::getParameter)
+               .addFunction ("setParameter", &Vamp::PluginBase::setParameter)
+               .addFunction ("getPrograms", &Vamp::PluginBase::getPrograms)
+               .addFunction ("getCurrentProgram", &Vamp::PluginBase::getCurrentProgram)
+               .addFunction ("selectProgram", &Vamp::PluginBase::selectProgram)
+               .addFunction ("getType", &Vamp::PluginBase::getType)
+               .endClass ()
+
+               .beginNamespace ("PluginBase")
+               .beginClass<Vamp::PluginBase::ParameterDescriptor> ("ParameterDescriptor")
+               .addData ("identifier", &Vamp::PluginBase::ParameterDescriptor::identifier)
+               .addData ("name", &Vamp::PluginBase::ParameterDescriptor::name)
+               .addData ("description", &Vamp::PluginBase::ParameterDescriptor::description)
+               .addData ("unit", &Vamp::PluginBase::ParameterDescriptor::unit)
+               .addData ("minValue", &Vamp::PluginBase::ParameterDescriptor::minValue)
+               .addData ("maxValue", &Vamp::PluginBase::ParameterDescriptor::maxValue)
+               .addData ("defaultValue", &Vamp::PluginBase::ParameterDescriptor::defaultValue)
+               .addData ("isQuantized", &Vamp::PluginBase::ParameterDescriptor::isQuantized)
+               .addData ("quantizeStep", &Vamp::PluginBase::ParameterDescriptor::quantizeStep)
+               .addData ("valueNames", &Vamp::PluginBase::ParameterDescriptor::valueNames)
+               .endClass ()
+
+               .beginStdVector <Vamp::PluginBase::ParameterDescriptor> ("ParameterList")
+               .endClass ()
+               .endNamespace () // Vamp::PluginBase
+
+               .deriveClass<Vamp::Plugin, Vamp::PluginBase> ("Plugin")
+               // TODO add wrapper std::vector<FloatArray>
+               .addFunction ("process", &Vamp::Plugin::process) // XXX unusable due to  float * const *
+               .addFunction ("getRemainingFeatures", &Vamp::Plugin::getRemainingFeatures)
+               .endClass ()
+
+               .beginNamespace ("Plugin")
+               .beginClass<Vamp::Plugin::OutputDescriptor> ("OutputDescriptor")
+               .addData ("identifier", &Vamp::Plugin::OutputDescriptor::identifier)
+               .addData ("description", &Vamp::Plugin::OutputDescriptor::description)
+               .addData ("unit", &Vamp::Plugin::OutputDescriptor::unit)
+               .addData ("hasFixedBinCount", &Vamp::Plugin::OutputDescriptor::hasFixedBinCount)
+               .addData ("binCount", &Vamp::Plugin::OutputDescriptor::binCount)
+               .addData ("binNames", &Vamp::Plugin::OutputDescriptor::binNames)
+               .addData ("hasKnownExtents", &Vamp::Plugin::OutputDescriptor::hasKnownExtents)
+               .addData ("minValue", &Vamp::Plugin::OutputDescriptor::minValue)
+               .addData ("maxValue", &Vamp::Plugin::OutputDescriptor::maxValue)
+               .addData ("isQuantized", &Vamp::Plugin::OutputDescriptor::isQuantized)
+               .addData ("quantizeStep", &Vamp::Plugin::OutputDescriptor::quantizeStep)
+               .addData ("sampleType", &Vamp::Plugin::OutputDescriptor::sampleType)
+               .addData ("sampleRate", &Vamp::Plugin::OutputDescriptor::sampleRate)
+               .addData ("hasDuration", &Vamp::Plugin::OutputDescriptor::hasDuration)
+               .endClass ()
+
+               .beginNamespace ("OutputDescriptor")
+               /* Vamp::Plugin::OutputDescriptor enum */
+               .beginNamespace ("SampleType")
+               .addConst ("OneSamplePerStep", Vamp::Plugin::OutputDescriptor::SampleType(Vamp::Plugin::OutputDescriptor::OneSamplePerStep))
+               .addConst ("FixedSampleRate", Vamp::Plugin::OutputDescriptor::SampleType(Vamp::Plugin::OutputDescriptor::FixedSampleRate))
+               .addConst ("VariableSampleRate", Vamp::Plugin::OutputDescriptor::SampleType(Vamp::Plugin::OutputDescriptor::VariableSampleRate))
+               .endNamespace ()
+               .endNamespace () /* Vamp::Plugin::OutputDescriptor */
+
+               .beginClass<Vamp::Plugin::Feature> ("Feature")
+               .addData ("hasTimestamp", &Vamp::Plugin::Feature::hasTimestamp, false)
+               .addData ("timestamp", &Vamp::Plugin::Feature::timestamp, false)
+               .addData ("hasDuration", &Vamp::Plugin::Feature::hasDuration, false)
+               .addData ("duration", &Vamp::Plugin::Feature::duration, false)
+               .addData ("values", &Vamp::Plugin::Feature::values, false)
+               .addData ("label", &Vamp::Plugin::Feature::label, false)
+               .endClass ()
+
+               .beginStdVector <Vamp::Plugin::OutputDescriptor> ("OutputList")
+               .endClass ()
+
+               .beginStdVector <Vamp::Plugin::Feature> ("FeatureList")
+               .endClass ()
+
+               .beginStdMap <int, Vamp::Plugin::FeatureList> ("FeatureSet")
+               .endClass ()
+
+               .endNamespace () // Vamp::Plugin
+               .endNamespace () // Vamp
+
                .beginNamespace ("ARDOUR")
 
                .beginClass <InterThreadInfo> ("InterThreadInfo")
@@ -712,6 +814,7 @@ LuaBindings::common (lua_State* L)
                .endClass ()
 
                .deriveWSPtrClass <Playlist, SessionObject> ("Playlist")
+               .addCast<AudioPlaylist> ("to_audioplaylist")
                .addFunction ("region_by_id", &Playlist::region_by_id)
                .addFunction ("data_type", &Playlist::data_type)
                .addFunction ("n_regions", &Playlist::n_regions)
@@ -746,6 +849,10 @@ LuaBindings::common (lua_State* L)
 #endif
                .endClass ()
 
+               .deriveWSPtrClass <AudioPlaylist, Playlist> ("AudioPlaylist")
+               .addFunction ("read", &AudioPlaylist::read)
+               .endClass ()
+
                .deriveWSPtrClass <Track, Route> ("Track")
                .addCast<AudioTrack> ("to_audio_track")
                .addCast<MidiTrack> ("to_midi_track")
@@ -763,7 +870,14 @@ LuaBindings::common (lua_State* L)
                .deriveWSPtrClass <MidiTrack, Track> ("MidiTrack")
                .endClass ()
 
+               .beginWSPtrClass <Readable> ("Readable")
+               .addFunction ("read", &Readable::read)
+               .addFunction ("readable_length", &Readable::readable_length)
+               .addFunction ("n_channels", &Readable::n_channels)
+               .endClass ()
+
                .deriveWSPtrClass <Region, SessionObject> ("Region")
+               .addCast<Readable> ("to_readable")
                /* properties */
                .addFunction ("position", &Region::position)
                .addFunction ("start", &Region::start)
@@ -1472,6 +1586,15 @@ LuaBindings::common (lua_State* L)
                .addCFunction ("hsla_to_rgba", ARDOUR::LuaAPI::hsla_to_rgba)
                .addFunction ("usleep", Glib::usleep)
                .addCFunction ("build_filename", ARDOUR::LuaAPI::build_filename)
+
+               .beginClass <ARDOUR::LuaAPI::Vamp> ("Vamp")
+               .addConstructor <void (*) (const std::string&, float)> ()
+               .addFunction ("plugin", &ARDOUR::LuaAPI::Vamp::plugin)
+               .addFunction ("analyze", &ARDOUR::LuaAPI::Vamp::analyze)
+               .addFunction ("reset", &ARDOUR::LuaAPI::Vamp::reset)
+               .addFunction ("initialize", &ARDOUR::LuaAPI::Vamp::initialize)
+               .endClass ()
+
                .endNamespace () // end LuaAPI
                .endNamespace ();// end ARDOUR
 }