annotate PyPlugScanner.cpp @ 2:211ebe55d521

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