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