diff PyPlugScanner.cpp @ 37:27bab3a16c9a vampy2final

new branch Vampy2final
author fazekasgy
date Mon, 05 Oct 2009 11:28:00 +0000
parents
children 62dcaa5fe6f8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PyPlugScanner.cpp	Mon Oct 05 11:28:00 2009 +0000
@@ -0,0 +1,315 @@
+/*
+
+ * Vampy : This plugin is a wrapper around the Vamp plugin API.
+ * It allows for writing Vamp plugins in Python.
+
+ * Centre for Digital Music, Queen Mary University of London.
+ * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources 
+ * for licence information.)
+
+*/
+
+
+#include "PyPlugScanner.h"
+#include <algorithm>
+#include <cstdlib>
+//#include "vamp-hostsdk/PluginHostAdapter.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#include <tchar.h>
+#define pathsep ("\\")
+#else 
+#include <dirent.h>
+#include <dlfcn.h>
+#define pathsep ("/")
+#endif 
+#define joinPath(a,b) ( (a)+pathsep+(b) )
+
+using std::string;
+using std::vector;
+using std::cerr;
+using std::endl;
+using std::find;
+
+PyPlugScanner::PyPlugScanner()
+{
+
+} 
+
+PyPlugScanner *PyPlugScanner::m_instance = NULL;
+bool PyPlugScanner::m_hasInstance = false;
+
+PyPlugScanner*
+PyPlugScanner::getInstance()
+{
+	if (!m_hasInstance) {
+		m_instance = new PyPlugScanner();
+		m_hasInstance = true;
+	}
+	return m_instance;
+}
+
+void
+PyPlugScanner::setPath(vector<string> path)
+{
+	m_path=path;
+}
+
+// We assume that each script on the path has one valid class
+vector<string> 
+PyPlugScanner::getPyPlugs()
+{
+	//for_each m_path listFiles then return vector<pyPlugs>
+	//key format: FullPathString/FileName.py:ClassName
+	
+	bool getCompiled = true;
+	char* getPyc = getenv("VAMPY_COMPILED");
+	if (getPyc) {
+		string value(getPyc);
+		cerr << "VAMPY_COMPILED=" << value << endl;
+		getCompiled = value.compare("1")?false:true;
+	}
+	
+	vector<string> pyPlugs;
+	string pluginKey;
+	PyObject *pyClass;
+	
+    for (size_t i = 0; i < m_path.size(); ++i) {
+        
+        vector<string> files = listFiles(m_path[i],"py");
+
+        /// recognise byte compiled plugins
+		if (getCompiled) {
+        	vector<string> compiled_files = listFiles(m_path[i],"pyc");
+			mergeFileLists(compiled_files,files);
+		}
+
+        for (vector<string>::iterator fi = files.begin();
+             fi != files.end(); ++fi) {
+				string script = *fi;
+				if (!script.empty()) {					
+					string classname=script.substr(0,script.rfind('.'));
+					pluginKey=joinPath(m_path[i],script)+":"+classname;
+					pyClass = getScriptClass(m_path[i],classname);
+					if (pyClass == NULL) 
+					cerr << "Warning: Syntax error in VamPy plugin:  " 
+					     << classname << ". Avoiding plugin." << endl;
+					else { 
+							pyPlugs.push_back(pluginKey);
+							m_pyClasses.push_back(pyClass);
+						}
+					//pyPlugs.push_back(pluginKey);
+				}
+		}		
+	}
+		
+return pyPlugs;	
+
+}
+
+/// insert python byte code names (.pyc) if a .py file can not be found
+/// The interpreter automatically generates byte code files and executes
+/// them if they exist. Therefore, we prefer .py files, but we allow
+/// (relatively) closed source distributions by recognising .pyc files.
+void
+PyPlugScanner::mergeFileLists(vector<string> &pyc, vector<string> &py)
+{
+    for (vector<string>::iterator pycit = pyc.begin();
+    	pycit != pyc.end(); ++pycit) {
+			// cerr << *pycit;
+			string pyc_name = *pycit;
+			string py_name = pyc_name.substr(0,pyc_name.rfind('.')) + ".py";
+			vector<string>::iterator pyit = find (py.begin(), py.end(), py_name);
+			if (pyit == py.end()) py.push_back(pyc_name);
+	}
+	
+}
+
+
+//For now return one class object found in each script
+vector<PyObject*> 
+PyPlugScanner::getPyClasses()
+{
+return m_pyClasses;	
+
+}
+
+//Validate
+//This should not be called more than once!
+PyObject* 
+PyPlugScanner::getScriptClass(string path, string classname)
+{
+
+	//Add plugin path to active Python Path 
+	string pyCmd = "import sys\nsys.path.append('" + path + "')\n";
+	PyRun_SimpleString(pyCmd.c_str());
+
+	//Assign an object to the source code
+	PyObject *pySource = PyString_FromString(classname.c_str());
+
+	//Import it as a module into the py interpreter
+	PyObject *pyModule = PyImport_Import(pySource);
+	PyObject* pyError = PyErr_Occurred();
+	if (! pyError == 0) { 
+		cerr << "ERROR: error importing source: " << classname << endl; 
+		PyErr_Print(); 
+		Py_DECREF(pySource); 
+		Py_CLEAR(pyModule);  // safer if pyModule==NULL
+		return NULL; 
+	}
+	Py_DECREF(pySource);
+
+	//Read the dictionary object holding the namespace of the module (borrowed reference)
+	PyObject *pyDict = PyModule_GetDict(pyModule);
+	Py_DECREF(pyModule);
+
+	//Get the PluginClass from the module (borrowed reference)
+	PyObject *pyClass = PyDict_GetItemString(pyDict, classname.c_str());
+
+	//Check if class is present and a callable method is implemented
+	if (pyClass && PyCallable_Check(pyClass)) {
+
+	    return pyClass;
+	}	
+	else {
+		cerr << "ERROR: callable plugin class could not be found in source: " << classname << endl 
+			<< "Hint: plugin source filename and plugin class name must be the same." << endl;
+		PyErr_Print(); 
+		return NULL;
+	}
+}
+
+
+
+// Return a list of files in dir with given extension
+// Code taken from hostext/PluginLoader.cpp
+vector<string>
+PyPlugScanner::listFiles(string dir, string extension)
+{
+    vector<string> files;
+
+#ifdef _WIN32
+
+    string expression = dir + "\\*." + extension;
+    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);
+
+#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;
+        }
+		//cerr << "pyscripts: " << e->d_name <<  endl;
+        files.push_back(e->d_name);
+    }
+
+    closedir(d);
+#endif
+
+    return files;
+}
+
+
+//!!! It would probably be better to actually call
+// PluginHostAdapter::getPluginPath.  That would mean this "plugin"
+// needs to link against vamp-hostsdk, but that's probably acceptable
+// as it is sort of a host as well.
+
+// std::vector<std::string>
+// PyPlugScanner::getAllValidPath()
+// { 
+// 	Vamp::PluginHostAdapter host_adapter( ??? );
+// 	return host_adapter.getPluginPath();
+// }
+
+// tried to implement it, but found a bit confusing how to 
+// instantiate the host adapter here...
+
+
+//Return correct plugin directories as per platform
+//Code taken from vamp-sdk/PluginHostAdapter.cpp
+std::vector<std::string>
+PyPlugScanner::getAllValidPath()
+{
+	
+    std::vector<std::string> path;
+    std::string envPath;
+
+    char *cpath = getenv("VAMP_PATH");
+    if (cpath) envPath = cpath;
+
+#ifdef _WIN32
+#define PATH_SEPARATOR ';'
+#define DEFAULT_VAMP_PATH "%ProgramFiles%\\Vamp Plugins"
+#else
+#define PATH_SEPARATOR ':'
+#ifdef __APPLE__
+#define DEFAULT_VAMP_PATH "$HOME/Library/Audio/Plug-Ins/Vamp:/Library/Audio/Plug-Ins/Vamp"
+#else
+#define DEFAULT_VAMP_PATH "$HOME/vamp:$HOME/.vamp:/usr/local/lib/vamp:/usr/lib/vamp"
+#endif
+#endif
+
+    if (envPath == "") {
+        envPath = DEFAULT_VAMP_PATH;
+        char *chome = getenv("HOME");
+        if (chome) {
+            std::string home(chome);
+            std::string::size_type f;
+            while ((f = envPath.find("$HOME")) != std::string::npos &&
+                    f < envPath.length()) {
+                envPath.replace(f, 5, home);
+            }
+        }
+#ifdef _WIN32
+        char *cpfiles = getenv("ProgramFiles");
+        if (!cpfiles) cpfiles = "C:\\Program Files";
+        std::string pfiles(cpfiles);
+        std::string::size_type f;
+        while ((f = envPath.find("%ProgramFiles%")) != std::string::npos &&
+               f < envPath.length()) {
+            envPath.replace(f, 14, pfiles);
+        }
+#endif
+    }
+
+    std::string::size_type index = 0, newindex = 0;
+
+    while ((newindex = envPath.find(PATH_SEPARATOR, index)) < envPath.size()) {
+	path.push_back(envPath.substr(index, newindex - index));
+	index = newindex + 1;
+    }
+    
+    path.push_back(envPath.substr(index));
+
+	//can add an extra path for vampy plugins
+	char* extraPath = getenv("VAMPY_EXTPATH");
+	if (extraPath) {
+		string vampyPath(extraPath);
+		cerr << "VAMPY_EXTPATH=" << vampyPath << endl;
+		path.push_back(vampyPath);
+	}
+	
+    return path;
+}