annotate pyvamp-main.cpp @ 24:7d28bed0864e

* Rearrange Python plugin construction. Formerly, the PyPluginAdapter has retained a single plugin instance pointer for each plugin found, and its createPlugin method has simply returned a new PyPlugin object wrapping the same instance pointer. This has a couple of negative consequences: - Because construction of the actual Python instance occurred before the wrapper was constructed, it was not possible to pass arguments (i.e. the sample rate) from the wrapper constructor to the Python plugin instance constructor -- they had to be passed later, to initialise, disadvantaging those plugins that would like to use the sample rate for parameter & step/block size calculations etc - Because there was only a single Python plugin instance, it was not possible to run more than one instance at once with any isolation This rework instead stores the Python class pointer (rather than instance pointer) in the PyPluginAdapter, and each PyPlugin wrapper instance creates its own Python plugin instance. What could possibly go wrong?
author cannam
date Mon, 17 Aug 2009 15:22:06 +0000
parents 812fbde7eca5
children 7648f3f2fa14
rev   line source
fazekasgy@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
fazekasgy@0 2
fazekasgy@0 3 /**
cannam@7 4 * This Vamp plugin is a wrapper for Python Scripts. (VamPy)
fazekasgy@0 5 * Centre for Digital Music, Queen Mary, University of London.
fazekasgy@0 6 * Copyright 2008, George Fazekas.
cannam@24 7 */
fazekasgy@0 8
cannam@3 9 #include <Python.h>
fazekasgy@0 10 #include "vamp/vamp.h"
fazekasgy@0 11 #include "vamp-sdk/PluginAdapter.h"
fazekasgy@0 12 #include "PyPlugScanner.h"
fazekasgy@0 13 #include "PyPlugin.h"
fazekasgy@0 14
fazekasgy@0 15 #ifdef _WIN32
fazekasgy@0 16 #define pathsep ('\\')
fazekasgy@0 17 #include <windows.h>
fazekasgy@0 18 #include <tchar.h>
fazekasgy@0 19 #else
fazekasgy@0 20 #define pathsep ('/')
fazekasgy@0 21 #include <dirent.h>
fazekasgy@0 22 #include <dlfcn.h>
fazekasgy@0 23 #endif
fazekasgy@0 24
fazekasgy@0 25 using std::cerr;
fazekasgy@0 26 using std::endl;
fazekasgy@0 27 using std::string;
fazekasgy@0 28 using std::vector;
fazekasgy@0 29
fazekasgy@6 30 //volatile bool mutex = false;
fazekasgy@0 31 static int adinstcount;
fazekasgy@0 32
fazekasgy@0 33 class PyPluginAdapter : public Vamp::PluginAdapterBase
fazekasgy@0 34 {
fazekasgy@0 35 public:
cannam@24 36 PyPluginAdapter(std::string pyPlugId, PyObject* pyClass) :
cannam@24 37 PluginAdapterBase(),
cannam@24 38 m_plug(pyPlugId),
cannam@24 39 m_pyClass(pyClass)
cannam@24 40 {
cannam@24 41 cerr << "PyPluginAdapter:ctor:"<< adinstcount << ": " << m_plug << endl;
cannam@24 42 adinstcount++;
cannam@24 43 m_instanceCount = 0;
cannam@24 44 }
cannam@24 45
cannam@24 46 ~PyPluginAdapter()
cannam@24 47 {
cannam@24 48 }
cannam@24 49
fazekasgy@0 50 protected:
cannam@24 51 Vamp::Plugin *createPlugin(float inputSampleRate)
cannam@24 52 {
cannam@24 53 try {
cannam@24 54 PyPlugin *plugin = new PyPlugin(m_plug, inputSampleRate, m_pyClass);
cannam@24 55 m_instanceCount++;
cannam@24 56 return plugin;
cannam@24 57 } catch (...) {
cannam@24 58 cerr << "PyPluginAdapter::createPlugin: Failed to construct PyPlugin" << endl;
cannam@24 59 return 0;
cannam@24 60 }
cannam@24 61 }
cannam@24 62
cannam@24 63 std::string m_plug;
cannam@24 64 bool m_haveInitialized;
cannam@24 65 PyObject *m_pyClass;
cannam@24 66 int m_instanceCount;
fazekasgy@0 67 };
fazekasgy@0 68
fazekasgy@0 69
fazekasgy@0 70 static std::vector<PyPluginAdapter *> adapters;
fazekasgy@0 71 static bool haveScannedPlugins = false;
fazekasgy@0 72
cannam@16 73 static bool tryPreload(string name)
cannam@16 74 {
cannam@19 75 cerr << "Trying to load Python interpreter library \"" << name << "\"...";
cannam@19 76 #ifdef _WIN32
cannam@19 77 void *lib = LoadLibrary(name.c_str());
cannam@19 78 if (!lib) {
cannam@19 79 cerr << " failed" << endl;
cannam@19 80 return false;
cannam@19 81 }
cannam@19 82 #else
cannam@16 83 void *lib = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL);
cannam@16 84 if (!lib) {
cannam@19 85 cerr << " failed" << endl;
cannam@16 86 return false;
cannam@16 87 }
cannam@19 88 #endif
cannam@19 89 cerr << " succeeded" << endl;
cannam@16 90 return true;
cannam@16 91 }
cannam@16 92
cannam@16 93 static bool preloadPython()
cannam@16 94 {
cannam@20 95 #ifdef _WIN32
cannam@20 96 // this doesn't seem to be necessary at all on Windows
cannam@20 97 return true;
cannam@20 98 #endif
cannam@16 99
cannam@16 100 string pyver = Py_GetVersion();
cannam@16 101 int dots = 2;
cannam@16 102 string shortver;
cannam@18 103 for (size_t i = 0; i < pyver.length(); ++i) {
cannam@16 104 if (pyver[i] == '.') {
cannam@16 105 if (--dots == 0) {
cannam@16 106 shortver = pyver.substr(0, i);
cannam@16 107 break;
cannam@16 108 }
cannam@16 109 }
cannam@16 110 }
cannam@16 111 cerr << "Short version: " << shortver << endl;
cannam@16 112
cannam@16 113 vector<string> pfxs;
cannam@16 114 pfxs.push_back("");
cannam@16 115 pfxs.push_back(string(Py_GetExecPrefix()) + "/lib/");
cannam@16 116 pfxs.push_back(string(Py_GetExecPrefix()) + "/");
cannam@16 117 pfxs.push_back("/usr/lib/");
cannam@16 118 pfxs.push_back("/usr/local/lib/");
cannam@16 119 char buffer[5];
cannam@16 120
cannam@16 121 // hahaha! grossness is like a brother to us
cannam@17 122 #ifdef __APPLE__
cannam@18 123 for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) {
cannam@17 124 for (int minor = 8; minor >= 0; --minor) {
cannam@17 125 sprintf(buffer, "%d", minor);
cannam@17 126 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib." + buffer)) return true;
cannam@17 127 }
cannam@17 128 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib")) return true;
cannam@17 129 if (tryPreload(pfxs[pfxidx] + string("libpython.dylib"))) return true;
cannam@17 130 }
cannam@17 131 #else
cannam@18 132 for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) {
cannam@16 133 for (int minor = 8; minor >= 0; --minor) {
cannam@16 134 sprintf(buffer, "%d", minor);
cannam@16 135 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so." + buffer)) return true;
cannam@16 136 }
cannam@16 137 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so")) return true;
cannam@16 138 if (tryPreload(pfxs[pfxidx] + string("libpython.so"))) return true;
cannam@16 139 }
cannam@17 140 #endif
cannam@16 141
cannam@16 142 return false;
cannam@16 143 }
cannam@16 144
cannam@16 145
fazekasgy@0 146 const VampPluginDescriptor
fazekasgy@0 147 *vampGetPluginDescriptor(unsigned int version,unsigned int index)
fazekasgy@0 148 {
fazekasgy@0 149 if (version < 1) return 0;
fazekasgy@0 150
fazekasgy@0 151 int isPythonInitialized = Py_IsInitialized();
cannam@24 152 cerr << "# isPythonInitialized: " << isPythonInitialized << endl;
cannam@24 153 cerr << "# haveScannedPlugins: " << haveScannedPlugins << endl;
fazekasgy@0 154
fazekasgy@0 155 if (!haveScannedPlugins) {
fazekasgy@8 156
fazekasgy@0 157 if (!isPythonInitialized) {
fazekasgy@0 158
cannam@16 159 if (!preloadPython()) {
cannam@16 160 cerr << "Warning: Could not preload Python."
cannam@16 161 << " Dynamic loading in scripts will fail." << endl;
cannam@16 162 }
cannam@16 163
cannam@16 164 /*
fazekasgy@0 165 void *pylib = 0;
fazekasgy@0 166
fazekasgy@0 167 cerr << "Loading Python Interpreter at: " << pythonPath << endl;
fazekasgy@1 168 //Preloading the binary allows the load of shared libs
fazekasgy@0 169 //TODO: check how to do RTLD_NOW on Windows
fazekasgy@2 170 #ifdef _WIN32
fazekasgy@0 171 pylib = LoadLibrary(pythonPath.c_str());
fazekasgy@0 172 #else
fazekasgy@0 173 pylib = dlopen(pythonPath.c_str(), RTLD_NOW|RTLD_GLOBAL);
fazekasgy@0 174 #endif
fazekasgy@0 175 if (!pylib) cerr << "Warning: Could not preload Python."
fazekasgy@1 176 << " Dynamic loading in scripts will fail." << endl;
cannam@16 177 */
fazekasgy@0 178 Py_Initialize();
cannam@24 179 cerr << "# isPythonInitialized after initialize: " << Py_IsInitialized() << endl;
fazekasgy@0 180 PyEval_InitThreads();
fazekasgy@0 181 } else {
fazekasgy@0 182 //Py_InitializeEx(1);
fazekasgy@0 183 }
fazekasgy@0 184
fazekasgy@0 185 vector<string> pyPlugs;
fazekasgy@0 186 vector<string> pyPath;
cannam@24 187 vector<PyObject *> pyClasses;
fazekasgy@0 188 static PyPlugScanner *scanner;
fazekasgy@0 189
fazekasgy@0 190 //Scanning Plugins
fazekasgy@0 191 cerr << "Scanning PyPlugins" << endl;
fazekasgy@0 192 scanner = PyPlugScanner::getInstance();
fazekasgy@0 193 pyPath=scanner->getAllValidPath();
fazekasgy@0 194 //add this as extra path for development
fazekasgy@6 195 //pyPath.push_back("/Users/Shared/Development/vamp-experiments");
fazekasgy@0 196 scanner->setPath(pyPath);
fazekasgy@0 197 pyPlugs = scanner->getPyPlugs();
fazekasgy@0 198 cerr << "Found " << pyPlugs.size() << " Scripts ...OK" << endl;
fazekasgy@0 199 //TODO: this will support multiple classes per script
cannam@24 200 pyClasses = scanner->getPyClasses();
cannam@24 201 cerr << "Found " << pyClasses.size() << " Classes ...OK" << endl;
fazekasgy@0 202
fazekasgy@0 203 for (size_t i = 0; i < pyPlugs.size(); ++i) {
cannam@24 204 adapters.push_back( new PyPluginAdapter(pyPlugs[i],pyClasses[i]));
fazekasgy@0 205 }
fazekasgy@0 206 haveScannedPlugins=true;
fazekasgy@6 207
fazekasgy@0 208 }
fazekasgy@0 209
fazekasgy@0 210 cerr << "Accessing adapter index: " << index << " (adapters: " << adapters.size() << ")" << endl;
fazekasgy@0 211 if (index<adapters.size()) {
fazekasgy@0 212 const VampPluginDescriptor *tmp = adapters[index]->getDescriptor();
fazekasgy@0 213 return tmp;
fazekasgy@0 214 } else return 0;
fazekasgy@0 215
fazekasgy@0 216
fazekasgy@0 217 }
fazekasgy@0 218
fazekasgy@0 219
fazekasgy@0 220
fazekasgy@0 221
fazekasgy@0 222
fazekasgy@0 223
fazekasgy@0 224
fazekasgy@0 225