# HG changeset patch # User fazekasgy # Date 1213375800 0 # Node ID 3af6b5990ad825908588c8732f30e4acaadfd6de # Parent a4c955e9a70b4328f9ddfa392f83588d17c718d8 more examples and some bug fixes diff -r a4c955e9a70b -r 3af6b5990ad8 Example VamPy plugins/PyZeroCrossing.py --- a/Example VamPy plugins/PyZeroCrossing.py Wed Jun 11 16:04:52 2008 +0000 +++ b/Example VamPy plugins/PyZeroCrossing.py Fri Jun 13 16:50:00 2008 +0000 @@ -1,9 +1,6 @@ '''PyZeroCrossing.py - Example plugin demonstrates''' '''how to call a python class using the VamPy Vamp plugin''' -#from time import * -#import sys - class PyZeroCrossing: def __init__(self): @@ -12,7 +9,7 @@ self.m_blockSize = 0 self.m_channels = 0 self.previousSample = 0.0 - self.threshold = 0.05 + self.threshold = 0.01 def initialise(self,channels,stepSize,blockSize,inputSampleRate): self.m_channels = channels @@ -25,7 +22,7 @@ return 'VamPy Example Plugins' def getName(self): - return 'Zero Crossing (VamPy)' + return 'Vampy Zero Crossings' def getIdentifier(self): return 'python-zc' @@ -78,7 +75,7 @@ 'unit':'v', 'minValue':0.0, 'maxValue':0.5, - 'defaultValue':0.05, + 'defaultValue':0.005, 'isQuantized':False } return [paramlist1] @@ -94,10 +91,11 @@ else: return 0.0 - def process(self,inbuf): + def process(self,inbuf,timestamp): crossing = False prev = self.previousSample count = 0.0; + channel = inbuf[0] #we have two outputs defined thus we have to declare #them as empty dictionaries in our output list @@ -105,11 +103,11 @@ output0=[] output1=[] - if abs(sum(inbuf)) > self.threshold : - for x in range(len(inbuf)-1) : - + if sum([abs(s) for s in channel]) > self.threshold : + + for x in range(len(channel)-1) : crossing = False - sample = inbuf[x] + sample = channel[x] if sample <= 0.0 : if prev > 0.0 : crossing = True else : @@ -121,20 +119,22 @@ feature1={ 'hasTimestamp':True, #for now return sample position and convert to RealTime in C code - 'timeStamp':x - #'values':[count] - #'label':label + 'timeStamp':long(timestamp + x), + 'values':[count], + 'label':str(count), } output1.append(feature1) prev = sample self.previousSample = prev + else : count = 0.0 - self.previousSample = inbuf[len(inbuf)-1] + self.previousSample = channel[len(channel)-1] feature0={ 'hasTimestamp':False, + #'timeStamp':timestamp, 'values':[count], #strictly must be a list 'label':str(count) } diff -r a4c955e9a70b -r 3af6b5990ad8 Makefile --- a/Makefile Wed Jun 11 16:04:52 2008 +0000 +++ b/Makefile Fri Jun 13 16:50:00 2008 +0000 @@ -20,7 +20,7 @@ mkdir -p $(INSTALL_DIR) rm -f $(INSTALL_DIR)/$(PLUGIN_NAME)$(PLUGIN_EXT) cp $(PLUGIN_NAME)$(PLUGIN_EXT) $(INSTALL_DIR)/$(PLUGIN_NAME)$(PLUGIN_EXT) - cp $(PYEXAMPLE_DIR)/*.py $(INSTALL_DIR) +# cp $(PYEXAMPLE_DIR)/*.py $(INSTALL_DIR) installplug : install cleanplug : clean diff -r a4c955e9a70b -r 3af6b5990ad8 PyPlugScanner.cpp --- a/PyPlugScanner.cpp Wed Jun 11 16:04:52 2008 +0000 +++ b/PyPlugScanner.cpp Fri Jun 13 16:50:00 2008 +0000 @@ -140,7 +140,13 @@ PyObject *pyInstance = PyObject_CallObject(pyClass, NULL); //cerr << "__(getInstance) PyPlugin Class: " << m_class << " successfully created.__" << endl; return pyInstance; - } else return NULL; + } + else { + cerr << "ERROR: callable plugin class could not be found in source: " << classname << endl + << "Hint: plugin source filename and plugin class name must be the same." << endl; + PyErr_Print(); + return NULL; + } } diff -r a4c955e9a70b -r 3af6b5990ad8 PyPlugin.cpp --- a/PyPlugin.cpp Wed Jun 11 16:04:52 2008 +0000 +++ b/PyPlugin.cpp Fri Jun 13 16:50:00 2008 +0000 @@ -35,7 +35,7 @@ /** - * This Vamp plugin is a wrapper for Python Scripts. (vampy) + * This Vamp plugin is a wrapper for Python Scripts. (VamPy) * Centre for Digital Music, Queen Mary, University of London. * Copyright 2008, George Fazekas. @@ -258,6 +258,9 @@ bool PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize) { + //useful for debugging Python plugins + char method[]="initialise"; + cerr << "[call] " << method << endl; //placing Mutex before these calls causes deadlock if (channels < getMinChannelCount() || @@ -267,10 +270,6 @@ MutexLocker locker(&m_pythonInterpreterMutex); - //useful for debugging Python plugins - char method[]="initialise"; - cerr << "[call] " << method << endl; - initMaps(); m_stepSize = stepSize; @@ -280,15 +279,16 @@ //quering process implementation type char legacyMethod[]="process"; char numpyMethod[]="processN"; - m_processType = 0; - if (PyObject_HasAttrString(m_pyInstance,legacyMethod)) + if (PyObject_HasAttrString(m_pyInstance,legacyMethod) & + m_processType == 0) { m_processType = legacyProcess; m_pyProcess = PyString_FromString(legacyMethod); } - if (PyObject_HasAttrString(m_pyInstance,numpyMethod)) + if (PyObject_HasAttrString(m_pyInstance,numpyMethod) & + m_processType == 0) { m_processType = numpyProcess; m_pyProcess = PyString_FromString(numpyMethod); @@ -315,7 +315,7 @@ //Call the method PyObject *pyBool = PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyChannels,pyStepSize,pyBlockSize,pyInputSampleRate,NULL); - + Py_DECREF(pyMethod); Py_DECREF(pyChannels); Py_DECREF(pyStepSize); @@ -323,7 +323,8 @@ Py_DECREF(pyInputSampleRate); //Check return value - if (!PyBool_Check(pyBool)) { + if (PyErr_Occurred() || !PyBool_Check(pyBool)) { + PyErr_Print(); PyErr_Clear(); Py_CLEAR(pyBool); cerr << "ERROR: In Python plugin [" << m_class << "::" << method << "] Expected Bool return value." << endl; @@ -415,7 +416,7 @@ char method[]="getPreferredStepSize"; cerr << "[call] " << method << endl; - size_t rValue=0; //not set by default + size_t rValue=1024; //not set by default if ( PyObject_HasAttrString(m_pyInstance,method) ) { PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); @@ -515,7 +516,7 @@ //Parse Output List for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { - //Get i-th Vamp output descriptor (Borrowed Reference) + //Get i-th VAMP output descriptor (Borrowed Reference) pyDict = PyList_GET_ITEM(pyList,i); //We only care about dictionaries holding output descriptors @@ -530,7 +531,7 @@ switch (outKeys[PyString_AsString(pyKey)]) { case not_found : - cerr << "Unknown key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; + cerr << "Unknown key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl; break; case identifier: od.identifier = PyString_AsString(pyValue); @@ -573,9 +574,11 @@ break; case sampleRate: od.sampleRate = (float) PyFloat_AS_DOUBLE(pyValue); +// od.sampleRate = m_inputSampleRate / m_stepSize; + cerr << od.sampleRate << endl; break; default : - cerr << "Invalid key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; + cerr << "Invalid key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl; } } // while dict list.push_back(od); @@ -616,7 +619,7 @@ //Parse Output List for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { - //Get i-th Vamp output descriptor (Borrowed Reference) + //Get i-th VAMP output descriptor (Borrowed Reference) pyDict = PyList_GET_ITEM(pyList,i); //We only care about dictionaries holding output descriptors @@ -631,7 +634,7 @@ switch (parmKeys[PyString_AsString(pyKey)]) { case not_found : - cerr << "Unknown key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; + cerr << "Unknown key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl; break; case p::identifier: pd.identifier = PyString_AsString(pyValue); @@ -658,7 +661,7 @@ pd.isQuantized = (bool) PyInt_AS_LONG(pyValue); break; default : - cerr << "Invalid key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; + cerr << "Invalid key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl; } } // while dict list.push_back(pd); @@ -750,7 +753,7 @@ proccounter++; #endif - if (m_blockSize == 0) { + if (m_blockSize == 0 || m_channels == 0) { cerr << "ERROR: PyPlugin::process: " << "Plugin has not been initialised" << endl; return FeatureSet(); @@ -764,49 +767,77 @@ string method=PyString_AsString(m_pyProcess); - - PyObject *pyOutputList = NULL; /*new numPy support*/ if (m_processType == numpyProcess) { + + //create a list of buffers + PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels); + for (size_t i=0; i < m_channels; ++i) { - //declare buffer object - PyObject *pyBuffer; + //Expose memory using the Buffer Interface of C/API + //This will virtually pass a pointer which can be + //recasted in Python code as float or complex array + PyObject *pyBuffer = PyBuffer_FromMemory + ((void *) (float *) inputBuffers[i], + (Py_ssize_t) sizeof(float) * m_blockSize); - //Expose memory using the Buffer Interface of C/API - //This will virtually pass a pointer only that can be - //recasted in Python code - pyBuffer = - PyBuffer_FromMemory((void *) (float *) inputBuffers[0], - (Py_ssize_t) sizeof(float) * m_blockSize); + PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyBuffer); + } + + //pass RealTime as frameCount + PyObject *pyLongSample = PyLong_FromLong ( + Vamp::RealTime::realTime2Frame + (timestamp, (unsigned int) m_inputSampleRate)); - //Call python process (returns new reference) - pyOutputList = - PyObject_CallMethodObjArgs(m_pyInstance,m_pyProcess,pyBuffer,NULL); + //Call python process (returns new reference) + pyOutputList = PyObject_CallMethodObjArgs + (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL); + + Py_DECREF(pyChannelList); + Py_DECREF(pyLongSample); } if (m_processType == legacyProcess) { - //Declare new list object - PyObject *pyFloat, *pyList; - pyList = PyList_New((Py_ssize_t) m_blockSize); + //create a list of lists + PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels); + for (size_t i=0; i < m_channels; ++i) { - //Pack samples into a Python List Object - //pyFloat types will always be new references, - //these will be discarded when the list is deallocated - for (size_t i = 0; i < m_blockSize; ++i) { - pyFloat=PyFloat_FromDouble((double) inputBuffers[0][i]); - PyList_SET_ITEM(pyList, (Py_ssize_t) i, pyFloat); + //Declare new list object + PyObject *pyFloat, *pyList; + pyList = PyList_New((Py_ssize_t) m_blockSize); + + //Pack samples into a Python List Object + //pyFloat types will always be new references, + //these will be discarded when the list is deallocated + for (size_t j = 0; j < m_blockSize; ++j) { + pyFloat=PyFloat_FromDouble( + (double) inputBuffers[i][j]); + PyList_SET_ITEM(pyList, (Py_ssize_t) j, pyFloat); + } + PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyList); + } + + //pass RealTime as frameCount + PyObject *pyLongSample = PyLong_FromLong ( + Vamp::RealTime::realTime2Frame + (timestamp, (unsigned int) m_inputSampleRate)); + + //Call python process (returns new reference) + pyOutputList = PyObject_CallMethodObjArgs + (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL); + + Py_DECREF(pyChannelList); + Py_DECREF(pyLongSample); + } - //Call python process (returns new reference) - pyOutputList = - PyObject_CallMethodObjArgs(m_pyInstance,m_pyProcess,pyList,NULL); - - } - + //return nothing + //Py_CLEAR(pyOutputList); + //return FeatureSet(); //Check return type if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) { @@ -818,12 +849,10 @@ cerr << "ERROR: In Python plugin [" << m_class << "::" << method << "] Expected List return type." << endl; } - //Py_CLEAR(pyMethod); Py_CLEAR(pyOutputList); return FeatureSet(); } - //Py_DECREF(pyMethod); // Py_DECREF(pyList); // This appears to be tracked by the cyclic garbage collector // hence decrefing produces GC error @@ -864,16 +893,22 @@ switch (ffKeys[PyString_AsString(pyKey)]) { case not_found : - cerr << "Unknown key in Vamp FeatureSet: " + cerr << "Unknown key in VAMP FeatureSet: " << PyString_AsString(pyKey) << endl; break; case hasTimestamp: feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue); break; case timeStamp: - feature.timestamp = timestamp + - Vamp::RealTime::frame2RealTime - ((size_t)PyInt_AS_LONG(pyValue), (size_t)m_inputSampleRate); + feature.timestamp = + Vamp::RealTime::frame2RealTime( + PyLong_AsLong(pyValue), + (unsigned int) m_inputSampleRate ); +#ifdef _DEBUG + cerr << "Timestamp: " + << (long)PyLong_AsLong(pyValue) << ", ->" + << feature.timestamp.toString() << endl; +#endif break; case values: feature.values = PyList_As_FloatVector(pyValue); @@ -882,7 +917,7 @@ feature.label = PyString_AsString(pyValue); break; default : - cerr << "Invalid key in Vamp FeatureSet: " + cerr << "Invalid key in VAMP FeatureSet: " << PyString_AsString(pyKey) << endl; } // switch @@ -935,13 +970,17 @@ PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue; FeatureSet returnFeatures; - + + //iterate through list of outputs for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyOutputList); ++i) { pyFeatureList = PyList_GET_ITEM(pyOutputList,i); + //iterate list of Features for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) { - +#ifdef _DEBUG + cerr << "feature: " << j << endl; +#endif pyDict = PyList_GET_ITEM(pyFeatureList,j); if ( !PyDict_Check(pyDict) ) continue; @@ -956,17 +995,22 @@ switch (ffKeys[PyString_AsString(pyKey)]) { case not_found : - cerr << "Unknown key in Vamp FeatureSet: " + cerr << "Unknown key in VAMP FeatureSet: " << PyString_AsString(pyKey) << endl; break; case hasTimestamp: feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue); break; - // TODO: clarify what to do here case timeStamp: feature.timestamp = - Vamp::RealTime::frame2RealTime - ((size_t)PyInt_AS_LONG(pyValue), (size_t)m_inputSampleRate); + Vamp::RealTime::frame2RealTime( + PyLong_AsLong(pyValue), + (unsigned int) m_inputSampleRate ); +#ifdef _DEBUG + cerr << "Timestamp: " + << (long)PyLong_AsLong(pyValue) << ", ->" + << feature.timestamp.toString() << endl; +#endif break; case values: feature.values = PyList_As_FloatVector(pyValue); @@ -1062,6 +1106,9 @@ //Get next list item (Borrowed Reference) pyFloat = PyList_GET_ITEM(inputList,k); ListElement = (float) PyFloat_AS_DOUBLE(pyFloat); +#ifdef _DEBUG + cerr << "value: " << ListElement << endl; +#endif Output.push_back(ListElement); } diff -r a4c955e9a70b -r 3af6b5990ad8 pyvamp-main.cpp --- a/pyvamp-main.cpp Wed Jun 11 16:04:52 2008 +0000 +++ b/pyvamp-main.cpp Fri Jun 13 16:50:00 2008 +0000 @@ -114,7 +114,7 @@ //cerr << "# haveScannedPlugins: " << haveScannedPlugins << endl; if (!haveScannedPlugins) { - + if (!isPythonInitialized) { string pythonPath =