annotate PyPlugScanner.cpp @ 14:8cd6346e755f

now installs example python plugins to the correct place
author fazekasgy
date Wed, 02 Jul 2008 12:17:58 +0000
parents 3af6b5990ad8
children 7d28bed0864e
rev   line source
fazekasgy@0 1 /**
cannam@7 2 * This Vamp plugin is a wrapper for Python Scripts. (VamPy)
fazekasgy@0 3 * Centre for Digital Music, Queen Mary, University of London.
fazekasgy@0 4 * Copyright 2008, George Fazekas.
fazekasgy@0 5
fazekasgy@0 6 */
fazekasgy@0 7
fazekasgy@0 8
fazekasgy@0 9 #include "PyPlugScanner.h"
fazekasgy@0 10
fazekasgy@0 11 //#include <fstream>
fazekasgy@0 12 //#include <cctype>
fazekasgy@0 13
fazekasgy@0 14 #ifdef _WIN32
fazekasgy@0 15 #include <windows.h>
fazekasgy@0 16 #include <tchar.h>
fazekasgy@0 17 #define pathsep ("\\")
fazekasgy@0 18 #else
fazekasgy@0 19 #include <dirent.h>
fazekasgy@0 20 #include <dlfcn.h>
fazekasgy@0 21 #define pathsep ("/")
fazekasgy@0 22 #endif
fazekasgy@0 23 #define joinPath(a,b) ( (a)+pathsep+(b) )
fazekasgy@0 24
fazekasgy@0 25 using std::string;
fazekasgy@0 26 using std::vector;
fazekasgy@0 27 using std::cerr;
fazekasgy@0 28 using std::endl;
fazekasgy@0 29
fazekasgy@0 30 PyPlugScanner::PyPlugScanner()
fazekasgy@0 31 {
fazekasgy@0 32
fazekasgy@0 33 }
fazekasgy@0 34
fazekasgy@0 35 PyPlugScanner *PyPlugScanner::m_instance = NULL;
fazekasgy@0 36 bool PyPlugScanner::m_hasInstance = false;
fazekasgy@0 37
fazekasgy@0 38 PyPlugScanner*
fazekasgy@0 39 PyPlugScanner::getInstance()
fazekasgy@0 40 {
fazekasgy@0 41 if (!m_hasInstance) {
fazekasgy@0 42 m_instance = new PyPlugScanner();
fazekasgy@0 43 m_hasInstance = true;
fazekasgy@0 44 }
fazekasgy@0 45 return m_instance;
fazekasgy@0 46 }
fazekasgy@0 47
fazekasgy@0 48 void
fazekasgy@0 49 PyPlugScanner::setPath(vector<string> path)
fazekasgy@0 50 {
fazekasgy@0 51 m_path=path;
fazekasgy@0 52 }
fazekasgy@0 53
fazekasgy@0 54 // TODO: This should return all scripts for all valid paths
fazekasgy@0 55 // Validate python classes here?
fazekasgy@0 56 // For now, we assume that each script on the path has one valid class
fazekasgy@0 57 vector<string>
fazekasgy@0 58 PyPlugScanner::getPyPlugs()
fazekasgy@0 59 {
fazekasgy@0 60 //foreach m_path listFiles then return vector<pyPlugs>
fazekasgy@0 61 //format: FullPathString/FileName.py:ClassName
fazekasgy@0 62
fazekasgy@0 63 vector<string> pyPlugs;
fazekasgy@0 64 string pluginKey;
fazekasgy@0 65 PyObject *pyClassInstance;
fazekasgy@0 66
fazekasgy@0 67 for (size_t i = 0; i < m_path.size(); ++i) {
fazekasgy@0 68
fazekasgy@0 69 vector<string> files = listFiles(m_path[i],"py");
fazekasgy@0 70
fazekasgy@0 71 for (vector<string>::iterator fi = files.begin();
fazekasgy@0 72 fi != files.end(); ++fi) {
fazekasgy@0 73 string script = *fi;
fazekasgy@0 74 if (!script.empty()) {
fazekasgy@0 75 string classname=script.substr(0,script.rfind('.'));
fazekasgy@0 76 pluginKey=joinPath(m_path[i],script)+":"+classname;
fazekasgy@0 77 pyClassInstance = getScriptInstance(m_path[i],classname);
fazekasgy@0 78 if (pyClassInstance == NULL)
fazekasgy@0 79 cerr << "Warning: Syntax error in VamPy plugin: "
fazekasgy@0 80 << classname << ". Avoiding plugin." << endl;
fazekasgy@0 81 else {
fazekasgy@0 82 pyPlugs.push_back(pluginKey);
fazekasgy@0 83 m_pyInstances.push_back(pyClassInstance);
fazekasgy@0 84 }
fazekasgy@0 85 //pyPlugs.push_back(pluginKey);
fazekasgy@0 86 }
fazekasgy@0 87 }
fazekasgy@0 88 }
fazekasgy@0 89
fazekasgy@0 90 return pyPlugs;
fazekasgy@0 91
fazekasgy@0 92 }
fazekasgy@0 93
fazekasgy@0 94
fazekasgy@0 95 //For now return one class instance found in each script
fazekasgy@0 96 vector<PyObject*>
fazekasgy@0 97 PyPlugScanner::getPyInstances()
fazekasgy@0 98 {
fazekasgy@0 99 return m_pyInstances;
fazekasgy@0 100
fazekasgy@0 101 }
fazekasgy@0 102
fazekasgy@0 103
fazekasgy@0 104 //Validate
fazekasgy@0 105 //This should not be called more than once!
fazekasgy@0 106 PyObject*
fazekasgy@0 107 PyPlugScanner::getScriptInstance(string path, string classname)
fazekasgy@0 108 {
fazekasgy@0 109
fazekasgy@0 110 //Add plugin path to active Python Path
fazekasgy@0 111 string pyCmd = "import sys\nsys.path.append('" + path + "')\n";
fazekasgy@0 112 PyRun_SimpleString(pyCmd.c_str());
fazekasgy@0 113
fazekasgy@0 114 //Assign an object to the source code
fazekasgy@0 115 PyObject *pySource = PyString_FromString(classname.c_str());
fazekasgy@0 116
fazekasgy@0 117 //Import it as a module into the py interpreter
fazekasgy@0 118 PyObject *pyModule = PyImport_Import(pySource);
fazekasgy@0 119 PyObject* pyError = PyErr_Occurred();
fazekasgy@0 120 if (! pyError == 0) {
fazekasgy@0 121 cerr << "ERROR: error importing source: " << classname << endl;
fazekasgy@0 122 PyErr_Print();
fazekasgy@0 123 Py_DECREF(pySource);
fazekasgy@0 124 Py_CLEAR(pyModule); // safer if pyModule==NULL
fazekasgy@0 125 return NULL;
fazekasgy@0 126 }
fazekasgy@0 127 Py_DECREF(pySource);
fazekasgy@0 128
fazekasgy@0 129 //Read the namespace of the module into a dictionary object (borrowed reference)
fazekasgy@0 130 PyObject *pyDict = PyModule_GetDict(pyModule);
fazekasgy@0 131 Py_DECREF(pyModule);
fazekasgy@0 132
fazekasgy@0 133 //Get the PluginClass from the module (borrowed reference)
fazekasgy@0 134 PyObject *pyClass = PyDict_GetItemString(pyDict, classname.c_str());
fazekasgy@0 135
fazekasgy@0 136 //Check if class is present and a callable method is implemented
fazekasgy@0 137 if (pyClass && PyCallable_Check(pyClass)) {
fazekasgy@0 138
fazekasgy@0 139 //Create an instance
fazekasgy@0 140 PyObject *pyInstance = PyObject_CallObject(pyClass, NULL);
fazekasgy@0 141 //cerr << "__(getInstance) PyPlugin Class: " << m_class << " successfully created.__" << endl;
fazekasgy@0 142 return pyInstance;
fazekasgy@8 143 }
fazekasgy@8 144 else {
fazekasgy@8 145 cerr << "ERROR: callable plugin class could not be found in source: " << classname << endl
fazekasgy@8 146 << "Hint: plugin source filename and plugin class name must be the same." << endl;
fazekasgy@8 147 PyErr_Print();
fazekasgy@8 148 return NULL;
fazekasgy@8 149 }
fazekasgy@0 150 }
fazekasgy@0 151
fazekasgy@0 152
fazekasgy@0 153
fazekasgy@0 154 // Return a list of files in dir with given extension
fazekasgy@0 155 // Code taken from hostext/PluginLoader.cpp
fazekasgy@0 156 vector<string>
fazekasgy@0 157 PyPlugScanner::listFiles(string dir, string extension)
fazekasgy@0 158 {
fazekasgy@0 159 vector<string> files;
fazekasgy@0 160
fazekasgy@0 161 #ifdef _WIN32
fazekasgy@0 162
fazekasgy@0 163 string expression = dir + "\\*." + extension;
fazekasgy@0 164 WIN32_FIND_DATA data;
fazekasgy@0 165 HANDLE fh = FindFirstFile(expression.c_str(), &data);
fazekasgy@0 166 if (fh == INVALID_HANDLE_VALUE) return files;
fazekasgy@0 167
fazekasgy@0 168 bool ok = true;
fazekasgy@0 169 while (ok) {
fazekasgy@0 170 files.push_back(data.cFileName);
fazekasgy@0 171 ok = FindNextFile(fh, &data);
fazekasgy@0 172 }
fazekasgy@0 173
fazekasgy@0 174 FindClose(fh);
fazekasgy@0 175
fazekasgy@0 176 #else
fazekasgy@0 177
fazekasgy@0 178 size_t extlen = extension.length();
fazekasgy@0 179 DIR *d = opendir(dir.c_str());
fazekasgy@0 180 if (!d) return files;
fazekasgy@0 181
fazekasgy@0 182 struct dirent *e = 0;
fazekasgy@0 183 while ((e = readdir(d))) {
fazekasgy@0 184
fazekasgy@0 185 if (!(e->d_type & DT_REG) && (e->d_type != DT_UNKNOWN)) continue;
fazekasgy@0 186
fazekasgy@0 187 if (!e->d_name) continue;
fazekasgy@0 188
fazekasgy@0 189 size_t len = strlen(e->d_name);
fazekasgy@0 190 if (len < extlen + 2 ||
fazekasgy@0 191 e->d_name + len - extlen - 1 != "." + extension) {
fazekasgy@0 192 continue;
fazekasgy@0 193 }
fazekasgy@0 194 //cerr << "pyscripts: " << e->d_name << endl;
fazekasgy@0 195 files.push_back(e->d_name);
fazekasgy@0 196 }
fazekasgy@0 197
fazekasgy@0 198 closedir(d);
fazekasgy@0 199 #endif
fazekasgy@0 200
fazekasgy@0 201 return files;
fazekasgy@0 202 }
fazekasgy@0 203
fazekasgy@0 204 //Return correct plugin directories as per platform
fazekasgy@0 205 //Code taken from vamp-sdk/PluginHostAdapter.cpp
cannam@3 206
cannam@3 207 //!!! It would probably be better to actually call
cannam@3 208 // PluginHostAdapter::getPluginPath. That would mean this "plugin"
cannam@3 209 // needs to link against vamp-hostsdk, but that's probably acceptable
cannam@3 210 // as it is sort of a host as well.
cannam@3 211
fazekasgy@0 212 std::vector<std::string>
fazekasgy@0 213 PyPlugScanner::getAllValidPath()
fazekasgy@0 214 {
fazekasgy@0 215 std::vector<std::string> path;
fazekasgy@0 216 std::string envPath;
fazekasgy@0 217
fazekasgy@0 218 char *cpath = getenv("VAMP_PATH");
fazekasgy@0 219 if (cpath) envPath = cpath;
fazekasgy@0 220
fazekasgy@0 221 #ifdef _WIN32
fazekasgy@0 222 #define PATH_SEPARATOR ';'
fazekasgy@0 223 #define DEFAULT_VAMP_PATH "%ProgramFiles%\\Vamp Plugins"
fazekasgy@0 224 #else
fazekasgy@0 225 #define PATH_SEPARATOR ':'
fazekasgy@0 226 #ifdef __APPLE__
fazekasgy@0 227 #define DEFAULT_VAMP_PATH "$HOME/Library/Audio/Plug-Ins/Vamp:/Library/Audio/Plug-Ins/Vamp"
fazekasgy@0 228 #else
fazekasgy@0 229 #define DEFAULT_VAMP_PATH "$HOME/vamp:$HOME/.vamp:/usr/local/lib/vamp:/usr/lib/vamp"
fazekasgy@0 230 #endif
fazekasgy@0 231 #endif
fazekasgy@0 232
fazekasgy@0 233 if (envPath == "") {
fazekasgy@0 234 envPath = DEFAULT_VAMP_PATH;
fazekasgy@0 235 char *chome = getenv("HOME");
fazekasgy@0 236 if (chome) {
fazekasgy@0 237 std::string home(chome);
fazekasgy@0 238 std::string::size_type f;
fazekasgy@0 239 while ((f = envPath.find("$HOME")) != std::string::npos &&
fazekasgy@0 240 f < envPath.length()) {
fazekasgy@0 241 envPath.replace(f, 5, home);
fazekasgy@0 242 }
fazekasgy@0 243 }
fazekasgy@0 244 #ifdef _WIN32
fazekasgy@0 245 char *cpfiles = getenv("ProgramFiles");
fazekasgy@0 246 if (!cpfiles) cpfiles = "C:\\Program Files";
fazekasgy@0 247 std::string pfiles(cpfiles);
fazekasgy@0 248 std::string::size_type f;
fazekasgy@0 249 while ((f = envPath.find("%ProgramFiles%")) != std::string::npos &&
fazekasgy@0 250 f < envPath.length()) {
fazekasgy@0 251 envPath.replace(f, 14, pfiles);
fazekasgy@0 252 }
fazekasgy@0 253 #endif
fazekasgy@0 254 }
fazekasgy@0 255
fazekasgy@0 256 std::string::size_type index = 0, newindex = 0;
fazekasgy@0 257
fazekasgy@0 258 while ((newindex = envPath.find(PATH_SEPARATOR, index)) < envPath.size()) {
fazekasgy@0 259 path.push_back(envPath.substr(index, newindex - index));
fazekasgy@0 260 index = newindex + 1;
fazekasgy@0 261 }
fazekasgy@0 262
fazekasgy@0 263 path.push_back(envPath.substr(index));
fazekasgy@0 264
fazekasgy@0 265 return path;
fazekasgy@0 266 }