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@29
|
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@29
|
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@29
|
85 // cerr << " failed" << endl;
|
cannam@16
|
86 return false;
|
cannam@16
|
87 }
|
cannam@19
|
88 #endif
|
cannam@29
|
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 }
|
fazekasgy@25
|
144
|
fazekasgy@25
|
145 /* This doesn't work: don't try it again.
|
fazekasgy@25
|
146 static bool initPython()
|
fazekasgy@25
|
147 {
|
fazekasgy@25
|
148 // preloadPython();
|
fazekasgy@25
|
149 Py_Initialize();
|
fazekasgy@25
|
150 #ifndef _WIN32
|
fazekasgy@25
|
151 //set dlopen flags form Python
|
fazekasgy@25
|
152 string pyCmd = "from sys import setdlopenflags\nimport dl\nsetdlopenflags(dl.RTLD_NOW|dl.RTLD_GLOBAL)\n";
|
fazekasgy@25
|
153 if (PyRun_SimpleString(pyCmd.c_str()) == -1)
|
fazekasgy@25
|
154 {
|
fazekasgy@25
|
155 cerr << "Warning: Could not set dlopen flasgs. Dynamic loading in scripts will fail." << endl;
|
fazekasgy@25
|
156 return false;
|
fazekasgy@25
|
157 }
|
fazekasgy@25
|
158 #endif
|
fazekasgy@25
|
159 PyEval_InitThreads();
|
fazekasgy@25
|
160 return Py_IsInitialized();
|
fazekasgy@25
|
161 }
|
fazekasgy@25
|
162 */
|
cannam@16
|
163
|
fazekasgy@0
|
164 const VampPluginDescriptor
|
fazekasgy@0
|
165 *vampGetPluginDescriptor(unsigned int version,unsigned int index)
|
fazekasgy@0
|
166 {
|
fazekasgy@0
|
167 if (version < 1) return 0;
|
fazekasgy@0
|
168
|
fazekasgy@0
|
169 int isPythonInitialized = Py_IsInitialized();
|
cannam@24
|
170 cerr << "# isPythonInitialized: " << isPythonInitialized << endl;
|
cannam@24
|
171 cerr << "# haveScannedPlugins: " << haveScannedPlugins << endl;
|
fazekasgy@0
|
172
|
fazekasgy@0
|
173 if (!haveScannedPlugins) {
|
fazekasgy@8
|
174
|
fazekasgy@25
|
175 if (!isPythonInitialized){
|
fazekasgy@0
|
176
|
fazekasgy@25
|
177 if (!preloadPython())
|
fazekasgy@25
|
178 cerr << "Warning: Could not preload Python. Dynamic loading in scripts will fail." << endl;
|
fazekasgy@25
|
179 Py_Initialize();
|
fazekasgy@25
|
180 cerr << "# isPythonInitialized after initialize: " << Py_IsInitialized() << endl;
|
fazekasgy@25
|
181 // PyEval_InitThreads(); //not sure why this was needed
|
fazekasgy@25
|
182 }
|
cannam@16
|
183
|
fazekasgy@0
|
184 vector<string> pyPlugs;
|
fazekasgy@0
|
185 vector<string> pyPath;
|
cannam@24
|
186 vector<PyObject *> pyClasses;
|
fazekasgy@0
|
187 static PyPlugScanner *scanner;
|
fazekasgy@0
|
188
|
fazekasgy@0
|
189 //Scanning Plugins
|
fazekasgy@0
|
190 cerr << "Scanning PyPlugins" << endl;
|
fazekasgy@0
|
191 scanner = PyPlugScanner::getInstance();
|
fazekasgy@0
|
192 pyPath=scanner->getAllValidPath();
|
fazekasgy@0
|
193 //add this as extra path for development
|
fazekasgy@6
|
194 //pyPath.push_back("/Users/Shared/Development/vamp-experiments");
|
fazekasgy@0
|
195 scanner->setPath(pyPath);
|
fazekasgy@0
|
196 pyPlugs = scanner->getPyPlugs();
|
fazekasgy@0
|
197 cerr << "Found " << pyPlugs.size() << " Scripts ...OK" << endl;
|
fazekasgy@0
|
198 //TODO: this will support multiple classes per script
|
cannam@24
|
199 pyClasses = scanner->getPyClasses();
|
cannam@24
|
200 cerr << "Found " << pyClasses.size() << " Classes ...OK" << endl;
|
fazekasgy@0
|
201
|
fazekasgy@0
|
202 for (size_t i = 0; i < pyPlugs.size(); ++i) {
|
cannam@24
|
203 adapters.push_back( new PyPluginAdapter(pyPlugs[i],pyClasses[i]));
|
fazekasgy@0
|
204 }
|
fazekasgy@0
|
205 haveScannedPlugins=true;
|
fazekasgy@6
|
206
|
fazekasgy@0
|
207 }
|
fazekasgy@0
|
208
|
fazekasgy@0
|
209 cerr << "Accessing adapter index: " << index << " (adapters: " << adapters.size() << ")" << endl;
|
fazekasgy@0
|
210 if (index<adapters.size()) {
|
fazekasgy@0
|
211 const VampPluginDescriptor *tmp = adapters[index]->getDescriptor();
|
fazekasgy@0
|
212 return tmp;
|
fazekasgy@0
|
213 } else return 0;
|
fazekasgy@0
|
214
|
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
|