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@514
|
53 #include <cstdlib>
|
Chris@390
|
54 #include <dirent.h>
|
Chris@390
|
55 #include <dlfcn.h>
|
Chris@390
|
56
|
Chris@390
|
57 #ifdef __APPLE__
|
Chris@390
|
58 #define PLUGIN_SUFFIX "dylib"
|
Chris@390
|
59 #else /* ! __APPLE__ */
|
Chris@390
|
60 #define PLUGIN_SUFFIX "so"
|
Chris@390
|
61 #endif /* ! __APPLE__ */
|
Chris@390
|
62
|
Chris@390
|
63 #endif /* ! _WIN32 */
|
Chris@390
|
64
|
Chris@390
|
65 using namespace std;
|
Chris@390
|
66
|
Chris@390
|
67 vector<string>
|
Chris@390
|
68 Files::listLibraryFiles()
|
Chris@390
|
69 {
|
Chris@477
|
70 return listLibraryFilesMatching(Filter());
|
Chris@390
|
71 }
|
Chris@390
|
72
|
Chris@390
|
73 vector<string>
|
Chris@473
|
74 Files::listLibraryFilesMatching(Filter filter)
|
Chris@390
|
75 {
|
Chris@390
|
76 vector<string> path = Vamp::PluginHostAdapter::getPluginPath();
|
Chris@390
|
77 vector<string> libraryFiles;
|
Chris@390
|
78
|
Chris@421
|
79 // we match case-insensitively, but only with ascii range
|
Chris@473
|
80 // characters (input strings are expected to be utf-8)
|
Chris@473
|
81 vector<string> libraryNames;
|
Chris@477
|
82 for (int j = 0; j < int(filter.libraryNames.size()); ++j) {
|
Chris@477
|
83 string n = filter.libraryNames[j];
|
Chris@473
|
84 for (size_t i = 0; i < n.length(); ++i) {
|
Chris@473
|
85 if (!(n[i] & 0x80)) {
|
Chris@473
|
86 n[i] = char(tolower(n[i]));
|
Chris@473
|
87 }
|
Chris@421
|
88 }
|
Chris@473
|
89 libraryNames.push_back(n);
|
Chris@390
|
90 }
|
Chris@390
|
91
|
Chris@390
|
92 for (size_t i = 0; i < path.size(); ++i) {
|
Chris@390
|
93
|
Chris@390
|
94 vector<string> files = listFiles(path[i], PLUGIN_SUFFIX);
|
Chris@390
|
95
|
Chris@390
|
96 for (vector<string>::iterator fi = files.begin();
|
Chris@390
|
97 fi != files.end(); ++fi) {
|
Chris@473
|
98
|
Chris@473
|
99 // we match case-insensitively, but only with ascii range
|
Chris@473
|
100 // characters (this string is expected to be utf-8)
|
Chris@473
|
101 string cleaned = *fi;
|
Chris@477
|
102 for (size_t j = 0; j < cleaned.length(); ++j) {
|
Chris@477
|
103 if (!(cleaned[j] & 0x80)) {
|
Chris@477
|
104 cleaned[j] = char(tolower(cleaned[j]));
|
Chris@390
|
105 }
|
Chris@390
|
106 }
|
Chris@390
|
107
|
Chris@473
|
108 // libraryName should be lacking an extension, as it is
|
Chris@473
|
109 // supposed to have come from the plugin key
|
Chris@473
|
110 string::size_type pi = cleaned.find('.');
|
Chris@473
|
111 if (pi != string::npos) {
|
Chris@473
|
112 cleaned = cleaned.substr(0, pi);
|
Chris@473
|
113 }
|
Chris@473
|
114
|
Chris@473
|
115 bool matched = false;
|
Chris@473
|
116
|
Chris@473
|
117 switch (filter.type) {
|
Chris@473
|
118
|
Chris@473
|
119 case Filter::All:
|
Chris@473
|
120 matched = true;
|
Chris@473
|
121 break;
|
Chris@473
|
122
|
Chris@473
|
123 case Filter::Matching:
|
Chris@477
|
124 for (int j = 0; j < int(libraryNames.size()); ++j) {
|
Chris@477
|
125 if (cleaned == libraryNames[j]) {
|
Chris@473
|
126 matched = true;
|
Chris@473
|
127 break;
|
Chris@473
|
128 }
|
Chris@473
|
129 }
|
Chris@473
|
130 break;
|
Chris@473
|
131
|
Chris@473
|
132 case Filter::NotMatching:
|
Chris@473
|
133 matched = true;
|
Chris@477
|
134 for (int j = 0; j < int(libraryNames.size()); ++j) {
|
Chris@477
|
135 if (cleaned == libraryNames[j]) {
|
Chris@473
|
136 matched = false;
|
Chris@473
|
137 break;
|
Chris@473
|
138 }
|
Chris@473
|
139 }
|
Chris@473
|
140 break;
|
Chris@473
|
141 }
|
Chris@473
|
142
|
Chris@473
|
143 if (!matched) continue;
|
Chris@473
|
144
|
Chris@390
|
145 string fullPath = path[i];
|
Chris@390
|
146 fullPath = splicePath(fullPath, *fi);
|
Chris@390
|
147 libraryFiles.push_back(fullPath);
|
Chris@390
|
148 }
|
Chris@390
|
149 }
|
Chris@390
|
150
|
Chris@390
|
151 return libraryFiles;
|
Chris@390
|
152 }
|
Chris@390
|
153
|
Chris@390
|
154 void *
|
Chris@390
|
155 Files::loadLibrary(string path)
|
Chris@390
|
156 {
|
Chris@390
|
157 void *handle = 0;
|
Chris@390
|
158 #ifdef _WIN32
|
Chris@390
|
159 #ifdef UNICODE
|
Chris@472
|
160 int wlen = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), path.length(), 0, 0);
|
Chris@472
|
161 if (wlen < 0) {
|
Chris@390
|
162 cerr << "Vamp::HostExt: Unable to convert library path \""
|
Chris@390
|
163 << path << "\" to wide characters " << endl;
|
Chris@390
|
164 return handle;
|
Chris@390
|
165 }
|
Chris@472
|
166 wchar_t *buffer = new wchar_t[wlen+1];
|
Chris@472
|
167 (void)MultiByteToWideChar(CP_UTF8, 0, path.c_str(), path.length(), buffer, wlen);
|
Chris@472
|
168 buffer[wlen] = L'\0';
|
Chris@390
|
169 handle = LoadLibrary(buffer);
|
Chris@390
|
170 delete[] buffer;
|
Chris@390
|
171 #else
|
Chris@390
|
172 handle = LoadLibrary(path.c_str());
|
Chris@390
|
173 #endif
|
Chris@390
|
174 if (!handle) {
|
Chris@390
|
175 cerr << "Vamp::HostExt: Unable to load library \""
|
Chris@535
|
176 << path << "\": error code " << GetLastError() << endl;
|
Chris@390
|
177 }
|
Chris@390
|
178 #else
|
Chris@390
|
179 handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
Chris@390
|
180 if (!handle) {
|
Chris@390
|
181 cerr << "Vamp::HostExt: Unable to load library \""
|
Chris@390
|
182 << path << "\": " << dlerror() << endl;
|
Chris@390
|
183 }
|
Chris@390
|
184 #endif
|
Chris@390
|
185 return handle;
|
Chris@390
|
186 }
|
Chris@390
|
187
|
Chris@390
|
188 void
|
Chris@390
|
189 Files::unloadLibrary(void *handle)
|
Chris@390
|
190 {
|
Chris@390
|
191 #ifdef _WIN32
|
Chris@390
|
192 FreeLibrary((HINSTANCE)handle);
|
Chris@390
|
193 #else
|
Chris@390
|
194 dlclose(handle);
|
Chris@390
|
195 #endif
|
Chris@390
|
196 }
|
Chris@390
|
197
|
Chris@390
|
198 void *
|
Chris@390
|
199 Files::lookupInLibrary(void *handle, const char *symbol)
|
Chris@390
|
200 {
|
Chris@390
|
201 #ifdef _WIN32
|
Chris@390
|
202 return (void *)GetProcAddress((HINSTANCE)handle, symbol);
|
Chris@390
|
203 #else
|
Chris@390
|
204 return (void *)dlsym(handle, symbol);
|
Chris@390
|
205 #endif
|
Chris@390
|
206 }
|
Chris@390
|
207
|
Chris@390
|
208 string
|
Chris@390
|
209 Files::lcBasename(string path)
|
Chris@390
|
210 {
|
Chris@390
|
211 string basename(path);
|
Chris@390
|
212
|
Chris@390
|
213 string::size_type li = basename.rfind('/');
|
Chris@390
|
214 if (li != string::npos) basename = basename.substr(li + 1);
|
Chris@390
|
215
|
Chris@403
|
216 #ifdef _WIN32
|
Chris@405
|
217 li = basename.rfind('\\');
|
Chris@403
|
218 if (li != string::npos) basename = basename.substr(li + 1);
|
Chris@403
|
219 #endif
|
Chris@403
|
220
|
Chris@390
|
221 li = basename.find('.');
|
Chris@390
|
222 if (li != string::npos) basename = basename.substr(0, li);
|
Chris@390
|
223
|
Chris@421
|
224 // case-insensitive, but only with ascii range characters (this
|
Chris@421
|
225 // string is expected to be utf-8)
|
Chris@390
|
226 for (size_t i = 0; i < basename.length(); ++i) {
|
Chris@421
|
227 if (!(basename[i] & 0x80)) {
|
Chris@421
|
228 basename[i] = char(tolower(basename[i]));
|
Chris@421
|
229 }
|
Chris@390
|
230 }
|
Chris@390
|
231
|
Chris@390
|
232 return basename;
|
Chris@390
|
233 }
|
Chris@390
|
234
|
Chris@390
|
235 string
|
Chris@390
|
236 Files::splicePath(string a, string b)
|
Chris@390
|
237 {
|
Chris@390
|
238 #ifdef _WIN32
|
Chris@390
|
239 return a + "\\" + b;
|
Chris@390
|
240 #else
|
Chris@390
|
241 return a + "/" + b;
|
Chris@390
|
242 #endif
|
Chris@390
|
243 }
|
Chris@390
|
244
|
Chris@390
|
245 vector<string>
|
Chris@390
|
246 Files::listFiles(string dir, string extension)
|
Chris@390
|
247 {
|
Chris@390
|
248 vector<string> files;
|
Chris@390
|
249
|
Chris@390
|
250 #ifdef _WIN32
|
Chris@390
|
251 string expression = dir + "\\*." + extension;
|
Chris@390
|
252 #ifdef UNICODE
|
Chris@472
|
253 int wlen = MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), expression.length(), 0, 0);
|
Chris@472
|
254 if (wlen < 0) {
|
Chris@390
|
255 cerr << "Vamp::HostExt: Unable to convert wildcard path \""
|
Chris@390
|
256 << expression << "\" to wide characters" << endl;
|
Chris@390
|
257 return files;
|
Chris@390
|
258 }
|
Chris@472
|
259 wchar_t *buffer = new wchar_t[wlen+1];
|
Chris@472
|
260 (void)MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), expression.length(), buffer, wlen);
|
Chris@472
|
261 buffer[wlen] = L'\0';
|
Chris@390
|
262 WIN32_FIND_DATA data;
|
Chris@390
|
263 HANDLE fh = FindFirstFile(buffer, &data);
|
Chris@390
|
264 if (fh == INVALID_HANDLE_VALUE) {
|
Chris@390
|
265 delete[] buffer;
|
Chris@390
|
266 return files;
|
Chris@390
|
267 }
|
Chris@390
|
268
|
Chris@390
|
269 bool ok = true;
|
Chris@390
|
270 while (ok) {
|
Chris@390
|
271 wchar_t *fn = data.cFileName;
|
Chris@472
|
272 int wlen = wcslen(fn);
|
Chris@472
|
273 int len = WideCharToMultiByte(CP_UTF8, 0, fn, wlen, 0, 0, 0, 0);
|
Chris@472
|
274 if (len < 0) {
|
Chris@472
|
275 cerr << "Vamp::HostExt: Unable to convert wide char filename to utf-8" << endl;
|
Chris@472
|
276 break;
|
Chris@472
|
277 }
|
Chris@472
|
278 char *conv = new char[len+1];
|
Chris@472
|
279 (void)WideCharToMultiByte(CP_UTF8, 0, fn, wlen, conv, len, 0, 0);
|
Chris@472
|
280 conv[len] = '\0';
|
Chris@472
|
281 if (len > 0) {
|
Chris@390
|
282 files.push_back(conv);
|
Chris@390
|
283 }
|
Chris@390
|
284 delete[] conv;
|
Chris@390
|
285 ok = FindNextFile(fh, &data);
|
Chris@390
|
286 }
|
Chris@390
|
287
|
Chris@390
|
288 FindClose(fh);
|
Chris@390
|
289 delete[] buffer;
|
Chris@390
|
290 #else
|
Chris@390
|
291 WIN32_FIND_DATA data;
|
Chris@390
|
292 HANDLE fh = FindFirstFile(expression.c_str(), &data);
|
Chris@390
|
293 if (fh == INVALID_HANDLE_VALUE) return files;
|
Chris@390
|
294
|
Chris@390
|
295 bool ok = true;
|
Chris@390
|
296 while (ok) {
|
Chris@390
|
297 files.push_back(data.cFileName);
|
Chris@390
|
298 ok = FindNextFile(fh, &data);
|
Chris@390
|
299 }
|
Chris@390
|
300
|
Chris@390
|
301 FindClose(fh);
|
Chris@390
|
302 #endif
|
Chris@390
|
303 #else
|
Chris@390
|
304
|
Chris@390
|
305 size_t extlen = extension.length();
|
Chris@390
|
306 DIR *d = opendir(dir.c_str());
|
Chris@390
|
307 if (!d) return files;
|
Chris@390
|
308
|
Chris@390
|
309 struct dirent *e = 0;
|
Chris@390
|
310 while ((e = readdir(d))) {
|
Chris@390
|
311
|
Chris@390
|
312 size_t len = strlen(e->d_name);
|
Chris@390
|
313 if (len < extlen + 2 ||
|
Chris@390
|
314 e->d_name + len - extlen - 1 != "." + extension) {
|
Chris@390
|
315 continue;
|
Chris@390
|
316 }
|
Chris@390
|
317
|
Chris@390
|
318 files.push_back(e->d_name);
|
Chris@390
|
319 }
|
Chris@390
|
320
|
Chris@390
|
321 closedir(d);
|
Chris@390
|
322 #endif
|
Chris@390
|
323
|
Chris@390
|
324 return files;
|
Chris@390
|
325 }
|
Chris@512
|
326
|
Chris@512
|
327 bool
|
Chris@513
|
328 Files::isNonNative32Bit()
|
Chris@513
|
329 {
|
Chris@513
|
330 // Return true if we are running on a system for which we should
|
Chris@513
|
331 // use the VAMP_PATH_32 variable instead of VAMP_PATH. This will
|
Chris@513
|
332 // be the case if we are a 32-bit executable but the OS is
|
Chris@513
|
333 // natively 64-bit.
|
Chris@513
|
334 //
|
Chris@513
|
335 // This currently works only on Windows; other operating systems
|
Chris@513
|
336 // will use VAMP_PATH always.
|
Chris@513
|
337
|
Chris@513
|
338 if (sizeof(void *) == 8) {
|
Chris@513
|
339 return false;
|
Chris@513
|
340 }
|
Chris@513
|
341
|
Chris@513
|
342 #ifdef _WIN32
|
Chris@513
|
343 BOOL wow64 = FALSE;
|
Chris@513
|
344 BOOL (WINAPI *fnIsWow64Process)(HANDLE, PBOOL) =
|
Chris@513
|
345 (BOOL (WINAPI *)(HANDLE, PBOOL)) GetProcAddress
|
Chris@513
|
346 (GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
|
Chris@513
|
347 if (fnIsWow64Process) {
|
Chris@513
|
348 if (fnIsWow64Process(GetCurrentProcess(), &wow64)) {
|
Chris@513
|
349 if (wow64) {
|
Chris@513
|
350 return true;
|
Chris@513
|
351 } else {
|
Chris@513
|
352 return false;
|
Chris@513
|
353 }
|
Chris@513
|
354 } else {
|
Chris@513
|
355 cerr << "Vamp::HostExt: Unable to query process architecture"
|
Chris@513
|
356 << endl;
|
Chris@513
|
357 return false;
|
Chris@513
|
358 }
|
Chris@513
|
359 } else {
|
Chris@513
|
360 cerr << "Vamp::HostExt: Unable to query process architecture: "
|
Chris@513
|
361 << "Function not available" << endl;
|
Chris@513
|
362 return false;
|
Chris@513
|
363 }
|
Chris@513
|
364 #endif
|
Chris@513
|
365
|
Chris@513
|
366 return false;
|
Chris@513
|
367 }
|
Chris@513
|
368
|
Chris@513
|
369 bool
|
Chris@512
|
370 Files::getEnvUtf8(std::string variable, std::string &value)
|
Chris@512
|
371 {
|
Chris@512
|
372 value = "";
|
Chris@512
|
373
|
Chris@512
|
374 #ifdef _WIN32
|
Chris@512
|
375 int wvarlen = MultiByteToWideChar(CP_UTF8, 0,
|
Chris@512
|
376 variable.c_str(), int(variable.length()),
|
Chris@512
|
377 0, 0);
|
Chris@512
|
378 if (wvarlen < 0) {
|
Chris@512
|
379 cerr << "Vamp::HostExt: Unable to convert environment variable name "
|
Chris@512
|
380 << variable << " to wide characters" << endl;
|
Chris@512
|
381 return false;
|
Chris@512
|
382 }
|
Chris@512
|
383
|
Chris@512
|
384 wchar_t *wvarbuf = new wchar_t[wvarlen + 1];
|
Chris@512
|
385 (void)MultiByteToWideChar(CP_UTF8, 0,
|
Chris@512
|
386 variable.c_str(), int(variable.length()),
|
Chris@512
|
387 wvarbuf, wvarlen);
|
Chris@512
|
388 wvarbuf[wvarlen] = L'\0';
|
Chris@512
|
389
|
Chris@512
|
390 wchar_t *wvalue = _wgetenv(wvarbuf);
|
Chris@512
|
391
|
Chris@512
|
392 delete[] wvarbuf;
|
Chris@512
|
393
|
Chris@512
|
394 if (!wvalue) {
|
Chris@512
|
395 return false;
|
Chris@512
|
396 }
|
Chris@512
|
397
|
Chris@512
|
398 int wvallen = int(wcslen(wvalue));
|
Chris@512
|
399 int vallen = WideCharToMultiByte(CP_UTF8, 0,
|
Chris@512
|
400 wvalue, wvallen,
|
Chris@512
|
401 0, 0, 0, 0);
|
Chris@512
|
402 if (vallen < 0) {
|
Chris@512
|
403 cerr << "Vamp::HostExt: Unable to convert environment value to UTF-8"
|
Chris@512
|
404 << endl;
|
Chris@512
|
405 return false;
|
Chris@512
|
406 }
|
Chris@512
|
407
|
Chris@512
|
408 char *val = new char[vallen + 1];
|
Chris@512
|
409 (void)WideCharToMultiByte(CP_UTF8, 0,
|
Chris@512
|
410 wvalue, wvallen,
|
Chris@512
|
411 val, vallen, 0, 0);
|
Chris@512
|
412 val[vallen] = '\0';
|
Chris@512
|
413
|
Chris@512
|
414 value = val;
|
Chris@512
|
415
|
Chris@512
|
416 delete[] val;
|
Chris@512
|
417 return true;
|
Chris@512
|
418
|
Chris@512
|
419 #else
|
Chris@512
|
420
|
Chris@512
|
421 char *val = getenv(variable.c_str());
|
Chris@512
|
422 if (!val) {
|
Chris@512
|
423 return false;
|
Chris@512
|
424 }
|
Chris@512
|
425
|
Chris@512
|
426 value = val;
|
Chris@512
|
427 return true;
|
Chris@512
|
428
|
Chris@512
|
429 #endif
|
Chris@512
|
430 }
|