annotate PyTypeInterface.h @ 79:f5c028376bf9

Some old changes that, I think, probably did not help anything
author Chris Cannam
date Wed, 12 Aug 2015 15:25:04 +0100
parents 5e26aaba2eed
children ef4989f33648
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
fazekasgy@37 15 and basic C/C++ types and Vamp API 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"
fazekasgy@37 28 #include <vector>
fazekasgy@37 29 #include <queue>
fazekasgy@37 30 #include <string>
fazekasgy@37 31 #include <sstream>
fazekasgy@37 32 #include "vamp-sdk/Plugin.h"
fazekasgy@37 33
fazekasgy@37 34 using std::cerr;
fazekasgy@37 35 using std::endl;
fazekasgy@37 36
fazekasgy@37 37 #ifdef HAVE_NUMPY
fazekasgy@37 38 enum eArrayDataType {
fazekasgy@37 39 dtype_float32 = (int) NPY_FLOAT,
fazekasgy@37 40 dtype_complex64 = (int) NPY_CFLOAT
fazekasgy@37 41 };
fazekasgy@37 42 #endif
fazekasgy@37 43
fazekasgy@37 44 namespace o {
fazekasgy@37 45 enum eOutDescriptors {
fazekasgy@37 46 not_found,
fazekasgy@37 47 identifier,
fazekasgy@37 48 name,
fazekasgy@37 49 description,
fazekasgy@37 50 unit,
fazekasgy@37 51 hasFixedBinCount,
fazekasgy@37 52 binCount,
fazekasgy@37 53 binNames,
fazekasgy@37 54 hasKnownExtents,
fazekasgy@37 55 minValue,
fazekasgy@37 56 maxValue,
fazekasgy@37 57 isQuantized,
fazekasgy@37 58 quantizeStep,
fazekasgy@37 59 sampleType,
fazekasgy@37 60 sampleRate,
fazekasgy@37 61 hasDuration,
fazekasgy@37 62 endNode
fazekasgy@37 63 };
fazekasgy@37 64 }
fazekasgy@37 65
fazekasgy@37 66 namespace p {
fazekasgy@37 67 enum eParmDescriptors {
fazekasgy@37 68 not_found,
fazekasgy@37 69 identifier,
fazekasgy@37 70 name,
fazekasgy@37 71 description,
fazekasgy@37 72 unit,
fazekasgy@37 73 minValue,
fazekasgy@37 74 maxValue,
fazekasgy@37 75 defaultValue,
fazekasgy@37 76 isQuantized,
gyorgyf@62 77 quantizeStep,
gyorgyf@62 78 valueNames
fazekasgy@37 79 };
fazekasgy@37 80 }
fazekasgy@37 81
fazekasgy@37 82 enum eSampleTypes {
fazekasgy@37 83 OneSamplePerStep,
fazekasgy@37 84 FixedSampleRate,
fazekasgy@37 85 VariableSampleRate
fazekasgy@37 86 };
fazekasgy@37 87
fazekasgy@37 88 enum eFeatureFields {
fazekasgy@37 89 unknown,
fazekasgy@37 90 hasTimestamp,
fazekasgy@37 91 timestamp,
fazekasgy@37 92 hasDuration,
fazekasgy@37 93 duration,
fazekasgy@37 94 values,
fazekasgy@37 95 label
fazekasgy@37 96 };
fazekasgy@37 97
fazekasgy@37 98 /* C++ mapping of PyNone Type */
fazekasgy@37 99 struct NoneType {};
fazekasgy@37 100
fazekasgy@37 101 class PyTypeInterface
fazekasgy@37 102 {
fazekasgy@37 103 public:
fazekasgy@37 104 PyTypeInterface();
fazekasgy@37 105 ~PyTypeInterface();
fazekasgy@37 106
fazekasgy@37 107 // Data
fazekasgy@37 108 class ValueError
fazekasgy@37 109 {
fazekasgy@37 110 public:
fazekasgy@37 111 ValueError() {}
fazekasgy@37 112 ValueError(std::string m, bool s) : message(m),strict(s) {}
fazekasgy@37 113 std::string location;
fazekasgy@37 114 std::string message;
fazekasgy@37 115 bool strict;
fazekasgy@37 116 std::string str() const {
fazekasgy@37 117 return (location.empty()) ? message : message + "\nLocation: " + location;}
fazekasgy@37 118 void print() const { cerr << str() << endl; }
fazekasgy@37 119 template<typename V> ValueError &operator<< (const V& v)
fazekasgy@37 120 {
fazekasgy@37 121 std::ostringstream ss;
fazekasgy@37 122 ss << v;
fazekasgy@37 123 location += ss.str();
fazekasgy@37 124 return *this;
fazekasgy@37 125 }
fazekasgy@37 126 };
fazekasgy@37 127
fazekasgy@37 128 // Utilities
fazekasgy@37 129 void setStrictTypingFlag(bool b) {m_strict = b;}
fazekasgy@51 130 void setNumpyInstalled(bool b) {m_numpyInstalled = b;}
fazekasgy@37 131 ValueError getError() const;
fazekasgy@37 132 std::string PyValue_Get_TypeName(PyObject*) const;
fazekasgy@37 133 bool initMaps() const;
fazekasgy@37 134
fazekasgy@37 135 // Basic type conversion: Python to C++
fazekasgy@37 136 float PyValue_To_Float(PyObject*) const;
fazekasgy@37 137 size_t PyValue_To_Size_t(PyObject*) const;
fazekasgy@37 138 bool PyValue_To_Bool(PyObject*) const;
fazekasgy@37 139 std::string PyValue_To_String(PyObject*) const;
fazekasgy@37 140 long PyValue_To_Long(PyObject*) const;
fazekasgy@37 141 // int PyValue_To_Int(PyObject* pyValue) const;
fazekasgy@37 142
fazekasgy@37 143
fazekasgy@37 144 // C++ to Python
fazekasgy@37 145 PyObject *PyValue_From_CValue(const char*) const;
fazekasgy@37 146 PyObject *PyValue_From_CValue(const std::string& x) const { return PyValue_From_CValue(x.c_str()); }
fazekasgy@37 147 PyObject *PyValue_From_CValue(size_t) const;
fazekasgy@37 148 PyObject *PyValue_From_CValue(double) const;
fazekasgy@37 149 PyObject *PyValue_From_CValue(float x) const { return PyValue_From_CValue((double)x); }
fazekasgy@37 150 PyObject *PyValue_From_CValue(bool) const;
fazekasgy@37 151
fazekasgy@37 152 // Sequence types
fazekasgy@37 153 std::vector<std::string> PyValue_To_StringVector (PyObject*) const;
fazekasgy@37 154 std::vector<float> PyValue_To_FloatVector (PyObject*) const;
fazekasgy@37 155 std::vector<float> PyList_To_FloatVector (PyObject*) const;
fazekasgy@37 156
fazekasgy@37 157 // Input buffers to Python
fazekasgy@37 158 PyObject* InputBuffers_As_PythonLists(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype);
fazekasgy@47 159 PyObject* InputBuffers_As_SharedMemoryList(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype);
fazekasgy@37 160
fazekasgy@37 161 // Numpy types
fazekasgy@37 162 #ifdef HAVE_NUMPY
fazekasgy@37 163 std::vector<float> PyArray_To_FloatVector (PyObject *pyValue) const;
fazekasgy@37 164 PyObject* InputBuffers_As_NumpyArray(const float *const *inputBuffers, const size_t&, const size_t&, const Vamp::Plugin::InputDomain& dtype);
fazekasgy@37 165 #endif
fazekasgy@37 166
fazekasgy@37 167
fazekasgy@37 168
fazekasgy@37 169
fazekasgy@37 170 /* Template functions */
fazekasgy@37 171
fazekasgy@37 172
fazekasgy@37 173 /// Common wrappers to set values in Vamp API structs. (to be used in template functions)
fazekasgy@37 174 void SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const;
fazekasgy@37 175 void SetValue(Vamp::Plugin::ParameterDescriptor& od, std::string& key, PyObject* pyValue) const;
fazekasgy@37 176 bool SetValue(Vamp::Plugin::Feature& od, std::string& key, PyObject* pyValue) const;
fazekasgy@37 177 PyObject* GetDescriptor_As_Dict(PyObject* pyValue) const
fazekasgy@37 178 {
fazekasgy@37 179 if PyFeature_CheckExact(pyValue) return PyFeature_AS_DICT(pyValue);
fazekasgy@37 180 if PyOutputDescriptor_CheckExact(pyValue) return PyOutputDescriptor_AS_DICT(pyValue);
fazekasgy@37 181 if PyParameterDescriptor_CheckExact(pyValue) return PyParameterDescriptor_AS_DICT(pyValue);
fazekasgy@37 182 return NULL;
fazekasgy@37 183 }
fazekasgy@37 184
fazekasgy@37 185 //returns e.g. Vamp::Plugin::OutputDescriptor or Vamp::Plugin::Feature
fazekasgy@37 186 template<typename RET>
fazekasgy@37 187 RET PyValue_To_VampDescriptor(PyObject* pyValue) const
fazekasgy@37 188 {
fazekasgy@37 189 PyObject* pyDict;
fazekasgy@37 190
fazekasgy@37 191 // Descriptors encoded as dicts
fazekasgy@37 192 pyDict = GetDescriptor_As_Dict(pyValue);
fazekasgy@37 193 if (!pyDict) pyDict = pyValue;
fazekasgy@37 194
fazekasgy@37 195 // TODO: support full mapping protocol as fallback.
fazekasgy@37 196 if (!PyDict_Check(pyDict)) {
fazekasgy@37 197 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 198 #ifdef _DEBUG
fazekasgy@37 199 cerr << "PyTypeInterface::PyValue_To_VampDescriptor failed. Error: Unexpected return type." << endl;
fazekasgy@37 200 #endif
fazekasgy@37 201 return RET();
fazekasgy@37 202 }
fazekasgy@37 203
fazekasgy@37 204 Py_ssize_t pyPos = 0;
fazekasgy@37 205 PyObject *pyKey, *pyDictValue;
fazekasgy@37 206 initMaps();
fazekasgy@37 207 int errors = 0;
fazekasgy@37 208 m_error = false;
fazekasgy@37 209 RET rd;
fazekasgy@37 210
fazekasgy@37 211 //Python Dictionary Iterator:
fazekasgy@37 212 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyDictValue))
fazekasgy@37 213 {
fazekasgy@37 214 std::string key = PyValue_To_String(pyKey);
fazekasgy@37 215 #ifdef _DEBUG_VALUES
fazekasgy@37 216 cerr << "key: '" << key << "' value: '" << PyValue_To_String(pyDictValue) << "' " << endl;
fazekasgy@37 217 #endif
fazekasgy@37 218 SetValue(rd,key,pyDictValue);
fazekasgy@37 219 if (m_error) {
fazekasgy@37 220 errors++;
fazekasgy@37 221 lastError() << "attribute '" << key << "'";// << " of " << getDescriptorId(rd);
fazekasgy@37 222 }
fazekasgy@37 223 }
fazekasgy@37 224 if (errors) {
fazekasgy@37 225 lastError() << " of " << getDescriptorId(rd);
fazekasgy@37 226 m_error = true;
fazekasgy@37 227 #ifdef _DEBUG
fazekasgy@37 228 cerr << "PyTypeInterface::PyValue_To_VampDescriptor: Warning: Value error in descriptor." << endl;
fazekasgy@37 229 #endif
fazekasgy@37 230 }
fazekasgy@37 231 return rd;
fazekasgy@37 232 }
fazekasgy@37 233
fazekasgy@37 234 /// Convert a sequence (tipically list) of PySomething to
fazekasgy@37 235 /// OutputList,ParameterList or FeatureList
fazekasgy@37 236 /// <OutputList> <OutputDescriptor>
fazekasgy@37 237 template<typename RET,typename ELEM>
fazekasgy@37 238 RET PyValue_To_VampList(PyObject* pyValue) const
fazekasgy@37 239 {
fazekasgy@37 240 RET list; // e.g. Vamp::Plugin::OutputList
fazekasgy@37 241 ELEM element; // e.g. Vamp::Plugin::OutputDescriptor
fazekasgy@37 242
fazekasgy@37 243 /// convert lists (ParameterList, OutputList, FeatureList)
fazekasgy@37 244 if (PyList_Check(pyValue)) {
fazekasgy@37 245 PyObject *pyDict; //This reference will be borrowed
fazekasgy@37 246 m_error = false; int errors = 0;
fazekasgy@37 247 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyValue); ++i) {
fazekasgy@37 248 //Get i-th Vamp output descriptor (Borrowed Reference)
fazekasgy@37 249 pyDict = PyList_GET_ITEM(pyValue,i);
fazekasgy@37 250 element = PyValue_To_VampDescriptor<ELEM>(pyDict);
fazekasgy@37 251 if (m_error) errors++;
fazekasgy@37 252 // Check for empty Feature/Descriptor as before?
fazekasgy@37 253 list.push_back(element);
fazekasgy@37 254 }
fazekasgy@37 255 if (errors) m_error=true;
fazekasgy@37 256 return list;
fazekasgy@37 257 }
fazekasgy@37 258
fazekasgy@37 259 /// convert other types implementing the sequence protocol
fazekasgy@37 260 if (PySequence_Check(pyValue)) {
fazekasgy@37 261 PyObject *pySequence = PySequence_Fast(pyValue,"Returned value can not be converted to list or tuple.");
fazekasgy@37 262 PyObject **pyElements = PySequence_Fast_ITEMS(pySequence);
fazekasgy@37 263 m_error = false; int errors = 0;
fazekasgy@37 264 for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(pySequence); ++i)
fazekasgy@37 265 {
fazekasgy@37 266 element = PyValue_To_VampDescriptor<ELEM>(pyElements[i]);
fazekasgy@37 267 if (m_error) errors++;
fazekasgy@37 268 list.push_back(element);
fazekasgy@37 269 }
fazekasgy@37 270 if (errors) m_error=true;
fazekasgy@37 271 Py_XDECREF(pySequence);
fazekasgy@37 272 return list;
fazekasgy@37 273 }
fazekasgy@37 274
fazekasgy@37 275 // accept None as an empty list
fazekasgy@37 276 if (pyValue == Py_None) return list;
fazekasgy@37 277
fazekasgy@37 278 // in strict mode, returning a single value is not allowed
fazekasgy@37 279 if (m_strict) {
fazekasgy@37 280 setValueError("Strict conversion error: object is not list or iterable sequence.",m_strict);
fazekasgy@37 281 return list;
fazekasgy@37 282 }
fazekasgy@37 283
fazekasgy@37 284 /// try to insert single, non-iterable values. i.e. feature <- [feature]
fazekasgy@37 285 element = PyValue_To_VampDescriptor<ELEM>(pyValue);
fazekasgy@37 286 if (m_error) {
fazekasgy@37 287 setValueError("Could not insert returned value to Vamp List.",m_strict);
fazekasgy@37 288 return list;
fazekasgy@37 289 }
fazekasgy@37 290 list.push_back(element);
fazekasgy@37 291 return list;
fazekasgy@37 292
fazekasgy@37 293 #ifdef _DEBUG
fazekasgy@37 294 cerr << "PyTypeInterface::PyValue_To_VampList failed. Expected iterable return type." << endl;
fazekasgy@37 295 #endif
fazekasgy@37 296
fazekasgy@37 297 }
fazekasgy@37 298
fazekasgy@37 299 /// Convert DTYPE type 1D NumpyArray to std::vector<RET>
fazekasgy@37 300 template<typename RET, typename DTYPE>
Chris@66 301 std::vector<RET> PyArray_Convert(void* raw_data_ptr, long length, size_t strides) const
fazekasgy@37 302 {
fazekasgy@37 303 std::vector<RET> rValue;
fazekasgy@37 304
fazekasgy@37 305 /// check if the array is continuous, if not use strides info
fazekasgy@37 306 if (sizeof(DTYPE)!=strides) {
fazekasgy@37 307 #ifdef _DEBUG_VALUES
fazekasgy@37 308 cerr << "Warning: discontinuous numpy array. Strides: " << strides << " bytes. sizeof(dtype): " << sizeof(DTYPE) << endl;
fazekasgy@37 309 #endif
fazekasgy@37 310 char* data = (char*) raw_data_ptr;
fazekasgy@37 311 for (long i = 0; i<length; ++i){
fazekasgy@37 312 rValue.push_back((RET)(*((DTYPE*)data)));
fazekasgy@37 313 #ifdef _DEBUG_VALUES
fazekasgy@37 314 cerr << "value: " << (RET)(*((DTYPE*)data)) << endl;
fazekasgy@37 315 #endif
fazekasgy@37 316 data+=strides;
fazekasgy@37 317 }
fazekasgy@37 318 return rValue;
fazekasgy@37 319 }
fazekasgy@37 320
fazekasgy@37 321 DTYPE* data = (DTYPE*) raw_data_ptr;
fazekasgy@37 322 for (long i = 0; i<length; ++i){
fazekasgy@37 323 #ifdef _DEBUG_VALUES
fazekasgy@37 324 cerr << "value: " << (RET)data[i] << endl;
fazekasgy@37 325 #endif
fazekasgy@37 326 rValue.push_back((RET)data[i]);
fazekasgy@37 327 }
fazekasgy@37 328 return rValue;
fazekasgy@37 329 }
fazekasgy@37 330
Chris@77 331 #ifdef HAVE_NUMPY
fazekasgy@37 332 /// this is a special case. numpy.float64 has an array interface but no array descriptor
fazekasgy@37 333 inline std::vector<float> PyArray0D_Convert(PyArrayInterface *ai) const
fazekasgy@37 334 {
fazekasgy@37 335 std::vector<float> rValue;
fazekasgy@37 336 if ((ai->typekind) == *"f")
fazekasgy@37 337 rValue.push_back((float)*(double*)(ai->data));
fazekasgy@37 338 else {
fazekasgy@37 339 setValueError("Unsupported NumPy data type.",m_strict);
fazekasgy@37 340 return rValue;
fazekasgy@37 341 }
fazekasgy@37 342 #ifdef _DEBUG_VALUES
fazekasgy@37 343 cerr << "value: " << rValue[0] << endl;
fazekasgy@37 344 #endif
fazekasgy@37 345 return rValue;
fazekasgy@37 346 }
Chris@77 347 #endif
Chris@77 348
fazekasgy@37 349 //Vamp specific types
fazekasgy@37 350 Vamp::Plugin::FeatureSet PyValue_To_FeatureSet(PyObject*) const;
fazekasgy@37 351 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureSet &r) const
fazekasgy@37 352 { r = this->PyValue_To_FeatureSet(pyValue); }
fazekasgy@37 353
fazekasgy@37 354 Vamp::RealTime PyValue_To_RealTime(PyObject*) const;
fazekasgy@37 355 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::RealTime &r) const
fazekasgy@37 356 { r = this->PyValue_To_RealTime(pyValue); }
fazekasgy@37 357
fazekasgy@37 358 Vamp::Plugin::OutputDescriptor::SampleType PyValue_To_SampleType(PyObject*) const;
fazekasgy@37 359
fazekasgy@37 360 Vamp::Plugin::InputDomain PyValue_To_InputDomain(PyObject*) const;
fazekasgy@37 361 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::InputDomain &r) const
fazekasgy@37 362 { r = this->PyValue_To_InputDomain(pyValue); }
fazekasgy@37 363
fazekasgy@37 364
fazekasgy@37 365 /* Overloaded PyValue_To_rValue() to support generic functions */
fazekasgy@37 366 inline void PyValue_To_rValue(PyObject *pyValue, float &defValue) const
fazekasgy@37 367 { float tmp = this->PyValue_To_Float(pyValue);
fazekasgy@37 368 if(!m_error) defValue = tmp; }
fazekasgy@37 369 inline void PyValue_To_rValue(PyObject *pyValue, size_t &defValue) const
fazekasgy@37 370 { size_t tmp = this->PyValue_To_Size_t(pyValue);
fazekasgy@37 371 if(!m_error) defValue = tmp; }
fazekasgy@37 372 inline void PyValue_To_rValue(PyObject *pyValue, bool &defValue) const
fazekasgy@37 373 { bool tmp = this->PyValue_To_Bool(pyValue);
fazekasgy@37 374 if(!m_error) defValue = tmp; }
fazekasgy@37 375 inline void PyValue_To_rValue(PyObject *pyValue, std::string &defValue) const
fazekasgy@37 376 { std::string tmp = this->PyValue_To_String(pyValue);
fazekasgy@37 377 if(!m_error) defValue = tmp; }
fazekasgy@37 378 /*used by templates where we expect no return value, if there is one it will be ignored*/
fazekasgy@37 379 inline void PyValue_To_rValue(PyObject *pyValue, NoneType &defValue) const
fazekasgy@37 380 { if (m_strict && pyValue != Py_None)
fazekasgy@37 381 setValueError("Strict conversion error: Expected 'None' type.",m_strict);
fazekasgy@37 382 }
fazekasgy@37 383
fazekasgy@37 384 /* convert sequence types to Vamp List types */
fazekasgy@37 385 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::OutputList &r) const
fazekasgy@37 386 { r = this->PyValue_To_VampList<Vamp::Plugin::OutputList,Vamp::Plugin::OutputDescriptor>(pyValue); }
fazekasgy@37 387 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::ParameterList &r) const
fazekasgy@37 388 { r = this->PyValue_To_VampList<Vamp::Plugin::ParameterList,Vamp::Plugin::ParameterDescriptor>(pyValue); }
fazekasgy@37 389 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureList &r) const
fazekasgy@37 390 { r = this->PyValue_To_VampList<Vamp::Plugin::FeatureList,Vamp::Plugin::Feature>(pyValue); }
fazekasgy@37 391
fazekasgy@37 392 /// this is only needed for RealTime->Frame conversion
fazekasgy@37 393 void setInputSampleRate(float inputSampleRate)
fazekasgy@37 394 { m_inputSampleRate = (unsigned int) inputSampleRate; }
fazekasgy@37 395
fazekasgy@37 396 private:
fazekasgy@37 397 bool m_strict;
fazekasgy@37 398 mutable bool m_error;
fazekasgy@37 399 mutable std::queue<ValueError> m_errorQueue;
fazekasgy@37 400 unsigned int m_inputSampleRate;
fazekasgy@51 401 bool m_numpyInstalled;
fazekasgy@37 402
fazekasgy@37 403 void setValueError(std::string,bool) const;
fazekasgy@37 404 ValueError& lastError() const;
fazekasgy@37 405
fazekasgy@37 406 /* Overloaded _convert(), bypasses error checking to avoid doing it twice in internals. */
fazekasgy@37 407 inline void _convert(PyObject *pyValue,float &r) const
fazekasgy@37 408 { r = PyValue_To_Float(pyValue); }
fazekasgy@37 409 inline void _convert(PyObject *pyValue,size_t &r) const
fazekasgy@37 410 { r = PyValue_To_Size_t(pyValue); }
fazekasgy@37 411 inline void _convert(PyObject *pyValue,bool &r) const
fazekasgy@37 412 { r = PyValue_To_Bool(pyValue); }
fazekasgy@37 413 inline void _convert(PyObject *pyValue,std::string &r) const
fazekasgy@37 414 { r = PyValue_To_String(pyValue); }
fazekasgy@37 415 inline void _convert(PyObject *pyValue,std::vector<std::string> &r) const
fazekasgy@37 416 { r = PyValue_To_StringVector(pyValue); }
fazekasgy@37 417 inline void _convert(PyObject *pyValue,std::vector<float> &r) const
fazekasgy@37 418 { r = PyValue_To_FloatVector(pyValue); }
fazekasgy@37 419 inline void _convert(PyObject *pyValue,Vamp::RealTime &r) const
fazekasgy@37 420 { r = PyValue_To_RealTime(pyValue); }
fazekasgy@37 421 inline void _convert(PyObject *pyValue,Vamp::Plugin::OutputDescriptor::SampleType &r) const
fazekasgy@37 422 { r = PyValue_To_SampleType(pyValue); }
fazekasgy@37 423 // inline void _convert(PyObject *pyValue,Vamp::Plugin::InputDomain &r) const
fazekasgy@37 424 // { r = PyValue_To_InputDomain(pyValue); }
fazekasgy@37 425
fazekasgy@37 426
fazekasgy@37 427 /* Identify descriptors for error reporting */
fazekasgy@37 428 inline std::string getDescriptorId(Vamp::Plugin::OutputDescriptor d) const
fazekasgy@37 429 {return std::string("Output Descriptor '") + d.identifier +"' ";}
fazekasgy@37 430 inline std::string getDescriptorId(Vamp::Plugin::ParameterDescriptor d) const
fazekasgy@37 431 {return std::string("Parameter Descriptor '") + d.identifier +"' ";}
fazekasgy@37 432 inline std::string getDescriptorId(Vamp::Plugin::Feature f) const
fazekasgy@37 433 {return std::string("Feature (") + f.label + ")"; }
fazekasgy@37 434
fazekasgy@37 435 public:
fazekasgy@37 436 const bool& error;
fazekasgy@37 437
fazekasgy@37 438 };
fazekasgy@37 439
fazekasgy@37 440 /* Convert Sample Buffers to Python */
fazekasgy@37 441
fazekasgy@51 442 /// passing the sample buffers as builtin python lists
fazekasgy@37 443 /// Optimization: using fast sequence protocol
fazekasgy@37 444 inline PyObject*
fazekasgy@37 445 PyTypeInterface::InputBuffers_As_PythonLists(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype)
fazekasgy@37 446 {
fazekasgy@37 447 //create a list of lists (new references)
fazekasgy@37 448 PyObject *pyChannelList = PyList_New((Py_ssize_t) channels);
fazekasgy@37 449
fazekasgy@37 450 // Pack samples into a Python List Object
fazekasgy@37 451 // pyFloat/pyComplex types will always be new references,
fazekasgy@37 452 // they will be freed when the lists are deallocated.
fazekasgy@37 453
fazekasgy@37 454 PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList);
fazekasgy@37 455 for (size_t i=0; i < channels; ++i) {
fazekasgy@51 456
fazekasgy@51 457 size_t arraySize;
fazekasgy@47 458 if (dtype==Vamp::Plugin::FrequencyDomain)
fazekasgy@51 459 arraySize = (blockSize / 2) + 1; //blockSize + 2; if cplx list isn't used
fazekasgy@47 460 else
fazekasgy@47 461 arraySize = blockSize;
fazekasgy@51 462
fazekasgy@51 463 PyObject *pySampleList = PyList_New((Py_ssize_t) arraySize);
fazekasgy@51 464 PyObject **pySampleListArray = PySequence_Fast_ITEMS(pySampleList);
fazekasgy@37 465
fazekasgy@37 466 // Note: passing a complex list crashes the C-style plugin
fazekasgy@37 467 // when it tries to convert it to a numpy array directly.
fazekasgy@37 468 // This plugin will be obsolete, but we have to find a way
fazekasgy@51 469 // to prevent such crash: possibly a numpy bug,
fazekasgy@51 470 // works fine above 1.0.4
fazekasgy@37 471
fazekasgy@51 472 switch (dtype) //(Vamp::Plugin::TimeDomain)
fazekasgy@37 473 {
fazekasgy@37 474 case Vamp::Plugin::TimeDomain :
fazekasgy@37 475
fazekasgy@47 476 for (size_t j = 0; j < arraySize; ++j) {
fazekasgy@37 477 PyObject *pyFloat=PyFloat_FromDouble(
fazekasgy@37 478 (double) inputBuffers[i][j]);
fazekasgy@37 479 pySampleListArray[j] = pyFloat;
fazekasgy@37 480 }
fazekasgy@37 481 break;
fazekasgy@37 482
fazekasgy@37 483 case Vamp::Plugin::FrequencyDomain :
fazekasgy@37 484
fazekasgy@37 485 size_t k = 0;
fazekasgy@51 486 for (size_t j = 0; j < arraySize; ++j) {
fazekasgy@37 487 PyObject *pyComplex=PyComplex_FromDoubles(
fazekasgy@37 488 (double) inputBuffers[i][k],
fazekasgy@37 489 (double) inputBuffers[i][k+1]);
fazekasgy@37 490 pySampleListArray[j] = pyComplex;
fazekasgy@37 491 k += 2;
fazekasgy@37 492 }
fazekasgy@37 493 break;
fazekasgy@37 494
fazekasgy@37 495 }
fazekasgy@37 496 pyChannelListArray[i] = pySampleList;
fazekasgy@37 497 }
fazekasgy@37 498 return pyChannelList;
fazekasgy@37 499 }
fazekasgy@37 500
fazekasgy@37 501 /// numpy buffer interface: passing the sample buffers as shared memory buffers
fazekasgy@37 502 /// Optimization: using sequence protocol for creating the buffer list
fazekasgy@37 503 inline PyObject*
fazekasgy@47 504 PyTypeInterface::InputBuffers_As_SharedMemoryList(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype)
fazekasgy@37 505 {
fazekasgy@37 506 //create a list of buffers (returns new references)
fazekasgy@37 507 PyObject *pyChannelList = PyList_New((Py_ssize_t) channels);
fazekasgy@37 508 PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList);
fazekasgy@37 509
fazekasgy@37 510 // Expose memory using the Buffer Interface.
fazekasgy@37 511 // This will pass a pointer which can be recasted in Python code
fazekasgy@37 512 // as complex or float array using Numpy's frombuffer() method
fazekasgy@37 513 // (this will not copy values just keep the starting adresses
fazekasgy@37 514 // for each channel in a list)
fazekasgy@47 515 Py_ssize_t bufferSize;
fazekasgy@47 516
fazekasgy@47 517 if (dtype==Vamp::Plugin::FrequencyDomain)
fazekasgy@47 518 bufferSize = (Py_ssize_t) sizeof(float) * (blockSize+2);
fazekasgy@47 519 else
fazekasgy@47 520 bufferSize = (Py_ssize_t) sizeof(float) * blockSize;
fazekasgy@47 521
fazekasgy@37 522 for (size_t i=0; i < channels; ++i) {
fazekasgy@37 523 PyObject *pyBuffer = PyBuffer_FromMemory
fazekasgy@37 524 ((void *) (float *) inputBuffers[i],bufferSize);
fazekasgy@37 525 pyChannelListArray[i] = pyBuffer;
fazekasgy@37 526 }
fazekasgy@37 527 return pyChannelList;
fazekasgy@37 528 }
fazekasgy@37 529
fazekasgy@37 530
fazekasgy@37 531 /// numpy array interface: passing the sample buffers as 2D numpy array
fazekasgy@37 532 /// Optimization: using array API (needs numpy headers)
fazekasgy@37 533 #ifdef HAVE_NUMPY
fazekasgy@37 534 inline PyObject*
fazekasgy@37 535 PyTypeInterface::InputBuffers_As_NumpyArray(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype)
fazekasgy@37 536 {
fazekasgy@37 537 /*
fazekasgy@37 538 NOTE: We create a list of 1D Numpy arrays for each channel instead
fazekasgy@37 539 of a matrix, because the address space of inputBuffers doesn't seem
fazekasgy@37 540 to be continuous. Although the array strides could be calculated for
fazekasgy@37 541 2 channels (i.e. inputBuffers[1] - inputBuffers[0]) i'm not sure
fazekasgy@37 542 if this can be trusted, especially for more than 2 channels.
fazekasgy@37 543
fazekasgy@37 544 cerr << "First channel: " << inputBuffers[0][0] << " address: " << inputBuffers[0] << endl;
fazekasgy@37 545 if (channels == 2)
fazekasgy@37 546 cerr << "Second channel: " << inputBuffers[1][0] << " address: " << inputBuffers[1] << endl;
fazekasgy@37 547
fazekasgy@37 548 */
fazekasgy@37 549
fazekasgy@37 550 // create a list of arrays (returns new references)
fazekasgy@37 551 PyObject *pyChannelList = PyList_New((Py_ssize_t) channels);
fazekasgy@37 552 PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList);
fazekasgy@37 553
fazekasgy@37 554 // Expose memory using the Numpy Array Interface.
fazekasgy@37 555 // This will wrap an array objects around the data.
fazekasgy@37 556 // (will not copy values just steal the starting adresses)
fazekasgy@37 557
fazekasgy@37 558 int arraySize, typenum;
fazekasgy@37 559
fazekasgy@37 560 switch (dtype)
fazekasgy@37 561 {
fazekasgy@37 562 case Vamp::Plugin::TimeDomain :
fazekasgy@37 563 typenum = dtype_float32; //NPY_FLOAT;
fazekasgy@37 564 arraySize = (int) blockSize;
fazekasgy@37 565 break;
fazekasgy@37 566
fazekasgy@37 567 case Vamp::Plugin::FrequencyDomain :
fazekasgy@37 568 typenum = dtype_complex64; //NPY_CFLOAT;
fazekasgy@47 569 arraySize = (int) (blockSize / 2) + 1;
fazekasgy@37 570 break;
fazekasgy@37 571
fazekasgy@37 572 default :
fazekasgy@37 573 cerr << "PyTypeInterface::InputBuffers_As_NumpyArray: Error: Unsupported numpy array data type." << endl;
fazekasgy@37 574 return pyChannelList;
fazekasgy@37 575 }
fazekasgy@37 576
fazekasgy@37 577 // size for each dimension
fazekasgy@37 578 npy_intp ndims[1]={arraySize};
fazekasgy@37 579
fazekasgy@37 580 for (size_t i=0; i < channels; ++i) {
fazekasgy@37 581 PyObject *pyChannelArray =
fazekasgy@37 582 //args: (dimensions, size in each dim, type kind, pointer to continuous array)
fazekasgy@37 583 PyArray_SimpleNewFromData(1, ndims, typenum, (void*) inputBuffers[i]);
Chris@66 584 // make it read-only: set all flags to false except NPY_C_CONTIGUOUS
Chris@66 585 //!!! what about NPY_ARRAY_OWNDATA?
Chris@66 586 PyArray_CLEARFLAGS((PyArrayObject *)pyChannelArray, 0xff);
Chris@66 587 PyArray_ENABLEFLAGS((PyArrayObject *)pyChannelArray, NPY_ARRAY_C_CONTIGUOUS);
fazekasgy@37 588 pyChannelListArray[i] = pyChannelArray;
fazekasgy@37 589 }
fazekasgy@37 590 return pyChannelList;
fazekasgy@37 591 }
fazekasgy@37 592 #endif
fazekasgy@37 593
fazekasgy@37 594
fazekasgy@37 595
fazekasgy@37 596 #ifdef NUMPY_REFERENCE
fazekasgy@37 597 /// This should be all we need to compile without direct dependency,
fazekasgy@37 598 /// but we don't do that. (it may not work on some platforms)
fazekasgy@37 599 typedef struct {
fazekasgy@37 600 int two; /* contains the integer 2 -- simple sanity check */
fazekasgy@37 601 int nd; /* number of dimensions */
fazekasgy@37 602 char typekind; /* kind in array --- character code of typestr */
fazekasgy@37 603 int itemsize; /* size of each element */
fazekasgy@37 604 int flags; /* flags indicating how the data should be interpreted */
fazekasgy@37 605 /* must set ARR_HAS_DESCR bit to validate descr */
fazekasgy@37 606 Py_intptr_t *shape; /* A length-nd array of shape information */
fazekasgy@37 607 Py_intptr_t *strides; /* A length-nd array of stride information */
fazekasgy@37 608 void *data; /* A pointer to the first element of the array */
fazekasgy@37 609 PyObject *descr; /* NULL or data-description (same as descr key */
fazekasgy@37 610 /* of __array_interface__) -- must set ARR_HAS_DESCR */
fazekasgy@37 611 /* flag or this will be ignored. */
fazekasgy@37 612 } PyArrayInterface;
fazekasgy@37 613
fazekasgy@37 614 typedef struct PyArrayObject {
fazekasgy@37 615 PyObject_HEAD
fazekasgy@37 616 char *data; /* pointer to raw data buffer */
fazekasgy@37 617 int nd; /* number of dimensions, also called ndim */
fazekasgy@37 618 npy_intp *dimensions; /* size in each dimension */
fazekasgy@37 619 npy_intp *strides; /* bytes to jump to get to the
fazekasgy@37 620 next element in each dimension */
fazekasgy@37 621 PyObject *base; /* This object should be decref'd
fazekasgy@37 622 upon deletion of array */
fazekasgy@37 623 /* For views it points to the original array */
fazekasgy@37 624 /* For creation from buffer object it points
fazekasgy@37 625 to an object that shold be decref'd on
fazekasgy@37 626 deletion */
fazekasgy@37 627 /* For UPDATEIFCOPY flag this is an array
fazekasgy@37 628 to-be-updated upon deletion of this one */
fazekasgy@37 629 PyArray_Descr *descr; /* Pointer to type structure */
fazekasgy@37 630 int flags; /* Flags describing array -- see below*/
fazekasgy@37 631 PyObject *weakreflist; /* For weakreferences */
fazekasgy@37 632 } PyArrayObject;
fazekasgy@37 633
fazekasgy@37 634 typedef struct _PyArray_Descr {
fazekasgy@37 635 PyObject_HEAD
fazekasgy@37 636 PyTypeObject *typeobj; /* the type object representing an
fazekasgy@37 637 instance of this type -- should not
fazekasgy@37 638 be two type_numbers with the same type
fazekasgy@37 639 object. */
fazekasgy@37 640 char kind; /* kind for this type */
fazekasgy@37 641 char type; /* unique-character representing this type */
fazekasgy@37 642 char byteorder; /* '>' (big), '<' (little), '|'
fazekasgy@37 643 (not-applicable), or '=' (native). */
fazekasgy@37 644 char hasobject; /* non-zero if it has object arrays
fazekasgy@37 645 in fields */
fazekasgy@37 646 int type_num; /* number representing this type */
fazekasgy@37 647 int elsize; /* element size for this type */
fazekasgy@37 648 int alignment; /* alignment needed for this type */
fazekasgy@37 649 struct _arr_descr \
fazekasgy@37 650 *subarray; /* Non-NULL if this type is
fazekasgy@37 651 is an array (C-contiguous)
fazekasgy@37 652 of some other type
fazekasgy@37 653 */
fazekasgy@37 654 PyObject *fields; /* The fields dictionary for this type */
fazekasgy@37 655 /* For statically defined descr this
fazekasgy@37 656 is always Py_None */
fazekasgy@37 657
fazekasgy@37 658 PyObject *names; /* An ordered tuple of field names or NULL
fazekasgy@37 659 if no fields are defined */
fazekasgy@37 660
fazekasgy@37 661 PyArray_ArrFuncs *f; /* a table of functions specific for each
fazekasgy@37 662 basic data descriptor */
fazekasgy@37 663 } PyArray_Descr;
fazekasgy@37 664
fazekasgy@37 665 enum NPY_TYPES { NPY_BOOL=0,
fazekasgy@37 666 NPY_BYTE, NPY_UBYTE,
fazekasgy@37 667 NPY_SHORT, NPY_USHORT,
fazekasgy@37 668 NPY_INT, NPY_UINT,
fazekasgy@37 669 NPY_LONG, NPY_ULONG,
fazekasgy@37 670 NPY_LONGLONG, NPY_ULONGLONG,
fazekasgy@37 671 NPY_FLOAT, NPY_DOUBLE, NPY_LONGDOUBLE,
fazekasgy@37 672 NPY_CFLOAT, NPY_CDOUBLE, NPY_CLONGDOUBLE,
fazekasgy@37 673 NPY_OBJECT=17,
fazekasgy@37 674 NPY_STRING, NPY_UNICODE,
fazekasgy@37 675 NPY_VOID,
fazekasgy@37 676 NPY_NTYPES,
fazekasgy@37 677 NPY_NOTYPE,
fazekasgy@37 678 NPY_CHAR, /* special flag */
fazekasgy@37 679 NPY_USERDEF=256 /* leave room for characters */
fazekasgy@37 680 };
fazekasgy@37 681 #endif /*NUMPY_REFERENCE*/
fazekasgy@37 682 #endif