Chris@390
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@390
|
2
|
Chris@390
|
3 /*
|
Chris@390
|
4 Vamp
|
Chris@390
|
5
|
Chris@390
|
6 An API for audio analysis and feature extraction plugins.
|
Chris@390
|
7
|
Chris@390
|
8 Centre for Digital Music, Queen Mary, University of London.
|
Chris@390
|
9 Copyright 2006-2015 Chris Cannam and QMUL.
|
Chris@390
|
10
|
Chris@390
|
11 Permission is hereby granted, free of charge, to any person
|
Chris@390
|
12 obtaining a copy of this software and associated documentation
|
Chris@390
|
13 files (the "Software"), to deal in the Software without
|
Chris@390
|
14 restriction, including without limitation the rights to use, copy,
|
Chris@390
|
15 modify, merge, publish, distribute, sublicense, and/or sell copies
|
Chris@390
|
16 of the Software, and to permit persons to whom the Software is
|
Chris@390
|
17 furnished to do so, subject to the following conditions:
|
Chris@390
|
18
|
Chris@390
|
19 The above copyright notice and this permission notice shall be
|
Chris@390
|
20 included in all copies or substantial portions of the Software.
|
Chris@390
|
21
|
Chris@390
|
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
Chris@390
|
23 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
Chris@390
|
24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
Chris@390
|
25 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
Chris@390
|
26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
Chris@390
|
27 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
Chris@390
|
28 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
Chris@390
|
29
|
Chris@390
|
30 Except as contained in this notice, the names of the Centre for
|
Chris@390
|
31 Digital Music; Queen Mary, University of London; and Chris Cannam
|
Chris@390
|
32 shall not be used in advertising or otherwise to promote the sale,
|
Chris@390
|
33 use or other dealings in this Software without prior written
|
Chris@390
|
34 authorization.
|
Chris@390
|
35 */
|
Chris@390
|
36
|
Chris@390
|
37 #include <vamp-hostsdk/PluginHostAdapter.h>
|
Chris@390
|
38
|
Chris@390
|
39 #include "Files.h"
|
Chris@390
|
40
|
Chris@390
|
41 #include <cctype> // tolower
|
Chris@390
|
42
|
Chris@390
|
43 #include <cstring>
|
Chris@390
|
44
|
Chris@390
|
45 #ifdef _WIN32
|
Chris@390
|
46
|
Chris@390
|
47 #include <windows.h>
|
Chris@390
|
48 #include <tchar.h>
|
Chris@390
|
49 #define PLUGIN_SUFFIX "dll"
|
Chris@390
|
50
|
Chris@390
|
51 #else /* ! _WIN32 */
|
Chris@390
|
52
|
Chris@390
|
53 #include <dirent.h>
|
Chris@390
|
54 #include <dlfcn.h>
|
Chris@390
|
55
|
Chris@390
|
56 #ifdef __APPLE__
|
Chris@390
|
57 #define PLUGIN_SUFFIX "dylib"
|
Chris@390
|
58 #else /* ! __APPLE__ */
|
Chris@390
|
59 #define PLUGIN_SUFFIX "so"
|
Chris@390
|
60 #endif /* ! __APPLE__ */
|
Chris@390
|
61
|
Chris@390
|
62 #endif /* ! _WIN32 */
|
Chris@390
|
63
|
Chris@390
|
64 using namespace std;
|
Chris@390
|
65
|
Chris@390
|
66 vector<string>
|
Chris@390
|
67 Files::listLibraryFiles()
|
Chris@390
|
68 {
|
Chris@477
|
69 return listLibraryFilesMatching(Filter());
|
Chris@390
|
70 }
|
Chris@390
|
71
|
Chris@390
|
72 vector<string>
|
Chris@473
|
73 Files::listLibraryFilesMatching(Filter filter)
|
Chris@390
|
74 {
|
Chris@390
|
75 vector<string> path = Vamp::PluginHostAdapter::getPluginPath();
|
Chris@390
|
76 vector<string> libraryFiles;
|
Chris@390
|
77
|
Chris@421
|
78 // we match case-insensitively, but only with ascii range
|
Chris@473
|
79 // characters (input strings are expected to be utf-8)
|
Chris@473
|
80 vector<string> libraryNames;
|
Chris@477
|
81 for (int j = 0; j < int(filter.libraryNames.size()); ++j) {
|
Chris@477
|
82 string n = filter.libraryNames[j];
|
Chris@473
|
83 for (size_t i = 0; i < n.length(); ++i) {
|
Chris@473
|
84 if (!(n[i] & 0x80)) {
|
Chris@473
|
85 n[i] = char(tolower(n[i]));
|
Chris@473
|
86 }
|
Chris@421
|
87 }
|
Chris@473
|
88 libraryNames.push_back(n);
|
Chris@390
|
89 }
|
Chris@390
|
90
|
Chris@390
|
91 for (size_t i = 0; i < path.size(); ++i) {
|
Chris@390
|
92
|
Chris@390
|
93 vector<string> files = listFiles(path[i], PLUGIN_SUFFIX);
|
Chris@390
|
94
|
Chris@390
|
95 for (vector<string>::iterator fi = files.begin();
|
Chris@390
|
96 fi != files.end(); ++fi) {
|
Chris@473
|
97
|
Chris@473
|
98 // we match case-insensitively, but only with ascii range
|
Chris@473
|
99 // characters (this string is expected to be utf-8)
|
Chris@473
|
100 string cleaned = *fi;
|
Chris@477
|
101 for (size_t j = 0; j < cleaned.length(); ++j) {
|
Chris@477
|
102 if (!(cleaned[j] & 0x80)) {
|
Chris@477
|
103 cleaned[j] = char(tolower(cleaned[j]));
|
Chris@390
|
104 }
|
Chris@390
|
105 }
|
Chris@390
|
106
|
Chris@473
|
107 // libraryName should be lacking an extension, as it is
|
Chris@473
|
108 // supposed to have come from the plugin key
|
Chris@473
|
109 string::size_type pi = cleaned.find('.');
|
Chris@473
|
110 if (pi != string::npos) {
|
Chris@473
|
111 cleaned = cleaned.substr(0, pi);
|
Chris@473
|
112 }
|
Chris@473
|
113
|
Chris@473
|
114 bool matched = false;
|
Chris@473
|
115
|
Chris@473
|
116 switch (filter.type) {
|
Chris@473
|
117
|
Chris@473
|
118 case Filter::All:
|
Chris@473
|
119 matched = true;
|
Chris@473
|
120 break;
|
Chris@473
|
121
|
Chris@473
|
122 case Filter::Matching:
|
Chris@477
|
123 for (int j = 0; j < int(libraryNames.size()); ++j) {
|
Chris@477
|
124 if (cleaned == libraryNames[j]) {
|
Chris@473
|
125 matched = true;
|
Chris@473
|
126 break;
|
Chris@473
|
127 }
|
Chris@473
|
128 }
|
Chris@473
|
129 break;
|
Chris@473
|
130
|
Chris@473
|
131 case Filter::NotMatching:
|
Chris@473
|
132 matched = true;
|
Chris@477
|
133 for (int j = 0; j < int(libraryNames.size()); ++j) {
|
Chris@477
|
134 if (cleaned == libraryNames[j]) {
|
Chris@473
|
135 matched = false;
|
Chris@473
|
136 break;
|
Chris@473
|
137 }
|
Chris@473
|
138 }
|
Chris@473
|
139 break;
|
Chris@473
|
140 }
|
Chris@473
|
141
|
Chris@473
|
142 if (!matched) continue;
|
Chris@473
|
143
|
Chris@390
|
144 string fullPath = path[i];
|
Chris@390
|
145 fullPath = splicePath(fullPath, *fi);
|
Chris@390
|
146 libraryFiles.push_back(fullPath);
|
Chris@390
|
147 }
|
Chris@390
|
148 }
|
Chris@390
|
149
|
Chris@390
|
150 return libraryFiles;
|
Chris@390
|
151 }
|
Chris@390
|
152
|
Chris@390
|
153 void *
|
Chris@390
|
154 Files::loadLibrary(string path)
|
Chris@390
|
155 {
|
Chris@390
|
156 void *handle = 0;
|
Chris@390
|
157 #ifdef _WIN32
|
Chris@390
|
158 #ifdef UNICODE
|
Chris@472
|
159 int wlen = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), path.length(), 0, 0);
|
Chris@472
|
160 if (wlen < 0) {
|
Chris@390
|
161 cerr << "Vamp::HostExt: Unable to convert library path \""
|
Chris@390
|
162 << path << "\" to wide characters " << endl;
|
Chris@390
|
163 return handle;
|
Chris@390
|
164 }
|
Chris@472
|
165 wchar_t *buffer = new wchar_t[wlen+1];
|
Chris@472
|
166 (void)MultiByteToWideChar(CP_UTF8, 0, path.c_str(), path.length(), buffer, wlen);
|
Chris@472
|
167 buffer[wlen] = L'\0';
|
Chris@390
|
168 handle = LoadLibrary(buffer);
|
Chris@390
|
169 delete[] buffer;
|
Chris@390
|
170 #else
|
Chris@390
|
171 handle = LoadLibrary(path.c_str());
|
Chris@390
|
172 #endif
|
Chris@390
|
173 if (!handle) {
|
Chris@390
|
174 cerr << "Vamp::HostExt: Unable to load library \""
|
Chris@390
|
175 << path << "\"" << endl;
|
Chris@390
|
176 }
|
Chris@390
|
177 #else
|
Chris@390
|
178 handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
Chris@390
|
179 if (!handle) {
|
Chris@390
|
180 cerr << "Vamp::HostExt: Unable to load library \""
|
Chris@390
|
181 << path << "\": " << dlerror() << endl;
|
Chris@390
|
182 }
|
Chris@390
|
183 #endif
|
Chris@390
|
184 return handle;
|
Chris@390
|
185 }
|
Chris@390
|
186
|
Chris@390
|
187 void
|
Chris@390
|
188 Files::unloadLibrary(void *handle)
|
Chris@390
|
189 {
|
Chris@390
|
190 #ifdef _WIN32
|
Chris@390
|
191 FreeLibrary((HINSTANCE)handle);
|
Chris@390
|
192 #else
|
Chris@390
|
193 dlclose(handle);
|
Chris@390
|
194 #endif
|
Chris@390
|
195 }
|
Chris@390
|
196
|
Chris@390
|
197 void *
|
Chris@390
|
198 Files::lookupInLibrary(void *handle, const char *symbol)
|
Chris@390
|
199 {
|
Chris@390
|
200 #ifdef _WIN32
|
Chris@390
|
201 return (void *)GetProcAddress((HINSTANCE)handle, symbol);
|
Chris@390
|
202 #else
|
Chris@390
|
203 return (void *)dlsym(handle, symbol);
|
Chris@390
|
204 #endif
|
Chris@390
|
205 }
|
Chris@390
|
206
|
Chris@390
|
207 string
|
Chris@390
|
208 Files::lcBasename(string path)
|
Chris@390
|
209 {
|
Chris@390
|
210 string basename(path);
|
Chris@390
|
211
|
Chris@390
|
212 string::size_type li = basename.rfind('/');
|
Chris@390
|
213 if (li != string::npos) basename = basename.substr(li + 1);
|
Chris@390
|
214
|
Chris@403
|
215 #ifdef _WIN32
|
Chris@405
|
216 li = basename.rfind('\\');
|
Chris@403
|
217 if (li != string::npos) basename = basename.substr(li + 1);
|
Chris@403
|
218 #endif
|
Chris@403
|
219
|
Chris@390
|
220 li = basename.find('.');
|
Chris@390
|
221 if (li != string::npos) basename = basename.substr(0, li);
|
Chris@390
|
222
|
Chris@421
|
223 // case-insensitive, but only with ascii range characters (this
|
Chris@421
|
224 // string is expected to be utf-8)
|
Chris@390
|
225 for (size_t i = 0; i < basename.length(); ++i) {
|
Chris@421
|
226 if (!(basename[i] & 0x80)) {
|
Chris@421
|
227 basename[i] = char(tolower(basename[i]));
|
Chris@421
|
228 }
|
Chris@390
|
229 }
|
Chris@390
|
230
|
Chris@390
|
231 return basename;
|
Chris@390
|
232 }
|
Chris@390
|
233
|
Chris@390
|
234 string
|
Chris@390
|
235 Files::splicePath(string a, string b)
|
Chris@390
|
236 {
|
Chris@390
|
237 #ifdef _WIN32
|
Chris@390
|
238 return a + "\\" + b;
|
Chris@390
|
239 #else
|
Chris@390
|
240 return a + "/" + b;
|
Chris@390
|
241 #endif
|
Chris@390
|
242 }
|
Chris@390
|
243
|
Chris@390
|
244 vector<string>
|
Chris@390
|
245 Files::listFiles(string dir, string extension)
|
Chris@390
|
246 {
|
Chris@390
|
247 vector<string> files;
|
Chris@390
|
248
|
Chris@390
|
249 #ifdef _WIN32
|
Chris@390
|
250 string expression = dir + "\\*." + extension;
|
Chris@390
|
251 #ifdef UNICODE
|
Chris@472
|
252 int wlen = MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), expression.length(), 0, 0);
|
Chris@472
|
253 if (wlen < 0) {
|
Chris@390
|
254 cerr << "Vamp::HostExt: Unable to convert wildcard path \""
|
Chris@390
|
255 << expression << "\" to wide characters" << endl;
|
Chris@390
|
256 return files;
|
Chris@390
|
257 }
|
Chris@472
|
258 wchar_t *buffer = new wchar_t[wlen+1];
|
Chris@472
|
259 (void)MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), expression.length(), buffer, wlen);
|
Chris@472
|
260 buffer[wlen] = L'\0';
|
Chris@390
|
261 WIN32_FIND_DATA data;
|
Chris@390
|
262 HANDLE fh = FindFirstFile(buffer, &data);
|
Chris@390
|
263 if (fh == INVALID_HANDLE_VALUE) {
|
Chris@390
|
264 delete[] buffer;
|
Chris@390
|
265 return files;
|
Chris@390
|
266 }
|
Chris@390
|
267
|
Chris@390
|
268 bool ok = true;
|
Chris@390
|
269 while (ok) {
|
Chris@390
|
270 wchar_t *fn = data.cFileName;
|
Chris@472
|
271 int wlen = wcslen(fn);
|
Chris@472
|
272 int len = WideCharToMultiByte(CP_UTF8, 0, fn, wlen, 0, 0, 0, 0);
|
Chris@472
|
273 if (len < 0) {
|
Chris@472
|
274 cerr << "Vamp::HostExt: Unable to convert wide char filename to utf-8" << endl;
|
Chris@472
|
275 break;
|
Chris@472
|
276 }
|
Chris@472
|
277 char *conv = new char[len+1];
|
Chris@472
|
278 (void)WideCharToMultiByte(CP_UTF8, 0, fn, wlen, conv, len, 0, 0);
|
Chris@472
|
279 conv[len] = '\0';
|
Chris@472
|
280 if (len > 0) {
|
Chris@390
|
281 files.push_back(conv);
|
Chris@390
|
282 }
|
Chris@390
|
283 delete[] conv;
|
Chris@390
|
284 ok = FindNextFile(fh, &data);
|
Chris@390
|
285 }
|
Chris@390
|
286
|
Chris@390
|
287 FindClose(fh);
|
Chris@390
|
288 delete[] buffer;
|
Chris@390
|
289 #else
|
Chris@390
|
290 WIN32_FIND_DATA data;
|
Chris@390
|
291 HANDLE fh = FindFirstFile(expression.c_str(), &data);
|
Chris@390
|
292 if (fh == INVALID_HANDLE_VALUE) return files;
|
Chris@390
|
293
|
Chris@390
|
294 bool ok = true;
|
Chris@390
|
295 while (ok) {
|
Chris@390
|
296 files.push_back(data.cFileName);
|
Chris@390
|
297 ok = FindNextFile(fh, &data);
|
Chris@390
|
298 }
|
Chris@390
|
299
|
Chris@390
|
300 FindClose(fh);
|
Chris@390
|
301 #endif
|
Chris@390
|
302 #else
|
Chris@390
|
303
|
Chris@390
|
304 size_t extlen = extension.length();
|
Chris@390
|
305 DIR *d = opendir(dir.c_str());
|
Chris@390
|
306 if (!d) return files;
|
Chris@390
|
307
|
Chris@390
|
308 struct dirent *e = 0;
|
Chris@390
|
309 while ((e = readdir(d))) {
|
Chris@390
|
310
|
Chris@390
|
311 size_t len = strlen(e->d_name);
|
Chris@390
|
312 if (len < extlen + 2 ||
|
Chris@390
|
313 e->d_name + len - extlen - 1 != "." + extension) {
|
Chris@390
|
314 continue;
|
Chris@390
|
315 }
|
Chris@390
|
316
|
Chris@390
|
317 files.push_back(e->d_name);
|
Chris@390
|
318 }
|
Chris@390
|
319
|
Chris@390
|
320 closedir(d);
|
Chris@390
|
321 #endif
|
Chris@390
|
322
|
Chris@390
|
323 return files;
|
Chris@390
|
324 }
|