Mercurial > hg > vampy-host
changeset 52:b56513f872a5
Move files into subdirs
author | Chris Cannam |
---|---|
date | Wed, 14 Jan 2015 08:30:47 +0000 |
parents | a78b14c41c74 |
children | 808cc721b313 |
files | .hgignore FloatConversion.h Makefile.inc PyPluginObject.cpp PyPluginObject.h PyRealTime.cpp PyRealTime.h VectorConversion.cpp VectorConversion.h native/FloatConversion.h native/PyPluginObject.cpp native/PyPluginObject.h native/PyRealTime.cpp native/PyRealTime.h native/VectorConversion.cpp native/VectorConversion.h native/vampyhost-junk.cpp native/vampyhost.cpp test/test_metadata.py test/test_plugin_metadata.py test/test_process.py test_metadata.py test_plugin_metadata.py test_process.py vampyhost-junk.cpp vampyhost.cpp |
diffstat | 26 files changed, 2431 insertions(+), 2424 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Tue Jan 13 12:16:38 2015 +0000 +++ b/.hgignore Wed Jan 14 08:30:47 2015 +0000 @@ -5,3 +5,5 @@ *.dll *.dylib *.pyc +*.bak +*.orig
--- a/FloatConversion.h Tue Jan 13 12:16:38 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -/* -*- 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. -*/ - -#ifndef VAMPYHOST_FLOAT_CONVERSION_H -#define VAMPYHOST_FLOAT_CONVERSION_H - -class FloatConversion -{ -public: - static bool check(PyObject *pyValue) { - if (pyValue && PyFloat_Check(pyValue)) { - return true; - } - if (pyValue && PyLong_Check(pyValue)) { - return true; - } - if (pyValue && PyInt_Check(pyValue)) { - return true; - } - return false; - } - - static float convert(PyObject* pyValue) { - - if (pyValue && PyFloat_Check(pyValue)) { - return (float) PyFloat_AS_DOUBLE(pyValue); - } - - if (pyValue && PyLong_Check(pyValue)) { - return (float) PyLong_AsDouble(pyValue); - } - - if (pyValue && PyInt_Check(pyValue)) { - return (float) PyInt_AsLong(pyValue); - } - - return 0.0; - } -}; - -#endif -
--- a/Makefile.inc Tue Jan 13 12:16:38 2015 +0000 +++ b/Makefile.inc Wed Jan 14 08:30:47 2015 +0000 @@ -4,8 +4,10 @@ CXX ?= g++ CC ?= gcc -HEADERS := PyPluginObject.h PyRealTime.h FloatConversion.h VectorConversion.h -SOURCES := PyPluginObject.cpp PyRealTime.cpp VectorConversion.cpp vampyhost.cpp +SRC_DIR := native + +HEADERS := $(SRC_DIR)/PyPluginObject.h $(SRC_DIR)/PyRealTime.h $(SRC_DIR)/FloatConversion.h $(SRC_DIR)/VectorConversion.h +SOURCES := $(SRC_DIR)/PyPluginObject.cpp $(SRC_DIR)/PyRealTime.cpp $(SRC_DIR)/VectorConversion.cpp $(SRC_DIR)/vampyhost.cpp OBJECTS := $(SOURCES:.cpp=.o) OBJECTS := $(OBJECTS:.c=.o) @@ -24,6 +26,9 @@ # DO NOT DELETE -PyRealTime.o: PyRealTime.h -vampyhost.o: PyRealTime.h VectorConversion.h -VectorConversion.o: VectorConversion.h +native/PyPluginObject.o: native/PyPluginObject.h native/FloatConversion.h +native/PyPluginObject.o: native/VectorConversion.h native/PyRealTime.h +native/PyRealTime.o: native/PyRealTime.h +native/VectorConversion.o: native/FloatConversion.h native/VectorConversion.h +native/vampyhost.o: native/PyRealTime.h native/PyPluginObject.h +native/vampyhost.o: native/VectorConversion.h
--- a/PyPluginObject.cpp Tue Jan 13 12:16:38 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,725 +0,0 @@ -/* -*- 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 "structmember.h" - -#include "FloatConversion.h" -#include "VectorConversion.h" -#include "PyRealTime.h" - -#include <string> -#include <vector> -#include <cstddef> -#include <set> - -using namespace std; -using namespace Vamp; - -static -PyPluginObject * -getPluginObject(PyObject *pyPluginHandle) -{ - 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; - } -} - -static -PyObject * -pystr(const string &s) -{ - return PyString_FromString(s.c_str()); -} - -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; - - PyObject *infodict = PyDict_New(); - PyDict_SetItemString - (infodict, "apiVersion", PyInt_FromLong(plugin->getVampApiVersion())); - PyDict_SetItemString - (infodict, "pluginVersion", PyInt_FromLong(plugin->getPluginVersion())); - PyDict_SetItemString - (infodict, "identifier", pystr(plugin->getIdentifier())); - PyDict_SetItemString - (infodict, "name", pystr(plugin->getName())); - PyDict_SetItemString - (infodict, "description", pystr(plugin->getDescription())); - PyDict_SetItemString - (infodict, "maker", pystr(plugin->getMaker())); - PyDict_SetItemString - (infodict, "copyright", pystr(plugin->getCopyright())); - pd->info = infodict; - - pd->inputDomain = plugin->getInputDomain(); - - VectorConversion conv; - - Plugin::ParameterList pl = plugin->getParameterDescriptors(); - PyObject *params = PyList_New(pl.size()); - - for (int i = 0; i < (int)pl.size(); ++i) { - PyObject *paramdict = PyDict_New(); - PyDict_SetItemString - (paramdict, "identifier", pystr(pl[i].identifier)); - PyDict_SetItemString - (paramdict, "name", pystr(pl[i].name)); - PyDict_SetItemString - (paramdict, "description", pystr(pl[i].description)); - PyDict_SetItemString - (paramdict, "unit", pystr(pl[i].unit)); - PyDict_SetItemString - (paramdict, "minValue", PyFloat_FromDouble(pl[i].minValue)); - PyDict_SetItemString - (paramdict, "maxValue", PyFloat_FromDouble(pl[i].maxValue)); - PyDict_SetItemString - (paramdict, "defaultValue", PyFloat_FromDouble(pl[i].defaultValue)); - if (pl[i].isQuantized) { - PyDict_SetItemString - (paramdict, "isQuantized", Py_True); - PyDict_SetItemString - (paramdict, "quantizeStep", PyFloat_FromDouble(pl[i].quantizeStep)); - if (!pl[i].valueNames.empty()) { - PyDict_SetItemString - (paramdict, "valueNames", conv.PyValue_From_StringVector(pl[i].valueNames)); - } - } else { - PyDict_SetItemString - (paramdict, "isQuantized", Py_False); - } - - PyList_SET_ITEM(params, i, paramdict); - } - - pd->parameters = params; - - Plugin::ProgramList prl = plugin->getPrograms(); - PyObject *progs = PyList_New(prl.size()); - - for (int i = 0; i < (int)prl.size(); ++i) { - PyList_SET_ITEM(progs, i, pystr(prl[i])); - } - - pd->programs = progs; - - return (PyObject *)pd; -} - -static void -PyPluginObject_dealloc(PyPluginObject *self) -{ - delete self->plugin; - PyObject_Del(self); -} - -static PyObject * -getOutputs(PyObject *self, PyObject *args) -{ - PyPluginObject *pd = getPluginObject(self); - if (!pd) return 0; - - Plugin::OutputList ol = pd->plugin->getOutputDescriptors(); - PyObject *outputs = PyList_New(ol.size()); - - for (int i = 0; i < (int)ol.size(); ++i) { - PyObject *outdict = PyDict_New(); - PyDict_SetItemString - (outdict, "identifier", pystr(ol[i].identifier)); - PyDict_SetItemString - (outdict, "name", pystr(ol[i].name)); - PyDict_SetItemString - (outdict, "description", pystr(ol[i].description)); - PyDict_SetItemString - (outdict, "binCount", PyInt_FromLong(ol[i].binCount)); - if (ol[i].binCount > 0) { - if (ol[i].hasKnownExtents) { - PyDict_SetItemString - (outdict, "hasKnownExtents", Py_True); - PyDict_SetItemString - (outdict, "minValue", PyFloat_FromDouble(ol[i].minValue)); - PyDict_SetItemString - (outdict, "maxValue", PyFloat_FromDouble(ol[i].maxValue)); - } else { - PyDict_SetItemString - (outdict, "hasKnownExtents", Py_False); - } - if (ol[i].isQuantized) { - PyDict_SetItemString - (outdict, "isQuantized", Py_True); - PyDict_SetItemString - (outdict, "quantizeStep", PyFloat_FromDouble(ol[i].quantizeStep)); - } else { - PyDict_SetItemString - (outdict, "isQuantized", Py_False); - } - } - PyDict_SetItemString - (outdict, "sampleType", PyInt_FromLong((int)ol[i].sampleType)); - PyDict_SetItemString - (outdict, "sampleRate", PyFloat_FromDouble(ol[i].sampleRate)); - PyDict_SetItemString - (outdict, "hasDuration", ol[i].hasDuration ? Py_True : Py_False); - - PyList_SET_ITEM(outputs, i, outdict); - } - - return outputs; -} - -static PyObject * -initialise(PyObject *self, PyObject *args) -{ - 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 << endl; - PyErr_SetString(PyExc_TypeError, - "Plugin initialization failed"); - return 0; - } - - pd->isInitialised = true; - - return Py_True; -} - -static PyObject * -reset(PyObject *self, PyObject *) -{ - 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 bool -hasParameter(PyPluginObject *pd, string id) -{ - PluginBase::ParameterList pl = pd->plugin->getParameterDescriptors(); - for (int i = 0; i < (int)pl.size(); ++i) { - if (pl[i].identifier == id) { - return true; - } - } - return false; -} - -static PyObject * -getParameterValue(PyObject *self, PyObject *args) -{ - PyObject *pyParam; - - if (!PyArg_ParseTuple(args, "S", &pyParam)) { - PyErr_SetString(PyExc_TypeError, - "getParameterValue() takes parameter id (string) argument"); - return 0; } - - PyPluginObject *pd = getPluginObject(self); - if (!pd) return 0; - - string param = PyString_AS_STRING(pyParam); - - if (!hasParameter(pd, param)) { - PyErr_SetString(PyExc_StandardError, - (string("Unknown parameter id \"") + param + "\"").c_str()); - return 0; - } - - float value = pd->plugin->getParameter(param); - return PyFloat_FromDouble(double(value)); -} - -static PyObject * -setParameterValue(PyObject *self, PyObject *args) -{ - PyObject *pyParam; - float value; - - if (!PyArg_ParseTuple(args, "Sf", &pyParam, &value)) { - PyErr_SetString(PyExc_TypeError, - "setParameterValue() takes parameter id (string), and value (float) arguments"); - return 0; } - - PyPluginObject *pd = getPluginObject(self); - if (!pd) return 0; - - string param = PyString_AS_STRING(pyParam); - - if (!hasParameter(pd, param)) { - PyErr_SetString(PyExc_StandardError, - (string("Unknown parameter id \"") + param + "\"").c_str()); - return 0; - } - - pd->plugin->setParameter(param, value); - return Py_True; -} - -static PyObject * -setParameterValues(PyObject *self, PyObject *args) -{ - PyObject *dict; - - if (!PyArg_ParseTuple(args, "O", &dict)) { - PyErr_SetString(PyExc_TypeError, - "setParameterValues() takes dict argument"); - return 0; } - - if (!PyDict_Check(dict)) { - PyErr_SetString(PyExc_TypeError, - "setParameterValues() takes dict argument"); - return 0; } - - PyPluginObject *pd = getPluginObject(self); - if (!pd) return 0; - - PluginBase::ParameterList pl = pd->plugin->getParameterDescriptors(); - set<string> paramIds; - for (int i = 0; i < (int)pl.size(); ++i) { - paramIds.insert(pl[i].identifier); - } - - Py_ssize_t pos = 0; - PyObject *key, *value; - while (PyDict_Next(dict, &pos, &key, &value)) { - if (!key || !PyString_CheckExact(key)) { - PyErr_SetString(PyExc_TypeError, - "Parameter dict keys must all have string type"); - return 0; - } - if (!value || !FloatConversion::check(value)) { - PyErr_SetString(PyExc_TypeError, - "Parameter dict values must be convertible to float"); - return 0; - } - string param = PyString_AS_STRING(key); - if (paramIds.find(param) == paramIds.end()) { - PyErr_SetString(PyExc_StandardError, - (string("Unknown parameter id \"") + param + "\"").c_str()); - return 0; - } - pd->plugin->setParameter(param, FloatConversion::convert(value)); - } - - return Py_True; -} - -static PyObject * -selectProgram(PyObject *self, PyObject *args) -{ - PyObject *pyParam; - - if (!PyArg_ParseTuple(args, "S", &pyParam)) { - PyErr_SetString(PyExc_TypeError, - "selectProgram() takes parameter id (string) argument"); - return 0; } - - PyPluginObject *pd = getPluginObject(self); - if (!pd) return 0; - - pd->plugin->selectProgram(PyString_AS_STRING(pyParam)); - return Py_True; -} - -static -PyObject * -convertFeatureSet(const Plugin::FeatureSet &fs) -{ - 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", pystr(f.label)); - - 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); - } - } - - return pyFs; -} - -static vector<vector<float> > -convertPluginInput(PyObject *pyBuffer, int channels, int blockSize) -{ - vector<vector<float> > data; - - VectorConversion conv; - - if (PyArray_CheckExact(pyBuffer)) { - - data = conv.Py2DArray_To_FloatVector(pyBuffer); - - if (conv.error) { - PyErr_SetString(PyExc_TypeError, conv.getError().str().c_str()); - return data; - } - - if ((int)data.size() != channels) { -// cerr << "Wrong number of channels: got " << data.size() << ", expected " << channels << endl; - PyErr_SetString(PyExc_TypeError, "Wrong number of channels"); - return vector<vector<float> >(); - } - - } else { - - if (!PyList_Check(pyBuffer)) { - PyErr_SetString(PyExc_TypeError, "List of NumPy arrays or lists of numbers required for process input"); - return data; - } - - 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 data; - } - - for (int c = 0; c < channels; ++c) { - PyObject *cbuf = PyList_GET_ITEM(pyBuffer, c); - data.push_back(conv.PyValue_To_FloatVector(cbuf)); - if (conv.error) { - PyErr_SetString(PyExc_TypeError, conv.getError().str().c_str()); - return vector<vector<float> >(); - } - } - } - - for (int c = 0; c < channels; ++c) { - if ((int)data[c].size() != blockSize) { -// cerr << "Wrong number of samples on channel " << c << ": expected " << blockSize << " (plugin's block size), got " << data[c].size() << endl; - PyErr_SetString(PyExc_TypeError, "Wrong number of samples for process block"); - return vector<vector<float> >(); - } - } - - return data; -} - -static PyObject * -process(PyObject *self, PyObject *args) -{ - PyObject *pyBuffer; - PyObject *pyRealTime; - - if (!PyArg_ParseTuple(args, "OO", - &pyBuffer, // Audio data - &pyRealTime)) { // TimeStamp - PyErr_SetString(PyExc_TypeError, - "process() takes buffer (2D array or list of arrays, one row per channel) and timestamp (RealTime) arguments"); - return 0; } - - if (!PyRealTime_Check(pyRealTime)) { - PyErr_SetString(PyExc_TypeError, "Valid timestamp required."); - 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; - vector<vector<float> > data = - convertPluginInput(pyBuffer, channels, pd->blockSize); - if (data.empty()) return 0; - - float **inbuf = new float *[channels]; - for (int c = 0; c < channels; ++c) { - inbuf[c] = &data[c][0]; - } - RealTime timeStamp = *PyRealTime_AsRealTime(pyRealTime); - Plugin::FeatureSet fs = pd->plugin->process(inbuf, timeStamp); - delete[] inbuf; - - return convertFeatureSet(fs); -} - -static PyObject * -getRemainingFeatures(PyObject *self, PyObject *) -{ - PyPluginObject *pd = getPluginObject(self); - if (!pd) return 0; - - if (!pd->isInitialised) { - PyErr_SetString(PyExc_StandardError, - "Plugin has not been initialised."); - return 0; - } - - Plugin::FeatureSet fs = pd->plugin->getRemainingFeatures(); - - return convertFeatureSet(fs); -} - -static PyObject * -getPreferredBlockSize(PyObject *self, PyObject *) -{ - PyPluginObject *pd = getPluginObject(self); - if (!pd) return 0; - return PyInt_FromLong(pd->plugin->getPreferredBlockSize()); -} - -static PyObject * -getPreferredStepSize(PyObject *self, PyObject *) -{ - PyPluginObject *pd = getPluginObject(self); - if (!pd) return 0; - return PyInt_FromLong(pd->plugin->getPreferredStepSize()); -} - -static PyObject * -getMinChannelCount(PyObject *self, PyObject *) -{ - PyPluginObject *pd = getPluginObject(self); - if (!pd) return 0; - return PyInt_FromLong(pd->plugin->getMinChannelCount()); -} - -static PyObject * -getMaxChannelCount(PyObject *self, PyObject *) -{ - PyPluginObject *pd = getPluginObject(self); - if (!pd) return 0; - return PyInt_FromLong(pd->plugin->getMaxChannelCount()); -} - -static PyObject * -unload(PyObject *self, PyObject *) -{ - PyPluginObject *pd = getPluginObject(self); - if (!pd) return 0; - - delete pd->plugin; - pd->plugin = 0; // This is checked by getPluginObject, so we avoid - // blowing up if called repeatedly - - return Py_True; -} - -static PyMemberDef PyPluginObject_members[] = -{ - {(char *)"info", T_OBJECT, offsetof(PyPluginObject, info), READONLY, - (char *)"info -> A read-only dictionary of plugin metadata."}, - - {(char *)"inputDomain", T_INT, offsetof(PyPluginObject, inputDomain), READONLY, - (char *)"inputDomain -> The format of input audio required by the plugin, either vampyhost.TimeDomain or vampyhost.FrequencyDomain."}, - - {(char *)"parameters", T_OBJECT, offsetof(PyPluginObject, parameters), READONLY, - (char *)"parameters -> A list of metadata dictionaries describing the plugin's configurable parameters."}, - - {(char *)"programs", T_OBJECT, offsetof(PyPluginObject, programs), READONLY, - (char *)"programs -> A list of the programs available for this plugin, if any."}, - - {0, 0} -}; - -static PyMethodDef PyPluginObject_methods[] = -{ - {"getOutputs", getOutputs, METH_NOARGS, - "getOutputs() -> Obtain the output descriptors for all of the plugin's outputs."}, - - {"getParameterValue", getParameterValue, METH_VARARGS, - "getParameterValue(identifier) -> Return the value of the parameter with the given identifier."}, - - {"setParameterValue", setParameterValue, METH_VARARGS, - "setParameterValue(identifier, value) -> Set the parameter with the given identifier to the given value."}, - - {"setParameterValues", setParameterValues, METH_VARARGS, - "setParameterValues(dict) -> Set multiple parameters to values corresponding to the key/value pairs in the dict. Any parameters not mentioned in the dict are unchanged."}, - - {"selectProgram", selectProgram, METH_VARARGS, - "selectProgram(name) -> Select the processing program with the given name."}, - - {"getPreferredBlockSize", getPreferredBlockSize, METH_VARARGS, - "getPreferredBlockSize() -> Return the plugin's preferred processing block size, or 0 if the plugin accepts any block size."}, - - {"getPreferredStepSize", getPreferredStepSize, METH_VARARGS, - "getPreferredStepSize() -> Return the plugin's preferred processing step size, or 0 if the plugin allows the host to select. If this is 0, the host should normally choose the same step as block size for time-domain plugins, or half the block size for frequency-domain plugins."}, - - {"getMinChannelCount", getMinChannelCount, METH_VARARGS, - "getMinChannelCount() -> Return the minimum number of channels of audio data the plugin accepts as input."}, - - {"getMaxChannelCount", getMaxChannelCount, METH_VARARGS, - "getMaxChannelCount() -> Return the maximum number of channels of audio data the plugin accepts as input."}, - - {"initialise", initialise, METH_VARARGS, - "initialise(channels, stepSize, blockSize) -> Initialise the plugin for the given number of channels and processing frame sizes. This must be called before process() can be used."}, - - {"reset", reset, METH_NOARGS, - "reset() -> Reset the plugin after processing, to prepare for another processing run with the same parameters."}, - - {"process", process, METH_VARARGS, - "process(block, timestamp) -> Provide one processing frame to the plugin, with its timestamp, and obtain any features that were extracted immediately from this frame."}, - - {"getRemainingFeatures", getRemainingFeatures, METH_NOARGS, - "getRemainingFeatures() -> Obtain any features extracted at the end of processing."}, - - {"unload", unload, METH_NOARGS, - "unload() -> Dispose of the plugin. You cannot use the plugin object again after calling this. Note that unloading also happens automatically when the plugin object's reference count reaches zero; this function is only necessary if you wish to ensure the native part of the plugin is disposed of before then."}, - - {0, 0} -}; - -/* 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*/ - 0, /*tp_getattr*/ - 0, /*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*/ - PyObject_GenericGetAttr, /*tp_getattro*/ - PyObject_GenericSetAttr, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - "Plugin object, providing a low-level API for running a Vamp plugin.", /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - PyPluginObject_methods, /*tp_methods*/ - PyPluginObject_members, /*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*/ -}; -
--- a/PyPluginObject.h Tue Jan 13 12:16:38 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -/* -*- 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. -*/ - -#ifndef PYPLUGINOBJECT_H -#define PYPLUGINOBJECT_H - -#include <Python.h> -#include <vamp-hostsdk/Plugin.h> - -#include <string> - -struct PyPluginObject -{ - PyObject_HEAD - Vamp::Plugin *plugin; - bool isInitialised; - size_t channels; - size_t blockSize; - size_t stepSize; - PyObject *info; - int inputDomain; - PyObject *parameters; - PyObject *programs; -}; - -PyAPI_DATA(PyTypeObject) Plugin_Type; -#define PyPlugin_Check(v) PyObject_TypeCheck(v, &Plugin_Type) - -PyAPI_FUNC(PyObject *) -PyPluginObject_From_Plugin(Vamp::Plugin *); - -#endif - -
--- a/PyRealTime.cpp Tue Jan 13 12:16:38 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,363 +0,0 @@ -/* -*- 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 "PyRealTime.h" - -#include <string> - -using namespace std; -using namespace Vamp; - -/* CONSTRUCTOR: New RealTime object from sec and nsec */ -static PyObject* -RealTime_new(PyTypeObject *type, PyObject *args, PyObject *kw) -{ - unsigned int sec = 0; - unsigned int nsec = 0; - double unary = 0; - const char *fmt = NULL; - - if ( - /// new RealTime from ('format',float) e.g. ('seconds',2.34123) - !PyArg_ParseTuple(args, "|sd:RealTime.new ", - (const char *) &fmt, - (double *) &unary) && - - /// new RealTime from (sec{int},nsec{int}) e.g. (2,34) - !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); - RealTimeObject *self = (RealTimeObject*)type->tp_alloc(type, 0); - - if (self == NULL) return NULL; - - self->rt = NULL; - - if (sec == 0 && nsec == 0 && fmt == 0) - self->rt = new RealTime(); - else if (fmt == 0) - self->rt = new RealTime(sec,nsec); - else { - /// new RealTime from seconds or milliseconds: i.e. >>>RealTime('seconds',12.3) - if (!string(fmt).compare("float") || - !string(fmt).compare("seconds")) - self->rt = new RealTime( - RealTime::fromSeconds((double) unary)); - - if (!string(fmt).compare("milliseconds")) { - self->rt = new RealTime( - RealTime::fromSeconds((double) unary / 1000.0)); } - } - - if (!self->rt) { - PyErr_SetString(PyExc_TypeError, - "RealTime initialised with wrong arguments."); - return NULL; - } - - return (PyObject *) self; -} - -/* DESTRUCTOR: delete type object */ -static void -RealTimeObject_dealloc(RealTimeObject *self) -{ - if (self->rt) delete self->rt; //delete the C object - PyObject_Del(self); //delete the Python object (original) - /// this requires PyType_Ready() which fills ob_type - // self->ob_type->tp_free((PyObject*)self); -} - -/* RealTime Object's Methods */ -//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_toString(RealTimeObject *self, PyObject *args) -{ - return Py_BuildValue("s",self->rt->toText().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,"Integer 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); -} - - -/* Type object's (RealTime) methods table */ -static PyMethodDef RealTime_methods[] = -{ - {"values", (PyCFunction)RealTime_values, METH_NOARGS, - PyDoc_STR("values() -> Tuple of sec,nsec representation.")}, - - {"toString", (PyCFunction)RealTime_toString, METH_NOARGS, - PyDoc_STR("toString() -> Return a user-readable string to the nearest millisecond in a form like HH:MM:SS.mmm")}, - - {"toFrame", (PyCFunction)RealTime_toFrame, METH_VARARGS, - PyDoc_STR("toFrame(samplerate) -> Sample count for given sample rate.")}, - - {"toFloat", (PyCFunction)RealTime_float, METH_NOARGS, - PyDoc_STR("toFloat() -> Floating point representation.")}, - - {NULL, NULL} /* sentinel */ -}; - - -/* Methods implementing protocols */ -// these functions are called by the interpreter - -/* Object Protocol */ - -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; -} - -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); -} - -/* 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()); -} - - -/* Number Protocol */ -/// TODO: implement all methods available in Vamp::RealTime() objects - -static PyObject * -RealTime_add(PyObject *s, PyObject *w) -{ - RealTimeObject *result = - PyObject_New(RealTimeObject, &RealTime_Type); - if (result == NULL) return NULL; - - result->rt = new 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( - *((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*/ -}; - -/* REAL-TIME TYPE OBJECT */ - -#define RealTime_alloc PyType_GenericAlloc -#define RealTime_free PyObject_Del - -/* Doc:: 10.3 Type Objects */ /* static */ -PyTypeObject RealTime_Type = -{ - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "vampy.RealTime", /*tp_name*/ - sizeof(RealTimeObject), /*tp_basicsize*/ - 0, /*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, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - "RealTime object, used for Vamp plugin timestamps.", /*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*/ - RealTime_alloc, /*tp_alloc*/ - RealTime_new, /*tp_new*/ - RealTime_free, /*tp_free*/ - 0, /*tp_is_gc*/ -}; - - - -/* PyRealTime C++ API */ - -/*PyRealTime from RealTime*/ -PyObject* -PyRealTime_FromRealTime(const Vamp::RealTime& rt) { - - RealTimeObject *self = - PyObject_New(RealTimeObject, &RealTime_Type); - if (self == NULL) return NULL; - - self->rt = new RealTime(rt); - return (PyObject*) self; -} - -/*RealTime* from PyRealTime*/ -const Vamp::RealTime* -PyRealTime_AsRealTime (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; -}; -
--- a/PyRealTime.h Tue Jan 13 12:16:38 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/* -*- 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. -*/ - -#ifndef PYREALTIME_H -#define PYREALTIME_H - -#include <Python.h> -#include <vamp-hostsdk/RealTime.h> - -typedef struct { - PyObject_HEAD - Vamp::RealTime *rt; -} RealTimeObject; - -PyAPI_DATA(PyTypeObject) RealTime_Type; - -#define PyRealTime_Check(v) PyObject_TypeCheck(v, &RealTime_Type) -#define PyRealTime_AS_REALTIME(v) ((const RealTimeObject* const) (v))->rt - -PyAPI_FUNC(PyObject *) -PyRealTime_FromRealTime(const Vamp::RealTime&); - -PyAPI_FUNC(const Vamp::RealTime*) -PyRealTime_AsRealTime (PyObject *self); - -#endif
--- a/VectorConversion.cpp Tue Jan 13 12:16:38 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,318 +0,0 @@ -/* -*- 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 <Python.h> - -#include "FloatConversion.h" -#include "VectorConversion.h" - -#include <math.h> -#include <float.h> - -using namespace std; - -/* Note: NO FUNCTION IN THIS CLASS SHOULD ALTER REFERENCE COUNTS - (EXCEPT FOR TEMPORARY PYTHON OBJECTS)! */ - -VectorConversion::VectorConversion() : - m_error(false), - error(m_error) // const public reference for easy access -{ -} - -VectorConversion::~VectorConversion() -{ -} - -/// floating point numbers (TODO: check numpy.float128) -float -VectorConversion::PyValue_To_Float(PyObject* pyValue) const -{ - if (FloatConversion::check(pyValue)) { - return FloatConversion::convert(pyValue); - } - - setValueError("Conversion error: object" + PyValue_Get_TypeName(pyValue) +" is not float, int, or long."); - return 0.0; -} - -vector<float> -VectorConversion::PyValue_To_FloatVector (PyObject *pyValue) const -{ - /// numpy array - if (PyArray_CheckExact(pyValue)) - return PyArray_To_FloatVector(pyValue); - - /// python list of floats (backward compatible) - if (PyList_Check(pyValue)) { - return PyList_To_FloatVector(pyValue); - } - - string msg = "Value is not list or array of floats"; - setValueError(msg); -#ifdef _DEBUG - cerr << "VectorConversion::PyValue_To_FloatVector failed. " << msg << endl; -#endif - return vector<float>(); -} - -vector<float> -VectorConversion::PyList_To_FloatVector (PyObject *inputList) const -{ - vector<float> v; - - if (!PyList_Check(inputList)) { - setValueError("Value is not a list"); - return v; - } - - PyObject **pyObjectArray = PySequence_Fast_ITEMS(inputList); - int n = PyList_GET_SIZE(inputList); - - for (int i = 0; i < n; ++i) { - v.push_back(PyValue_To_Float(pyObjectArray[i])); - } - - return v; -} - -vector<float> -VectorConversion::PyArray_To_FloatVector (PyObject *pyValue) const -{ - vector<float> v; - - if (!PyArray_Check(pyValue)) { - setValueError("Value is not an array"); - return v; - } - - PyArrayObject* pyArray = (PyArrayObject*) pyValue; - PyArray_Descr* descr = PyArray_DESCR(pyArray); - - if (PyArray_DATA(pyArray) == 0 || descr == 0) { - string msg = "NumPy array with NULL data or descriptor pointer encountered."; - setValueError(msg); - return v; - } - - if (PyArray_NDIM(pyArray) != 1) { - string msg = "NumPy array must be a one-dimensional vector."; - setValueError(msg); - return v; - } - - /// check strides (useful if array is not continuous) - size_t strides = *((size_t*) PyArray_STRIDES(pyArray)); - - /// convert the array - switch (descr->type_num) { - - case NPY_FLOAT : // dtype='float32' - return PyArray_Convert<float,float>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); - case NPY_DOUBLE : // dtype='float64' - return PyArray_Convert<float,double>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); - case NPY_INT : // dtype='int' - return PyArray_Convert<float,int>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); - case NPY_LONG : // dtype='long' - return PyArray_Convert<float,long>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); - default : - string msg = "Unsupported value type in NumPy array object."; - setValueError(msg); -#ifdef _DEBUG - cerr << "VectorConversion::PyArray_To_FloatVector failed. Error: " << msg << endl; -#endif - return v; - } -} - -vector<vector<float> > -VectorConversion::Py2DArray_To_FloatVector (PyObject *pyValue) const -{ - vector<vector<float> > v; - - if (!PyArray_Check(pyValue)) { - setValueError("Value is not an array"); - return v; - } - - PyArrayObject* pyArray = (PyArrayObject*) pyValue; - PyArray_Descr* descr = PyArray_DESCR(pyArray); - - if (PyArray_DATA(pyArray) == 0 || descr == 0) { - string msg = "NumPy array with NULL data or descriptor pointer encountered."; - setValueError(msg); - return v; - } - - if (PyArray_NDIM(pyArray) != 2) { - string msg = "NumPy array must be a two-dimensional matrix."; - setValueError(msg); - return v; - } - - /// check strides (useful if array is not continuous) - size_t *strideptr = (size_t*) PyArray_STRIDES(pyArray); - - /// convert the array - for (int i = 0; i < PyArray_DIMS(pyArray)[0]; ++i) { - - vector<float> vv; - - switch (descr->type_num) { - - case NPY_FLOAT : // dtype='float32' - vv = PyArray_Convert<float,float>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]); - break; - case NPY_DOUBLE : // dtype='float64' - vv = PyArray_Convert<float,double>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]); - break; - case NPY_INT : // dtype='int' - vv = PyArray_Convert<float,int>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]); - break; - case NPY_LONG : // dtype='long' - vv = PyArray_Convert<float,long>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]); - break; - default : - string msg = "Unsupported value type in NumPy array object."; - cerr << "VectorConversion::PyArray_To_FloatVector failed (value type = " << descr->type_num << "). Error: " << msg << endl; - setValueError(msg); - return v; - } - - v.push_back(vv); - } - - return v; -} - -PyObject * -VectorConversion::PyArray_From_FloatVector(const vector<float> &v) const -{ - npy_intp ndims[1]; - ndims[0] = (int)v.size(); - PyObject *arr = PyArray_SimpleNew(1, ndims, NPY_FLOAT); - float *data = (float *)PyArray_DATA((PyArrayObject *)arr); - for (int i = 0; i < ndims[0]; ++i) { - data[i] = v[i]; - } - return arr; -} - -PyObject * -VectorConversion::PyValue_From_StringVector(const vector<string> &v) const -{ - PyObject *pyList = PyList_New(v.size()); - for (size_t i = 0; i < v.size(); ++i) { - PyObject *pyStr = PyString_FromString(v[i].c_str()); - PyList_SET_ITEM(pyList, i, pyStr); - } - return pyList; -} - - -/* Error handling */ - -void -VectorConversion::setValueError (string message) const -{ - m_error = true; - m_errorQueue.push(ValueError(message)); -} - -/// return a reference to the last error or creates a new one. -ValueError& -VectorConversion::lastError() const -{ - m_error = false; - if (!m_errorQueue.empty()) return m_errorQueue.back(); - else { - m_errorQueue.push(ValueError("Type conversion error.")); - return m_errorQueue.back(); - } -} - -/// helper function to iterate over the error message queue: -/// pops the oldest item -ValueError -VectorConversion::getError() const -{ - if (!m_errorQueue.empty()) { - ValueError e = m_errorQueue.front(); - m_errorQueue.pop(); - if (m_errorQueue.empty()) m_error = false; - return e; - } - else { - m_error = false; - return ValueError(); - } -} - -/* Utilities */ - -/// get the type name of an object -string -VectorConversion::PyValue_Get_TypeName(PyObject* pyValue) const -{ - PyObject *pyType = PyObject_Type(pyValue); - if (!pyType) - { - cerr << "Warning: Object type name could not be found." << endl; - if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} - return string ("< unknown type >"); - } - PyObject *pyString = PyObject_Str(pyType); - if (!pyString) - { - cerr << "Warning: Object type name could not be found." << endl; - if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} - Py_CLEAR(pyType); - return string ("< unknown type >"); - } - char *cstr = PyString_AS_STRING(pyString); - if (!cstr) - { - cerr << "Warning: Object type name could not be found." << endl; - if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} - Py_DECREF(pyType); - Py_CLEAR(pyString); - return string("< unknown type >"); - } - Py_DECREF(pyType); - Py_DECREF(pyString); - return string(cstr); -}
--- a/VectorConversion.h Tue Jan 13 12:16:38 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,139 +0,0 @@ -/* -*- 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. -*/ - -/* - VectorConversion: A handful of type safe conversion utilities - between Python types and C++ vectors. -*/ - -#ifndef VAMPYHOST_VECTOR_CONVERSION_H -#define VAMPYHOST_VECTOR_CONVERSION_H - -#include <Python.h> - -// NumPy is required here -#define PY_ARRAY_UNIQUE_SYMBOL VAMPYHOST_ARRAY_API -#define NO_IMPORT_ARRAY -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -#include "numpy/arrayobject.h" - -#include <vector> -#include <queue> -#include <string> -#include <sstream> -#include <iostream> - -// Data -class ValueError -{ -public: - ValueError() {} - ValueError(std::string m) : message(m) {} - std::string location; - std::string message; - std::string str() const { - return (location.empty()) ? message : message + "\nLocation: " + location;} - template<typename V> ValueError &operator<< (const V& v) - { - std::ostringstream ss; - ss << v; - location += ss.str(); - return *this; - } -}; - -class VectorConversion -{ -public: - VectorConversion(); - ~VectorConversion(); - - ValueError getError() const; - - std::vector<float> PyValue_To_FloatVector (PyObject*) const; - std::vector<float> PyArray_To_FloatVector (PyObject *) const; - std::vector<float> PyList_To_FloatVector (PyObject*) const; - std::vector<std::vector<float> > Py2DArray_To_FloatVector (PyObject *) const; - - PyObject *PyValue_From_StringVector(const std::vector<std::string> &) const; - PyObject *PyArray_From_FloatVector(const std::vector<float> &) const; - -private: - std::string PyValue_Get_TypeName(PyObject*) const; - float PyValue_To_Float(PyObject*) const; - - /// Convert DTYPE type 1D NumpyArray to std::vector<RET> - template<typename RET, typename DTYPE> - std::vector<RET> PyArray_Convert(void* raw_data_ptr, - int length, - size_t strides) const { - - std::vector<RET> v(length); - - /// check if the array is continuous, if not use strides info - if (sizeof(DTYPE) != strides) { -#ifdef _DEBUG_VALUES - cerr << "Warning: discontinuous numpy array. Strides: " << strides << " bytes. sizeof(dtype): " << sizeof(DTYPE) << endl; -#endif - char* data = (char*) raw_data_ptr; - for (int i = 0; i < length; ++i){ - v[i] = (RET)(*((DTYPE*)data)); - data += strides; - } - return v; - } - - DTYPE* data = (DTYPE*) raw_data_ptr; - for (int i = 0; i < length; ++i){ - v[i] = (RET)data[i]; - } - - return v; - } - -private: - mutable bool m_error; - mutable std::queue<ValueError> m_errorQueue; - - void setValueError(std::string) const; - ValueError& lastError() const; - -public: - const bool& error; -}; - -#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/native/FloatConversion.h Wed Jan 14 08:30:47 2015 +0000 @@ -0,0 +1,76 @@ +/* -*- 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. +*/ + +#ifndef VAMPYHOST_FLOAT_CONVERSION_H +#define VAMPYHOST_FLOAT_CONVERSION_H + +class FloatConversion +{ +public: + static bool check(PyObject *pyValue) { + if (pyValue && PyFloat_Check(pyValue)) { + return true; + } + if (pyValue && PyLong_Check(pyValue)) { + return true; + } + if (pyValue && PyInt_Check(pyValue)) { + return true; + } + return false; + } + + static float convert(PyObject* pyValue) { + + if (pyValue && PyFloat_Check(pyValue)) { + return (float) PyFloat_AS_DOUBLE(pyValue); + } + + if (pyValue && PyLong_Check(pyValue)) { + return (float) PyLong_AsDouble(pyValue); + } + + if (pyValue && PyInt_Check(pyValue)) { + return (float) PyInt_AsLong(pyValue); + } + + return 0.0; + } +}; + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/native/PyPluginObject.cpp Wed Jan 14 08:30:47 2015 +0000 @@ -0,0 +1,725 @@ +/* -*- 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 "structmember.h" + +#include "FloatConversion.h" +#include "VectorConversion.h" +#include "PyRealTime.h" + +#include <string> +#include <vector> +#include <cstddef> +#include <set> + +using namespace std; +using namespace Vamp; + +static +PyPluginObject * +getPluginObject(PyObject *pyPluginHandle) +{ + 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; + } +} + +static +PyObject * +pystr(const string &s) +{ + return PyString_FromString(s.c_str()); +} + +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; + + PyObject *infodict = PyDict_New(); + PyDict_SetItemString + (infodict, "apiVersion", PyInt_FromLong(plugin->getVampApiVersion())); + PyDict_SetItemString + (infodict, "pluginVersion", PyInt_FromLong(plugin->getPluginVersion())); + PyDict_SetItemString + (infodict, "identifier", pystr(plugin->getIdentifier())); + PyDict_SetItemString + (infodict, "name", pystr(plugin->getName())); + PyDict_SetItemString + (infodict, "description", pystr(plugin->getDescription())); + PyDict_SetItemString + (infodict, "maker", pystr(plugin->getMaker())); + PyDict_SetItemString + (infodict, "copyright", pystr(plugin->getCopyright())); + pd->info = infodict; + + pd->inputDomain = plugin->getInputDomain(); + + VectorConversion conv; + + Plugin::ParameterList pl = plugin->getParameterDescriptors(); + PyObject *params = PyList_New(pl.size()); + + for (int i = 0; i < (int)pl.size(); ++i) { + PyObject *paramdict = PyDict_New(); + PyDict_SetItemString + (paramdict, "identifier", pystr(pl[i].identifier)); + PyDict_SetItemString + (paramdict, "name", pystr(pl[i].name)); + PyDict_SetItemString + (paramdict, "description", pystr(pl[i].description)); + PyDict_SetItemString + (paramdict, "unit", pystr(pl[i].unit)); + PyDict_SetItemString + (paramdict, "minValue", PyFloat_FromDouble(pl[i].minValue)); + PyDict_SetItemString + (paramdict, "maxValue", PyFloat_FromDouble(pl[i].maxValue)); + PyDict_SetItemString + (paramdict, "defaultValue", PyFloat_FromDouble(pl[i].defaultValue)); + if (pl[i].isQuantized) { + PyDict_SetItemString + (paramdict, "isQuantized", Py_True); + PyDict_SetItemString + (paramdict, "quantizeStep", PyFloat_FromDouble(pl[i].quantizeStep)); + if (!pl[i].valueNames.empty()) { + PyDict_SetItemString + (paramdict, "valueNames", conv.PyValue_From_StringVector(pl[i].valueNames)); + } + } else { + PyDict_SetItemString + (paramdict, "isQuantized", Py_False); + } + + PyList_SET_ITEM(params, i, paramdict); + } + + pd->parameters = params; + + Plugin::ProgramList prl = plugin->getPrograms(); + PyObject *progs = PyList_New(prl.size()); + + for (int i = 0; i < (int)prl.size(); ++i) { + PyList_SET_ITEM(progs, i, pystr(prl[i])); + } + + pd->programs = progs; + + return (PyObject *)pd; +} + +static void +PyPluginObject_dealloc(PyPluginObject *self) +{ + delete self->plugin; + PyObject_Del(self); +} + +static PyObject * +getOutputs(PyObject *self, PyObject *args) +{ + PyPluginObject *pd = getPluginObject(self); + if (!pd) return 0; + + Plugin::OutputList ol = pd->plugin->getOutputDescriptors(); + PyObject *outputs = PyList_New(ol.size()); + + for (int i = 0; i < (int)ol.size(); ++i) { + PyObject *outdict = PyDict_New(); + PyDict_SetItemString + (outdict, "identifier", pystr(ol[i].identifier)); + PyDict_SetItemString + (outdict, "name", pystr(ol[i].name)); + PyDict_SetItemString + (outdict, "description", pystr(ol[i].description)); + PyDict_SetItemString + (outdict, "binCount", PyInt_FromLong(ol[i].binCount)); + if (ol[i].binCount > 0) { + if (ol[i].hasKnownExtents) { + PyDict_SetItemString + (outdict, "hasKnownExtents", Py_True); + PyDict_SetItemString + (outdict, "minValue", PyFloat_FromDouble(ol[i].minValue)); + PyDict_SetItemString + (outdict, "maxValue", PyFloat_FromDouble(ol[i].maxValue)); + } else { + PyDict_SetItemString + (outdict, "hasKnownExtents", Py_False); + } + if (ol[i].isQuantized) { + PyDict_SetItemString + (outdict, "isQuantized", Py_True); + PyDict_SetItemString + (outdict, "quantizeStep", PyFloat_FromDouble(ol[i].quantizeStep)); + } else { + PyDict_SetItemString + (outdict, "isQuantized", Py_False); + } + } + PyDict_SetItemString + (outdict, "sampleType", PyInt_FromLong((int)ol[i].sampleType)); + PyDict_SetItemString + (outdict, "sampleRate", PyFloat_FromDouble(ol[i].sampleRate)); + PyDict_SetItemString + (outdict, "hasDuration", ol[i].hasDuration ? Py_True : Py_False); + + PyList_SET_ITEM(outputs, i, outdict); + } + + return outputs; +} + +static PyObject * +initialise(PyObject *self, PyObject *args) +{ + 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 << endl; + PyErr_SetString(PyExc_TypeError, + "Plugin initialization failed"); + return 0; + } + + pd->isInitialised = true; + + return Py_True; +} + +static PyObject * +reset(PyObject *self, PyObject *) +{ + 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 bool +hasParameter(PyPluginObject *pd, string id) +{ + PluginBase::ParameterList pl = pd->plugin->getParameterDescriptors(); + for (int i = 0; i < (int)pl.size(); ++i) { + if (pl[i].identifier == id) { + return true; + } + } + return false; +} + +static PyObject * +getParameterValue(PyObject *self, PyObject *args) +{ + PyObject *pyParam; + + if (!PyArg_ParseTuple(args, "S", &pyParam)) { + PyErr_SetString(PyExc_TypeError, + "getParameterValue() takes parameter id (string) argument"); + return 0; } + + PyPluginObject *pd = getPluginObject(self); + if (!pd) return 0; + + string param = PyString_AS_STRING(pyParam); + + if (!hasParameter(pd, param)) { + PyErr_SetString(PyExc_StandardError, + (string("Unknown parameter id \"") + param + "\"").c_str()); + return 0; + } + + float value = pd->plugin->getParameter(param); + return PyFloat_FromDouble(double(value)); +} + +static PyObject * +setParameterValue(PyObject *self, PyObject *args) +{ + PyObject *pyParam; + float value; + + if (!PyArg_ParseTuple(args, "Sf", &pyParam, &value)) { + PyErr_SetString(PyExc_TypeError, + "setParameterValue() takes parameter id (string), and value (float) arguments"); + return 0; } + + PyPluginObject *pd = getPluginObject(self); + if (!pd) return 0; + + string param = PyString_AS_STRING(pyParam); + + if (!hasParameter(pd, param)) { + PyErr_SetString(PyExc_StandardError, + (string("Unknown parameter id \"") + param + "\"").c_str()); + return 0; + } + + pd->plugin->setParameter(param, value); + return Py_True; +} + +static PyObject * +setParameterValues(PyObject *self, PyObject *args) +{ + PyObject *dict; + + if (!PyArg_ParseTuple(args, "O", &dict)) { + PyErr_SetString(PyExc_TypeError, + "setParameterValues() takes dict argument"); + return 0; } + + if (!PyDict_Check(dict)) { + PyErr_SetString(PyExc_TypeError, + "setParameterValues() takes dict argument"); + return 0; } + + PyPluginObject *pd = getPluginObject(self); + if (!pd) return 0; + + PluginBase::ParameterList pl = pd->plugin->getParameterDescriptors(); + set<string> paramIds; + for (int i = 0; i < (int)pl.size(); ++i) { + paramIds.insert(pl[i].identifier); + } + + Py_ssize_t pos = 0; + PyObject *key, *value; + while (PyDict_Next(dict, &pos, &key, &value)) { + if (!key || !PyString_CheckExact(key)) { + PyErr_SetString(PyExc_TypeError, + "Parameter dict keys must all have string type"); + return 0; + } + if (!value || !FloatConversion::check(value)) { + PyErr_SetString(PyExc_TypeError, + "Parameter dict values must be convertible to float"); + return 0; + } + string param = PyString_AS_STRING(key); + if (paramIds.find(param) == paramIds.end()) { + PyErr_SetString(PyExc_StandardError, + (string("Unknown parameter id \"") + param + "\"").c_str()); + return 0; + } + pd->plugin->setParameter(param, FloatConversion::convert(value)); + } + + return Py_True; +} + +static PyObject * +selectProgram(PyObject *self, PyObject *args) +{ + PyObject *pyParam; + + if (!PyArg_ParseTuple(args, "S", &pyParam)) { + PyErr_SetString(PyExc_TypeError, + "selectProgram() takes parameter id (string) argument"); + return 0; } + + PyPluginObject *pd = getPluginObject(self); + if (!pd) return 0; + + pd->plugin->selectProgram(PyString_AS_STRING(pyParam)); + return Py_True; +} + +static +PyObject * +convertFeatureSet(const Plugin::FeatureSet &fs) +{ + 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", pystr(f.label)); + + 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); + } + } + + return pyFs; +} + +static vector<vector<float> > +convertPluginInput(PyObject *pyBuffer, int channels, int blockSize) +{ + vector<vector<float> > data; + + VectorConversion conv; + + if (PyArray_CheckExact(pyBuffer)) { + + data = conv.Py2DArray_To_FloatVector(pyBuffer); + + if (conv.error) { + PyErr_SetString(PyExc_TypeError, conv.getError().str().c_str()); + return data; + } + + if ((int)data.size() != channels) { +// cerr << "Wrong number of channels: got " << data.size() << ", expected " << channels << endl; + PyErr_SetString(PyExc_TypeError, "Wrong number of channels"); + return vector<vector<float> >(); + } + + } else { + + if (!PyList_Check(pyBuffer)) { + PyErr_SetString(PyExc_TypeError, "List of NumPy arrays or lists of numbers required for process input"); + return data; + } + + 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 data; + } + + for (int c = 0; c < channels; ++c) { + PyObject *cbuf = PyList_GET_ITEM(pyBuffer, c); + data.push_back(conv.PyValue_To_FloatVector(cbuf)); + if (conv.error) { + PyErr_SetString(PyExc_TypeError, conv.getError().str().c_str()); + return vector<vector<float> >(); + } + } + } + + for (int c = 0; c < channels; ++c) { + if ((int)data[c].size() != blockSize) { +// cerr << "Wrong number of samples on channel " << c << ": expected " << blockSize << " (plugin's block size), got " << data[c].size() << endl; + PyErr_SetString(PyExc_TypeError, "Wrong number of samples for process block"); + return vector<vector<float> >(); + } + } + + return data; +} + +static PyObject * +processBlock(PyObject *self, PyObject *args) +{ + PyObject *pyBuffer; + PyObject *pyRealTime; + + if (!PyArg_ParseTuple(args, "OO", + &pyBuffer, // Audio data + &pyRealTime)) { // TimeStamp + PyErr_SetString(PyExc_TypeError, + "processBlock() takes buffer (2D array or list of arrays, one row per channel) and timestamp (RealTime) arguments"); + return 0; } + + if (!PyRealTime_Check(pyRealTime)) { + PyErr_SetString(PyExc_TypeError, "Valid timestamp required."); + 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; + vector<vector<float> > data = + convertPluginInput(pyBuffer, channels, pd->blockSize); + if (data.empty()) return 0; + + float **inbuf = new float *[channels]; + for (int c = 0; c < channels; ++c) { + inbuf[c] = &data[c][0]; + } + RealTime timeStamp = *PyRealTime_AsRealTime(pyRealTime); + Plugin::FeatureSet fs = pd->plugin->process(inbuf, timeStamp); + delete[] inbuf; + + return convertFeatureSet(fs); +} + +static PyObject * +getRemainingFeatures(PyObject *self, PyObject *) +{ + PyPluginObject *pd = getPluginObject(self); + if (!pd) return 0; + + if (!pd->isInitialised) { + PyErr_SetString(PyExc_StandardError, + "Plugin has not been initialised."); + return 0; + } + + Plugin::FeatureSet fs = pd->plugin->getRemainingFeatures(); + + return convertFeatureSet(fs); +} + +static PyObject * +getPreferredBlockSize(PyObject *self, PyObject *) +{ + PyPluginObject *pd = getPluginObject(self); + if (!pd) return 0; + return PyInt_FromLong(pd->plugin->getPreferredBlockSize()); +} + +static PyObject * +getPreferredStepSize(PyObject *self, PyObject *) +{ + PyPluginObject *pd = getPluginObject(self); + if (!pd) return 0; + return PyInt_FromLong(pd->plugin->getPreferredStepSize()); +} + +static PyObject * +getMinChannelCount(PyObject *self, PyObject *) +{ + PyPluginObject *pd = getPluginObject(self); + if (!pd) return 0; + return PyInt_FromLong(pd->plugin->getMinChannelCount()); +} + +static PyObject * +getMaxChannelCount(PyObject *self, PyObject *) +{ + PyPluginObject *pd = getPluginObject(self); + if (!pd) return 0; + return PyInt_FromLong(pd->plugin->getMaxChannelCount()); +} + +static PyObject * +unload(PyObject *self, PyObject *) +{ + PyPluginObject *pd = getPluginObject(self); + if (!pd) return 0; + + delete pd->plugin; + pd->plugin = 0; // This is checked by getPluginObject, so we avoid + // blowing up if called repeatedly + + return Py_True; +} + +static PyMemberDef PyPluginObject_members[] = +{ + {(char *)"info", T_OBJECT, offsetof(PyPluginObject, info), READONLY, + (char *)"info -> A read-only dictionary of plugin metadata."}, + + {(char *)"inputDomain", T_INT, offsetof(PyPluginObject, inputDomain), READONLY, + (char *)"inputDomain -> The format of input audio required by the plugin, either vampyhost.TimeDomain or vampyhost.FrequencyDomain."}, + + {(char *)"parameters", T_OBJECT, offsetof(PyPluginObject, parameters), READONLY, + (char *)"parameters -> A list of metadata dictionaries describing the plugin's configurable parameters."}, + + {(char *)"programs", T_OBJECT, offsetof(PyPluginObject, programs), READONLY, + (char *)"programs -> A list of the programs available for this plugin, if any."}, + + {0, 0} +}; + +static PyMethodDef PyPluginObject_methods[] = +{ + {"getOutputs", getOutputs, METH_NOARGS, + "getOutputs() -> Obtain the output descriptors for all of the plugin's outputs."}, + + {"getParameterValue", getParameterValue, METH_VARARGS, + "getParameterValue(identifier) -> Return the value of the parameter with the given identifier."}, + + {"setParameterValue", setParameterValue, METH_VARARGS, + "setParameterValue(identifier, value) -> Set the parameter with the given identifier to the given value."}, + + {"setParameterValues", setParameterValues, METH_VARARGS, + "setParameterValues(dict) -> Set multiple parameters to values corresponding to the key/value pairs in the dict. Any parameters not mentioned in the dict are unchanged."}, + + {"selectProgram", selectProgram, METH_VARARGS, + "selectProgram(name) -> Select the processing program with the given name."}, + + {"getPreferredBlockSize", getPreferredBlockSize, METH_VARARGS, + "getPreferredBlockSize() -> Return the plugin's preferred processing block size, or 0 if the plugin accepts any block size."}, + + {"getPreferredStepSize", getPreferredStepSize, METH_VARARGS, + "getPreferredStepSize() -> Return the plugin's preferred processing step size, or 0 if the plugin allows the host to select. If this is 0, the host should normally choose the same step as block size for time-domain plugins, or half the block size for frequency-domain plugins."}, + + {"getMinChannelCount", getMinChannelCount, METH_VARARGS, + "getMinChannelCount() -> Return the minimum number of channels of audio data the plugin accepts as input."}, + + {"getMaxChannelCount", getMaxChannelCount, METH_VARARGS, + "getMaxChannelCount() -> Return the maximum number of channels of audio data the plugin accepts as input."}, + + {"initialise", initialise, METH_VARARGS, + "initialise(channels, stepSize, blockSize) -> Initialise the plugin for the given number of channels and processing frame sizes. This must be called before processBlock() can be used."}, + + {"reset", reset, METH_NOARGS, + "reset() -> Reset the plugin after processing, to prepare for another processing run with the same parameters."}, + + {"processBlock", processBlock, METH_VARARGS, + "processBlock(block, timestamp) -> Provide one processing frame to the plugin, with its timestamp, and obtain any features that were extracted immediately from this frame."}, + + {"getRemainingFeatures", getRemainingFeatures, METH_NOARGS, + "getRemainingFeatures() -> Obtain any features extracted at the end of processing."}, + + {"unload", unload, METH_NOARGS, + "unload() -> Dispose of the plugin. You cannot use the plugin object again after calling this. Note that unloading also happens automatically when the plugin object's reference count reaches zero; this function is only necessary if you wish to ensure the native part of the plugin is disposed of before then."}, + + {0, 0} +}; + +/* 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*/ + 0, /*tp_getattr*/ + 0, /*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*/ + PyObject_GenericGetAttr, /*tp_getattro*/ + PyObject_GenericSetAttr, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Plugin object, providing a low-level API for running a Vamp plugin.", /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + PyPluginObject_methods, /*tp_methods*/ + PyPluginObject_members, /*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*/ +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/native/PyPluginObject.h Wed Jan 14 08:30:47 2015 +0000 @@ -0,0 +1,68 @@ +/* -*- 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. +*/ + +#ifndef PYPLUGINOBJECT_H +#define PYPLUGINOBJECT_H + +#include <Python.h> +#include <vamp-hostsdk/Plugin.h> + +#include <string> + +struct PyPluginObject +{ + PyObject_HEAD + Vamp::Plugin *plugin; + bool isInitialised; + size_t channels; + size_t blockSize; + size_t stepSize; + PyObject *info; + int inputDomain; + PyObject *parameters; + PyObject *programs; +}; + +PyAPI_DATA(PyTypeObject) Plugin_Type; +#define PyPlugin_Check(v) PyObject_TypeCheck(v, &Plugin_Type) + +PyAPI_FUNC(PyObject *) +PyPluginObject_From_Plugin(Vamp::Plugin *); + +#endif + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/native/PyRealTime.cpp Wed Jan 14 08:30:47 2015 +0000 @@ -0,0 +1,363 @@ +/* -*- 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 "PyRealTime.h" + +#include <string> + +using namespace std; +using namespace Vamp; + +/* CONSTRUCTOR: New RealTime object from sec and nsec */ +static PyObject* +RealTime_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + unsigned int sec = 0; + unsigned int nsec = 0; + double unary = 0; + const char *fmt = NULL; + + if ( + /// new RealTime from ('format',float) e.g. ('seconds',2.34123) + !PyArg_ParseTuple(args, "|sd:RealTime.new ", + (const char *) &fmt, + (double *) &unary) && + + /// new RealTime from (sec{int},nsec{int}) e.g. (2,34) + !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); + RealTimeObject *self = (RealTimeObject*)type->tp_alloc(type, 0); + + if (self == NULL) return NULL; + + self->rt = NULL; + + if (sec == 0 && nsec == 0 && fmt == 0) + self->rt = new RealTime(); + else if (fmt == 0) + self->rt = new RealTime(sec,nsec); + else { + /// new RealTime from seconds or milliseconds: i.e. >>>RealTime('seconds',12.3) + if (!string(fmt).compare("float") || + !string(fmt).compare("seconds")) + self->rt = new RealTime( + RealTime::fromSeconds((double) unary)); + + if (!string(fmt).compare("milliseconds")) { + self->rt = new RealTime( + RealTime::fromSeconds((double) unary / 1000.0)); } + } + + if (!self->rt) { + PyErr_SetString(PyExc_TypeError, + "RealTime initialised with wrong arguments."); + return NULL; + } + + return (PyObject *) self; +} + +/* DESTRUCTOR: delete type object */ +static void +RealTimeObject_dealloc(RealTimeObject *self) +{ + if (self->rt) delete self->rt; //delete the C object + PyObject_Del(self); //delete the Python object (original) + /// this requires PyType_Ready() which fills ob_type + // self->ob_type->tp_free((PyObject*)self); +} + +/* RealTime Object's Methods */ +//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_toString(RealTimeObject *self, PyObject *args) +{ + return Py_BuildValue("s",self->rt->toText().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,"Integer 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); +} + + +/* Type object's (RealTime) methods table */ +static PyMethodDef RealTime_methods[] = +{ + {"values", (PyCFunction)RealTime_values, METH_NOARGS, + PyDoc_STR("values() -> Tuple of sec,nsec representation.")}, + + {"toString", (PyCFunction)RealTime_toString, METH_NOARGS, + PyDoc_STR("toString() -> Return a user-readable string to the nearest millisecond in a form like HH:MM:SS.mmm")}, + + {"toFrame", (PyCFunction)RealTime_toFrame, METH_VARARGS, + PyDoc_STR("toFrame(samplerate) -> Sample count for given sample rate.")}, + + {"toFloat", (PyCFunction)RealTime_float, METH_NOARGS, + PyDoc_STR("toFloat() -> Floating point representation.")}, + + {NULL, NULL} /* sentinel */ +}; + + +/* Methods implementing protocols */ +// these functions are called by the interpreter + +/* Object Protocol */ + +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; +} + +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); +} + +/* 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()); +} + + +/* Number Protocol */ +/// TODO: implement all methods available in Vamp::RealTime() objects + +static PyObject * +RealTime_add(PyObject *s, PyObject *w) +{ + RealTimeObject *result = + PyObject_New(RealTimeObject, &RealTime_Type); + if (result == NULL) return NULL; + + result->rt = new 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( + *((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*/ +}; + +/* REAL-TIME TYPE OBJECT */ + +#define RealTime_alloc PyType_GenericAlloc +#define RealTime_free PyObject_Del + +/* Doc:: 10.3 Type Objects */ /* static */ +PyTypeObject RealTime_Type = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "vampy.RealTime", /*tp_name*/ + sizeof(RealTimeObject), /*tp_basicsize*/ + 0, /*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, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "RealTime object, used for Vamp plugin timestamps.", /*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*/ + RealTime_alloc, /*tp_alloc*/ + RealTime_new, /*tp_new*/ + RealTime_free, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + + + +/* PyRealTime C++ API */ + +/*PyRealTime from RealTime*/ +PyObject* +PyRealTime_FromRealTime(const Vamp::RealTime& rt) { + + RealTimeObject *self = + PyObject_New(RealTimeObject, &RealTime_Type); + if (self == NULL) return NULL; + + self->rt = new RealTime(rt); + return (PyObject*) self; +} + +/*RealTime* from PyRealTime*/ +const Vamp::RealTime* +PyRealTime_AsRealTime (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; +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/native/PyRealTime.h Wed Jan 14 08:30:47 2015 +0000 @@ -0,0 +1,60 @@ +/* -*- 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. +*/ + +#ifndef PYREALTIME_H +#define PYREALTIME_H + +#include <Python.h> +#include <vamp-hostsdk/RealTime.h> + +typedef struct { + PyObject_HEAD + Vamp::RealTime *rt; +} RealTimeObject; + +PyAPI_DATA(PyTypeObject) RealTime_Type; + +#define PyRealTime_Check(v) PyObject_TypeCheck(v, &RealTime_Type) +#define PyRealTime_AS_REALTIME(v) ((const RealTimeObject* const) (v))->rt + +PyAPI_FUNC(PyObject *) +PyRealTime_FromRealTime(const Vamp::RealTime&); + +PyAPI_FUNC(const Vamp::RealTime*) +PyRealTime_AsRealTime (PyObject *self); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/native/VectorConversion.cpp Wed Jan 14 08:30:47 2015 +0000 @@ -0,0 +1,318 @@ +/* -*- 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 <Python.h> + +#include "FloatConversion.h" +#include "VectorConversion.h" + +#include <math.h> +#include <float.h> + +using namespace std; + +/* Note: NO FUNCTION IN THIS CLASS SHOULD ALTER REFERENCE COUNTS + (EXCEPT FOR TEMPORARY PYTHON OBJECTS)! */ + +VectorConversion::VectorConversion() : + m_error(false), + error(m_error) // const public reference for easy access +{ +} + +VectorConversion::~VectorConversion() +{ +} + +/// floating point numbers (TODO: check numpy.float128) +float +VectorConversion::PyValue_To_Float(PyObject* pyValue) const +{ + if (FloatConversion::check(pyValue)) { + return FloatConversion::convert(pyValue); + } + + setValueError("Conversion error: object" + PyValue_Get_TypeName(pyValue) +" is not float, int, or long."); + return 0.0; +} + +vector<float> +VectorConversion::PyValue_To_FloatVector (PyObject *pyValue) const +{ + /// numpy array + if (PyArray_CheckExact(pyValue)) + return PyArray_To_FloatVector(pyValue); + + /// python list of floats (backward compatible) + if (PyList_Check(pyValue)) { + return PyList_To_FloatVector(pyValue); + } + + string msg = "Value is not list or array of floats"; + setValueError(msg); +#ifdef _DEBUG + cerr << "VectorConversion::PyValue_To_FloatVector failed. " << msg << endl; +#endif + return vector<float>(); +} + +vector<float> +VectorConversion::PyList_To_FloatVector (PyObject *inputList) const +{ + vector<float> v; + + if (!PyList_Check(inputList)) { + setValueError("Value is not a list"); + return v; + } + + PyObject **pyObjectArray = PySequence_Fast_ITEMS(inputList); + int n = PyList_GET_SIZE(inputList); + + for (int i = 0; i < n; ++i) { + v.push_back(PyValue_To_Float(pyObjectArray[i])); + } + + return v; +} + +vector<float> +VectorConversion::PyArray_To_FloatVector (PyObject *pyValue) const +{ + vector<float> v; + + if (!PyArray_Check(pyValue)) { + setValueError("Value is not an array"); + return v; + } + + PyArrayObject* pyArray = (PyArrayObject*) pyValue; + PyArray_Descr* descr = PyArray_DESCR(pyArray); + + if (PyArray_DATA(pyArray) == 0 || descr == 0) { + string msg = "NumPy array with NULL data or descriptor pointer encountered."; + setValueError(msg); + return v; + } + + if (PyArray_NDIM(pyArray) != 1) { + string msg = "NumPy array must be a one-dimensional vector."; + setValueError(msg); + return v; + } + + /// check strides (useful if array is not continuous) + size_t strides = *((size_t*) PyArray_STRIDES(pyArray)); + + /// convert the array + switch (descr->type_num) { + + case NPY_FLOAT : // dtype='float32' + return PyArray_Convert<float,float>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); + case NPY_DOUBLE : // dtype='float64' + return PyArray_Convert<float,double>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); + case NPY_INT : // dtype='int' + return PyArray_Convert<float,int>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); + case NPY_LONG : // dtype='long' + return PyArray_Convert<float,long>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); + default : + string msg = "Unsupported value type in NumPy array object."; + setValueError(msg); +#ifdef _DEBUG + cerr << "VectorConversion::PyArray_To_FloatVector failed. Error: " << msg << endl; +#endif + return v; + } +} + +vector<vector<float> > +VectorConversion::Py2DArray_To_FloatVector (PyObject *pyValue) const +{ + vector<vector<float> > v; + + if (!PyArray_Check(pyValue)) { + setValueError("Value is not an array"); + return v; + } + + PyArrayObject* pyArray = (PyArrayObject*) pyValue; + PyArray_Descr* descr = PyArray_DESCR(pyArray); + + if (PyArray_DATA(pyArray) == 0 || descr == 0) { + string msg = "NumPy array with NULL data or descriptor pointer encountered."; + setValueError(msg); + return v; + } + + if (PyArray_NDIM(pyArray) != 2) { + string msg = "NumPy array must be a two-dimensional matrix."; + setValueError(msg); + return v; + } + + /// check strides (useful if array is not continuous) + size_t *strideptr = (size_t*) PyArray_STRIDES(pyArray); + + /// convert the array + for (int i = 0; i < PyArray_DIMS(pyArray)[0]; ++i) { + + vector<float> vv; + + switch (descr->type_num) { + + case NPY_FLOAT : // dtype='float32' + vv = PyArray_Convert<float,float>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]); + break; + case NPY_DOUBLE : // dtype='float64' + vv = PyArray_Convert<float,double>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]); + break; + case NPY_INT : // dtype='int' + vv = PyArray_Convert<float,int>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]); + break; + case NPY_LONG : // dtype='long' + vv = PyArray_Convert<float,long>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]); + break; + default : + string msg = "Unsupported value type in NumPy array object."; + cerr << "VectorConversion::PyArray_To_FloatVector failed (value type = " << descr->type_num << "). Error: " << msg << endl; + setValueError(msg); + return v; + } + + v.push_back(vv); + } + + return v; +} + +PyObject * +VectorConversion::PyArray_From_FloatVector(const vector<float> &v) const +{ + npy_intp ndims[1]; + ndims[0] = (int)v.size(); + PyObject *arr = PyArray_SimpleNew(1, ndims, NPY_FLOAT); + float *data = (float *)PyArray_DATA((PyArrayObject *)arr); + for (int i = 0; i < ndims[0]; ++i) { + data[i] = v[i]; + } + return arr; +} + +PyObject * +VectorConversion::PyValue_From_StringVector(const vector<string> &v) const +{ + PyObject *pyList = PyList_New(v.size()); + for (size_t i = 0; i < v.size(); ++i) { + PyObject *pyStr = PyString_FromString(v[i].c_str()); + PyList_SET_ITEM(pyList, i, pyStr); + } + return pyList; +} + + +/* Error handling */ + +void +VectorConversion::setValueError (string message) const +{ + m_error = true; + m_errorQueue.push(ValueError(message)); +} + +/// return a reference to the last error or creates a new one. +ValueError& +VectorConversion::lastError() const +{ + m_error = false; + if (!m_errorQueue.empty()) return m_errorQueue.back(); + else { + m_errorQueue.push(ValueError("Type conversion error.")); + return m_errorQueue.back(); + } +} + +/// helper function to iterate over the error message queue: +/// pops the oldest item +ValueError +VectorConversion::getError() const +{ + if (!m_errorQueue.empty()) { + ValueError e = m_errorQueue.front(); + m_errorQueue.pop(); + if (m_errorQueue.empty()) m_error = false; + return e; + } + else { + m_error = false; + return ValueError(); + } +} + +/* Utilities */ + +/// get the type name of an object +string +VectorConversion::PyValue_Get_TypeName(PyObject* pyValue) const +{ + PyObject *pyType = PyObject_Type(pyValue); + if (!pyType) + { + cerr << "Warning: Object type name could not be found." << endl; + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + return string ("< unknown type >"); + } + PyObject *pyString = PyObject_Str(pyType); + if (!pyString) + { + cerr << "Warning: Object type name could not be found." << endl; + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + Py_CLEAR(pyType); + return string ("< unknown type >"); + } + char *cstr = PyString_AS_STRING(pyString); + if (!cstr) + { + cerr << "Warning: Object type name could not be found." << endl; + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + Py_DECREF(pyType); + Py_CLEAR(pyString); + return string("< unknown type >"); + } + Py_DECREF(pyType); + Py_DECREF(pyString); + return string(cstr); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/native/VectorConversion.h Wed Jan 14 08:30:47 2015 +0000 @@ -0,0 +1,139 @@ +/* -*- 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. +*/ + +/* + VectorConversion: A handful of type safe conversion utilities + between Python types and C++ vectors. +*/ + +#ifndef VAMPYHOST_VECTOR_CONVERSION_H +#define VAMPYHOST_VECTOR_CONVERSION_H + +#include <Python.h> + +// NumPy is required here +#define PY_ARRAY_UNIQUE_SYMBOL VAMPYHOST_ARRAY_API +#define NO_IMPORT_ARRAY +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include "numpy/arrayobject.h" + +#include <vector> +#include <queue> +#include <string> +#include <sstream> +#include <iostream> + +// Data +class ValueError +{ +public: + ValueError() {} + ValueError(std::string m) : message(m) {} + std::string location; + std::string message; + std::string str() const { + return (location.empty()) ? message : message + "\nLocation: " + location;} + template<typename V> ValueError &operator<< (const V& v) + { + std::ostringstream ss; + ss << v; + location += ss.str(); + return *this; + } +}; + +class VectorConversion +{ +public: + VectorConversion(); + ~VectorConversion(); + + ValueError getError() const; + + std::vector<float> PyValue_To_FloatVector (PyObject*) const; + std::vector<float> PyArray_To_FloatVector (PyObject *) const; + std::vector<float> PyList_To_FloatVector (PyObject*) const; + std::vector<std::vector<float> > Py2DArray_To_FloatVector (PyObject *) const; + + PyObject *PyValue_From_StringVector(const std::vector<std::string> &) const; + PyObject *PyArray_From_FloatVector(const std::vector<float> &) const; + +private: + std::string PyValue_Get_TypeName(PyObject*) const; + float PyValue_To_Float(PyObject*) const; + + /// Convert DTYPE type 1D NumpyArray to std::vector<RET> + template<typename RET, typename DTYPE> + std::vector<RET> PyArray_Convert(void* raw_data_ptr, + int length, + size_t strides) const { + + std::vector<RET> v(length); + + /// check if the array is continuous, if not use strides info + if (sizeof(DTYPE) != strides) { +#ifdef _DEBUG_VALUES + cerr << "Warning: discontinuous numpy array. Strides: " << strides << " bytes. sizeof(dtype): " << sizeof(DTYPE) << endl; +#endif + char* data = (char*) raw_data_ptr; + for (int i = 0; i < length; ++i){ + v[i] = (RET)(*((DTYPE*)data)); + data += strides; + } + return v; + } + + DTYPE* data = (DTYPE*) raw_data_ptr; + for (int i = 0; i < length; ++i){ + v[i] = (RET)data[i]; + } + + return v; + } + +private: + mutable bool m_error; + mutable std::queue<ValueError> m_errorQueue; + + void setValueError(std::string) const; + ValueError& lastError() const; + +public: + const bool& error; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/native/vampyhost-junk.cpp Wed Jan 14 08:30:47 2015 +0000 @@ -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/native/vampyhost.cpp Wed Jan 14 08:30:47 2015 +0000 @@ -0,0 +1,297 @@ +/* -*- 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 for python extension module: must be first +#include <Python.h> + +// define a unique API pointer +#define PY_ARRAY_UNIQUE_SYMBOL VAMPYHOST_ARRAY_API +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include "numpy/arrayobject.h" + +#include "PyRealTime.h" +#include "PyPluginObject.h" + +#include "vamp-hostsdk/PluginHostAdapter.h" +#include "vamp-hostsdk/PluginChannelAdapter.h" +#include "vamp-hostsdk/PluginInputDomainAdapter.h" +#include "vamp-hostsdk/PluginLoader.h" + +#include "VectorConversion.h" +#include "PyRealTime.h" + +#include <iostream> +#include <string> + +#include <cmath> + +using namespace std; +using namespace Vamp; +using namespace Vamp::HostExt; + +static PyObject * +enumeratePlugins(PyObject *self, PyObject *) +{ + PluginLoader *loader = PluginLoader::getInstance(); + vector<PluginLoader::PluginKey> plugins = loader->listPlugins(); + VectorConversion conv; + return conv.PyValue_From_StringVector(plugins); +} + +static PyObject * +getPluginPath(PyObject *self, PyObject *) +{ + vector<string> path = PluginHostAdapter::getPluginPath(); + VectorConversion 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 * +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 * +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); + + VectorConversion conv; + return conv.PyValue_From_StringVector(category); +} + +static PyObject * +getOutputList(PyObject *self, PyObject *args) +{ + PyObject *pyPluginKey; + + if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) { + PyErr_SetString(PyExc_TypeError, + "getOutputList() takes plugin key (string) argument"); + return 0; } + + Plugin::OutputList outputs; + + string pluginKey = toPluginKey(pyPluginKey); + if (pluginKey == "") return 0; + + PluginLoader *loader = PluginLoader::getInstance(); + + Plugin *plugin = loader->loadPlugin(pluginKey, 48000, 0); + if (!plugin) { + string pyerr("Failed to load plugin: "); pyerr += pluginKey; + PyErr_SetString(PyExc_TypeError,pyerr.c_str()); + return 0; + } + + outputs = 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 * +loadPlugin(PyObject *self, PyObject *args) +{ + PyObject *pyPluginKey; + float inputSampleRate; + int adapterFlags; + + if (!PyArg_ParseTuple(args, "Sfn", + &pyPluginKey, + &inputSampleRate, + &adapterFlags)) { + PyErr_SetString(PyExc_TypeError, + "loadPlugin() takes plugin key (string), sample rate (float), and adapter flags (int) arguments"); + return 0; } + + string pluginKey = toPluginKey(pyPluginKey); + if (pluginKey == "") return 0; + + PluginLoader *loader = PluginLoader::getInstance(); + + Plugin *plugin = loader->loadPlugin(pluginKey, + inputSampleRate, + adapterFlags); + if (!plugin) { + string pyerr("Failed to load plugin: "); pyerr += pluginKey; + PyErr_SetString(PyExc_TypeError,pyerr.c_str()); + return 0; + } + + return PyPluginObject_From_Plugin(plugin); +} + +// module methods table +static PyMethodDef vampyhost_methods[] = { + + {"listPlugins", enumeratePlugins, METH_NOARGS, + "listPlugins() -> Return a list of the plugin keys of all installed Vamp plugins." }, + + {"getPluginPath", getPluginPath, METH_NOARGS, + "getPluginPath() -> Return a list of directories which will be searched for Vamp plugins. This may be changed by setting the VAMP_PATH environment variable."}, + + {"getCategoryOf", getPluginCategory, METH_VARARGS, + "getCategoryOf(pluginKey) -> Return the category of a Vamp plugin given its key, if known. The category is expressed as a list of nested types from least to most specific."}, + + {"getLibraryFor", getLibraryFor, METH_VARARGS, + "getLibraryFor(pluginKey) -> Return the file path of the Vamp plugin library in which the given plugin key is found, or an empty string if the plugin is not installed."}, + + {"getOutputsOf", getOutputList, METH_VARARGS, + "getOutputsOf(pluginKey) -> Return a list of the output identifiers of the plugin with the given key, if installed."}, + + {"loadPlugin", loadPlugin, METH_VARARGS, + "loadPlugin(pluginKey, samplerate) -> Load the plugin that has the given key, if installed, and return the plugin object."}, + + {0, 0} /* sentinel */ +}; + +PyDoc_STRVAR(module_doc, "Load and run Vamp audio analysis plugins."); + +static int +setint(PyObject *d, const char *name, int value) +{ + PyObject *v; + int err; + v = PyInt_FromLong((long)value); + err = PyDict_SetItemString(d, name, v); + Py_XDECREF(v); + return err; +} + +/* Initialization function for the module (*must* be called initxx) */ + +// module initialization (includes extern C {...} as necessary) +PyMODINIT_FUNC +initvampyhost(void) +{ + PyObject *m; + + if (PyType_Ready(&RealTime_Type) < 0) return; + if (PyType_Ready(&Plugin_Type) < 0) return; + + m = Py_InitModule3("vampyhost", vampyhost_methods, module_doc); + if (!m) { + cerr << "ERROR: initvampyhost: Failed to initialise module" << endl; + return; + } + + import_array(); + + PyModule_AddObject(m, "RealTime", (PyObject *)&RealTime_Type); + PyModule_AddObject(m, "Plugin", (PyObject *)&Plugin_Type); + + // Some enum types + PyObject *dict = PyModule_GetDict(m); + if (!dict) { + cerr << "ERROR: initvampyhost: Failed to obtain module dictionary" << endl; + return; + } + + if (setint(dict, "OneSamplePerStep", + Plugin::OutputDescriptor::OneSamplePerStep) < 0 || + setint(dict, "FixedSampleRate", + Plugin::OutputDescriptor::FixedSampleRate) < 0 || + setint(dict, "VariableSampleRate", + Plugin::OutputDescriptor::VariableSampleRate) < 0 || + setint(dict, "TimeDomain", + Plugin::TimeDomain) < 0 || + setint(dict, "FrequencyDomain", + Plugin::FrequencyDomain) < 0 || + setint(dict, "AdaptNone", + 0) < 0 || + setint(dict, "AdaptChannelCount", + PluginLoader::ADAPT_CHANNEL_COUNT) < 0 || + setint(dict, "AdaptBufferSize", + PluginLoader::ADAPT_BUFFER_SIZE) < 0 || + setint(dict, "AdaptAllSafe", + PluginLoader::ADAPT_ALL_SAFE) < 0 || + setint(dict, "AdaptAll", + PluginLoader::ADAPT_ALL) < 0) { + cerr << "ERROR: initvampyhost: Failed to add enums to module dictionary" << endl; + return; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/test_metadata.py Wed Jan 14 08:30:47 2015 +0000 @@ -0,0 +1,28 @@ + +import vampyhost as vh + +testPluginKey = "vamp-test-plugin:vamp-test-plugin" + +##!!! could use: plugin version + +def test_list(): + plugins = vh.listPlugins() + if testPluginKey not in plugins: + print("Test plugin " + testPluginKey + " not installed or not returned by enumerate: can't run any tests without it") + assert testPluginKey in plugins + +def test_path(): + path = vh.getPluginPath() + assert len(path) > 0 + +def test_getlibrary(): + lib = vh.getLibraryFor(testPluginKey) + assert lib.find("vamp-test-plugin") >= 0 + try: + lib = vh.getLibraryFor("not a well-formatted plugin key") + assert False + except TypeError: + pass + lib = vh.getLibraryFor("nonexistent-library:nonexistent-plugin") + assert lib == "" +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/test_plugin_metadata.py Wed Jan 14 08:30:47 2015 +0000 @@ -0,0 +1,59 @@ + +import vampyhost as vh + +testPluginKey = "vamp-test-plugin:vamp-test-plugin" + +rate = 44100 + +def test_getoutputlist(): + outputs = vh.getOutputsOf(testPluginKey) + assert len(outputs) == 9 + assert "input-summary" in outputs + +def test_inputdomain(): + plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) + assert plug.inputDomain == vh.TimeDomain + +def test_info(): + plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) + assert plug.info["identifier"] == "vamp-test-plugin" + +def test_parameterdescriptors(): + plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) + assert plug.parameters[0]["identifier"] == "produce_output" + +def test_setparameter(): + plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) + assert plug.parameters[0]["identifier"] == "produce_output" + assert plug.parameters[0]["defaultValue"] == 1 + assert plug.getParameterValue("produce_output") == plug.parameters[0]["defaultValue"] + assert plug.setParameterValue("produce_output", 0) == True + assert plug.getParameterValue("produce_output") == 0 + assert plug.setParameterValues({ "produce_output": 1 }) == True + assert plug.getParameterValue("produce_output") == 1 + try: + plug.setParameterValue("produce_output", "fish") + assert False + except TypeError: + pass + try: + plug.setParameterValue(4, 0) + assert False + except TypeError: + pass + try: + plug.setParameterValue("steak", 0) + assert False + except StandardError: + pass + try: + plug.getParameterValue(4) + assert False + except TypeError: + pass + try: + plug.getParameterValue("steak") + assert False + except StandardError: + pass +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/test_process.py Wed Jan 14 08:30:47 2015 +0000 @@ -0,0 +1,116 @@ + +import vampyhost as vh +import numpy as np + +testPluginKey = "vamp-test-plugin:vamp-test-plugin" + +##!!! todo: support for, and test for, correct version of test plugin (with parameter) + +rate = 44100 + +def test_load_unload(): + plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) + plug.unload() + try: + plug.unload() # should throw but not crash + assert(False) + except AttributeError: + pass + +def test_get_set_parameter(): + plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) + value = plug.getParameterValue("produce_output") + assert(value == 1.0) + plug.setParameterValue("produce_output", 0.0) + value = plug.getParameterValue("produce_output") + assert(value == 0.0) + +def test_process_without_initialise(): + plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) + try: + plug.process([[1,2,3,4]], vh.RealTime(0, 0)) + assert False + except StandardError: + pass + +def test_process_input_format(): + plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) + plug.initialise(2, 4, 4) # channels, stepsize, blocksize + result = plug.process([[1,2,3,4],[5,6,7,8]], vh.RealTime(0, 0)) + result = plug.process([np.array([1,2,3,4]),np.array([5,6,7,8])], vh.RealTime(0, 0)) + result = plug.process(np.array([[1,2,3,4],[5,6,7,8]]), vh.RealTime(0, 0)) + try: + # Wrong number of channels + result = plug.process(np.array([[1,2,3,4]]), vh.RealTime(0, 0)) + assert False + except TypeError: + pass + try: + # Wrong number of samples per channel + result = plug.process(np.array([[1,2,3],[4,5,6]]), vh.RealTime(0, 0)) + assert False + except TypeError: + pass + try: + # Differing numbers of samples per channel + result = plug.process(np.array([[1,2,3,4],[5,6,7]]), vh.RealTime(0, 0)) + assert False + except TypeError: + pass + +def test_process_output_1ch(): + plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) + plug.initialise(1, 2, 2) + try: + # Too many channels + result = plug.process([[3,4],[5,6]], vh.RealTime(0, 0)) + assert False + except TypeError: + pass + result = plug.process([[3,3]], vh.RealTime(0, 0)) + assert result[8] == [ { "label" : "", "values" : np.array([5.0]) } ] + result = plug.process([[3,0]], vh.RealTime(0, 0)) + assert result[8] == [ { "label" : "", "values" : np.array([4.0]) } ] + +def test_process_output_2ch(): + plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) + plug.initialise(2, 2, 2) + try: + # Too few channels + result = plug.process([[3,4]], vh.RealTime(0, 0)) + assert False + except TypeError: + pass + try: + # Too many channels + result = plug.process([[3,4],[5,6],[7,8]], vh.RealTime(0, 0)) + assert False + except TypeError: + pass + result = plug.process([[3,3],[4,4]], vh.RealTime(0, 0)) + assert (result[8][0]["values"] == np.array([5.0,6.0])).all() + result = plug.process([[3,0],[4,0]], vh.RealTime(0, 0)) + assert (result[8][0]["values"] == np.array([4.0,5.0])).all() + +def test_process_output_3ch(): + plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) + plug.initialise(3, 2, 2) + try: + # Too few channels + result = plug.process([[3,4],[5,6]], vh.RealTime(0, 0)) + assert False + except TypeError: + pass + try: + # Too many channels + result = plug.process([[3,4],[5,6],[7,8],[9,10]], vh.RealTime(0, 0)) + assert False + except TypeError: + pass + result = plug.process([[3,3],[4,4],[5,5]], vh.RealTime(0, 0)) + assert (result[8][0]["values"] == np.array([5.0,6.0,7.0])).all() + result = plug.process([[3,0],[4,0],[5,0]], vh.RealTime(0, 0)) + assert (result[8][0]["values"] == np.array([4.0,5.0,6.0])).all() + + +
--- a/test_metadata.py Tue Jan 13 12:16:38 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ - -import vampyhost as vh - -testPluginKey = "vamp-test-plugin:vamp-test-plugin" - -##!!! could use: plugin version - -def test_list(): - plugins = vh.listPlugins() - if testPluginKey not in plugins: - print("Test plugin " + testPluginKey + " not installed or not returned by enumerate: can't run any tests without it") - assert testPluginKey in plugins - -def test_path(): - path = vh.getPluginPath() - assert len(path) > 0 - -def test_getlibrary(): - lib = vh.getLibraryFor(testPluginKey) - assert lib.find("vamp-test-plugin") >= 0 - try: - lib = vh.getLibraryFor("not a well-formatted plugin key") - assert False - except TypeError: - pass - lib = vh.getLibraryFor("nonexistent-library:nonexistent-plugin") - assert lib == "" -
--- a/test_plugin_metadata.py Tue Jan 13 12:16:38 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ - -import vampyhost as vh - -testPluginKey = "vamp-test-plugin:vamp-test-plugin" - -rate = 44100 - -def test_getoutputlist(): - outputs = vh.getOutputsOf(testPluginKey) - assert len(outputs) == 9 - assert "input-summary" in outputs - -def test_inputdomain(): - plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) - assert plug.inputDomain == vh.TimeDomain - -def test_info(): - plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) - assert plug.info["identifier"] == "vamp-test-plugin" - -def test_parameterdescriptors(): - plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) - assert plug.parameters[0]["identifier"] == "produce_output" - -def test_setparameter(): - plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) - assert plug.parameters[0]["identifier"] == "produce_output" - assert plug.parameters[0]["defaultValue"] == 1 - assert plug.getParameterValue("produce_output") == plug.parameters[0]["defaultValue"] - assert plug.setParameterValue("produce_output", 0) == True - assert plug.getParameterValue("produce_output") == 0 - assert plug.setParameterValues({ "produce_output": 1 }) == True - assert plug.getParameterValue("produce_output") == 1 - try: - plug.setParameterValue("produce_output", "fish") - assert False - except TypeError: - pass - try: - plug.setParameterValue(4, 0) - assert False - except TypeError: - pass - try: - plug.setParameterValue("steak", 0) - assert False - except StandardError: - pass - try: - plug.getParameterValue(4) - assert False - except TypeError: - pass - try: - plug.getParameterValue("steak") - assert False - except StandardError: - pass -
--- a/test_process.py Tue Jan 13 12:16:38 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ - -import vampyhost as vh -import numpy as np - -testPluginKey = "vamp-test-plugin:vamp-test-plugin" - -##!!! todo: support for, and test for, correct version of test plugin (with parameter) - -rate = 44100 - -def test_load_unload(): - plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) - plug.unload() - try: - plug.unload() # should throw but not crash - assert(False) - except AttributeError: - pass - -def test_get_set_parameter(): - plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) - value = plug.getParameterValue("produce_output") - assert(value == 1.0) - plug.setParameterValue("produce_output", 0.0) - value = plug.getParameterValue("produce_output") - assert(value == 0.0) - -def test_process_without_initialise(): - plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) - try: - plug.process([[1,2,3,4]], vh.RealTime(0, 0)) - assert False - except StandardError: - pass - -def test_process_input_format(): - plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) - plug.initialise(2, 4, 4) # channels, stepsize, blocksize - result = plug.process([[1,2,3,4],[5,6,7,8]], vh.RealTime(0, 0)) - result = plug.process([np.array([1,2,3,4]),np.array([5,6,7,8])], vh.RealTime(0, 0)) - result = plug.process(np.array([[1,2,3,4],[5,6,7,8]]), vh.RealTime(0, 0)) - try: - # Wrong number of channels - result = plug.process(np.array([[1,2,3,4]]), vh.RealTime(0, 0)) - assert False - except TypeError: - pass - try: - # Wrong number of samples per channel - result = plug.process(np.array([[1,2,3],[4,5,6]]), vh.RealTime(0, 0)) - assert False - except TypeError: - pass - try: - # Differing numbers of samples per channel - result = plug.process(np.array([[1,2,3,4],[5,6,7]]), vh.RealTime(0, 0)) - assert False - except TypeError: - pass - -def test_process_output_1ch(): - plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) - plug.initialise(1, 2, 2) - try: - # Too many channels - result = plug.process([[3,4],[5,6]], vh.RealTime(0, 0)) - assert False - except TypeError: - pass - result = plug.process([[3,3]], vh.RealTime(0, 0)) - assert result[8] == [ { "label" : "", "values" : np.array([5.0]) } ] - result = plug.process([[3,0]], vh.RealTime(0, 0)) - assert result[8] == [ { "label" : "", "values" : np.array([4.0]) } ] - -def test_process_output_2ch(): - plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) - plug.initialise(2, 2, 2) - try: - # Too few channels - result = plug.process([[3,4]], vh.RealTime(0, 0)) - assert False - except TypeError: - pass - try: - # Too many channels - result = plug.process([[3,4],[5,6],[7,8]], vh.RealTime(0, 0)) - assert False - except TypeError: - pass - result = plug.process([[3,3],[4,4]], vh.RealTime(0, 0)) - assert (result[8][0]["values"] == np.array([5.0,6.0])).all() - result = plug.process([[3,0],[4,0]], vh.RealTime(0, 0)) - assert (result[8][0]["values"] == np.array([4.0,5.0])).all() - -def test_process_output_3ch(): - plug = vh.loadPlugin(testPluginKey, rate, vh.AdaptNone) - plug.initialise(3, 2, 2) - try: - # Too few channels - result = plug.process([[3,4],[5,6]], vh.RealTime(0, 0)) - assert False - except TypeError: - pass - try: - # Too many channels - result = plug.process([[3,4],[5,6],[7,8],[9,10]], vh.RealTime(0, 0)) - assert False - except TypeError: - pass - result = plug.process([[3,3],[4,4],[5,5]], vh.RealTime(0, 0)) - assert (result[8][0]["values"] == np.array([5.0,6.0,7.0])).all() - result = plug.process([[3,0],[4,0],[5,0]], vh.RealTime(0, 0)) - assert (result[8][0]["values"] == np.array([4.0,5.0,6.0])).all() - - -
--- a/vampyhost-junk.cpp Tue Jan 13 12:16:38 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,170 +0,0 @@ -/* -*- 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; - -}
--- a/vampyhost.cpp Tue Jan 13 12:16:38 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,297 +0,0 @@ -/* -*- 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 for python extension module: must be first -#include <Python.h> - -// define a unique API pointer -#define PY_ARRAY_UNIQUE_SYMBOL VAMPYHOST_ARRAY_API -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -#include "numpy/arrayobject.h" - -#include "PyRealTime.h" -#include "PyPluginObject.h" - -#include "vamp-hostsdk/PluginHostAdapter.h" -#include "vamp-hostsdk/PluginChannelAdapter.h" -#include "vamp-hostsdk/PluginInputDomainAdapter.h" -#include "vamp-hostsdk/PluginLoader.h" - -#include "VectorConversion.h" -#include "PyRealTime.h" - -#include <iostream> -#include <string> - -#include <cmath> - -using namespace std; -using namespace Vamp; -using namespace Vamp::HostExt; - -static PyObject * -enumeratePlugins(PyObject *self, PyObject *) -{ - PluginLoader *loader = PluginLoader::getInstance(); - vector<PluginLoader::PluginKey> plugins = loader->listPlugins(); - VectorConversion conv; - return conv.PyValue_From_StringVector(plugins); -} - -static PyObject * -getPluginPath(PyObject *self, PyObject *) -{ - vector<string> path = PluginHostAdapter::getPluginPath(); - VectorConversion 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 * -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 * -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); - - VectorConversion conv; - return conv.PyValue_From_StringVector(category); -} - -static PyObject * -getOutputList(PyObject *self, PyObject *args) -{ - PyObject *pyPluginKey; - - if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) { - PyErr_SetString(PyExc_TypeError, - "getOutputList() takes plugin key (string) argument"); - return 0; } - - Plugin::OutputList outputs; - - string pluginKey = toPluginKey(pyPluginKey); - if (pluginKey == "") return 0; - - PluginLoader *loader = PluginLoader::getInstance(); - - Plugin *plugin = loader->loadPlugin(pluginKey, 48000, 0); - if (!plugin) { - string pyerr("Failed to load plugin: "); pyerr += pluginKey; - PyErr_SetString(PyExc_TypeError,pyerr.c_str()); - return 0; - } - - outputs = 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 * -loadPlugin(PyObject *self, PyObject *args) -{ - PyObject *pyPluginKey; - float inputSampleRate; - int adapterFlags; - - if (!PyArg_ParseTuple(args, "Sfn", - &pyPluginKey, - &inputSampleRate, - &adapterFlags)) { - PyErr_SetString(PyExc_TypeError, - "loadPlugin() takes plugin key (string), sample rate (float), and adapter flags (int) arguments"); - return 0; } - - string pluginKey = toPluginKey(pyPluginKey); - if (pluginKey == "") return 0; - - PluginLoader *loader = PluginLoader::getInstance(); - - Plugin *plugin = loader->loadPlugin(pluginKey, - inputSampleRate, - adapterFlags); - if (!plugin) { - string pyerr("Failed to load plugin: "); pyerr += pluginKey; - PyErr_SetString(PyExc_TypeError,pyerr.c_str()); - return 0; - } - - return PyPluginObject_From_Plugin(plugin); -} - -// module methods table -static PyMethodDef vampyhost_methods[] = { - - {"listPlugins", enumeratePlugins, METH_NOARGS, - "listPlugins() -> Return a list of the plugin keys of all installed Vamp plugins." }, - - {"getPluginPath", getPluginPath, METH_NOARGS, - "getPluginPath() -> Return a list of directories which will be searched for Vamp plugins. This may be changed by setting the VAMP_PATH environment variable."}, - - {"getCategoryOf", getPluginCategory, METH_VARARGS, - "getCategoryOf(pluginKey) -> Return the category of a Vamp plugin given its key, if known. The category is expressed as a list of nested types from least to most specific."}, - - {"getLibraryFor", getLibraryFor, METH_VARARGS, - "getLibraryFor(pluginKey) -> Return the file path of the Vamp plugin library in which the given plugin key is found, or an empty string if the plugin is not installed."}, - - {"getOutputsOf", getOutputList, METH_VARARGS, - "getOutputsOf(pluginKey) -> Return a list of the output identifiers of the plugin with the given key, if installed."}, - - {"loadPlugin", loadPlugin, METH_VARARGS, - "loadPlugin(pluginKey, samplerate) -> Load the plugin that has the given key, if installed, and return the plugin object."}, - - {0, 0} /* sentinel */ -}; - -PyDoc_STRVAR(module_doc, "Load and run Vamp audio analysis plugins."); - -static int -setint(PyObject *d, const char *name, int value) -{ - PyObject *v; - int err; - v = PyInt_FromLong((long)value); - err = PyDict_SetItemString(d, name, v); - Py_XDECREF(v); - return err; -} - -/* Initialization function for the module (*must* be called initxx) */ - -// module initialization (includes extern C {...} as necessary) -PyMODINIT_FUNC -initvampyhost(void) -{ - PyObject *m; - - if (PyType_Ready(&RealTime_Type) < 0) return; - if (PyType_Ready(&Plugin_Type) < 0) return; - - m = Py_InitModule3("vampyhost", vampyhost_methods, module_doc); - if (!m) { - cerr << "ERROR: initvampyhost: Failed to initialise module" << endl; - return; - } - - import_array(); - - PyModule_AddObject(m, "RealTime", (PyObject *)&RealTime_Type); - PyModule_AddObject(m, "Plugin", (PyObject *)&Plugin_Type); - - // Some enum types - PyObject *dict = PyModule_GetDict(m); - if (!dict) { - cerr << "ERROR: initvampyhost: Failed to obtain module dictionary" << endl; - return; - } - - if (setint(dict, "OneSamplePerStep", - Plugin::OutputDescriptor::OneSamplePerStep) < 0 || - setint(dict, "FixedSampleRate", - Plugin::OutputDescriptor::FixedSampleRate) < 0 || - setint(dict, "VariableSampleRate", - Plugin::OutputDescriptor::VariableSampleRate) < 0 || - setint(dict, "TimeDomain", - Plugin::TimeDomain) < 0 || - setint(dict, "FrequencyDomain", - Plugin::FrequencyDomain) < 0 || - setint(dict, "AdaptNone", - 0) < 0 || - setint(dict, "AdaptChannelCount", - PluginLoader::ADAPT_CHANNEL_COUNT) < 0 || - setint(dict, "AdaptBufferSize", - PluginLoader::ADAPT_BUFFER_SIZE) < 0 || - setint(dict, "AdaptAllSafe", - PluginLoader::ADAPT_ALL_SAFE) < 0 || - setint(dict, "AdaptAll", - PluginLoader::ADAPT_ALL) < 0) { - cerr << "ERROR: initvampyhost: Failed to add enums to module dictionary" << endl; - return; - } -}