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