fazekasgy@0
|
1 /**
|
fazekasgy@0
|
2 * This VAMP plugin is a wrapper for Python Scripts. (VamPy)
|
fazekasgy@0
|
3 * Centre for Digital Music, Queen Mary, University of London.
|
fazekasgy@0
|
4 * Copyright 2008, George Fazekas.
|
fazekasgy@0
|
5
|
fazekasgy@0
|
6 */
|
fazekasgy@0
|
7
|
fazekasgy@0
|
8
|
fazekasgy@0
|
9 #include "/usr/include/python/Python.h"
|
fazekasgy@0
|
10 #include "PyPlugScanner.h"
|
fazekasgy@0
|
11
|
fazekasgy@0
|
12 //#include <fstream>
|
fazekasgy@0
|
13 //#include <cctype>
|
fazekasgy@0
|
14
|
fazekasgy@0
|
15 #ifdef _WIN32
|
fazekasgy@0
|
16 #include <windows.h>
|
fazekasgy@0
|
17 #include <tchar.h>
|
fazekasgy@0
|
18 #define pathsep ("\\")
|
fazekasgy@0
|
19 #else
|
fazekasgy@0
|
20 #include <dirent.h>
|
fazekasgy@0
|
21 #include <dlfcn.h>
|
fazekasgy@0
|
22 #define pathsep ("/")
|
fazekasgy@0
|
23 #endif
|
fazekasgy@0
|
24 #define joinPath(a,b) ( (a)+pathsep+(b) )
|
fazekasgy@0
|
25
|
fazekasgy@0
|
26 using std::string;
|
fazekasgy@0
|
27 using std::vector;
|
fazekasgy@0
|
28 using std::cerr;
|
fazekasgy@0
|
29 using std::endl;
|
fazekasgy@0
|
30
|
fazekasgy@0
|
31 PyPlugScanner::PyPlugScanner()
|
fazekasgy@0
|
32 {
|
fazekasgy@0
|
33
|
fazekasgy@0
|
34 }
|
fazekasgy@0
|
35
|
fazekasgy@0
|
36 PyPlugScanner *PyPlugScanner::m_instance = NULL;
|
fazekasgy@0
|
37 bool PyPlugScanner::m_hasInstance = false;
|
fazekasgy@0
|
38
|
fazekasgy@0
|
39 PyPlugScanner*
|
fazekasgy@0
|
40 PyPlugScanner::getInstance()
|
fazekasgy@0
|
41 {
|
fazekasgy@0
|
42 if (!m_hasInstance) {
|
fazekasgy@0
|
43 m_instance = new PyPlugScanner();
|
fazekasgy@0
|
44 m_hasInstance = true;
|
fazekasgy@0
|
45 }
|
fazekasgy@0
|
46 return m_instance;
|
fazekasgy@0
|
47 }
|
fazekasgy@0
|
48
|
fazekasgy@0
|
49 void
|
fazekasgy@0
|
50 PyPlugScanner::setPath(vector<string> path)
|
fazekasgy@0
|
51 {
|
fazekasgy@0
|
52 m_path=path;
|
fazekasgy@0
|
53 }
|
fazekasgy@0
|
54
|
fazekasgy@0
|
55 // TODO: This should return all scripts for all valid paths
|
fazekasgy@0
|
56 // Validate python classes here?
|
fazekasgy@0
|
57 // For now, we assume that each script on the path has one valid class
|
fazekasgy@0
|
58 vector<string>
|
fazekasgy@0
|
59 PyPlugScanner::getPyPlugs()
|
fazekasgy@0
|
60 {
|
fazekasgy@0
|
61 //foreach m_path listFiles then return vector<pyPlugs>
|
fazekasgy@0
|
62 //format: FullPathString/FileName.py:ClassName
|
fazekasgy@0
|
63
|
fazekasgy@0
|
64 vector<string> pyPlugs;
|
fazekasgy@0
|
65 string pluginKey;
|
fazekasgy@0
|
66 PyObject *pyClassInstance;
|
fazekasgy@0
|
67
|
fazekasgy@0
|
68 for (size_t i = 0; i < m_path.size(); ++i) {
|
fazekasgy@0
|
69
|
fazekasgy@0
|
70 vector<string> files = listFiles(m_path[i],"py");
|
fazekasgy@0
|
71
|
fazekasgy@0
|
72 for (vector<string>::iterator fi = files.begin();
|
fazekasgy@0
|
73 fi != files.end(); ++fi) {
|
fazekasgy@0
|
74 string script = *fi;
|
fazekasgy@0
|
75 if (!script.empty()) {
|
fazekasgy@0
|
76 string classname=script.substr(0,script.rfind('.'));
|
fazekasgy@0
|
77 pluginKey=joinPath(m_path[i],script)+":"+classname;
|
fazekasgy@0
|
78 pyClassInstance = getScriptInstance(m_path[i],classname);
|
fazekasgy@0
|
79 if (pyClassInstance == NULL)
|
fazekasgy@0
|
80 cerr << "Warning: Syntax error in VamPy plugin: "
|
fazekasgy@0
|
81 << classname << ". Avoiding plugin." << endl;
|
fazekasgy@0
|
82 else {
|
fazekasgy@0
|
83 pyPlugs.push_back(pluginKey);
|
fazekasgy@0
|
84 m_pyInstances.push_back(pyClassInstance);
|
fazekasgy@0
|
85 }
|
fazekasgy@0
|
86 //pyPlugs.push_back(pluginKey);
|
fazekasgy@0
|
87 }
|
fazekasgy@0
|
88 }
|
fazekasgy@0
|
89 }
|
fazekasgy@0
|
90
|
fazekasgy@0
|
91 return pyPlugs;
|
fazekasgy@0
|
92
|
fazekasgy@0
|
93 }
|
fazekasgy@0
|
94
|
fazekasgy@0
|
95
|
fazekasgy@0
|
96 //For now return one class instance found in each script
|
fazekasgy@0
|
97 vector<PyObject*>
|
fazekasgy@0
|
98 PyPlugScanner::getPyInstances()
|
fazekasgy@0
|
99 {
|
fazekasgy@0
|
100 return m_pyInstances;
|
fazekasgy@0
|
101
|
fazekasgy@0
|
102 }
|
fazekasgy@0
|
103
|
fazekasgy@0
|
104
|
fazekasgy@0
|
105 //Validate
|
fazekasgy@0
|
106 //This should not be called more than once!
|
fazekasgy@0
|
107 PyObject*
|
fazekasgy@0
|
108 PyPlugScanner::getScriptInstance(string path, string classname)
|
fazekasgy@0
|
109 {
|
fazekasgy@0
|
110
|
fazekasgy@0
|
111 //Add plugin path to active Python Path
|
fazekasgy@0
|
112 string pyCmd = "import sys\nsys.path.append('" + path + "')\n";
|
fazekasgy@0
|
113 PyRun_SimpleString(pyCmd.c_str());
|
fazekasgy@0
|
114
|
fazekasgy@0
|
115 //Assign an object to the source code
|
fazekasgy@0
|
116 PyObject *pySource = PyString_FromString(classname.c_str());
|
fazekasgy@0
|
117
|
fazekasgy@0
|
118 //Import it as a module into the py interpreter
|
fazekasgy@0
|
119 PyObject *pyModule = PyImport_Import(pySource);
|
fazekasgy@0
|
120 PyObject* pyError = PyErr_Occurred();
|
fazekasgy@0
|
121 if (! pyError == 0) {
|
fazekasgy@0
|
122 cerr << "ERROR: error importing source: " << classname << endl;
|
fazekasgy@0
|
123 PyErr_Print();
|
fazekasgy@0
|
124 Py_DECREF(pySource);
|
fazekasgy@0
|
125 Py_CLEAR(pyModule); // safer if pyModule==NULL
|
fazekasgy@0
|
126 return NULL;
|
fazekasgy@0
|
127 }
|
fazekasgy@0
|
128 Py_DECREF(pySource);
|
fazekasgy@0
|
129
|
fazekasgy@0
|
130 //Read the namespace of the module into a dictionary object (borrowed reference)
|
fazekasgy@0
|
131 PyObject *pyDict = PyModule_GetDict(pyModule);
|
fazekasgy@0
|
132 Py_DECREF(pyModule);
|
fazekasgy@0
|
133
|
fazekasgy@0
|
134 //Get the PluginClass from the module (borrowed reference)
|
fazekasgy@0
|
135 PyObject *pyClass = PyDict_GetItemString(pyDict, classname.c_str());
|
fazekasgy@0
|
136
|
fazekasgy@0
|
137 //Check if class is present and a callable method is implemented
|
fazekasgy@0
|
138 if (pyClass && PyCallable_Check(pyClass)) {
|
fazekasgy@0
|
139
|
fazekasgy@0
|
140 //Create an instance
|
fazekasgy@0
|
141 PyObject *pyInstance = PyObject_CallObject(pyClass, NULL);
|
fazekasgy@0
|
142 //cerr << "__(getInstance) PyPlugin Class: " << m_class << " successfully created.__" << endl;
|
fazekasgy@0
|
143 return pyInstance;
|
fazekasgy@0
|
144 } else return NULL;
|
fazekasgy@0
|
145 }
|
fazekasgy@0
|
146
|
fazekasgy@0
|
147
|
fazekasgy@0
|
148
|
fazekasgy@0
|
149 // Return a list of files in dir with given extension
|
fazekasgy@0
|
150 // Code taken from hostext/PluginLoader.cpp
|
fazekasgy@0
|
151 vector<string>
|
fazekasgy@0
|
152 PyPlugScanner::listFiles(string dir, string extension)
|
fazekasgy@0
|
153 {
|
fazekasgy@0
|
154 vector<string> files;
|
fazekasgy@0
|
155
|
fazekasgy@0
|
156 #ifdef _WIN32
|
fazekasgy@0
|
157
|
fazekasgy@0
|
158 string expression = dir + "\\*." + extension;
|
fazekasgy@0
|
159 WIN32_FIND_DATA data;
|
fazekasgy@0
|
160 HANDLE fh = FindFirstFile(expression.c_str(), &data);
|
fazekasgy@0
|
161 if (fh == INVALID_HANDLE_VALUE) return files;
|
fazekasgy@0
|
162
|
fazekasgy@0
|
163 bool ok = true;
|
fazekasgy@0
|
164 while (ok) {
|
fazekasgy@0
|
165 files.push_back(data.cFileName);
|
fazekasgy@0
|
166 ok = FindNextFile(fh, &data);
|
fazekasgy@0
|
167 }
|
fazekasgy@0
|
168
|
fazekasgy@0
|
169 FindClose(fh);
|
fazekasgy@0
|
170
|
fazekasgy@0
|
171 #else
|
fazekasgy@0
|
172
|
fazekasgy@0
|
173 size_t extlen = extension.length();
|
fazekasgy@0
|
174 DIR *d = opendir(dir.c_str());
|
fazekasgy@0
|
175 if (!d) return files;
|
fazekasgy@0
|
176
|
fazekasgy@0
|
177 struct dirent *e = 0;
|
fazekasgy@0
|
178 while ((e = readdir(d))) {
|
fazekasgy@0
|
179
|
fazekasgy@0
|
180 if (!(e->d_type & DT_REG) && (e->d_type != DT_UNKNOWN)) continue;
|
fazekasgy@0
|
181
|
fazekasgy@0
|
182 if (!e->d_name) continue;
|
fazekasgy@0
|
183
|
fazekasgy@0
|
184 size_t len = strlen(e->d_name);
|
fazekasgy@0
|
185 if (len < extlen + 2 ||
|
fazekasgy@0
|
186 e->d_name + len - extlen - 1 != "." + extension) {
|
fazekasgy@0
|
187 continue;
|
fazekasgy@0
|
188 }
|
fazekasgy@0
|
189 //cerr << "pyscripts: " << e->d_name << endl;
|
fazekasgy@0
|
190 files.push_back(e->d_name);
|
fazekasgy@0
|
191 }
|
fazekasgy@0
|
192
|
fazekasgy@0
|
193 closedir(d);
|
fazekasgy@0
|
194 #endif
|
fazekasgy@0
|
195
|
fazekasgy@0
|
196 return files;
|
fazekasgy@0
|
197 }
|
fazekasgy@0
|
198
|
fazekasgy@0
|
199 //Return correct plugin directories as per platform
|
fazekasgy@0
|
200 //Code taken from vamp-sdk/PluginHostAdapter.cpp
|
fazekasgy@0
|
201 std::vector<std::string>
|
fazekasgy@0
|
202 PyPlugScanner::getAllValidPath()
|
fazekasgy@0
|
203 {
|
fazekasgy@0
|
204 std::vector<std::string> path;
|
fazekasgy@0
|
205 std::string envPath;
|
fazekasgy@0
|
206
|
fazekasgy@0
|
207 char *cpath = getenv("VAMP_PATH");
|
fazekasgy@0
|
208 if (cpath) envPath = cpath;
|
fazekasgy@0
|
209
|
fazekasgy@0
|
210 #ifdef _WIN32
|
fazekasgy@0
|
211 #define PATH_SEPARATOR ';'
|
fazekasgy@0
|
212 #define DEFAULT_VAMP_PATH "%ProgramFiles%\\Vamp Plugins"
|
fazekasgy@0
|
213 #else
|
fazekasgy@0
|
214 #define PATH_SEPARATOR ':'
|
fazekasgy@0
|
215 #ifdef __APPLE__
|
fazekasgy@0
|
216 #define DEFAULT_VAMP_PATH "$HOME/Library/Audio/Plug-Ins/Vamp:/Library/Audio/Plug-Ins/Vamp"
|
fazekasgy@0
|
217 #else
|
fazekasgy@0
|
218 #define DEFAULT_VAMP_PATH "$HOME/vamp:$HOME/.vamp:/usr/local/lib/vamp:/usr/lib/vamp"
|
fazekasgy@0
|
219 #endif
|
fazekasgy@0
|
220 #endif
|
fazekasgy@0
|
221
|
fazekasgy@0
|
222 if (envPath == "") {
|
fazekasgy@0
|
223 envPath = DEFAULT_VAMP_PATH;
|
fazekasgy@0
|
224 char *chome = getenv("HOME");
|
fazekasgy@0
|
225 if (chome) {
|
fazekasgy@0
|
226 std::string home(chome);
|
fazekasgy@0
|
227 std::string::size_type f;
|
fazekasgy@0
|
228 while ((f = envPath.find("$HOME")) != std::string::npos &&
|
fazekasgy@0
|
229 f < envPath.length()) {
|
fazekasgy@0
|
230 envPath.replace(f, 5, home);
|
fazekasgy@0
|
231 }
|
fazekasgy@0
|
232 }
|
fazekasgy@0
|
233 #ifdef _WIN32
|
fazekasgy@0
|
234 char *cpfiles = getenv("ProgramFiles");
|
fazekasgy@0
|
235 if (!cpfiles) cpfiles = "C:\\Program Files";
|
fazekasgy@0
|
236 std::string pfiles(cpfiles);
|
fazekasgy@0
|
237 std::string::size_type f;
|
fazekasgy@0
|
238 while ((f = envPath.find("%ProgramFiles%")) != std::string::npos &&
|
fazekasgy@0
|
239 f < envPath.length()) {
|
fazekasgy@0
|
240 envPath.replace(f, 14, pfiles);
|
fazekasgy@0
|
241 }
|
fazekasgy@0
|
242 #endif
|
fazekasgy@0
|
243 }
|
fazekasgy@0
|
244
|
fazekasgy@0
|
245 std::string::size_type index = 0, newindex = 0;
|
fazekasgy@0
|
246
|
fazekasgy@0
|
247 while ((newindex = envPath.find(PATH_SEPARATOR, index)) < envPath.size()) {
|
fazekasgy@0
|
248 path.push_back(envPath.substr(index, newindex - index));
|
fazekasgy@0
|
249 index = newindex + 1;
|
fazekasgy@0
|
250 }
|
fazekasgy@0
|
251
|
fazekasgy@0
|
252 path.push_back(envPath.substr(index));
|
fazekasgy@0
|
253
|
fazekasgy@0
|
254 return path;
|
fazekasgy@0
|
255 }
|