fazekasgy@31
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
fazekasgy@31
|
2
|
fazekasgy@31
|
3 /**
|
fazekasgy@31
|
4 * This Vamp plugin is a wrapper for Python Scripts. (VamPy)
|
fazekasgy@31
|
5 * Centre for Digital Music, Queen Mary, University of London.
|
fazekasgy@31
|
6 * Copyright 2008, George Fazekas.
|
fazekasgy@31
|
7 */
|
fazekasgy@31
|
8
|
fazekasgy@31
|
9 #include <Python.h>
|
fazekasgy@31
|
10 #include "vamp/vamp.h"
|
fazekasgy@31
|
11 #include "vamp-sdk/PluginAdapter.h"
|
fazekasgy@31
|
12 #include "PyPlugScanner.h"
|
fazekasgy@31
|
13 #include "PyPlugin.h"
|
fazekasgy@31
|
14 // #include "host/pyRealTime.h"
|
fazekasgy@31
|
15 #include "PyExtensionModule.h"
|
fazekasgy@31
|
16
|
fazekasgy@31
|
17
|
fazekasgy@31
|
18 #ifdef _WIN32
|
fazekasgy@31
|
19 #define pathsep ('\\')
|
fazekasgy@31
|
20 #include <windows.h>
|
fazekasgy@31
|
21 #include <tchar.h>
|
fazekasgy@31
|
22 #else
|
fazekasgy@31
|
23 #define pathsep ('/')
|
fazekasgy@31
|
24 #include <dirent.h>
|
fazekasgy@31
|
25 #include <dlfcn.h>
|
fazekasgy@31
|
26 #endif
|
fazekasgy@31
|
27
|
fazekasgy@31
|
28 using std::cerr;
|
fazekasgy@31
|
29 using std::endl;
|
fazekasgy@31
|
30 using std::string;
|
fazekasgy@31
|
31 using std::vector;
|
fazekasgy@31
|
32
|
fazekasgy@31
|
33
|
fazekasgy@31
|
34 //volatile bool mutex = false;
|
fazekasgy@31
|
35 static int adinstcount;
|
fazekasgy@31
|
36 static int totinstcount;
|
fazekasgy@31
|
37
|
fazekasgy@31
|
38 class PyPluginAdapter : public Vamp::PluginAdapterBase
|
fazekasgy@31
|
39 {
|
fazekasgy@31
|
40 public:
|
fazekasgy@31
|
41 PyPluginAdapter(std::string pyPlugId, PyObject* pyClass) :
|
fazekasgy@31
|
42 PluginAdapterBase(),
|
fazekasgy@31
|
43 m_plug(pyPlugId),
|
fazekasgy@31
|
44 m_pyClass(pyClass)
|
fazekasgy@31
|
45 {
|
fazekasgy@31
|
46 cerr << "PyPluginAdapter:ctor:"<< adinstcount << ": " << m_plug << endl;
|
fazekasgy@31
|
47 adinstcount++;
|
fazekasgy@31
|
48 m_instanceCount = 0;
|
fazekasgy@31
|
49 }
|
fazekasgy@31
|
50
|
fazekasgy@31
|
51 ~PyPluginAdapter()
|
fazekasgy@31
|
52 {
|
fazekasgy@31
|
53 }
|
fazekasgy@31
|
54
|
fazekasgy@31
|
55 protected:
|
fazekasgy@31
|
56 Vamp::Plugin *createPlugin(float inputSampleRate)
|
fazekasgy@31
|
57 {
|
fazekasgy@31
|
58 try {
|
fazekasgy@31
|
59 PyPlugin *plugin = new PyPlugin(m_plug, inputSampleRate, m_pyClass, totinstcount);
|
fazekasgy@31
|
60 // m_instanceCount++; /// do this in the ctors
|
fazekasgy@31
|
61 return plugin;
|
fazekasgy@31
|
62 } catch (...) {
|
fazekasgy@31
|
63 cerr << "PyPluginAdapter::createPlugin: Failed to construct PyPlugin" << endl;
|
fazekasgy@31
|
64 return 0;
|
fazekasgy@31
|
65 }
|
fazekasgy@31
|
66 }
|
fazekasgy@31
|
67
|
fazekasgy@31
|
68 std::string m_plug;
|
fazekasgy@31
|
69 bool m_haveInitialized;
|
fazekasgy@31
|
70 PyObject *m_pyClass;
|
fazekasgy@31
|
71 int m_instanceCount;
|
fazekasgy@31
|
72 };
|
fazekasgy@31
|
73
|
fazekasgy@31
|
74
|
fazekasgy@31
|
75 static std::vector<PyPluginAdapter *> adapters;
|
fazekasgy@31
|
76 static bool haveScannedPlugins = false;
|
fazekasgy@31
|
77 static bool haveVampyInitialised = false;
|
fazekasgy@31
|
78
|
fazekasgy@31
|
79 static bool tryPreload(string name)
|
fazekasgy@31
|
80 {
|
fazekasgy@31
|
81 cerr << "Trying to load Python interpreter library \"" << name << "\"...";
|
fazekasgy@31
|
82 #ifdef _WIN32
|
fazekasgy@31
|
83 void *lib = LoadLibrary(name.c_str());
|
fazekasgy@31
|
84 if (!lib) {
|
fazekasgy@31
|
85 cerr << " failed" << endl;
|
fazekasgy@31
|
86 return false;
|
fazekasgy@31
|
87 }
|
fazekasgy@31
|
88 #else
|
fazekasgy@31
|
89 void *lib = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL);
|
fazekasgy@31
|
90 if (!lib) {
|
fazekasgy@31
|
91 cerr << " failed" << endl;
|
fazekasgy@31
|
92 return false;
|
fazekasgy@31
|
93 }
|
fazekasgy@31
|
94 #endif
|
fazekasgy@31
|
95 cerr << " succeeded" << endl;
|
fazekasgy@31
|
96 return true;
|
fazekasgy@31
|
97 }
|
fazekasgy@31
|
98
|
fazekasgy@31
|
99 static bool preloadPython()
|
fazekasgy@31
|
100 {
|
fazekasgy@31
|
101 #ifdef _WIN32
|
fazekasgy@31
|
102 // this doesn't seem to be necessary at all on Windows
|
fazekasgy@31
|
103 return true;
|
fazekasgy@31
|
104 #endif
|
fazekasgy@31
|
105
|
fazekasgy@31
|
106 string pyver = Py_GetVersion();
|
fazekasgy@31
|
107 int dots = 2;
|
fazekasgy@31
|
108 string shortver;
|
fazekasgy@31
|
109 for (size_t i = 0; i < pyver.length(); ++i) {
|
fazekasgy@31
|
110 if (pyver[i] == '.') {
|
fazekasgy@31
|
111 if (--dots == 0) {
|
fazekasgy@31
|
112 shortver = pyver.substr(0, i);
|
fazekasgy@31
|
113 break;
|
fazekasgy@31
|
114 }
|
fazekasgy@31
|
115 }
|
fazekasgy@31
|
116 }
|
fazekasgy@31
|
117 cerr << "Short version: " << shortver << endl;
|
fazekasgy@31
|
118
|
fazekasgy@31
|
119 vector<string> pfxs;
|
fazekasgy@31
|
120 pfxs.push_back("");
|
fazekasgy@31
|
121 pfxs.push_back(string(Py_GetExecPrefix()) + "/lib/");
|
fazekasgy@31
|
122 pfxs.push_back(string(Py_GetExecPrefix()) + "/");
|
fazekasgy@31
|
123 pfxs.push_back("/usr/lib/");
|
fazekasgy@31
|
124 pfxs.push_back("/usr/local/lib/");
|
fazekasgy@31
|
125 char buffer[5];
|
fazekasgy@31
|
126
|
fazekasgy@31
|
127 // hahaha! grossness is like a brother to us
|
fazekasgy@31
|
128 #ifdef __APPLE__
|
fazekasgy@31
|
129 for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) {
|
fazekasgy@31
|
130 for (int minor = 8; minor >= 0; --minor) {
|
fazekasgy@31
|
131 sprintf(buffer, "%d", minor);
|
fazekasgy@31
|
132 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib." + buffer)) return true;
|
fazekasgy@31
|
133 }
|
fazekasgy@31
|
134 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib")) return true;
|
fazekasgy@31
|
135 if (tryPreload(pfxs[pfxidx] + string("libpython.dylib"))) return true;
|
fazekasgy@31
|
136 }
|
fazekasgy@31
|
137 #else
|
fazekasgy@31
|
138 for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) {
|
fazekasgy@31
|
139 for (int minor = 8; minor >= 0; --minor) {
|
fazekasgy@31
|
140 sprintf(buffer, "%d", minor);
|
fazekasgy@31
|
141 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so." + buffer)) return true;
|
fazekasgy@31
|
142 }
|
fazekasgy@31
|
143 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so")) return true;
|
fazekasgy@31
|
144 if (tryPreload(pfxs[pfxidx] + string("libpython.so"))) return true;
|
fazekasgy@31
|
145 }
|
fazekasgy@31
|
146 #endif
|
fazekasgy@31
|
147
|
fazekasgy@31
|
148 return false;
|
fazekasgy@31
|
149 }
|
fazekasgy@31
|
150
|
fazekasgy@31
|
151 /* This doesn't work: don't try it again.
|
fazekasgy@31
|
152 static bool initPython()
|
fazekasgy@31
|
153 {
|
fazekasgy@31
|
154 // preloadPython();
|
fazekasgy@31
|
155 Py_Initialize();
|
fazekasgy@31
|
156 #ifndef _WIN32
|
fazekasgy@31
|
157 //set dlopen flags form Python
|
fazekasgy@31
|
158 string pyCmd = "from sys import setdlopenflags\nimport dl\nsetdlopenflags(dl.RTLD_NOW|dl.RTLD_GLOBAL)\n";
|
fazekasgy@31
|
159 if (PyRun_SimpleString(pyCmd.c_str()) == -1)
|
fazekasgy@31
|
160 {
|
fazekasgy@31
|
161 cerr << "Warning: Could not set dlopen flasgs. Dynamic loading in scripts will fail." << endl;
|
fazekasgy@31
|
162 return false;
|
fazekasgy@31
|
163 }
|
fazekasgy@31
|
164 #endif
|
fazekasgy@31
|
165 PyEval_InitThreads();
|
fazekasgy@31
|
166 return Py_IsInitialized();
|
fazekasgy@31
|
167 }
|
fazekasgy@31
|
168 */
|
fazekasgy@31
|
169
|
fazekasgy@31
|
170 const VampPluginDescriptor
|
fazekasgy@31
|
171 *vampGetPluginDescriptor(unsigned int version,unsigned int index)
|
fazekasgy@31
|
172 {
|
fazekasgy@31
|
173 if (version < 1) return 0;
|
fazekasgy@31
|
174
|
fazekasgy@31
|
175 int isPythonInitialized = Py_IsInitialized();
|
fazekasgy@31
|
176 cerr << "# isPythonInitialized: " << isPythonInitialized << endl;
|
fazekasgy@31
|
177 cerr << "# haveScannedPlugins: " << haveScannedPlugins << endl;
|
fazekasgy@31
|
178
|
fazekasgy@31
|
179 if (!haveScannedPlugins) {
|
fazekasgy@31
|
180
|
fazekasgy@31
|
181 if (!isPythonInitialized){
|
fazekasgy@31
|
182
|
fazekasgy@31
|
183 if (!preloadPython())
|
fazekasgy@31
|
184 cerr << "Warning: Could not preload Python. Dynamic loading in scripts will fail." << endl;
|
fazekasgy@31
|
185
|
fazekasgy@31
|
186 int ext = PyImport_AppendInittab("vampy",initvampy);
|
fazekasgy@31
|
187 if (ext == -1) cerr << "Extension unsuccessful." << endl;
|
fazekasgy@31
|
188 else cerr << "Extension successful." << endl;
|
fazekasgy@31
|
189 Py_Initialize();
|
fazekasgy@31
|
190 initvampy();
|
fazekasgy@31
|
191 // if (!PyImport_ImportModule("vampy"))
|
fazekasgy@31
|
192 // cerr << "Could not import extension." << endl;
|
fazekasgy@31
|
193
|
fazekasgy@31
|
194 // Py_InitModule("vampy", VampyMethods);
|
fazekasgy@31
|
195
|
fazekasgy@31
|
196 // initpyRealTime();
|
fazekasgy@31
|
197 cerr << "# isPythonInitialized after initialize: " << Py_IsInitialized() << endl;
|
fazekasgy@31
|
198 // PyEval_InitThreads(); //not sure why this was needed
|
fazekasgy@31
|
199 }
|
fazekasgy@31
|
200
|
fazekasgy@31
|
201 vector<string> pyPlugs;
|
fazekasgy@31
|
202 vector<string> pyPath;
|
fazekasgy@31
|
203 vector<PyObject *> pyClasses;
|
fazekasgy@31
|
204 static PyPlugScanner *scanner;
|
fazekasgy@31
|
205
|
fazekasgy@31
|
206 //Scanning Plugins
|
fazekasgy@31
|
207 cerr << "Scanning PyPlugins" << endl;
|
fazekasgy@31
|
208 scanner = PyPlugScanner::getInstance();
|
fazekasgy@31
|
209 pyPath=scanner->getAllValidPath();
|
fazekasgy@31
|
210 //add this as extra path for development
|
fazekasgy@31
|
211 //pyPath.push_back("/Users/Shared/Development/vamp-experiments");
|
fazekasgy@31
|
212 scanner->setPath(pyPath);
|
fazekasgy@31
|
213 pyPlugs = scanner->getPyPlugs();
|
fazekasgy@31
|
214 cerr << "Found " << pyPlugs.size() << " Scripts ...OK" << endl;
|
fazekasgy@31
|
215 //TODO: this should support multiple classes per script (?)
|
fazekasgy@31
|
216 pyClasses = scanner->getPyClasses();
|
fazekasgy@31
|
217 cerr << "Found " << pyClasses.size() << " Classes ...OK" << endl;
|
fazekasgy@31
|
218
|
fazekasgy@31
|
219 for (size_t i = 0; i < pyPlugs.size(); ++i) {
|
fazekasgy@31
|
220 adapters.push_back( new PyPluginAdapter(pyPlugs[i],pyClasses[i]));
|
fazekasgy@31
|
221 }
|
fazekasgy@31
|
222 haveScannedPlugins=true;
|
fazekasgy@31
|
223 // if (!haveVampyInitialised) {
|
fazekasgy@31
|
224 // // PyImport_AddModule("vampy");
|
fazekasgy@31
|
225 // // if (!PyImport_ImportModule("vampy"))
|
fazekasgy@31
|
226 // cerr << "Could not import extension." << endl;
|
fazekasgy@31
|
227 // initvampy();
|
fazekasgy@31
|
228 // haveVampyInitialised = true;
|
fazekasgy@31
|
229 // cerr << "Extension initialised from main." << endl;
|
fazekasgy@31
|
230 // }
|
fazekasgy@31
|
231 }
|
fazekasgy@31
|
232
|
fazekasgy@31
|
233 cerr << "Accessing adapter index: " << index << " (adapters: " << adapters.size() << ")" << endl;
|
fazekasgy@31
|
234 if (index<adapters.size()) {
|
fazekasgy@31
|
235 const VampPluginDescriptor *tmp = adapters[index]->getDescriptor();
|
fazekasgy@31
|
236 return tmp;
|
fazekasgy@31
|
237 } else return 0;
|
fazekasgy@31
|
238
|
fazekasgy@31
|
239
|
fazekasgy@31
|
240 }
|
fazekasgy@31
|
241
|
fazekasgy@31
|
242
|
fazekasgy@31
|
243
|
fazekasgy@31
|
244
|
fazekasgy@31
|
245
|
fazekasgy@31
|
246
|
fazekasgy@31
|
247
|
fazekasgy@31
|
248
|