annotate PyPlugin.cpp @ 24:7d28bed0864e

* Rearrange Python plugin construction. Formerly, the PyPluginAdapter has retained a single plugin instance pointer for each plugin found, and its createPlugin method has simply returned a new PyPlugin object wrapping the same instance pointer. This has a couple of negative consequences: - Because construction of the actual Python instance occurred before the wrapper was constructed, it was not possible to pass arguments (i.e. the sample rate) from the wrapper constructor to the Python plugin instance constructor -- they had to be passed later, to initialise, disadvantaging those plugins that would like to use the sample rate for parameter & step/block size calculations etc - Because there was only a single Python plugin instance, it was not possible to run more than one instance at once with any isolation This rework instead stores the Python class pointer (rather than instance pointer) in the PyPluginAdapter, and each PyPlugin wrapper instance creates its own Python plugin instance. What could possibly go wrong?
author cannam
date Mon, 17 Aug 2009 15:22:06 +0000
parents 1ae350e97f93
children 046ba4183373
rev   line source
cannam@18 1 /* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */
fazekasgy@0 2 /*
fazekasgy@0 3 Vamp
fazekasgy@0 4
fazekasgy@0 5 An API for audio analysis and feature extraction plugins.
fazekasgy@0 6
fazekasgy@0 7 Centre for Digital Music, Queen Mary, University of London.
fazekasgy@0 8 Copyright 2006 Chris Cannam.
fazekasgy@0 9
fazekasgy@0 10 Permission is hereby granted, free of charge, to any person
fazekasgy@0 11 obtaining a copy of this software and associated documentation
fazekasgy@0 12 files (the "Software"), to deal in the Software without
fazekasgy@0 13 restriction, including without limitation the rights to use, copy,
fazekasgy@0 14 modify, merge, publish, distribute, sublicense, and/or sell copies
fazekasgy@0 15 of the Software, and to permit persons to whom the Software is
fazekasgy@0 16 furnished to do so, subject to the following conditions:
fazekasgy@0 17
fazekasgy@0 18 The above copyright notice and this permission notice shall be
fazekasgy@0 19 included in all copies or substantial portions of the Software.
fazekasgy@0 20
fazekasgy@0 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
fazekasgy@0 22 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
fazekasgy@0 23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
fazekasgy@0 24 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
fazekasgy@0 25 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
fazekasgy@0 26 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
fazekasgy@0 27 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
fazekasgy@0 28
fazekasgy@0 29 Except as contained in this notice, the names of the Centre for
fazekasgy@0 30 Digital Music; Queen Mary, University of London; and Chris Cannam
fazekasgy@0 31 shall not be used in advertising or otherwise to promote the sale,
fazekasgy@0 32 use or other dealings in this Software without prior written
fazekasgy@0 33 authorization.
fazekasgy@0 34 */
fazekasgy@0 35
fazekasgy@0 36
fazekasgy@0 37
fazekasgy@0 38 /**
fazekasgy@8 39 * This Vamp plugin is a wrapper for Python Scripts. (VamPy)
fazekasgy@0 40 * Centre for Digital Music, Queen Mary, University of London.
fazekasgy@0 41 * Copyright 2008, George Fazekas.
fazekasgy@0 42
fazekasgy@0 43 TODO: needs more complete error checking
fazekasgy@0 44 needs correct implementation of Python threading
fazekasgy@0 45 more efficient data conversion using the buffering interface or ctypes
cannam@7 46 Vamp programs not implemented
fazekasgy@0 47 support multiple plugins per script in scanner
fazekasgy@0 48 ensure proper cleanup, host do a good job though
fazekasgy@0 49
fazekasgy@0 50 */
fazekasgy@0 51
cannam@3 52 #include <Python.h>
fazekasgy@0 53 #include "PyPlugin.h"
fazekasgy@0 54
fazekasgy@0 55 #ifdef _WIN32
fazekasgy@0 56 #define pathsep ('\\')
fazekasgy@0 57 #else
fazekasgy@0 58 #define pathsep ('/')
fazekasgy@0 59 #endif
fazekasgy@0 60
fazekasgy@6 61 //#define _DEBUG
fazekasgy@0 62
fazekasgy@0 63 using std::string;
fazekasgy@0 64 using std::vector;
fazekasgy@0 65 using std::cerr;
fazekasgy@0 66 using std::endl;
fazekasgy@0 67 using std::map;
fazekasgy@0 68
fazekasgy@0 69 // Maps to associate strings with enum values
cannam@18 70 static std::map<std::string, o::eOutDescriptors> outKeys;
fazekasgy@0 71 static std::map<std::string, eSampleTypes> sampleKeys;
fazekasgy@0 72 static std::map<std::string, eFeatureFields> ffKeys;
fazekasgy@0 73 static std::map<std::string, p::eParmDescriptors> parmKeys;
fazekasgy@0 74
cannam@3 75 Mutex PyPlugin::m_pythonInterpreterMutex;
fazekasgy@6 76 static bool isMapInitialised = false;
fazekasgy@0 77
cannam@24 78 PyPlugin::PyPlugin(std::string pluginKey, float inputSampleRate, PyObject *pyClass) :
cannam@24 79 Plugin(inputSampleRate),
cannam@24 80 m_pyClass(pyClass),
fazekasgy@0 81 m_stepSize(0),
fazekasgy@6 82 m_blockSize(0),
cannam@24 83 m_channels(0),
fazekasgy@0 84 m_plugin(pluginKey),
fazekasgy@0 85 m_class(pluginKey.substr(pluginKey.rfind(':')+1,pluginKey.size()-1)),
fazekasgy@6 86 m_path((pluginKey.substr(0,pluginKey.rfind(pathsep)))),
fazekasgy@6 87 m_processType(0),
fazekasgy@6 88 m_pyProcess(NULL),
fazekasgy@6 89 m_inputDomain(TimeDomain)
fazekasgy@0 90 {
cannam@24 91 // Create an instance
cannam@24 92 PyObject *pyInputSampleRate = PyFloat_FromDouble(inputSampleRate);
cannam@24 93 PyObject *args = PyTuple_Pack(1, pyInputSampleRate);
cannam@24 94
cannam@24 95 m_pyInstance = PyObject_CallObject(m_pyClass, args);
cannam@24 96
cannam@24 97 if (!m_pyInstance) {
cannam@24 98 cerr << "PyPlugin::PyPlugin: Failed to create Python plugin instance for key \"" << pluginKey << "\" (is the 1-arg class constructor from sample rate correctly provided?)" << endl;
cannam@24 99 throw std::string("Constructor failed");
cannam@24 100 }
cannam@24 101
cannam@24 102 Py_DECREF(args);
cannam@24 103 Py_DECREF(pyInputSampleRate);
fazekasgy@0 104 }
fazekasgy@0 105
fazekasgy@0 106 PyPlugin::~PyPlugin()
fazekasgy@0 107 {
cannam@24 108 if (m_pyInstance) Py_DECREF(m_pyInstance);
cannam@24 109
fazekasgy@6 110 Py_CLEAR(m_pyProcess);
fazekasgy@6 111 #ifdef _DEBUG
fazekasgy@0 112 cerr << "PyPlugin::PyPlugin:" << m_class
cannam@24 113 << " Instance deleted." << endl;
fazekasgy@6 114 #endif
fazekasgy@0 115 }
fazekasgy@0 116
fazekasgy@0 117
fazekasgy@0 118 string
fazekasgy@0 119 PyPlugin::getIdentifier() const
fazekasgy@0 120 {
cannam@3 121 MutexLocker locker(&m_pythonInterpreterMutex);
cannam@3 122
fazekasgy@0 123 char method[]="getIdentifier";
fazekasgy@0 124 cerr << "[call] " << method << endl;
fazekasgy@6 125 string rString="vampy-x";
fazekasgy@0 126
fazekasgy@0 127 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
fazekasgy@0 128
fazekasgy@0 129 //Call the method
fazekasgy@0 130 PyObject *pyString =
fazekasgy@0 131 PyObject_CallMethod(m_pyInstance, method, NULL);
fazekasgy@0 132
fazekasgy@0 133 //Check return value
fazekasgy@0 134 if (!PyString_Check(pyString)) {
fazekasgy@0 135 Py_CLEAR(pyString);
fazekasgy@0 136 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 137 << "] Expected String return value." << endl;
fazekasgy@0 138 return rString;
fazekasgy@0 139 }
fazekasgy@0 140
fazekasgy@0 141 rString=PyString_AsString(pyString);
fazekasgy@0 142 Py_CLEAR(pyString);
fazekasgy@6 143 return rString;
fazekasgy@0 144 }
fazekasgy@0 145 cerr << "Warning: Plugin must return a unique identifier." << endl;
fazekasgy@0 146 return rString;
fazekasgy@0 147 }
fazekasgy@0 148
fazekasgy@0 149
fazekasgy@0 150 string
fazekasgy@0 151 PyPlugin::getName() const
fazekasgy@0 152 {
cannam@3 153 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@0 154
fazekasgy@0 155 char method[]="getName";
fazekasgy@0 156 cerr << "[call] " << method << endl;
fazekasgy@0 157 string rString="VamPy Plugin (Noname)";
fazekasgy@0 158
fazekasgy@0 159 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
fazekasgy@0 160
fazekasgy@0 161 //Call the method
fazekasgy@0 162 PyObject *pyString =
fazekasgy@0 163 PyObject_CallMethod(m_pyInstance, method, NULL);
fazekasgy@0 164
fazekasgy@0 165 //Check return value
fazekasgy@0 166 if (!PyString_Check(pyString)) {
fazekasgy@0 167 Py_CLEAR(pyString);
fazekasgy@0 168 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 169 << "] Expected String return value." << endl;
fazekasgy@0 170 return rString;
fazekasgy@0 171 }
fazekasgy@0 172
fazekasgy@0 173 rString=PyString_AsString(pyString);
fazekasgy@0 174 Py_CLEAR(pyString);
fazekasgy@0 175 }
fazekasgy@0 176 return rString;
fazekasgy@0 177 }
fazekasgy@0 178
fazekasgy@0 179 string
fazekasgy@0 180 PyPlugin::getDescription() const
fazekasgy@0 181 {
cannam@3 182 MutexLocker locker(&m_pythonInterpreterMutex);
cannam@3 183
fazekasgy@0 184 char method[]="getDescription";
fazekasgy@0 185 cerr << "[call] " << method << endl;
fazekasgy@0 186 string rString="Not given. (Hint: Implement getDescription method.)";
fazekasgy@0 187
fazekasgy@0 188 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
fazekasgy@0 189
fazekasgy@0 190 //Call the method
fazekasgy@0 191 PyObject *pyString =
fazekasgy@0 192 PyObject_CallMethod(m_pyInstance, method, NULL);
fazekasgy@0 193
fazekasgy@0 194 //Check return value
fazekasgy@0 195 if (!PyString_Check(pyString)) {
fazekasgy@0 196 Py_CLEAR(pyString);
fazekasgy@0 197 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 198 << "] Expected String return value." << endl;
fazekasgy@0 199 return rString;
fazekasgy@0 200 }
fazekasgy@0 201
fazekasgy@0 202 rString=PyString_AsString(pyString);
fazekasgy@0 203 Py_CLEAR(pyString);
fazekasgy@0 204 }
fazekasgy@0 205 return rString;
fazekasgy@0 206 }
fazekasgy@0 207
fazekasgy@0 208 string
fazekasgy@0 209 PyPlugin::getMaker() const
fazekasgy@0 210 {
cannam@3 211 MutexLocker locker(&m_pythonInterpreterMutex);
cannam@3 212
fazekasgy@0 213 char method[]="getMaker";
fazekasgy@0 214 cerr << "[call] " << method << endl;
fazekasgy@0 215 string rString="Generic VamPy Plugin.";
fazekasgy@0 216
fazekasgy@0 217 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
fazekasgy@0 218
fazekasgy@0 219 //Call the method
fazekasgy@0 220 PyObject *pyString =
fazekasgy@0 221 PyObject_CallMethod(m_pyInstance, method, NULL);
fazekasgy@0 222
fazekasgy@0 223 //Check return value
fazekasgy@0 224 if (!PyString_Check(pyString)) {
fazekasgy@0 225 Py_CLEAR(pyString);
fazekasgy@0 226 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 227 << "] Expected String return value." << endl;
fazekasgy@0 228 return rString;
fazekasgy@0 229 }
fazekasgy@0 230
fazekasgy@0 231 rString=PyString_AsString(pyString);
fazekasgy@0 232 Py_CLEAR(pyString);
fazekasgy@0 233 }
fazekasgy@0 234 return rString;
fazekasgy@0 235 }
fazekasgy@0 236
fazekasgy@0 237 int
fazekasgy@0 238 PyPlugin::getPluginVersion() const
fazekasgy@0 239 {
cannam@24 240 //!!! implement
cannam@24 241
cannam@24 242 return 2;
fazekasgy@0 243 }
fazekasgy@0 244
fazekasgy@0 245 string
fazekasgy@0 246 PyPlugin::getCopyright() const
fazekasgy@0 247 {
cannam@3 248 MutexLocker locker(&m_pythonInterpreterMutex);
cannam@3 249
fazekasgy@0 250 char method[]="getCopyright";
fazekasgy@0 251 cerr << "[call] " << method << endl;
fazekasgy@0 252 string rString="BSD License";
fazekasgy@0 253
fazekasgy@0 254 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
fazekasgy@0 255
fazekasgy@0 256 //Call the method
fazekasgy@0 257 PyObject *pyString =
fazekasgy@0 258 PyObject_CallMethod(m_pyInstance, method, NULL);
fazekasgy@0 259
fazekasgy@0 260 //Check return value
fazekasgy@0 261 if (!PyString_Check(pyString)) {
fazekasgy@0 262 Py_CLEAR(pyString);
fazekasgy@0 263 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 264 << "] Expected String return value." << endl;
fazekasgy@0 265 return rString;
fazekasgy@0 266 }
fazekasgy@0 267
fazekasgy@0 268
fazekasgy@0 269 rString=PyString_AsString(pyString);
fazekasgy@0 270 Py_CLEAR(pyString);
fazekasgy@0 271 }
cannam@24 272
cannam@24 273 return rString;
fazekasgy@0 274 }
fazekasgy@0 275
fazekasgy@0 276
fazekasgy@0 277 bool
fazekasgy@0 278 PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
fazekasgy@0 279 {
fazekasgy@8 280 //useful for debugging Python plugins
fazekasgy@8 281 char method[]="initialise";
fazekasgy@8 282 cerr << "[call] " << method << endl;
fazekasgy@6 283
fazekasgy@6 284 //placing Mutex before these calls causes deadlock
cannam@24 285 if (channels < getMinChannelCount() ||
cannam@24 286 channels > getMaxChannelCount()) return false;
fazekasgy@6 287
fazekasgy@6 288 m_inputDomain = getInputDomain();
fazekasgy@6 289
cannam@3 290 MutexLocker locker(&m_pythonInterpreterMutex);
cannam@3 291
fazekasgy@6 292 initMaps();
fazekasgy@6 293
fazekasgy@6 294 m_stepSize = stepSize;
fazekasgy@6 295 m_blockSize = blockSize;
fazekasgy@6 296 m_channels = channels;
fazekasgy@6 297
fazekasgy@6 298 //quering process implementation type
fazekasgy@6 299 char legacyMethod[]="process";
fazekasgy@6 300 char numpyMethod[]="processN";
fazekasgy@6 301
cannam@18 302 if (PyObject_HasAttrString(m_pyInstance,legacyMethod) &&
cannam@18 303 m_processType == 0)
fazekasgy@6 304 {
fazekasgy@6 305 m_processType = legacyProcess;
fazekasgy@6 306 m_pyProcess = PyString_FromString(legacyMethod);
fazekasgy@6 307 }
fazekasgy@6 308
cannam@18 309 if (PyObject_HasAttrString(m_pyInstance,numpyMethod) &&
cannam@18 310 m_processType == 0)
fazekasgy@6 311 {
fazekasgy@6 312 m_processType = numpyProcess;
fazekasgy@6 313 m_pyProcess = PyString_FromString(numpyMethod);
fazekasgy@6 314 }
fazekasgy@6 315
fazekasgy@6 316 if (!m_processType)
fazekasgy@6 317 {
fazekasgy@6 318 m_processType = not_implemented;
fazekasgy@6 319 m_pyProcess = NULL;
fazekasgy@6 320 cerr << "Warning: Python plugin [" << m_class << "::" << method
cannam@24 321 << "] No process implementation found. Plugin will do nothing." << endl;
fazekasgy@6 322 }
fazekasgy@6 323
cannam@24 324 //Check if the method is implemented in Python else return false
cannam@24 325 if (PyObject_HasAttrString(m_pyInstance,method)) {
fazekasgy@0 326
cannam@24 327 PyObject *pyMethod = PyString_FromString(method);
cannam@24 328 PyObject *pyChannels = PyInt_FromSsize_t((Py_ssize_t)channels);
cannam@24 329 PyObject *pyStepSize = PyInt_FromSsize_t((Py_ssize_t)m_stepSize);
cannam@24 330 PyObject *pyBlockSize = PyInt_FromSsize_t((Py_ssize_t)blockSize);
cannam@24 331 //Call the method
cannam@24 332 PyObject *pyBool =
cannam@24 333 PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyChannels,pyStepSize,pyBlockSize,NULL);
fazekasgy@8 334
cannam@24 335 Py_DECREF(pyMethod);
cannam@24 336 Py_DECREF(pyChannels);
cannam@24 337 Py_DECREF(pyStepSize);
cannam@24 338 Py_DECREF(pyBlockSize);
fazekasgy@0 339
cannam@24 340 //Check return value
cannam@24 341 if (PyErr_Occurred() || !PyBool_Check(pyBool)) {
cannam@24 342 PyErr_Print(); PyErr_Clear();
cannam@24 343 Py_CLEAR(pyBool);
cannam@24 344 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
cannam@24 345 << "] Expected Bool return value." << endl;
cannam@24 346 return false;
cannam@24 347 }
cannam@24 348
cannam@24 349 if (pyBool == Py_True) {
cannam@24 350 Py_CLEAR(pyBool);
cannam@24 351 return true;
cannam@24 352 } else {
cannam@24 353 Py_CLEAR(pyBool);
cannam@24 354 return false;
cannam@24 355 }
cannam@24 356 }
fazekasgy@6 357 return false;
fazekasgy@0 358 }
fazekasgy@0 359
fazekasgy@0 360 void
fazekasgy@0 361 PyPlugin::reset()
fazekasgy@0 362 {
fazekasgy@6 363 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@6 364
fazekasgy@6 365 char method[]="reset";
fazekasgy@6 366 cerr << "[call] " << method << endl;
fazekasgy@6 367
fazekasgy@6 368 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
fazekasgy@6 369
fazekasgy@6 370 PyObject_CallMethod(m_pyInstance, method, NULL);
fazekasgy@6 371 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
fazekasgy@6 372
fazekasgy@6 373 }
fazekasgy@0 374 }
fazekasgy@0 375
fazekasgy@6 376 PyPlugin::InputDomain PyPlugin::getInputDomain() const
fazekasgy@0 377 {
cannam@3 378 MutexLocker locker(&m_pythonInterpreterMutex);
cannam@3 379
fazekasgy@0 380 char method[]="getInputDomain";
fazekasgy@0 381 cerr << "[call] " << method << endl;
fazekasgy@0 382 PyPlugin::InputDomain rValue = TimeDomain; // TimeDomain
fazekasgy@0 383
fazekasgy@0 384 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
fazekasgy@0 385
fazekasgy@0 386 PyObject *pyString = PyObject_CallMethod(m_pyInstance, method, NULL);
fazekasgy@0 387
fazekasgy@0 388 //Check return value
fazekasgy@0 389 if (!PyString_Check(pyString)) {
fazekasgy@0 390 Py_CLEAR(pyString);
fazekasgy@0 391 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 392 << "] Expected String return value." << endl;
fazekasgy@0 393 return rValue;
fazekasgy@0 394 }
fazekasgy@0 395
fazekasgy@0 396 string domain = (string) PyString_AsString(pyString);
fazekasgy@0 397 if (domain == "FrequencyDomain") rValue = FrequencyDomain;
fazekasgy@0 398 Py_CLEAR(pyString);
fazekasgy@0 399 }
fazekasgy@0 400 return rValue;
fazekasgy@0 401 }
fazekasgy@0 402
fazekasgy@6 403
fazekasgy@0 404 size_t PyPlugin::getPreferredBlockSize() const
fazekasgy@0 405 {
cannam@3 406 MutexLocker locker(&m_pythonInterpreterMutex);
cannam@3 407
fazekasgy@0 408 char method[]="getPreferredBlockSize";
fazekasgy@0 409 cerr << "[call] " << method << endl;
fazekasgy@0 410 size_t rValue=0; //not set by default
fazekasgy@0 411 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
fazekasgy@0 412 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
fazekasgy@0 413
fazekasgy@0 414 //Check return value
fazekasgy@0 415 if (!PyInt_Check(pyInt)) {
fazekasgy@0 416 Py_CLEAR(pyInt);
fazekasgy@0 417 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 418 << "] Expected Integer return value." << endl;
fazekasgy@0 419 return rValue;
fazekasgy@0 420 }
fazekasgy@0 421
fazekasgy@0 422 rValue=(size_t)PyInt_AS_LONG(pyInt);
fazekasgy@0 423 Py_CLEAR(pyInt);
fazekasgy@0 424 }
fazekasgy@0 425 return rValue;
fazekasgy@0 426 }
fazekasgy@0 427
fazekasgy@0 428 //size_t PyPlugin::getPreferredStepSize() const { return 0; }
fazekasgy@0 429 size_t PyPlugin::getPreferredStepSize() const
fazekasgy@0 430 {
cannam@3 431 MutexLocker locker(&m_pythonInterpreterMutex);
cannam@3 432
fazekasgy@0 433 char method[]="getPreferredStepSize";
fazekasgy@0 434 cerr << "[call] " << method << endl;
fazekasgy@8 435 size_t rValue=1024; //not set by default
fazekasgy@0 436 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
fazekasgy@0 437 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
fazekasgy@0 438
fazekasgy@0 439 //Check return value
fazekasgy@0 440 if (!PyInt_Check(pyInt)) {
fazekasgy@0 441 Py_CLEAR(pyInt);
fazekasgy@0 442 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 443 << "] Expected Integer return value." << endl;
fazekasgy@0 444 return rValue;
fazekasgy@0 445 }
fazekasgy@0 446
fazekasgy@0 447 rValue=(size_t)PyInt_AS_LONG(pyInt);
fazekasgy@0 448 Py_CLEAR(pyInt);
fazekasgy@0 449 }
fazekasgy@0 450 return rValue;
fazekasgy@0 451 }
fazekasgy@0 452
fazekasgy@0 453 size_t PyPlugin::getMinChannelCount() const
fazekasgy@0 454 {
cannam@3 455 MutexLocker locker(&m_pythonInterpreterMutex);
cannam@3 456
fazekasgy@0 457 char method[]="getMinChannelCount";
fazekasgy@0 458 cerr << "[call] " << method << endl;
fazekasgy@0 459 size_t rValue=1; //default value
fazekasgy@0 460 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
fazekasgy@0 461 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
fazekasgy@0 462
fazekasgy@0 463 //Check return value
fazekasgy@0 464 if (!PyInt_Check(pyInt)) {
fazekasgy@0 465 Py_CLEAR(pyInt);
fazekasgy@0 466 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 467 << "] Expected String return value." << endl;
fazekasgy@0 468 return rValue;
fazekasgy@0 469 }
fazekasgy@0 470
fazekasgy@0 471 rValue=(size_t)PyInt_AS_LONG(pyInt);
fazekasgy@0 472 Py_CLEAR(pyInt);
fazekasgy@0 473 }
fazekasgy@0 474 return rValue;
fazekasgy@0 475 }
fazekasgy@0 476
fazekasgy@0 477 size_t PyPlugin::getMaxChannelCount() const
fazekasgy@0 478 {
cannam@3 479 MutexLocker locker(&m_pythonInterpreterMutex);
cannam@3 480
fazekasgy@0 481 char method[]="getMaxChannelCount";
fazekasgy@0 482 cerr << "[call] " << method << endl;
fazekasgy@0 483 size_t rValue=1; //default value
fazekasgy@0 484 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
fazekasgy@0 485 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
fazekasgy@0 486
fazekasgy@0 487 //Check return value
fazekasgy@0 488 if (!PyInt_Check(pyInt)) {
fazekasgy@0 489 Py_CLEAR(pyInt);
fazekasgy@0 490 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 491 << "] Expected String return value." << endl;
fazekasgy@0 492 return rValue;
fazekasgy@0 493 }
fazekasgy@0 494
fazekasgy@0 495 rValue=(size_t)PyInt_AS_LONG(pyInt);
fazekasgy@0 496 Py_CLEAR(pyInt);
fazekasgy@0 497 }
fazekasgy@0 498 return rValue;
fazekasgy@0 499 }
fazekasgy@0 500
fazekasgy@0 501
fazekasgy@0 502 PyPlugin::OutputList
fazekasgy@0 503 PyPlugin::getOutputDescriptors() const
fazekasgy@0 504 {
fazekasgy@6 505
cannam@3 506 MutexLocker locker(&m_pythonInterpreterMutex);
fazekasgy@6 507
fazekasgy@0 508 //PyEval_AcquireThread(newThreadState);
fazekasgy@0 509 OutputList list;
fazekasgy@0 510 OutputDescriptor od;
fazekasgy@0 511 char method[]="getOutputDescriptors";
fazekasgy@0 512 cerr << "[call] " << method << endl;
fazekasgy@0 513
fazekasgy@0 514 //Check if the method is implemented in Python
fazekasgy@6 515 if ( ! PyObject_HasAttrString(m_pyInstance,method) ) return list;
fazekasgy@0 516
fazekasgy@0 517 //Call the method: must return list object (new reference)
fazekasgy@0 518 PyObject *pyList =
fazekasgy@0 519 PyObject_CallMethod(m_pyInstance,method, NULL);
fazekasgy@0 520
fazekasgy@0 521 //Check return type
fazekasgy@0 522 if (! PyList_Check(pyList) ) {
fazekasgy@0 523 Py_CLEAR(pyList);
fazekasgy@0 524 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 525 << "] Expected List return type." << endl;
fazekasgy@0 526 return list;
fazekasgy@0 527 }
fazekasgy@0 528
fazekasgy@0 529 //These will all be borrowed references (no need to DECREF)
fazekasgy@0 530 PyObject *pyDict, *pyKey, *pyValue;
fazekasgy@0 531
fazekasgy@0 532 //Parse Output List
fazekasgy@0 533 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) {
fazekasgy@0 534
cannam@18 535 //Get i-th Vamp output descriptor (Borrowed Reference)
fazekasgy@0 536 pyDict = PyList_GET_ITEM(pyList,i);
fazekasgy@0 537
fazekasgy@0 538 //We only care about dictionaries holding output descriptors
fazekasgy@0 539 if ( !PyDict_Check(pyDict) ) continue;
fazekasgy@0 540
fazekasgy@0 541 Py_ssize_t pyPos = NULL;
fazekasgy@0 542 initMaps();
fazekasgy@0 543
fazekasgy@0 544 //Python Sequence Iterator
fazekasgy@0 545 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
fazekasgy@0 546 {
fazekasgy@0 547 switch (outKeys[PyString_AsString(pyKey)])
fazekasgy@0 548 {
cannam@18 549 case o::not_found :
cannam@18 550 cerr << "Unknown key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl;
fazekasgy@0 551 break;
cannam@18 552 case o::identifier:
fazekasgy@0 553 od.identifier = PyString_AsString(pyValue);
fazekasgy@0 554 break;
cannam@18 555 case o::name:
fazekasgy@0 556 od.name = PyString_AsString(pyValue);
fazekasgy@0 557 break;
cannam@18 558 case o::description:
fazekasgy@0 559 od.description = PyString_AsString(pyValue);
fazekasgy@0 560 break;
cannam@18 561 case o::unit:
fazekasgy@0 562 od.unit = PyString_AsString(pyValue);
fazekasgy@0 563 break;
cannam@18 564 case o::hasFixedBinCount:
fazekasgy@0 565 od.hasFixedBinCount = (bool) PyInt_AS_LONG(pyValue);
fazekasgy@0 566 break;
cannam@18 567 case o::binCount:
fazekasgy@0 568 od.binCount = (size_t) PyInt_AS_LONG(pyValue);
fazekasgy@0 569 break;
cannam@18 570 case o::binNames:
fazekasgy@0 571 od.binNames = PyList_To_StringVector(pyValue);
fazekasgy@0 572 break;
cannam@18 573 case o::hasKnownExtents:
fazekasgy@0 574 od.hasKnownExtents = (bool) PyInt_AS_LONG(pyValue);
fazekasgy@0 575 break;
cannam@18 576 case o::minValue:
fazekasgy@0 577 od.minValue = (float) PyFloat_AS_DOUBLE(pyValue);
fazekasgy@0 578 break;
cannam@18 579 case o::maxValue:
fazekasgy@0 580 od.maxValue = (float) PyFloat_AS_DOUBLE(pyValue);
fazekasgy@0 581 break;
cannam@18 582 case o::isQuantized:
fazekasgy@0 583 od.isQuantized = (bool) PyInt_AS_LONG(pyValue);
fazekasgy@0 584 break;
cannam@18 585 case o::quantizeStep:
fazekasgy@0 586 od.quantizeStep = (float) PyFloat_AS_DOUBLE(pyValue);
fazekasgy@0 587 break;
cannam@18 588 case o::sampleType:
fazekasgy@0 589 od.sampleType = (OutputDescriptor::SampleType) sampleKeys[PyString_AsString(pyValue)];
fazekasgy@0 590 break;
cannam@18 591 case o::sampleRate:
fazekasgy@0 592 od.sampleRate = (float) PyFloat_AS_DOUBLE(pyValue);
fazekasgy@8 593 // od.sampleRate = m_inputSampleRate / m_stepSize;
fazekasgy@8 594 cerr << od.sampleRate << endl;
fazekasgy@0 595 break;
cannam@18 596 case o::hasDuration:
cannam@18 597 od.hasDuration = (bool)PyInt_AS_LONG(pyValue);
cannam@18 598 break;
fazekasgy@0 599 default :
cannam@18 600 cerr << "Invalid key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl;
fazekasgy@6 601 }
fazekasgy@6 602 } // while dict
fazekasgy@0 603 list.push_back(od);
fazekasgy@6 604 } // for list
fazekasgy@0 605 Py_CLEAR(pyList);
fazekasgy@0 606 return list;
fazekasgy@0 607 }
fazekasgy@0 608
fazekasgy@0 609 PyPlugin::ParameterList
fazekasgy@0 610 PyPlugin::getParameterDescriptors() const
fazekasgy@0 611 {
cannam@3 612 MutexLocker locker(&m_pythonInterpreterMutex);
cannam@3 613
fazekasgy@0 614 ParameterList list;
fazekasgy@0 615 ParameterDescriptor pd;
fazekasgy@0 616 char method[]="getParameterDescriptors";
fazekasgy@0 617 cerr << "[call] " << method << endl;
fazekasgy@0 618
fazekasgy@0 619 //Check if the method is implemented in Python
fazekasgy@6 620 if ( ! PyObject_HasAttrString(m_pyInstance,method) ) return list;
fazekasgy@0 621
fazekasgy@0 622 //Call the method: must return list object (new reference)
fazekasgy@0 623 PyObject *pyList =
fazekasgy@0 624 PyObject_CallMethod(m_pyInstance,method, NULL);
fazekasgy@0 625
fazekasgy@0 626 //Check return type
fazekasgy@0 627 if (! PyList_Check(pyList) ) {
fazekasgy@0 628 Py_CLEAR(pyList);
fazekasgy@0 629 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 630 << "] Expected List return type." << endl;
fazekasgy@0 631 return list;
fazekasgy@0 632 }
fazekasgy@0 633
fazekasgy@0 634
fazekasgy@0 635 //These will all be borrowed references (no need to DECREF)
fazekasgy@0 636 PyObject *pyDict, *pyKey, *pyValue;
fazekasgy@0 637
fazekasgy@0 638 //Parse Output List
fazekasgy@0 639 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) {
fazekasgy@0 640
cannam@18 641 //Get i-th Vamp output descriptor (Borrowed Reference)
fazekasgy@0 642 pyDict = PyList_GET_ITEM(pyList,i);
fazekasgy@0 643
fazekasgy@0 644 //We only care about dictionaries holding output descriptors
fazekasgy@0 645 if ( !PyDict_Check(pyDict) ) continue;
fazekasgy@0 646
fazekasgy@0 647 Py_ssize_t pyPos = NULL;
fazekasgy@0 648 initMaps();
fazekasgy@0 649
fazekasgy@0 650 //Python Sequence Iterator
fazekasgy@0 651 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
fazekasgy@0 652 {
fazekasgy@0 653 switch (parmKeys[PyString_AsString(pyKey)])
fazekasgy@0 654 {
cannam@18 655 case p::not_found :
cannam@18 656 cerr << "Unknown key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl;
fazekasgy@0 657 break;
fazekasgy@0 658 case p::identifier:
fazekasgy@0 659 pd.identifier = PyString_AsString(pyValue);
fazekasgy@0 660 break;
fazekasgy@0 661 case p::name:
fazekasgy@0 662 pd.name = PyString_AsString(pyValue);
fazekasgy@0 663 break;
fazekasgy@0 664 case p::description:
fazekasgy@0 665 pd.description = PyString_AsString(pyValue);
fazekasgy@0 666 break;
fazekasgy@0 667 case p::unit:
fazekasgy@0 668 pd.unit = PyString_AsString(pyValue);
fazekasgy@0 669 break;
fazekasgy@0 670 case p::minValue:
fazekasgy@0 671 pd.minValue = (float) PyFloat_AS_DOUBLE(pyValue);
fazekasgy@0 672 break;
fazekasgy@0 673 case p::maxValue:
fazekasgy@0 674 pd.maxValue = (float) PyFloat_AS_DOUBLE(pyValue);
fazekasgy@0 675 break;
fazekasgy@0 676 case p::defaultValue:
fazekasgy@0 677 pd.defaultValue = (float) PyFloat_AS_DOUBLE(pyValue);
fazekasgy@0 678 break;
fazekasgy@0 679 case p::isQuantized:
fazekasgy@0 680 pd.isQuantized = (bool) PyInt_AS_LONG(pyValue);
cannam@22 681 break; case p::quantizeStep:
cannam@22 682 pd.quantizeStep = (float) PyFloat_AS_DOUBLE(pyValue);
cannam@22 683 break;
fazekasgy@0 684 default :
cannam@18 685 cerr << "Invalid key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl;
fazekasgy@6 686 }
fazekasgy@6 687 } // while dict
fazekasgy@0 688 list.push_back(pd);
fazekasgy@6 689 } // for list
fazekasgy@0 690 Py_CLEAR(pyList);
fazekasgy@0 691 return list;
fazekasgy@0 692 }
fazekasgy@0 693
fazekasgy@0 694 void PyPlugin::setParameter(std::string paramid, float newval)
fazekasgy@0 695 {
cannam@3 696 MutexLocker locker(&m_pythonInterpreterMutex);
cannam@3 697
fazekasgy@0 698 char method[]="setParameter";
fazekasgy@0 699 cerr << "[call] " << method << endl;
fazekasgy@0 700
fazekasgy@0 701 //Check if the method is implemented in Python
fazekasgy@0 702 if (PyObject_HasAttrString(m_pyInstance,method)) {
fazekasgy@0 703
fazekasgy@0 704 PyObject *pyMethod = PyString_FromString(method);
fazekasgy@0 705 PyObject *pyParamid = PyString_FromString(paramid.c_str());
fazekasgy@0 706 PyObject *pyNewval = PyFloat_FromDouble((double)newval);
fazekasgy@0 707
fazekasgy@0 708 //Call the method
fazekasgy@0 709 PyObject *pyBool =
fazekasgy@0 710 PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyParamid,pyNewval,NULL);
fazekasgy@0 711
fazekasgy@0 712 //This only happens if there is a syntax error or so
fazekasgy@0 713 if (pyBool == NULL) {
fazekasgy@0 714 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 715 << "] Error setting parameter: " << paramid << endl;
fazekasgy@0 716 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
fazekasgy@0 717 }
fazekasgy@0 718
fazekasgy@0 719 Py_DECREF(pyMethod);
fazekasgy@0 720 Py_DECREF(pyParamid);
fazekasgy@0 721 Py_DECREF(pyNewval);
fazekasgy@0 722 }
fazekasgy@0 723 }
fazekasgy@0 724
fazekasgy@0 725 float PyPlugin::getParameter(std::string paramid) const
fazekasgy@0 726 {
cannam@3 727 MutexLocker locker(&m_pythonInterpreterMutex);
cannam@3 728
fazekasgy@0 729 char method[]="getParameter";
fazekasgy@0 730 cerr << "[call] " << method << endl;
fazekasgy@0 731 float rValue = 0.0f;
fazekasgy@0 732
fazekasgy@0 733 //Check if the method is implemented in Python
fazekasgy@0 734 if (PyObject_HasAttrString(m_pyInstance,method)) {
fazekasgy@0 735
fazekasgy@0 736 PyObject *pyMethod = PyString_FromString(method);
fazekasgy@0 737 PyObject *pyParamid = PyString_FromString(paramid.c_str());
fazekasgy@0 738
fazekasgy@0 739 //Call the method
fazekasgy@0 740 PyObject *pyFloat =
fazekasgy@0 741 PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyParamid,NULL);
fazekasgy@0 742
fazekasgy@0 743 //Check return type
fazekasgy@0 744 if (! PyFloat_Check(pyFloat) ) {
fazekasgy@0 745 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 746 << "] Expected Float return type." << endl;
fazekasgy@0 747 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
fazekasgy@0 748 Py_CLEAR(pyFloat);
fazekasgy@0 749 return rValue;
fazekasgy@0 750 }
fazekasgy@0 751
fazekasgy@0 752 rValue = (float) PyFloat_AS_DOUBLE(pyFloat);
fazekasgy@0 753
fazekasgy@0 754 Py_DECREF(pyMethod);
fazekasgy@0 755 Py_DECREF(pyParamid);
fazekasgy@0 756 Py_DECREF(pyFloat);
fazekasgy@0 757 }
fazekasgy@0 758
fazekasgy@0 759 return rValue;
fazekasgy@0 760 }
fazekasgy@0 761
fazekasgy@0 762 #ifdef _DEBUG
fazekasgy@0 763 static int proccounter = 0;
fazekasgy@0 764 #endif
fazekasgy@0 765
fazekasgy@0 766 PyPlugin::FeatureSet
fazekasgy@0 767 PyPlugin::process(const float *const *inputBuffers,
fazekasgy@0 768 Vamp::RealTime timestamp)
fazekasgy@0 769 {
cannam@3 770 MutexLocker locker(&m_pythonInterpreterMutex);
cannam@3 771
fazekasgy@6 772 #ifdef _DEBUG
fazekasgy@6 773 cerr << "[call] process, frame:" << proccounter << endl;
fazekasgy@6 774 proccounter++;
fazekasgy@6 775 #endif
fazekasgy@6 776
fazekasgy@8 777 if (m_blockSize == 0 || m_channels == 0) {
fazekasgy@0 778 cerr << "ERROR: PyPlugin::process: "
fazekasgy@0 779 << "Plugin has not been initialised" << endl;
fazekasgy@0 780 return FeatureSet();
fazekasgy@0 781 }
fazekasgy@0 782
fazekasgy@6 783 if (m_processType == not_implemented) {
fazekasgy@6 784 cerr << "ERROR: In Python plugin [" << m_class
fazekasgy@6 785 << "] No process implementation found. Returning empty feature set." << endl;
fazekasgy@6 786 return FeatureSet();
fazekasgy@6 787 }
fazekasgy@0 788
fazekasgy@6 789 string method=PyString_AsString(m_pyProcess);
fazekasgy@6 790
fazekasgy@6 791 PyObject *pyOutputList = NULL;
fazekasgy@0 792
fazekasgy@6 793 /*new numPy support*/
fazekasgy@6 794 if (m_processType == numpyProcess) {
fazekasgy@8 795
fazekasgy@8 796 //create a list of buffers
fazekasgy@8 797 PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels);
fazekasgy@8 798 for (size_t i=0; i < m_channels; ++i) {
fazekasgy@6 799
fazekasgy@8 800 //Expose memory using the Buffer Interface of C/API
fazekasgy@8 801 //This will virtually pass a pointer which can be
fazekasgy@8 802 //recasted in Python code as float or complex array
fazekasgy@8 803 PyObject *pyBuffer = PyBuffer_FromMemory
fazekasgy@8 804 ((void *) (float *) inputBuffers[i],
fazekasgy@8 805 (Py_ssize_t) sizeof(float) * m_blockSize);
fazekasgy@6 806
fazekasgy@8 807 PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyBuffer);
fazekasgy@8 808 }
fazekasgy@8 809
fazekasgy@8 810 //pass RealTime as frameCount
fazekasgy@8 811 PyObject *pyLongSample = PyLong_FromLong (
fazekasgy@8 812 Vamp::RealTime::realTime2Frame
fazekasgy@8 813 (timestamp, (unsigned int) m_inputSampleRate));
fazekasgy@6 814
fazekasgy@8 815 //Call python process (returns new reference)
fazekasgy@8 816 pyOutputList = PyObject_CallMethodObjArgs
fazekasgy@8 817 (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL);
fazekasgy@8 818
fazekasgy@8 819 Py_DECREF(pyChannelList);
fazekasgy@8 820 Py_DECREF(pyLongSample);
fazekasgy@6 821
fazekasgy@6 822 }
fazekasgy@6 823
fazekasgy@6 824 if (m_processType == legacyProcess) {
fazekasgy@0 825
fazekasgy@8 826 //create a list of lists
fazekasgy@8 827 PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels);
fazekasgy@8 828 for (size_t i=0; i < m_channels; ++i) {
fazekasgy@0 829
fazekasgy@8 830 //Declare new list object
fazekasgy@8 831 PyObject *pyFloat, *pyList;
fazekasgy@8 832 pyList = PyList_New((Py_ssize_t) m_blockSize);
fazekasgy@8 833
fazekasgy@8 834 //Pack samples into a Python List Object
fazekasgy@8 835 //pyFloat types will always be new references,
fazekasgy@8 836 //these will be discarded when the list is deallocated
fazekasgy@8 837 for (size_t j = 0; j < m_blockSize; ++j) {
fazekasgy@8 838 pyFloat=PyFloat_FromDouble(
fazekasgy@8 839 (double) inputBuffers[i][j]);
fazekasgy@8 840 PyList_SET_ITEM(pyList, (Py_ssize_t) j, pyFloat);
fazekasgy@8 841 }
fazekasgy@8 842 PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyList);
fazekasgy@8 843 }
fazekasgy@8 844
fazekasgy@8 845 //pass RealTime as frameCount
fazekasgy@8 846 PyObject *pyLongSample = PyLong_FromLong (
fazekasgy@8 847 Vamp::RealTime::realTime2Frame
fazekasgy@8 848 (timestamp, (unsigned int) m_inputSampleRate));
fazekasgy@8 849
fazekasgy@8 850 //Call python process (returns new reference)
fazekasgy@8 851 pyOutputList = PyObject_CallMethodObjArgs
fazekasgy@8 852 (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL);
fazekasgy@8 853
fazekasgy@8 854 Py_DECREF(pyChannelList);
fazekasgy@8 855 Py_DECREF(pyLongSample);
fazekasgy@8 856
fazekasgy@0 857 }
fazekasgy@6 858
fazekasgy@8 859 //return nothing
fazekasgy@8 860 //Py_CLEAR(pyOutputList);
fazekasgy@8 861 //return FeatureSet();
fazekasgy@0 862
fazekasgy@0 863 //Check return type
fazekasgy@0 864 if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) {
fazekasgy@0 865 if (pyOutputList == NULL) {
fazekasgy@0 866 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 867 << "] Unexpected result." << endl;
fazekasgy@0 868 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
fazekasgy@0 869 } else {
fazekasgy@0 870 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 871 << "] Expected List return type." << endl;
fazekasgy@0 872 }
fazekasgy@0 873 Py_CLEAR(pyOutputList);
fazekasgy@0 874 return FeatureSet();
fazekasgy@0 875 }
fazekasgy@0 876
fazekasgy@0 877 // Py_DECREF(pyList);
fazekasgy@0 878 // This appears to be tracked by the cyclic garbage collector
fazekasgy@0 879 // hence decrefing produces GC error
fazekasgy@0 880 #ifdef _DEBUG
fazekasgy@0 881 cerr << "Process Returned Features" << endl;
fazekasgy@0 882 #endif
fazekasgy@0 883 // These will ALL be borrowed references
fazekasgy@0 884 PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue;
fazekasgy@0 885
fazekasgy@0 886 FeatureSet returnFeatures;
fazekasgy@0 887
fazekasgy@0 888 //Parse Output List for each element (FeatureSet)
fazekasgy@0 889 for (Py_ssize_t i = 0;
fazekasgy@0 890 i < PyList_GET_SIZE(pyOutputList); ++i) {
fazekasgy@0 891 //cerr << "output (FeatureSet): " << i << endl;
fazekasgy@0 892
fazekasgy@0 893 //Get i-th FeatureList (Borrowed Reference)
fazekasgy@0 894 pyFeatureList = PyList_GET_ITEM(pyOutputList,i);
fazekasgy@0 895
fazekasgy@0 896 //Parse FeatureList for each element (Feature)
fazekasgy@0 897 for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) {
fazekasgy@0 898 //cerr << "element (FeatureList): " << j << endl;
fazekasgy@0 899
fazekasgy@0 900 //Get j-th Feature (Borrowed Reference)
fazekasgy@0 901 pyDict = PyList_GET_ITEM(pyFeatureList,j);
fazekasgy@0 902
fazekasgy@0 903 //We only care about dictionaries holding a Feature struct
fazekasgy@0 904 if ( !PyDict_Check(pyDict) ) continue;
fazekasgy@0 905
fazekasgy@0 906 Py_ssize_t pyPos = NULL;
fazekasgy@0 907 bool emptyFeature = true;
fazekasgy@0 908 Feature feature;
fazekasgy@0 909
fazekasgy@0 910 //process::Python Sequence Iterator for dictionary
fazekasgy@0 911 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
fazekasgy@0 912 {
fazekasgy@0 913 emptyFeature = false;
fazekasgy@0 914 switch (ffKeys[PyString_AsString(pyKey)])
fazekasgy@0 915 {
cannam@18 916 case unknown:
cannam@18 917 cerr << "Unknown key in Vamp FeatureSet: "
fazekasgy@0 918 << PyString_AsString(pyKey) << endl;
fazekasgy@0 919 break;
fazekasgy@0 920 case hasTimestamp:
fazekasgy@0 921 feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue);
fazekasgy@0 922 break;
fazekasgy@0 923 case timeStamp:
fazekasgy@8 924 feature.timestamp =
fazekasgy@8 925 Vamp::RealTime::frame2RealTime(
fazekasgy@8 926 PyLong_AsLong(pyValue),
fazekasgy@8 927 (unsigned int) m_inputSampleRate );
fazekasgy@8 928 #ifdef _DEBUG
fazekasgy@8 929 cerr << "Timestamp: "
fazekasgy@8 930 << (long)PyLong_AsLong(pyValue) << ", ->"
fazekasgy@8 931 << feature.timestamp.toString() << endl;
fazekasgy@8 932 #endif
fazekasgy@0 933 break;
cannam@18 934 case hasDuration:
cannam@18 935 feature.hasDuration = (bool) PyInt_AS_LONG(pyValue);
cannam@18 936 break;
cannam@18 937 case duration:
cannam@18 938 feature.duration =
cannam@18 939 Vamp::RealTime::frame2RealTime(
cannam@18 940 PyLong_AsLong(pyValue),
cannam@18 941 (unsigned int) m_inputSampleRate );
cannam@18 942 #ifdef _DEBUG
cannam@18 943 cerr << "Duration: "
cannam@18 944 << (long)PyLong_AsLong(pyValue) << ", ->"
cannam@18 945 << feature.duration.toString() << endl;
cannam@18 946 #endif
cannam@18 947 break;
fazekasgy@0 948 case values:
fazekasgy@0 949 feature.values = PyList_As_FloatVector(pyValue);
fazekasgy@0 950 break;
fazekasgy@0 951 case label:
fazekasgy@0 952 feature.label = PyString_AsString(pyValue);
fazekasgy@0 953 break;
fazekasgy@0 954 default :
cannam@18 955 cerr << "Invalid key in Vamp FeatureSet: "
fazekasgy@0 956 << PyString_AsString(pyKey) << endl;
fazekasgy@0 957 } // switch
fazekasgy@0 958
fazekasgy@0 959 } // while
fazekasgy@0 960 if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl;
fazekasgy@0 961 else returnFeatures[i].push_back(feature);
fazekasgy@0 962
fazekasgy@0 963 }// for j = FeatureList
fazekasgy@0 964
fazekasgy@0 965 }//for i = FeatureSet
fazekasgy@0 966 Py_CLEAR(pyOutputList);
fazekasgy@0 967 return returnFeatures;
fazekasgy@0 968 }
fazekasgy@0 969
fazekasgy@0 970
fazekasgy@0 971
fazekasgy@0 972 PyPlugin::FeatureSet
fazekasgy@0 973 PyPlugin::getRemainingFeatures()
fazekasgy@0 974 {
cannam@3 975 MutexLocker locker(&m_pythonInterpreterMutex);
cannam@3 976
fazekasgy@0 977 static char method[]="getRemainingFeatures";
fazekasgy@0 978 cerr << "[call] " << method << endl;
fazekasgy@0 979
fazekasgy@0 980 //check if the method is implemented
fazekasgy@0 981 if ( ! PyObject_HasAttrString(m_pyInstance,method) ) {
fazekasgy@0 982 return FeatureSet();
fazekasgy@0 983 }
fazekasgy@0 984
fazekasgy@0 985 PyObject *pyMethod = PyString_FromString(method);
fazekasgy@0 986
fazekasgy@0 987 PyObject *pyOutputList =
fazekasgy@0 988 PyObject_CallMethod(m_pyInstance,method, NULL);
fazekasgy@0 989
fazekasgy@0 990 //Check return type
fazekasgy@0 991 if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) {
fazekasgy@0 992 if (pyOutputList == NULL) {
fazekasgy@0 993 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 994 << "] Unexpected result." << endl;
fazekasgy@0 995 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
fazekasgy@0 996 } else {
fazekasgy@0 997 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
fazekasgy@0 998 << "] Expected List return type." << endl;
fazekasgy@0 999 }
fazekasgy@0 1000 Py_CLEAR(pyMethod);
fazekasgy@0 1001 Py_CLEAR(pyOutputList);
fazekasgy@0 1002 return FeatureSet();
fazekasgy@0 1003 }
fazekasgy@0 1004 Py_DECREF(pyMethod);
fazekasgy@0 1005
fazekasgy@0 1006 PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue;
fazekasgy@0 1007 FeatureSet returnFeatures;
fazekasgy@8 1008
fazekasgy@8 1009 //iterate through list of outputs
fazekasgy@0 1010 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyOutputList); ++i) {
fazekasgy@0 1011
fazekasgy@0 1012 pyFeatureList = PyList_GET_ITEM(pyOutputList,i);
fazekasgy@0 1013
fazekasgy@8 1014 //iterate list of Features
fazekasgy@0 1015 for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) {
fazekasgy@8 1016 #ifdef _DEBUG
fazekasgy@8 1017 cerr << "feature: " << j << endl;
fazekasgy@8 1018 #endif
fazekasgy@0 1019 pyDict = PyList_GET_ITEM(pyFeatureList,j);
fazekasgy@0 1020
fazekasgy@0 1021 if ( !PyDict_Check(pyDict) ) continue;
fazekasgy@0 1022
fazekasgy@0 1023 Py_ssize_t pyPos = NULL;
fazekasgy@0 1024 bool emptyFeature = true;
fazekasgy@0 1025 Feature feature;
fazekasgy@0 1026
fazekasgy@0 1027 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
fazekasgy@0 1028 {
fazekasgy@0 1029 emptyFeature = false;
fazekasgy@0 1030 switch (ffKeys[PyString_AsString(pyKey)])
fazekasgy@0 1031 {
cannam@18 1032 case unknown :
cannam@18 1033 cerr << "Unknown key in Vamp FeatureSet: "
fazekasgy@0 1034 << PyString_AsString(pyKey) << endl;
fazekasgy@0 1035 break;
fazekasgy@0 1036 case hasTimestamp:
fazekasgy@0 1037 feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue);
fazekasgy@0 1038 break;
fazekasgy@0 1039 case timeStamp:
fazekasgy@0 1040 feature.timestamp =
fazekasgy@8 1041 Vamp::RealTime::frame2RealTime(
fazekasgy@8 1042 PyLong_AsLong(pyValue),
fazekasgy@8 1043 (unsigned int) m_inputSampleRate );
fazekasgy@8 1044 #ifdef _DEBUG
fazekasgy@8 1045 cerr << "Timestamp: "
fazekasgy@8 1046 << (long)PyLong_AsLong(pyValue) << ", ->"
fazekasgy@8 1047 << feature.timestamp.toString() << endl;
fazekasgy@8 1048 #endif
fazekasgy@0 1049 break;
cannam@18 1050 case hasDuration:
cannam@18 1051 feature.hasDuration = (bool) PyInt_AS_LONG(pyValue);
cannam@18 1052 break;
cannam@18 1053 case duration:
cannam@18 1054 feature.duration =
cannam@18 1055 Vamp::RealTime::frame2RealTime(
cannam@18 1056 PyLong_AsLong(pyValue),
cannam@18 1057 (unsigned int) m_inputSampleRate );
cannam@18 1058 #ifdef _DEBUG
cannam@18 1059 cerr << "Duration: "
cannam@18 1060 << (long)PyLong_AsLong(pyValue) << ", ->"
cannam@18 1061 << feature.duration.toString() << endl;
cannam@18 1062 #endif
cannam@18 1063 break;
fazekasgy@0 1064 case values:
fazekasgy@0 1065 feature.values = PyList_As_FloatVector(pyValue);
fazekasgy@0 1066 break;
fazekasgy@0 1067 case label:
fazekasgy@0 1068 feature.label = PyString_AsString(pyValue);
fazekasgy@0 1069 break;
fazekasgy@0 1070 } // switch
fazekasgy@0 1071 } // while
fazekasgy@0 1072 if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl;
fazekasgy@0 1073 else returnFeatures[i].push_back(feature);
fazekasgy@0 1074 }// for j
fazekasgy@0 1075 }//for i
fazekasgy@0 1076 Py_CLEAR(pyOutputList);
fazekasgy@0 1077 return returnFeatures;
fazekasgy@0 1078 }
fazekasgy@0 1079
fazekasgy@6 1080 bool
fazekasgy@6 1081 PyPlugin::initMaps() const
fazekasgy@6 1082 {
fazekasgy@6 1083
fazekasgy@6 1084 if (isMapInitialised) return true;
fazekasgy@6 1085
cannam@18 1086 outKeys["identifier"] = o::identifier;
cannam@18 1087 outKeys["name"] = o::name;
cannam@18 1088 outKeys["description"] = o::description;
cannam@18 1089 outKeys["unit"] = o::unit;
cannam@18 1090 outKeys["hasFixedBinCount"] = o::hasFixedBinCount;
cannam@18 1091 outKeys["binCount"] = o::binCount;
cannam@18 1092 outKeys["binNames"] = o::binNames;
cannam@18 1093 outKeys["hasKnownExtents"] = o::hasKnownExtents;
cannam@18 1094 outKeys["minValue"] = o::minValue;
cannam@18 1095 outKeys["maxValue"] = o::maxValue;
cannam@18 1096 outKeys["isQuantized"] = o::isQuantized;
cannam@18 1097 outKeys["quantizeStep"] = o::quantizeStep;
cannam@18 1098 outKeys["sampleType"] = o::sampleType;
cannam@18 1099 outKeys["sampleRate"] = o::sampleRate;
cannam@18 1100 outKeys["hasDuration"] = o::hasDuration;
fazekasgy@6 1101
fazekasgy@6 1102 sampleKeys["OneSamplePerStep"] = OneSamplePerStep;
fazekasgy@6 1103 sampleKeys["FixedSampleRate"] = FixedSampleRate;
fazekasgy@6 1104 sampleKeys["VariableSampleRate"] = VariableSampleRate;
fazekasgy@6 1105
fazekasgy@6 1106 ffKeys["hasTimestamp"] = hasTimestamp;
fazekasgy@6 1107 ffKeys["timeStamp"] = timeStamp;
cannam@18 1108 ffKeys["hasDuration"] = hasDuration;
cannam@18 1109 ffKeys["duration"] = duration;
fazekasgy@6 1110 ffKeys["values"] = values;
fazekasgy@6 1111 ffKeys["label"] = label;
fazekasgy@6 1112
fazekasgy@6 1113 parmKeys["identifier"] = p::identifier;
fazekasgy@6 1114 parmKeys["name"] = p::name;
fazekasgy@6 1115 parmKeys["description"] = p::description;
fazekasgy@6 1116 parmKeys["unit"] = p::unit;
fazekasgy@6 1117 parmKeys["minValue"] = p::minValue;
fazekasgy@6 1118 parmKeys["maxValue"] = p::maxValue;
fazekasgy@6 1119 parmKeys["defaultValue"] = p::defaultValue;
fazekasgy@6 1120 parmKeys["isQuantized"] = p::isQuantized;
cannam@22 1121 parmKeys["quantizeStep"] = p::quantizeStep;
fazekasgy@6 1122
fazekasgy@6 1123 isMapInitialised = true;
fazekasgy@6 1124 return true;
fazekasgy@6 1125 }
fazekasgy@6 1126
fazekasgy@6 1127
fazekasgy@6 1128 //missing API helper: convert Python list to C++ vector of strings
fazekasgy@6 1129 //TODO: these could be templates if we need more of this kind
fazekasgy@6 1130 std::vector<std::string>
fazekasgy@6 1131 PyPlugin::PyList_To_StringVector (PyObject *inputList) const {
fazekasgy@6 1132
fazekasgy@6 1133 std::vector<std::string> Output;
fazekasgy@6 1134 std::string ListElement;
fazekasgy@6 1135 PyObject *pyString = NULL;
fazekasgy@6 1136
fazekasgy@6 1137 if (!PyList_Check(inputList)) return Output;
fazekasgy@6 1138
fazekasgy@6 1139 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(inputList); ++i) {
fazekasgy@6 1140 //Get next list item (Borrowed Reference)
fazekasgy@6 1141 pyString = PyList_GET_ITEM(inputList,i);
fazekasgy@6 1142 ListElement = (string) PyString_AsString(PyObject_Str(pyString));
fazekasgy@6 1143 Output.push_back(ListElement);
fazekasgy@6 1144 }
fazekasgy@6 1145 return Output;
fazekasgy@6 1146 }
fazekasgy@6 1147
fazekasgy@6 1148 //missing API helper: convert Python list to C++ vector of floats
fazekasgy@6 1149 std::vector<float>
fazekasgy@6 1150 PyPlugin::PyList_As_FloatVector (PyObject *inputList) const {
fazekasgy@6 1151
fazekasgy@6 1152 std::vector<float> Output;
fazekasgy@6 1153 float ListElement;
fazekasgy@6 1154 PyObject *pyFloat = NULL;
fazekasgy@6 1155
fazekasgy@6 1156 if (!PyList_Check(inputList)) return Output;
fazekasgy@6 1157
fazekasgy@6 1158 for (Py_ssize_t k = 0; k < PyList_GET_SIZE(inputList); ++k) {
fazekasgy@6 1159 //Get next list item (Borrowed Reference)
fazekasgy@6 1160 pyFloat = PyList_GET_ITEM(inputList,k);
fazekasgy@6 1161 ListElement = (float) PyFloat_AS_DOUBLE(pyFloat);
fazekasgy@8 1162 #ifdef _DEBUG
fazekasgy@8 1163 cerr << "value: " << ListElement << endl;
fazekasgy@8 1164 #endif
fazekasgy@6 1165 Output.push_back(ListElement);
fazekasgy@6 1166 }
fazekasgy@6 1167
fazekasgy@6 1168 return Output;
fazekasgy@6 1169 }
fazekasgy@6 1170
fazekasgy@6 1171 /* TODO: find out why this produces error, also
fazekasgy@6 1172 do sg more clever about handling RealTime
fazekasgy@6 1173 Vamp::RealTime
fazekasgy@6 1174 PyFrame_As_RealTime (PyObject *frameNo,size_t inputSampleRate) {
fazekasgy@6 1175 Vamp::RealTime result =
fazekasgy@6 1176 Vamp::RealTime::frame2RealTime((size_t)PyInt_AS_LONG(frameNo), inputSampleRate);
fazekasgy@6 1177 return result;
fazekasgy@6 1178 }
fazekasgy@6 1179 */
fazekasgy@6 1180