changeset 395:96cdf661d538

Merge from branch vh
author Chris Cannam
date Tue, 16 Jun 2015 10:12:36 +0100
parents a69901aa85d2 (current diff) 632c662e95e7 (diff)
children 0d9d6a7d6ca8
files build/Makefile.osx
diffstat 10 files changed, 737 insertions(+), 252 deletions(-) [+]
line wrap: on
line diff
--- a/build/Makefile.mingw32	Tue Jun 02 14:18:27 2015 +0100
+++ b/build/Makefile.mingw32	Tue Jun 16 10:12:36 2015 +0100
@@ -142,6 +142,7 @@
 		$(SDKSRCDIR)/acsymbols.o
 
 HOSTSDK_OBJECTS	= \
+		$(HOSTSDKSRCDIR)/Files.o \
 		$(HOSTSDKSRCDIR)/PluginHostAdapter.o \
 		$(HOSTSDKSRCDIR)/RealTime.o \
 		$(HOSTSDKSRCDIR)/PluginBufferingAdapter.o \
@@ -150,6 +151,7 @@
 		$(HOSTSDKSRCDIR)/PluginLoader.o \
 		$(HOSTSDKSRCDIR)/PluginSummarisingAdapter.o \
 		$(HOSTSDKSRCDIR)/PluginWrapper.o \
+		$(HOSTSDKSRCDIR)/host-c.o \
 		$(HOSTSDKSRCDIR)/acsymbols.o
 
 SDK_STATIC	= \
--- a/build/Makefile.mingw64	Tue Jun 02 14:18:27 2015 +0100
+++ b/build/Makefile.mingw64	Tue Jun 16 10:12:36 2015 +0100
@@ -142,6 +142,7 @@
 		$(SDKSRCDIR)/acsymbols.o
 
 HOSTSDK_OBJECTS	= \
+		$(HOSTSDKSRCDIR)/Files.o \
 		$(HOSTSDKSRCDIR)/PluginHostAdapter.o \
 		$(HOSTSDKSRCDIR)/RealTime.o \
 		$(HOSTSDKSRCDIR)/PluginBufferingAdapter.o \
@@ -150,6 +151,7 @@
 		$(HOSTSDKSRCDIR)/PluginLoader.o \
 		$(HOSTSDKSRCDIR)/PluginSummarisingAdapter.o \
 		$(HOSTSDKSRCDIR)/PluginWrapper.o \
+		$(HOSTSDKSRCDIR)/host-c.o \
 		$(HOSTSDKSRCDIR)/acsymbols.o
 
 SDK_STATIC	= \
--- a/build/Makefile.osx	Tue Jun 02 14:18:27 2015 +0100
+++ b/build/Makefile.osx	Tue Jun 16 10:12:36 2015 +0100
@@ -150,6 +150,7 @@
 		$(SDKSRCDIR)/acsymbols.o 
 
 HOSTSDK_OBJECTS	= \
+		$(HOSTSDKSRCDIR)/Files.o \
 		$(HOSTSDKSRCDIR)/PluginHostAdapter.o \
 		$(HOSTSDKSRCDIR)/RealTime.o \
 		$(HOSTSDKSRCDIR)/PluginBufferingAdapter.o \
@@ -158,6 +159,7 @@
 		$(HOSTSDKSRCDIR)/PluginLoader.o \
 		$(HOSTSDKSRCDIR)/PluginSummarisingAdapter.o \
 		$(HOSTSDKSRCDIR)/PluginWrapper.o \
+		$(HOSTSDKSRCDIR)/host-c.o \
 		$(HOSTSDKSRCDIR)/acsymbols.o 
 
 SDK_STATIC	= \
--- a/build/Makefile.osx.106	Tue Jun 02 14:18:27 2015 +0100
+++ b/build/Makefile.osx.106	Tue Jun 16 10:12:36 2015 +0100
@@ -171,6 +171,7 @@
 		$(SDKSRCDIR)/acsymbols.o
 
 HOSTSDK_OBJECTS	= \
+		$(HOSTSDKSRCDIR)/Files.o \
 		$(HOSTSDKSRCDIR)/PluginHostAdapter.o \
 		$(HOSTSDKSRCDIR)/RealTime.o \
 		$(HOSTSDKSRCDIR)/PluginBufferingAdapter.o \
@@ -179,6 +180,7 @@
 		$(HOSTSDKSRCDIR)/PluginLoader.o \
 		$(HOSTSDKSRCDIR)/PluginSummarisingAdapter.o \
 		$(HOSTSDKSRCDIR)/PluginWrapper.o \
+		$(HOSTSDKSRCDIR)/host-c.o \
 		$(HOSTSDKSRCDIR)/acsymbols.o
 
 SDK_STATIC	= \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/test-c.c	Tue Jun 16 10:12:36 2015 +0100
@@ -0,0 +1,40 @@
+
+#include <vamp-hostsdk/host-c.h>
+
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+    int i;
+    int libcount = vhGetLibraryCount();
+
+    printf("Vamp plugin libraries found:\n");
+    for (i = 0; i < libcount; ++i) {
+	printf("%d: %s\n", i, vhGetLibraryName(i));
+    }
+
+    printf("Going to try loading qm-vamp-plugins...\n");
+    int libindex = vhGetLibraryIndex("qm-vamp-plugins");
+    vhLibrary lib = vhLoadLibrary(libindex);
+    if (!lib) {
+	printf("Failure!\n");
+	return 1;
+    }
+
+    int plugincount = vhGetPluginCount(lib);
+    printf("Success: it contains %d plugins; they are:\n", plugincount);
+
+    for (i = 0; i < plugincount; ++i) {
+	const VampPluginDescriptor *descriptor = vhGetPluginDescriptor(lib, i);
+	if (!descriptor) {
+	    printf("<unknown! failed to load>\n");
+	} else {
+	    printf("%s\n", descriptor->identifier);
+	}
+    }
+
+    vhUnloadLibrary(lib);
+    
+    return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/vamp-hostsdk/Files.cpp	Tue Jun 16 10:12:36 2015 +0100
@@ -0,0 +1,274 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vamp
+
+    An API for audio analysis and feature extraction plugins.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006-2015 Chris Cannam and QMUL.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#include <vamp-hostsdk/PluginHostAdapter.h>
+
+#include "Files.h"
+
+#include <cctype> // tolower
+
+#include <cstring>
+
+#ifdef _WIN32
+
+#include <windows.h>
+#include <tchar.h>
+#define PLUGIN_SUFFIX "dll"
+
+#else /* ! _WIN32 */
+
+#include <dirent.h>
+#include <dlfcn.h>
+
+#ifdef __APPLE__
+#define PLUGIN_SUFFIX "dylib"
+#else /* ! __APPLE__ */
+#define PLUGIN_SUFFIX "so"
+#endif /* ! __APPLE__ */
+
+#endif /* ! _WIN32 */
+
+using namespace std;
+
+vector<string>
+Files::listLibraryFiles()
+{
+    return listLibraryFilesMatching("");
+}
+
+vector<string>
+Files::listLibraryFilesMatching(string libraryName)
+{
+    vector<string> path = Vamp::PluginHostAdapter::getPluginPath();
+    vector<string> libraryFiles;
+
+    // we match case-insensitively
+    for (size_t i = 0; i < libraryName.length(); ++i) {
+	libraryName[i] = tolower(libraryName[i]);
+    }
+
+    for (size_t i = 0; i < path.size(); ++i) {
+        
+        vector<string> files = listFiles(path[i], PLUGIN_SUFFIX);
+
+        for (vector<string>::iterator fi = files.begin();
+             fi != files.end(); ++fi) {
+            
+            if (libraryName != "") {
+		// we match case-insensitively
+                string temp = *fi;
+                for (size_t i = 0; i < temp.length(); ++i) {
+                    temp[i] = tolower(temp[i]);
+                }
+                // libraryName should be lacking an extension, as it
+                // is supposed to have come from the plugin key
+                string::size_type pi = temp.find('.');
+                if (pi == string::npos) {
+                    if (libraryName != temp) continue;
+                } else {
+                    if (libraryName != temp.substr(0, pi)) continue;
+                }
+            }
+
+            string fullPath = path[i];
+            fullPath = splicePath(fullPath, *fi);
+	    libraryFiles.push_back(fullPath);
+	}
+    }
+
+    return libraryFiles;
+}
+
+void *
+Files::loadLibrary(string path)
+{
+    void *handle = 0;
+#ifdef _WIN32
+#ifdef UNICODE
+    int len = path.length() + 1; // cannot be more wchars than length in bytes of utf8 string
+    wchar_t *buffer = new wchar_t[len];
+    int rv = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), len, buffer, len);
+    if (rv <= 0) {
+        cerr << "Vamp::HostExt: Unable to convert library path \""
+             << path << "\" to wide characters " << endl;
+        delete[] buffer;
+        return handle;
+    }
+    handle = LoadLibrary(buffer);
+    delete[] buffer;
+#else
+    handle = LoadLibrary(path.c_str());
+#endif
+    if (!handle) {
+        cerr << "Vamp::HostExt: Unable to load library \""
+             << path << "\"" << endl;
+    }
+#else
+    handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
+    if (!handle) {
+        cerr << "Vamp::HostExt: Unable to load library \""
+             << path << "\": " << dlerror() << endl;
+    }
+#endif
+    return handle;
+}
+
+void
+Files::unloadLibrary(void *handle)
+{
+#ifdef _WIN32
+    FreeLibrary((HINSTANCE)handle);
+#else
+    dlclose(handle);
+#endif
+}
+
+void *
+Files::lookupInLibrary(void *handle, const char *symbol)
+{
+#ifdef _WIN32
+    return (void *)GetProcAddress((HINSTANCE)handle, symbol);
+#else
+    return (void *)dlsym(handle, symbol);
+#endif
+}
+
+string
+Files::lcBasename(string path)
+{
+    string basename(path);
+	
+    string::size_type li = basename.rfind('/');
+    if (li != string::npos) basename = basename.substr(li + 1);
+
+    li = basename.find('.');
+    if (li != string::npos) basename = basename.substr(0, li);
+
+    for (size_t i = 0; i < basename.length(); ++i) {
+        basename[i] = tolower(basename[i]);
+    }
+
+    return basename;
+}
+
+string
+Files::splicePath(string a, string b)
+{
+#ifdef _WIN32
+    return a + "\\" + b;
+#else
+    return a + "/" + b;
+#endif
+}
+
+vector<string>
+Files::listFiles(string dir, string extension)
+{
+    vector<string> files;
+
+#ifdef _WIN32
+    string expression = dir + "\\*." + extension;
+#ifdef UNICODE
+    int len = expression.length() + 1; // cannot be more wchars than length in bytes of utf8 string
+    wchar_t *buffer = new wchar_t[len];
+    int rv = MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), len, buffer, len);
+    if (rv <= 0) {
+        cerr << "Vamp::HostExt: Unable to convert wildcard path \""
+             << expression << "\" to wide characters" << endl;
+        delete[] buffer;
+        return files;
+    }
+    WIN32_FIND_DATA data;
+    HANDLE fh = FindFirstFile(buffer, &data);
+    if (fh == INVALID_HANDLE_VALUE) {
+        delete[] buffer;
+        return files;
+    }
+
+    bool ok = true;
+    while (ok) {
+        wchar_t *fn = data.cFileName;
+        int wlen = wcslen(fn) + 1;
+        int maxlen = wlen * 6;
+        char *conv = new char[maxlen];
+        int rv = WideCharToMultiByte(CP_UTF8, 0, fn, wlen, conv, maxlen, 0, 0);
+        if (rv > 0) {
+            files.push_back(conv);
+        }
+        delete[] conv;
+        ok = FindNextFile(fh, &data);
+    }
+
+    FindClose(fh);
+    delete[] buffer;
+#else
+    WIN32_FIND_DATA data;
+    HANDLE fh = FindFirstFile(expression.c_str(), &data);
+    if (fh == INVALID_HANDLE_VALUE) return files;
+
+    bool ok = true;
+    while (ok) {
+        files.push_back(data.cFileName);
+        ok = FindNextFile(fh, &data);
+    }
+
+    FindClose(fh);
+#endif
+#else
+
+    size_t extlen = extension.length();
+    DIR *d = opendir(dir.c_str());
+    if (!d) return files;
+            
+    struct dirent *e = 0;
+    while ((e = readdir(d))) {
+ 
+        if (!e->d_name) continue;
+       
+        size_t len = strlen(e->d_name);
+        if (len < extlen + 2 ||
+            e->d_name + len - extlen - 1 != "." + extension) {
+            continue;
+        }
+
+        files.push_back(e->d_name);
+    }
+
+    closedir(d);
+#endif
+
+    return files;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/vamp-hostsdk/Files.h	Tue Jun 16 10:12:36 2015 +0100
@@ -0,0 +1,63 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vamp
+
+    An API for audio analysis and feature extraction plugins.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006-2015 Chris Cannam and QMUL.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#ifndef VAMP_FILES_H
+#define VAMP_FILES_H
+
+#include <vector>
+#include <string>
+
+/**
+ * This is a private implementation class for the Vamp Host SDK.
+ */
+class Files
+{
+public:
+    static std::vector<std::string> listLibraryFiles();
+    static std::vector<std::string> listLibraryFilesMatching(std::string libname);
+
+    static void *loadLibrary(std::string filename);
+    static void unloadLibrary(void *);
+    static void *lookupInLibrary(void *, const char *symbol);
+
+    static std::string lcBasename(std::string path);
+    static std::string splicePath(std::string a, std::string b);
+    static std::vector<std::string> listFiles(std::string dir, std::string ext);
+};
+
+#endif
+
+    
--- a/src/vamp-hostsdk/PluginLoader.cpp	Tue Jun 02 14:18:27 2015 +0100
+++ b/src/vamp-hostsdk/PluginLoader.cpp	Tue Jun 16 10:12:36 2015 +0100
@@ -34,35 +34,17 @@
     authorization.
 */
 
-#include <vamp-hostsdk/PluginHostAdapter.h>
 #include <vamp-hostsdk/PluginLoader.h>
 #include <vamp-hostsdk/PluginInputDomainAdapter.h>
 #include <vamp-hostsdk/PluginChannelAdapter.h>
 #include <vamp-hostsdk/PluginBufferingAdapter.h>
+#include <vamp-hostsdk/PluginHostAdapter.h>
+
+#include <vamp/vamp.h>
+
+#include "Files.h"
 
 #include <fstream>
-#include <cctype> // tolower
-
-#include <cstring>
-
-#ifdef _WIN32
-
-#include <windows.h>
-#include <tchar.h>
-#define PLUGIN_SUFFIX "dll"
-
-#else /* ! _WIN32 */
-
-#include <dirent.h>
-#include <dlfcn.h>
-
-#ifdef __APPLE__
-#define PLUGIN_SUFFIX "dylib"
-#else /* ! __APPLE__ */
-#define PLUGIN_SUFFIX "so"
-#endif /* ! __APPLE__ */
-
-#endif /* ! _WIN32 */
 
 using namespace std;
 
@@ -124,13 +106,6 @@
     bool decomposePluginKey(PluginKey key,
                             string &libraryName, string &identifier);
 
-    void *loadLibrary(string path);
-    void unloadLibrary(void *handle);
-    void *lookupInLibrary(void *handle, const char *symbol);
-
-    string splicePath(string a, string b);
-    vector<string> listFiles(string dir, string ext);
-    
     static InstanceCleaner m_cleaner;
 };
 
@@ -227,83 +202,64 @@
 void
 PluginLoader::Impl::enumeratePlugins(PluginKey forPlugin)
 {
-    vector<string> path = PluginHostAdapter::getPluginPath();
-
     string libraryName, identifier;
+    vector<string> fullPaths;
+    
     if (forPlugin != "") {
         if (!decomposePluginKey(forPlugin, libraryName, identifier)) {
             std::cerr << "WARNING: Vamp::HostExt::PluginLoader: Invalid plugin key \""
                       << forPlugin << "\" in enumerate" << std::endl;
             return;
         }
+        fullPaths = Files::listLibraryFilesMatching(libraryName);
+    } else {
+        fullPaths = Files::listLibraryFiles();
     }
 
-    for (size_t i = 0; i < path.size(); ++i) {
-        
-        vector<string> files = listFiles(path[i], PLUGIN_SUFFIX);
+    for (size_t i = 0; i < fullPaths.size(); ++i) {
 
-        for (vector<string>::iterator fi = files.begin();
-             fi != files.end(); ++fi) {
+        string fullPath = fullPaths[i];
+        void *handle = Files::loadLibrary(fullPath);
+        if (!handle) continue;
             
-            if (libraryName != "") {
-                // libraryName is lowercased and lacking an extension,
-                // as it came from the plugin key
-                string temp = *fi;
-                for (size_t i = 0; i < temp.length(); ++i) {
-                    temp[i] = tolower(temp[i]);
-                }
-                string::size_type pi = temp.find('.');
-                if (pi == string::npos) {
-                    if (libraryName != temp) continue;
-                } else {
-                    if (libraryName != temp.substr(0, pi)) continue;
-                }
-            }
-
-            string fullPath = path[i];
-            fullPath = splicePath(fullPath, *fi);
-            void *handle = loadLibrary(fullPath);
-            if (!handle) continue;
+        VampGetPluginDescriptorFunction fn =
+            (VampGetPluginDescriptorFunction)Files::lookupInLibrary
+            (handle, "vampGetPluginDescriptor");
             
-            VampGetPluginDescriptorFunction fn =
-                (VampGetPluginDescriptorFunction)lookupInLibrary
-                (handle, "vampGetPluginDescriptor");
-            
-            if (!fn) {
-                if (forPlugin != "") {
-                    cerr << "Vamp::HostExt::PluginLoader: No vampGetPluginDescriptor function found in library \""
-                         << fullPath << "\"" << endl;
-                }
-                unloadLibrary(handle);
-                continue;
-            }
-            
-            int index = 0;
-            const VampPluginDescriptor *descriptor = 0;
-            bool found = false;
-            
-            while ((descriptor = fn(VAMP_API_VERSION, index))) {
-                ++index;
-                if (identifier != "") {
-                    if (descriptor->identifier != identifier) continue;
-                }
-                found = true;
-                PluginKey key = composePluginKey(*fi, descriptor->identifier);
-//                std::cerr << "enumerate: " << key << " (path: " << fullPath << ")" << std::endl;
-                if (m_pluginLibraryNameMap.find(key) ==
-                    m_pluginLibraryNameMap.end()) {
-                    m_pluginLibraryNameMap[key] = fullPath;
-                }
-            }
-
-            if (!found && forPlugin != "") {
-                cerr << "Vamp::HostExt::PluginLoader: Plugin \""
-                     << identifier << "\" not found in library \""
+        if (!fn) {
+            if (forPlugin != "") {
+                cerr << "Vamp::HostExt::PluginLoader: No vampGetPluginDescriptor function found in library \""
                      << fullPath << "\"" << endl;
             }
+            Files::unloadLibrary(handle);
+            continue;
+        }
             
-            unloadLibrary(handle);
+        int index = 0;
+        const VampPluginDescriptor *descriptor = 0;
+        bool found = false;
+            
+        while ((descriptor = fn(VAMP_API_VERSION, index))) {
+            ++index;
+            if (identifier != "") {
+                if (descriptor->identifier != identifier) continue;
+            }
+            found = true;
+            PluginKey key = composePluginKey(fullPath, descriptor->identifier);
+//                std::cerr << "enumerate: " << key << " (path: " << fullPath << ")" << std::endl;
+            if (m_pluginLibraryNameMap.find(key) ==
+                m_pluginLibraryNameMap.end()) {
+                m_pluginLibraryNameMap[key] = fullPath;
+            }
         }
+
+        if (!found && forPlugin != "") {
+            cerr << "Vamp::HostExt::PluginLoader: Plugin \""
+                 << identifier << "\" not found in library \""
+                 << fullPath << "\"" << endl;
+        }
+            
+        Files::unloadLibrary(handle);
     }
 
     if (forPlugin == "") m_allPluginsEnumerated = true;
@@ -312,18 +268,7 @@
 PluginLoader::PluginKey
 PluginLoader::Impl::composePluginKey(string libraryName, string identifier)
 {
-    string basename = libraryName;
-
-    string::size_type li = basename.rfind('/');
-    if (li != string::npos) basename = basename.substr(li + 1);
-
-    li = basename.find('.');
-    if (li != string::npos) basename = basename.substr(0, li);
-
-    for (size_t i = 0; i < basename.length(); ++i) {
-        basename[i] = tolower(basename[i]);
-    }
-
+    string basename = Files::lcBasename(libraryName);
     return basename + ":" + identifier;
 }
 
@@ -382,17 +327,17 @@
         return 0;
     }
     
-    void *handle = loadLibrary(fullPath);
+    void *handle = Files::loadLibrary(fullPath);
     if (!handle) return 0;
     
     VampGetPluginDescriptorFunction fn =
-        (VampGetPluginDescriptorFunction)lookupInLibrary
+        (VampGetPluginDescriptorFunction)Files::lookupInLibrary
         (handle, "vampGetPluginDescriptor");
 
     if (!fn) {
         cerr << "Vamp::HostExt::PluginLoader: No vampGetPluginDescriptor function found in library \""
              << fullPath << "\"" << endl;
-        unloadLibrary(handle);
+        Files::unloadLibrary(handle);
         return 0;
     }
 
@@ -474,12 +419,12 @@
     for (vector<string>::iterator i = catpath.begin();
          i != catpath.end(); ++i) {
         
-        vector<string> files = listFiles(*i, suffix);
+        vector<string> files = Files::listFiles(*i, suffix);
 
         for (vector<string>::iterator fi = files.begin();
              fi != files.end(); ++fi) {
 
-            string filepath = splicePath(*i, *fi);
+            string filepath = Files::splicePath(*i, *fi);
             ifstream is(filepath.c_str(), ifstream::in | ifstream::binary);
 
             if (is.fail()) {
@@ -525,154 +470,11 @@
     }
 }    
 
-void *
-PluginLoader::Impl::loadLibrary(string path)
-{
-    void *handle = 0;
-#ifdef _WIN32
-#ifdef UNICODE
-    int len = path.length() + 1; // cannot be more wchars than length in bytes of utf8 string
-    wchar_t *buffer = new wchar_t[len];
-    int rv = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), len, buffer, len);
-    if (rv <= 0) {
-        cerr << "Vamp::HostExt::PluginLoader: Unable to convert library path \""
-             << path << "\" to wide characters " << endl;
-        delete[] buffer;
-        return handle;
-    }
-    handle = LoadLibrary(buffer);
-    delete[] buffer;
-#else
-    handle = LoadLibrary(path.c_str());
-#endif
-    if (!handle) {
-        cerr << "Vamp::HostExt::PluginLoader: Unable to load library \""
-             << path << "\"" << endl;
-    }
-#else
-    handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
-    if (!handle) {
-        cerr << "Vamp::HostExt::PluginLoader: Unable to load library \""
-             << path << "\": " << dlerror() << endl;
-    }
-#endif
-    return handle;
-}
-
-void
-PluginLoader::Impl::unloadLibrary(void *handle)
-{
-#ifdef _WIN32
-    FreeLibrary((HINSTANCE)handle);
-#else
-    dlclose(handle);
-#endif
-}
-
-void *
-PluginLoader::Impl::lookupInLibrary(void *handle, const char *symbol)
-{
-#ifdef _WIN32
-    return (void *)GetProcAddress((HINSTANCE)handle, symbol);
-#else
-    return (void *)dlsym(handle, symbol);
-#endif
-}
-
-string
-PluginLoader::Impl::splicePath(string a, string b)
-{
-#ifdef _WIN32
-    return a + "\\" + b;
-#else
-    return a + "/" + b;
-#endif
-}
-
-vector<string>
-PluginLoader::Impl::listFiles(string dir, string extension)
-{
-    vector<string> files;
-
-#ifdef _WIN32
-    string expression = dir + "\\*." + extension;
-#ifdef UNICODE
-    int len = expression.length() + 1; // cannot be more wchars than length in bytes of utf8 string
-    wchar_t *buffer = new wchar_t[len];
-    int rv = MultiByteToWideChar(CP_UTF8, 0, expression.c_str(), len, buffer, len);
-    if (rv <= 0) {
-        cerr << "Vamp::HostExt::PluginLoader: Unable to convert wildcard path \""
-             << expression << "\" to wide characters" << endl;
-        delete[] buffer;
-        return files;
-    }
-    WIN32_FIND_DATA data;
-    HANDLE fh = FindFirstFile(buffer, &data);
-    if (fh == INVALID_HANDLE_VALUE) {
-        delete[] buffer;
-        return files;
-    }
-
-    bool ok = true;
-    while (ok) {
-        wchar_t *fn = data.cFileName;
-        int wlen = wcslen(fn) + 1;
-        int maxlen = wlen * 6;
-        char *conv = new char[maxlen];
-        int rv = WideCharToMultiByte(CP_UTF8, 0, fn, wlen, conv, maxlen, 0, 0);
-        if (rv > 0) {
-            files.push_back(conv);
-        }
-        delete[] conv;
-        ok = FindNextFile(fh, &data);
-    }
-
-    FindClose(fh);
-    delete[] buffer;
-#else
-    WIN32_FIND_DATA data;
-    HANDLE fh = FindFirstFile(expression.c_str(), &data);
-    if (fh == INVALID_HANDLE_VALUE) return files;
-
-    bool ok = true;
-    while (ok) {
-        files.push_back(data.cFileName);
-        ok = FindNextFile(fh, &data);
-    }
-
-    FindClose(fh);
-#endif
-#else
-
-    size_t extlen = extension.length();
-    DIR *d = opendir(dir.c_str());
-    if (!d) return files;
-            
-    struct dirent *e = 0;
-    while ((e = readdir(d))) {
- 
-        if (!e->d_name) continue;
-       
-        size_t len = strlen(e->d_name);
-        if (len < extlen + 2 ||
-            e->d_name + len - extlen - 1 != "." + extension) {
-            continue;
-        }
-
-        files.push_back(e->d_name);
-    }
-
-    closedir(d);
-#endif
-
-    return files;
-}
-
 void
 PluginLoader::Impl::pluginDeleted(PluginDeletionNotifyAdapter *adapter)
 {
     void *handle = m_pluginLibraryHandleMap[adapter];
-    if (handle) unloadLibrary(handle);
+    if (handle) Files::unloadLibrary(handle);
     m_pluginLibraryHandleMap.erase(adapter);
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/vamp-hostsdk/host-c.cpp	Tue Jun 16 10:12:36 2015 +0100
@@ -0,0 +1,151 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vamp
+
+    An API for audio analysis and feature extraction plugins.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006-2015 Chris Cannam and QMUL.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#include <vamp-hostsdk/host-c.h>
+
+#include "Files.h"
+
+#include <map>
+#include <iostream>
+
+#include <cstring>
+
+using namespace std;
+
+static vector<string> files;
+static map<string, const char *> cnames;
+static bool haveFiles = false;
+
+struct vhLibrary_t {
+    vhLibrary_t(void *h, VampGetPluginDescriptorFunction f)
+        : handle(h), func(f), nplugins(0) { } 
+    void *handle;
+    VampGetPluginDescriptorFunction func;
+    int nplugins;
+};
+
+static void initFilenames()
+{
+    if (!haveFiles) {
+        files = Files::listLibraryFiles();
+        for (size_t i = 0; i < files.size(); ++i) {
+            cnames[files[i]] = strdup(Files::lcBasename(files[i]).c_str());
+        }
+        haveFiles = true;
+    }
+}
+
+int vhGetLibraryCount()
+{
+    initFilenames();
+    return int(files.size());
+}
+
+const char *vhGetLibraryName(int index)
+{
+    initFilenames();
+    if (index >= 0 && index < int(files.size())) {
+        return cnames[files[index]];
+    }
+    else return 0;
+}
+
+int vhGetLibraryIndex(const char *name)
+{
+    for (size_t i = 0; i < files.size(); ++i) {
+        if (Files::lcBasename(name) == Files::lcBasename(files[i])) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+vhLibrary vhLoadLibrary(int index)
+{
+    initFilenames();
+    if (index < 0 || index >= int(files.size())) {
+        return 0;
+    }
+
+    string fullPath = files[index];
+    void *lib = Files::loadLibrary(fullPath);
+
+    if (!lib) return 0;
+            
+    VampGetPluginDescriptorFunction func =
+        (VampGetPluginDescriptorFunction)Files::lookupInLibrary
+        (lib, "vampGetPluginDescriptor");
+    if (!func) {
+        cerr << "vhLoadLibrary: No vampGetPluginDescriptor function found in library \""
+             << fullPath << "\"" << endl;
+        Files::unloadLibrary(lib);
+        return 0;
+    }
+
+    vhLibrary_t *vhl = new vhLibrary_t(lib, func);
+    while (vhl->func(VAMP_API_VERSION, vhl->nplugins)) {
+        ++vhl->nplugins;
+    }
+    return vhl;
+}
+
+int vhGetPluginCount(vhLibrary library)
+{
+    vhLibrary_t *vhl = static_cast<vhLibrary_t *>(library);
+    if (vhl) return vhl->nplugins;
+    else return 0;
+}
+
+const VampPluginDescriptor *vhGetPluginDescriptor(vhLibrary library,
+                                                  int plugin)
+{
+    vhLibrary_t *vhl = static_cast<vhLibrary_t *>(library);
+    if (vhl && plugin >= 0 && plugin < vhl->nplugins) {
+        return vhl->func(VAMP_API_VERSION, plugin);
+    } else {
+        return 0;
+    }
+}
+
+void vhUnloadLibrary(vhLibrary library)
+{
+    vhLibrary_t *vhl = static_cast<vhLibrary_t *>(library);
+    if (vhl && vhl->handle) {
+        Files::unloadLibrary(vhl->handle);
+    }
+    delete vhl;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vamp-hostsdk/host-c.h	Tue Jun 16 10:12:36 2015 +0100
@@ -0,0 +1,147 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vamp
+
+    An API for audio analysis and feature extraction plugins.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006-2015 Chris Cannam and QMUL.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+/*
+    This file defines a low-level API for enumerating and loading
+    plugin libraries using C calling conventions. It could be used in
+    C programs, or in languages with C-compatible foreign-function
+    interfaces. Note that this works by calling to the C++ Vamp host
+    SDK, so any program using this interface must still link against
+    the rest of the Vamp plugin library and the C++ standard library.
+
+    This is not the simplest or easiest interface for hosting Vamp
+    plugins -- if you have the capability to use the C++ API, please
+    do that instead. (Most programs should not even include this
+    header.)
+
+    The C and C++ interfaces provide different abstraction levels:
+
+    In the C++ interface, the class PluginLoader provides a list of
+    keys corresponding to the installed plugins (where a key combines
+    the plugin's library name and plugin identifier into a single
+    string) plus a method to load a single plugin based on its key
+    (obtaining an instance of class Plugin). With the C++ interface
+    you go straight from the key to a live instance of the plugin. The
+    PluginLoader also provides various facilities to adapt the plugin
+    based on your requirements (e.g. to do time- to frequency-domain
+    conversion for you if the plugin requires it).
+
+    This low-level C interface, on the other hand, deals only in
+    plugin libraries and static descriptors, not in plugin
+    instances. You can enumerate the installed libraries, getting just
+    the base .soname of each library. Then you can retrieve each of
+    the raw C plugin descriptors from a library, and use the
+    descriptor (whose interface is defined in vamp/vamp.h) to
+    instantiate the plugin.
+
+    So this header corresponds to the first part of the PluginLoader
+    class interface: finding and loading plugin libraries and
+    retrieving plugin descriptors from them. But it does not do any of
+    the rest, i.e. instantiating and adapting the plugins themselves.
+    Although this makes the API appear very simple, it means the
+    resulting plugins are relatively hard to use compared to those
+    obtained by the PluginLoader API. There is no way to get to the
+    full C++ abstraction using this API.
+
+    This API is not thread-safe; use it from a single application
+    thread, or guard access to it with a mutex.
+
+    This header was introduced in version 2.6 of the Vamp plugin SDK.
+*/
+
+#ifndef VAMPHOST_C_H_INCLUDED
+#define VAMPHOST_C_H_INCLUDED
+
+#include <vamp/vamp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct vhLibrary_t *vhLibrary;
+
+/**
+ * Return the number of Vamp plugin libraries discovered in the
+ * installation path. This number will remain fixed after the first
+ * call -- plugins are only discovered once, the first time this
+ * function is called.
+ */
+extern int vhGetLibraryCount();
+
+/**
+ * Return the library name (base soname) of the library with the given
+ * index, in the range 0..(vhGetLibraryCount()-1).
+ */
+extern const char *vhGetLibraryName(int library);
+
+/**
+ * Return the library index for the given library name, or -1 if the
+ * name is not known.
+ */
+extern int vhGetLibraryIndex(const char *name);
+    
+/**
+ * Load the library with the given index. If the library cannot be
+ * loaded for any reason, the return value is 0; otherwise it is an
+ * opaque pointer suitable for passing to other functions in this API.
+ */
+extern vhLibrary vhLoadLibrary(int library);
+
+/**
+ * Return the number of Vamp plugins in the given library.
+ */
+extern int vhGetPluginCount(vhLibrary library);
+
+/**
+ * Return a Vamp plugin descriptor for a plugin in a given
+ * library. This simply calls the vampGetPluginDescriptor function in
+ * that library with the given plugin index and returns the
+ * result. See vamp/vamp.h for details about the plugin descriptor.
+ */ 
+extern const VampPluginDescriptor *vhGetPluginDescriptor(vhLibrary library,
+							 int plugin);
+
+/**
+ * Unload a plugin library. Do not do this while any of its plugins
+ * are still in use.
+ */
+extern void vhUnloadLibrary(vhLibrary);
+    
+#ifdef __cplusplus
+}
+#endif
+
+#endif