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 /*
|
fazekasgy@0
|
4 Vamp
|
fazekasgy@0
|
5
|
fazekasgy@0
|
6 An API for audio analysis and feature extraction plugins.
|
fazekasgy@0
|
7
|
fazekasgy@0
|
8 Centre for Digital Music, Queen Mary, University of London.
|
fazekasgy@0
|
9 Copyright 2006 Chris Cannam.
|
fazekasgy@0
|
10
|
fazekasgy@0
|
11 Permission is hereby granted, free of charge, to any person
|
fazekasgy@0
|
12 obtaining a copy of this software and associated documentation
|
fazekasgy@0
|
13 files (the "Software"), to deal in the Software without
|
fazekasgy@0
|
14 restriction, including without limitation the rights to use, copy,
|
fazekasgy@0
|
15 modify, merge, publish, distribute, sublicense, and/or sell copies
|
fazekasgy@0
|
16 of the Software, and to permit persons to whom the Software is
|
fazekasgy@0
|
17 furnished to do so, subject to the following conditions:
|
fazekasgy@0
|
18
|
fazekasgy@0
|
19 The above copyright notice and this permission notice shall be
|
fazekasgy@0
|
20 included in all copies or substantial portions of the Software.
|
fazekasgy@0
|
21
|
fazekasgy@0
|
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
fazekasgy@0
|
23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
fazekasgy@0
|
24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
fazekasgy@0
|
25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
fazekasgy@0
|
26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
fazekasgy@0
|
27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
fazekasgy@0
|
28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
fazekasgy@0
|
29
|
fazekasgy@0
|
30 Except as contained in this notice, the names of the Centre for
|
fazekasgy@0
|
31 Digital Music; Queen Mary, University of London; and Chris Cannam
|
fazekasgy@0
|
32 shall not be used in advertising or otherwise to promote the sale,
|
fazekasgy@0
|
33 use or other dealings in this Software without prior written
|
fazekasgy@0
|
34 authorization.
|
fazekasgy@0
|
35 */
|
fazekasgy@0
|
36
|
fazekasgy@0
|
37 /**
|
cannam@7
|
38 * This Vamp plugin is a wrapper for Python Scripts. (VamPy)
|
fazekasgy@0
|
39 * Centre for Digital Music, Queen Mary, University of London.
|
fazekasgy@0
|
40 * Copyright 2008, George Fazekas.
|
fazekasgy@0
|
41 */
|
fazekasgy@0
|
42
|
cannam@3
|
43 #include <Python.h>
|
fazekasgy@0
|
44 #include "vamp/vamp.h"
|
fazekasgy@0
|
45 #include "vamp-sdk/PluginAdapter.h"
|
fazekasgy@0
|
46 #include "PyPlugScanner.h"
|
fazekasgy@0
|
47 #include "PyPlugin.h"
|
fazekasgy@0
|
48
|
fazekasgy@0
|
49 #ifdef _WIN32
|
fazekasgy@0
|
50 #define pathsep ('\\')
|
fazekasgy@0
|
51 #include <windows.h>
|
fazekasgy@0
|
52 #include <tchar.h>
|
fazekasgy@0
|
53 #else
|
fazekasgy@0
|
54 #define pathsep ('/')
|
fazekasgy@0
|
55 #include <dirent.h>
|
fazekasgy@0
|
56 #include <dlfcn.h>
|
fazekasgy@0
|
57 #endif
|
fazekasgy@0
|
58
|
fazekasgy@0
|
59 using std::cerr;
|
fazekasgy@0
|
60 using std::endl;
|
fazekasgy@0
|
61 using std::string;
|
fazekasgy@0
|
62 using std::vector;
|
fazekasgy@0
|
63
|
fazekasgy@6
|
64 //volatile bool mutex = false;
|
fazekasgy@0
|
65 static int adinstcount;
|
fazekasgy@0
|
66
|
fazekasgy@0
|
67 class PyPluginAdapter : public Vamp::PluginAdapterBase
|
fazekasgy@0
|
68 {
|
fazekasgy@0
|
69 public:
|
fazekasgy@0
|
70 PyPluginAdapter(std::string pyPlugId, PyObject* pyInstance) :
|
fazekasgy@0
|
71 PluginAdapterBase(),
|
fazekasgy@0
|
72 m_plug(pyPlugId),
|
fazekasgy@0
|
73 m_pyInstance(pyInstance)
|
fazekasgy@0
|
74 {
|
fazekasgy@0
|
75 cerr << "PyPluginAdapter:ctor:"<< adinstcount << ": " << m_plug << endl;
|
fazekasgy@0
|
76 adinstcount++;
|
fazekasgy@0
|
77 m_instanceCount = 0;
|
fazekasgy@0
|
78 }
|
fazekasgy@0
|
79
|
fazekasgy@0
|
80 ~PyPluginAdapter()
|
fazekasgy@0
|
81 {
|
fazekasgy@0
|
82 }
|
fazekasgy@0
|
83
|
fazekasgy@0
|
84 protected:
|
fazekasgy@0
|
85 Vamp::Plugin *createPlugin(float inputSampleRate) {
|
fazekasgy@0
|
86
|
fazekasgy@0
|
87 std::string pclass = m_plug.substr(m_plug.rfind(':')+1,m_plug.size()-1);
|
fazekasgy@0
|
88 std::string ppath = m_plug.substr(0,m_plug.rfind(pathsep));
|
fazekasgy@0
|
89 PyPlugin *plugin = new PyPlugin(m_plug,inputSampleRate,m_pyInstance);
|
fazekasgy@0
|
90 m_instanceCount++;
|
fazekasgy@0
|
91 cerr << "PyPluginAdapter::createPlugin:" << pclass << " (instance: " << m_instanceCount << ")" << endl;
|
fazekasgy@0
|
92 return plugin;
|
fazekasgy@0
|
93
|
fazekasgy@0
|
94 }
|
fazekasgy@0
|
95
|
fazekasgy@0
|
96 std::string m_plug;
|
fazekasgy@0
|
97 bool m_haveInitialized;
|
fazekasgy@0
|
98 PyObject *m_pyInstance;
|
fazekasgy@0
|
99 int m_instanceCount;
|
fazekasgy@0
|
100
|
fazekasgy@0
|
101 };
|
fazekasgy@0
|
102
|
fazekasgy@0
|
103
|
fazekasgy@0
|
104 static std::vector<PyPluginAdapter *> adapters;
|
fazekasgy@0
|
105 static bool haveScannedPlugins = false;
|
fazekasgy@0
|
106
|
cannam@16
|
107 static bool tryPreload(string name)
|
cannam@16
|
108 {
|
cannam@19
|
109 cerr << "Trying to load Python interpreter library \"" << name << "\"...";
|
cannam@19
|
110 #ifdef _WIN32
|
cannam@19
|
111 void *lib = LoadLibrary(name.c_str());
|
cannam@19
|
112 if (!lib) {
|
cannam@19
|
113 cerr << " failed" << endl;
|
cannam@19
|
114 return false;
|
cannam@19
|
115 }
|
cannam@19
|
116 #else
|
cannam@16
|
117 void *lib = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL);
|
cannam@16
|
118 if (!lib) {
|
cannam@19
|
119 cerr << " failed" << endl;
|
cannam@16
|
120 return false;
|
cannam@16
|
121 }
|
cannam@19
|
122 #endif
|
cannam@19
|
123 cerr << " succeeded" << endl;
|
cannam@16
|
124 return true;
|
cannam@16
|
125 }
|
cannam@16
|
126
|
cannam@16
|
127 static bool preloadPython()
|
cannam@16
|
128 {
|
cannam@20
|
129 #ifdef _WIN32
|
cannam@20
|
130 // this doesn't seem to be necessary at all on Windows
|
cannam@20
|
131 return true;
|
cannam@20
|
132 #endif
|
cannam@16
|
133
|
cannam@16
|
134 string pyver = Py_GetVersion();
|
cannam@16
|
135 int dots = 2;
|
cannam@16
|
136 string shortver;
|
cannam@18
|
137 for (size_t i = 0; i < pyver.length(); ++i) {
|
cannam@16
|
138 if (pyver[i] == '.') {
|
cannam@16
|
139 if (--dots == 0) {
|
cannam@16
|
140 shortver = pyver.substr(0, i);
|
cannam@16
|
141 break;
|
cannam@16
|
142 }
|
cannam@16
|
143 }
|
cannam@16
|
144 }
|
cannam@16
|
145 cerr << "Short version: " << shortver << endl;
|
cannam@16
|
146
|
cannam@16
|
147 vector<string> pfxs;
|
cannam@16
|
148 pfxs.push_back("");
|
cannam@16
|
149 pfxs.push_back(string(Py_GetExecPrefix()) + "/lib/");
|
cannam@16
|
150 pfxs.push_back(string(Py_GetExecPrefix()) + "/");
|
cannam@16
|
151 pfxs.push_back("/usr/lib/");
|
cannam@16
|
152 pfxs.push_back("/usr/local/lib/");
|
cannam@16
|
153 char buffer[5];
|
cannam@16
|
154
|
cannam@16
|
155 // hahaha! grossness is like a brother to us
|
cannam@17
|
156 #ifdef __APPLE__
|
cannam@18
|
157 for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) {
|
cannam@17
|
158 for (int minor = 8; minor >= 0; --minor) {
|
cannam@17
|
159 sprintf(buffer, "%d", minor);
|
cannam@17
|
160 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib." + buffer)) return true;
|
cannam@17
|
161 }
|
cannam@17
|
162 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib")) return true;
|
cannam@17
|
163 if (tryPreload(pfxs[pfxidx] + string("libpython.dylib"))) return true;
|
cannam@17
|
164 }
|
cannam@17
|
165 #else
|
cannam@18
|
166 for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) {
|
cannam@16
|
167 for (int minor = 8; minor >= 0; --minor) {
|
cannam@16
|
168 sprintf(buffer, "%d", minor);
|
cannam@16
|
169 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so." + buffer)) return true;
|
cannam@16
|
170 }
|
cannam@16
|
171 if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so")) return true;
|
cannam@16
|
172 if (tryPreload(pfxs[pfxidx] + string("libpython.so"))) return true;
|
cannam@16
|
173 }
|
cannam@17
|
174 #endif
|
cannam@16
|
175
|
cannam@16
|
176 return false;
|
cannam@16
|
177 }
|
cannam@16
|
178
|
cannam@16
|
179
|
fazekasgy@0
|
180 const VampPluginDescriptor
|
fazekasgy@0
|
181 *vampGetPluginDescriptor(unsigned int version,unsigned int index)
|
fazekasgy@0
|
182 {
|
fazekasgy@0
|
183 if (version < 1) return 0;
|
fazekasgy@0
|
184
|
fazekasgy@0
|
185 int isPythonInitialized = Py_IsInitialized();
|
fazekasgy@0
|
186 //cerr << "# isPythonInitialized: " << isPythonInitialized << endl;
|
fazekasgy@0
|
187 //cerr << "# haveScannedPlugins: " << haveScannedPlugins << endl;
|
fazekasgy@0
|
188
|
fazekasgy@0
|
189 if (!haveScannedPlugins) {
|
fazekasgy@8
|
190
|
fazekasgy@0
|
191 if (!isPythonInitialized) {
|
fazekasgy@0
|
192
|
cannam@16
|
193 if (!preloadPython()) {
|
cannam@16
|
194 cerr << "Warning: Could not preload Python."
|
cannam@16
|
195 << " Dynamic loading in scripts will fail." << endl;
|
cannam@16
|
196 }
|
cannam@16
|
197
|
cannam@16
|
198 /*
|
fazekasgy@0
|
199 void *pylib = 0;
|
fazekasgy@0
|
200
|
fazekasgy@0
|
201 cerr << "Loading Python Interpreter at: " << pythonPath << endl;
|
fazekasgy@1
|
202 //Preloading the binary allows the load of shared libs
|
fazekasgy@0
|
203 //TODO: check how to do RTLD_NOW on Windows
|
fazekasgy@2
|
204 #ifdef _WIN32
|
fazekasgy@0
|
205 pylib = LoadLibrary(pythonPath.c_str());
|
fazekasgy@0
|
206 #else
|
fazekasgy@0
|
207 pylib = dlopen(pythonPath.c_str(), RTLD_NOW|RTLD_GLOBAL);
|
fazekasgy@0
|
208 #endif
|
fazekasgy@0
|
209 if (!pylib) cerr << "Warning: Could not preload Python."
|
fazekasgy@1
|
210 << " Dynamic loading in scripts will fail." << endl;
|
cannam@16
|
211 */
|
fazekasgy@0
|
212 Py_Initialize();
|
fazekasgy@0
|
213 PyEval_InitThreads();
|
fazekasgy@0
|
214 } else {
|
fazekasgy@0
|
215 //Py_InitializeEx(1);
|
fazekasgy@0
|
216 }
|
fazekasgy@0
|
217
|
fazekasgy@0
|
218 vector<string> pyPlugs;
|
fazekasgy@0
|
219 vector<string> pyPath;
|
fazekasgy@0
|
220 vector<PyObject *> pyInstances;
|
fazekasgy@0
|
221 static PyPlugScanner *scanner;
|
fazekasgy@0
|
222
|
fazekasgy@0
|
223 //Scanning Plugins
|
fazekasgy@0
|
224 cerr << "Scanning PyPlugins" << endl;
|
fazekasgy@0
|
225 scanner = PyPlugScanner::getInstance();
|
fazekasgy@0
|
226 pyPath=scanner->getAllValidPath();
|
fazekasgy@0
|
227 //add this as extra path for development
|
fazekasgy@6
|
228 //pyPath.push_back("/Users/Shared/Development/vamp-experiments");
|
fazekasgy@0
|
229 scanner->setPath(pyPath);
|
fazekasgy@0
|
230 pyPlugs = scanner->getPyPlugs();
|
fazekasgy@0
|
231 cerr << "Found " << pyPlugs.size() << " Scripts ...OK" << endl;
|
fazekasgy@0
|
232 //TODO: this will support multiple classes per script
|
fazekasgy@0
|
233 pyInstances = scanner->getPyInstances();
|
fazekasgy@0
|
234 cerr << "Found " << pyInstances.size() << " Instances ...OK" << endl;
|
fazekasgy@0
|
235
|
fazekasgy@0
|
236 for (size_t i = 0; i < pyPlugs.size(); ++i) {
|
fazekasgy@0
|
237 adapters.push_back( new PyPluginAdapter(pyPlugs[i],pyInstances[i]));
|
fazekasgy@0
|
238 }
|
fazekasgy@0
|
239 haveScannedPlugins=true;
|
fazekasgy@6
|
240
|
fazekasgy@0
|
241 }
|
fazekasgy@0
|
242
|
fazekasgy@0
|
243 cerr << "Accessing adapter index: " << index << " (adapters: " << adapters.size() << ")" << endl;
|
fazekasgy@0
|
244 if (index<adapters.size()) {
|
fazekasgy@0
|
245 const VampPluginDescriptor *tmp = adapters[index]->getDescriptor();
|
fazekasgy@0
|
246 return tmp;
|
fazekasgy@0
|
247 } else return 0;
|
fazekasgy@0
|
248
|
fazekasgy@0
|
249
|
fazekasgy@0
|
250 }
|
fazekasgy@0
|
251
|
fazekasgy@0
|
252
|
fazekasgy@0
|
253
|
fazekasgy@0
|
254
|
fazekasgy@0
|
255
|
fazekasgy@0
|
256
|
fazekasgy@0
|
257
|
fazekasgy@0
|
258
|