fazekasgy@31: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ fazekasgy@31: fazekasgy@31: /** fazekasgy@31: * This Vamp plugin is a wrapper for Python Scripts. (VamPy) fazekasgy@31: * Centre for Digital Music, Queen Mary, University of London. fazekasgy@31: * Copyright 2008, George Fazekas. fazekasgy@31: */ fazekasgy@31: fazekasgy@31: #include fazekasgy@31: #include "vamp/vamp.h" fazekasgy@31: #include "vamp-sdk/PluginAdapter.h" fazekasgy@31: #include "PyPlugScanner.h" fazekasgy@31: #include "PyPlugin.h" fazekasgy@31: // #include "host/pyRealTime.h" fazekasgy@31: #include "PyExtensionModule.h" fazekasgy@31: fazekasgy@31: fazekasgy@31: #ifdef _WIN32 fazekasgy@31: #define pathsep ('\\') fazekasgy@31: #include fazekasgy@31: #include fazekasgy@31: #else fazekasgy@31: #define pathsep ('/') fazekasgy@31: #include fazekasgy@31: #include fazekasgy@31: #endif fazekasgy@31: fazekasgy@31: using std::cerr; fazekasgy@31: using std::endl; fazekasgy@31: using std::string; fazekasgy@31: using std::vector; fazekasgy@31: fazekasgy@31: fazekasgy@31: //volatile bool mutex = false; fazekasgy@31: static int adinstcount; fazekasgy@31: static int totinstcount; fazekasgy@31: fazekasgy@31: class PyPluginAdapter : public Vamp::PluginAdapterBase fazekasgy@31: { fazekasgy@31: public: fazekasgy@31: PyPluginAdapter(std::string pyPlugId, PyObject* pyClass) : fazekasgy@31: PluginAdapterBase(), fazekasgy@31: m_plug(pyPlugId), fazekasgy@31: m_pyClass(pyClass) fazekasgy@31: { fazekasgy@31: cerr << "PyPluginAdapter:ctor:"<< adinstcount << ": " << m_plug << endl; fazekasgy@31: adinstcount++; fazekasgy@31: m_instanceCount = 0; fazekasgy@31: } fazekasgy@31: fazekasgy@31: ~PyPluginAdapter() fazekasgy@31: { fazekasgy@31: } fazekasgy@31: fazekasgy@31: protected: fazekasgy@31: Vamp::Plugin *createPlugin(float inputSampleRate) fazekasgy@31: { fazekasgy@31: try { fazekasgy@31: PyPlugin *plugin = new PyPlugin(m_plug, inputSampleRate, m_pyClass, totinstcount); fazekasgy@31: // m_instanceCount++; /// do this in the ctors fazekasgy@31: return plugin; fazekasgy@31: } catch (...) { fazekasgy@31: cerr << "PyPluginAdapter::createPlugin: Failed to construct PyPlugin" << endl; fazekasgy@31: return 0; fazekasgy@31: } fazekasgy@31: } fazekasgy@31: fazekasgy@31: std::string m_plug; fazekasgy@31: bool m_haveInitialized; fazekasgy@31: PyObject *m_pyClass; fazekasgy@31: int m_instanceCount; fazekasgy@31: }; fazekasgy@31: fazekasgy@31: fazekasgy@31: static std::vector adapters; fazekasgy@31: static bool haveScannedPlugins = false; fazekasgy@31: static bool haveVampyInitialised = false; fazekasgy@31: fazekasgy@31: static bool tryPreload(string name) fazekasgy@31: { fazekasgy@31: cerr << "Trying to load Python interpreter library \"" << name << "\"..."; fazekasgy@31: #ifdef _WIN32 fazekasgy@31: void *lib = LoadLibrary(name.c_str()); fazekasgy@31: if (!lib) { fazekasgy@31: cerr << " failed" << endl; fazekasgy@31: return false; fazekasgy@31: } fazekasgy@31: #else fazekasgy@31: void *lib = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL); fazekasgy@31: if (!lib) { fazekasgy@31: cerr << " failed" << endl; fazekasgy@31: return false; fazekasgy@31: } fazekasgy@31: #endif fazekasgy@31: cerr << " succeeded" << endl; fazekasgy@31: return true; fazekasgy@31: } fazekasgy@31: fazekasgy@31: static bool preloadPython() fazekasgy@31: { fazekasgy@31: #ifdef _WIN32 fazekasgy@31: // this doesn't seem to be necessary at all on Windows fazekasgy@31: return true; fazekasgy@31: #endif fazekasgy@31: fazekasgy@31: string pyver = Py_GetVersion(); fazekasgy@31: int dots = 2; fazekasgy@31: string shortver; fazekasgy@31: for (size_t i = 0; i < pyver.length(); ++i) { fazekasgy@31: if (pyver[i] == '.') { fazekasgy@31: if (--dots == 0) { fazekasgy@31: shortver = pyver.substr(0, i); fazekasgy@31: break; fazekasgy@31: } fazekasgy@31: } fazekasgy@31: } fazekasgy@31: cerr << "Short version: " << shortver << endl; fazekasgy@31: fazekasgy@31: vector pfxs; fazekasgy@31: pfxs.push_back(""); fazekasgy@31: pfxs.push_back(string(Py_GetExecPrefix()) + "/lib/"); fazekasgy@31: pfxs.push_back(string(Py_GetExecPrefix()) + "/"); fazekasgy@31: pfxs.push_back("/usr/lib/"); fazekasgy@31: pfxs.push_back("/usr/local/lib/"); fazekasgy@31: char buffer[5]; fazekasgy@31: fazekasgy@31: // hahaha! grossness is like a brother to us fazekasgy@31: #ifdef __APPLE__ fazekasgy@31: for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) { fazekasgy@31: for (int minor = 8; minor >= 0; --minor) { fazekasgy@31: sprintf(buffer, "%d", minor); fazekasgy@31: if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib." + buffer)) return true; fazekasgy@31: } fazekasgy@31: if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib")) return true; fazekasgy@31: if (tryPreload(pfxs[pfxidx] + string("libpython.dylib"))) return true; fazekasgy@31: } fazekasgy@31: #else fazekasgy@31: for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) { fazekasgy@31: for (int minor = 8; minor >= 0; --minor) { fazekasgy@31: sprintf(buffer, "%d", minor); fazekasgy@31: if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so." + buffer)) return true; fazekasgy@31: } fazekasgy@31: if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so")) return true; fazekasgy@31: if (tryPreload(pfxs[pfxidx] + string("libpython.so"))) return true; fazekasgy@31: } fazekasgy@31: #endif fazekasgy@31: fazekasgy@31: return false; fazekasgy@31: } fazekasgy@31: fazekasgy@31: /* This doesn't work: don't try it again. fazekasgy@31: static bool initPython() fazekasgy@31: { fazekasgy@31: // preloadPython(); fazekasgy@31: Py_Initialize(); fazekasgy@31: #ifndef _WIN32 fazekasgy@31: //set dlopen flags form Python fazekasgy@31: string pyCmd = "from sys import setdlopenflags\nimport dl\nsetdlopenflags(dl.RTLD_NOW|dl.RTLD_GLOBAL)\n"; fazekasgy@31: if (PyRun_SimpleString(pyCmd.c_str()) == -1) fazekasgy@31: { fazekasgy@31: cerr << "Warning: Could not set dlopen flasgs. Dynamic loading in scripts will fail." << endl; fazekasgy@31: return false; fazekasgy@31: } fazekasgy@31: #endif fazekasgy@31: PyEval_InitThreads(); fazekasgy@31: return Py_IsInitialized(); fazekasgy@31: } fazekasgy@31: */ fazekasgy@31: fazekasgy@31: const VampPluginDescriptor fazekasgy@31: *vampGetPluginDescriptor(unsigned int version,unsigned int index) fazekasgy@31: { fazekasgy@31: if (version < 1) return 0; fazekasgy@31: fazekasgy@31: int isPythonInitialized = Py_IsInitialized(); fazekasgy@31: cerr << "# isPythonInitialized: " << isPythonInitialized << endl; fazekasgy@31: cerr << "# haveScannedPlugins: " << haveScannedPlugins << endl; fazekasgy@31: fazekasgy@31: if (!haveScannedPlugins) { fazekasgy@31: fazekasgy@31: if (!isPythonInitialized){ fazekasgy@31: fazekasgy@31: if (!preloadPython()) fazekasgy@31: cerr << "Warning: Could not preload Python. Dynamic loading in scripts will fail." << endl; fazekasgy@31: fazekasgy@31: int ext = PyImport_AppendInittab("vampy",initvampy); fazekasgy@31: if (ext == -1) cerr << "Extension unsuccessful." << endl; fazekasgy@31: else cerr << "Extension successful." << endl; fazekasgy@31: Py_Initialize(); fazekasgy@31: initvampy(); fazekasgy@31: // if (!PyImport_ImportModule("vampy")) fazekasgy@31: // cerr << "Could not import extension." << endl; fazekasgy@31: fazekasgy@31: // Py_InitModule("vampy", VampyMethods); fazekasgy@31: fazekasgy@31: // initpyRealTime(); fazekasgy@31: cerr << "# isPythonInitialized after initialize: " << Py_IsInitialized() << endl; fazekasgy@31: // PyEval_InitThreads(); //not sure why this was needed fazekasgy@31: } fazekasgy@31: fazekasgy@31: vector pyPlugs; fazekasgy@31: vector pyPath; fazekasgy@31: vector pyClasses; fazekasgy@31: static PyPlugScanner *scanner; fazekasgy@31: fazekasgy@31: //Scanning Plugins fazekasgy@31: cerr << "Scanning PyPlugins" << endl; fazekasgy@31: scanner = PyPlugScanner::getInstance(); fazekasgy@31: pyPath=scanner->getAllValidPath(); fazekasgy@31: //add this as extra path for development fazekasgy@31: //pyPath.push_back("/Users/Shared/Development/vamp-experiments"); fazekasgy@31: scanner->setPath(pyPath); fazekasgy@31: pyPlugs = scanner->getPyPlugs(); fazekasgy@31: cerr << "Found " << pyPlugs.size() << " Scripts ...OK" << endl; fazekasgy@31: //TODO: this should support multiple classes per script (?) fazekasgy@31: pyClasses = scanner->getPyClasses(); fazekasgy@31: cerr << "Found " << pyClasses.size() << " Classes ...OK" << endl; fazekasgy@31: fazekasgy@31: for (size_t i = 0; i < pyPlugs.size(); ++i) { fazekasgy@31: adapters.push_back( new PyPluginAdapter(pyPlugs[i],pyClasses[i])); fazekasgy@31: } fazekasgy@31: haveScannedPlugins=true; fazekasgy@31: // if (!haveVampyInitialised) { fazekasgy@31: // // PyImport_AddModule("vampy"); fazekasgy@31: // // if (!PyImport_ImportModule("vampy")) fazekasgy@31: // cerr << "Could not import extension." << endl; fazekasgy@31: // initvampy(); fazekasgy@31: // haveVampyInitialised = true; fazekasgy@31: // cerr << "Extension initialised from main." << endl; fazekasgy@31: // } fazekasgy@31: } fazekasgy@31: fazekasgy@31: cerr << "Accessing adapter index: " << index << " (adapters: " << adapters.size() << ")" << endl; fazekasgy@31: if (indexgetDescriptor(); fazekasgy@31: return tmp; fazekasgy@31: } else return 0; fazekasgy@31: fazekasgy@31: fazekasgy@31: } fazekasgy@31: fazekasgy@31: fazekasgy@31: fazekasgy@31: fazekasgy@31: fazekasgy@31: fazekasgy@31: fazekasgy@31: