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@473
|
69 return listLibraryFilesMatching({});
|
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@473
|
81 for (auto n: filter.libraryNames) {
|
Chris@473
|
82 for (size_t i = 0; i < n.length(); ++i) {
|
Chris@473
|
83 if (!(n[i] & 0x80)) {
|
Chris@473
|
84 n[i] = char(tolower(n[i]));
|
Chris@473
|
85 }
|
Chris@421
|
86 }
|
Chris@473
|
87 libraryNames.push_back(n);
|
Chris@390
|
88 }
|
Chris@390
|
89
|
Chris@390
|
90 for (size_t i = 0; i < path.size(); ++i) {
|
Chris@390
|
91
|
Chris@390
|
92 vector<string> files = listFiles(path[i], PLUGIN_SUFFIX);
|
Chris@390
|
93
|
Chris@390
|
94 for (vector<string>::iterator fi = files.begin();
|
Chris@390
|
95 fi != files.end(); ++fi) {
|
Chris@473
|
96
|
Chris@473
|
97 // we match case-insensitively, but only with ascii range
|
Chris@473
|
98 // characters (this string is expected to be utf-8)
|
Chris@473
|
99 string cleaned = *fi;
|
Chris@473
|
100 for (size_t i = 0; i < cleaned.length(); ++i) {
|
Chris@473
|
101 if (!(cleaned[i] & 0x80)) {
|
Chris@473
|
102 cleaned[i] = char(tolower(cleaned[i]));
|
Chris@390
|
103 }
|
Chris@390
|
104 }
|
Chris@390
|
105
|
Chris@473
|
106 // libraryName should be lacking an extension, as it is
|
Chris@473
|
107 // supposed to have come from the plugin key
|
Chris@473
|
108 string::size_type pi = cleaned.find('.');
|
Chris@473
|
109 if (pi != string::npos) {
|
Chris@473
|
110 cleaned = cleaned.substr(0, pi);
|
Chris@473
|
111 }
|
Chris@473
|
112
|
Chris@473
|
113 bool matched = false;
|
Chris@473
|
114
|
Chris@473
|
115 switch (filter.type) {
|
Chris@473
|
116
|
Chris@473
|
117 case Filter::All:
|
Chris@473
|
118 matched = true;
|
Chris@473
|
119 break;
|
Chris@473
|
120
|
Chris@473
|
121 case Filter::Matching:
|
Chris@473
|
122 for (const auto &n: libraryNames) {
|
Chris@473
|
123 if (cleaned == n) {
|
Chris@473
|
124 matched = true;
|
Chris@473
|
125 break;
|
Chris@473
|
126 }
|
Chris@473
|
127 }
|
Chris@473
|
128 break;
|
Chris@473
|
129
|
Chris@473
|
130 case Filter::NotMatching:
|
Chris@473
|
131 matched = true;
|
Chris@473
|
132 for (const auto &n: libraryNames) {
|
Chris@473
|
133 if (cleaned == n) {
|
Chris@473
|
134 matched = false;
|
Chris@473
|
135 break;
|
Chris@473
|
136 }
|
Chris@473
|
137 }
|
Chris@473
|
138 break;
|
Chris@473
|
139 }
|
Chris@473
|
140
|
Chris@473
|
141 if (!matched) continue;
|
Chris@473
|
142
|
Chris@390
|
143 string fullPath = path[i];
|
Chris@390
|
144 fullPath = splicePath(fullPath, *fi);
|
Chris@390
|
145 libraryFiles.push_back(fullPath);
|
Chris@390
|
146 }
|
Chris@390
|
147 }
|
Chris@390
|
148
|
Chris@390
|
149 return libraryFiles;
|
Chris@390
|
150 }
|
Chris@390
|
151
|
Chris@390
|
152 void *
|
Chris@390
|
153 Files::loadLibrary(string path)
|
Chris@390
|
154 {
|
Chris@390
|
155 void *handle = 0;
|
Chris@390
|
156 #ifdef _WIN32
|
Chris@390
|
157 #ifdef UNICODE
|
Chris@472
|
158 int wlen = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), path.length(), 0, 0);
|
Chris@472
|
159 if (wlen < 0) {
|
Chris@390
|
160 cerr << "Vamp::HostExt: Unable to convert library path \""
|
Chris@390
|
161 << path << "\" to wide characters " << endl;
|
Chris@390
|
162 return handle;
|
Chris@390
|
163 }
|
Chris@472
|
164 wchar_t *buffer = new wchar_t[wlen+1];
|
Chris@472
|
165 (void)MultiByteToWideChar(CP_UTF8, 0, path.c_str(), path.length(), buffer, wlen);
|
Chris@472
|
166 buffer[wlen] = L'\0';
|
Chris@390
|
167 handle = LoadLibrary(buffer);
|
Chris@390
|
168 delete[] buffer;
|
Chris@390
|
169 #else
|
Chris@390
|
170 handle = LoadLibrary(path.c_str());
|
Chris@390
|
171 #endif
|
Chris@390
|
172 if (!handle) {
|
Chris@390
|
173 cerr << "Vamp::HostExt: Unable to load library \""
|
Chris@390
|
174 << path << "\"" << endl;
|
Chris@390
|
175 }
|
Chris@390
|
176 #else
|
Chris@390
|
177 handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
Chris@390
|
178 if (!handle) {
|
Chris@390
|
179 cerr << "Vamp::HostExt: Unable to load library \""
|
Chris@390
|
180 << path << "\": " << dlerror() << endl;
|
Chris@390
|
181 }
|
Chris@390
|
182 #endif
|
Chris@390
|
183 return handle;
|
Chris@390
|
184 }
|
Chris@390
|
185
|
Chris@390
|
186 void
|
Chris@390
|
187 Files::unloadLibrary(void *handle)
|
Chris@390
|
188 {
|
Chris@390
|
189 #ifdef _WIN32
|
Chris@390
|
190 FreeLibrary((HINSTANCE)handle);
|
Chris@390
|
191 #else
|
Chris@390
|
192 dlclose(handle);
|
Chris@390
|
193 #endif
|
Chris@390
|
194 }
|
Chris@390
|
195
|
Chris@390
|
196 void *
|
Chris@390
|
197 Files::lookupInLibrary(void *handle, const char *symbol)
|
Chris@390
|
198 {
|
Chris@390
|
199 #ifdef _WIN32
|
Chris@390
|
200 return (void *)GetProcAddress((HINSTANCE)handle, symbol);
|
Chris@390
|
201 #else
|
Chris@390
|
202 return (void *)dlsym(handle, symbol);
|
Chris@390
|
203 #endif
|
Chris@390
|
204 }
|
Chris@390
|
205
|
Chris@390
|
206 string
|
Chris@390
|
207 Files::lcBasename(string path)
|
Chris@390
|
208 {
|
Chris@390
|
209 string basename(path);
|
Chris@390
|
210
|
Chris@390
|
211 string::size_type li = basename.rfind('/');
|
Chris@390
|
212 if (li != string::npos) basename = basename.substr(li + 1);
|
Chris@390
|
213
|
Chris@403
|
214 #ifdef _WIN32
|
Chris@405
|
215 li = basename.rfind('\\');
|
Chris@403
|
216 if (li != string::npos) basename = basename.substr(li + 1);
|
Chris@403
|
217 #endif
|
Chris@403
|
218
|
Chris@390
|
219 li = basename.find('.');
|
Chris@390
|
220 if (li != string::npos) basename = basename.substr(0, li);
|
Chris@390
|
221
|
Chris@421
|
222 // case-insensitive, but only with ascii range characters (this
|
Chris@421
|
223 // string is expected to be utf-8)
|
Chris@390
|
224 for (size_t i = 0; i < basename.length(); ++i) {
|
Chris@421
|
225 if (!(basename[i] & 0x80)) {
|
Chris@421
|
226 basename[i] = char(tolower(basename[i]));
|
Chris@421
|
227 }
|
Chris@390
|
228 }
|
Chris@390
|
229
|
Chris@390
|
230 return basename;
|
Chris@390
|
231 }
|
Chris@390
|
232
|
Chris@390
|
233 string
|
Chris@390
|
234 Files::splicePath(string a, string b)
|
Chris@390
|
235 {
|
Chris@390
|
236 #ifdef _WIN32
|
Chris@390
|
237 return a + "\\" + b;
|
Chris@390
|
238 #else
|
Chris@390
|
239 return a + "/" + b;
|
Chris@390
|
240 #endif
|
Chris@390
|
241 }
|
Chris@390
|
242
|
Chris@390
|
243 vector<string>
|
Chris@390
|
244 Files::listFiles(string dir, string extension)
|
Chris@390
|
245 {
|
Chris@390
|
246 vector<string> files;
|
Chris@390
|
247
|
Chris@390
|
248 #ifdef _WIN32
|
Chris@390
|
249 string expression = dir + "\\*." + extension;
|
Chris@390
|
250 #ifdef UNICODE
|
Chris@472
|
251 int wlen = MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), expression.length(), 0, 0);
|
Chris@472
|
252 if (wlen < 0) {
|
Chris@390
|
253 cerr << "Vamp::HostExt: Unable to convert wildcard path \""
|
Chris@390
|
254 << expression << "\" to wide characters" << endl;
|
Chris@390
|
255 return files;
|
Chris@390
|
256 }
|
Chris@472
|
257 wchar_t *buffer = new wchar_t[wlen+1];
|
Chris@472
|
258 (void)MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), expression.length(), buffer, wlen);
|
Chris@472
|
259 buffer[wlen] = L'\0';
|
Chris@390
|
260 WIN32_FIND_DATA data;
|
Chris@390
|
261 HANDLE fh = FindFirstFile(buffer, &data);
|
Chris@390
|
262 if (fh == INVALID_HANDLE_VALUE) {
|
Chris@390
|
263 delete[] buffer;
|
Chris@390
|
264 return files;
|
Chris@390
|
265 }
|
Chris@390
|
266
|
Chris@390
|
267 bool ok = true;
|
Chris@390
|
268 while (ok) {
|
Chris@390
|
269 wchar_t *fn = data.cFileName;
|
Chris@472
|
270 int wlen = wcslen(fn);
|
Chris@472
|
271 int len = WideCharToMultiByte(CP_UTF8, 0, fn, wlen, 0, 0, 0, 0);
|
Chris@472
|
272 if (len < 0) {
|
Chris@472
|
273 cerr << "Vamp::HostExt: Unable to convert wide char filename to utf-8" << endl;
|
Chris@472
|
274 break;
|
Chris@472
|
275 }
|
Chris@472
|
276 char *conv = new char[len+1];
|
Chris@472
|
277 (void)WideCharToMultiByte(CP_UTF8, 0, fn, wlen, conv, len, 0, 0);
|
Chris@472
|
278 conv[len] = '\0';
|
Chris@472
|
279 if (len > 0) {
|
Chris@390
|
280 files.push_back(conv);
|
Chris@390
|
281 }
|
Chris@390
|
282 delete[] conv;
|
Chris@390
|
283 ok = FindNextFile(fh, &data);
|
Chris@390
|
284 }
|
Chris@390
|
285
|
Chris@390
|
286 FindClose(fh);
|
Chris@390
|
287 delete[] buffer;
|
Chris@390
|
288 #else
|
Chris@390
|
289 WIN32_FIND_DATA data;
|
Chris@390
|
290 HANDLE fh = FindFirstFile(expression.c_str(), &data);
|
Chris@390
|
291 if (fh == INVALID_HANDLE_VALUE) return files;
|
Chris@390
|
292
|
Chris@390
|
293 bool ok = true;
|
Chris@390
|
294 while (ok) {
|
Chris@390
|
295 files.push_back(data.cFileName);
|
Chris@390
|
296 ok = FindNextFile(fh, &data);
|
Chris@390
|
297 }
|
Chris@390
|
298
|
Chris@390
|
299 FindClose(fh);
|
Chris@390
|
300 #endif
|
Chris@390
|
301 #else
|
Chris@390
|
302
|
Chris@390
|
303 size_t extlen = extension.length();
|
Chris@390
|
304 DIR *d = opendir(dir.c_str());
|
Chris@390
|
305 if (!d) return files;
|
Chris@390
|
306
|
Chris@390
|
307 struct dirent *e = 0;
|
Chris@390
|
308 while ((e = readdir(d))) {
|
Chris@390
|
309
|
Chris@390
|
310 size_t len = strlen(e->d_name);
|
Chris@390
|
311 if (len < extlen + 2 ||
|
Chris@390
|
312 e->d_name + len - extlen - 1 != "." + extension) {
|
Chris@390
|
313 continue;
|
Chris@390
|
314 }
|
Chris@390
|
315
|
Chris@390
|
316 files.push_back(e->d_name);
|
Chris@390
|
317 }
|
Chris@390
|
318
|
Chris@390
|
319 closedir(d);
|
Chris@390
|
320 #endif
|
Chris@390
|
321
|
Chris@390
|
322 return files;
|
Chris@390
|
323 }
|