Mercurial > hg > vampy-host
diff vampyhost.cpp @ 0:68f3f32565b4
Import the early draft version
author | Chris Cannam |
---|---|
date | Mon, 22 Oct 2012 16:10:46 +0100 |
parents | |
children | cb0d3af1be4d |
line wrap: on
line diff
--- /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); + +}