Chris@390: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@390: Chris@390: /* Chris@390: Vamp Chris@390: Chris@390: An API for audio analysis and feature extraction plugins. Chris@390: Chris@390: Centre for Digital Music, Queen Mary, University of London. Chris@390: Copyright 2006-2015 Chris Cannam and QMUL. Chris@390: Chris@390: Permission is hereby granted, free of charge, to any person Chris@390: obtaining a copy of this software and associated documentation Chris@390: files (the "Software"), to deal in the Software without Chris@390: restriction, including without limitation the rights to use, copy, Chris@390: modify, merge, publish, distribute, sublicense, and/or sell copies Chris@390: of the Software, and to permit persons to whom the Software is Chris@390: furnished to do so, subject to the following conditions: Chris@390: Chris@390: The above copyright notice and this permission notice shall be Chris@390: included in all copies or substantial portions of the Software. Chris@390: Chris@390: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, Chris@390: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF Chris@390: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND Chris@390: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR Chris@390: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF Chris@390: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION Chris@390: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Chris@390: Chris@390: Except as contained in this notice, the names of the Centre for Chris@390: Digital Music; Queen Mary, University of London; and Chris Cannam Chris@390: shall not be used in advertising or otherwise to promote the sale, Chris@390: use or other dealings in this Software without prior written Chris@390: authorization. Chris@390: */ Chris@390: Chris@390: #include Chris@390: Chris@390: #include "Files.h" Chris@390: Chris@390: #include // tolower Chris@390: Chris@390: #include Chris@390: Chris@390: #ifdef _WIN32 Chris@390: Chris@390: #include Chris@390: #include Chris@390: #define PLUGIN_SUFFIX "dll" Chris@390: Chris@390: #else /* ! _WIN32 */ Chris@390: Chris@390: #include Chris@390: #include Chris@390: Chris@390: #ifdef __APPLE__ Chris@390: #define PLUGIN_SUFFIX "dylib" Chris@390: #else /* ! __APPLE__ */ Chris@390: #define PLUGIN_SUFFIX "so" Chris@390: #endif /* ! __APPLE__ */ Chris@390: Chris@390: #endif /* ! _WIN32 */ Chris@390: Chris@390: using namespace std; Chris@390: Chris@390: vector Chris@390: Files::listLibraryFiles() Chris@390: { Chris@477: return listLibraryFilesMatching(Filter()); Chris@390: } Chris@390: Chris@390: vector Chris@473: Files::listLibraryFilesMatching(Filter filter) Chris@390: { Chris@390: vector path = Vamp::PluginHostAdapter::getPluginPath(); Chris@390: vector libraryFiles; Chris@390: Chris@421: // we match case-insensitively, but only with ascii range Chris@473: // characters (input strings are expected to be utf-8) Chris@473: vector libraryNames; Chris@477: for (int j = 0; j < int(filter.libraryNames.size()); ++j) { Chris@477: string n = filter.libraryNames[j]; Chris@473: for (size_t i = 0; i < n.length(); ++i) { Chris@473: if (!(n[i] & 0x80)) { Chris@473: n[i] = char(tolower(n[i])); Chris@473: } Chris@421: } Chris@473: libraryNames.push_back(n); Chris@390: } Chris@390: Chris@390: for (size_t i = 0; i < path.size(); ++i) { Chris@390: Chris@390: vector files = listFiles(path[i], PLUGIN_SUFFIX); Chris@390: Chris@390: for (vector::iterator fi = files.begin(); Chris@390: fi != files.end(); ++fi) { Chris@473: Chris@473: // we match case-insensitively, but only with ascii range Chris@473: // characters (this string is expected to be utf-8) Chris@473: string cleaned = *fi; Chris@477: for (size_t j = 0; j < cleaned.length(); ++j) { Chris@477: if (!(cleaned[j] & 0x80)) { Chris@477: cleaned[j] = char(tolower(cleaned[j])); Chris@390: } Chris@390: } Chris@390: Chris@473: // libraryName should be lacking an extension, as it is Chris@473: // supposed to have come from the plugin key Chris@473: string::size_type pi = cleaned.find('.'); Chris@473: if (pi != string::npos) { Chris@473: cleaned = cleaned.substr(0, pi); Chris@473: } Chris@473: Chris@473: bool matched = false; Chris@473: Chris@473: switch (filter.type) { Chris@473: Chris@473: case Filter::All: Chris@473: matched = true; Chris@473: break; Chris@473: Chris@473: case Filter::Matching: Chris@477: for (int j = 0; j < int(libraryNames.size()); ++j) { Chris@477: if (cleaned == libraryNames[j]) { Chris@473: matched = true; Chris@473: break; Chris@473: } Chris@473: } Chris@473: break; Chris@473: Chris@473: case Filter::NotMatching: Chris@473: matched = true; Chris@477: for (int j = 0; j < int(libraryNames.size()); ++j) { Chris@477: if (cleaned == libraryNames[j]) { Chris@473: matched = false; Chris@473: break; Chris@473: } Chris@473: } Chris@473: break; Chris@473: } Chris@473: Chris@473: if (!matched) continue; Chris@473: Chris@390: string fullPath = path[i]; Chris@390: fullPath = splicePath(fullPath, *fi); Chris@390: libraryFiles.push_back(fullPath); Chris@390: } Chris@390: } Chris@390: Chris@390: return libraryFiles; Chris@390: } Chris@390: Chris@390: void * Chris@390: Files::loadLibrary(string path) Chris@390: { Chris@390: void *handle = 0; Chris@390: #ifdef _WIN32 Chris@390: #ifdef UNICODE Chris@472: int wlen = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), path.length(), 0, 0); Chris@472: if (wlen < 0) { Chris@390: cerr << "Vamp::HostExt: Unable to convert library path \"" Chris@390: << path << "\" to wide characters " << endl; Chris@390: return handle; Chris@390: } Chris@472: wchar_t *buffer = new wchar_t[wlen+1]; Chris@472: (void)MultiByteToWideChar(CP_UTF8, 0, path.c_str(), path.length(), buffer, wlen); Chris@472: buffer[wlen] = L'\0'; Chris@390: handle = LoadLibrary(buffer); Chris@390: delete[] buffer; Chris@390: #else Chris@390: handle = LoadLibrary(path.c_str()); Chris@390: #endif Chris@390: if (!handle) { Chris@390: cerr << "Vamp::HostExt: Unable to load library \"" Chris@390: << path << "\"" << endl; Chris@390: } Chris@390: #else Chris@390: handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); Chris@390: if (!handle) { Chris@390: cerr << "Vamp::HostExt: Unable to load library \"" Chris@390: << path << "\": " << dlerror() << endl; Chris@390: } Chris@390: #endif Chris@390: return handle; Chris@390: } Chris@390: Chris@390: void Chris@390: Files::unloadLibrary(void *handle) Chris@390: { Chris@390: #ifdef _WIN32 Chris@390: FreeLibrary((HINSTANCE)handle); Chris@390: #else Chris@390: dlclose(handle); Chris@390: #endif Chris@390: } Chris@390: Chris@390: void * Chris@390: Files::lookupInLibrary(void *handle, const char *symbol) Chris@390: { Chris@390: #ifdef _WIN32 Chris@390: return (void *)GetProcAddress((HINSTANCE)handle, symbol); Chris@390: #else Chris@390: return (void *)dlsym(handle, symbol); Chris@390: #endif Chris@390: } Chris@390: Chris@390: string Chris@390: Files::lcBasename(string path) Chris@390: { Chris@390: string basename(path); Chris@390: Chris@390: string::size_type li = basename.rfind('/'); Chris@390: if (li != string::npos) basename = basename.substr(li + 1); Chris@390: Chris@403: #ifdef _WIN32 Chris@405: li = basename.rfind('\\'); Chris@403: if (li != string::npos) basename = basename.substr(li + 1); Chris@403: #endif Chris@403: Chris@390: li = basename.find('.'); Chris@390: if (li != string::npos) basename = basename.substr(0, li); Chris@390: Chris@421: // case-insensitive, but only with ascii range characters (this Chris@421: // string is expected to be utf-8) Chris@390: for (size_t i = 0; i < basename.length(); ++i) { Chris@421: if (!(basename[i] & 0x80)) { Chris@421: basename[i] = char(tolower(basename[i])); Chris@421: } Chris@390: } Chris@390: Chris@390: return basename; Chris@390: } Chris@390: Chris@390: string Chris@390: Files::splicePath(string a, string b) Chris@390: { Chris@390: #ifdef _WIN32 Chris@390: return a + "\\" + b; Chris@390: #else Chris@390: return a + "/" + b; Chris@390: #endif Chris@390: } Chris@390: Chris@390: vector Chris@390: Files::listFiles(string dir, string extension) Chris@390: { Chris@390: vector files; Chris@390: Chris@390: #ifdef _WIN32 Chris@390: string expression = dir + "\\*." + extension; Chris@390: #ifdef UNICODE Chris@472: int wlen = MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), expression.length(), 0, 0); Chris@472: if (wlen < 0) { Chris@390: cerr << "Vamp::HostExt: Unable to convert wildcard path \"" Chris@390: << expression << "\" to wide characters" << endl; Chris@390: return files; Chris@390: } Chris@472: wchar_t *buffer = new wchar_t[wlen+1]; Chris@472: (void)MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), expression.length(), buffer, wlen); Chris@472: buffer[wlen] = L'\0'; Chris@390: WIN32_FIND_DATA data; Chris@390: HANDLE fh = FindFirstFile(buffer, &data); Chris@390: if (fh == INVALID_HANDLE_VALUE) { Chris@390: delete[] buffer; Chris@390: return files; Chris@390: } Chris@390: Chris@390: bool ok = true; Chris@390: while (ok) { Chris@390: wchar_t *fn = data.cFileName; Chris@472: int wlen = wcslen(fn); Chris@472: int len = WideCharToMultiByte(CP_UTF8, 0, fn, wlen, 0, 0, 0, 0); Chris@472: if (len < 0) { Chris@472: cerr << "Vamp::HostExt: Unable to convert wide char filename to utf-8" << endl; Chris@472: break; Chris@472: } Chris@472: char *conv = new char[len+1]; Chris@472: (void)WideCharToMultiByte(CP_UTF8, 0, fn, wlen, conv, len, 0, 0); Chris@472: conv[len] = '\0'; Chris@472: if (len > 0) { Chris@390: files.push_back(conv); Chris@390: } Chris@390: delete[] conv; Chris@390: ok = FindNextFile(fh, &data); Chris@390: } Chris@390: Chris@390: FindClose(fh); Chris@390: delete[] buffer; Chris@390: #else Chris@390: WIN32_FIND_DATA data; Chris@390: HANDLE fh = FindFirstFile(expression.c_str(), &data); Chris@390: if (fh == INVALID_HANDLE_VALUE) return files; Chris@390: Chris@390: bool ok = true; Chris@390: while (ok) { Chris@390: files.push_back(data.cFileName); Chris@390: ok = FindNextFile(fh, &data); Chris@390: } Chris@390: Chris@390: FindClose(fh); Chris@390: #endif Chris@390: #else Chris@390: Chris@390: size_t extlen = extension.length(); Chris@390: DIR *d = opendir(dir.c_str()); Chris@390: if (!d) return files; Chris@390: Chris@390: struct dirent *e = 0; Chris@390: while ((e = readdir(d))) { Chris@390: Chris@390: size_t len = strlen(e->d_name); Chris@390: if (len < extlen + 2 || Chris@390: e->d_name + len - extlen - 1 != "." + extension) { Chris@390: continue; Chris@390: } Chris@390: Chris@390: files.push_back(e->d_name); Chris@390: } Chris@390: Chris@390: closedir(d); Chris@390: #endif Chris@390: Chris@390: return files; Chris@390: }