annotate native/vampyhost.cpp @ 151:5a6b8f4be9b9 tracks tip

Docs
author Chris Cannam
date Fri, 21 Apr 2017 14:33:57 +0100
parents aa96f69e2f14
children
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@26 3 /*
Chris@26 4 VampyHost
Chris@26 5
Chris@26 6 Use Vamp audio analysis plugins in Python
Chris@26 7
Chris@26 8 Gyorgy Fazekas and Chris Cannam
Chris@26 9 Centre for Digital Music, Queen Mary, University of London
Chris@117 10 Copyright 2008-2015 Queen Mary, University of London
Chris@26 11
Chris@26 12 Permission is hereby granted, free of charge, to any person
Chris@26 13 obtaining a copy of this software and associated documentation
Chris@26 14 files (the "Software"), to deal in the Software without
Chris@26 15 restriction, including without limitation the rights to use, copy,
Chris@26 16 modify, merge, publish, distribute, sublicense, and/or sell copies
Chris@26 17 of the Software, and to permit persons to whom the Software is
Chris@26 18 furnished to do so, subject to the following conditions:
Chris@26 19
Chris@26 20 The above copyright notice and this permission notice shall be
Chris@26 21 included in all copies or substantial portions of the Software.
Chris@26 22
Chris@26 23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Chris@26 24 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Chris@26 25 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Chris@26 26 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
Chris@26 27 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
Chris@26 28 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
Chris@26 29 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Chris@26 30
Chris@26 31 Except as contained in this notice, the names of the Centre for
Chris@26 32 Digital Music; Queen Mary, University of London; and the authors
Chris@26 33 shall not be used in advertising or otherwise to promote the sale,
Chris@26 34 use or other dealings in this Software without prior written
Chris@26 35 authorization.
Chris@26 36 */
Chris@26 37
Chris@39 38 // include for python extension module: must be first
Chris@0 39 #include <Python.h>
Chris@14 40
Chris@14 41 // define a unique API pointer
Chris@27 42 #define PY_ARRAY_UNIQUE_SYMBOL VAMPYHOST_ARRAY_API
Chris@14 43 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
Chris@14 44 #include "numpy/arrayobject.h"
Chris@14 45
Chris@31 46 #include "PyRealTime.h"
Chris@31 47 #include "PyPluginObject.h"
Chris@12 48
Chris@1 49 #include "vamp-hostsdk/PluginHostAdapter.h"
Chris@1 50 #include "vamp-hostsdk/PluginChannelAdapter.h"
Chris@1 51 #include "vamp-hostsdk/PluginInputDomainAdapter.h"
Chris@1 52 #include "vamp-hostsdk/PluginLoader.h"
Chris@16 53
Chris@29 54 #include "VectorConversion.h"
Chris@112 55 #include "StringConversion.h"
Chris@16 56 #include "PyRealTime.h"
Chris@0 57
Chris@0 58 #include <iostream>
Chris@0 59 #include <string>
Chris@0 60
Chris@0 61 #include <cmath>
Chris@0 62
Chris@120 63 #if (VAMP_SDK_MAJOR_VERSION != 2 || VAMP_SDK_MINOR_VERSION < 6)
Chris@120 64 #error "Vamp plugin SDK v2, version 2.6 or newer required"
Chris@120 65 #endif
Chris@120 66
Chris@0 67 using namespace std;
Chris@0 68 using namespace Vamp;
Chris@31 69 using namespace Vamp::HostExt;
Chris@21 70
Chris@0 71 static PyObject *
Chris@79 72 list_plugins(PyObject *self, PyObject *)
Chris@0 73 {
Chris@0 74 PluginLoader *loader = PluginLoader::getInstance();
Chris@0 75 vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
Chris@29 76 VectorConversion conv;
Chris@15 77 return conv.PyValue_From_StringVector(plugins);
Chris@0 78 }
Chris@0 79
Chris@15 80 static PyObject *
Chris@79 81 get_plugin_path(PyObject *self, PyObject *)
Chris@15 82 {
Chris@15 83 vector<string> path = PluginHostAdapter::getPluginPath();
Chris@29 84 VectorConversion conv;
Chris@15 85 return conv.PyValue_From_StringVector(path);
Chris@15 86 }
Chris@0 87
Chris@15 88 static string toPluginKey(PyObject *pyPluginKey)
Chris@0 89 {
Chris@36 90 // convert to stl string
Chris@112 91 string pluginKey(StringConversion().py2string(pyPluginKey));
Chris@0 92
Chris@36 93 // check pluginKey validity
Chris@0 94 string::size_type ki = pluginKey.find(':');
Chris@0 95 if (ki == string::npos) {
Chris@39 96 PyErr_SetString(PyExc_TypeError,
Chris@39 97 "Plugin key must be of the form library:identifier");
Chris@39 98 return "";
Chris@0 99 }
Chris@0 100
Chris@15 101 return pluginKey;
Chris@15 102 }
Chris@15 103
Chris@15 104 static PyObject *
Chris@79 105 get_library_for(PyObject *self, PyObject *args)
Chris@15 106 {
Chris@15 107 PyObject *pyPluginKey;
Chris@15 108
Chris@112 109 if (!PyArg_ParseTuple(args,
Chris@112 110 #if (PY_MAJOR_VERSION >= 3)
Chris@112 111 "U",
Chris@112 112 #else
Chris@112 113 "S",
Chris@112 114 #endif
Chris@112 115 &pyPluginKey)) {
Chris@39 116 PyErr_SetString(PyExc_TypeError,
Chris@79 117 "get_library_for() takes plugin key (string) argument");
Chris@39 118 return 0; }
Chris@15 119
Chris@15 120 string pluginKey = toPluginKey(pyPluginKey);
Chris@16 121 if (pluginKey == "") return 0;
Chris@15 122
Chris@0 123 PluginLoader *loader = PluginLoader::getInstance();
Chris@0 124 string path = loader->getLibraryPathForPlugin(pluginKey);
Chris@112 125 PyObject *pyPath = StringConversion().string2py(path.c_str());
Chris@0 126 return pyPath;
Chris@0 127 }
Chris@0 128
Chris@0 129 static PyObject *
Chris@79 130 get_category_of(PyObject *self, PyObject *args)
Chris@0 131 {
Chris@0 132 PyObject *pyPluginKey;
Chris@0 133
Chris@112 134 if (!PyArg_ParseTuple(args,
Chris@112 135 #if (PY_MAJOR_VERSION >= 3)
Chris@112 136 "U",
Chris@112 137 #else
Chris@112 138 "S",
Chris@112 139 #endif
Chris@112 140 &pyPluginKey)) {
Chris@39 141 PyErr_SetString(PyExc_TypeError,
Chris@79 142 "get_category_of() takes plugin key (string) argument");
Chris@39 143 return 0; }
Chris@0 144
Chris@15 145 string pluginKey = toPluginKey(pyPluginKey);
Chris@16 146 if (pluginKey == "") return 0;
Chris@0 147
Chris@0 148 PluginLoader *loader = PluginLoader::getInstance();
luis@7 149 PluginLoader::PluginCategoryHierarchy
Chris@39 150 category = loader->getPluginCategory(pluginKey);
Chris@0 151
Chris@29 152 VectorConversion conv;
Chris@15 153 return conv.PyValue_From_StringVector(category);
Chris@0 154 }
Chris@0 155
Chris@0 156 static PyObject *
Chris@79 157 get_outputs_of(PyObject *self, PyObject *args)
Chris@0 158 {
Chris@31 159 PyObject *pyPluginKey;
Chris@31 160
Chris@112 161 if (!PyArg_ParseTuple(args,
Chris@112 162 #if (PY_MAJOR_VERSION >= 3)
Chris@112 163 "U",
Chris@112 164 #else
Chris@112 165 "S",
Chris@112 166 #endif
Chris@112 167 &pyPluginKey)) {
Chris@39 168 PyErr_SetString(PyExc_TypeError,
Chris@79 169 "get_outputs_of() takes plugin key (string) argument");
Chris@39 170 return 0; }
Chris@31 171
Chris@15 172 Plugin::OutputList outputs;
Chris@0 173
Chris@31 174 string pluginKey = toPluginKey(pyPluginKey);
Chris@31 175 if (pluginKey == "") return 0;
Chris@31 176
Chris@31 177 PluginLoader *loader = PluginLoader::getInstance();
Chris@31 178
Chris@47 179 Plugin *plugin = loader->loadPlugin(pluginKey, 48000, 0);
Chris@31 180 if (!plugin) {
Chris@31 181 string pyerr("Failed to load plugin: "); pyerr += pluginKey;
Chris@134 182 PyErr_SetString(PyExc_TypeError, pyerr.c_str());
Chris@31 183 return 0;
Chris@0 184 }
Chris@0 185
Chris@31 186 outputs = plugin->getOutputDescriptors();
luis@7 187
Chris@0 188 PyObject *pyList = PyList_New(outputs.size());
Chris@0 189
Chris@0 190 for (size_t i = 0; i < outputs.size(); ++i) {
Chris@39 191 PyObject *pyOutputId =
Chris@112 192 StringConversion().string2py(outputs[i].identifier.c_str());
Chris@39 193 PyList_SET_ITEM(pyList, i, pyOutputId);
Chris@0 194 }
Chris@0 195
Chris@0 196 return pyList;
Chris@0 197 }
Chris@0 198
Chris@0 199 static PyObject *
Chris@79 200 load_plugin(PyObject *self, PyObject *args)
Chris@0 201 {
Chris@0 202 PyObject *pyPluginKey;
Chris@0 203 float inputSampleRate;
Chris@112 204 ssize_t adapterFlags;
Chris@0 205
Chris@112 206 if (!PyArg_ParseTuple(args,
Chris@112 207 #if (PY_MAJOR_VERSION >= 3)
Chris@112 208 "Ufn",
Chris@112 209 #else
Chris@112 210 "Sfn",
Chris@112 211 #endif
Chris@39 212 &pyPluginKey,
Chris@47 213 &inputSampleRate,
Chris@47 214 &adapterFlags)) {
Chris@39 215 PyErr_SetString(PyExc_TypeError,
Chris@79 216 "load_plugin() takes plugin key (string), sample rate (float), and adapter flags (int) arguments");
Chris@39 217 return 0; }
Chris@0 218
Chris@15 219 string pluginKey = toPluginKey(pyPluginKey);
Chris@16 220 if (pluginKey == "") return 0;
Chris@0 221
Chris@0 222 PluginLoader *loader = PluginLoader::getInstance();
luis@7 223
Chris@47 224 Plugin *plugin = loader->loadPlugin(pluginKey,
Chris@47 225 inputSampleRate,
Chris@47 226 adapterFlags);
luis@7 227 if (!plugin) {
Chris@39 228 string pyerr("Failed to load plugin: "); pyerr += pluginKey;
Chris@39 229 PyErr_SetString(PyExc_TypeError,pyerr.c_str());
Chris@39 230 return 0;
luis@7 231 }
Chris@15 232
Chris@31 233 return PyPluginObject_From_Plugin(plugin);
Chris@0 234 }
Chris@0 235
Chris@60 236 static PyObject *
Chris@79 237 frame_to_realtime(PyObject *self, PyObject *args)
Chris@60 238 {
Chris@112 239 ssize_t frame;
Chris@112 240 float rate;
Chris@60 241
Chris@112 242 if (!PyArg_ParseTuple(args, "nf",
Chris@60 243 &frame,
Chris@60 244 &rate)) {
Chris@60 245 PyErr_SetString(PyExc_TypeError,
Chris@112 246 "frame_to_realtime() takes frame (int) and sample rate (float) arguments");
Chris@60 247 return 0; }
Chris@60 248
Chris@60 249 RealTime rt = RealTime::frame2RealTime(frame, rate);
Chris@60 250 return PyRealTime_FromRealTime(rt);
Chris@60 251 }
Chris@60 252
Chris@18 253 // module methods table
Chris@0 254 static PyMethodDef vampyhost_methods[] = {
Chris@60 255
Chris@79 256 {"list_plugins", list_plugins, METH_NOARGS,
Chris@79 257 "list_plugins() -> Return a list of the plugin keys of all installed Vamp plugins." },
Chris@0 258
Chris@79 259 {"get_plugin_path", get_plugin_path, METH_NOARGS,
Chris@79 260 "get_plugin_path() -> Return a list of directories which will be searched for Vamp plugins. This may be changed by setting the VAMP_PATH environment variable."},
Chris@15 261
Chris@79 262 {"get_category_of", get_category_of, METH_VARARGS,
Chris@139 263 "get_category_of(plugin_key) -> Return the category of a Vamp plugin given its key, if known. The category is expressed as a list of nested types from least to most specific."},
Chris@0 264
Chris@79 265 {"get_library_for", get_library_for, METH_VARARGS,
Chris@139 266 "get_library_for(plugin_key) -> Return the file path of the Vamp plugin library in which the given plugin key is found, or an empty string if the plugin is not installed."},
Chris@0 267
Chris@79 268 {"get_outputs_of", get_outputs_of, METH_VARARGS,
Chris@139 269 "get_outputs_of(plugin_key) -> Return a list of the output identifiers of the plugin with the given key, if installed."},
Chris@0 270
Chris@79 271 {"load_plugin", load_plugin, METH_VARARGS,
Chris@139 272 "load_plugin(plugin_key, sample_rate, adapter_flags) -> Load the plugin that has the given key, if installed, and return the plugin object. The adapter_flags may be ADAPT_NONE, any additive combination of ADAPT_INPUT_DOMAIN, ADAPT_CHANNEL_COUNT, ADAPT_BUFFER_SIZE, or one of the special flags ADAPT_ALL_SAFE or ADAPT_ALL. If in doubt, pass ADAPT_ALL_SAFE. See the Vamp SDK documentation for the PluginLoader class for more details."},
Chris@0 273
Chris@79 274 {"frame_to_realtime", frame_to_realtime, METH_VARARGS,
Chris@79 275 "frame_to_realtime() -> Convert sample frame number and sample rate to a RealTime object." },
Chris@60 276
Chris@39 277 {0, 0} /* sentinel */
Chris@0 278 };
Chris@0 279
Chris@25 280 static int
Chris@25 281 setint(PyObject *d, const char *name, int value)
Chris@25 282 {
Chris@25 283 PyObject *v;
Chris@25 284 int err;
Chris@112 285 #if (PY_MAJOR_VERSION >= 3)
Chris@112 286 v = PyLong_FromLong((long)value);
Chris@112 287 #else
Chris@25 288 v = PyInt_FromLong((long)value);
Chris@112 289 #endif
Chris@25 290 err = PyDict_SetItemString(d, name, v);
Chris@25 291 Py_XDECREF(v);
Chris@25 292 return err;
Chris@25 293 }
Chris@14 294
Chris@0 295 /* Initialization function for the module (*must* be called initxx) */
Chris@0 296
Chris@112 297 #if (PY_MAJOR_VERSION >= 3)
Chris@112 298 static struct PyModuleDef vampyhostdef = {
Chris@112 299 PyModuleDef_HEAD_INIT,
Chris@112 300 "vampyhost",
Chris@112 301 "Load and run Vamp audio analysis plugins.",
Chris@112 302 -1,
Chris@112 303 vampyhost_methods,
Chris@112 304 0, 0, 0, 0
Chris@112 305 };
Chris@112 306 #else
Chris@112 307 PyDoc_STRVAR(module_doc, "Load and run Vamp audio analysis plugins.");
Chris@112 308 #endif
Chris@112 309
Chris@25 310 // module initialization (includes extern C {...} as necessary)
Chris@0 311 PyMODINIT_FUNC
Chris@112 312 #if (PY_MAJOR_VERSION >= 3)
Chris@112 313 PyInit_vampyhost(void)
Chris@112 314 #else
Chris@0 315 initvampyhost(void)
Chris@112 316 #endif
Chris@0 317 {
Chris@0 318 PyObject *m;
Chris@0 319
Chris@112 320 #if (PY_MAJOR_VERSION >= 3)
Chris@112 321 #define GOOD_RETURN m
Chris@112 322 #define BAD_RETURN 0
Chris@112 323 #else
Chris@112 324 #define GOOD_RETURN
Chris@112 325 #define BAD_RETURN
Chris@112 326 #endif
Chris@112 327
Chris@112 328 if (PyType_Ready(&RealTime_Type) < 0) return BAD_RETURN;
Chris@112 329 if (PyType_Ready(&Plugin_Type) < 0) return BAD_RETURN;
Chris@0 330
Chris@112 331 #if (PY_MAJOR_VERSION >= 3)
Chris@112 332 m = PyModule_Create(&vampyhostdef);
Chris@112 333 #else
Chris@0 334 m = Py_InitModule3("vampyhost", vampyhost_methods, module_doc);
Chris@112 335 #endif
Chris@112 336
Chris@25 337 if (!m) {
Chris@25 338 cerr << "ERROR: initvampyhost: Failed to initialise module" << endl;
Chris@112 339 return BAD_RETURN;
Chris@25 340 }
Chris@0 341
Chris@14 342 import_array();
Chris@14 343
Chris@17 344 PyModule_AddObject(m, "RealTime", (PyObject *)&RealTime_Type);
Chris@25 345 PyModule_AddObject(m, "Plugin", (PyObject *)&Plugin_Type);
Chris@25 346
Chris@25 347 // Some enum types
Chris@25 348 PyObject *dict = PyModule_GetDict(m);
Chris@25 349 if (!dict) {
Chris@25 350 cerr << "ERROR: initvampyhost: Failed to obtain module dictionary" << endl;
Chris@112 351 return BAD_RETURN;
Chris@25 352 }
Chris@25 353
Chris@77 354 if (setint(dict, "ONE_SAMPLE_PER_STEP",
Chris@25 355 Plugin::OutputDescriptor::OneSamplePerStep) < 0 ||
Chris@77 356 setint(dict, "FIXED_SAMPLE_RATE",
Chris@25 357 Plugin::OutputDescriptor::FixedSampleRate) < 0 ||
Chris@77 358 setint(dict, "VARIABLE_SAMPLE_RATE",
Chris@25 359 Plugin::OutputDescriptor::VariableSampleRate) < 0 ||
Chris@77 360 setint(dict, "TIME_DOMAIN",
Chris@25 361 Plugin::TimeDomain) < 0 ||
Chris@77 362 setint(dict, "FREQUENCY_DOMAIN",
Chris@47 363 Plugin::FrequencyDomain) < 0 ||
Chris@77 364 setint(dict, "ADAPT_NONE",
Chris@47 365 0) < 0 ||
Chris@77 366 setint(dict, "ADAPT_INPUT_DOMAIN",
Chris@57 367 PluginLoader::ADAPT_INPUT_DOMAIN) < 0 ||
Chris@77 368 setint(dict, "ADAPT_CHANNEL_COUNT",
Chris@47 369 PluginLoader::ADAPT_CHANNEL_COUNT) < 0 ||
Chris@77 370 setint(dict, "ADAPT_BUFFER_SIZE",
Chris@47 371 PluginLoader::ADAPT_BUFFER_SIZE) < 0 ||
Chris@77 372 setint(dict, "ADAPT_ALL_SAFE",
Chris@47 373 PluginLoader::ADAPT_ALL_SAFE) < 0 ||
Chris@77 374 setint(dict, "ADAPT_ALL",
Chris@139 375 PluginLoader::ADAPT_ALL) < 0 ||
Chris@139 376 setint(dict, "SHIFT_TIMESTAMP",
Chris@139 377 PluginInputDomainAdapter::ShiftTimestamp) < 0 ||
Chris@139 378 setint(dict, "SHIFT_DATA",
Chris@139 379 PluginInputDomainAdapter::ShiftData) < 0 ||
Chris@139 380 setint(dict, "NO_SHIFT",
Chris@139 381 PluginInputDomainAdapter::NoShift) < 0) {
Chris@25 382 cerr << "ERROR: initvampyhost: Failed to add enums to module dictionary" << endl;
Chris@112 383 return BAD_RETURN;
Chris@25 384 }
Chris@112 385
Chris@112 386 return GOOD_RETURN;
Chris@0 387 }