annotate PyTypeInterface.h @ 53:7e59caea821b

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