fazekasgy@37: /* fazekasgy@37: fazekasgy@37: * Vampy : This plugin is a wrapper around the Vamp plugin API. fazekasgy@37: * It allows for writing Vamp plugins in Python. fazekasgy@37: fazekasgy@37: * Centre for Digital Music, Queen Mary University of London. fazekasgy@37: * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources fazekasgy@37: * for licence information.) fazekasgy@37: fazekasgy@37: */ fazekasgy@37: fazekasgy@37: #include fazekasgy@37: fazekasgy@37: #ifdef HAVE_NUMPY fazekasgy@37: #define PY_ARRAY_UNIQUE_SYMBOL VAMPY_ARRAY_API fazekasgy@37: #include "numpy/arrayobject.h" fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: #include "vamp/vamp.h" fazekasgy@37: #include "vamp-sdk/PluginAdapter.h" fazekasgy@37: #include "PyPlugScanner.h" fazekasgy@37: #include "PyPlugin.h" fazekasgy@37: #include "PyExtensionModule.h" fazekasgy@37: #include "PyExtensionManager.h" fazekasgy@37: fazekasgy@37: fazekasgy@37: #ifdef _WIN32 fazekasgy@37: #define pathsep ('\\') fazekasgy@37: #include fazekasgy@37: #include fazekasgy@37: #else fazekasgy@37: #define pathsep ('/') fazekasgy@37: #include fazekasgy@37: #include fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: using std::cerr; fazekasgy@37: using std::endl; fazekasgy@37: using std::string; fazekasgy@37: using std::vector; fazekasgy@37: fazekasgy@37: static int adinstcount; fazekasgy@37: static int totinstcount; fazekasgy@37: fazekasgy@37: class PyPluginAdapter : public Vamp::PluginAdapterBase fazekasgy@37: { fazekasgy@37: public: fazekasgy@37: PyPluginAdapter(std::string pyPlugId, PyObject* pyClass) : fazekasgy@37: PluginAdapterBase(), fazekasgy@37: m_plug(pyPlugId), fazekasgy@37: m_pyClass(pyClass), fazekasgy@37: m_failed(false) fazekasgy@37: { fazekasgy@37: cerr << "PyPluginAdapter:ctor:"<< adinstcount << ": " << m_plug << endl; fazekasgy@37: adinstcount++; fazekasgy@37: } fazekasgy@37: fazekasgy@37: ~PyPluginAdapter() fazekasgy@37: { fazekasgy@37: } fazekasgy@37: fazekasgy@37: bool failed() { return m_failed; } fazekasgy@37: std::string getPlugKey() { return m_plug; } fazekasgy@37: fazekasgy@37: protected: fazekasgy@37: Vamp::Plugin *createPlugin(float inputSampleRate) fazekasgy@37: { fazekasgy@37: try { fazekasgy@37: PyPlugin *plugin = new PyPlugin(m_plug, inputSampleRate, m_pyClass, totinstcount); fazekasgy@37: return plugin; fazekasgy@37: } catch (...) { fazekasgy@37: cerr << "PyPluginAdapter::createPlugin: Failed to construct PyPlugin" << endl; fazekasgy@37: // any plugin with syntax errors will fail to construct fazekasgy@37: m_failed = true; fazekasgy@37: return 0; fazekasgy@37: } fazekasgy@37: } fazekasgy@37: fazekasgy@37: std::string m_plug; fazekasgy@37: PyObject *m_pyClass; fazekasgy@37: bool m_failed; fazekasgy@37: }; fazekasgy@37: fazekasgy@37: static void array_API_initialiser() fazekasgy@37: { fazekasgy@37: /// numpy C-API requirement fazekasgy@37: #ifdef HAVE_NUMPY fazekasgy@37: import_array(); fazekasgy@37: if(NPY_VERSION != PyArray_GetNDArrayCVersion()) fazekasgy@37: cerr << "Warning: Numpy ABI version mismatch. (Build version: " fazekasgy@37: << NPY_VERSION << " Runtime version: " << PyArray_GetNDArrayCVersion() << ")" << endl; fazekasgy@37: #endif fazekasgy@37: } fazekasgy@37: fazekasgy@37: fazekasgy@37: static std::vector adapters; fazekasgy@37: static bool haveScannedPlugins = false; fazekasgy@37: fazekasgy@37: static bool tryPreload(string name) fazekasgy@37: { fazekasgy@37: #ifdef _WIN32 fazekasgy@37: void *lib = LoadLibrary(name.c_str()); fazekasgy@37: if (!lib) { fazekasgy@37: return false; fazekasgy@37: } fazekasgy@37: #else fazekasgy@37: void *lib = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL); fazekasgy@37: if (!lib) { fazekasgy@37: return false; fazekasgy@37: } fazekasgy@37: #endif fazekasgy@37: return true; fazekasgy@37: } fazekasgy@37: fazekasgy@37: static bool preloadPython() fazekasgy@37: { fazekasgy@37: #ifdef _WIN32 fazekasgy@37: // this doesn't seem to be necessary at all on Windows fazekasgy@37: return true; fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: string pyver = Py_GetVersion(); fazekasgy@37: int dots = 2; fazekasgy@37: string shortver; fazekasgy@37: for (size_t i = 0; i < pyver.length(); ++i) { fazekasgy@37: if (pyver[i] == '.') { fazekasgy@37: if (--dots == 0) { fazekasgy@37: shortver = pyver.substr(0, i); fazekasgy@37: break; fazekasgy@37: } fazekasgy@37: } fazekasgy@37: } fazekasgy@37: cerr << "Short version: " << shortver << endl; fazekasgy@37: // this is useful to find out where the loaded library might be loaded from fazekasgy@37: cerr << "Python exec prefix: " << Py_GetExecPrefix() << endl; fazekasgy@37: fazekasgy@37: vector pfxs; fazekasgy@37: pfxs.push_back(""); fazekasgy@37: pfxs.push_back(string(Py_GetExecPrefix()) + "/lib/"); fazekasgy@37: pfxs.push_back(string(Py_GetExecPrefix()) + "/"); fazekasgy@37: pfxs.push_back("/usr/lib/"); fazekasgy@37: pfxs.push_back("/usr/local/lib/"); fazekasgy@37: char buffer[5]; fazekasgy@37: fazekasgy@37: // hahaha! grossness is like a brother to us fazekasgy@37: #ifdef __APPLE__ fazekasgy@37: for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) { fazekasgy@37: for (int minor = 8; minor >= 0; --minor) { fazekasgy@37: sprintf(buffer, "%d", minor); fazekasgy@37: if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib." + buffer)) return true; fazekasgy@37: } fazekasgy@37: if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib")) return true; fazekasgy@37: if (tryPreload(pfxs[pfxidx] + string("libpython.dylib"))) return true; fazekasgy@37: } fazekasgy@37: #else fazekasgy@37: for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) { fazekasgy@37: for (int minor = 8; minor >= 0; --minor) { fazekasgy@37: sprintf(buffer, "%d", minor); fazekasgy@37: if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so." + buffer)) return true; fazekasgy@37: } fazekasgy@37: if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so")) return true; fazekasgy@37: if (tryPreload(pfxs[pfxidx] + string("libpython.so"))) return true; fazekasgy@37: } fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: return false; fazekasgy@37: } fazekasgy@37: fazekasgy@37: fazekasgy@37: static PyExtensionManager pyExtensionManager; fazekasgy@37: fazekasgy@37: const VampPluginDescriptor fazekasgy@37: *vampGetPluginDescriptor(unsigned int version,unsigned int index) fazekasgy@37: { fazekasgy@37: if (version < 1) return 0; fazekasgy@37: fazekasgy@37: int isPythonInitialized = Py_IsInitialized(); fazekasgy@37: cerr << "# isPythonInitialized: " << isPythonInitialized << endl; fazekasgy@37: cerr << "# haveScannedPlugins: " << haveScannedPlugins << endl; fazekasgy@37: fazekasgy@37: if (!haveScannedPlugins) { fazekasgy@37: fazekasgy@37: if (!isPythonInitialized){ fazekasgy@37: fazekasgy@37: if (!preloadPython()) fazekasgy@37: cerr << "Warning: Could not preload Python. Dynamic loading in scripts will fail." << endl; fazekasgy@37: if (PyImport_AppendInittab("vampy",initvampy) != 0) fazekasgy@37: cerr << "Warning: Extension module could not be added to module inittab." << endl; fazekasgy@37: Py_Initialize(); fazekasgy@37: initvampy(); fazekasgy@37: #ifdef _DEBUG fazekasgy@37: cerr << "# isPythonInitialized after initialize: " << Py_IsInitialized() << endl; fazekasgy@37: #endif fazekasgy@37: } fazekasgy@37: fazekasgy@37: vector pyPlugs; fazekasgy@37: vector pyPath; fazekasgy@37: vector pyClasses; fazekasgy@37: static PyPlugScanner *scanner; fazekasgy@37: fazekasgy@37: //Scanning Plugins fazekasgy@37: cerr << "Scanning Vampy Plugins" << endl; fazekasgy@37: scanner = PyPlugScanner::getInstance(); fazekasgy@37: fazekasgy@37: // added env. varable support VAMPY_EXTPATH fazekasgy@37: pyPath=scanner->getAllValidPath(); fazekasgy@37: scanner->setPath(pyPath); fazekasgy@37: fazekasgy@37: // added env. variable support: fazekasgy@37: // VAMPY_COMPILED=1 to recognise .pyc files (default is 1) fazekasgy@37: pyPlugs = scanner->getPyPlugs(); fazekasgy@37: fazekasgy@37: cerr << "Found " << pyPlugs.size() << " Scripts." << endl; fazekasgy@37: //TODO: should this support multiple classes per script (?) fazekasgy@37: pyClasses = scanner->getPyClasses(); fazekasgy@37: cerr << "Found " << pyClasses.size() << " Classes." << endl; fazekasgy@37: fazekasgy@37: for (size_t i = 0; i < pyPlugs.size(); ++i) { fazekasgy@37: adapters.push_back( new PyPluginAdapter(pyPlugs[i],pyClasses[i])); fazekasgy@37: } fazekasgy@37: pyExtensionManager.setPlugModuleNames(pyPlugs); fazekasgy@37: pyExtensionManager.initExtension(); fazekasgy@37: array_API_initialiser(); fazekasgy@37: haveScannedPlugins=true; fazekasgy@37: } fazekasgy@37: fazekasgy@37: #ifdef _DEBUG fazekasgy@37: cerr << "Accessing adapter index: " << index << " (adapters: " << adapters.size() << ")" << endl; fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: if (indexgetDescriptor(); fazekasgy@37: fazekasgy@37: if (adapters[index]->failed()) { fazekasgy@37: cerr << "\nERROR: [in vampGetPluginDescriptor] Removing adapter of: \n'" fazekasgy@37: << adapters[index]->getPlugKey() << "'\n" fazekasgy@37: << "The plugin has failed to construct. Hint: Check __init__() function." << endl; fazekasgy@37: pyExtensionManager.deleteModuleName(adapters[index]->getPlugKey()); fazekasgy@37: delete adapters[index]; fazekasgy@37: adapters.erase(adapters.begin()+index); fazekasgy@37: return 0; fazekasgy@37: } fazekasgy@37: fazekasgy@37: return tmp; fazekasgy@37: fazekasgy@37: } else return 0; fazekasgy@37: } fazekasgy@37: fazekasgy@37: fazekasgy@37: fazekasgy@37: fazekasgy@37: fazekasgy@37: fazekasgy@37: fazekasgy@37: