annotate native/vampyhost.cpp @ 60:2437c52b4d62

Add frame2RealTime
author Chris Cannam
date Wed, 14 Jan 2015 10:36:22 +0000
parents a0243984805b
children aa8491a11530
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@26 10 Copyright 2008-2014 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@16 55 #include "PyRealTime.h"
Chris@0 56
Chris@0 57 #include <iostream>
Chris@0 58 #include <string>
Chris@0 59
Chris@0 60 #include <cmath>
Chris@0 61
Chris@0 62 using namespace std;
Chris@0 63 using namespace Vamp;
Chris@31 64 using namespace Vamp::HostExt;
Chris@21 65
Chris@0 66 static PyObject *
Chris@39 67 enumeratePlugins(PyObject *self, PyObject *)
Chris@0 68 {
Chris@0 69 PluginLoader *loader = PluginLoader::getInstance();
Chris@0 70 vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
Chris@29 71 VectorConversion conv;
Chris@15 72 return conv.PyValue_From_StringVector(plugins);
Chris@0 73 }
Chris@0 74
Chris@15 75 static PyObject *
Chris@39 76 getPluginPath(PyObject *self, PyObject *)
Chris@15 77 {
Chris@15 78 vector<string> path = PluginHostAdapter::getPluginPath();
Chris@29 79 VectorConversion conv;
Chris@15 80 return conv.PyValue_From_StringVector(path);
Chris@15 81 }
Chris@0 82
Chris@15 83 static string toPluginKey(PyObject *pyPluginKey)
Chris@0 84 {
Chris@36 85 // convert to stl string
Chris@0 86 string pluginKey(PyString_AS_STRING(pyPluginKey));
Chris@0 87
Chris@36 88 // check pluginKey validity
Chris@0 89 string::size_type ki = pluginKey.find(':');
Chris@0 90 if (ki == string::npos) {
Chris@39 91 PyErr_SetString(PyExc_TypeError,
Chris@39 92 "Plugin key must be of the form library:identifier");
Chris@39 93 return "";
Chris@0 94 }
Chris@0 95
Chris@15 96 return pluginKey;
Chris@15 97 }
Chris@15 98
Chris@15 99 static PyObject *
Chris@39 100 getLibraryFor(PyObject *self, PyObject *args)
Chris@15 101 {
Chris@15 102 PyObject *pyPluginKey;
Chris@15 103
Chris@15 104 if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) {
Chris@39 105 PyErr_SetString(PyExc_TypeError,
Chris@39 106 "getLibraryPathForPlugin() takes plugin key (string) argument");
Chris@39 107 return 0; }
Chris@15 108
Chris@15 109 string pluginKey = toPluginKey(pyPluginKey);
Chris@16 110 if (pluginKey == "") return 0;
Chris@15 111
Chris@0 112 PluginLoader *loader = PluginLoader::getInstance();
Chris@0 113 string path = loader->getLibraryPathForPlugin(pluginKey);
Chris@0 114 PyObject *pyPath = PyString_FromString(path.c_str());
Chris@0 115 return pyPath;
Chris@0 116 }
Chris@0 117
Chris@0 118 static PyObject *
Chris@39 119 getPluginCategory(PyObject *self, PyObject *args)
Chris@0 120 {
Chris@0 121 PyObject *pyPluginKey;
Chris@0 122
Chris@0 123 if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) {
Chris@39 124 PyErr_SetString(PyExc_TypeError,
Chris@39 125 "getPluginCategory() takes plugin key (string) argument");
Chris@39 126 return 0; }
Chris@0 127
Chris@15 128 string pluginKey = toPluginKey(pyPluginKey);
Chris@16 129 if (pluginKey == "") return 0;
Chris@0 130
Chris@0 131 PluginLoader *loader = PluginLoader::getInstance();
luis@7 132 PluginLoader::PluginCategoryHierarchy
Chris@39 133 category = loader->getPluginCategory(pluginKey);
Chris@0 134
Chris@29 135 VectorConversion conv;
Chris@15 136 return conv.PyValue_From_StringVector(category);
Chris@0 137 }
Chris@0 138
Chris@0 139 static PyObject *
Chris@39 140 getOutputList(PyObject *self, PyObject *args)
Chris@0 141 {
Chris@31 142 PyObject *pyPluginKey;
Chris@31 143
Chris@31 144 if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) {
Chris@39 145 PyErr_SetString(PyExc_TypeError,
Chris@39 146 "getOutputList() takes plugin key (string) argument");
Chris@39 147 return 0; }
Chris@31 148
Chris@15 149 Plugin::OutputList outputs;
Chris@0 150
Chris@31 151 string pluginKey = toPluginKey(pyPluginKey);
Chris@31 152 if (pluginKey == "") return 0;
Chris@31 153
Chris@31 154 PluginLoader *loader = PluginLoader::getInstance();
Chris@31 155
Chris@47 156 Plugin *plugin = loader->loadPlugin(pluginKey, 48000, 0);
Chris@31 157 if (!plugin) {
Chris@31 158 string pyerr("Failed to load plugin: "); pyerr += pluginKey;
Chris@31 159 PyErr_SetString(PyExc_TypeError,pyerr.c_str());
Chris@31 160 return 0;
Chris@0 161 }
Chris@0 162
Chris@31 163 outputs = plugin->getOutputDescriptors();
luis@7 164
Chris@0 165 PyObject *pyList = PyList_New(outputs.size());
Chris@0 166
Chris@0 167 for (size_t i = 0; i < outputs.size(); ++i) {
Chris@39 168 PyObject *pyOutputId =
Chris@39 169 PyString_FromString(outputs[i].identifier.c_str());
Chris@39 170 PyList_SET_ITEM(pyList, i, pyOutputId);
Chris@0 171 }
Chris@0 172
Chris@0 173 return pyList;
Chris@0 174 }
Chris@0 175
Chris@0 176 static PyObject *
Chris@39 177 loadPlugin(PyObject *self, PyObject *args)
Chris@0 178 {
Chris@0 179 PyObject *pyPluginKey;
Chris@0 180 float inputSampleRate;
Chris@47 181 int adapterFlags;
Chris@0 182
Chris@47 183 if (!PyArg_ParseTuple(args, "Sfn",
Chris@39 184 &pyPluginKey,
Chris@47 185 &inputSampleRate,
Chris@47 186 &adapterFlags)) {
Chris@39 187 PyErr_SetString(PyExc_TypeError,
Chris@47 188 "loadPlugin() takes plugin key (string), sample rate (float), and adapter flags (int) arguments");
Chris@39 189 return 0; }
Chris@0 190
Chris@15 191 string pluginKey = toPluginKey(pyPluginKey);
Chris@16 192 if (pluginKey == "") return 0;
Chris@0 193
Chris@0 194 PluginLoader *loader = PluginLoader::getInstance();
luis@7 195
Chris@47 196 Plugin *plugin = loader->loadPlugin(pluginKey,
Chris@47 197 inputSampleRate,
Chris@47 198 adapterFlags);
luis@7 199 if (!plugin) {
Chris@39 200 string pyerr("Failed to load plugin: "); pyerr += pluginKey;
Chris@39 201 PyErr_SetString(PyExc_TypeError,pyerr.c_str());
Chris@39 202 return 0;
luis@7 203 }
Chris@15 204
Chris@31 205 return PyPluginObject_From_Plugin(plugin);
Chris@0 206 }
Chris@0 207
Chris@60 208 static PyObject *
Chris@60 209 frame2RealTime(PyObject *self, PyObject *args)
Chris@60 210 {
Chris@60 211 int frame;
Chris@60 212 int rate;
Chris@60 213
Chris@60 214 if (!PyArg_ParseTuple(args, "nn",
Chris@60 215 &frame,
Chris@60 216 &rate)) {
Chris@60 217 PyErr_SetString(PyExc_TypeError,
Chris@60 218 "frame2RealTime() takes frame (int) and sample rate (int) arguments");
Chris@60 219 return 0; }
Chris@60 220
Chris@60 221 RealTime rt = RealTime::frame2RealTime(frame, rate);
Chris@60 222 return PyRealTime_FromRealTime(rt);
Chris@60 223 }
Chris@60 224
Chris@18 225 // module methods table
Chris@0 226 static PyMethodDef vampyhost_methods[] = {
Chris@60 227
Chris@39 228 {"listPlugins", enumeratePlugins, METH_NOARGS,
Chris@39 229 "listPlugins() -> Return a list of the plugin keys of all installed Vamp plugins." },
Chris@0 230
Chris@39 231 {"getPluginPath", getPluginPath, METH_NOARGS,
Chris@39 232 "getPluginPath() -> 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 233
Chris@39 234 {"getCategoryOf", getPluginCategory, METH_VARARGS,
Chris@39 235 "getCategoryOf(pluginKey) -> 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 236
Chris@39 237 {"getLibraryFor", getLibraryFor, METH_VARARGS,
Chris@39 238 "getLibraryFor(pluginKey) -> 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 239
Chris@39 240 {"getOutputsOf", getOutputList, METH_VARARGS,
Chris@39 241 "getOutputsOf(pluginKey) -> Return a list of the output identifiers of the plugin with the given key, if installed."},
Chris@0 242
Chris@39 243 {"loadPlugin", loadPlugin, METH_VARARGS,
Chris@39 244 "loadPlugin(pluginKey, samplerate) -> Load the plugin that has the given key, if installed, and return the plugin object."},
Chris@0 245
Chris@60 246 {"frame2RealTime", frame2RealTime, METH_VARARGS,
Chris@60 247 "frame2RealTime() -> Convert sample frame number and sample rate to a RealTime object." },
Chris@60 248
Chris@39 249 {0, 0} /* sentinel */
Chris@0 250 };
Chris@0 251
Chris@39 252 PyDoc_STRVAR(module_doc, "Load and run Vamp audio analysis plugins.");
Chris@0 253
Chris@25 254 static int
Chris@25 255 setint(PyObject *d, const char *name, int value)
Chris@25 256 {
Chris@25 257 PyObject *v;
Chris@25 258 int err;
Chris@25 259 v = PyInt_FromLong((long)value);
Chris@25 260 err = PyDict_SetItemString(d, name, v);
Chris@25 261 Py_XDECREF(v);
Chris@25 262 return err;
Chris@25 263 }
Chris@14 264
Chris@0 265 /* Initialization function for the module (*must* be called initxx) */
Chris@0 266
Chris@25 267 // module initialization (includes extern C {...} as necessary)
Chris@0 268 PyMODINIT_FUNC
Chris@0 269 initvampyhost(void)
Chris@0 270 {
Chris@0 271 PyObject *m;
Chris@0 272
Chris@25 273 if (PyType_Ready(&RealTime_Type) < 0) return;
Chris@25 274 if (PyType_Ready(&Plugin_Type) < 0) return;
Chris@0 275
Chris@0 276 m = Py_InitModule3("vampyhost", vampyhost_methods, module_doc);
Chris@25 277 if (!m) {
Chris@25 278 cerr << "ERROR: initvampyhost: Failed to initialise module" << endl;
Chris@25 279 return;
Chris@25 280 }
Chris@0 281
Chris@14 282 import_array();
Chris@14 283
Chris@17 284 PyModule_AddObject(m, "RealTime", (PyObject *)&RealTime_Type);
Chris@25 285 PyModule_AddObject(m, "Plugin", (PyObject *)&Plugin_Type);
Chris@25 286
Chris@25 287 // Some enum types
Chris@25 288 PyObject *dict = PyModule_GetDict(m);
Chris@25 289 if (!dict) {
Chris@25 290 cerr << "ERROR: initvampyhost: Failed to obtain module dictionary" << endl;
Chris@25 291 return;
Chris@25 292 }
Chris@25 293
Chris@25 294 if (setint(dict, "OneSamplePerStep",
Chris@25 295 Plugin::OutputDescriptor::OneSamplePerStep) < 0 ||
Chris@25 296 setint(dict, "FixedSampleRate",
Chris@25 297 Plugin::OutputDescriptor::FixedSampleRate) < 0 ||
Chris@25 298 setint(dict, "VariableSampleRate",
Chris@25 299 Plugin::OutputDescriptor::VariableSampleRate) < 0 ||
Chris@25 300 setint(dict, "TimeDomain",
Chris@25 301 Plugin::TimeDomain) < 0 ||
Chris@25 302 setint(dict, "FrequencyDomain",
Chris@47 303 Plugin::FrequencyDomain) < 0 ||
Chris@47 304 setint(dict, "AdaptNone",
Chris@47 305 0) < 0 ||
Chris@57 306 setint(dict, "AdaptInputDomain",
Chris@57 307 PluginLoader::ADAPT_INPUT_DOMAIN) < 0 ||
Chris@47 308 setint(dict, "AdaptChannelCount",
Chris@47 309 PluginLoader::ADAPT_CHANNEL_COUNT) < 0 ||
Chris@47 310 setint(dict, "AdaptBufferSize",
Chris@47 311 PluginLoader::ADAPT_BUFFER_SIZE) < 0 ||
Chris@47 312 setint(dict, "AdaptAllSafe",
Chris@47 313 PluginLoader::ADAPT_ALL_SAFE) < 0 ||
Chris@47 314 setint(dict, "AdaptAll",
Chris@47 315 PluginLoader::ADAPT_ALL) < 0) {
Chris@25 316 cerr << "ERROR: initvampyhost: Failed to add enums to module dictionary" << endl;
Chris@25 317 return;
Chris@25 318 }
Chris@0 319 }