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@0
|
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@2
|
66 PyDoc_STRVAR(xx_foo_doc, "Some description"); //!!!
|
Chris@0
|
67
|
Chris@28
|
68 //!!! todo: conv errors
|
Chris@28
|
69
|
Chris@0
|
70 static PyObject *
|
Chris@23
|
71 vampyhost_enumeratePlugins(PyObject *self, PyObject *)
|
Chris@0
|
72 {
|
Chris@21
|
73 cerr << "vampyhost_enumeratePlugins" << endl;
|
Chris@21
|
74
|
Chris@0
|
75 PluginLoader *loader = PluginLoader::getInstance();
|
Chris@0
|
76 vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
|
Chris@29
|
77 VectorConversion conv;
|
Chris@15
|
78 return conv.PyValue_From_StringVector(plugins);
|
Chris@0
|
79 }
|
Chris@0
|
80
|
Chris@15
|
81 static PyObject *
|
Chris@23
|
82 vampyhost_getPluginPath(PyObject *self, PyObject *)
|
Chris@15
|
83 {
|
Chris@21
|
84 cerr << "vampyhost_getPluginPath" << endl;
|
Chris@21
|
85
|
Chris@15
|
86 vector<string> path = PluginHostAdapter::getPluginPath();
|
Chris@29
|
87 VectorConversion conv;
|
Chris@15
|
88 return conv.PyValue_From_StringVector(path);
|
Chris@15
|
89 }
|
Chris@0
|
90
|
Chris@15
|
91 static string toPluginKey(PyObject *pyPluginKey)
|
Chris@0
|
92 {
|
Chris@21
|
93 cerr << "toPluginKey" << endl;
|
Chris@21
|
94
|
Chris@0
|
95 //convert to stl string
|
Chris@0
|
96 string pluginKey(PyString_AS_STRING(pyPluginKey));
|
Chris@0
|
97
|
Chris@0
|
98 //check pluginKey Validity
|
Chris@0
|
99 string::size_type ki = pluginKey.find(':');
|
Chris@0
|
100 if (ki == string::npos) {
|
Chris@0
|
101 PyErr_SetString(PyExc_TypeError,
|
Chris@15
|
102 "Plugin key must be of the form library:identifier");
|
Chris@15
|
103 return "";
|
Chris@0
|
104 }
|
Chris@0
|
105
|
Chris@15
|
106 return pluginKey;
|
Chris@15
|
107 }
|
Chris@15
|
108
|
Chris@15
|
109 static PyObject *
|
Chris@15
|
110 vampyhost_getLibraryFor(PyObject *self, PyObject *args)
|
Chris@15
|
111 {
|
Chris@21
|
112 cerr << "vampyhost_getLibraryFor" << endl;
|
Chris@21
|
113
|
Chris@15
|
114 PyObject *pyPluginKey;
|
Chris@15
|
115
|
Chris@15
|
116 if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) {
|
Chris@15
|
117 PyErr_SetString(PyExc_TypeError,
|
Chris@15
|
118 "getLibraryPathForPlugin() takes plugin key (string) argument");
|
Chris@16
|
119 return 0; }
|
Chris@15
|
120
|
Chris@15
|
121 string pluginKey = toPluginKey(pyPluginKey);
|
Chris@16
|
122 if (pluginKey == "") return 0;
|
Chris@15
|
123
|
Chris@0
|
124 PluginLoader *loader = PluginLoader::getInstance();
|
Chris@0
|
125 string path = loader->getLibraryPathForPlugin(pluginKey);
|
Chris@0
|
126 PyObject *pyPath = PyString_FromString(path.c_str());
|
Chris@0
|
127 return pyPath;
|
Chris@0
|
128 }
|
Chris@0
|
129
|
Chris@0
|
130 static PyObject *
|
Chris@0
|
131 vampyhost_getPluginCategory(PyObject *self, PyObject *args)
|
Chris@0
|
132 {
|
Chris@21
|
133 cerr << "vampyhost_getPluginCategory" << endl;
|
Chris@21
|
134
|
Chris@0
|
135 PyObject *pyPluginKey;
|
Chris@0
|
136
|
Chris@0
|
137 if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) {
|
Chris@0
|
138 PyErr_SetString(PyExc_TypeError,
|
Chris@15
|
139 "getPluginCategory() takes plugin key (string) argument");
|
Chris@16
|
140 return 0; }
|
Chris@0
|
141
|
Chris@15
|
142 string pluginKey = toPluginKey(pyPluginKey);
|
Chris@16
|
143 if (pluginKey == "") return 0;
|
Chris@0
|
144
|
Chris@0
|
145 PluginLoader *loader = PluginLoader::getInstance();
|
luis@7
|
146 PluginLoader::PluginCategoryHierarchy
|
Chris@0
|
147 category = loader->getPluginCategory(pluginKey);
|
Chris@0
|
148
|
Chris@29
|
149 VectorConversion conv;
|
Chris@15
|
150 return conv.PyValue_From_StringVector(category);
|
Chris@0
|
151 }
|
Chris@0
|
152
|
Chris@0
|
153 static PyObject *
|
Chris@0
|
154 vampyhost_getOutputList(PyObject *self, PyObject *args)
|
Chris@0
|
155 {
|
Chris@21
|
156 cerr << "vampyhost_getOutputList" << endl;
|
Chris@21
|
157
|
Chris@31
|
158 PyObject *pyPluginKey;
|
Chris@31
|
159
|
Chris@31
|
160 if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) {
|
Chris@31
|
161 PyErr_SetString(PyExc_TypeError,
|
Chris@31
|
162 "getOutputList() takes plugin key (string) argument");
|
Chris@31
|
163 return 0; }
|
Chris@31
|
164
|
Chris@15
|
165 Plugin::OutputList outputs;
|
Chris@0
|
166
|
Chris@31
|
167 string pluginKey = toPluginKey(pyPluginKey);
|
Chris@31
|
168 if (pluginKey == "") return 0;
|
Chris@31
|
169
|
Chris@31
|
170 PluginLoader *loader = PluginLoader::getInstance();
|
Chris@31
|
171
|
Chris@31
|
172 Plugin *plugin = loader->loadPlugin
|
Chris@31
|
173 (pluginKey, 48000, PluginLoader::ADAPT_ALL_SAFE);
|
Chris@31
|
174 if (!plugin) {
|
Chris@31
|
175 string pyerr("Failed to load plugin: "); pyerr += pluginKey;
|
Chris@31
|
176 PyErr_SetString(PyExc_TypeError,pyerr.c_str());
|
Chris@31
|
177 return 0;
|
Chris@0
|
178 }
|
Chris@0
|
179
|
Chris@31
|
180 outputs = plugin->getOutputDescriptors();
|
luis@7
|
181
|
Chris@0
|
182 PyObject *pyList = PyList_New(outputs.size());
|
Chris@0
|
183
|
Chris@0
|
184 for (size_t i = 0; i < outputs.size(); ++i) {
|
luis@7
|
185 PyObject *pyOutputId =
|
Chris@0
|
186 PyString_FromString(outputs[i].identifier.c_str());
|
Chris@15
|
187 PyList_SET_ITEM(pyList, i, pyOutputId);
|
Chris@0
|
188 }
|
Chris@0
|
189
|
Chris@0
|
190 return pyList;
|
Chris@0
|
191 }
|
Chris@0
|
192
|
Chris@0
|
193 static PyObject *
|
Chris@0
|
194 vampyhost_loadPlugin(PyObject *self, PyObject *args)
|
Chris@0
|
195 {
|
Chris@21
|
196 cerr << "vampyhost_loadPlugin" << endl;
|
Chris@21
|
197
|
Chris@0
|
198 PyObject *pyPluginKey;
|
Chris@0
|
199 float inputSampleRate;
|
Chris@0
|
200
|
luis@7
|
201 if (!PyArg_ParseTuple(args, "Sf",
|
Chris@0
|
202 &pyPluginKey,
|
Chris@0
|
203 &inputSampleRate)) {
|
Chris@0
|
204 PyErr_SetString(PyExc_TypeError,
|
Chris@20
|
205 "loadPlugin() takes plugin key (string) and sample rate (float) arguments");
|
Chris@16
|
206 return 0; }
|
Chris@0
|
207
|
Chris@15
|
208 string pluginKey = toPluginKey(pyPluginKey);
|
Chris@16
|
209 if (pluginKey == "") return 0;
|
Chris@0
|
210
|
Chris@0
|
211 PluginLoader *loader = PluginLoader::getInstance();
|
luis@7
|
212
|
Chris@15
|
213 Plugin *plugin = loader->loadPlugin(pluginKey, inputSampleRate,
|
Chris@15
|
214 PluginLoader::ADAPT_ALL_SAFE);
|
luis@7
|
215 if (!plugin) {
|
Chris@0
|
216 string pyerr("Failed to load plugin: "); pyerr += pluginKey;
|
luis@7
|
217 PyErr_SetString(PyExc_TypeError,pyerr.c_str());
|
Chris@16
|
218 return 0;
|
luis@7
|
219 }
|
Chris@15
|
220
|
Chris@31
|
221 return PyPluginObject_From_Plugin(plugin);
|
Chris@0
|
222 }
|
Chris@0
|
223
|
Chris@18
|
224 // module methods table
|
Chris@0
|
225 static PyMethodDef vampyhost_methods[] = {
|
Chris@0
|
226
|
Chris@18
|
227 {"listPlugins", vampyhost_enumeratePlugins, METH_NOARGS,
|
Chris@0
|
228 xx_foo_doc},
|
Chris@0
|
229
|
Chris@15
|
230 {"getPluginPath", vampyhost_getPluginPath, METH_NOARGS,
|
Chris@15
|
231 xx_foo_doc},
|
Chris@15
|
232
|
Chris@18
|
233 {"getCategoryOf", vampyhost_getPluginCategory, METH_VARARGS,
|
Chris@0
|
234 xx_foo_doc},
|
Chris@0
|
235
|
Chris@18
|
236 {"getLibraryFor", vampyhost_getLibraryFor, METH_VARARGS,
|
Chris@0
|
237 xx_foo_doc},
|
Chris@0
|
238
|
Chris@18
|
239 {"getOutputsOf", vampyhost_getOutputList, METH_VARARGS,
|
Chris@0
|
240 xx_foo_doc},
|
Chris@0
|
241
|
Chris@0
|
242 {"loadPlugin", vampyhost_loadPlugin, METH_VARARGS,
|
Chris@0
|
243 xx_foo_doc},
|
Chris@0
|
244
|
Chris@16
|
245 {0, 0} /* sentinel */
|
Chris@0
|
246 };
|
Chris@0
|
247
|
Chris@0
|
248 //Documentation for our new module
|
Chris@0
|
249 PyDoc_STRVAR(module_doc, "This is a template module just for instruction.");
|
Chris@0
|
250
|
Chris@25
|
251 static int
|
Chris@25
|
252 setint(PyObject *d, const char *name, int value)
|
Chris@25
|
253 {
|
Chris@25
|
254 PyObject *v;
|
Chris@25
|
255 int err;
|
Chris@25
|
256 v = PyInt_FromLong((long)value);
|
Chris@25
|
257 err = PyDict_SetItemString(d, name, v);
|
Chris@25
|
258 Py_XDECREF(v);
|
Chris@25
|
259 return err;
|
Chris@25
|
260 }
|
Chris@14
|
261
|
Chris@0
|
262 /* Initialization function for the module (*must* be called initxx) */
|
Chris@0
|
263
|
Chris@25
|
264 // module initialization (includes extern C {...} as necessary)
|
Chris@0
|
265 PyMODINIT_FUNC
|
Chris@0
|
266 initvampyhost(void)
|
Chris@0
|
267 {
|
Chris@0
|
268 PyObject *m;
|
Chris@0
|
269
|
Chris@25
|
270 if (PyType_Ready(&RealTime_Type) < 0) return;
|
Chris@25
|
271 if (PyType_Ready(&Plugin_Type) < 0) return;
|
Chris@0
|
272
|
Chris@0
|
273 m = Py_InitModule3("vampyhost", vampyhost_methods, module_doc);
|
Chris@25
|
274 if (!m) {
|
Chris@25
|
275 cerr << "ERROR: initvampyhost: Failed to initialise module" << endl;
|
Chris@25
|
276 return;
|
Chris@25
|
277 }
|
Chris@0
|
278
|
Chris@14
|
279 import_array();
|
Chris@14
|
280
|
Chris@17
|
281 PyModule_AddObject(m, "RealTime", (PyObject *)&RealTime_Type);
|
Chris@25
|
282 PyModule_AddObject(m, "Plugin", (PyObject *)&Plugin_Type);
|
Chris@25
|
283
|
Chris@25
|
284 // Some enum types
|
Chris@25
|
285 PyObject *dict = PyModule_GetDict(m);
|
Chris@25
|
286 if (!dict) {
|
Chris@25
|
287 cerr << "ERROR: initvampyhost: Failed to obtain module dictionary" << endl;
|
Chris@25
|
288 return;
|
Chris@25
|
289 }
|
Chris@25
|
290
|
Chris@25
|
291 if (setint(dict, "OneSamplePerStep",
|
Chris@25
|
292 Plugin::OutputDescriptor::OneSamplePerStep) < 0 ||
|
Chris@25
|
293 setint(dict, "FixedSampleRate",
|
Chris@25
|
294 Plugin::OutputDescriptor::FixedSampleRate) < 0 ||
|
Chris@25
|
295 setint(dict, "VariableSampleRate",
|
Chris@25
|
296 Plugin::OutputDescriptor::VariableSampleRate) < 0 ||
|
Chris@25
|
297 setint(dict, "TimeDomain",
|
Chris@25
|
298 Plugin::TimeDomain) < 0 ||
|
Chris@25
|
299 setint(dict, "FrequencyDomain",
|
Chris@25
|
300 Plugin::FrequencyDomain) < 0) {
|
Chris@25
|
301 cerr << "ERROR: initvampyhost: Failed to add enums to module dictionary" << endl;
|
Chris@25
|
302 return;
|
Chris@25
|
303 }
|
Chris@0
|
304 }
|