comparison vampyhost.cpp @ 0:68f3f32565b4

Import the early draft version
author Chris Cannam
date Mon, 22 Oct 2012 16:10:46 +0100
parents
children cb0d3af1be4d
comparison
equal deleted inserted replaced
-1:000000000000 0:68f3f32565b4
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 //include for python extension module: must be first
4 #include <Python.h>
5 #include <vampyhost.h>
6 #include <pyRealTime.h>
7
8 //includes for vamp host
9 #include "vamp-sdk/Plugin.h"
10 #include "vamp-sdk/PluginHostAdapter.h"
11 #include "vamp-sdk/hostext/PluginChannelAdapter.h"
12 #include "vamp-sdk/hostext/PluginInputDomainAdapter.h"
13 #include "vamp-sdk/hostext/PluginLoader.h"
14 //#include "vamp/vamp.h"
15
16 #include <iostream>
17 #include <fstream>
18 #include <set>
19 #include <sndfile.h>
20
21 #include <cstring>
22 #include <cstdlib>
23 #include <string>
24
25 #include "system.h"
26
27 #include <cmath>
28
29
30 using namespace std;
31 using namespace Vamp;
32
33 using Vamp::Plugin;
34 using Vamp::PluginHostAdapter;
35 using Vamp::RealTime;
36 using Vamp::HostExt::PluginLoader;
37
38 #define HOST_VERSION "1.1"
39
40
41 /* MODULE HELPER FUNCTIONS */
42 PyDoc_STRVAR(xx_foo_doc, "Some description");
43
44 /*obtain C plugin handle and key from pyCobject */
45 int getPluginHandle
46 (PyObject *pyPluginHandle, Plugin **plugin, string **pKey=NULL) {
47
48 //char errormsg[]="Wrong input argument: Plugin Handle required.";
49
50 *plugin = NULL;
51 if (!PyCObject_Check(pyPluginHandle)) return NULL;
52
53 //try to convert to Plugin pointer
54 Plugin *p = (Plugin*) PyCObject_AsVoidPtr(pyPluginHandle);
55 if (!p) return NULL;
56
57 string pId;
58
59 if (pKey) {
60 *pKey = (string*) PyCObject_GetDesc(pyPluginHandle);
61 if (!*pKey) return NULL;
62 pId = *(string*) *pKey;
63
64 } else {
65
66 void *pKey = PyCObject_GetDesc(pyPluginHandle);
67 if (!pKey) return NULL;
68 pId = *(string*) pKey;
69 }
70
71 string::size_type pos = pId.find(':');
72 if (pos == string::npos) return NULL;
73
74 pId = pId.substr(pId.rfind(':')+1);
75 string identifier = p->getIdentifier();
76
77 if (pId.compare(identifier)) return NULL;
78
79 *plugin = p;
80 return true;
81 }
82
83 /*
84 ----------------------------------------------------------------
85 */
86
87
88
89 /*
90 VAMPYHOST MAIN
91 ---------------------------------------------------------------------
92 */
93
94 /* ENUMERATE PLUGINS*/
95
96 static PyObject *
97 vampyhost_enumeratePlugins(PyObject *self, PyObject *args)
98 {
99 string retType;
100
101 if (!PyArg_ParseTuple(args, "|s:enumeratePlugins", &retType))
102 return NULL;
103
104 //list available plugins
105 PluginLoader *loader = PluginLoader::getInstance();
106 vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
107
108 //library Map
109 typedef multimap<string, PluginLoader::PluginKey> LibraryMap;
110 LibraryMap libraryMap;
111
112 //New list object
113 PyObject *pyList = PyList_New(plugins.size());
114
115 for (size_t i = 0; i < plugins.size(); ++i) {
116 string path = loader->getLibraryPathForPlugin(plugins[i]);
117 libraryMap.insert(LibraryMap::value_type(path, plugins[i]));
118
119 PyObject *pyPluginKey = PyString_FromString(plugins[i].c_str());
120 PyList_SET_ITEM(pyList,i,pyPluginKey);
121
122 }
123
124 PyList_Sort(pyList);
125 return pyList;
126 }
127
128
129 /* GET PLUGIN LIBRARY PATH*/
130
131 static PyObject *
132 vampyhost_getLibraryPath(PyObject *self, PyObject *args)
133 {
134 PyObject *pyPluginKey;
135
136 if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) {
137 PyErr_SetString(PyExc_TypeError,
138 "String input argument required: pluginKey");
139 return NULL; }
140
141 //convert to stl string
142 string pluginKey(PyString_AS_STRING(pyPluginKey));
143
144 //check pluginKey Validity
145 string::size_type ki = pluginKey.find(':');
146 if (ki == string::npos) {
147 PyErr_SetString(PyExc_TypeError,
148 "String input argument required: pluginLibrary:Identifier");
149 return NULL;
150 }
151
152 PluginLoader *loader = PluginLoader::getInstance();
153 string path = loader->getLibraryPathForPlugin(pluginKey);
154 PyObject *pyPath = PyString_FromString(path.c_str());
155 return pyPath;
156 }
157
158
159 /* GET PLUGIN CATEGORY*/
160
161 static PyObject *
162 vampyhost_getPluginCategory(PyObject *self, PyObject *args)
163 {
164 PyObject *pyPluginKey;
165
166 if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) {
167 PyErr_SetString(PyExc_TypeError,
168 "String input argument required: pluginKey");
169 return NULL; }
170
171 //convert to stl string
172 string pluginKey(PyString_AS_STRING(pyPluginKey));
173
174 //check pluginKey Validity
175 string::size_type ki = pluginKey.find(':');
176 if (ki == string::npos) {
177 PyErr_SetString(PyExc_TypeError,
178 "String input argument required: pluginLibrary:Identifier");
179 return NULL;
180 }
181
182 PluginLoader *loader = PluginLoader::getInstance();
183 PluginLoader::PluginCategoryHierarchy
184 category = loader->getPluginCategory(pluginKey);
185 string catstring;
186
187 if (!category.empty()) {
188 catstring = "";
189 for (size_t ci = 0; ci < category.size(); ++ci) {
190 catstring.append(category[ci]);
191 catstring.append(" ");
192 }
193 PyObject *pyCat = PyString_FromString(catstring.c_str());
194 return pyCat;
195 }
196 PyObject *pyCat = PyString_FromString("");
197 return pyCat;
198 }
199
200
201
202 /* GET PLUGIN OUTPUT LIST*/
203
204 static PyObject *
205 vampyhost_getOutputList(PyObject *self, PyObject *args)
206 {
207 PyObject *pyPluginHandle;
208 string pluginKey;
209
210 if (!PyArg_ParseTuple(args, "O", &pyPluginHandle)) {
211 PyErr_SetString(PyExc_TypeError,
212 "Invalid argument: plugin handle or plugin key required.");
213 return NULL;
214 }
215
216 //check if we have a plugin key string or a handle object
217 if (PyString_Check(pyPluginHandle) ) {
218
219 pluginKey.assign(PyString_AS_STRING(pyPluginHandle));
220 //check pluginKey Validity
221 string::size_type ki = pluginKey.find(':');
222 if (ki == string::npos) {
223 PyErr_SetString(PyExc_TypeError,
224 "String input argument required: pluginLibrary:Identifier");
225 return NULL;
226 }
227
228 } else {
229
230 string *key;
231 Plugin *plugin;
232
233 if ( !getPluginHandle(pyPluginHandle, &plugin, &key) ) {
234 PyErr_SetString(PyExc_TypeError,
235 "Invalid or deleted plugin handle.");
236 return NULL; }
237 pluginKey.assign(*key);
238 }
239
240 //This code creates new instance of the plugin anyway
241 PluginLoader *loader = PluginLoader::getInstance();
242
243 //load plugin
244 Plugin *plugin = loader->loadPlugin (pluginKey, 48000);
245 if (!plugin) {
246 string pyerr("Failed to load plugin: "); pyerr += pluginKey;
247 PyErr_SetString(PyExc_TypeError,pyerr.c_str());
248 return NULL;
249 }
250
251 Plugin::OutputList outputs = plugin->getOutputDescriptors();
252 //Plugin::OutputDescriptor od;
253
254 if (outputs.size()<1) {
255 string pyerr("Plugin has no output: "); pyerr += pluginKey;
256 PyErr_SetString(PyExc_TypeError,pyerr.c_str());
257 return NULL;
258 }
259
260 //New list object
261 PyObject *pyList = PyList_New(outputs.size());
262
263 for (size_t i = 0; i < outputs.size(); ++i) {
264 PyObject *pyOutputId =
265 PyString_FromString(outputs[i].identifier.c_str());
266 PyList_SET_ITEM(pyList,i,pyOutputId);
267 }
268
269 delete plugin;
270 return pyList;
271 }
272
273
274
275 /* LOAD PLUGIN */
276
277 static PyObject *
278 vampyhost_loadPlugin(PyObject *self, PyObject *args)
279 {
280 PyObject *pyPluginKey;
281 float inputSampleRate;
282
283 if (!PyArg_ParseTuple(args, "Sf",
284 &pyPluginKey,
285 &inputSampleRate)) {
286 PyErr_SetString(PyExc_TypeError,
287 "String input argument required: pluginKey");
288 return NULL; }
289
290 //convert to stl string
291 string pluginKey(PyString_AS_STRING(pyPluginKey));
292
293 //check pluginKey Validity
294 string::size_type ki = pluginKey.find(':');
295 if (ki == string::npos) {
296 PyErr_SetString(PyExc_TypeError,
297 "String input argument required: pluginLibrary:Identifier");
298 return NULL;
299 }
300
301 PluginLoader *loader = PluginLoader::getInstance();
302
303 //load plugin
304 Plugin *plugin = loader->loadPlugin (pluginKey, inputSampleRate);
305 if (!plugin) {
306 string pyerr("Failed to load plugin: "); pyerr += pluginKey;
307 PyErr_SetString(PyExc_TypeError,pyerr.c_str());
308 return NULL;
309 }
310 //void *identifier = (void*) new string(pluginKey);
311 PyPluginDescriptor *pd = new PyPluginDescriptor;
312
313 pd->key = pluginKey;
314 pd->isInitialised = false;
315 pd->inputSampleRate = inputSampleRate;
316
317 //New PyCObject
318 //PyObject *pyPluginHandle = PyCObject_FromVoidPtrAndDesc(
319 //(void*) plugin, identifier, NULL);
320
321 PyObject *pyPluginHandle = PyCObject_FromVoidPtrAndDesc(
322 (void*) plugin, (void*) pd, NULL);
323
324 return pyPluginHandle;
325 }
326
327
328
329 /* UNLOAD PLUGIN */
330
331 static PyObject *
332 vampyhost_unloadPlugin(PyObject *self, PyObject *args)
333 {
334 PyObject *pyPluginHandle;
335
336 if (!PyArg_ParseTuple(args, "O", &pyPluginHandle)) {
337 PyErr_SetString(PyExc_TypeError,
338 "Wrong input argument: Plugin Handle required.");
339 return NULL; }
340
341 string *key;
342 Plugin *plugin;
343
344 if ( !getPluginHandle(pyPluginHandle, &plugin, &key) ) {
345 PyErr_SetString(PyExc_TypeError,
346 "Invalid or already deleted plugin handle.");
347 return NULL; }
348
349 /* Prevent repeated calls from causing segfault
350 sice it will fail type checking the 2nd time: */
351 PyCObject_SetVoidPtr(pyPluginHandle,NULL);
352
353 PyPluginDescriptor *pd = (PyPluginDescriptor*) key;
354
355 delete plugin;
356 delete pd;
357 return pyPluginHandle;
358
359 }
360
361
362 /* INITIALISE PLUGIN */
363
364 static PyObject *
365 vampyhost_initialise(PyObject *self, PyObject *args)
366 {
367 PyObject *pyPluginHandle;
368 size_t channels,blockSize,stepSize;
369 //PyObject pyInputSampleType;
370 bool mixChannels = false;
371
372 if (!PyArg_ParseTuple (args, "Oiii", &pyPluginHandle,
373 (size_t) &channels,
374 (size_t) &stepSize,
375 (size_t) &blockSize))
376 {
377 PyErr_SetString(PyExc_TypeError,
378 "Wrong input arguments: requires a valid plugin handle,channels,stepSize,blockSize.");
379 return NULL;
380 }
381
382 Plugin *plugin;
383 string *key;
384
385 if ( !getPluginHandle(pyPluginHandle, &plugin, &key) ) {
386 PyErr_SetString(PyExc_TypeError,
387 "Invalid plugin handle.");
388 return NULL; }
389
390 // here we cast the void pointer as PyPluginDescriptor instead of string
391 PyPluginDescriptor *plugDesc = (PyPluginDescriptor*) key;
392
393 plugDesc->channels = channels;
394 plugDesc->stepSize = stepSize;
395 plugDesc->blockSize = blockSize;
396 plugDesc->inputSampleType = PyPluginDescriptor::int16;
397 plugDesc->sampleSize = 2;
398 plugDesc->mixChannels = mixChannels;
399
400 size_t minch = plugin->getMinChannelCount();
401 size_t maxch = plugin->getMaxChannelCount();
402 if (mixChannels) channels = 1;
403
404 /* TODO: DO WE WANT TO MIX IT DOWN? */
405 if (maxch < channels || channels < minch) {
406 PyErr_SetString(PyExc_TypeError,
407 "Invalid number of channels.");
408 return NULL; }
409
410 if (!plugin->initialise(channels, stepSize, blockSize)) {
411 PyErr_SetString(PyExc_TypeError,
412 "Plugin initialization failed.");
413 return NULL; }
414
415 plugDesc->identifier =
416 plugDesc->key.substr(plugDesc->key.rfind(':')+1);
417 plugDesc->isInitialised = true;
418
419 return Py_True;
420 }
421
422
423 /* RUN PROCESS */
424
425 static PyObject *
426 vampyhost_process(PyObject *self, PyObject *args)
427 {
428 PyObject *pyPluginHandle;
429 PyObject *pyBuffer;
430 PyObject *pyRealTime;
431
432 if (!PyArg_ParseTuple(args, "OOO",
433 &pyPluginHandle, // C object holding a pointer to a plugin and its descriptor
434 &pyBuffer, // Audio data
435 &pyRealTime)) { // TimeStamp
436 PyErr_SetString(PyExc_TypeError,
437 "Required: plugin handle, buffer, timestmap.");
438 return NULL; }
439
440 if (!PyRealTime_Check(pyRealTime)) {
441 PyErr_SetString(PyExc_TypeError,"Valid timestamp required.");
442 return NULL; }
443
444 // RealTime *rt = PyRealTime_AsPointer(pyRealTime);
445 // if (!rt) return NULL;
446 // cerr << ">>>sec: " << rt->sec << " nsec: " << rt->nsec << endl;
447 //
448 // PyObject *rrt = PyRealTime_FromRealTime (rt);
449
450 string *key;
451 Plugin *plugin;
452
453 if ( !getPluginHandle(pyPluginHandle, &plugin, &key) ) {
454 PyErr_SetString(PyExc_AttributeError,
455 "Invalid or already deleted plugin handle.");
456 return NULL; }
457
458 PyPluginDescriptor *pd = (PyPluginDescriptor*) key;
459
460 if (!pd->isInitialised) {
461 PyErr_SetString(PyExc_StandardError,
462 "Plugin has not been initialised.");
463 return NULL; }
464
465 size_t channels = pd->channels;
466 size_t blockSize = pd->blockSize;
467
468 /*
469 Handle the case when we get the data as a character buffer
470 Handle SampleFormats: int16, float32
471
472 */
473
474 if (PyString_Check(pyBuffer)) {
475 cerr << ">>> String obj passed in." << endl;
476 }
477
478 size_t sample_size = sizeof(short);
479
480 long buflen = (long) PyString_GET_SIZE(pyBuffer);
481
482 size_t input_length =
483 static_cast <size_t> (buflen/channels/sample_size);
484
485 if (input_length == pd->blockSize) {
486 cerr << ">>> A full block has been passed in." << endl; }
487 short *input =
488 reinterpret_cast <short*> (PyString_AS_STRING(pyBuffer));
489
490 //convert int16 PCM data to 32-bit floats
491 float **plugbuf = new float*[channels];
492 float normfact = 1.0f / static_cast <float> (SHRT_MAX);
493
494 for (size_t c = 0; c < channels; ++c) {
495
496 plugbuf[c] = new float[blockSize+2];
497
498 size_t j = 0;
499 while (j < input_length) {
500 plugbuf[c][j] = normfact *
501 static_cast <float> (input[j * channels + c]);
502 ++j;
503 }
504 while (j < blockSize) {
505 plugbuf[c][j] = 0.0f;
506 ++j;
507 }
508 }
509
510 const char *output = reinterpret_cast <const char*> (plugbuf[0]);
511 Py_ssize_t len = (Py_ssize_t) channels*blockSize*4;
512
513 PyObject* pyReturnBuffer =
514 PyString_FromStringAndSize(output,len);
515
516 // long frame = 1;
517 // unsigned int samplerate = (unsigned int) pd->inputSampleRate;
518
519 RealTime timeStamp = *PyRealTime_AsPointer(pyRealTime);
520
521 //Call process and store the output
522 pd->output = plugin->process(
523 plugbuf,
524 timeStamp);
525
526 /* TODO: DO SOMETHONG WITH THE FEATURE SET HERE */
527 /// convert to appropriate python objects, reuse types and conversion utilities from Vampy ...
528
529
530 //We can safely delete here
531 for(size_t k=0; k<channels; k++){
532 delete[] plugbuf[k];
533 }
534 delete[] plugbuf;
535
536 return pyReturnBuffer;
537
538 }
539
540 /* GET / SET OUTPUT */
541
542 //getOutput(plugin,outputNo)
543 static PyObject *
544 vampyhost_getOutput(PyObject *self, PyObject *args) {
545
546 PyObject *pyPluginHandle;
547 // PyObject *pyBuffer;
548 // PyObject *pyRealTime;
549 PyObject *pyOutput;
550
551 if (!PyArg_ParseTuple(args, "OO",
552 &pyPluginHandle, // C object holding a pointer to a plugin and its descriptor
553 &pyOutput)) { // Output reference
554 PyErr_SetString(PyExc_TypeError,
555 "Required: plugin handle, buffer, timestmap.");
556 return NULL; }
557
558 string *key;
559 Plugin *plugin;
560
561 if ( !getPluginHandle(pyPluginHandle, &plugin, &key) ) {
562 PyErr_SetString(PyExc_AttributeError,
563 "Invalid or already deleted plugin handle.");
564 return NULL; }
565
566 PyPluginDescriptor *pd = (PyPluginDescriptor*) key;
567
568 unsigned int outputNo = (unsigned int) PyInt_AS_LONG(pyOutput);
569
570 //Get output list: but we don't need it
571 //Plugin::FeatureList features = pd->output[outputNo];
572
573 size_t outLength = pd->output[outputNo].size();
574
575 //New PyList for the featurelist
576 PyObject *pyFeatureList = PyList_New(outLength);
577
578 for (size_t i = 0; i < outLength; ++i) {
579 // Test:
580 /*
581 XxoObject *pyFeature = PyObject_New(XxoObject, &Xxo_Type);
582 if (pyFeature == NULL) break; //return NULL;
583
584 pyFeature->x_attr = NULL;
585 pyFeature->feature = &pd->output[outputNo][i];
586
587 PyList_SET_ITEM(pyFeatureList,i,(PyObject*)pyFeature);
588 */
589 }
590
591 Py_INCREF(pyFeatureList);
592 return pyFeatureList;
593
594 // EXPLAIN WHAT WE NEED TO DO HERE:
595 // We have the block output in pd->output
596 // FeatureSet[output] -> [Feature[x]] -> Feature.hasTimestamp = v
597 // Vamp::Plugin::FeatureSet output; = pd->output
598 // typedef std::vector<Feature> FeatureList;
599 // typedef std::map<int, FeatureList> FeatureSet; // key is output no
600
601 // THIS IS FOR OUTPUT id LOOKUP LATER
602 // Plugin::OutputList outputs = plugin->getOutputDescriptors();
603 //
604 // if (outputs.size()<1) {
605 // string pyerr("Plugin has no output: "); pyerr += pluginKey;
606 // PyErr_SetString(PyExc_TypeError,pyerr.c_str());
607 // return NULL;
608 // }
609 //
610 // //New list object
611 // PyObject *pyList = PyList_New(outputs.size());
612 //
613 // for (size_t i = 0; i < outputs.size(); ++i) {
614 // PyObject *pyOutputId =
615 // PyString_FromString(outputs[i].identifier.c_str());
616 // PyList_SET_ITEM(pyList,i,pyOutputId);
617 // }
618
619 }
620
621
622
623
624 /* List of functions defined in this module */
625 //module methods table
626 static PyMethodDef vampyhost_methods[] = {
627
628 {"enumeratePlugins", vampyhost_enumeratePlugins, METH_VARARGS,
629 xx_foo_doc},
630
631 {"getLibraryPath", vampyhost_getLibraryPath, METH_VARARGS,
632 xx_foo_doc},
633
634 {"getPluginCategory", vampyhost_getPluginCategory, METH_VARARGS,
635 xx_foo_doc},
636
637 {"getOutputList", vampyhost_getOutputList, METH_VARARGS,
638 xx_foo_doc},
639
640 {"loadPlugin", vampyhost_loadPlugin, METH_VARARGS,
641 xx_foo_doc},
642
643 {"process", vampyhost_process, METH_VARARGS,
644 xx_foo_doc},
645
646 {"unloadPlugin", vampyhost_unloadPlugin, METH_VARARGS,
647 xx_foo_doc},
648
649 {"initialise", vampyhost_initialise, METH_VARARGS,
650 xx_foo_doc},
651
652 {"getOutput", vampyhost_getOutput, METH_VARARGS,
653 xx_foo_doc},
654
655 /* Add RealTime Module Methods */
656
657 {"frame2RealTime", (PyCFunction)RealTime_frame2RealTime, METH_VARARGS,
658 PyDoc_STR("frame2RealTime((int64)frame, (uint32)sampleRate ) -> returns new RealTime object from frame.")},
659
660 {"realtime", (PyCFunction)RealTime_new, METH_VARARGS,
661 PyDoc_STR("realtime() -> returns new RealTime object")},
662
663 {NULL, NULL} /* sentinel */
664 };
665
666 //Documentation for our new module
667 PyDoc_STRVAR(module_doc, "This is a template module just for instruction.");
668
669 /* Initialization function for the module (*must* be called initxx) */
670
671 //module initialization (includes extern C {...} as necessary)
672 PyMODINIT_FUNC
673 initvampyhost(void)
674 {
675 PyObject *m;
676
677 /* Finalize the type object including setting type of the new type
678 * object; doing it here is required for portability to Windows
679 * without requiring C++. */
680
681 if (PyType_Ready(&RealTime_Type) < 0)
682 return;
683 // PyModule_AddObject(m, "Real_Time", (PyObject *)&RealTime_Type);
684
685 /* Create the module and add the functions */
686 m = Py_InitModule3("vampyhost", vampyhost_methods, module_doc);
687 if (m == NULL) return;
688
689 // PyModule_AddObject(m, "realtime", (PyObject *)&RealTime_Type);
690
691 }