annotate vampy-main.cpp @ 120:a38d318c85a9 tip

MSVC fixes
author Chris Cannam
date Wed, 18 Dec 2019 16:51:20 +0000
parents c85d26cb9dab
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 #include <Python.h>
fazekasgy@37 14
fazekasgy@37 15 #ifdef HAVE_NUMPY
fazekasgy@51 16
fazekasgy@51 17 // define a unique API pointer
Chris@66 18 #define PY_ARRAY_UNIQUE_SYMBOL VAMPY_ARRAY_API
Chris@66 19 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
fazekasgy@37 20 #include "numpy/arrayobject.h"
fazekasgy@51 21
fazekasgy@51 22 // prevent building with very old versions of numpy
fazekasgy@51 23 #ifndef NPY_VERSION
fazekasgy@51 24 #undef HAVE_NUMPY
fazekasgy@51 25 #endif
fazekasgy@51 26
fazekasgy@51 27 #endif
fazekasgy@51 28
fazekasgy@51 29 // this is not part of the API, but we will require it for a bug workaround
fazekasgy@51 30 // define this symbol if you use another version of numpy in the makefile
fazekasgy@51 31 // Vampy will not attempt to load a lower version than specified
fazekasgy@51 32 #ifdef HAVE_NUMPY
Chris@81 33 #ifndef NUMPY_MAJORVERSION
Chris@81 34 #define NUMPY_MAJORVERSION 1
fazekasgy@51 35 #endif
Chris@81 36 #ifndef NUMPY_MINORVERSION
Chris@81 37 #define NUMPY_MINORVERSION 9
Chris@81 38 #endif
Chris@81 39
fazekasgy@37 40 #endif
fazekasgy@37 41
Chris@120 42 #ifdef _WIN32
Chris@120 43 // We should be using the unicode apis, but we're not (yet)
Chris@120 44 #undef UNICODE
Chris@120 45 #undef _UNICODE
Chris@120 46 #define _MBCS 1
Chris@120 47 #endif
Chris@120 48
fazekasgy@37 49 #include "vamp/vamp.h"
fazekasgy@37 50 #include "vamp-sdk/PluginAdapter.h"
fazekasgy@37 51 #include "PyPlugScanner.h"
fazekasgy@37 52 #include "PyPlugin.h"
fazekasgy@37 53 #include "PyExtensionModule.h"
fazekasgy@37 54 #include "PyExtensionManager.h"
Chris@67 55 #include "Debug.h"
gyorgyf@63 56 #include <sstream>
fazekasgy@37 57
fazekasgy@37 58 #ifdef _WIN32
fazekasgy@37 59 #define pathsep ('\\')
fazekasgy@37 60 #include <windows.h>
fazekasgy@37 61 #include <tchar.h>
fazekasgy@37 62 #else
fazekasgy@37 63 #define pathsep ('/')
fazekasgy@37 64 #include <dirent.h>
fazekasgy@37 65 #include <dlfcn.h>
fazekasgy@37 66 #endif
fazekasgy@37 67
Chris@116 68 #include "version.h"
Chris@116 69
fazekasgy@37 70 using std::cerr;
fazekasgy@37 71 using std::endl;
fazekasgy@37 72 using std::string;
fazekasgy@37 73 using std::vector;
fazekasgy@37 74
fazekasgy@37 75 static int adinstcount;
fazekasgy@37 76 static int totinstcount;
fazekasgy@51 77 static bool numpyInstalled = false;
fazekasgy@51 78 static bool arrayApiInitialised = false;
fazekasgy@37 79
fazekasgy@37 80 class PyPluginAdapter : public Vamp::PluginAdapterBase
fazekasgy@37 81 {
fazekasgy@37 82 public:
fazekasgy@37 83 PyPluginAdapter(std::string pyPlugId, PyObject* pyClass) :
fazekasgy@37 84 PluginAdapterBase(),
fazekasgy@37 85 m_plug(pyPlugId),
fazekasgy@37 86 m_pyClass(pyClass),
fazekasgy@37 87 m_failed(false)
fazekasgy@37 88 {
Chris@67 89 DSTREAM << "PyPluginAdapter:ctor:"<< adinstcount << ": " << m_plug << endl;
fazekasgy@37 90 adinstcount++;
fazekasgy@37 91 }
fazekasgy@37 92
fazekasgy@37 93 ~PyPluginAdapter()
fazekasgy@37 94 {
fazekasgy@37 95 }
fazekasgy@37 96
fazekasgy@37 97 bool failed() { return m_failed; }
fazekasgy@37 98 std::string getPlugKey() { return m_plug; }
fazekasgy@37 99
fazekasgy@37 100 protected:
fazekasgy@37 101 Vamp::Plugin *createPlugin(float inputSampleRate)
fazekasgy@37 102 {
fazekasgy@37 103 try {
fazekasgy@51 104 PyPlugin *plugin = new PyPlugin(m_plug, inputSampleRate, m_pyClass, totinstcount, numpyInstalled);
fazekasgy@37 105 return plugin;
fazekasgy@37 106 } catch (...) {
Chris@67 107 cerr << "ERROR: PyPluginAdapter::createPlugin: Failed to construct PyPlugin" << endl;
Chris@67 108 // any plugin with syntax errors will fail to construct
Chris@67 109 m_failed = true;
fazekasgy@37 110 return 0;
fazekasgy@37 111 }
fazekasgy@37 112 }
fazekasgy@37 113
fazekasgy@37 114 std::string m_plug;
fazekasgy@37 115 PyObject *m_pyClass;
fazekasgy@37 116 bool m_failed;
fazekasgy@37 117 };
fazekasgy@37 118
fazekasgy@51 119
fazekasgy@37 120 static void array_API_initialiser()
fazekasgy@37 121 {
fazekasgy@51 122 if (arrayApiInitialised) return;
fazekasgy@51 123
fazekasgy@51 124 /* Numpy 1.3 build note: there seems to be a bug
fazekasgy@51 125 in this version (at least on OS/X) which will cause memory
fazekasgy@51 126 access error in the array API import function if an earlier runtime
fazekasgy@51 127 version of Numpy is used when loading the library.
fazekasgy@51 128 (below is a horrible workaround)
fazekasgy@51 129 */
fazekasgy@51 130
fazekasgy@37 131 #ifdef HAVE_NUMPY
fazekasgy@51 132
Chris@81 133 string ver, majorver, minorver;
gyorgyf@63 134 std::istringstream verStream;
Chris@81 135 int numpyVersionMajor, numpyVersionMinor;
fazekasgy@51 136
fazekasgy@51 137 /// attmept to test numpy version before importing the array API
Chris@67 138 DSTREAM << "Numpy build information: ABI level: " << NPY_VERSION
Chris@81 139 << " Numpy version: " << NUMPY_MAJORVERSION << "." << NUMPY_MINORVERSION << endl;
fazekasgy@51 140
fazekasgy@51 141 PyObject *pyModule, *pyDict, *pyVer;
fazekasgy@51 142
fazekasgy@51 143 pyModule = PyImport_ImportModule("numpy"); //numpy.core.multiarray
fazekasgy@51 144 if (!pyModule) {
Chris@67 145 cerr << "ERROR: Vampy was compiled with Numpy support but Numpy does not seem to be installed." << endl;
fazekasgy@51 146 #ifdef __APPLE__
fazekasgy@51 147 cerr << "Hint: Check if Numpy is installed for the particular setup of Python used by Vampy (given by Python exec prefix)." << endl;
fazekasgy@51 148 #endif
fazekasgy@51 149 goto numpyFailure;
fazekasgy@51 150 }
fazekasgy@51 151
fazekasgy@51 152 pyDict = PyModule_GetDict(pyModule); // borrowed ref
fazekasgy@51 153 if (!pyDict) {
Chris@67 154 cerr << "ERROR: Can not access Numpy module dictionary." << endl;
fazekasgy@51 155 goto numpyFailure;
fazekasgy@51 156 }
fazekasgy@51 157
fazekasgy@51 158 pyVer = PyDict_GetItemString(pyDict,"__version__"); //borrowed ref
fazekasgy@51 159 if (!pyVer) {
Chris@67 160 cerr << "ERROR: Can not access Numpy version information." << endl;
fazekasgy@51 161 goto numpyFailure;
fazekasgy@51 162 }
fazekasgy@51 163
fazekasgy@51 164 ver = PyString_AsString(pyVer);
fazekasgy@51 165 ver = ver.substr(0,ver.rfind("."));
Chris@81 166 majorver = ver.substr(0,ver.rfind("."));
Chris@81 167 minorver = ver.substr(ver.rfind(".")+1);
Chris@81 168
Chris@81 169 // parse version string to float
Chris@81 170 verStream.str(majorver);
Chris@81 171 verStream >> numpyVersionMajor;
Chris@81 172 verStream.str(minorver);
Chris@81 173 verStream >> numpyVersionMinor;
Chris@67 174
Chris@81 175 DSTREAM << "Numpy runtime version: " << numpyVersionMajor << "." << numpyVersionMinor << endl;
Chris@81 176
Chris@81 177 if(numpyVersionMajor < NUMPY_MAJORVERSION ||
Chris@81 178 (numpyVersionMajor < NUMPY_MAJORVERSION &&
Chris@81 179 numpyVersionMinor < NUMPY_MINORVERSION)) {
Chris@81 180 cerr << "ERROR: Incompatible Numpy version found: " << ver << endl;
fazekasgy@51 181 goto numpyFailure;
fazekasgy@51 182 }
fazekasgy@51 183
fazekasgy@51 184 Py_DECREF(pyModule);
fazekasgy@51 185
fazekasgy@51 186 // At least we catch import errors, but if binary compatibility
fazekasgy@51 187 // has changed without notice, this would still fail.
fazekasgy@51 188 // However, we should never get to this point now anyway.
fazekasgy@37 189 import_array();
fazekasgy@51 190 if (PyErr_Occurred()) {
Chris@67 191 cerr << "ERROR: Import error while loading the Numpy Array API." << endl;
fazekasgy@51 192 PyErr_Print(); PyErr_Clear();
fazekasgy@51 193 goto numpyFailure;
fazekasgy@51 194 }
fazekasgy@51 195 else {
fazekasgy@51 196 numpyInstalled = true;
fazekasgy@51 197 arrayApiInitialised = true;
fazekasgy@51 198 return;
fazekasgy@51 199 }
fazekasgy@51 200
fazekasgy@51 201
fazekasgy@51 202 numpyFailure:
Chris@81 203 cerr << "Please make sure you have Numpy " << NUMPY_MAJORVERSION << "." << NUMPY_MINORVERSION << " or greater installed." << endl;
fazekasgy@51 204 cerr << "Vampy: Numpy support disabled." << endl;
fazekasgy@51 205 numpyInstalled = false;
fazekasgy@51 206 arrayApiInitialised = true;
fazekasgy@51 207 if (pyModule) Py_XDECREF(pyModule);
fazekasgy@51 208 return;
fazekasgy@51 209
fazekasgy@51 210 /*HAVE_NUMPY*/
Chris@116 211 #else
Chris@116 212 DSTREAM << "Numpy support deselected at compile time, not attempting to initialise it" << endl;
Chris@116 213 #endif
fazekasgy@51 214
Chris@116 215 numpyInstalled = false;
fazekasgy@51 216 arrayApiInitialised = true;
fazekasgy@51 217 return;
fazekasgy@37 218 }
fazekasgy@37 219
fazekasgy@37 220
fazekasgy@37 221 static std::vector<PyPluginAdapter *> adapters;
fazekasgy@37 222 static bool haveScannedPlugins = false;
fazekasgy@37 223
fazekasgy@37 224 static bool tryPreload(string name)
fazekasgy@37 225 {
cannam@53 226 // cerr << "tryPreload: " << name << endl;
fazekasgy@37 227 #ifdef _WIN32
fazekasgy@37 228 void *lib = LoadLibrary(name.c_str());
fazekasgy@37 229 if (!lib) {
fazekasgy@37 230 return false;
fazekasgy@37 231 }
fazekasgy@37 232 #else
fazekasgy@37 233 void *lib = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL);
fazekasgy@37 234 if (!lib) {
cannam@56 235 //perror("dlopen");
fazekasgy@37 236 return false;
fazekasgy@37 237 }
fazekasgy@37 238 #endif
Chris@67 239 DSTREAM << "Preloaded Python from " << name << endl;
fazekasgy@37 240 return true;
fazekasgy@37 241 }
fazekasgy@37 242
fazekasgy@37 243 static bool preloadPython()
fazekasgy@37 244 {
fazekasgy@37 245 #ifdef _WIN32
fazekasgy@37 246 // this doesn't seem to be necessary at all on Windows
fazekasgy@37 247 return true;
fazekasgy@37 248 #endif
fazekasgy@37 249
fazekasgy@37 250 string pyver = Py_GetVersion();
fazekasgy@37 251 int dots = 2;
fazekasgy@37 252 string shortver;
fazekasgy@37 253 for (size_t i = 0; i < pyver.length(); ++i) {
fazekasgy@37 254 if (pyver[i] == '.') {
fazekasgy@37 255 if (--dots == 0) {
fazekasgy@37 256 shortver = pyver.substr(0, i);
fazekasgy@37 257 break;
fazekasgy@37 258 }
fazekasgy@37 259 }
fazekasgy@37 260 }
Chris@67 261 DSTREAM << "Short version: " << shortver << endl;
Chris@67 262 // this is useful to find out where the loaded library might be loaded from
Chris@67 263 DSTREAM << "Python exec prefix: " << Py_GetExecPrefix() << endl;
fazekasgy@37 264
cannam@55 265 char *pylib = getenv("VAMPY_PYLIB");
cannam@54 266 if (pylib && *pylib) {
Chris@67 267 DSTREAM << "Trying to preload Python from specified location " << pylib
Chris@67 268 << "..." << endl;
cannam@54 269 return tryPreload(string(pylib));
cannam@54 270 }
cannam@54 271
fazekasgy@37 272 vector<string> pfxs;
cannam@53 273 pfxs.push_back(string(Py_GetExecPrefix()) + "/");
cannam@53 274 pfxs.push_back(string(Py_GetExecPrefix()) + "/lib/");
fazekasgy@37 275 pfxs.push_back("");
fazekasgy@37 276 pfxs.push_back("/usr/lib/");
fazekasgy@37 277 pfxs.push_back("/usr/local/lib/");
fazekasgy@37 278 char buffer[5];
fazekasgy@37 279
fazekasgy@37 280 // hahaha! grossness is like a brother to us
fazekasgy@37 281 #ifdef __APPLE__
fazekasgy@37 282 for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) {
cannam@53 283 // cerr << "prefix: " << pfxs[pfxidx] << endl;
cannam@53 284 if (tryPreload(pfxs[pfxidx] + string("Python"))) return true;
fazekasgy@37 285 for (int minor = 8; minor >= 0; --minor) {
fazekasgy@37 286 sprintf(buffer, "%d", minor);
fazekasgy@37 287 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib." + buffer)) return true;
fazekasgy@37 288 }
fazekasgy@37 289 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib")) return true;
fazekasgy@37 290 if (tryPreload(pfxs[pfxidx] + string("libpython.dylib"))) return true;
fazekasgy@37 291 }
fazekasgy@37 292 #else
fazekasgy@37 293 for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) {
fazekasgy@37 294 for (int minor = 8; minor >= 0; --minor) {
fazekasgy@37 295 sprintf(buffer, "%d", minor);
fazekasgy@37 296 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so." + buffer)) return true;
fazekasgy@37 297 }
fazekasgy@37 298 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so")) return true;
fazekasgy@37 299 if (tryPreload(pfxs[pfxidx] + string("libpython.so"))) return true;
fazekasgy@37 300 }
fazekasgy@37 301 #endif
fazekasgy@37 302
fazekasgy@37 303 return false;
fazekasgy@37 304 }
fazekasgy@37 305
fazekasgy@37 306
fazekasgy@37 307 static PyExtensionManager pyExtensionManager;
fazekasgy@37 308
fazekasgy@37 309 const VampPluginDescriptor
fazekasgy@37 310 *vampGetPluginDescriptor(unsigned int version,unsigned int index)
Chris@79 311 {
Chris@79 312 DSTREAM << "# vampGetPluginDescriptor(" << version << "," << index << ")" << endl;
Chris@79 313
Chris@116 314 if (version < 1) return 0;
fazekasgy@37 315
Chris@116 316 DSTREAM << "# Vampy version: " << VAMPY_VERSION << endl;
Chris@116 317
fazekasgy@37 318 int isPythonInitialized = Py_IsInitialized();
Chris@67 319 DSTREAM << "# isPythonInitialized: " << isPythonInitialized << endl;
Chris@67 320 DSTREAM << "# haveScannedPlugins: " << haveScannedPlugins << endl;
fazekasgy@37 321
fazekasgy@37 322 if (!haveScannedPlugins) {
fazekasgy@37 323
fazekasgy@37 324 if (!isPythonInitialized){
fazekasgy@37 325
fazekasgy@37 326 if (!preloadPython())
fazekasgy@37 327 cerr << "Warning: Could not preload Python. Dynamic loading in scripts will fail." << endl;
fazekasgy@37 328 if (PyImport_AppendInittab("vampy",initvampy) != 0)
fazekasgy@37 329 cerr << "Warning: Extension module could not be added to module inittab." << endl;
fazekasgy@37 330 Py_Initialize();
fazekasgy@51 331 array_API_initialiser();
fazekasgy@37 332 initvampy();
Chris@67 333 DSTREAM << "# isPythonInitialized after initialize: " << Py_IsInitialized() << endl;
fazekasgy@37 334 }
fazekasgy@37 335
fazekasgy@37 336 vector<string> pyPlugs;
fazekasgy@37 337 vector<string> pyPath;
fazekasgy@37 338 vector<PyObject *> pyClasses;
fazekasgy@37 339 static PyPlugScanner *scanner;
fazekasgy@37 340
fazekasgy@37 341 //Scanning Plugins
Chris@67 342 DSTREAM << "Scanning Vampy Plugins" << endl;
fazekasgy@37 343 scanner = PyPlugScanner::getInstance();
fazekasgy@37 344
fazekasgy@37 345 // added env. varable support VAMPY_EXTPATH
fazekasgy@37 346 pyPath=scanner->getAllValidPath();
fazekasgy@37 347 scanner->setPath(pyPath);
fazekasgy@37 348
fazekasgy@37 349 // added env. variable support:
fazekasgy@37 350 // VAMPY_COMPILED=1 to recognise .pyc files (default is 1)
fazekasgy@37 351 pyPlugs = scanner->getPyPlugs();
fazekasgy@37 352
Chris@67 353 DSTREAM << "Found " << pyPlugs.size() << " Scripts." << endl;
fazekasgy@37 354 //TODO: should this support multiple classes per script (?)
fazekasgy@37 355 pyClasses = scanner->getPyClasses();
Chris@67 356 DSTREAM << "Found " << pyClasses.size() << " Classes." << endl;
fazekasgy@37 357
fazekasgy@37 358 for (size_t i = 0; i < pyPlugs.size(); ++i) {
fazekasgy@37 359 adapters.push_back( new PyPluginAdapter(pyPlugs[i],pyClasses[i]));
fazekasgy@37 360 }
fazekasgy@37 361 pyExtensionManager.setPlugModuleNames(pyPlugs);
fazekasgy@37 362 pyExtensionManager.initExtension();
fazekasgy@37 363 array_API_initialiser();
fazekasgy@37 364 haveScannedPlugins=true;
fazekasgy@37 365 }
fazekasgy@37 366
Chris@67 367 DSTREAM << "Accessing adapter index: " << index << " (adapters: " << adapters.size() << ")" << endl;
fazekasgy@37 368
Chris@84 369 while (index < adapters.size()) {
fazekasgy@37 370
fazekasgy@37 371 const VampPluginDescriptor *tmp = adapters[index]->getDescriptor();
fazekasgy@37 372
fazekasgy@37 373 if (adapters[index]->failed()) {
fazekasgy@37 374 cerr << "\nERROR: [in vampGetPluginDescriptor] Removing adapter of: \n'"
Chris@87 375 << adapters[index]->getPlugKey() << "'\n"
Chris@87 376 << "The plugin has failed to construct. Hint: Check __init__() function." << endl;
fazekasgy@37 377 pyExtensionManager.deleteModuleName(adapters[index]->getPlugKey());
fazekasgy@37 378 delete adapters[index];
Chris@87 379 adapters.erase(adapters.begin() + index);
Chris@84 380 continue;
fazekasgy@37 381 }
fazekasgy@37 382
fazekasgy@37 383 return tmp;
Chris@84 384 }
fazekasgy@37 385
Chris@84 386 return 0;
fazekasgy@37 387 }
fazekasgy@37 388
fazekasgy@37 389
fazekasgy@37 390
fazekasgy@37 391
fazekasgy@37 392
fazekasgy@37 393
fazekasgy@37 394
fazekasgy@37 395