fazekasgy@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ fazekasgy@0: fazekasgy@0: /* fazekasgy@0: Vamp fazekasgy@0: fazekasgy@0: An API for audio analysis and feature extraction plugins. fazekasgy@0: fazekasgy@0: Centre for Digital Music, Queen Mary, University of London. fazekasgy@0: Copyright 2006 Chris Cannam. fazekasgy@0: fazekasgy@0: Permission is hereby granted, free of charge, to any person fazekasgy@0: obtaining a copy of this software and associated documentation fazekasgy@0: files (the "Software"), to deal in the Software without fazekasgy@0: restriction, including without limitation the rights to use, copy, fazekasgy@0: modify, merge, publish, distribute, sublicense, and/or sell copies fazekasgy@0: of the Software, and to permit persons to whom the Software is fazekasgy@0: furnished to do so, subject to the following conditions: fazekasgy@0: fazekasgy@0: The above copyright notice and this permission notice shall be fazekasgy@0: included in all copies or substantial portions of the Software. fazekasgy@0: fazekasgy@0: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, fazekasgy@0: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF fazekasgy@0: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND fazekasgy@0: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR fazekasgy@0: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF fazekasgy@0: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION fazekasgy@0: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fazekasgy@0: fazekasgy@0: Except as contained in this notice, the names of the Centre for fazekasgy@0: Digital Music; Queen Mary, University of London; and Chris Cannam fazekasgy@0: shall not be used in advertising or otherwise to promote the sale, fazekasgy@0: use or other dealings in this Software without prior written fazekasgy@0: authorization. fazekasgy@0: */ 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. fazekasgy@0: */ 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: fazekasgy@0: PyPluginAdapter(std::string pyPlugId, PyObject* pyInstance) : fazekasgy@0: PluginAdapterBase(), fazekasgy@0: m_plug(pyPlugId), fazekasgy@0: m_pyInstance(pyInstance) fazekasgy@0: { fazekasgy@0: cerr << "PyPluginAdapter:ctor:"<< adinstcount << ": " << m_plug << endl; fazekasgy@0: adinstcount++; fazekasgy@0: m_instanceCount = 0; fazekasgy@0: } fazekasgy@0: fazekasgy@0: ~PyPluginAdapter() fazekasgy@0: { fazekasgy@0: } fazekasgy@0: fazekasgy@0: protected: fazekasgy@0: Vamp::Plugin *createPlugin(float inputSampleRate) { fazekasgy@0: fazekasgy@0: std::string pclass = m_plug.substr(m_plug.rfind(':')+1,m_plug.size()-1); fazekasgy@0: std::string ppath = m_plug.substr(0,m_plug.rfind(pathsep)); fazekasgy@0: PyPlugin *plugin = new PyPlugin(m_plug,inputSampleRate,m_pyInstance); fazekasgy@0: m_instanceCount++; fazekasgy@0: cerr << "PyPluginAdapter::createPlugin:" << pclass << " (instance: " << m_instanceCount << ")" << endl; fazekasgy@0: return plugin; fazekasgy@0: fazekasgy@0: } fazekasgy@0: fazekasgy@0: std::string m_plug; fazekasgy@0: bool m_haveInitialized; fazekasgy@0: PyObject *m_pyInstance; fazekasgy@0: int m_instanceCount; fazekasgy@0: 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@19: 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@19: 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@19: cerr << " failed" << endl; cannam@16: return false; cannam@16: } cannam@19: #endif cannam@19: 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: } cannam@16: 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(); fazekasgy@0: //cerr << "# isPythonInitialized: " << isPythonInitialized << endl; fazekasgy@0: //cerr << "# haveScannedPlugins: " << haveScannedPlugins << endl; fazekasgy@0: fazekasgy@0: if (!haveScannedPlugins) { fazekasgy@8: fazekasgy@0: if (!isPythonInitialized) { fazekasgy@0: cannam@16: if (!preloadPython()) { cannam@16: cerr << "Warning: Could not preload Python." cannam@16: << " Dynamic loading in scripts will fail." << endl; cannam@16: } cannam@16: cannam@16: /* fazekasgy@0: void *pylib = 0; fazekasgy@0: fazekasgy@0: cerr << "Loading Python Interpreter at: " << pythonPath << endl; fazekasgy@1: //Preloading the binary allows the load of shared libs fazekasgy@0: //TODO: check how to do RTLD_NOW on Windows fazekasgy@2: #ifdef _WIN32 fazekasgy@0: pylib = LoadLibrary(pythonPath.c_str()); fazekasgy@0: #else fazekasgy@0: pylib = dlopen(pythonPath.c_str(), RTLD_NOW|RTLD_GLOBAL); fazekasgy@0: #endif fazekasgy@0: if (!pylib) cerr << "Warning: Could not preload Python." fazekasgy@1: << " Dynamic loading in scripts will fail." << endl; cannam@16: */ fazekasgy@0: Py_Initialize(); fazekasgy@0: PyEval_InitThreads(); fazekasgy@0: } else { fazekasgy@0: //Py_InitializeEx(1); fazekasgy@0: } fazekasgy@0: fazekasgy@0: vector pyPlugs; fazekasgy@0: vector pyPath; fazekasgy@0: vector pyInstances; 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 fazekasgy@0: pyInstances = scanner->getPyInstances(); fazekasgy@0: cerr << "Found " << pyInstances.size() << " Instances ...OK" << endl; fazekasgy@0: fazekasgy@0: for (size_t i = 0; i < pyPlugs.size(); ++i) { fazekasgy@0: adapters.push_back( new PyPluginAdapter(pyPlugs[i],pyInstances[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: