# HG changeset patch # User Chris Cannam # Date 1359654277 0 # Node ID 825d787f12dff15b3e232ea3c188de3736d4084a # Parent f12ab1553882bbd198e95c79cdb0a2f14de5c4d2 Toward using NumPy structures instead of low-level strings diff -r f12ab1553882 -r 825d787f12df Makefile --- a/Makefile Thu Jan 31 13:31:52 2013 +0000 +++ b/Makefile Thu Jan 31 17:44:37 2013 +0000 @@ -1,8 +1,9 @@ PY_INCLUDE_PATH := /usr/include/python2.7 +NUMPY_INCLUDE_PATH := /usr/lib/python2.7/site-packages/numpy/core/include -CFLAGS := -O2 -fPIC -Wall -I$(PY_INCLUDE_PATH) -I. -CXXFLAGS := -O2 -fPIC -Wall -I$(PY_INCLUDE_PATH) -I. +CFLAGS := -O2 -fPIC -Wall -I$(PY_INCLUDE_PATH) -I$(NUMPY_INCLUDE_PATH) -I. +CXXFLAGS := -O2 -fPIC -Wall -I$(PY_INCLUDE_PATH) -I$(NUMPY_INCLUDE_PATH) -I. LDFLAGS := -shared -lpython2.7 -lvamp-hostsdk #LDFLAGS := -dynamiclib -lpython2.5 /usr/lib/libvamp-hostsdk.a diff -r f12ab1553882 -r 825d787f12df vampyhost.cpp --- a/vampyhost.cpp Thu Jan 31 13:31:52 2013 +0000 +++ b/vampyhost.cpp Thu Jan 31 17:44:37 2013 +0000 @@ -5,6 +5,10 @@ #include #include +//!!! NB all our NumPy stuff is currently using the deprecated API -- +//!!! need to work out how to update this +#include "numpy/arrayobject.h" + //includes for vamp host #include "vamp-hostsdk/Plugin.h" #include "vamp-hostsdk/PluginHostAdapter.h" @@ -367,8 +371,6 @@ { PyObject *pyPluginHandle; size_t channels,blockSize,stepSize; - //PyObject pyInputSampleType; - bool mixChannels = false; if (!PyArg_ParseTuple (args, "Oiii", &pyPluginHandle, (size_t) &channels, @@ -394,23 +396,7 @@ plugDesc->channels = channels; plugDesc->stepSize = stepSize; plugDesc->blockSize = blockSize; - plugDesc->inputSampleType = PyPluginDescriptor::int16; - plugDesc->sampleSize = 2; -/*!!! not a problem with plugin loader adapter - plugDesc->mixChannels = mixChannels; - - size_t minch = plugin->getMinChannelCount(); - size_t maxch = plugin->getMaxChannelCount(); - if (mixChannels) channels = 1; -*/ - /* TODO: DO WE WANT TO MIX IT DOWN? */ -/* - if (maxch < channels || channels < minch) { - PyErr_SetString(PyExc_TypeError, - "Invalid number of channels."); - return NULL; } -*/ if (!plugin->initialise(channels, stepSize, blockSize)) { PyErr_SetString(PyExc_TypeError, "Plugin initialization failed."); @@ -423,6 +409,70 @@ return Py_True; } +// These conversion functions are borrowed from PyTypeInterface in VamPy + +template +static +RET *pyArrayConvert(char* raw_data_ptr, long length, size_t strides) +{ + RET *rValue = new RET[length]; + + /// check if the array is continuous, if not use strides info + if (sizeof(DTYPE)!=strides) { + char* data = (char*) raw_data_ptr; + for (long i = 0; idescr; + + /// check raw data and descriptor pointers + if (pyArray->data == 0 || descr == 0) { + cerr << "pyArrayToFloatArray: Failed, NumPy array has NULL data or descriptor" << endl; + return 0; + } + + /// check dimensions + if (pyArray->nd != 1) { + cerr << "pyArrayToFloatArray: Failed, NumPy array is multi-dimensional" << endl; + return 0; + } + + /// check strides (useful if array is not continuous) + size_t strides = *((size_t*) pyArray->strides); + + /// convert the array + switch (descr->type_num) { + case NPY_FLOAT : // dtype='float32' + return pyArrayConvert(pyArray->data,pyArray->dimensions[0],strides); + case NPY_DOUBLE : // dtype='float64' + return pyArrayConvert(pyArray->data,pyArray->dimensions[0],strides); + default: + cerr << "pyArrayToFloatArray: Failed: Unsupported value type " << descr->type_num << " in NumPy array object (only float32, float64 supported)" << endl; + return 0; + } +} + /* RUN PROCESS */ @@ -445,19 +495,14 @@ PyErr_SetString(PyExc_TypeError,"Valid timestamp required."); return NULL; } - // RealTime *rt = PyRealTime_AsPointer(pyRealTime); - // if (!rt) return NULL; - // cerr << ">>>sec: " << rt->sec << " nsec: " << rt->nsec << endl; - // - // PyObject *rrt = PyRealTime_FromRealTime (rt); - - string *key; + string *key; Plugin *plugin; - if ( !getPluginHandle(pyPluginHandle, &plugin, &key) ) { + if (!getPluginHandle(pyPluginHandle, &plugin, &key)) { PyErr_SetString(PyExc_AttributeError, "Invalid or already deleted plugin handle."); - return NULL; } + return NULL; + } PyPluginDescriptor *pd = (PyPluginDescriptor*) key; @@ -469,75 +514,42 @@ size_t channels = pd->channels; size_t blockSize = pd->blockSize; -/* - Handle the case when we get the data as a character buffer - Handle SampleFormats: int16, float32 - -*/ - - if (PyString_Check(pyBuffer)) { - cerr << ">>> String obj passed in." << endl; + if (!PyList_Check(pyBuffer)) { + PyErr_SetString(PyExc_TypeError, "List of NumPy Array required for process input."); + return NULL; } - size_t sample_size = sizeof(short); + if (PyList_GET_SIZE(pyBuffer) != channels) { + PyErr_SetString(PyExc_TypeError, "Wrong number of channels"); + return NULL; + } - long buflen = (long) PyString_GET_SIZE(pyBuffer); + float **inbuf = new float *[channels]; - size_t input_length = - static_cast (buflen/channels/sample_size); - - if (input_length == pd->blockSize) { - cerr << ">>> A full block has been passed in." << endl; } - short *input = - reinterpret_cast (PyString_AS_STRING(pyBuffer)); - - //convert int16 PCM data to 32-bit floats - float **plugbuf = new float*[channels]; - float normfact = 1.0f / static_cast (SHRT_MAX); - - for (size_t c = 0; c < channels; ++c) { - - plugbuf[c] = new float[blockSize+2]; - - size_t j = 0; - while (j < input_length) { - plugbuf[c][j] = normfact * - static_cast (input[j * channels + c]); - ++j; + for (int c = 0; c < channels; ++c) { + PyObject *cbuf = PyList_GET_ITEM(pyBuffer, c); + inbuf[c] = pyArrayToFloatArray(cbuf); + if (!inbuf[c]) { + PyErr_SetString(PyExc_TypeError,"NumPy Array required for each channel in process input."); + return NULL; } - while (j < blockSize) { - plugbuf[c][j] = 0.0f; - ++j; - } - } - - const char *output = reinterpret_cast (plugbuf[0]); - Py_ssize_t len = (Py_ssize_t) channels*blockSize*4; - - PyObject* pyReturnBuffer = - PyString_FromStringAndSize(output,len); - - // long frame = 1; - // unsigned int samplerate = (unsigned int) pd->inputSampleRate; + } RealTime timeStamp = *PyRealTime_AsPointer(pyRealTime); //Call process and store the output - pd->output = plugin->process( - plugbuf, - timeStamp); + pd->output = plugin->process(inbuf, timeStamp); /* TODO: DO SOMETHONG WITH THE FEATURE SET HERE */ /// convert to appropriate python objects, reuse types and conversion utilities from Vampy ... - //We can safely delete here - for(size_t k=0; k -// structure of NumPy array intrface (just a hack, shouldn't be needed here...) -typedef struct { - int two; /* contains the integer 2 -- simple sanity check */ - int nd; /* number of dimensions */ - char typekind; /* kind in array --- character code of typestr */ - int itemsize; /* size of each element */ - int flags; /* flags indicating how the data should be interpreted */ - /* must set ARR_HAS_DESCR bit to validate descr */ - Py_intptr_t *shape; /* A length-nd array of shape information */ - Py_intptr_t *strides; /* A length-nd array of stride information */ - void *data; /* A pointer to the first element of the array */ - PyObject *descr; /* NULL or data-description (same as descr key */ - /* of __array_interface__) -- must set ARR_HAS_DESCR */ - /* flag or this will be ignored. */ -} PyArrayInterface; - //structure for holding plugin instance data typedef struct { std::string key; @@ -31,12 +15,6 @@ size_t channels; size_t blockSize; size_t stepSize; - size_t sampleSize; - bool mixChannels; - enum InputSampleType { - int16, - float32 }; - InputSampleType inputSampleType; Vamp::Plugin::FeatureSet output; } PyPluginDescriptor; diff -r f12ab1553882 -r 825d787f12df vampyhost_test.py --- a/vampyhost_test.py Thu Jan 31 13:31:52 2013 +0000 +++ b/vampyhost_test.py Thu Jan 31 17:44:37 2013 +0000 @@ -4,10 +4,10 @@ sys.path.append(os.getcwd()) +import scikits.audiolab as al; + #from melscale import melscale #from melscale import initialize -import wave -from wave import * from pylab import * # from melscale import * from numpy import * @@ -20,28 +20,17 @@ #import pyRealTime #from pyRealTime import * - #deal with an audio file wavfile='test.wav' -wavobj = wave.open(wavfile,'r') -samplerate = wavobj.getframerate() +audio, samplerate, format = al.wavread(wavfile); + print "samplerate: ",samplerate -print "number of samples (frames): ",wavobj.getnframes() #total number of samples 4647744 -channels = wavobj.getnchannels(); +print "number of samples (frames): ",audio.size #total number of samples 4647744 +channels = audio[0].size print "channels: ",channels -print "sample-width: ",wavobj.getsampwidth() -print "position: ",wavobj.tell() -#wavobj.setpos(1000000) -print wavobj.tell() -audio = wavobj.readframes(1024) #returns an 8-bit buffer -print wavobj.tell() -print dir(audio) -print len(audio) -wavobj.close() - - +#!!! continue with this lark rt=realtime(4,70)