Mercurial > hg > vampy-host
comparison vampyhost.cpp @ 4:825d787f12df
Toward using NumPy structures instead of low-level strings
author | Chris Cannam |
---|---|
date | Thu, 31 Jan 2013 17:44:37 +0000 |
parents | f12ab1553882 |
children | aacfd14a0ae2 |
comparison
equal
deleted
inserted
replaced
3:f12ab1553882 | 4:825d787f12df |
---|---|
2 | 2 |
3 //include for python extension module: must be first | 3 //include for python extension module: must be first |
4 #include <Python.h> | 4 #include <Python.h> |
5 #include <vampyhost.h> | 5 #include <vampyhost.h> |
6 #include <pyRealTime.h> | 6 #include <pyRealTime.h> |
7 | |
8 //!!! NB all our NumPy stuff is currently using the deprecated API -- | |
9 //!!! need to work out how to update this | |
10 #include "numpy/arrayobject.h" | |
7 | 11 |
8 //includes for vamp host | 12 //includes for vamp host |
9 #include "vamp-hostsdk/Plugin.h" | 13 #include "vamp-hostsdk/Plugin.h" |
10 #include "vamp-hostsdk/PluginHostAdapter.h" | 14 #include "vamp-hostsdk/PluginHostAdapter.h" |
11 #include "vamp-hostsdk/PluginChannelAdapter.h" | 15 #include "vamp-hostsdk/PluginChannelAdapter.h" |
365 static PyObject * | 369 static PyObject * |
366 vampyhost_initialise(PyObject *self, PyObject *args) | 370 vampyhost_initialise(PyObject *self, PyObject *args) |
367 { | 371 { |
368 PyObject *pyPluginHandle; | 372 PyObject *pyPluginHandle; |
369 size_t channels,blockSize,stepSize; | 373 size_t channels,blockSize,stepSize; |
370 //PyObject pyInputSampleType; | |
371 bool mixChannels = false; | |
372 | 374 |
373 if (!PyArg_ParseTuple (args, "Oiii", &pyPluginHandle, | 375 if (!PyArg_ParseTuple (args, "Oiii", &pyPluginHandle, |
374 (size_t) &channels, | 376 (size_t) &channels, |
375 (size_t) &stepSize, | 377 (size_t) &stepSize, |
376 (size_t) &blockSize)) | 378 (size_t) &blockSize)) |
392 PyPluginDescriptor *plugDesc = (PyPluginDescriptor*) key; | 394 PyPluginDescriptor *plugDesc = (PyPluginDescriptor*) key; |
393 | 395 |
394 plugDesc->channels = channels; | 396 plugDesc->channels = channels; |
395 plugDesc->stepSize = stepSize; | 397 plugDesc->stepSize = stepSize; |
396 plugDesc->blockSize = blockSize; | 398 plugDesc->blockSize = blockSize; |
397 plugDesc->inputSampleType = PyPluginDescriptor::int16; | 399 |
398 plugDesc->sampleSize = 2; | |
399 | |
400 /*!!! not a problem with plugin loader adapter | |
401 plugDesc->mixChannels = mixChannels; | |
402 | |
403 size_t minch = plugin->getMinChannelCount(); | |
404 size_t maxch = plugin->getMaxChannelCount(); | |
405 if (mixChannels) channels = 1; | |
406 */ | |
407 /* TODO: DO WE WANT TO MIX IT DOWN? */ | |
408 /* | |
409 if (maxch < channels || channels < minch) { | |
410 PyErr_SetString(PyExc_TypeError, | |
411 "Invalid number of channels."); | |
412 return NULL; } | |
413 */ | |
414 if (!plugin->initialise(channels, stepSize, blockSize)) { | 400 if (!plugin->initialise(channels, stepSize, blockSize)) { |
415 PyErr_SetString(PyExc_TypeError, | 401 PyErr_SetString(PyExc_TypeError, |
416 "Plugin initialization failed."); | 402 "Plugin initialization failed."); |
417 return NULL; } | 403 return NULL; } |
418 | 404 |
419 plugDesc->identifier = | 405 plugDesc->identifier = |
420 plugDesc->key.substr(plugDesc->key.rfind(':')+1); | 406 plugDesc->key.substr(plugDesc->key.rfind(':')+1); |
421 plugDesc->isInitialised = true; | 407 plugDesc->isInitialised = true; |
422 | 408 |
423 return Py_True; | 409 return Py_True; |
410 } | |
411 | |
412 // These conversion functions are borrowed from PyTypeInterface in VamPy | |
413 | |
414 template<typename RET, typename DTYPE> | |
415 static | |
416 RET *pyArrayConvert(char* raw_data_ptr, long length, size_t strides) | |
417 { | |
418 RET *rValue = new RET[length]; | |
419 | |
420 /// check if the array is continuous, if not use strides info | |
421 if (sizeof(DTYPE)!=strides) { | |
422 char* data = (char*) raw_data_ptr; | |
423 for (long i = 0; i<length; ++i){ | |
424 rValue[i] = (RET)(*((DTYPE*)data)); | |
425 data += strides; | |
426 } | |
427 return rValue; | |
428 } | |
429 | |
430 DTYPE* data = (DTYPE*) raw_data_ptr; | |
431 for (long i = 0; i<length; ++i){ | |
432 rValue[i] = (RET)data[i]; | |
433 } | |
434 | |
435 return rValue; | |
436 } | |
437 | |
438 static float * | |
439 pyArrayToFloatArray(PyObject *pyValue) | |
440 { | |
441 if (!PyArray_Check(pyValue)) { | |
442 cerr << "pyArrayToFloatArray: Failed, object has no array interface" << endl; | |
443 return 0; | |
444 } | |
445 | |
446 PyArrayObject* pyArray = (PyArrayObject*) pyValue; | |
447 PyArray_Descr* descr = pyArray->descr; | |
448 | |
449 /// check raw data and descriptor pointers | |
450 if (pyArray->data == 0 || descr == 0) { | |
451 cerr << "pyArrayToFloatArray: Failed, NumPy array has NULL data or descriptor" << endl; | |
452 return 0; | |
453 } | |
454 | |
455 /// check dimensions | |
456 if (pyArray->nd != 1) { | |
457 cerr << "pyArrayToFloatArray: Failed, NumPy array is multi-dimensional" << endl; | |
458 return 0; | |
459 } | |
460 | |
461 /// check strides (useful if array is not continuous) | |
462 size_t strides = *((size_t*) pyArray->strides); | |
463 | |
464 /// convert the array | |
465 switch (descr->type_num) { | |
466 case NPY_FLOAT : // dtype='float32' | |
467 return pyArrayConvert<float,float>(pyArray->data,pyArray->dimensions[0],strides); | |
468 case NPY_DOUBLE : // dtype='float64' | |
469 return pyArrayConvert<float,double>(pyArray->data,pyArray->dimensions[0],strides); | |
470 default: | |
471 cerr << "pyArrayToFloatArray: Failed: Unsupported value type " << descr->type_num << " in NumPy array object (only float32, float64 supported)" << endl; | |
472 return 0; | |
473 } | |
424 } | 474 } |
425 | 475 |
426 | 476 |
427 /* RUN PROCESS */ | 477 /* RUN PROCESS */ |
428 | 478 |
443 | 493 |
444 if (!PyRealTime_Check(pyRealTime)) { | 494 if (!PyRealTime_Check(pyRealTime)) { |
445 PyErr_SetString(PyExc_TypeError,"Valid timestamp required."); | 495 PyErr_SetString(PyExc_TypeError,"Valid timestamp required."); |
446 return NULL; } | 496 return NULL; } |
447 | 497 |
448 // RealTime *rt = PyRealTime_AsPointer(pyRealTime); | 498 string *key; |
449 // if (!rt) return NULL; | |
450 // cerr << ">>>sec: " << rt->sec << " nsec: " << rt->nsec << endl; | |
451 // | |
452 // PyObject *rrt = PyRealTime_FromRealTime (rt); | |
453 | |
454 string *key; | |
455 Plugin *plugin; | 499 Plugin *plugin; |
456 | 500 |
457 if ( !getPluginHandle(pyPluginHandle, &plugin, &key) ) { | 501 if (!getPluginHandle(pyPluginHandle, &plugin, &key)) { |
458 PyErr_SetString(PyExc_AttributeError, | 502 PyErr_SetString(PyExc_AttributeError, |
459 "Invalid or already deleted plugin handle."); | 503 "Invalid or already deleted plugin handle."); |
460 return NULL; } | 504 return NULL; |
505 } | |
461 | 506 |
462 PyPluginDescriptor *pd = (PyPluginDescriptor*) key; | 507 PyPluginDescriptor *pd = (PyPluginDescriptor*) key; |
463 | 508 |
464 if (!pd->isInitialised) { | 509 if (!pd->isInitialised) { |
465 PyErr_SetString(PyExc_StandardError, | 510 PyErr_SetString(PyExc_StandardError, |
467 return NULL; } | 512 return NULL; } |
468 | 513 |
469 size_t channels = pd->channels; | 514 size_t channels = pd->channels; |
470 size_t blockSize = pd->blockSize; | 515 size_t blockSize = pd->blockSize; |
471 | 516 |
472 /* | 517 if (!PyList_Check(pyBuffer)) { |
473 Handle the case when we get the data as a character buffer | 518 PyErr_SetString(PyExc_TypeError, "List of NumPy Array required for process input."); |
474 Handle SampleFormats: int16, float32 | 519 return NULL; |
475 | 520 } |
476 */ | 521 |
477 | 522 if (PyList_GET_SIZE(pyBuffer) != channels) { |
478 if (PyString_Check(pyBuffer)) { | 523 PyErr_SetString(PyExc_TypeError, "Wrong number of channels"); |
479 cerr << ">>> String obj passed in." << endl; | 524 return NULL; |
480 } | 525 } |
481 | 526 |
482 size_t sample_size = sizeof(short); | 527 float **inbuf = new float *[channels]; |
483 | 528 |
484 long buflen = (long) PyString_GET_SIZE(pyBuffer); | 529 for (int c = 0; c < channels; ++c) { |
485 | 530 PyObject *cbuf = PyList_GET_ITEM(pyBuffer, c); |
486 size_t input_length = | 531 inbuf[c] = pyArrayToFloatArray(cbuf); |
487 static_cast <size_t> (buflen/channels/sample_size); | 532 if (!inbuf[c]) { |
488 | 533 PyErr_SetString(PyExc_TypeError,"NumPy Array required for each channel in process input."); |
489 if (input_length == pd->blockSize) { | 534 return NULL; |
490 cerr << ">>> A full block has been passed in." << endl; } | |
491 short *input = | |
492 reinterpret_cast <short*> (PyString_AS_STRING(pyBuffer)); | |
493 | |
494 //convert int16 PCM data to 32-bit floats | |
495 float **plugbuf = new float*[channels]; | |
496 float normfact = 1.0f / static_cast <float> (SHRT_MAX); | |
497 | |
498 for (size_t c = 0; c < channels; ++c) { | |
499 | |
500 plugbuf[c] = new float[blockSize+2]; | |
501 | |
502 size_t j = 0; | |
503 while (j < input_length) { | |
504 plugbuf[c][j] = normfact * | |
505 static_cast <float> (input[j * channels + c]); | |
506 ++j; | |
507 } | 535 } |
508 while (j < blockSize) { | 536 } |
509 plugbuf[c][j] = 0.0f; | |
510 ++j; | |
511 } | |
512 } | |
513 | |
514 const char *output = reinterpret_cast <const char*> (plugbuf[0]); | |
515 Py_ssize_t len = (Py_ssize_t) channels*blockSize*4; | |
516 | |
517 PyObject* pyReturnBuffer = | |
518 PyString_FromStringAndSize(output,len); | |
519 | |
520 // long frame = 1; | |
521 // unsigned int samplerate = (unsigned int) pd->inputSampleRate; | |
522 | 537 |
523 RealTime timeStamp = *PyRealTime_AsPointer(pyRealTime); | 538 RealTime timeStamp = *PyRealTime_AsPointer(pyRealTime); |
524 | 539 |
525 //Call process and store the output | 540 //Call process and store the output |
526 pd->output = plugin->process( | 541 pd->output = plugin->process(inbuf, timeStamp); |
527 plugbuf, | |
528 timeStamp); | |
529 | 542 |
530 /* TODO: DO SOMETHONG WITH THE FEATURE SET HERE */ | 543 /* TODO: DO SOMETHONG WITH THE FEATURE SET HERE */ |
531 /// convert to appropriate python objects, reuse types and conversion utilities from Vampy ... | 544 /// convert to appropriate python objects, reuse types and conversion utilities from Vampy ... |
532 | 545 |
533 | 546 |
534 //We can safely delete here | 547 for (int c = 0; c < channels; ++c){ |
535 for(size_t k=0; k<channels; k++){ | 548 delete[] inbuf[c]; |
536 delete[] plugbuf[k]; | 549 } |
537 } | 550 delete[] inbuf; |
538 delete[] plugbuf; | 551 |
539 | 552 return NULL; //!!! Need to return actual features! |
540 return pyReturnBuffer; | |
541 | 553 |
542 } | 554 } |
543 | 555 |
544 /* GET / SET OUTPUT */ | 556 /* GET / SET OUTPUT */ |
545 | 557 |