Mercurial > hg > vampy-host
diff PyPluginObject.cpp @ 31:f565f4b5cbaa
Pull out plugin object implementation into separate source file
author | Chris Cannam |
---|---|
date | Wed, 26 Nov 2014 11:12:00 +0000 |
parents | vampyhost.cpp@7e7f2f7d9542 |
children | d5aba4c3c229 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyPluginObject.cpp Wed Nov 26 11:12:00 2014 +0000 @@ -0,0 +1,409 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + VampyHost + + Use Vamp audio analysis plugins in Python + + Gyorgy Fazekas and Chris Cannam + Centre for Digital Music, Queen Mary, University of London + Copyright 2008-2014 Queen Mary, University of London + + 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 the authors + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in this Software without prior written + authorization. +*/ + +#include "PyPluginObject.h" + +// define a unique API pointer +#define PY_ARRAY_UNIQUE_SYMBOL VAMPYHOST_ARRAY_API +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#define NO_IMPORT_ARRAY +#include "numpy/arrayobject.h" + +#include "VectorConversion.h" +#include "PyRealTime.h" + +#include <string> +#include <vector> + +using namespace std; +using namespace Vamp; + +static void +PyPluginObject_dealloc(PyPluginObject *self) +{ + cerr << "PyPluginObject_dealloc" << endl; + delete self->plugin; + PyObject_Del(self); +} + +PyDoc_STRVAR(xx_foo_doc, "Some description"); //!!! + +//!!! todo: conv errors + +static +PyPluginObject * +getPluginObject(PyObject *pyPluginHandle) +{ + cerr << "getPluginObject" << endl; + + PyPluginObject *pd = 0; + if (PyPlugin_Check(pyPluginHandle)) { + pd = (PyPluginObject *)pyPluginHandle; + } + if (!pd || !pd->plugin) { + PyErr_SetString(PyExc_AttributeError, + "Invalid or already deleted plugin handle."); + return 0; + } else { + return pd; + } +} + +PyObject * +PyPluginObject_From_Plugin(Plugin *plugin) +{ + PyPluginObject *pd = + (PyPluginObject *)PyType_GenericAlloc(&Plugin_Type, 0); + pd->plugin = plugin; + pd->isInitialised = false; + pd->channels = 0; + pd->blockSize = 0; + pd->stepSize = 0; + return (PyObject *)pd; +} + +static PyObject * +vampyhost_initialise(PyObject *self, PyObject *args) +{ + cerr << "vampyhost_initialise" << endl; + + size_t channels, blockSize, stepSize; + + if (!PyArg_ParseTuple (args, "nnn", + (size_t) &channels, + (size_t) &stepSize, + (size_t) &blockSize)) { + PyErr_SetString(PyExc_TypeError, + "initialise() takes channel count, step size, and block size arguments"); + return 0; + } + + PyPluginObject *pd = getPluginObject(self); + 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 *) +{ + cerr << "vampyhost_reset" << endl; + + PyPluginObject *pd = getPluginObject(self); + 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_getParameter(PyObject *self, PyObject *args) +{ + cerr << "vampyhost_getParameter" << endl; + + PyObject *pyParam; + + if (!PyArg_ParseTuple(args, "S", &pyParam)) { + PyErr_SetString(PyExc_TypeError, + "getParameter() takes parameter id (string) argument"); + return 0; } + + PyPluginObject *pd = getPluginObject(self); + if (!pd) return 0; + + float value = pd->plugin->getParameter(PyString_AS_STRING(pyParam)); + return PyFloat_FromDouble(double(value)); +} + +static PyObject * +vampyhost_setParameter(PyObject *self, PyObject *args) +{ + cerr << "vampyhost_setParameter" << endl; + + PyObject *pyParam; + float value; + + if (!PyArg_ParseTuple(args, "Sf", &pyParam, &value)) { + PyErr_SetString(PyExc_TypeError, + "setParameter() takes parameter id (string), and value (float) arguments"); + return 0; } + + PyPluginObject *pd = getPluginObject(self); + if (!pd) return 0; + + pd->plugin->setParameter(PyString_AS_STRING(pyParam), value); + return Py_True; +} + +static PyObject * +vampyhost_process(PyObject *self, PyObject *args) +{ + cerr << "vampyhost_process" << endl; + + PyObject *pyBuffer; + PyObject *pyRealTime; + + if (!PyArg_ParseTuple(args, "OO", + &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; + } + + PyPluginObject *pd = getPluginObject(self); + 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]; + + VectorConversion typeConv; + + 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; + + VectorConversion conv; + + 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.PyArray_From_FloatVector(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; +} + +static PyObject * +vampyhost_unload(PyObject *self, PyObject *) +{ + cerr << "vampyhost_unloadPlugin" << endl; + + PyPluginObject *pd = getPluginObject(self); + if (!pd) return 0; + + delete pd->plugin; + pd->plugin = 0; // This is checked by getPluginObject, so we + // attempt to avoid repeated calls from blowing up + + return Py_True; +} + +static PyMethodDef PyPluginObject_methods[] = +{ + {"getParameter", vampyhost_getParameter, METH_VARARGS, + xx_foo_doc}, //!!! fix all these! + + {"setParameter", vampyhost_setParameter, METH_VARARGS, + xx_foo_doc}, + + {"initialise", vampyhost_initialise, METH_VARARGS, + xx_foo_doc}, + + {"reset", vampyhost_reset, METH_NOARGS, + xx_foo_doc}, + + {"process", vampyhost_process, METH_VARARGS, + xx_foo_doc}, + + {"unload", vampyhost_unload, METH_NOARGS, + xx_foo_doc}, + + {0, 0} +}; + +static int +PyPluginObject_setattr(PyPluginObject *self, char *name, PyObject *value) +{ + return -1; +} + +static PyObject * +PyPluginObject_getattr(PyPluginObject *self, char *name) +{ + return Py_FindMethod(PyPluginObject_methods, (PyObject *)self, name); +} + +/* Doc:: 10.3 Type Objects */ /* static */ +PyTypeObject Plugin_Type = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "vampyhost.Plugin", /*tp_name*/ + sizeof(PyPluginObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)PyPluginObject_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)PyPluginObject_getattr, /*tp_getattr*/ + (setattrfunc)PyPluginObject_setattr, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Plugin Object", /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + PyPluginObject_methods, /*tp_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*/ + PyType_GenericAlloc, /*tp_alloc*/ + 0, /*tp_new*/ + PyObject_Del, /*tp_free*/ + 0, /*tp_is_gc*/ +}; +