Chris@67
|
1 /* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */
|
fazekasgy@37
|
2 /*
|
fazekasgy@37
|
3
|
fazekasgy@37
|
4 * Vampy : This plugin is a wrapper around the Vamp plugin API.
|
fazekasgy@37
|
5 * It allows for writing Vamp plugins in Python.
|
fazekasgy@37
|
6
|
fazekasgy@37
|
7 * Centre for Digital Music, Queen Mary University of London.
|
fazekasgy@37
|
8 * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources
|
fazekasgy@37
|
9 * for licence information.)
|
fazekasgy@37
|
10
|
fazekasgy@37
|
11 */
|
fazekasgy@37
|
12
|
fazekasgy@37
|
13 #include <Python.h>
|
fazekasgy@37
|
14 #include "vamp/vamp.h"
|
fazekasgy@37
|
15 #include "PyExtensionModule.h"
|
fazekasgy@37
|
16 #include "PyExtensionManager.h"
|
fazekasgy@37
|
17 #include <algorithm>
|
Chris@67
|
18 #include "Debug.h"
|
fazekasgy@37
|
19
|
fazekasgy@37
|
20 using std::cerr;
|
fazekasgy@37
|
21 using std::endl;
|
fazekasgy@37
|
22 using std::string;
|
fazekasgy@37
|
23 using std::vector;
|
fazekasgy@37
|
24 using std::find;
|
fazekasgy@37
|
25
|
fazekasgy@37
|
26 //static
|
Chris@66
|
27 const char* PyExtensionManager::m_exposedNames[] = {
|
Chris@91
|
28
|
fazekasgy@37
|
29 "ParameterDescriptor",
|
fazekasgy@37
|
30 "OutputDescriptor",
|
fazekasgy@37
|
31 "FeatureSet",
|
fazekasgy@37
|
32 "Feature",
|
fazekasgy@37
|
33 "RealTime",
|
fazekasgy@37
|
34 "frame2RealTime",
|
Chris@91
|
35
|
Chris@91
|
36 /* // using builtin objects:
|
Chris@91
|
37 "ParameterList",
|
Chris@91
|
38 "OutputList",
|
Chris@91
|
39 "FeatureList",
|
fazekasgy@37
|
40 "OneSamplePerStep",
|
fazekasgy@37
|
41 "FixedSampleRate",
|
fazekasgy@37
|
42 "VariableSampleRate",
|
fazekasgy@37
|
43 "TimeDomain",
|
fazekasgy@37
|
44 "FrequencyDomain",
|
fazekasgy@37
|
45 */
|
Chris@91
|
46
|
fazekasgy@37
|
47 NULL
|
fazekasgy@37
|
48 };
|
fazekasgy@37
|
49
|
Chris@113
|
50 PyExtensionManager::PyExtensionManager() :
|
Chris@113
|
51 m_pyGlobalNamespace(0),
|
Chris@113
|
52 m_pyVampyNamespace(0)
|
fazekasgy@37
|
53 {
|
Chris@67
|
54 DSTREAM << "Creating extension manager." << endl;
|
fazekasgy@37
|
55 }
|
fazekasgy@37
|
56
|
fazekasgy@37
|
57 bool
|
fazekasgy@37
|
58 PyExtensionManager::initExtension()
|
fazekasgy@37
|
59 {
|
Chris@67
|
60 DSTREAM << "Initialising extension module." << endl;
|
fazekasgy@37
|
61
|
fazekasgy@37
|
62 /// call the module initialiser first
|
fazekasgy@37
|
63 initvampy();
|
fazekasgy@37
|
64
|
fazekasgy@37
|
65 /// these references are all borrowed
|
fazekasgy@37
|
66 m_pyGlobalNamespace = PyImport_GetModuleDict();
|
fazekasgy@37
|
67 if (!m_pyGlobalNamespace)
|
fazekasgy@37
|
68 {cerr << "Vampy::PyExtensionManager::initExtension: GlobalNamespace failed." << endl; return false;}
|
fazekasgy@37
|
69 PyObject *pyVampyModule = PyDict_GetItemString(m_pyGlobalNamespace,"vampy");
|
fazekasgy@37
|
70 if (!pyVampyModule)
|
fazekasgy@37
|
71 {cerr << "Vampy::PyExtensionManager::initExtension: VampyModule failed." << endl; return false;}
|
fazekasgy@37
|
72 m_pyVampyNamespace = PyModule_GetDict(pyVampyModule);
|
fazekasgy@37
|
73 if (!m_pyVampyNamespace)
|
fazekasgy@37
|
74 {cerr << "Vampy::PyExtensionManager::initExtension: VampyNamespace failed." << endl; return false;}
|
fazekasgy@37
|
75
|
fazekasgy@37
|
76 /// initialise local namespaces
|
fazekasgy@37
|
77 updateAllLocals();
|
Chris@67
|
78
|
Chris@67
|
79 DSTREAM << "Vampy: Extension namespaces updated." << endl;
|
Chris@67
|
80
|
fazekasgy@37
|
81 return true;
|
fazekasgy@37
|
82 }
|
fazekasgy@37
|
83
|
fazekasgy@37
|
84
|
fazekasgy@37
|
85 PyExtensionManager::~PyExtensionManager()
|
fazekasgy@37
|
86 {
|
Chris@113
|
87 if (!m_pyVampyNamespace) {
|
Chris@113
|
88 DSTREAM << "Vampy::~PyExtensionManager: manager was never initialised, or initialisation did not complete: not attempting cleanup" << endl;
|
Chris@113
|
89 return;
|
Chris@113
|
90 }
|
Chris@113
|
91
|
Chris@67
|
92 DSTREAM << "Cleaning locals..." << endl;
|
fazekasgy@37
|
93
|
fazekasgy@37
|
94 cleanAllLocals();
|
fazekasgy@37
|
95
|
Chris@67
|
96 DSTREAM << "Cleaning module..." << endl;
|
fazekasgy@37
|
97
|
Chris@113
|
98 if (!cleanModule()) {
|
fazekasgy@37
|
99 cerr << "Vampy::~PyExtensionManager: failed to clean extension module." << endl;
|
Chris@113
|
100 } else {
|
Chris@113
|
101 DSTREAM << "Vampy::~PyExtensionManager: Extension module cleaned." << endl;
|
Chris@113
|
102 }
|
fazekasgy@37
|
103 }
|
fazekasgy@37
|
104
|
fazekasgy@37
|
105
|
fazekasgy@37
|
106
|
fazekasgy@37
|
107 void
|
fazekasgy@37
|
108 PyExtensionManager::setPlugModuleNames(vector<string> pyPlugs)
|
fazekasgy@37
|
109 {
|
fazekasgy@37
|
110 for (size_t i = 0; i < pyPlugs.size(); ++i) {
|
fazekasgy@37
|
111 string modName = pyPlugs[i];
|
fazekasgy@37
|
112 string tmp = modName.substr(modName.rfind(':')+1,modName.size()-1);
|
fazekasgy@37
|
113 m_plugModuleNames.push_back(tmp);
|
fazekasgy@37
|
114
|
Chris@67
|
115 DSTREAM << "Inserted module name: " << tmp << endl;
|
fazekasgy@37
|
116 }
|
fazekasgy@37
|
117 }
|
fazekasgy@37
|
118
|
fazekasgy@37
|
119 void
|
fazekasgy@37
|
120 PyExtensionManager::deleteModuleName(string plugKey)
|
fazekasgy@37
|
121 {
|
fazekasgy@37
|
122 string name = plugKey.substr(plugKey.rfind(':')+1,plugKey.size()-1);
|
fazekasgy@37
|
123 vector<string>::iterator it =
|
fazekasgy@37
|
124 find (m_plugModuleNames.begin(), m_plugModuleNames.end(), name);
|
fazekasgy@37
|
125 if (it != m_plugModuleNames.end()) m_plugModuleNames.erase(it);
|
Chris@67
|
126
|
Chris@67
|
127 DSTREAM << "PyExtensionManager::deleteModuleName: Deleted module name: " << name << endl;
|
fazekasgy@37
|
128 }
|
fazekasgy@37
|
129
|
fazekasgy@37
|
130
|
fazekasgy@37
|
131 void
|
fazekasgy@37
|
132 PyExtensionManager::cleanAllLocals() const
|
fazekasgy@37
|
133 {
|
fazekasgy@37
|
134 for (size_t i = 0; i < m_plugModuleNames.size(); ++i) {
|
fazekasgy@37
|
135 cleanLocalNamespace(m_plugModuleNames[i].c_str());
|
fazekasgy@37
|
136 }
|
fazekasgy@37
|
137 }
|
fazekasgy@37
|
138
|
fazekasgy@37
|
139 void
|
fazekasgy@37
|
140 PyExtensionManager::updateAllLocals() const
|
fazekasgy@37
|
141 {
|
fazekasgy@37
|
142 for (size_t i = 0; i < m_plugModuleNames.size(); ++i) {
|
fazekasgy@37
|
143 updateLocalNamespace(m_plugModuleNames[i].c_str());
|
fazekasgy@37
|
144 }
|
fazekasgy@37
|
145 }
|
fazekasgy@37
|
146
|
fazekasgy@37
|
147 void
|
fazekasgy@37
|
148 PyExtensionManager::cleanLocalNamespace(const char* plugModuleName) const
|
fazekasgy@37
|
149 {
|
Chris@91
|
150 DSTREAM << "Cleaning local namespace: " << plugModuleName << endl;
|
Chris@91
|
151
|
fazekasgy@37
|
152 /// these references are all borrowed
|
fazekasgy@37
|
153 PyObject *pyPlugModule = PyDict_GetItemString(m_pyGlobalNamespace,plugModuleName);
|
fazekasgy@37
|
154 if (!pyPlugModule) return;
|
fazekasgy@37
|
155 PyObject *pyPlugDict = PyModule_GetDict(pyPlugModule);
|
fazekasgy@37
|
156 if (!pyPlugDict) return;
|
fazekasgy@37
|
157
|
fazekasgy@37
|
158 int i = 0;
|
fazekasgy@37
|
159 while (PyExtensionManager::m_exposedNames[i]) {
|
Chris@66
|
160 const char* name = PyExtensionManager::m_exposedNames[i];
|
fazekasgy@37
|
161 i++;
|
fazekasgy@37
|
162 PyObject *key = PyString_FromString(name);
|
fazekasgy@37
|
163 if (!key) break;
|
fazekasgy@37
|
164 if (PyDict_Contains(pyPlugDict,key)) {
|
fazekasgy@37
|
165 if (PyDict_SetItem(pyPlugDict,key,Py_None) != 0)
|
fazekasgy@37
|
166 cerr << "Vampy::PyExtensionManager::cleanLocalNamespace: Failed: "
|
fazekasgy@37
|
167 << name << " of "<< plugModuleName << endl;
|
Chris@67
|
168 else DSTREAM << "Cleaned local name: " << name << endl;
|
fazekasgy@37
|
169 }
|
fazekasgy@37
|
170 Py_DECREF(key);
|
fazekasgy@37
|
171 }
|
fazekasgy@37
|
172 }
|
fazekasgy@37
|
173
|
fazekasgy@37
|
174 void
|
fazekasgy@37
|
175 PyExtensionManager::updateLocalNamespace(const char* plugModuleName) const
|
fazekasgy@37
|
176 {
|
Chris@91
|
177 DSTREAM << "Updating local namespace: " << plugModuleName << endl;
|
Chris@91
|
178
|
fazekasgy@37
|
179 /// this allows the use of common syntax like:
|
fazekasgy@37
|
180 /// from vampy import *
|
fazekasgy@37
|
181 /// even after several unload/reload cycles
|
fazekasgy@37
|
182
|
fazekasgy@37
|
183 /// these references are all borrowed
|
fazekasgy@37
|
184 PyObject *pyPlugModule = PyDict_GetItemString(m_pyGlobalNamespace,plugModuleName);
|
fazekasgy@37
|
185 if (!pyPlugModule) return;
|
fazekasgy@37
|
186 PyObject *pyPlugDict = PyModule_GetDict(pyPlugModule);
|
fazekasgy@37
|
187 if (!pyPlugDict) return;
|
fazekasgy@37
|
188
|
fazekasgy@37
|
189 int i = 0;
|
fazekasgy@37
|
190 while (PyExtensionManager::m_exposedNames[i]) {
|
fazekasgy@37
|
191 const char* name = PyExtensionManager::m_exposedNames[i];
|
fazekasgy@37
|
192 i++;
|
fazekasgy@37
|
193 PyObject *key = PyString_FromString(name);
|
fazekasgy@37
|
194 if (!key) break;
|
fazekasgy@37
|
195 if (PyDict_Contains(pyPlugDict,key)) {
|
fazekasgy@37
|
196 PyObject* item = PyDict_GetItem(m_pyVampyNamespace,key);
|
fazekasgy@37
|
197 if (PyDict_SetItem(pyPlugDict,key,item) != 0)
|
fazekasgy@37
|
198 cerr << "Vampy::PyExtensionManager::updateLocalNamespace: Failed: "
|
fazekasgy@37
|
199 << name << " of "<< plugModuleName << endl;
|
Chris@67
|
200 else DSTREAM << "Updated local name: " << name << endl;
|
Chris@89
|
201 } else {
|
Chris@89
|
202 DSTREAM << "Local namespace does not contain name: " << name << endl;
|
fazekasgy@37
|
203 }
|
fazekasgy@37
|
204 Py_DECREF(key);
|
fazekasgy@37
|
205 }
|
fazekasgy@37
|
206 }
|
fazekasgy@37
|
207
|
fazekasgy@37
|
208
|
fazekasgy@37
|
209 bool
|
fazekasgy@37
|
210 PyExtensionManager::cleanModule(void) const
|
fazekasgy@37
|
211 {
|
fazekasgy@37
|
212 PyObject *m = PyImport_AddModule("vampy");
|
fazekasgy@37
|
213 if (!m) {
|
fazekasgy@37
|
214 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
fazekasgy@37
|
215 cerr << "Vampy::PyExtensionManager::cleanModule: PyImport_AddModule returned NULL!" << endl;
|
fazekasgy@37
|
216 return false;
|
fazekasgy@37
|
217 } else {
|
fazekasgy@37
|
218 PyObject *dict = PyModule_GetDict(m);
|
fazekasgy@37
|
219 #ifdef _DEBUG
|
fazekasgy@37
|
220 Py_ssize_t ln = PyDict_Size(dict);
|
Chris@67
|
221 DSTREAM << "Vampy::PyExtensionManager::cleanModule: Size of module dict = " << (int) ln << endl;
|
fazekasgy@37
|
222 #endif
|
fazekasgy@37
|
223 /// Clean the module dictionary.
|
fazekasgy@37
|
224 // printDict(dict);
|
fazekasgy@37
|
225 PyDict_Clear(dict);
|
fazekasgy@37
|
226 if (PyErr_Occurred())
|
fazekasgy@37
|
227 { PyErr_Print(); PyErr_Clear(); return false; }
|
fazekasgy@37
|
228 PyObject *name = PyString_FromString("vampy");
|
fazekasgy@37
|
229 if (name) PyDict_SetItemString(dict,"__name__",name);
|
fazekasgy@37
|
230 Py_XDECREF(name);
|
fazekasgy@37
|
231 #ifdef _DEBUG
|
fazekasgy@37
|
232 ln = PyDict_Size(dict);
|
Chris@67
|
233 DSTREAM << "Vampy::PyExtensionManager::cleanModule: Size of module dict (cleaned) = " << (int) ln << endl;
|
fazekasgy@37
|
234 #endif
|
fazekasgy@37
|
235 return true;
|
fazekasgy@37
|
236 }
|
fazekasgy@37
|
237 }
|
fazekasgy@37
|
238
|
fazekasgy@37
|
239 void
|
fazekasgy@37
|
240 PyExtensionManager::printDict(PyObject* inDict) const
|
fazekasgy@37
|
241 {
|
fazekasgy@37
|
242 Py_ssize_t pyPos = 0;
|
fazekasgy@37
|
243 PyObject *pyKey, *pyDictValue;
|
fazekasgy@37
|
244 cerr << endl << endl << "Module dictionary contents: " << endl;
|
fazekasgy@37
|
245 while (PyDict_Next(inDict, &pyPos, &pyKey, &pyDictValue))
|
fazekasgy@37
|
246 {
|
fazekasgy@37
|
247 char *key = PyString_AS_STRING(pyKey);
|
fazekasgy@37
|
248 char *val = PyString_AS_STRING(PyObject_Str(pyDictValue));
|
fazekasgy@37
|
249 cerr << "key: [ '" << key << "' ] value: " << val << endl;
|
fazekasgy@37
|
250 }
|
fazekasgy@37
|
251 }
|
fazekasgy@37
|
252
|