annotate PyTypeInterface.h @ 72:ffaa1fb3d7de vampyhost

inline is not a useful keyword with contemporary compilers
author Chris Cannam
date Mon, 24 Nov 2014 09:50:49 +0000
parents 40a01bb24209
children
rev   line source
Chris@66 1 /* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */
fazekasgy@37 2 /*
fazekasgy@37 3
fazekasgy@37 4 * Vampy : This plugin is a wrapper around the Vamp plugin API.
fazekasgy@37 5 * It allows for writing Vamp plugins in Python.
fazekasgy@37 6
fazekasgy@37 7 * Centre for Digital Music, Queen Mary University of London.
fazekasgy@37 8 * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources
fazekasgy@37 9 * for licence information.)
fazekasgy@37 10
fazekasgy@37 11 */
fazekasgy@37 12
fazekasgy@37 13 /*
fazekasgy@37 14 PyTypeInterface: Type safe conversion utilities between Python types
Chris@71 15 and Vamp API types. See PyTypeConversions for basic C/C++ types.
fazekasgy@37 16 */
fazekasgy@37 17
fazekasgy@37 18 #ifndef _PY_TYPE_INTERFACE_H_
fazekasgy@37 19 #define _PY_TYPE_INTERFACE_H_
fazekasgy@37 20 #include <Python.h>
fazekasgy@37 21 #ifdef HAVE_NUMPY
fazekasgy@37 22 #define PY_ARRAY_UNIQUE_SYMBOL VAMPY_ARRAY_API
fazekasgy@37 23 #define NO_IMPORT_ARRAY
Chris@66 24 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
fazekasgy@37 25 #include "numpy/arrayobject.h"
fazekasgy@37 26 #endif
fazekasgy@37 27 #include "PyExtensionModule.h"
Chris@71 28 #include "PyTypeConversions.h"
fazekasgy@37 29 #include <vector>
fazekasgy@37 30 #include <queue>
fazekasgy@37 31 #include <string>
fazekasgy@37 32 #include <sstream>
fazekasgy@37 33 #include "vamp-sdk/Plugin.h"
fazekasgy@37 34
fazekasgy@37 35 using std::cerr;
fazekasgy@37 36 using std::endl;
fazekasgy@37 37
fazekasgy@37 38 namespace o {
fazekasgy@37 39 enum eOutDescriptors {
fazekasgy@37 40 not_found,
fazekasgy@37 41 identifier,
fazekasgy@37 42 name,
fazekasgy@37 43 description,
fazekasgy@37 44 unit,
fazekasgy@37 45 hasFixedBinCount,
fazekasgy@37 46 binCount,
fazekasgy@37 47 binNames,
fazekasgy@37 48 hasKnownExtents,
fazekasgy@37 49 minValue,
fazekasgy@37 50 maxValue,
fazekasgy@37 51 isQuantized,
fazekasgy@37 52 quantizeStep,
fazekasgy@37 53 sampleType,
fazekasgy@37 54 sampleRate,
fazekasgy@37 55 hasDuration,
fazekasgy@37 56 endNode
fazekasgy@37 57 };
fazekasgy@37 58 }
fazekasgy@37 59
fazekasgy@37 60 namespace p {
fazekasgy@37 61 enum eParmDescriptors {
fazekasgy@37 62 not_found,
fazekasgy@37 63 identifier,
fazekasgy@37 64 name,
fazekasgy@37 65 description,
fazekasgy@37 66 unit,
fazekasgy@37 67 minValue,
fazekasgy@37 68 maxValue,
fazekasgy@37 69 defaultValue,
fazekasgy@37 70 isQuantized,
gyorgyf@62 71 quantizeStep,
gyorgyf@62 72 valueNames
fazekasgy@37 73 };
fazekasgy@37 74 }
fazekasgy@37 75
fazekasgy@37 76 enum eSampleTypes {
fazekasgy@37 77 OneSamplePerStep,
fazekasgy@37 78 FixedSampleRate,
fazekasgy@37 79 VariableSampleRate
fazekasgy@37 80 };
fazekasgy@37 81
fazekasgy@37 82 enum eFeatureFields {
fazekasgy@37 83 unknown,
fazekasgy@37 84 hasTimestamp,
fazekasgy@37 85 timestamp,
fazekasgy@37 86 hasDuration,
fazekasgy@37 87 duration,
fazekasgy@37 88 values,
fazekasgy@37 89 label
fazekasgy@37 90 };
fazekasgy@37 91
fazekasgy@37 92 class PyTypeInterface
fazekasgy@37 93 {
Chris@71 94 PyTypeConversions m_conv;
Chris@71 95
fazekasgy@37 96 public:
fazekasgy@37 97 PyTypeInterface();
fazekasgy@37 98 ~PyTypeInterface();
fazekasgy@37 99
fazekasgy@37 100 // Utilities
Chris@71 101 void setStrictTypingFlag(bool b) {m_strict = b; m_conv.setStrictTypingFlag(b);}
Chris@71 102 void setNumpyInstalled(bool b) {m_numpyInstalled = b; m_conv.setNumpyInstalled(b); }
fazekasgy@37 103 ValueError getError() const;
fazekasgy@37 104 std::string PyValue_Get_TypeName(PyObject*) const;
fazekasgy@37 105 bool initMaps() const;
fazekasgy@37 106
fazekasgy@37 107 // Input buffers to Python
fazekasgy@37 108 PyObject* InputBuffers_As_PythonLists(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype);
fazekasgy@47 109 PyObject* InputBuffers_As_SharedMemoryList(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype);
fazekasgy@37 110
fazekasgy@37 111 // Numpy types
fazekasgy@37 112 #ifdef HAVE_NUMPY
fazekasgy@37 113 PyObject* InputBuffers_As_NumpyArray(const float *const *inputBuffers, const size_t&, const size_t&, const Vamp::Plugin::InputDomain& dtype);
fazekasgy@37 114 #endif
fazekasgy@37 115
Chris@71 116 /* Template functions */
fazekasgy@37 117
fazekasgy@37 118
fazekasgy@37 119 /// Common wrappers to set values in Vamp API structs. (to be used in template functions)
fazekasgy@37 120 void SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const;
fazekasgy@37 121 void SetValue(Vamp::Plugin::ParameterDescriptor& od, std::string& key, PyObject* pyValue) const;
fazekasgy@37 122 bool SetValue(Vamp::Plugin::Feature& od, std::string& key, PyObject* pyValue) const;
fazekasgy@37 123 PyObject* GetDescriptor_As_Dict(PyObject* pyValue) const
fazekasgy@37 124 {
fazekasgy@37 125 if PyFeature_CheckExact(pyValue) return PyFeature_AS_DICT(pyValue);
fazekasgy@37 126 if PyOutputDescriptor_CheckExact(pyValue) return PyOutputDescriptor_AS_DICT(pyValue);
fazekasgy@37 127 if PyParameterDescriptor_CheckExact(pyValue) return PyParameterDescriptor_AS_DICT(pyValue);
fazekasgy@37 128 return NULL;
fazekasgy@37 129 }
fazekasgy@37 130
fazekasgy@37 131 //returns e.g. Vamp::Plugin::OutputDescriptor or Vamp::Plugin::Feature
fazekasgy@37 132 template<typename RET>
fazekasgy@37 133 RET PyValue_To_VampDescriptor(PyObject* pyValue) const
fazekasgy@37 134 {
fazekasgy@37 135 PyObject* pyDict;
fazekasgy@37 136
fazekasgy@37 137 // Descriptors encoded as dicts
fazekasgy@37 138 pyDict = GetDescriptor_As_Dict(pyValue);
fazekasgy@37 139 if (!pyDict) pyDict = pyValue;
fazekasgy@37 140
fazekasgy@37 141 // TODO: support full mapping protocol as fallback.
fazekasgy@37 142 if (!PyDict_Check(pyDict)) {
fazekasgy@37 143 setValueError("Error while converting descriptor or feature object.\nThe value is neither a dictionary nor a Vamp Feature or Descriptor type.",m_strict);
fazekasgy@37 144 #ifdef _DEBUG
fazekasgy@37 145 cerr << "PyTypeInterface::PyValue_To_VampDescriptor failed. Error: Unexpected return type." << endl;
fazekasgy@37 146 #endif
fazekasgy@37 147 return RET();
fazekasgy@37 148 }
fazekasgy@37 149
fazekasgy@37 150 Py_ssize_t pyPos = 0;
fazekasgy@37 151 PyObject *pyKey, *pyDictValue;
fazekasgy@37 152 initMaps();
fazekasgy@37 153 int errors = 0;
fazekasgy@37 154 m_error = false;
fazekasgy@37 155 RET rd;
fazekasgy@37 156
fazekasgy@37 157 //Python Dictionary Iterator:
fazekasgy@37 158 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyDictValue))
fazekasgy@37 159 {
Chris@71 160 std::string key = m_conv.PyValue_To_String(pyKey);
fazekasgy@37 161 #ifdef _DEBUG_VALUES
Chris@71 162 cerr << "key: '" << key << "' value: '" << m_conv.PyValue_To_String(pyDictValue) << "' " << endl;
fazekasgy@37 163 #endif
fazekasgy@37 164 SetValue(rd,key,pyDictValue);
fazekasgy@37 165 if (m_error) {
fazekasgy@37 166 errors++;
fazekasgy@37 167 lastError() << "attribute '" << key << "'";// << " of " << getDescriptorId(rd);
fazekasgy@37 168 }
fazekasgy@37 169 }
fazekasgy@37 170 if (errors) {
fazekasgy@37 171 lastError() << " of " << getDescriptorId(rd);
fazekasgy@37 172 m_error = true;
fazekasgy@37 173 #ifdef _DEBUG
fazekasgy@37 174 cerr << "PyTypeInterface::PyValue_To_VampDescriptor: Warning: Value error in descriptor." << endl;
fazekasgy@37 175 #endif
fazekasgy@37 176 }
fazekasgy@37 177 return rd;
fazekasgy@37 178 }
fazekasgy@37 179
fazekasgy@37 180 /// Convert a sequence (tipically list) of PySomething to
fazekasgy@37 181 /// OutputList,ParameterList or FeatureList
fazekasgy@37 182 /// <OutputList> <OutputDescriptor>
fazekasgy@37 183 template<typename RET,typename ELEM>
fazekasgy@37 184 RET PyValue_To_VampList(PyObject* pyValue) const
fazekasgy@37 185 {
fazekasgy@37 186 RET list; // e.g. Vamp::Plugin::OutputList
fazekasgy@37 187 ELEM element; // e.g. Vamp::Plugin::OutputDescriptor
fazekasgy@37 188
fazekasgy@37 189 /// convert lists (ParameterList, OutputList, FeatureList)
fazekasgy@37 190 if (PyList_Check(pyValue)) {
fazekasgy@37 191 PyObject *pyDict; //This reference will be borrowed
fazekasgy@37 192 m_error = false; int errors = 0;
fazekasgy@37 193 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyValue); ++i) {
fazekasgy@37 194 //Get i-th Vamp output descriptor (Borrowed Reference)
fazekasgy@37 195 pyDict = PyList_GET_ITEM(pyValue,i);
fazekasgy@37 196 element = PyValue_To_VampDescriptor<ELEM>(pyDict);
fazekasgy@37 197 if (m_error) errors++;
fazekasgy@37 198 // Check for empty Feature/Descriptor as before?
fazekasgy@37 199 list.push_back(element);
fazekasgy@37 200 }
fazekasgy@37 201 if (errors) m_error=true;
fazekasgy@37 202 return list;
fazekasgy@37 203 }
fazekasgy@37 204
fazekasgy@37 205 /// convert other types implementing the sequence protocol
fazekasgy@37 206 if (PySequence_Check(pyValue)) {
fazekasgy@37 207 PyObject *pySequence = PySequence_Fast(pyValue,"Returned value can not be converted to list or tuple.");
fazekasgy@37 208 PyObject **pyElements = PySequence_Fast_ITEMS(pySequence);
fazekasgy@37 209 m_error = false; int errors = 0;
fazekasgy@37 210 for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(pySequence); ++i)
fazekasgy@37 211 {
fazekasgy@37 212 element = PyValue_To_VampDescriptor<ELEM>(pyElements[i]);
fazekasgy@37 213 if (m_error) errors++;
fazekasgy@37 214 list.push_back(element);
fazekasgy@37 215 }
fazekasgy@37 216 if (errors) m_error=true;
fazekasgy@37 217 Py_XDECREF(pySequence);
fazekasgy@37 218 return list;
fazekasgy@37 219 }
fazekasgy@37 220
fazekasgy@37 221 // accept None as an empty list
fazekasgy@37 222 if (pyValue == Py_None) return list;
fazekasgy@37 223
fazekasgy@37 224 // in strict mode, returning a single value is not allowed
fazekasgy@37 225 if (m_strict) {
fazekasgy@37 226 setValueError("Strict conversion error: object is not list or iterable sequence.",m_strict);
fazekasgy@37 227 return list;
fazekasgy@37 228 }
fazekasgy@37 229
fazekasgy@37 230 /// try to insert single, non-iterable values. i.e. feature <- [feature]
fazekasgy@37 231 element = PyValue_To_VampDescriptor<ELEM>(pyValue);
fazekasgy@37 232 if (m_error) {
fazekasgy@37 233 setValueError("Could not insert returned value to Vamp List.",m_strict);
fazekasgy@37 234 return list;
fazekasgy@37 235 }
fazekasgy@37 236 list.push_back(element);
fazekasgy@37 237 return list;
fazekasgy@37 238
fazekasgy@37 239 #ifdef _DEBUG
fazekasgy@37 240 cerr << "PyTypeInterface::PyValue_To_VampList failed. Expected iterable return type." << endl;
fazekasgy@37 241 #endif
fazekasgy@37 242
fazekasgy@37 243 }
fazekasgy@37 244
fazekasgy@37 245 //Vamp specific types
fazekasgy@37 246 Vamp::Plugin::FeatureSet PyValue_To_FeatureSet(PyObject*) const;
Chris@72 247 void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureSet &r) const
fazekasgy@37 248 { r = this->PyValue_To_FeatureSet(pyValue); }
fazekasgy@37 249
fazekasgy@37 250 Vamp::RealTime PyValue_To_RealTime(PyObject*) const;
Chris@72 251 void PyValue_To_rValue(PyObject *pyValue, Vamp::RealTime &r) const
fazekasgy@37 252 { r = this->PyValue_To_RealTime(pyValue); }
fazekasgy@37 253
fazekasgy@37 254 Vamp::Plugin::OutputDescriptor::SampleType PyValue_To_SampleType(PyObject*) const;
fazekasgy@37 255
fazekasgy@37 256 Vamp::Plugin::InputDomain PyValue_To_InputDomain(PyObject*) const;
Chris@72 257 void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::InputDomain &r) const
fazekasgy@37 258 { r = this->PyValue_To_InputDomain(pyValue); }
fazekasgy@37 259
fazekasgy@37 260 /* Overloaded PyValue_To_rValue() to support generic functions */
Chris@72 261 void PyValue_To_rValue(PyObject *pyValue, float &defValue) const
Chris@71 262 { float tmp = m_conv.PyValue_To_Float(pyValue);
fazekasgy@37 263 if(!m_error) defValue = tmp; }
Chris@72 264 void PyValue_To_rValue(PyObject *pyValue, size_t &defValue) const
Chris@71 265 { size_t tmp = m_conv.PyValue_To_Size_t(pyValue);
fazekasgy@37 266 if(!m_error) defValue = tmp; }
Chris@72 267 void PyValue_To_rValue(PyObject *pyValue, bool &defValue) const
Chris@71 268 { bool tmp = m_conv.PyValue_To_Bool(pyValue);
fazekasgy@37 269 if(!m_error) defValue = tmp; }
Chris@72 270 void PyValue_To_rValue(PyObject *pyValue, std::string &defValue) const
Chris@71 271 { std::string tmp = m_conv.PyValue_To_String(pyValue);
fazekasgy@37 272 if(!m_error) defValue = tmp; }
fazekasgy@37 273 /*used by templates where we expect no return value, if there is one it will be ignored*/
Chris@72 274 void PyValue_To_rValue(PyObject *pyValue, NoneType &defValue) const
fazekasgy@37 275 { if (m_strict && pyValue != Py_None)
fazekasgy@37 276 setValueError("Strict conversion error: Expected 'None' type.",m_strict);
fazekasgy@37 277 }
Chris@71 278
fazekasgy@37 279 /* convert sequence types to Vamp List types */
Chris@72 280 void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::OutputList &r) const
fazekasgy@37 281 { r = this->PyValue_To_VampList<Vamp::Plugin::OutputList,Vamp::Plugin::OutputDescriptor>(pyValue); }
Chris@72 282 void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::ParameterList &r) const
fazekasgy@37 283 { r = this->PyValue_To_VampList<Vamp::Plugin::ParameterList,Vamp::Plugin::ParameterDescriptor>(pyValue); }
Chris@72 284 void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureList &r) const
fazekasgy@37 285 { r = this->PyValue_To_VampList<Vamp::Plugin::FeatureList,Vamp::Plugin::Feature>(pyValue); }
fazekasgy@37 286
fazekasgy@37 287 /// this is only needed for RealTime->Frame conversion
fazekasgy@37 288 void setInputSampleRate(float inputSampleRate)
fazekasgy@37 289 { m_inputSampleRate = (unsigned int) inputSampleRate; }
fazekasgy@37 290
fazekasgy@37 291 private:
fazekasgy@37 292 bool m_strict;
fazekasgy@37 293 mutable bool m_error;
fazekasgy@37 294 mutable std::queue<ValueError> m_errorQueue;
fazekasgy@37 295 unsigned int m_inputSampleRate;
fazekasgy@51 296 bool m_numpyInstalled;
fazekasgy@37 297
fazekasgy@37 298 void setValueError(std::string,bool) const;
fazekasgy@37 299 ValueError& lastError() const;
fazekasgy@37 300
fazekasgy@37 301 /* Overloaded _convert(), bypasses error checking to avoid doing it twice in internals. */
Chris@72 302 void _convert(PyObject *pyValue,float &r) const
Chris@71 303 { r = m_conv.PyValue_To_Float(pyValue); }
Chris@72 304 void _convert(PyObject *pyValue,size_t &r) const
Chris@71 305 { r = m_conv.PyValue_To_Size_t(pyValue); }
Chris@72 306 void _convert(PyObject *pyValue,bool &r) const
Chris@71 307 { r = m_conv.PyValue_To_Bool(pyValue); }
Chris@72 308 void _convert(PyObject *pyValue,std::string &r) const
Chris@71 309 { r = m_conv.PyValue_To_String(pyValue); }
Chris@72 310 void _convert(PyObject *pyValue,std::vector<std::string> &r) const
Chris@71 311 { r = m_conv.PyValue_To_StringVector(pyValue); }
Chris@72 312 void _convert(PyObject *pyValue,std::vector<float> &r) const
Chris@71 313 { r = m_conv.PyValue_To_FloatVector(pyValue); }
Chris@72 314 void _convert(PyObject *pyValue,Vamp::RealTime &r) const
fazekasgy@37 315 { r = PyValue_To_RealTime(pyValue); }
Chris@72 316 void _convert(PyObject *pyValue,Vamp::Plugin::OutputDescriptor::SampleType &r) const
fazekasgy@37 317 { r = PyValue_To_SampleType(pyValue); }
Chris@72 318 // void _convert(PyObject *pyValue,Vamp::Plugin::InputDomain &r) const
Chris@71 319 // { r = m_conv.PyValue_To_InputDomain(pyValue); }
fazekasgy@37 320
fazekasgy@37 321
fazekasgy@37 322 /* Identify descriptors for error reporting */
Chris@72 323 std::string getDescriptorId(Vamp::Plugin::OutputDescriptor d) const
fazekasgy@37 324 {return std::string("Output Descriptor '") + d.identifier +"' ";}
Chris@72 325 std::string getDescriptorId(Vamp::Plugin::ParameterDescriptor d) const
fazekasgy@37 326 {return std::string("Parameter Descriptor '") + d.identifier +"' ";}
Chris@72 327 std::string getDescriptorId(Vamp::Plugin::Feature f) const
fazekasgy@37 328 {return std::string("Feature (") + f.label + ")"; }
fazekasgy@37 329
fazekasgy@37 330 public:
fazekasgy@37 331 const bool& error;
fazekasgy@37 332
fazekasgy@37 333 };
fazekasgy@37 334
fazekasgy@37 335 #endif