cannam@56
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@56
|
2
|
cannam@56
|
3 /*
|
cannam@56
|
4 Vamp
|
cannam@56
|
5
|
cannam@56
|
6 An API for audio analysis and feature extraction plugins.
|
cannam@56
|
7
|
cannam@56
|
8 Centre for Digital Music, Queen Mary, University of London.
|
cannam@56
|
9 Copyright 2006 Chris Cannam.
|
cannam@56
|
10
|
cannam@56
|
11 Permission is hereby granted, free of charge, to any person
|
cannam@56
|
12 obtaining a copy of this software and associated documentation
|
cannam@56
|
13 files (the "Software"), to deal in the Software without
|
cannam@56
|
14 restriction, including without limitation the rights to use, copy,
|
cannam@56
|
15 modify, merge, publish, distribute, sublicense, and/or sell copies
|
cannam@56
|
16 of the Software, and to permit persons to whom the Software is
|
cannam@56
|
17 furnished to do so, subject to the following conditions:
|
cannam@56
|
18
|
cannam@56
|
19 The above copyright notice and this permission notice shall be
|
cannam@56
|
20 included in all copies or substantial portions of the Software.
|
cannam@56
|
21
|
cannam@56
|
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
cannam@56
|
23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
cannam@56
|
24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
cannam@56
|
25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
cannam@56
|
26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
cannam@56
|
27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
cannam@56
|
28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
cannam@56
|
29
|
cannam@56
|
30 Except as contained in this notice, the names of the Centre for
|
cannam@56
|
31 Digital Music; Queen Mary, University of London; and Chris Cannam
|
cannam@56
|
32 shall not be used in advertising or otherwise to promote the sale,
|
cannam@56
|
33 use or other dealings in this Software without prior written
|
cannam@56
|
34 authorization.
|
cannam@56
|
35 */
|
cannam@56
|
36
|
cannam@56
|
37 #include "PluginLoader.h"
|
cannam@56
|
38 #include "PluginHostAdapter.h"
|
cannam@56
|
39
|
cannam@56
|
40 #include "system.h"
|
cannam@56
|
41
|
cannam@57
|
42 #include <fstream>
|
cannam@57
|
43
|
cannam@56
|
44 #include <dirent.h> // POSIX directory open and read
|
cannam@56
|
45
|
cannam@57
|
46 using namespace std;
|
cannam@57
|
47
|
cannam@56
|
48 namespace Vamp {
|
cannam@56
|
49
|
cannam@56
|
50 PluginLoader::PluginLoader()
|
cannam@56
|
51 {
|
cannam@56
|
52 }
|
cannam@56
|
53
|
cannam@56
|
54 PluginLoader::~PluginLoader()
|
cannam@56
|
55 {
|
cannam@56
|
56 }
|
cannam@56
|
57
|
cannam@57
|
58 vector<PluginLoader::PluginKey>
|
cannam@56
|
59 PluginLoader::listPlugins()
|
cannam@56
|
60 {
|
cannam@56
|
61 if (m_pluginLibraryMap.empty()) {
|
cannam@56
|
62
|
cannam@57
|
63 vector<string> path = PluginHostAdapter::getPluginPath();
|
cannam@56
|
64
|
cannam@56
|
65 size_t suffixLen = strlen(PLUGIN_SUFFIX);
|
cannam@56
|
66
|
cannam@56
|
67 for (size_t i = 0; i < path.size(); ++i) {
|
cannam@57
|
68
|
cannam@57
|
69 vector<string> files = getFilesInDir(path[i], PLUGIN_SUFFIX);
|
cannam@57
|
70
|
cannam@56
|
71
|
cannam@57
|
72 for (vector<string>::iterator fi = files.begin();
|
cannam@57
|
73 fi != files.end(); ++fi) {
|
cannam@56
|
74
|
cannam@57
|
75 string basename = *fi;
|
cannam@57
|
76 basename = basename.substr(0, basename.length() - suffixLen - 1);
|
cannam@56
|
77
|
cannam@57
|
78 string fullPath = path[i];
|
cannam@57
|
79 fullPath = fullPath + "/" + *fi; //!!! systemize
|
cannam@56
|
80 void *handle = DLOPEN(fullPath, RTLD_LAZY);
|
cannam@56
|
81
|
cannam@56
|
82 if (!handle) {
|
cannam@57
|
83 cerr << "Vamp::PluginLoader: " << *fi
|
cannam@56
|
84 << ": unable to load library (" << DLERROR()
|
cannam@57
|
85 << ")" << endl;
|
cannam@56
|
86 continue;
|
cannam@56
|
87 }
|
cannam@56
|
88
|
cannam@56
|
89 VampGetPluginDescriptorFunction fn =
|
cannam@56
|
90 (VampGetPluginDescriptorFunction)DLSYM
|
cannam@56
|
91 (handle, "vampGetPluginDescriptor");
|
cannam@56
|
92
|
cannam@56
|
93 if (!fn) {
|
cannam@56
|
94 DLCLOSE(handle);
|
cannam@56
|
95 continue;
|
cannam@56
|
96 }
|
cannam@56
|
97
|
cannam@56
|
98 int index = 0;
|
cannam@56
|
99 const VampPluginDescriptor *descriptor = 0;
|
cannam@56
|
100
|
cannam@56
|
101 while ((descriptor = fn(VAMP_API_VERSION, index))) {
|
cannam@56
|
102 PluginKey key = basename + ":" + descriptor->identifier;
|
cannam@56
|
103 if (m_pluginLibraryMap.find(key) ==
|
cannam@56
|
104 m_pluginLibraryMap.end()) {
|
cannam@56
|
105 m_pluginLibraryMap[key] = fullPath;
|
cannam@56
|
106 }
|
cannam@56
|
107 ++index;
|
cannam@56
|
108 }
|
cannam@56
|
109
|
cannam@56
|
110 DLCLOSE(handle);
|
cannam@56
|
111 }
|
cannam@56
|
112 }
|
cannam@56
|
113 }
|
cannam@56
|
114
|
cannam@57
|
115 vector<PluginKey> plugins;
|
cannam@57
|
116 for (map<PluginKey, string>::iterator mi =
|
cannam@56
|
117 m_pluginLibraryMap.begin();
|
cannam@56
|
118 mi != m_pluginLibraryMap.end(); ++mi) {
|
cannam@56
|
119 plugins.push_back(mi->first);
|
cannam@56
|
120 }
|
cannam@56
|
121
|
cannam@56
|
122 return plugins;
|
cannam@56
|
123 }
|
cannam@56
|
124
|
cannam@57
|
125 PluginLoader::PluginCategoryHierarchy
|
cannam@57
|
126 PluginLoader::getPluginCategory(PluginKey plugin)
|
cannam@57
|
127 {
|
cannam@57
|
128 if (m_taxonomy.empty()) generateTaxonomy();
|
cannam@57
|
129 if (m_taxonomy.find(plugin) == m_taxonomy.end()) return PluginCategoryHierarchy();
|
cannam@57
|
130 return m_taxonomy[plugin];
|
cannam@57
|
131 }
|
cannam@57
|
132
|
cannam@57
|
133 string
|
cannam@57
|
134 PluginLoader::getLibraryPathForPlugin(PluginKey plugin)
|
cannam@56
|
135 {
|
cannam@56
|
136 if (m_pluginLibraryMap.empty()) (void)listPlugins();
|
cannam@57
|
137 if (m_pluginLibraryMap.find(plugin) == m_pluginLibraryMap.end()) return "";
|
cannam@57
|
138 return m_pluginLibraryMap[plugin];
|
cannam@56
|
139 }
|
cannam@56
|
140
|
cannam@56
|
141 Plugin *
|
cannam@56
|
142 PluginLoader::load(PluginKey key, float inputSampleRate)
|
cannam@56
|
143 {
|
cannam@57
|
144 string fullPath = getLibraryPathForPlugin(key);
|
cannam@56
|
145 if (fullPath == "") return 0;
|
cannam@56
|
146
|
cannam@57
|
147 string::size_type ki = key.find(':');
|
cannam@57
|
148 if (ki == string::npos) {
|
cannam@56
|
149 //!!! flag error
|
cannam@56
|
150 return 0;
|
cannam@56
|
151 }
|
cannam@56
|
152
|
cannam@57
|
153 string identifier = key.substr(ki + 1);
|
cannam@56
|
154
|
cannam@56
|
155 void *handle = DLOPEN(fullPath, RTLD_LAZY);
|
cannam@56
|
156
|
cannam@56
|
157 if (!handle) {
|
cannam@57
|
158 cerr << "Vamp::PluginLoader: " << fullPath
|
cannam@56
|
159 << ": unable to load library (" << DLERROR()
|
cannam@57
|
160 << ")" << endl;
|
cannam@56
|
161 return 0;
|
cannam@56
|
162 }
|
cannam@56
|
163
|
cannam@56
|
164 VampGetPluginDescriptorFunction fn =
|
cannam@56
|
165 (VampGetPluginDescriptorFunction)DLSYM
|
cannam@56
|
166 (handle, "vampGetPluginDescriptor");
|
cannam@56
|
167
|
cannam@56
|
168 if (!fn) {
|
cannam@56
|
169 //!!! refcount this! --!!! no, POSIX says dlopen/dlclose will
|
cannam@56
|
170 // reference count. check on win32
|
cannam@56
|
171 DLCLOSE(handle);
|
cannam@56
|
172 return 0;
|
cannam@56
|
173 }
|
cannam@56
|
174
|
cannam@56
|
175 int index = 0;
|
cannam@56
|
176 const VampPluginDescriptor *descriptor = 0;
|
cannam@56
|
177
|
cannam@56
|
178 while ((descriptor = fn(VAMP_API_VERSION, index))) {
|
cannam@57
|
179 if (string(descriptor->identifier) == identifier) {
|
cannam@56
|
180 return new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
|
cannam@56
|
181 }
|
cannam@56
|
182 ++index;
|
cannam@56
|
183 }
|
cannam@56
|
184
|
cannam@56
|
185 //!!! flag error
|
cannam@56
|
186 return 0;
|
cannam@56
|
187 }
|
cannam@56
|
188
|
cannam@57
|
189 vector<string>
|
cannam@57
|
190 PluginLoader::getFilesInDir(string dir, string extension)
|
cannam@57
|
191 {
|
cannam@57
|
192 vector<string> files;
|
cannam@57
|
193
|
cannam@57
|
194 DIR *d = opendir(dir.c_str());
|
cannam@57
|
195 if (!d) return files;
|
cannam@57
|
196
|
cannam@57
|
197 struct dirent *e = 0;
|
cannam@57
|
198 while ((e = readdir(d))) {
|
cannam@57
|
199
|
cannam@57
|
200 if (!(e->d_type & DT_REG) || !e->d_name) {
|
cannam@57
|
201 continue;
|
cannam@57
|
202 }
|
cannam@57
|
203
|
cannam@57
|
204 int len = strlen(e->d_name);
|
cannam@57
|
205 if (len < int(extension.length() + 2) ||
|
cannam@57
|
206 e->d_name[len - extension.length() - 1] != '.' ||
|
cannam@57
|
207 strcmp(e->d_name + len - extension.length(), extension.c_str())) {
|
cannam@57
|
208 continue;
|
cannam@57
|
209 }
|
cannam@57
|
210
|
cannam@57
|
211 files.push_back(e->d_name);
|
cannam@57
|
212 }
|
cannam@57
|
213
|
cannam@57
|
214 closedir(d);
|
cannam@57
|
215
|
cannam@57
|
216 return files;
|
cannam@56
|
217 }
|
cannam@56
|
218
|
cannam@57
|
219 void
|
cannam@57
|
220 PluginLoader::generateTaxonomy()
|
cannam@57
|
221 {
|
cannam@57
|
222 // cerr << "PluginLoader::generateTaxonomy" << endl;
|
cannam@57
|
223
|
cannam@57
|
224 vector<string> path = PluginHostAdapter::getPluginPath();
|
cannam@57
|
225 string libfragment = "/lib/";
|
cannam@57
|
226 vector<string> catpath;
|
cannam@57
|
227
|
cannam@57
|
228 string suffix = "cat";
|
cannam@57
|
229
|
cannam@57
|
230 for (vector<string>::iterator i = path.begin();
|
cannam@57
|
231 i != path.end(); ++i) {
|
cannam@57
|
232
|
cannam@57
|
233 string dir = *i;
|
cannam@57
|
234 string::size_type li = dir.find(libfragment);
|
cannam@57
|
235
|
cannam@57
|
236 if (li != string::npos) {
|
cannam@57
|
237 catpath.push_back
|
cannam@57
|
238 (dir.substr(0, li)
|
cannam@57
|
239 + "/share/"
|
cannam@57
|
240 + dir.substr(li + libfragment.length()));
|
cannam@57
|
241 }
|
cannam@57
|
242
|
cannam@57
|
243 catpath.push_back(dir);
|
cannam@57
|
244 }
|
cannam@57
|
245
|
cannam@57
|
246 char buffer[1024];
|
cannam@57
|
247
|
cannam@57
|
248 for (vector<string>::iterator i = catpath.begin();
|
cannam@57
|
249 i != catpath.end(); ++i) {
|
cannam@57
|
250
|
cannam@57
|
251 vector<string> files = getFilesInDir(*i, suffix);
|
cannam@57
|
252
|
cannam@57
|
253 for (vector<string>::iterator fi = files.begin();
|
cannam@57
|
254 fi != files.end(); ++fi) {
|
cannam@57
|
255
|
cannam@57
|
256 string filepath = *i + "/" + *fi; //!!! systemize
|
cannam@57
|
257 ifstream is(filepath.c_str(), ifstream::in | ifstream::binary);
|
cannam@57
|
258
|
cannam@57
|
259 if (is.fail()) {
|
cannam@57
|
260 // cerr << "failed to open: " << filepath << endl;
|
cannam@57
|
261 continue;
|
cannam@57
|
262 }
|
cannam@57
|
263
|
cannam@57
|
264 // cerr << "opened: " << filepath << endl;
|
cannam@57
|
265
|
cannam@57
|
266 while (!!is.getline(buffer, 1024)) {
|
cannam@57
|
267
|
cannam@57
|
268 string line(buffer);
|
cannam@57
|
269
|
cannam@57
|
270 // cerr << "line = " << line << endl;
|
cannam@57
|
271
|
cannam@57
|
272 string::size_type di = line.find("::");
|
cannam@57
|
273 if (di == string::npos) continue;
|
cannam@57
|
274
|
cannam@57
|
275 string id = line.substr(0, di);
|
cannam@57
|
276 string encodedCat = line.substr(di + 2);
|
cannam@57
|
277
|
cannam@57
|
278 if (id.substr(0, 5) != "vamp:") continue;
|
cannam@57
|
279 id = id.substr(5);
|
cannam@57
|
280
|
cannam@57
|
281 while (encodedCat.length() >= 1 &&
|
cannam@57
|
282 encodedCat[encodedCat.length()-1] == '\r') {
|
cannam@57
|
283 encodedCat = encodedCat.substr(0, encodedCat.length()-1);
|
cannam@57
|
284 }
|
cannam@57
|
285
|
cannam@57
|
286 // cerr << "id = " << id << ", cat = " << encodedCat << endl;
|
cannam@57
|
287
|
cannam@57
|
288 PluginCategoryHierarchy category;
|
cannam@57
|
289 string::size_type ai;
|
cannam@57
|
290 while ((ai = encodedCat.find(" > ")) != string::npos) {
|
cannam@57
|
291 category.push_back(encodedCat.substr(0, ai));
|
cannam@57
|
292 encodedCat = encodedCat.substr(ai + 3);
|
cannam@57
|
293 }
|
cannam@57
|
294 if (encodedCat != "") category.push_back(encodedCat);
|
cannam@57
|
295
|
cannam@57
|
296 m_taxonomy[id] = category;
|
cannam@57
|
297 }
|
cannam@57
|
298 }
|
cannam@57
|
299 }
|
cannam@57
|
300 }
|
cannam@57
|
301
|
cannam@57
|
302
|
cannam@57
|
303 }
|