annotate PyPlugScanner.cpp @ 7:a4c955e9a70b

* housekeeping: VAMP -> Vamp throughout (not an acronym)
author cannam
date Wed, 11 Jun 2008 16:04:52 +0000
parents 134313c59d82
children 3af6b5990ad8
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@0 143 } else return NULL;
fazekasgy@0 144 }
fazekasgy@0 145
fazekasgy@0 146
fazekasgy@0 147
fazekasgy@0 148 // Return a list of files in dir with given extension
fazekasgy@0 149 // Code taken from hostext/PluginLoader.cpp
fazekasgy@0 150 vector<string>
fazekasgy@0 151 PyPlugScanner::listFiles(string dir, string extension)
fazekasgy@0 152 {
fazekasgy@0 153 vector<string> files;
fazekasgy@0 154
fazekasgy@0 155 #ifdef _WIN32
fazekasgy@0 156
fazekasgy@0 157 string expression = dir + "\\*." + extension;
fazekasgy@0 158 WIN32_FIND_DATA data;
fazekasgy@0 159 HANDLE fh = FindFirstFile(expression.c_str(), &data);
fazekasgy@0 160 if (fh == INVALID_HANDLE_VALUE) return files;
fazekasgy@0 161
fazekasgy@0 162 bool ok = true;
fazekasgy@0 163 while (ok) {
fazekasgy@0 164 files.push_back(data.cFileName);
fazekasgy@0 165 ok = FindNextFile(fh, &data);
fazekasgy@0 166 }
fazekasgy@0 167
fazekasgy@0 168 FindClose(fh);
fazekasgy@0 169
fazekasgy@0 170 #else
fazekasgy@0 171
fazekasgy@0 172 size_t extlen = extension.length();
fazekasgy@0 173 DIR *d = opendir(dir.c_str());
fazekasgy@0 174 if (!d) return files;
fazekasgy@0 175
fazekasgy@0 176 struct dirent *e = 0;
fazekasgy@0 177 while ((e = readdir(d))) {
fazekasgy@0 178
fazekasgy@0 179 if (!(e->d_type & DT_REG) && (e->d_type != DT_UNKNOWN)) continue;
fazekasgy@0 180
fazekasgy@0 181 if (!e->d_name) continue;
fazekasgy@0 182
fazekasgy@0 183 size_t len = strlen(e->d_name);
fazekasgy@0 184 if (len < extlen + 2 ||
fazekasgy@0 185 e->d_name + len - extlen - 1 != "." + extension) {
fazekasgy@0 186 continue;
fazekasgy@0 187 }
fazekasgy@0 188 //cerr << "pyscripts: " << e->d_name << endl;
fazekasgy@0 189 files.push_back(e->d_name);
fazekasgy@0 190 }
fazekasgy@0 191
fazekasgy@0 192 closedir(d);
fazekasgy@0 193 #endif
fazekasgy@0 194
fazekasgy@0 195 return files;
fazekasgy@0 196 }
fazekasgy@0 197
fazekasgy@0 198 //Return correct plugin directories as per platform
fazekasgy@0 199 //Code taken from vamp-sdk/PluginHostAdapter.cpp
cannam@3 200
cannam@3 201 //!!! It would probably be better to actually call
cannam@3 202 // PluginHostAdapter::getPluginPath. That would mean this "plugin"
cannam@3 203 // needs to link against vamp-hostsdk, but that's probably acceptable
cannam@3 204 // as it is sort of a host as well.
cannam@3 205
fazekasgy@0 206 std::vector<std::string>
fazekasgy@0 207 PyPlugScanner::getAllValidPath()
fazekasgy@0 208 {
fazekasgy@0 209 std::vector<std::string> path;
fazekasgy@0 210 std::string envPath;
fazekasgy@0 211
fazekasgy@0 212 char *cpath = getenv("VAMP_PATH");
fazekasgy@0 213 if (cpath) envPath = cpath;
fazekasgy@0 214
fazekasgy@0 215 #ifdef _WIN32
fazekasgy@0 216 #define PATH_SEPARATOR ';'
fazekasgy@0 217 #define DEFAULT_VAMP_PATH "%ProgramFiles%\\Vamp Plugins"
fazekasgy@0 218 #else
fazekasgy@0 219 #define PATH_SEPARATOR ':'
fazekasgy@0 220 #ifdef __APPLE__
fazekasgy@0 221 #define DEFAULT_VAMP_PATH "$HOME/Library/Audio/Plug-Ins/Vamp:/Library/Audio/Plug-Ins/Vamp"
fazekasgy@0 222 #else
fazekasgy@0 223 #define DEFAULT_VAMP_PATH "$HOME/vamp:$HOME/.vamp:/usr/local/lib/vamp:/usr/lib/vamp"
fazekasgy@0 224 #endif
fazekasgy@0 225 #endif
fazekasgy@0 226
fazekasgy@0 227 if (envPath == "") {
fazekasgy@0 228 envPath = DEFAULT_VAMP_PATH;
fazekasgy@0 229 char *chome = getenv("HOME");
fazekasgy@0 230 if (chome) {
fazekasgy@0 231 std::string home(chome);
fazekasgy@0 232 std::string::size_type f;
fazekasgy@0 233 while ((f = envPath.find("$HOME")) != std::string::npos &&
fazekasgy@0 234 f < envPath.length()) {
fazekasgy@0 235 envPath.replace(f, 5, home);
fazekasgy@0 236 }
fazekasgy@0 237 }
fazekasgy@0 238 #ifdef _WIN32
fazekasgy@0 239 char *cpfiles = getenv("ProgramFiles");
fazekasgy@0 240 if (!cpfiles) cpfiles = "C:\\Program Files";
fazekasgy@0 241 std::string pfiles(cpfiles);
fazekasgy@0 242 std::string::size_type f;
fazekasgy@0 243 while ((f = envPath.find("%ProgramFiles%")) != std::string::npos &&
fazekasgy@0 244 f < envPath.length()) {
fazekasgy@0 245 envPath.replace(f, 14, pfiles);
fazekasgy@0 246 }
fazekasgy@0 247 #endif
fazekasgy@0 248 }
fazekasgy@0 249
fazekasgy@0 250 std::string::size_type index = 0, newindex = 0;
fazekasgy@0 251
fazekasgy@0 252 while ((newindex = envPath.find(PATH_SEPARATOR, index)) < envPath.size()) {
fazekasgy@0 253 path.push_back(envPath.substr(index, newindex - index));
fazekasgy@0 254 index = newindex + 1;
fazekasgy@0 255 }
fazekasgy@0 256
fazekasgy@0 257 path.push_back(envPath.substr(index));
fazekasgy@0 258
fazekasgy@0 259 return path;
fazekasgy@0 260 }