+
+static double hue2rgb (const double p, const double q, double t) {
+ if (t < 0.0) t += 1.0;
+ if (t > 1.0) t -= 1.0;
+ if (t < 1.0 / 6.0) return p + (q - p) * 6.0 * t;
+ if (t < 1.0 / 2.0) return q;
+ if (t < 2.0 / 3.0) return p + (q - p) * (2.0 / 3.0 - t) * 6.0;
+ return p;
+}
+
+int
+ARDOUR::LuaAPI::hsla_to_rgba (lua_State *L)
+{
+ int top = lua_gettop (L);
+ if (top < 3) {
+ return luaL_argerror (L, 1, "invalid number of arguments, :hsla_to_rgba (h, s, l [,a])");
+ }
+ double h = luabridge::Stack<double>::get (L, 1);
+ double s = luabridge::Stack<double>::get (L, 2);
+ double l = luabridge::Stack<double>::get (L, 3);
+ double a = 1.0;
+ if (top > 3) {
+ a = luabridge::Stack<double>::get (L, 4);
+ }
+
+ // we can't use ArdourCanvas::hsva_to_color here
+ // besides we want HSL not HSV and without intermediate
+ // color_to_rgba (rgba_to_color ())
+ double r, g, b;
+ const double cq = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ const double cp = 2.f * l - cq;
+ r = hue2rgb (cp, cq, h + 1.0 / 3.0);
+ g = hue2rgb (cp, cq, h);
+ b = hue2rgb (cp, cq, h - 1.0 / 3.0);
+
+ luabridge::Stack<double>::push (L, r);
+ luabridge::Stack<double>::push (L, g);
+ luabridge::Stack<double>::push (L, b);
+ luabridge::Stack<double>::push (L, a);
+ return 4;
+}
+
+int
+ARDOUR::LuaAPI::color_to_rgba (lua_State *L)
+{
+ int top = lua_gettop (L);
+ if (top < 1) {
+ return luaL_argerror (L, 1, "invalid number of arguments, color_to_rgba (uint32_t)");
+ }
+ uint32_t color = luabridge::Stack<uint32_t>::get (L, 1);
+ double r, g, b, a;
+
+ /* libardour is no user of libcanvas, otherwise
+ * we could just call
+ * ArdourCanvas::color_to_rgba (color, r, g, b, a);
+ */
+ r = ((color >> 24) & 0xff) / 255.0;
+ g = ((color >> 16) & 0xff) / 255.0;
+ b = ((color >> 8) & 0xff) / 255.0;
+ a = ((color >> 0) & 0xff) / 255.0;
+
+ luabridge::Stack <double>::push (L, r);
+ luabridge::Stack <double>::push (L, g);
+ luabridge::Stack <double>::push (L, b);
+ luabridge::Stack <double>::push (L, a);
+ return 4;
+}
+
+int
+ARDOUR::LuaAPI::build_filename (lua_State *L)
+{
+ std::vector<std::string> elem;
+ int top = lua_gettop (L);
+ if (top < 1) {
+ return luaL_argerror (L, 1, "invalid number of arguments, build_filename (path, ...)");
+ }
+ for (int i = 1; i <= top; ++i) {
+ int lt = lua_type (L, i);
+ if (lt != LUA_TSTRING) {
+ return luaL_argerror (L, i, "invalid argument type, expected string");
+ }
+ elem.push_back (luaL_checkstring (L, i));
+ }
+
+ luabridge::Stack<std::string>::push (L, Glib::build_filename (elem));
+ return 1;
+}
+
+luabridge::LuaRef::Proxy&
+luabridge::LuaRef::Proxy::clone_instance (const void* classkey, void* p) {
+ lua_rawgeti (m_L, LUA_REGISTRYINDEX, m_tableRef);
+ lua_rawgeti (m_L, LUA_REGISTRYINDEX, m_keyRef);
+
+ luabridge::UserdataPtr::push_raw (m_L, p, classkey);
+
+ lua_rawset (m_L, -3);
+ lua_pop (m_L, 1);
+ return *this;
+}
+
+LuaTableRef::LuaTableRef () {}
+LuaTableRef::~LuaTableRef () {}
+
+int
+LuaTableRef::get (lua_State* L)
+{
+ luabridge::LuaRef rv (luabridge::newTable (L));
+ for (std::vector<LuaTableEntry>::const_iterator i = _data.begin (); i != _data.end (); ++i) {
+ switch ((*i).keytype) {
+ case LUA_TSTRING:
+ assign (&rv, i->k_s, *i);
+ break;
+ case LUA_TNUMBER:
+ assign (&rv, i->k_n, *i);
+ break;
+ }
+ }
+ luabridge::push (L, rv);
+ return 1;
+}
+
+int
+LuaTableRef::set (lua_State* L)
+{
+ if (!lua_istable (L, -1)) { return luaL_error (L, "argument is not a table"); }
+ _data.clear ();
+
+ lua_pushvalue (L, -1);
+ lua_pushnil (L);
+ while (lua_next (L, -2)) {
+ lua_pushvalue (L, -2);
+
+ LuaTableEntry s (lua_type (L, -1), lua_type (L, -2));
+ switch (lua_type (L, -1)) {
+ case LUA_TSTRING:
+ s.k_s = luabridge::Stack<std::string>::get (L, -1);
+ break;
+ ;
+ case LUA_TNUMBER:
+ s.k_n = luabridge::Stack<unsigned int>::get (L, -1);
+ break;
+ default:
+ // invalid key
+ lua_pop (L, 2);
+ continue;
+ }
+
+ switch (lua_type (L, -2)) {
+ case LUA_TSTRING:
+ s.s = luabridge::Stack<std::string>::get (L, -2);
+ break;
+ case LUA_TBOOLEAN:
+ s.b = lua_toboolean (L, -2);
+ break;
+ case LUA_TNUMBER:
+ s.n = lua_tonumber (L, -2);
+ break;
+ case LUA_TUSERDATA:
+ {
+ bool ok = false;
+ lua_getmetatable (L, -2);
+ lua_rawgetp (L, -1, luabridge::getIdentityKey ());
+ if (lua_isboolean (L, -1)) {
+ lua_pop (L, 1);
+ const void* key = lua_topointer (L, -1);
+ lua_pop (L, 1);
+ void const* classkey = findclasskey (L, key);
+
+ if (classkey) {
+ ok = true;
+ s.c = classkey;
+ s.p = luabridge::Userdata::get_ptr (L, -2);
+ }
+ } else {
+ lua_pop (L, 2);
+ }
+
+ if (ok) {
+ break;
+ }
+ // invalid userdata -- fall through
+ }
+ // no break
+ case LUA_TFUNCTION: // no support -- we could... string.format("%q", string.dump(value, true))
+ case LUA_TTABLE: // no nested tables, sorry.
+ case LUA_TNIL: // fallthrough
+ default:
+ // invalid value
+ lua_pop (L, 2);
+ continue;
+ }
+
+ _data.push_back (s);
+ lua_pop (L, 2);
+ }
+ return 0;
+}
+
+void*
+LuaTableRef::findclasskey (lua_State *L, const void* key)
+{
+ lua_pushvalue (L, LUA_REGISTRYINDEX);
+ lua_pushnil (L);
+ while (lua_next (L, -2)) {
+ lua_pushvalue (L, -2);
+ if (lua_topointer (L, -2) == key) {
+ void* rv = lua_touserdata (L, -1);
+ lua_pop (L, 4);
+ return rv;
+ }
+ lua_pop (L, 2);
+ }
+ lua_pop (L, 1);
+ return NULL;
+}
+
+template<typename T>
+void LuaTableRef::assign (luabridge::LuaRef* rv, T key, const LuaTableEntry& s)
+{
+ switch (s.valuetype) {
+ case LUA_TSTRING:
+ (*rv)[key] = s.s;
+ break;
+ case LUA_TBOOLEAN:
+ (*rv)[key] = s.b;
+ break;
+ case LUA_TNUMBER:
+ (*rv)[key] = s.n;
+ break;
+ case LUA_TUSERDATA:
+ (*rv)[key].clone_instance (s.c, s.p);
+ break;
+ default:
+ assert (0);
+ break;
+ }
+}
+
+std::vector<std::string>
+LuaAPI::Vamp::list_plugins ()
+{
+ using namespace ::Vamp::HostExt;
+ PluginLoader* loader (PluginLoader::getInstance());
+ return loader->listPlugins ();
+}
+
+LuaAPI::Vamp::Vamp (const std::string& key, float sample_rate)
+ : _plugin (0)
+ , _sample_rate (sample_rate)
+ , _bufsize (1024)
+ , _stepsize (1024)
+ , _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 ();
+ }
+
+ size_t bs = _plugin->getPreferredBlockSize ();
+ size_t ss = _plugin->getPreferredStepSize ();
+
+ if (bs > 0 && ss > 0 && bs <= 8192 && ss <= 8192) {
+ _bufsize = bs;
+ _stepsize = bs;
+ }
+}
+
+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, _stepsize, _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) {
+ cb (&features, pos);
+ }
+
+ pos += std::min (_stepsize, to_read);
+
+ if (pos >= len) {
+ break;
+ }
+ }
+
+ delete [] data;
+ return rv;
+}
+
+::Vamp::Plugin::FeatureSet
+LuaAPI::Vamp::process (const std::vector<float*>& d, ::Vamp::RealTime rt)
+{
+ if (!_plugin || d.size() == 0) {
+ return ::Vamp::Plugin::FeatureSet ();
+ }
+ const float* const* bufs = &d[0];
+ return _plugin->process (bufs, rt);
+}
+
+boost::shared_ptr<Evoral::Note<Evoral::Beats> >
+LuaAPI::new_noteptr (uint8_t chan, Evoral::Beats beat_time, Evoral::Beats length, uint8_t note, uint8_t velocity)
+{
+ return boost::shared_ptr<Evoral::Note<Evoral::Beats> > (new Evoral::Note<Evoral::Beats>(chan, beat_time, length, note, velocity));
+}
+
+std::list<boost::shared_ptr<Evoral::Note<Evoral::Beats> > >
+LuaAPI::note_list (boost::shared_ptr<MidiModel> mm)
+{
+ typedef boost::shared_ptr<Evoral::Note<Evoral::Beats> > NotePtr;
+
+ std::list<NotePtr> note_ptr_list;
+
+ const MidiModel::Notes& notes = mm->notes();
+ for (MidiModel::Notes::const_iterator i = notes.begin(); i != notes.end(); ++i) {
+ note_ptr_list.push_back (*i);
+ }
+ return note_ptr_list;
+}