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