Chris@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: //include for python extension module: must be first Chris@0: #include Chris@14: Chris@14: // define a unique API pointer Chris@14: #define PY_ARRAY_UNIQUE_SYMBOL VAMPY_ARRAY_API Chris@14: #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION Chris@14: #include "numpy/arrayobject.h" Chris@14: Chris@0: #include Chris@12: Chris@12: #define HAVE_NUMPY 1 // Required Chris@12: Chris@0: //includes for vamp host Chris@1: #include "vamp-hostsdk/Plugin.h" Chris@1: #include "vamp-hostsdk/PluginHostAdapter.h" Chris@1: #include "vamp-hostsdk/PluginChannelAdapter.h" Chris@1: #include "vamp-hostsdk/PluginInputDomainAdapter.h" Chris@1: #include "vamp-hostsdk/PluginLoader.h" Chris@16: Chris@16: #include "PyTypeConversions.h" Chris@16: #include "PyRealTime.h" Chris@0: Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: Chris@0: #include Chris@0: Chris@0: using namespace std; Chris@0: using namespace Vamp; Chris@0: Chris@0: using Vamp::Plugin; Chris@0: using Vamp::PluginHostAdapter; Chris@0: using Vamp::RealTime; Chris@0: using Vamp::HostExt::PluginLoader; Chris@0: Chris@0: #define HOST_VERSION "1.1" Chris@0: Chris@16: // structure for holding plugin instance data Chris@16: struct PyPluginData Chris@16: { Chris@16: PyPluginData(string k, Plugin *p, float rate) : Chris@16: key(k), Chris@16: plugin(p), Chris@16: inputSampleRate(rate), Chris@16: isInitialised(false), Chris@16: channels(0), Chris@16: blockSize(0), Chris@16: stepSize(0) { Chris@16: } Chris@16: Chris@16: string key; Chris@16: Plugin *plugin; Chris@16: float inputSampleRate; Chris@16: bool isInitialised; Chris@16: size_t channels; Chris@16: size_t blockSize; Chris@16: size_t stepSize; Chris@16: }; Chris@0: Chris@0: /* MODULE HELPER FUNCTIONS */ Chris@2: PyDoc_STRVAR(xx_foo_doc, "Some description"); //!!! Chris@0: Chris@16: //!!! nb "The CObject API is deprecated" https://docs.python.org/2/c-api/cobject.html Chris@0: Chris@16: PyPluginData * Chris@16: getPluginData(PyObject *pyPluginHandle) Chris@16: { Chris@16: PyPluginData *pd = 0; Chris@16: if (PyCObject_Check(pyPluginHandle)) { Chris@16: pd = (PyPluginData *)PyCObject_AsVoidPtr(pyPluginHandle); Chris@16: } Chris@16: if (!pd || !pd->plugin) { Chris@16: PyErr_SetString(PyExc_AttributeError, Chris@16: "Invalid or already deleted plugin handle."); Chris@16: return 0; Chris@0: } else { Chris@16: return pd; Chris@0: } Chris@0: } Chris@0: Chris@0: static PyObject * Chris@0: vampyhost_enumeratePlugins(PyObject *self, PyObject *args) Chris@0: { Chris@0: PluginLoader *loader = PluginLoader::getInstance(); Chris@0: vector plugins = loader->listPlugins(); Chris@15: PyTypeConversions conv; Chris@15: return conv.PyValue_From_StringVector(plugins); Chris@0: } Chris@0: Chris@15: static PyObject * Chris@15: vampyhost_getPluginPath(PyObject *self, PyObject *args) Chris@15: { Chris@15: vector path = PluginHostAdapter::getPluginPath(); Chris@15: PyTypeConversions conv; Chris@15: return conv.PyValue_From_StringVector(path); Chris@15: } Chris@0: Chris@15: static string toPluginKey(PyObject *pyPluginKey) Chris@0: { Chris@0: //convert to stl string Chris@0: string pluginKey(PyString_AS_STRING(pyPluginKey)); Chris@0: Chris@0: //check pluginKey Validity Chris@0: string::size_type ki = pluginKey.find(':'); Chris@0: if (ki == string::npos) { Chris@0: PyErr_SetString(PyExc_TypeError, Chris@15: "Plugin key must be of the form library:identifier"); Chris@15: return ""; Chris@0: } Chris@0: Chris@15: return pluginKey; Chris@15: } Chris@15: Chris@15: static PyObject * Chris@15: vampyhost_getLibraryFor(PyObject *self, PyObject *args) Chris@15: { Chris@15: PyObject *pyPluginKey; Chris@15: Chris@15: if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) { Chris@15: PyErr_SetString(PyExc_TypeError, Chris@15: "getLibraryPathForPlugin() takes plugin key (string) argument"); Chris@16: return 0; } Chris@15: Chris@15: string pluginKey = toPluginKey(pyPluginKey); Chris@16: if (pluginKey == "") return 0; Chris@15: Chris@0: PluginLoader *loader = PluginLoader::getInstance(); Chris@0: string path = loader->getLibraryPathForPlugin(pluginKey); Chris@0: PyObject *pyPath = PyString_FromString(path.c_str()); Chris@0: return pyPath; Chris@0: } Chris@0: Chris@0: static PyObject * Chris@0: vampyhost_getPluginCategory(PyObject *self, PyObject *args) Chris@0: { Chris@0: PyObject *pyPluginKey; Chris@0: Chris@0: if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) { Chris@0: PyErr_SetString(PyExc_TypeError, Chris@15: "getPluginCategory() takes plugin key (string) argument"); Chris@16: return 0; } Chris@0: Chris@15: string pluginKey = toPluginKey(pyPluginKey); Chris@16: if (pluginKey == "") return 0; Chris@0: Chris@0: PluginLoader *loader = PluginLoader::getInstance(); luis@7: PluginLoader::PluginCategoryHierarchy Chris@0: category = loader->getPluginCategory(pluginKey); Chris@0: Chris@15: PyTypeConversions conv; Chris@15: return conv.PyValue_From_StringVector(category); Chris@0: } Chris@0: Chris@0: static PyObject * Chris@0: vampyhost_getOutputList(PyObject *self, PyObject *args) Chris@0: { Chris@16: PyObject *keyOrHandle; Chris@15: Plugin::OutputList outputs; Chris@0: Chris@16: if (!PyArg_ParseTuple(args, "O", &keyOrHandle)) { Chris@0: PyErr_SetString(PyExc_TypeError, Chris@15: "getOutputList() takes plugin handle (object) or plugin key (string) argument"); Chris@16: return 0; Chris@0: } Chris@0: Chris@16: if (PyString_Check(keyOrHandle) ) { Chris@0: Chris@15: // we have a plugin key Chris@0: Chris@16: string pluginKey = toPluginKey(keyOrHandle); Chris@16: if (pluginKey == "") return 0; Chris@15: Chris@15: PluginLoader *loader = PluginLoader::getInstance(); Chris@15: Chris@15: Plugin *plugin = loader->loadPlugin Chris@15: (pluginKey, 48000, PluginLoader::ADAPT_ALL_SAFE); Chris@15: if (!plugin) { Chris@15: string pyerr("Failed to load plugin: "); pyerr += pluginKey; Chris@15: PyErr_SetString(PyExc_TypeError,pyerr.c_str()); Chris@16: return 0; Chris@15: } Chris@15: Chris@15: outputs = plugin->getOutputDescriptors(); Chris@15: Chris@15: delete plugin; Chris@15: Chris@0: } else { luis@7: Chris@15: // we have a loaded plugin handle Chris@15: Chris@16: PyPluginData *pd = getPluginData(keyOrHandle); Chris@16: if (!pd) return 0; Chris@0: Chris@16: outputs = pd->plugin->getOutputDescriptors(); luis@7: } luis@7: Chris@0: PyObject *pyList = PyList_New(outputs.size()); Chris@0: Chris@0: for (size_t i = 0; i < outputs.size(); ++i) { luis@7: PyObject *pyOutputId = Chris@0: PyString_FromString(outputs[i].identifier.c_str()); Chris@15: PyList_SET_ITEM(pyList, i, pyOutputId); Chris@0: } Chris@0: Chris@0: return pyList; Chris@0: } Chris@0: Chris@0: static PyObject * Chris@0: vampyhost_loadPlugin(PyObject *self, PyObject *args) Chris@0: { Chris@0: PyObject *pyPluginKey; Chris@0: float inputSampleRate; Chris@0: luis@7: if (!PyArg_ParseTuple(args, "Sf", Chris@0: &pyPluginKey, Chris@0: &inputSampleRate)) { Chris@0: PyErr_SetString(PyExc_TypeError, Chris@15: "loadPlugin() takes plugin key (string) and sample rate (number) arguments"); Chris@16: return 0; } Chris@0: Chris@15: string pluginKey = toPluginKey(pyPluginKey); Chris@16: if (pluginKey == "") return 0; Chris@0: Chris@0: PluginLoader *loader = PluginLoader::getInstance(); luis@7: Chris@15: Plugin *plugin = loader->loadPlugin(pluginKey, inputSampleRate, Chris@15: PluginLoader::ADAPT_ALL_SAFE); luis@7: if (!plugin) { Chris@0: string pyerr("Failed to load plugin: "); pyerr += pluginKey; luis@7: PyErr_SetString(PyExc_TypeError,pyerr.c_str()); Chris@16: return 0; luis@7: } Chris@15: Chris@16: PyPluginData *pd = new PyPluginData(pluginKey, plugin, inputSampleRate); Chris@16: return PyCObject_FromVoidPtr(pd, 0); Chris@0: } Chris@0: Chris@0: static PyObject * Chris@0: vampyhost_unloadPlugin(PyObject *self, PyObject *args) Chris@0: { Chris@0: PyObject *pyPluginHandle; Chris@0: Chris@0: if (!PyArg_ParseTuple(args, "O", &pyPluginHandle)) { Chris@0: PyErr_SetString(PyExc_TypeError, Chris@16: "unloadPlugin() takes plugin handle (object) argument"); Chris@16: return 0; Chris@16: } Chris@0: Chris@16: PyPluginData *pd = getPluginData(pyPluginHandle); Chris@16: if (!pd) return 0; Chris@0: Chris@16: /* Prevent repeated calls from causing segfault since it will fail Chris@16: * type checking the 2nd time: */ Chris@16: PyCObject_SetVoidPtr(pyPluginHandle, 0); Chris@0: Chris@16: delete pd->plugin; Chris@0: delete pd; Chris@0: return pyPluginHandle; Chris@0: } Chris@0: Chris@0: static PyObject * Chris@0: vampyhost_initialise(PyObject *self, PyObject *args) Chris@0: { Chris@0: PyObject *pyPluginHandle; luis@7: size_t channels, blockSize, stepSize; Chris@0: luis@7: if (!PyArg_ParseTuple (args, "Onnn", &pyPluginHandle, luis@7: (size_t) &channels, luis@7: (size_t) &stepSize, luis@7: (size_t) &blockSize)) Chris@0: { Chris@0: PyErr_SetString(PyExc_TypeError, Chris@17: "initialise() takes plugin handle (object), channel count, step size, and block size arguments"); Chris@16: return 0; Chris@0: } Chris@0: Chris@16: PyPluginData *pd = getPluginData(pyPluginHandle); Chris@16: if (!pd) return 0; Chris@0: Chris@16: pd->channels = channels; Chris@16: pd->stepSize = stepSize; Chris@16: pd->blockSize = blockSize; Chris@0: Chris@16: if (!pd->plugin->initialise(channels, stepSize, blockSize)) { Chris@17: cerr << "Failed to initialise native plugin adapter with channels = " << channels << ", stepSize = " << stepSize << ", blockSize = " << blockSize << " and ADAPT_ALL_SAFE set" << endl; Chris@0: PyErr_SetString(PyExc_TypeError, Chris@17: "Plugin initialization failed"); Chris@16: return 0; Chris@6: } Chris@0: Chris@16: pd->isInitialised = true; luis@7: Chris@0: return Py_True; Chris@0: } Chris@0: Chris@0: static PyObject * Chris@0: vampyhost_process(PyObject *self, PyObject *args) Chris@0: { Chris@0: PyObject *pyPluginHandle; Chris@0: PyObject *pyBuffer; Chris@0: PyObject *pyRealTime; Chris@0: luis@7: if (!PyArg_ParseTuple(args, "OOO", Chris@0: &pyPluginHandle, // C object holding a pointer to a plugin and its descriptor Chris@0: &pyBuffer, // Audio data Chris@0: &pyRealTime)) { // TimeStamp Chris@0: PyErr_SetString(PyExc_TypeError, Chris@17: "process() takes plugin handle (object), buffer (2D array of channels * samples floats) and timestamp (RealTime) arguments"); Chris@16: return 0; } Chris@0: Chris@0: if (!PyRealTime_Check(pyRealTime)) { Chris@0: PyErr_SetString(PyExc_TypeError,"Valid timestamp required."); Chris@16: return 0; } Chris@0: Chris@17: if (!PyList_Check(pyBuffer)) { Chris@17: PyErr_SetString(PyExc_TypeError, "List of NumPy Array required for process input."); Chris@17: return 0; Chris@17: } Chris@17: Chris@16: PyPluginData *pd = getPluginData(pyPluginHandle); Chris@16: if (!pd) return 0; Chris@0: Chris@0: if (!pd->isInitialised) { Chris@0: PyErr_SetString(PyExc_StandardError, Chris@0: "Plugin has not been initialised."); Chris@16: return 0; Chris@16: } Chris@0: Chris@12: int channels = pd->channels; Chris@0: Chris@4: if (PyList_GET_SIZE(pyBuffer) != channels) { Chris@17: cerr << "Wrong number of channels: got " << PyList_GET_SIZE(pyBuffer) << ", expected " << channels << endl; Chris@4: PyErr_SetString(PyExc_TypeError, "Wrong number of channels"); Chris@16: return 0; Chris@4: } Chris@0: Chris@4: float **inbuf = new float *[channels]; Chris@0: Chris@12: PyTypeConversions typeConv; Chris@12: typeConv.setNumpyInstalled(true); Chris@17: Chris@17: cerr << "here!" << endl; Chris@12: Chris@12: vector > data; Chris@4: for (int c = 0; c < channels; ++c) { Chris@4: PyObject *cbuf = PyList_GET_ITEM(pyBuffer, c); Chris@17: data.push_back(typeConv.PyValue_To_FloatVector(cbuf)); Chris@12: } Chris@12: Chris@12: for (int c = 0; c < channels; ++c) { Chris@17: if (data[c].size() != pd->blockSize) { Chris@17: cerr << "Wrong number of samples on channel " << c << ": expected " << pd->blockSize << " (plugin's block size), got " << data[c].size() << endl; Chris@17: PyErr_SetString(PyExc_TypeError, "Wrong number of samples"); Chris@17: return 0; Chris@17: } Chris@12: inbuf[c] = &data[c][0]; Chris@4: } Chris@0: Chris@17: cerr << "no, here!" << endl; Chris@17: Chris@12: RealTime timeStamp = *PyRealTime_AsRealTime(pyRealTime); Chris@0: Chris@17: // Call process and store the output Chris@17: (void) pd->plugin->process(inbuf, timeStamp); //!!! return the output! Chris@0: Chris@0: /* TODO: DO SOMETHONG WITH THE FEATURE SET HERE */ Chris@0: /// convert to appropriate python objects, reuse types and conversion utilities from Vampy ... Chris@0: Chris@4: delete[] inbuf; Chris@0: Chris@16: return 0; //!!! Need to return actual features! Chris@0: Chris@0: } Chris@0: Chris@17: #ifdef NOPE Chris@0: static PyObject * Chris@0: vampyhost_getOutput(PyObject *self, PyObject *args) { Chris@0: Chris@0: PyObject *pyPluginHandle; Chris@0: // PyObject *pyBuffer; Chris@0: // PyObject *pyRealTime; Chris@0: PyObject *pyOutput; Chris@0: luis@7: if (!PyArg_ParseTuple(args, "OO", Chris@0: &pyPluginHandle, // C object holding a pointer to a plugin and its descriptor Chris@0: &pyOutput)) { // Output reference Chris@0: PyErr_SetString(PyExc_TypeError, Chris@0: "Required: plugin handle, buffer, timestmap."); Chris@16: return 0; } Chris@0: Chris@16: PyPluginData *pd = getPluginData(pyPluginHandle); Chris@16: if (!pd) return 0; Chris@0: Chris@0: unsigned int outputNo = (unsigned int) PyInt_AS_LONG(pyOutput); Chris@0: Chris@0: //Get output list: but we don't need it Chris@0: //Plugin::FeatureList features = pd->output[outputNo]; Chris@0: Chris@0: size_t outLength = pd->output[outputNo].size(); Chris@0: Chris@0: //New PyList for the featurelist Chris@0: PyObject *pyFeatureList = PyList_New(outLength); Chris@0: Chris@0: for (size_t i = 0; i < outLength; ++i) { Chris@0: // Test: Chris@0: /* luis@7: XxoObject *pyFeature = PyObject_New(XxoObject, &Xxo_Type); Chris@16: if (pyFeature == 0) break; //return 0; Chris@0: Chris@16: pyFeature->x_attr = 0; Chris@0: pyFeature->feature = &pd->output[outputNo][i]; Chris@0: luis@7: PyList_SET_ITEM(pyFeatureList,i,(PyObject*)pyFeature); Chris@0: */ Chris@0: } Chris@0: Chris@0: Py_INCREF(pyFeatureList); Chris@0: return pyFeatureList; Chris@0: Chris@0: // EXPLAIN WHAT WE NEED TO DO HERE: Chris@0: // We have the block output in pd->output luis@7: // FeatureSet[output] -> [Feature[x]] -> Feature.hasTimestamp = v luis@7: // Vamp::Plugin::FeatureSet output; = pd->output Chris@17: // typedef vector FeatureList; Chris@17: // typedef map FeatureSet; // key is output no Chris@0: luis@7: // THIS IS FOR OUTPUT id LOOKUP LATER Chris@0: // Plugin::OutputList outputs = plugin->getOutputDescriptors(); luis@7: // Chris@0: // if (outputs.size()<1) { Chris@0: // string pyerr("Plugin has no output: "); pyerr += pluginKey; luis@7: // PyErr_SetString(PyExc_TypeError,pyerr.c_str()); Chris@16: // return 0; Chris@0: // } luis@7: // Chris@0: // //New list object Chris@0: // PyObject *pyList = PyList_New(outputs.size()); luis@7: // Chris@0: // for (size_t i = 0; i < outputs.size(); ++i) { luis@7: // PyObject *pyOutputId = Chris@0: // PyString_FromString(outputs[i].identifier.c_str()); luis@7: // PyList_SET_ITEM(pyList,i,pyOutputId); Chris@0: // } luis@7: Chris@0: } Chris@17: #endif Chris@0: Chris@0: Chris@0: Chris@0: /* List of functions defined in this module */ Chris@0: //module methods table Chris@0: static PyMethodDef vampyhost_methods[] = { Chris@0: Chris@15: {"enumeratePlugins", vampyhost_enumeratePlugins, METH_NOARGS, Chris@0: xx_foo_doc}, Chris@0: Chris@15: {"getPluginPath", vampyhost_getPluginPath, METH_NOARGS, Chris@15: xx_foo_doc}, Chris@15: Chris@15: {"getLibraryForPlugin", vampyhost_getLibraryFor, METH_VARARGS, Chris@0: xx_foo_doc}, Chris@0: Chris@0: {"getPluginCategory", vampyhost_getPluginCategory, METH_VARARGS, Chris@0: xx_foo_doc}, Chris@0: Chris@0: {"getOutputList", vampyhost_getOutputList, METH_VARARGS, Chris@0: xx_foo_doc}, Chris@0: Chris@0: {"loadPlugin", vampyhost_loadPlugin, METH_VARARGS, Chris@0: xx_foo_doc}, Chris@0: Chris@0: {"process", vampyhost_process, METH_VARARGS, Chris@0: xx_foo_doc}, Chris@0: Chris@0: {"unloadPlugin", vampyhost_unloadPlugin, METH_VARARGS, Chris@0: xx_foo_doc}, Chris@0: Chris@0: {"initialise", vampyhost_initialise, METH_VARARGS, Chris@0: xx_foo_doc}, Chris@0: Chris@17: // {"getOutput", vampyhost_getOutput, METH_VARARGS, Chris@17: // xx_foo_doc}, Chris@0: Chris@0: /* Add RealTime Module Methods */ Chris@12: /* Chris@0: {"frame2RealTime", (PyCFunction)RealTime_frame2RealTime, METH_VARARGS, Chris@0: PyDoc_STR("frame2RealTime((int64)frame, (uint32)sampleRate ) -> returns new RealTime object from frame.")}, Chris@0: Chris@0: {"realtime", (PyCFunction)RealTime_new, METH_VARARGS, Chris@0: PyDoc_STR("realtime() -> returns new RealTime object")}, Chris@12: */ Chris@16: {0, 0} /* sentinel */ Chris@0: }; Chris@0: Chris@0: //Documentation for our new module Chris@0: PyDoc_STRVAR(module_doc, "This is a template module just for instruction."); Chris@0: Chris@14: Chris@14: Chris@0: /* Initialization function for the module (*must* be called initxx) */ Chris@0: Chris@0: //module initialization (includes extern C {...} as necessary) Chris@0: PyMODINIT_FUNC Chris@0: initvampyhost(void) Chris@0: { Chris@0: PyObject *m; Chris@0: Chris@0: /* Finalize the type object including setting type of the new type luis@7: * object; doing it here is required for portability to Windows Chris@0: * without requiring C++. */ Chris@0: Chris@0: if (PyType_Ready(&RealTime_Type) < 0) Chris@0: return; Chris@0: Chris@0: /* Create the module and add the functions */ Chris@0: m = Py_InitModule3("vampyhost", vampyhost_methods, module_doc); Chris@16: if (!m) return; Chris@0: Chris@14: import_array(); Chris@14: Chris@17: PyModule_AddObject(m, "RealTime", (PyObject *)&RealTime_Type); Chris@0: }