Mercurial > hg > vampy-host
changeset 0:68f3f32565b4
Import the early draft version
author | Chris Cannam |
---|---|
date | Mon, 22 Oct 2012 16:10:46 +0100 |
parents | |
children | cb0d3af1be4d |
files | Makefile pyRealTime.cpp pyRealTime.h system.h vampyhost-junk.cpp vampyhost.cpp vampyhost.h vampyhost_test.py |
diffstat | 8 files changed, 1603 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Mon Oct 22 16:10:46 2012 +0100 @@ -0,0 +1,23 @@ + +CFLAGS := -O2 -Wall -I/usr/include/python2.5 -I/usr/include/vamp-sdk/hostext/ -I/usr/include/vamp-sdk/ -I/Users/Shared/Development/vampy-host-experiments/ +CXXFLAGS := -O2 -Wall -I/usr/include/python2.5 -I/usr/include/vamp-sdk/hostext/ -I/usr/include/vamp-sdk/ -I/Users/Shared/Development/vampy-host-experiments/ +LDFLAGS := -dynamiclib -lpython2.5 /usr/lib/libvamp-hostsdk.a + + +all: pyRealTime.so vampyhost.so + +pyRealTime.a: pyRealTime.o + ar r $@ pyRealTime.o + +pyRealTime.so: pyRealTime.o + g++ -shared $^ -o $@ $(LDFLAGS) + +vampyhost.so: vampyhost.o pyRealTime.a + g++ -o $@ -shared $^ $(LDFLAGS) + + +clean: + rm *.o + rm *.so + rm *.a +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pyRealTime.cpp Mon Oct 22 16:10:46 2012 +0100 @@ -0,0 +1,436 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ +/* + + This module exposes a Type Object wrapping Vamp::RealTime + together with module level functions to create + new pyRealTime objects from frame count, samplerate or sec,nsec tuples. + + A small API is provided for the C/C++ programmer and relevant + functions are exposed to Python. + + TODO: implement number protocol (i.e. wrap arithmetic operators) + partly done + +*/ + +#include <Python.h> +#include <pyRealTime.h> +#include "vamp-sdk/Plugin.h" +#include <string> + +using namespace std; +using namespace Vamp; + +using Vamp::Plugin; +using Vamp::RealTime; + +/* REAL-TIME TYPE OBJECT */ + + +/* Documentation for our new module */ +PyDoc_STRVAR(module_doc, + "This module is a thin wrapper around Vamp::RealTime."); + + +/* RealTime Object's Methods */ +//Note: these are internals, not exposed by the module but the object + +/* Returns a Tuple containing sec and nsec values */ +static PyObject * +RealTime_values(RealTimeObject *self) +{ + return Py_BuildValue("(ii)", + self->rt->sec,self->rt->nsec); +} + +/* Returns a Text representation */ +static PyObject * +RealTime_toText(RealTimeObject *self, PyObject *args) +{ + return Py_BuildValue("s", + self->rt->toText().c_str()); +} + +/* String representation called by e.g. str(realtime), print realtime*/ +static PyObject * +RealTime_repr(PyObject *self) +{ + return Py_BuildValue("s", + ((RealTimeObject*)self)->rt->toString().c_str()); +} + + +/* Frame representation */ +static PyObject * +RealTime_toFrame(PyObject *self, PyObject *args) +{ + unsigned int samplerate; + + if ( !PyArg_ParseTuple(args, "I:realtime.toFrame object ", + (unsigned int *) &samplerate )) { + PyErr_SetString(PyExc_ValueError, + "Sample Rate Required."); + return NULL; + } + + return Py_BuildValue("k", + RealTime::realTime2Frame( + *(const RealTime*) ((RealTimeObject*)self)->rt, + (unsigned int) samplerate)); +} + +/* Conversion of realtime to a double precision floating point value */ +/* ...in Python called by e.g. float(realtime) */ +static PyObject * +RealTime_float(PyObject *s) +{ + double drt = ((double) ((RealTimeObject*)s)->rt->sec + + (double)((double) ((RealTimeObject*)s)->rt->nsec)/1000000000); + return PyFloat_FromDouble(drt); +} + +/* test */ +static PyObject * +RealTime_test(PyObject *self) +{ + + long frame = 100; + unsigned int sampleRate = 22050; + + const RealTime t = RealTime::frame2RealTime(frame,sampleRate); + long back = RealTime::realTime2Frame(t,sampleRate); + cerr << "Reverse Conversion: " << back << endl; + + return Py_BuildValue("s", + ((RealTimeObject*)self)->rt->toString().c_str()); +} + + +/* Type object's (RealTime) methods table */ +static PyMethodDef RealTime_methods[] = { + + {"toText", (PyCFunction)RealTime_toText, METH_NOARGS, + PyDoc_STR("toText() -> Return a user-readable string to the nearest millisecond in a form like HH:MM:SS.mmm")}, + + {"values", (PyCFunction)RealTime_values, METH_NOARGS, + PyDoc_STR("values() -> Tuple of sec,nsec representation.")}, + + {"toFrame", (PyCFunction)RealTime_toFrame, METH_VARARGS, + PyDoc_STR("toFrame(samplerate) -> Sample count for given sample rate.")}, + + {"toFloat", (PyCFunction)RealTime_float, METH_NOARGS, + PyDoc_STR("float() -> Floating point representation.")}, + + {"test", (PyCFunction)RealTime_test, METH_VARARGS, + PyDoc_STR("test() -> .")}, + + {NULL, NULL} /* sentinel */ +}; + + + +/* Function to set basic attributes */ +static int +RealTime_setattr(RealTimeObject *self, char *name, PyObject *value) +{ + + if ( !string(name).compare("sec")) { + self->rt->sec= (int) PyInt_AS_LONG(value); + return 0; + } + + if ( !string(name).compare("nsec")) { + self->rt->nsec= (int) PyInt_AS_LONG(value); + return 0; + } + + return -1; +} + +/* Function to get basic attributes */ +static PyObject * +RealTime_getattr(RealTimeObject *self, char *name) +{ + + if ( !string(name).compare("sec") ) { + return PyInt_FromSsize_t( + (Py_ssize_t) self->rt->sec); + } + + if ( !string(name).compare("nsec") ) { + return PyInt_FromSsize_t( + (Py_ssize_t) self->rt->nsec); + } + + return Py_FindMethod(RealTime_methods, + (PyObject *)self, name); +} + + +/* DESTRUCTOR: delete type object */ +static void +RealTimeObject_dealloc(RealTimeObject *self) +{ + delete self->rt; //delete the C object + PyObject_Del(self); //delete the Python object +} + +/* Number Protocol */ + + +static PyObject * +RealTime_add(PyObject *s, PyObject *w) +{ + + RealTimeObject *result = + PyObject_New(RealTimeObject, &RealTime_Type); + if (result == NULL) return NULL; + + result->rt = new RealTime::RealTime( + *((RealTimeObject*)s)->rt + *((RealTimeObject*)w)->rt); + return (PyObject*)result; +} + +static PyObject * +RealTime_subtract(PyObject *s, PyObject *w) +{ + + RealTimeObject *result = + PyObject_New(RealTimeObject, &RealTime_Type); + if (result == NULL) return NULL; + + result->rt = new RealTime::RealTime( + *((RealTimeObject*)s)->rt - *((RealTimeObject*)w)->rt); + return (PyObject*)result; +} + + +static PyNumberMethods realtime_as_number = { + RealTime_add, /*nb_add*/ + RealTime_subtract, /*nb_subtract*/ + 0, /*nb_multiply*/ + 0, /*nb_divide*/ + 0, /*nb_remainder*/ + 0, /*nb_divmod*/ + 0, /*nb_power*/ + 0, /*nb_neg*/ + 0, /*nb_pos*/ + 0, /*(unaryfunc)array_abs,*/ + 0, /*nb_nonzero*/ + 0, /*nb_invert*/ + 0, /*nb_lshift*/ + 0, /*nb_rshift*/ + 0, /*nb_and*/ + 0, /*nb_xor*/ + 0, /*nb_or*/ + 0, /*nb_coerce*/ + 0, /*nb_int*/ + 0, /*nb_long*/ + (unaryfunc)RealTime_float, /*nb_float*/ + 0, /*nb_oct*/ + 0, /*nb_hex*/ +}; + +/* pyRealTime TypeObject */ + + +/* Doc:: 10.3 Type Objects */ +/* static */ PyTypeObject RealTime_Type = { + /* The ob_type field must be initialized in the module init function + * to be portable to Windows without using C++. */ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "pyRealTime.realtime", /*tp_name*/ + sizeof(RealTimeObject), /*tp_basicsize*/ + sizeof(RealTime), /*tp_itemsize*/ + /* methods */ + (destructor)RealTimeObject_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)RealTime_getattr, /*tp_getattr*/ + (setattrfunc)RealTime_setattr, /*tp_setattr*/ + 0, /*tp_compare*/ + RealTime_repr, /*tp_repr*/ + &realtime_as_number, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0,//(ternaryfunc)RealTime_new, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + RealTime_methods, /*tp_methods*/ //TypeObject Methods + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + + +/* Remaining Functions Exposed by the MODULE */ + + +/* New RealTime object from Frame (with given samplerate) */ +/*static*/ PyObject * +RealTime_frame2RealTime(PyObject *ignored, PyObject *args) +{ + + long frame; + unsigned int sampleRate; + + if (!PyArg_ParseTuple(args, "lI:realtime.fame2RealTime ", + &frame, + &sampleRate)) + return NULL; + /*Doc:: 5.5 Parsing arguments and building values*/ + + RealTimeObject *self; + self = PyObject_New(RealTimeObject, &RealTime_Type); + if (self == NULL) + return NULL; + + self->rt = new RealTime::RealTime( + RealTime::frame2RealTime(frame,sampleRate)); + + return (PyObject *) self; +} + +/* New RealTime object from sec and nsec */ +/*static*/ PyObject * +RealTime_new(PyObject *ignored, PyObject *args) +{ + + unsigned int sec = 0; + unsigned int nsec = 0; + double unary = 0; + const char *fmt = NULL; + + /*Doc:: 5.5 Parsing arguments and building values*/ + if ( + + !PyArg_ParseTuple(args, "|sd:realtime.new ", + (const char *) &fmt, + (double *) &unary) && + + !PyArg_ParseTuple(args, "|II:realtime.new ", + (unsigned int*) &sec, + (unsigned int*) &nsec) + + ) { + PyErr_SetString(PyExc_TypeError, + "RealTime initialised with wrong arguments."); + return NULL; } + + PyErr_Clear(); + + RealTimeObject *self = + PyObject_New(RealTimeObject, &RealTime_Type); + if (self == NULL) return NULL; + + self->rt = NULL; + + if (sec == 0 && nsec == 0 && fmt == 0) + self->rt = new RealTime::RealTime(); + else if (fmt == 0) + self->rt = new RealTime::RealTime(sec,nsec); + else { + + if (!string(fmt).compare("float") || + !string(fmt).compare("seconds")) + self->rt = new RealTime::RealTime( + RealTime::fromSeconds((double) unary)); + + if (!string(fmt).compare("milliseconds")) { + self->rt = new RealTime::RealTime( + RealTime::fromSeconds((double) unary / 1000.0)); } + } + + if (!self->rt) { + PyErr_SetString(PyExc_TypeError, + "RealTime initialised with wrong arguments."); + return NULL; + } + + return (PyObject *) self; +} + + +/* pyRealTime Module's methods table */ +static PyMethodDef Module_methods[] = { + + {"frame2RealTime", (PyCFunction)RealTime_frame2RealTime, METH_VARARGS, + PyDoc_STR("frame2RealTime((int64)frame, (uint32)sampleRate ) -> returns new RealTime object from frame.")}, + + {"realtime", RealTime_new, METH_VARARGS, + PyDoc_STR("realtime() -> returns new RealTime object")}, + + {NULL, NULL} /* sentinel */ +}; + + +/* PyRealTime C API functions */ + + + +/*RealTime from PyRealTime*/ +RealTime* +PyRealTime_AsPointer (PyObject *self) { + + RealTimeObject *s = (RealTimeObject*) self; + + if (!PyRealTime_Check(s)) { + PyErr_SetString(PyExc_TypeError, "RealTime Object Expected."); + cerr << "in call PyRealTime_AsPointer(): RealTime Object Expected. " << endl; + return NULL; } + return s->rt; }; + +/*PyRealTime from RealTime*/ +PyObject* +PyRealTime_FromRealTime(Vamp::RealTime *rt) { + + RealTimeObject *self = + PyObject_New(RealTimeObject, &RealTime_Type); + if (self == NULL) return NULL; + + self->rt = new RealTime::RealTime(*rt); + return (PyObject*) self; + //TODO: check if we need to INCREF here +} + + +/* Module initialization (includes extern "C" {...}) */ +PyMODINIT_FUNC +initpyRealTime(void) +{ + PyObject *m; + + /* Finalize the type object including setting type of the new type + * object; doing it here is required for portability to Windows + * without requiring C++. */ + if (PyType_Ready(&RealTime_Type) < 0) + return; + + /* Create the module and add the functions */ + m = Py_InitModule3("pyRealTime", Module_methods, module_doc); + if (m == NULL) + return; + +// PyModule_AddObject(m, "realtime", (PyObject *)&RealTime_Type); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pyRealTime.h Mon Oct 22 16:10:46 2012 +0100 @@ -0,0 +1,47 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ +#ifndef _PYREALTIME_H_ +#define _PYREALTIME_H_ + +#include "vamp-sdk/Plugin.h" + +/* RealTime Type Object's structure */ +/* Doc:: 10.2 Common Object Structures */ +typedef struct { + PyObject_HEAD + /*PyObject *rt_attrs;*/ + Vamp::RealTime::RealTime *rt; +} RealTimeObject; + +PyAPI_DATA(PyTypeObject) RealTime_Type; + +#define PyRealTime_CheckExact(v) ((v)->ob_type == &RealTime_Type) +#define PyRealTime_Check(v) PyObject_TypeCheck(v, &RealTime_Type) + +/* pyRealTime C API functions */ +// Example from Python's stringobject.h +// PyAPI_FUNC(PyObject *) PyString_FromString(const char *); + +#ifdef __cplusplus +extern "C" { +#endif + +/* PyRealTime C API functions */ + +PyAPI_FUNC(PyObject *) +PyRealTime_FromRealTime(Vamp::RealTime *rt); + +PyAPI_FUNC(Vamp::RealTime::RealTime *) +PyRealTime_AsPointer (PyObject *self); + +/* PyRealTime Module functions */ + +PyAPI_FUNC(PyObject *) +RealTime_new(PyObject *ignored, PyObject *args); + +PyAPI_FUNC(PyObject *) +RealTime_frame2RealTime(PyObject *ignored, PyObject *args); + +#ifdef __cplusplus +} +#endif +#endif /* _PYREALTIME_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/system.h Mon Oct 22 16:10:46 2012 +0100 @@ -0,0 +1,75 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006 Chris Cannam. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the Centre for + Digital Music; Queen Mary, University of London; and Chris Cannam + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#ifndef _SYSTEM_H_ +#define _SYSTEM_H_ + +#ifdef _WIN32 + +#include <windows.h> + +#define DLOPEN(a,b) LoadLibrary((a).c_str()) +#define DLSYM(a,b) GetProcAddress((HINSTANCE)(a),(b)) +#define DLCLOSE(a) FreeLibrary((HINSTANCE)(a)) +#define DLERROR() "" + +#define PLUGIN_SUFFIX "dll" + +#else + +#include <dlfcn.h> + +#define DLOPEN(a,b) dlopen((a).c_str(),(b)) +#define DLSYM(a,b) dlsym((a),(b)) +#define DLCLOSE(a) dlclose((a)) +#define DLERROR() dlerror() + +#ifdef __APPLE__ + +#define PLUGIN_SUFFIX "dylib" +#define HAVE_OPENDIR 1 + +#else + +#define PLUGIN_SUFFIX "so" +#define HAVE_OPENDIR 1 + +#endif /* __APPLE__ */ + +#endif /* ! _WIN32 */ + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vampyhost-junk.cpp Mon Oct 22 16:10:46 2012 +0100 @@ -0,0 +1,170 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +// Moving stuff around + +static PyObject * +vampyhost_process(PyObject *self, PyObject *args) +{ +//check if the plugin has been initialised +//obtain sample Rate: maybe library:identifier:channels:stepSize:blockSize + PyObject *pyPluginHandle; + PyObject *pyBuffer; + + if (!PyArg_ParseTuple(args, "OO", + &pyPluginHandle, // C object holding a pointer to a plugin and its descriptor + &pyBuffer)) { // Audio data + PyErr_SetString(PyExc_TypeError, + "Required: plugin handle, buffer, timestmap."); + return NULL; } + + string *key; + Plugin *plugin; + long frame = 0; + + if ( !getPluginHandle(pyPluginHandle, &plugin, &key) ) { + PyErr_SetString(PyExc_AttributeError, + "Invalid or already deleted plugin handle."); + return NULL; } + + PyPluginDescriptor *pd = (PyPluginDescriptor*) key; + + if (!pd->isInitialised) { + PyErr_SetString(PyExc_StandardError, + "Plugin has not been initialised."); + return NULL; } + + 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; + } + +// size_t chlen = sizeof(short) / sizeof(char); + + //Assume interleaved signed 16-bit PCM data + + //int *intch = new int*[buflen/2]; + //int *intch = (int*) PyString_AS_STRING(pyBuffer); + //short *tmpch = + //reinterpret_cast <short*> (PyString_AS_STRING(pyBuffer)); + + typedef char int16[2]; //can we trust sizeof(short) = 2 ? + size_t sample_size = sizeof(int16); + + long buflen = (long) PyString_GET_SIZE(pyBuffer); + + size_t input_length = + static_cast <size_t> (buflen/channels/sample_size); + + if (input_length == pd->blockSize) { + cerr << ">>> A full block has been passed in." << endl; } + + int16 *input = + reinterpret_cast <int16*> (PyString_AS_STRING(pyBuffer)); + + // int16 *input = new int16[buflen/sample_size]; + // input = reinterpret_cast <int16*> (PyString_AS_STRING(pyBuffer)); + + // short *input = + // reinterpret_cast <short*> (PyString_AS_STRING(pyBuffer)); + + //float ffirst = + //static_cast <float> (*input[1000]) / + //static_cast <float> (SHRT_MAX); + +// int *proba[10]; -> pointer array + int *proba = new int[10]; // -> actual array of ints + int p = 234; + proba[1]=p; + size_t chlen = (size_t) buflen/2; + //short smax = SHRT_MAX; + cerr + << " c: " << sizeof(char) + << " s: " << sizeof(short) + //<< " i16: " << sizeof(int16) + << " i:" << sizeof(int) + << " float:" << sizeof(float) + << " [proba]: " << proba[1] + //<< " ffirst: " << ffirst + << endl; + + //vector<int> *intch = (vector<int>*) PyString_AS_STRING(pyBuffer); + //size_t chlen = intch->size(); + //cerr << ">>>Size of ch buffer: " << chlen << endl; + + //convert int16 PCM data to 32-bit floats + float **plugbuf = new float*[channels]; + float smax = static_cast <float> (SHRT_MAX); + + for (size_t c = 0; c < channels; ++c) { + + plugbuf[c] = new float[blockSize+2]; + + size_t j = 0; + while (j < input_length) { + //int *v = (*int) input[j * channels + c]; + //int value = 5;//input[j * channels + c]; + // short *v = (short*) input+j; + // short value = *v; + //int *v = (int*) input+j; + int *v = new int; + *v = 0; + char *wc = (char*) v; + char *ic = (char*) input[j]; + wc=wc+2; + *wc = *ic; + wc++; ic++; + *wc = *ic; + + int value = *v; + + plugbuf[c][j] = static_cast <float> (value/100000); +// works if short=2 static_cast <float> (*input[j * channels + c]) / smax; +// static_cast <float> (input[j * channels + c]) / smax; + ++j; + } + while (j < blockSize) { + plugbuf[c][j] = 0.0f; + ++j; + } + + //} + } + + const char *output = reinterpret_cast <const char*> (plugbuf[0]); + Py_ssize_t len = (Py_ssize_t) channels*blockSize*4; + + PyObject* pyReturnBuffer = + PyString_FromStringAndSize(output,len); + + return pyReturnBuffer; + + +/* NOW return the data in a PyBuffer + + */ + +/* + char* test = PyString_AS_STRING(pyBuffer); + cerr << "Passed in: " << buflen << " str: " << test << endl; + +//convert the buffer to plugbuf + +//plugin->process +// (plugbuf, RealTime::frame2RealTime(frame, samplerate)) + +for(size_t k=0; k<channels; k++){ +delete[] plugbuf[k]; +} +delete[] plugbuf; +*/ + return pyReturnBuffer; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vampyhost.cpp Mon Oct 22 16:10:46 2012 +0100 @@ -0,0 +1,691 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +//include for python extension module: must be first +#include <Python.h> +#include <vampyhost.h> +#include <pyRealTime.h> + +//includes for vamp host +#include "vamp-sdk/Plugin.h" +#include "vamp-sdk/PluginHostAdapter.h" +#include "vamp-sdk/hostext/PluginChannelAdapter.h" +#include "vamp-sdk/hostext/PluginInputDomainAdapter.h" +#include "vamp-sdk/hostext/PluginLoader.h" +//#include "vamp/vamp.h" + +#include <iostream> +#include <fstream> +#include <set> +#include <sndfile.h> + +#include <cstring> +#include <cstdlib> +#include <string> + +#include "system.h" + +#include <cmath> + + +using namespace std; +using namespace Vamp; + +using Vamp::Plugin; +using Vamp::PluginHostAdapter; +using Vamp::RealTime; +using Vamp::HostExt::PluginLoader; + +#define HOST_VERSION "1.1" + + +/* MODULE HELPER FUNCTIONS */ +PyDoc_STRVAR(xx_foo_doc, "Some description"); + +/*obtain C plugin handle and key from pyCobject */ +int getPluginHandle +(PyObject *pyPluginHandle, Plugin **plugin, string **pKey=NULL) { + + //char errormsg[]="Wrong input argument: Plugin Handle required."; + + *plugin = NULL; + if (!PyCObject_Check(pyPluginHandle)) return NULL; + + //try to convert to Plugin pointer + Plugin *p = (Plugin*) PyCObject_AsVoidPtr(pyPluginHandle); + if (!p) return NULL; + + string pId; + + if (pKey) { + *pKey = (string*) PyCObject_GetDesc(pyPluginHandle); + if (!*pKey) return NULL; + pId = *(string*) *pKey; + + } else { + + void *pKey = PyCObject_GetDesc(pyPluginHandle); + if (!pKey) return NULL; + pId = *(string*) pKey; + } + + string::size_type pos = pId.find(':'); + if (pos == string::npos) return NULL; + + pId = pId.substr(pId.rfind(':')+1); + string identifier = p->getIdentifier(); + + if (pId.compare(identifier)) return NULL; + + *plugin = p; + return true; +} + +/* + ---------------------------------------------------------------- +*/ + + + +/* + VAMPYHOST MAIN + --------------------------------------------------------------------- +*/ + +/* ENUMERATE PLUGINS*/ + +static PyObject * +vampyhost_enumeratePlugins(PyObject *self, PyObject *args) +{ + string retType; + + if (!PyArg_ParseTuple(args, "|s:enumeratePlugins", &retType)) + return NULL; + + //list available plugins + PluginLoader *loader = PluginLoader::getInstance(); + vector<PluginLoader::PluginKey> plugins = loader->listPlugins(); + + //library Map + typedef multimap<string, PluginLoader::PluginKey> LibraryMap; + LibraryMap libraryMap; + + //New list object + PyObject *pyList = PyList_New(plugins.size()); + + for (size_t i = 0; i < plugins.size(); ++i) { + string path = loader->getLibraryPathForPlugin(plugins[i]); + libraryMap.insert(LibraryMap::value_type(path, plugins[i])); + + PyObject *pyPluginKey = PyString_FromString(plugins[i].c_str()); + PyList_SET_ITEM(pyList,i,pyPluginKey); + + } + + PyList_Sort(pyList); + return pyList; +} + + +/* GET PLUGIN LIBRARY PATH*/ + +static PyObject * +vampyhost_getLibraryPath(PyObject *self, PyObject *args) +{ + PyObject *pyPluginKey; + + if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) { + PyErr_SetString(PyExc_TypeError, + "String input argument required: pluginKey"); + return NULL; } + + //convert to stl string + string pluginKey(PyString_AS_STRING(pyPluginKey)); + + //check pluginKey Validity + string::size_type ki = pluginKey.find(':'); + if (ki == string::npos) { + PyErr_SetString(PyExc_TypeError, + "String input argument required: pluginLibrary:Identifier"); + return NULL; + } + + PluginLoader *loader = PluginLoader::getInstance(); + string path = loader->getLibraryPathForPlugin(pluginKey); + PyObject *pyPath = PyString_FromString(path.c_str()); + return pyPath; +} + + +/* GET PLUGIN CATEGORY*/ + +static PyObject * +vampyhost_getPluginCategory(PyObject *self, PyObject *args) +{ + PyObject *pyPluginKey; + + if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) { + PyErr_SetString(PyExc_TypeError, + "String input argument required: pluginKey"); + return NULL; } + + //convert to stl string + string pluginKey(PyString_AS_STRING(pyPluginKey)); + + //check pluginKey Validity + string::size_type ki = pluginKey.find(':'); + if (ki == string::npos) { + PyErr_SetString(PyExc_TypeError, + "String input argument required: pluginLibrary:Identifier"); + return NULL; + } + + PluginLoader *loader = PluginLoader::getInstance(); + PluginLoader::PluginCategoryHierarchy + category = loader->getPluginCategory(pluginKey); + string catstring; + + if (!category.empty()) { + catstring = ""; + for (size_t ci = 0; ci < category.size(); ++ci) { + catstring.append(category[ci]); + catstring.append(" "); + } + PyObject *pyCat = PyString_FromString(catstring.c_str()); + return pyCat; + } + PyObject *pyCat = PyString_FromString(""); + return pyCat; +} + + + +/* GET PLUGIN OUTPUT LIST*/ + +static PyObject * +vampyhost_getOutputList(PyObject *self, PyObject *args) +{ + PyObject *pyPluginHandle; + string pluginKey; + + if (!PyArg_ParseTuple(args, "O", &pyPluginHandle)) { + PyErr_SetString(PyExc_TypeError, + "Invalid argument: plugin handle or plugin key required."); + return NULL; + } + + //check if we have a plugin key string or a handle object + if (PyString_Check(pyPluginHandle) ) { + + pluginKey.assign(PyString_AS_STRING(pyPluginHandle)); + //check pluginKey Validity + string::size_type ki = pluginKey.find(':'); + if (ki == string::npos) { + PyErr_SetString(PyExc_TypeError, + "String input argument required: pluginLibrary:Identifier"); + return NULL; + } + + } else { + + string *key; + Plugin *plugin; + + if ( !getPluginHandle(pyPluginHandle, &plugin, &key) ) { + PyErr_SetString(PyExc_TypeError, + "Invalid or deleted plugin handle."); + return NULL; } + pluginKey.assign(*key); + } + + //This code creates new instance of the plugin anyway + PluginLoader *loader = PluginLoader::getInstance(); + + //load plugin + Plugin *plugin = loader->loadPlugin (pluginKey, 48000); + if (!plugin) { + string pyerr("Failed to load plugin: "); pyerr += pluginKey; + PyErr_SetString(PyExc_TypeError,pyerr.c_str()); + return NULL; + } + + Plugin::OutputList outputs = plugin->getOutputDescriptors(); + //Plugin::OutputDescriptor od; + + if (outputs.size()<1) { + string pyerr("Plugin has no output: "); pyerr += pluginKey; + PyErr_SetString(PyExc_TypeError,pyerr.c_str()); + return NULL; + } + + //New list object + PyObject *pyList = PyList_New(outputs.size()); + + for (size_t i = 0; i < outputs.size(); ++i) { + PyObject *pyOutputId = + PyString_FromString(outputs[i].identifier.c_str()); + PyList_SET_ITEM(pyList,i,pyOutputId); + } + + delete plugin; + return pyList; +} + + + +/* LOAD PLUGIN */ + +static PyObject * +vampyhost_loadPlugin(PyObject *self, PyObject *args) +{ + PyObject *pyPluginKey; + float inputSampleRate; + + if (!PyArg_ParseTuple(args, "Sf", + &pyPluginKey, + &inputSampleRate)) { + PyErr_SetString(PyExc_TypeError, + "String input argument required: pluginKey"); + return NULL; } + + //convert to stl string + string pluginKey(PyString_AS_STRING(pyPluginKey)); + + //check pluginKey Validity + string::size_type ki = pluginKey.find(':'); + if (ki == string::npos) { + PyErr_SetString(PyExc_TypeError, + "String input argument required: pluginLibrary:Identifier"); + return NULL; + } + + PluginLoader *loader = PluginLoader::getInstance(); + + //load plugin + Plugin *plugin = loader->loadPlugin (pluginKey, inputSampleRate); + if (!plugin) { + string pyerr("Failed to load plugin: "); pyerr += pluginKey; + PyErr_SetString(PyExc_TypeError,pyerr.c_str()); + return NULL; + } + //void *identifier = (void*) new string(pluginKey); + PyPluginDescriptor *pd = new PyPluginDescriptor; + + pd->key = pluginKey; + pd->isInitialised = false; + pd->inputSampleRate = inputSampleRate; + + //New PyCObject + //PyObject *pyPluginHandle = PyCObject_FromVoidPtrAndDesc( + //(void*) plugin, identifier, NULL); + + PyObject *pyPluginHandle = PyCObject_FromVoidPtrAndDesc( + (void*) plugin, (void*) pd, NULL); + + return pyPluginHandle; +} + + + +/* UNLOAD PLUGIN */ + +static PyObject * +vampyhost_unloadPlugin(PyObject *self, PyObject *args) +{ + PyObject *pyPluginHandle; + + if (!PyArg_ParseTuple(args, "O", &pyPluginHandle)) { + PyErr_SetString(PyExc_TypeError, + "Wrong input argument: Plugin Handle required."); + return NULL; } + + string *key; + Plugin *plugin; + + if ( !getPluginHandle(pyPluginHandle, &plugin, &key) ) { + PyErr_SetString(PyExc_TypeError, + "Invalid or already deleted plugin handle."); + return NULL; } + +/* Prevent repeated calls from causing segfault + sice it will fail type checking the 2nd time: */ + PyCObject_SetVoidPtr(pyPluginHandle,NULL); + + PyPluginDescriptor *pd = (PyPluginDescriptor*) key; + + delete plugin; + delete pd; + return pyPluginHandle; + +} + + +/* INITIALISE PLUGIN */ + +static PyObject * +vampyhost_initialise(PyObject *self, PyObject *args) +{ + PyObject *pyPluginHandle; + size_t channels,blockSize,stepSize; + //PyObject pyInputSampleType; + bool mixChannels = false; + + if (!PyArg_ParseTuple (args, "Oiii", &pyPluginHandle, + (size_t) &channels, + (size_t) &stepSize, + (size_t) &blockSize)) + { + PyErr_SetString(PyExc_TypeError, + "Wrong input arguments: requires a valid plugin handle,channels,stepSize,blockSize."); + return NULL; + } + + Plugin *plugin; + string *key; + + if ( !getPluginHandle(pyPluginHandle, &plugin, &key) ) { + PyErr_SetString(PyExc_TypeError, + "Invalid plugin handle."); + return NULL; } + + // here we cast the void pointer as PyPluginDescriptor instead of string + PyPluginDescriptor *plugDesc = (PyPluginDescriptor*) key; + + plugDesc->channels = channels; + plugDesc->stepSize = stepSize; + plugDesc->blockSize = blockSize; + plugDesc->inputSampleType = PyPluginDescriptor::int16; + plugDesc->sampleSize = 2; + 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."); + return NULL; } + + plugDesc->identifier = + plugDesc->key.substr(plugDesc->key.rfind(':')+1); + plugDesc->isInitialised = true; + + return Py_True; +} + + +/* RUN PROCESS */ + +static PyObject * +vampyhost_process(PyObject *self, PyObject *args) +{ + PyObject *pyPluginHandle; + PyObject *pyBuffer; + PyObject *pyRealTime; + + if (!PyArg_ParseTuple(args, "OOO", + &pyPluginHandle, // C object holding a pointer to a plugin and its descriptor + &pyBuffer, // Audio data + &pyRealTime)) { // TimeStamp + PyErr_SetString(PyExc_TypeError, + "Required: plugin handle, buffer, timestmap."); + return NULL; } + + if (!PyRealTime_Check(pyRealTime)) { + 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; + Plugin *plugin; + + if ( !getPluginHandle(pyPluginHandle, &plugin, &key) ) { + PyErr_SetString(PyExc_AttributeError, + "Invalid or already deleted plugin handle."); + return NULL; } + + PyPluginDescriptor *pd = (PyPluginDescriptor*) key; + + if (!pd->isInitialised) { + PyErr_SetString(PyExc_StandardError, + "Plugin has not been initialised."); + return NULL; } + + 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; + } + + size_t sample_size = sizeof(short); + + long buflen = (long) PyString_GET_SIZE(pyBuffer); + + size_t input_length = + static_cast <size_t> (buflen/channels/sample_size); + + if (input_length == pd->blockSize) { + cerr << ">>> A full block has been passed in." << endl; } + short *input = + reinterpret_cast <short*> (PyString_AS_STRING(pyBuffer)); + + //convert int16 PCM data to 32-bit floats + float **plugbuf = new float*[channels]; + float normfact = 1.0f / static_cast <float> (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 <float> (input[j * channels + c]); + ++j; + } + while (j < blockSize) { + plugbuf[c][j] = 0.0f; + ++j; + } + } + + const char *output = reinterpret_cast <const char*> (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); + + /* 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<channels; k++){ + delete[] plugbuf[k]; + } + delete[] plugbuf; + + return pyReturnBuffer; + +} + +/* GET / SET OUTPUT */ + +//getOutput(plugin,outputNo) +static PyObject * +vampyhost_getOutput(PyObject *self, PyObject *args) { + + PyObject *pyPluginHandle; +// PyObject *pyBuffer; +// PyObject *pyRealTime; + PyObject *pyOutput; + + if (!PyArg_ParseTuple(args, "OO", + &pyPluginHandle, // C object holding a pointer to a plugin and its descriptor + &pyOutput)) { // Output reference + PyErr_SetString(PyExc_TypeError, + "Required: plugin handle, buffer, timestmap."); + return NULL; } + + string *key; + Plugin *plugin; + + if ( !getPluginHandle(pyPluginHandle, &plugin, &key) ) { + PyErr_SetString(PyExc_AttributeError, + "Invalid or already deleted plugin handle."); + return NULL; } + + PyPluginDescriptor *pd = (PyPluginDescriptor*) key; + + unsigned int outputNo = (unsigned int) PyInt_AS_LONG(pyOutput); + + //Get output list: but we don't need it + //Plugin::FeatureList features = pd->output[outputNo]; + + size_t outLength = pd->output[outputNo].size(); + + //New PyList for the featurelist + PyObject *pyFeatureList = PyList_New(outLength); + + for (size_t i = 0; i < outLength; ++i) { + // Test: + /* + XxoObject *pyFeature = PyObject_New(XxoObject, &Xxo_Type); + if (pyFeature == NULL) break; //return NULL; + + pyFeature->x_attr = NULL; + pyFeature->feature = &pd->output[outputNo][i]; + + PyList_SET_ITEM(pyFeatureList,i,(PyObject*)pyFeature); + */ + } + + Py_INCREF(pyFeatureList); + return pyFeatureList; + +// EXPLAIN WHAT WE NEED TO DO HERE: +// We have the block output in pd->output +// FeatureSet[output] -> [Feature[x]] -> Feature.hasTimestamp = v +// Vamp::Plugin::FeatureSet output; = pd->output +// typedef std::vector<Feature> FeatureList; +// typedef std::map<int, FeatureList> FeatureSet; // key is output no + + // THIS IS FOR OUTPUT id LOOKUP LATER + // Plugin::OutputList outputs = plugin->getOutputDescriptors(); + // + // if (outputs.size()<1) { + // string pyerr("Plugin has no output: "); pyerr += pluginKey; + // PyErr_SetString(PyExc_TypeError,pyerr.c_str()); + // return NULL; + // } + // + // //New list object + // PyObject *pyList = PyList_New(outputs.size()); + // + // for (size_t i = 0; i < outputs.size(); ++i) { + // PyObject *pyOutputId = + // PyString_FromString(outputs[i].identifier.c_str()); + // PyList_SET_ITEM(pyList,i,pyOutputId); + // } + +} + + + + +/* List of functions defined in this module */ +//module methods table +static PyMethodDef vampyhost_methods[] = { + + {"enumeratePlugins", vampyhost_enumeratePlugins, METH_VARARGS, + xx_foo_doc}, + + {"getLibraryPath", vampyhost_getLibraryPath, METH_VARARGS, + xx_foo_doc}, + + {"getPluginCategory", vampyhost_getPluginCategory, METH_VARARGS, + xx_foo_doc}, + + {"getOutputList", vampyhost_getOutputList, METH_VARARGS, + xx_foo_doc}, + + {"loadPlugin", vampyhost_loadPlugin, METH_VARARGS, + xx_foo_doc}, + + {"process", vampyhost_process, METH_VARARGS, + xx_foo_doc}, + + {"unloadPlugin", vampyhost_unloadPlugin, METH_VARARGS, + xx_foo_doc}, + + {"initialise", vampyhost_initialise, METH_VARARGS, + xx_foo_doc}, + + {"getOutput", vampyhost_getOutput, METH_VARARGS, + xx_foo_doc}, + + /* Add RealTime Module Methods */ + + {"frame2RealTime", (PyCFunction)RealTime_frame2RealTime, METH_VARARGS, + PyDoc_STR("frame2RealTime((int64)frame, (uint32)sampleRate ) -> returns new RealTime object from frame.")}, + + {"realtime", (PyCFunction)RealTime_new, METH_VARARGS, + PyDoc_STR("realtime() -> returns new RealTime object")}, + + {NULL, NULL} /* sentinel */ +}; + +//Documentation for our new module +PyDoc_STRVAR(module_doc, "This is a template module just for instruction."); + +/* Initialization function for the module (*must* be called initxx) */ + +//module initialization (includes extern C {...} as necessary) +PyMODINIT_FUNC +initvampyhost(void) +{ + PyObject *m; + + /* Finalize the type object including setting type of the new type + * object; doing it here is required for portability to Windows + * without requiring C++. */ + + if (PyType_Ready(&RealTime_Type) < 0) + return; +// PyModule_AddObject(m, "Real_Time", (PyObject *)&RealTime_Type); + + /* Create the module and add the functions */ + m = Py_InitModule3("vampyhost", vampyhost_methods, module_doc); + if (m == NULL) return; + + // PyModule_AddObject(m, "realtime", (PyObject *)&RealTime_Type); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vampyhost.h Mon Oct 22 16:10:46 2012 +0100 @@ -0,0 +1,43 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#ifndef _VAMPYHOST_H_ +#define _VAMPYHOST_H_ + +#include "vamp-sdk/Plugin.h" +#include <string> + +// 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; + std::string identifier; + bool isInitialised; + float inputSampleRate; + 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; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vampyhost_test.py Mon Oct 22 16:10:46 2012 +0100 @@ -0,0 +1,118 @@ + +import sys +sys.path.append('/Users/Shared/Development/python-extensions') +sys.path.append('/Users/Shared/Development/vampy-host-experiments/') +#from melscale import melscale +#from melscale import initialize +import wave +from wave import * +from pylab import * +# from melscale import * +from numpy import * +from pylab import * +from time import * + +from vampyhost import * +import vampyhost +import vampyhost as vh +#import pyRealTime +#from pyRealTime import * + + +#deal with an audio file +wavfile='/Users/Shared/multitrack (small)/mix.wav' + +wavobj = wave.open(wavfile,'r') +samplerate = wavobj.getframerate() +print "samplerate: ",samplerate +print "number of samples (frames): ",wavobj.getnframes() #total number of samples 4647744 +channels = wavobj.getnchannels(); +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() + + + +rt=realtime(4,70) + +#test RealTime Object +for i in [0,1,2] : + if (i==0) : rtl=[] + rtl.append(realtime()) + print ">>>>>RealTime's method: ", rtl[i].values() + + +class feature_example(): + def __init__(self): + self.hasTimestamp + self.timestamp + self.values + self.label + +pluginlist = vh.enumeratePlugins() +for i,n in enumerate(pluginlist) : print i,":",n +pluginKey=pluginlist[12]; + +retval = vh.getLibraryPath(pluginKey) +print pluginKey +print retval + +print vh.getPluginCategory(pluginKey) +print vh.getOutputList(pluginKey) +handle = vh.loadPlugin(pluginKey,samplerate); +print "\n\nPlugin handle: ",handle + +print "Output list of: ",pluginKey,"\n",vh.getOutputList(handle) + +#initialise: pluginhandle, channels, stepSize, blockSize +vh.initialise(handle,1,1024,1024) + +rt=frame2RealTime(100000,22050) +print type(rt) + +out=vh.process(handle,audio,rt) +output = vh.getOutput(handle,1); + +print type(output) +print output +#print output[1].label + +print "_______________OUTPUT TYPE_________:",type(out) +in_audio = frombuffer(audio,int16,-1,0) +out_audio = frombuffer(out,float32,-1,0) +subplot(211) +plot(in_audio) +subplot(212) +plot(out_audio) + +show() +#do some processing here + +#buffer is a multichannel frame or a numpy array containing samples +#buffer = vh.frame(audiodata,stepSize,blockSize) + +#output = vh.process(handle,buffer) + +#output is a list of list of features + +vh.unloadPlugin(handle); +vh.unloadPlugin(handle); # test if it chrashes... + +print vh.getOutputList(handle) + +#cases: +#buffer = blockSize : evaluate +#buffer > blockSize : enframe and zeropad +#return: +#oneSamplePerStep, FixedSamplerate : can return numpy array +#variableSamplerate : list of featres only + +#print dir(vampyhost) \ No newline at end of file