annotate vampy-main.cpp @ 42:06bbfa2aa51a

* Merge r1090 from earlier vampy2 branch
author cannam
date Mon, 05 Oct 2009 12:52:46 +0000
parents 27bab3a16c9a
children c1e4f706ca9a
rev   line source
fazekasgy@37 1 /*
fazekasgy@37 2
fazekasgy@37 3 * Vampy : This plugin is a wrapper around the Vamp plugin API.
fazekasgy@37 4 * It allows for writing Vamp plugins in Python.
fazekasgy@37 5
fazekasgy@37 6 * Centre for Digital Music, Queen Mary University of London.
fazekasgy@37 7 * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources
fazekasgy@37 8 * for licence information.)
fazekasgy@37 9
fazekasgy@37 10 */
fazekasgy@37 11
fazekasgy@37 12 #include <Python.h>
fazekasgy@37 13
fazekasgy@37 14 #ifdef HAVE_NUMPY
fazekasgy@37 15 #define PY_ARRAY_UNIQUE_SYMBOL VAMPY_ARRAY_API
fazekasgy@37 16 #include "numpy/arrayobject.h"
fazekasgy@37 17 #endif
fazekasgy@37 18
fazekasgy@37 19 #include "vamp/vamp.h"
fazekasgy@37 20 #include "vamp-sdk/PluginAdapter.h"
fazekasgy@37 21 #include "PyPlugScanner.h"
fazekasgy@37 22 #include "PyPlugin.h"
fazekasgy@37 23 #include "PyExtensionModule.h"
fazekasgy@37 24 #include "PyExtensionManager.h"
fazekasgy@37 25
fazekasgy@37 26
fazekasgy@37 27 #ifdef _WIN32
fazekasgy@37 28 #define pathsep ('\\')
fazekasgy@37 29 #include <windows.h>
fazekasgy@37 30 #include <tchar.h>
fazekasgy@37 31 #else
fazekasgy@37 32 #define pathsep ('/')
fazekasgy@37 33 #include <dirent.h>
fazekasgy@37 34 #include <dlfcn.h>
fazekasgy@37 35 #endif
fazekasgy@37 36
fazekasgy@37 37 using std::cerr;
fazekasgy@37 38 using std::endl;
fazekasgy@37 39 using std::string;
fazekasgy@37 40 using std::vector;
fazekasgy@37 41
fazekasgy@37 42 static int adinstcount;
fazekasgy@37 43 static int totinstcount;
fazekasgy@37 44
fazekasgy@37 45 class PyPluginAdapter : public Vamp::PluginAdapterBase
fazekasgy@37 46 {
fazekasgy@37 47 public:
fazekasgy@37 48 PyPluginAdapter(std::string pyPlugId, PyObject* pyClass) :
fazekasgy@37 49 PluginAdapterBase(),
fazekasgy@37 50 m_plug(pyPlugId),
fazekasgy@37 51 m_pyClass(pyClass),
fazekasgy@37 52 m_failed(false)
fazekasgy@37 53 {
fazekasgy@37 54 cerr << "PyPluginAdapter:ctor:"<< adinstcount << ": " << m_plug << endl;
fazekasgy@37 55 adinstcount++;
fazekasgy@37 56 }
fazekasgy@37 57
fazekasgy@37 58 ~PyPluginAdapter()
fazekasgy@37 59 {
fazekasgy@37 60 }
fazekasgy@37 61
fazekasgy@37 62 bool failed() { return m_failed; }
fazekasgy@37 63 std::string getPlugKey() { return m_plug; }
fazekasgy@37 64
fazekasgy@37 65 protected:
fazekasgy@37 66 Vamp::Plugin *createPlugin(float inputSampleRate)
fazekasgy@37 67 {
fazekasgy@37 68 try {
fazekasgy@37 69 PyPlugin *plugin = new PyPlugin(m_plug, inputSampleRate, m_pyClass, totinstcount);
fazekasgy@37 70 return plugin;
fazekasgy@37 71 } catch (...) {
fazekasgy@37 72 cerr << "PyPluginAdapter::createPlugin: Failed to construct PyPlugin" << endl;
fazekasgy@37 73 // any plugin with syntax errors will fail to construct
fazekasgy@37 74 m_failed = true;
fazekasgy@37 75 return 0;
fazekasgy@37 76 }
fazekasgy@37 77 }
fazekasgy@37 78
fazekasgy@37 79 std::string m_plug;
fazekasgy@37 80 PyObject *m_pyClass;
fazekasgy@37 81 bool m_failed;
fazekasgy@37 82 };
fazekasgy@37 83
fazekasgy@37 84 static void array_API_initialiser()
fazekasgy@37 85 {
fazekasgy@37 86 /// numpy C-API requirement
fazekasgy@37 87 #ifdef HAVE_NUMPY
fazekasgy@37 88 import_array();
fazekasgy@37 89 if(NPY_VERSION != PyArray_GetNDArrayCVersion())
fazekasgy@37 90 cerr << "Warning: Numpy ABI version mismatch. (Build version: "
fazekasgy@37 91 << NPY_VERSION << " Runtime version: " << PyArray_GetNDArrayCVersion() << ")" << endl;
fazekasgy@37 92 #endif
fazekasgy@37 93 }
fazekasgy@37 94
fazekasgy@37 95
fazekasgy@37 96 static std::vector<PyPluginAdapter *> adapters;
fazekasgy@37 97 static bool haveScannedPlugins = false;
fazekasgy@37 98
fazekasgy@37 99 static bool tryPreload(string name)
fazekasgy@37 100 {
fazekasgy@37 101 #ifdef _WIN32
fazekasgy@37 102 void *lib = LoadLibrary(name.c_str());
fazekasgy@37 103 if (!lib) {
fazekasgy@37 104 return false;
fazekasgy@37 105 }
fazekasgy@37 106 #else
fazekasgy@37 107 void *lib = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL);
fazekasgy@37 108 if (!lib) {
fazekasgy@37 109 return false;
fazekasgy@37 110 }
fazekasgy@37 111 #endif
fazekasgy@37 112 return true;
fazekasgy@37 113 }
fazekasgy@37 114
fazekasgy@37 115 static bool preloadPython()
fazekasgy@37 116 {
fazekasgy@37 117 #ifdef _WIN32
fazekasgy@37 118 // this doesn't seem to be necessary at all on Windows
fazekasgy@37 119 return true;
fazekasgy@37 120 #endif
fazekasgy@37 121
fazekasgy@37 122 string pyver = Py_GetVersion();
fazekasgy@37 123 int dots = 2;
fazekasgy@37 124 string shortver;
fazekasgy@37 125 for (size_t i = 0; i < pyver.length(); ++i) {
fazekasgy@37 126 if (pyver[i] == '.') {
fazekasgy@37 127 if (--dots == 0) {
fazekasgy@37 128 shortver = pyver.substr(0, i);
fazekasgy@37 129 break;
fazekasgy@37 130 }
fazekasgy@37 131 }
fazekasgy@37 132 }
fazekasgy@37 133 cerr << "Short version: " << shortver << endl;
fazekasgy@37 134 // this is useful to find out where the loaded library might be loaded from
fazekasgy@37 135 cerr << "Python exec prefix: " << Py_GetExecPrefix() << endl;
fazekasgy@37 136
fazekasgy@37 137 vector<string> pfxs;
fazekasgy@37 138 pfxs.push_back("");
fazekasgy@37 139 pfxs.push_back(string(Py_GetExecPrefix()) + "/lib/");
fazekasgy@37 140 pfxs.push_back(string(Py_GetExecPrefix()) + "/");
fazekasgy@37 141 pfxs.push_back("/usr/lib/");
fazekasgy@37 142 pfxs.push_back("/usr/local/lib/");
fazekasgy@37 143 char buffer[5];
fazekasgy@37 144
fazekasgy@37 145 // hahaha! grossness is like a brother to us
fazekasgy@37 146 #ifdef __APPLE__
fazekasgy@37 147 for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) {
fazekasgy@37 148 for (int minor = 8; minor >= 0; --minor) {
fazekasgy@37 149 sprintf(buffer, "%d", minor);
fazekasgy@37 150 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib." + buffer)) return true;
fazekasgy@37 151 }
fazekasgy@37 152 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib")) return true;
fazekasgy@37 153 if (tryPreload(pfxs[pfxidx] + string("libpython.dylib"))) return true;
fazekasgy@37 154 }
fazekasgy@37 155 #else
fazekasgy@37 156 for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) {
fazekasgy@37 157 for (int minor = 8; minor >= 0; --minor) {
fazekasgy@37 158 sprintf(buffer, "%d", minor);
fazekasgy@37 159 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so." + buffer)) return true;
fazekasgy@37 160 }
fazekasgy@37 161 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so")) return true;
fazekasgy@37 162 if (tryPreload(pfxs[pfxidx] + string("libpython.so"))) return true;
fazekasgy@37 163 }
fazekasgy@37 164 #endif
fazekasgy@37 165
fazekasgy@37 166 return false;
fazekasgy@37 167 }
fazekasgy@37 168
fazekasgy@37 169
fazekasgy@37 170 static PyExtensionManager pyExtensionManager;
fazekasgy@37 171
fazekasgy@37 172 const VampPluginDescriptor
fazekasgy@37 173 *vampGetPluginDescriptor(unsigned int version,unsigned int index)
fazekasgy@37 174 {
fazekasgy@37 175 if (version < 1) return 0;
fazekasgy@37 176
fazekasgy@37 177 int isPythonInitialized = Py_IsInitialized();
fazekasgy@37 178 cerr << "# isPythonInitialized: " << isPythonInitialized << endl;
fazekasgy@37 179 cerr << "# haveScannedPlugins: " << haveScannedPlugins << endl;
fazekasgy@37 180
fazekasgy@37 181 if (!haveScannedPlugins) {
fazekasgy@37 182
fazekasgy@37 183 if (!isPythonInitialized){
fazekasgy@37 184
fazekasgy@37 185 if (!preloadPython())
fazekasgy@37 186 cerr << "Warning: Could not preload Python. Dynamic loading in scripts will fail." << endl;
fazekasgy@37 187 if (PyImport_AppendInittab("vampy",initvampy) != 0)
fazekasgy@37 188 cerr << "Warning: Extension module could not be added to module inittab." << endl;
fazekasgy@37 189 Py_Initialize();
fazekasgy@37 190 initvampy();
fazekasgy@37 191 #ifdef _DEBUG
fazekasgy@37 192 cerr << "# isPythonInitialized after initialize: " << Py_IsInitialized() << endl;
fazekasgy@37 193 #endif
fazekasgy@37 194 }
fazekasgy@37 195
fazekasgy@37 196 vector<string> pyPlugs;
fazekasgy@37 197 vector<string> pyPath;
fazekasgy@37 198 vector<PyObject *> pyClasses;
fazekasgy@37 199 static PyPlugScanner *scanner;
fazekasgy@37 200
fazekasgy@37 201 //Scanning Plugins
fazekasgy@37 202 cerr << "Scanning Vampy Plugins" << endl;
fazekasgy@37 203 scanner = PyPlugScanner::getInstance();
fazekasgy@37 204
fazekasgy@37 205 // added env. varable support VAMPY_EXTPATH
fazekasgy@37 206 pyPath=scanner->getAllValidPath();
fazekasgy@37 207 scanner->setPath(pyPath);
fazekasgy@37 208
fazekasgy@37 209 // added env. variable support:
fazekasgy@37 210 // VAMPY_COMPILED=1 to recognise .pyc files (default is 1)
fazekasgy@37 211 pyPlugs = scanner->getPyPlugs();
fazekasgy@37 212
fazekasgy@37 213 cerr << "Found " << pyPlugs.size() << " Scripts." << endl;
fazekasgy@37 214 //TODO: should this support multiple classes per script (?)
fazekasgy@37 215 pyClasses = scanner->getPyClasses();
fazekasgy@37 216 cerr << "Found " << pyClasses.size() << " Classes." << endl;
fazekasgy@37 217
fazekasgy@37 218 for (size_t i = 0; i < pyPlugs.size(); ++i) {
fazekasgy@37 219 adapters.push_back( new PyPluginAdapter(pyPlugs[i],pyClasses[i]));
fazekasgy@37 220 }
fazekasgy@37 221 pyExtensionManager.setPlugModuleNames(pyPlugs);
fazekasgy@37 222 pyExtensionManager.initExtension();
fazekasgy@37 223 array_API_initialiser();
fazekasgy@37 224 haveScannedPlugins=true;
fazekasgy@37 225 }
fazekasgy@37 226
fazekasgy@37 227 #ifdef _DEBUG
fazekasgy@37 228 cerr << "Accessing adapter index: " << index << " (adapters: " << adapters.size() << ")" << endl;
fazekasgy@37 229 #endif
fazekasgy@37 230
fazekasgy@37 231 if (index<adapters.size()) {
fazekasgy@37 232
fazekasgy@37 233 const VampPluginDescriptor *tmp = adapters[index]->getDescriptor();
fazekasgy@37 234
fazekasgy@37 235 if (adapters[index]->failed()) {
fazekasgy@37 236 cerr << "\nERROR: [in vampGetPluginDescriptor] Removing adapter of: \n'"
fazekasgy@37 237 << adapters[index]->getPlugKey() << "'\n"
fazekasgy@37 238 << "The plugin has failed to construct. Hint: Check __init__() function." << endl;
fazekasgy@37 239 pyExtensionManager.deleteModuleName(adapters[index]->getPlugKey());
fazekasgy@37 240 delete adapters[index];
fazekasgy@37 241 adapters.erase(adapters.begin()+index);
fazekasgy@37 242 return 0;
fazekasgy@37 243 }
fazekasgy@37 244
fazekasgy@37 245 return tmp;
fazekasgy@37 246
fazekasgy@37 247 } else return 0;
fazekasgy@37 248 }
fazekasgy@37 249
fazekasgy@37 250
fazekasgy@37 251
fazekasgy@37 252
fazekasgy@37 253
fazekasgy@37 254
fazekasgy@37 255
fazekasgy@37 256