Mercurial > hg > vampy-host
view vampyhost.cpp @ 18:4b9adb4b532f
Return values from process
author | Chris Cannam |
---|---|
date | Tue, 25 Nov 2014 09:58:25 +0000 |
parents | 3893b76daf80 |
children | 8e5cefa70c99 |
line wrap: on
line source
/* -*- 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> // define a unique API pointer #define PY_ARRAY_UNIQUE_SYMBOL VAMPY_ARRAY_API #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include "numpy/arrayobject.h" #include <vampyhost.h> #define HAVE_NUMPY 1 // Required //includes for vamp host #include "vamp-hostsdk/Plugin.h" #include "vamp-hostsdk/PluginHostAdapter.h" #include "vamp-hostsdk/PluginChannelAdapter.h" #include "vamp-hostsdk/PluginInputDomainAdapter.h" #include "vamp-hostsdk/PluginLoader.h" #include "PyTypeConversions.h" #include "PyRealTime.h" #include <iostream> #include <fstream> #include <set> #include <sndfile.h> #include <cstring> #include <cstdlib> #include <string> #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" // structure for holding plugin instance data struct PyPluginData { PyPluginData(string k, Plugin *p, float rate) : key(k), plugin(p), inputSampleRate(rate), isInitialised(false), channels(0), blockSize(0), stepSize(0) { } string key; Plugin *plugin; float inputSampleRate; bool isInitialised; size_t channels; size_t blockSize; size_t stepSize; }; /* MODULE HELPER FUNCTIONS */ PyDoc_STRVAR(xx_foo_doc, "Some description"); //!!! //!!! nb "The CObject API is deprecated" https://docs.python.org/2/c-api/cobject.html PyPluginData * getPluginData(PyObject *pyPluginHandle) { PyPluginData *pd = 0; if (PyCObject_Check(pyPluginHandle)) { pd = (PyPluginData *)PyCObject_AsVoidPtr(pyPluginHandle); } if (!pd || !pd->plugin) { PyErr_SetString(PyExc_AttributeError, "Invalid or already deleted plugin handle."); return 0; } else { return pd; } } static PyObject * vampyhost_enumeratePlugins(PyObject *self, PyObject *args) { PluginLoader *loader = PluginLoader::getInstance(); vector<PluginLoader::PluginKey> plugins = loader->listPlugins(); PyTypeConversions conv; return conv.PyValue_From_StringVector(plugins); } static PyObject * vampyhost_getPluginPath(PyObject *self, PyObject *args) { vector<string> path = PluginHostAdapter::getPluginPath(); PyTypeConversions conv; return conv.PyValue_From_StringVector(path); } static string toPluginKey(PyObject *pyPluginKey) { //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, "Plugin key must be of the form library:identifier"); return ""; } return pluginKey; } static PyObject * vampyhost_getLibraryFor(PyObject *self, PyObject *args) { PyObject *pyPluginKey; if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) { PyErr_SetString(PyExc_TypeError, "getLibraryPathForPlugin() takes plugin key (string) argument"); return 0; } string pluginKey = toPluginKey(pyPluginKey); if (pluginKey == "") return 0; PluginLoader *loader = PluginLoader::getInstance(); string path = loader->getLibraryPathForPlugin(pluginKey); PyObject *pyPath = PyString_FromString(path.c_str()); return pyPath; } static PyObject * vampyhost_getPluginCategory(PyObject *self, PyObject *args) { PyObject *pyPluginKey; if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) { PyErr_SetString(PyExc_TypeError, "getPluginCategory() takes plugin key (string) argument"); return 0; } string pluginKey = toPluginKey(pyPluginKey); if (pluginKey == "") return 0; PluginLoader *loader = PluginLoader::getInstance(); PluginLoader::PluginCategoryHierarchy category = loader->getPluginCategory(pluginKey); PyTypeConversions conv; return conv.PyValue_From_StringVector(category); } static PyObject * vampyhost_getOutputList(PyObject *self, PyObject *args) { PyObject *keyOrHandle; Plugin::OutputList outputs; if (!PyArg_ParseTuple(args, "O", &keyOrHandle)) { PyErr_SetString(PyExc_TypeError, "getOutputList() takes plugin handle (object) or plugin key (string) argument"); return 0; } if (PyString_Check(keyOrHandle) ) { // we have a plugin key string pluginKey = toPluginKey(keyOrHandle); if (pluginKey == "") return 0; PluginLoader *loader = PluginLoader::getInstance(); Plugin *plugin = loader->loadPlugin (pluginKey, 48000, PluginLoader::ADAPT_ALL_SAFE); if (!plugin) { string pyerr("Failed to load plugin: "); pyerr += pluginKey; PyErr_SetString(PyExc_TypeError,pyerr.c_str()); return 0; } outputs = plugin->getOutputDescriptors(); delete plugin; } else { // we have a loaded plugin handle PyPluginData *pd = getPluginData(keyOrHandle); if (!pd) return 0; outputs = pd->plugin->getOutputDescriptors(); } 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); } return pyList; } static PyObject * vampyhost_loadPlugin(PyObject *self, PyObject *args) { PyObject *pyPluginKey; float inputSampleRate; if (!PyArg_ParseTuple(args, "Sf", &pyPluginKey, &inputSampleRate)) { PyErr_SetString(PyExc_TypeError, "loadPlugin() takes plugin key (string) and sample rate (number) arguments"); return 0; } string pluginKey = toPluginKey(pyPluginKey); if (pluginKey == "") return 0; PluginLoader *loader = PluginLoader::getInstance(); Plugin *plugin = loader->loadPlugin(pluginKey, inputSampleRate, PluginLoader::ADAPT_ALL_SAFE); if (!plugin) { string pyerr("Failed to load plugin: "); pyerr += pluginKey; PyErr_SetString(PyExc_TypeError,pyerr.c_str()); return 0; } PyPluginData *pd = new PyPluginData(pluginKey, plugin, inputSampleRate); return PyCObject_FromVoidPtr(pd, 0); } static PyObject * vampyhost_unloadPlugin(PyObject *self, PyObject *args) { PyObject *pyPluginHandle; if (!PyArg_ParseTuple(args, "O", &pyPluginHandle)) { PyErr_SetString(PyExc_TypeError, "unloadPlugin() takes plugin handle (object) argument"); return 0; } PyPluginData *pd = getPluginData(pyPluginHandle); if (!pd) return 0; /* Prevent repeated calls from causing segfault since it will fail * type checking the 2nd time: */ PyCObject_SetVoidPtr(pyPluginHandle, 0); delete pd->plugin; delete pd; return pyPluginHandle; } static PyObject * vampyhost_initialise(PyObject *self, PyObject *args) { PyObject *pyPluginHandle; size_t channels, blockSize, stepSize; if (!PyArg_ParseTuple (args, "Onnn", &pyPluginHandle, (size_t) &channels, (size_t) &stepSize, (size_t) &blockSize)) { PyErr_SetString(PyExc_TypeError, "initialise() takes plugin handle (object), channel count, step size, and block size arguments"); return 0; } PyPluginData *pd = getPluginData(pyPluginHandle); if (!pd) return 0; pd->channels = channels; pd->stepSize = stepSize; pd->blockSize = blockSize; if (!pd->plugin->initialise(channels, stepSize, blockSize)) { cerr << "Failed to initialise native plugin adapter with channels = " << channels << ", stepSize = " << stepSize << ", blockSize = " << blockSize << " and ADAPT_ALL_SAFE set" << endl; PyErr_SetString(PyExc_TypeError, "Plugin initialization failed"); return 0; } pd->isInitialised = true; return Py_True; } static PyObject * vampyhost_reset(PyObject *self, PyObject *args) { PyObject *pyPluginHandle; if (!PyArg_ParseTuple (args, "O", &pyPluginHandle)) { PyErr_SetString(PyExc_TypeError, "initialise() takes plugin handle (object) argument"); return 0; } PyPluginData *pd = getPluginData(pyPluginHandle); if (!pd) return 0; if (!pd->isInitialised) { PyErr_SetString(PyExc_StandardError, "Plugin has not been initialised"); return 0; } pd->plugin->reset(); return Py_True; } 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, "process() takes plugin handle (object), buffer (2D array of channels * samples floats) and timestamp (RealTime) arguments"); return 0; } if (!PyRealTime_Check(pyRealTime)) { PyErr_SetString(PyExc_TypeError,"Valid timestamp required."); return 0; } if (!PyList_Check(pyBuffer)) { PyErr_SetString(PyExc_TypeError, "List of NumPy Array required for process input."); return 0; } PyPluginData *pd = getPluginData(pyPluginHandle); if (!pd) return 0; if (!pd->isInitialised) { PyErr_SetString(PyExc_StandardError, "Plugin has not been initialised."); return 0; } int channels = pd->channels; if (PyList_GET_SIZE(pyBuffer) != channels) { cerr << "Wrong number of channels: got " << PyList_GET_SIZE(pyBuffer) << ", expected " << channels << endl; PyErr_SetString(PyExc_TypeError, "Wrong number of channels"); return 0; } float **inbuf = new float *[channels]; PyTypeConversions typeConv; typeConv.setNumpyInstalled(true); cerr << "here!" << endl; vector<vector<float> > data; for (int c = 0; c < channels; ++c) { PyObject *cbuf = PyList_GET_ITEM(pyBuffer, c); data.push_back(typeConv.PyValue_To_FloatVector(cbuf)); } for (int c = 0; c < channels; ++c) { if (data[c].size() != pd->blockSize) { cerr << "Wrong number of samples on channel " << c << ": expected " << pd->blockSize << " (plugin's block size), got " << data[c].size() << endl; PyErr_SetString(PyExc_TypeError, "Wrong number of samples"); return 0; } inbuf[c] = &data[c][0]; } cerr << "no, here!" << endl; RealTime timeStamp = *PyRealTime_AsRealTime(pyRealTime); cerr << "no no, here!" << endl; Plugin::FeatureSet fs = pd->plugin->process(inbuf, timeStamp); delete[] inbuf; cerr << "no no no, here!" << endl; PyTypeConversions conv; conv.setNumpyInstalled(true); PyObject *pyFs = PyDict_New(); for (Plugin::FeatureSet::const_iterator fsi = fs.begin(); fsi != fs.end(); ++fsi) { int fno = fsi->first; const Plugin::FeatureList &fl = fsi->second; if (!fl.empty()) { PyObject *pyFl = PyList_New(fl.size()); for (int fli = 0; fli < (int)fl.size(); ++fli) { const Plugin::Feature &f = fl[fli]; PyObject *pyF = PyDict_New(); if (f.hasTimestamp) { PyDict_SetItemString (pyF, "timestamp", PyRealTime_FromRealTime(f.timestamp)); } if (f.hasDuration) { PyDict_SetItemString (pyF, "duration", PyRealTime_FromRealTime(f.duration)); } PyDict_SetItemString (pyF, "label", PyString_FromString(f.label.c_str())); if (!f.values.empty()) { PyDict_SetItemString (pyF, "values", conv.FloatVector_To_PyArray(f.values)); } PyList_SET_ITEM(pyFl, fli, pyF); } PyObject *pyN = PyInt_FromLong(fno); PyDict_SetItem(pyFs, pyN, pyFl); } } cerr << "no you fool, here!" << endl; return pyFs; } #ifdef NOPE 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 0; } PyPluginData *pd = getPluginData(pyPluginHandle); if (!pd) return 0; 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 == 0) break; //return 0; pyFeature->x_attr = 0; 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 vector<Feature> FeatureList; // typedef 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 0; // } // // //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); // } } #endif // module methods table static PyMethodDef vampyhost_methods[] = { {"listPlugins", vampyhost_enumeratePlugins, METH_NOARGS, xx_foo_doc}, {"getPluginPath", vampyhost_getPluginPath, METH_NOARGS, xx_foo_doc}, {"getCategoryOf", vampyhost_getPluginCategory, METH_VARARGS, xx_foo_doc}, {"getLibraryFor", vampyhost_getLibraryFor, METH_VARARGS, xx_foo_doc}, {"getOutputsOf", vampyhost_getOutputList, METH_VARARGS, xx_foo_doc}, {"loadPlugin", vampyhost_loadPlugin, METH_VARARGS, xx_foo_doc}, {"initialise", vampyhost_initialise, METH_VARARGS, xx_foo_doc}, {"reset", vampyhost_reset, METH_VARARGS, xx_foo_doc}, {"process", vampyhost_process, METH_VARARGS, xx_foo_doc}, {"unloadPlugin", vampyhost_unloadPlugin, METH_VARARGS, xx_foo_doc}, {0, 0} /* 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; /* Create the module and add the functions */ m = Py_InitModule3("vampyhost", vampyhost_methods, module_doc); if (!m) return; import_array(); PyModule_AddObject(m, "RealTime", (PyObject *)&RealTime_Type); }