You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

526 lines
14KB

  1. /*
  2. LADSPAInfo.C - Class for indexing information on LADSPA Plugins
  3. Copyright (C) 2002 Mike Rawes
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  15. */
  16. #include <vector>
  17. #include <string>
  18. #include <iostream>
  19. #include <sstream>
  20. #include <map>
  21. #include <algorithm>
  22. #include <cstdlib>
  23. #include <sys/types.h>
  24. #include <sys/stat.h>
  25. #include <dirent.h>
  26. #include <dlfcn.h>
  27. #include <ladspa.h>
  28. #include "LADSPAInfo.h"
  29. using namespace std;
  30. LADSPAInfo::LADSPAInfo(bool override, const char *path_list)
  31. {
  32. if (strlen(path_list) > 0) {
  33. m_ExtraPaths = strdup(path_list);
  34. } else {
  35. m_ExtraPaths = NULL;
  36. }
  37. m_LADSPAPathOverride = override;
  38. RescanPlugins();
  39. }
  40. LADSPAInfo::~LADSPAInfo()
  41. {
  42. CleanUp();
  43. }
  44. void
  45. LADSPAInfo::RescanPlugins(void)
  46. {
  47. // Clear out what we've got
  48. CleanUp();
  49. if (!m_LADSPAPathOverride) {
  50. // Get $LADPSA_PATH, if available
  51. char *ladspa_path = getenv("LADSPA_PATH");
  52. if (!ladspa_path) {
  53. // Oops
  54. cerr << "WARNING: No LADSPA Path Set" << endl;
  55. }
  56. // Extract path elements and add path
  57. if (ladspa_path) {
  58. ScanPathList(ladspa_path);
  59. }
  60. }
  61. // Check any supplied extra paths
  62. if (m_ExtraPaths) {
  63. ScanPathList(m_ExtraPaths);
  64. }
  65. // Do we have any plugins now?
  66. if (m_Plugins.size() == 0) {
  67. cerr << "WARNING: No plugins found" << endl;
  68. } else {
  69. cerr << m_Plugins.size() << " plugins found in " << m_Libraries.size() << " libraries" << endl;
  70. }
  71. // Sort list by name
  72. sort(m_OrderedPluginList.begin(), m_OrderedPluginList.end(), PluginEntrySortAsc());
  73. // Deal with duplicates by numbering them
  74. for (vector<PluginEntry>::iterator i = m_OrderedPluginList.begin();
  75. i != m_OrderedPluginList.end(); ) {
  76. string name = i->Name;
  77. i++;
  78. unsigned long n = 2;
  79. while ((i != m_OrderedPluginList.end()) && (i->Name == name)) {
  80. stringstream s;
  81. s << n;
  82. i->Name = name + " (" + s.str() + ")";
  83. n++;
  84. i++;
  85. }
  86. }
  87. }
  88. void
  89. LADSPAInfo::UnloadAllLibraries(void)
  90. {
  91. for (vector<LibraryInfo>::iterator i = m_Libraries.begin();
  92. i != m_Libraries.end(); i++) {
  93. if (i->Handle) dlclose(i->Handle);
  94. i->RefCount = 0;
  95. }
  96. }
  97. const LADSPA_Descriptor *
  98. LADSPAInfo::GetDescriptorByID(unsigned long unique_id)
  99. {
  100. if (m_IDLookup.find(unique_id) == m_IDLookup.end()) {
  101. cerr << "LADSPA Plugin ID " << unique_id << " not found!" << endl;
  102. return NULL;
  103. }
  104. // Got plugin index
  105. unsigned long plugin_index = m_IDLookup[unique_id];
  106. PluginInfo *pi = &(m_Plugins[plugin_index]);
  107. LibraryInfo *li = &(m_Libraries[pi->LibraryIndex]);
  108. if (!(pi->Descriptor)) {
  109. LADSPA_Descriptor_Function desc_func = GetDescriptorFunctionForLibrary(pi->LibraryIndex);
  110. if (desc_func) pi->Descriptor = desc_func(pi->Index);
  111. }
  112. if (pi->Descriptor) {
  113. // Success, so increment ref counter for library
  114. li->RefCount++;
  115. }
  116. return pi->Descriptor;
  117. }
  118. void
  119. LADSPAInfo::DiscardDescriptorByID(unsigned long unique_id)
  120. {
  121. if (m_IDLookup.find(unique_id) == m_IDLookup.end()) {
  122. cerr << "LADSPA Plugin ID " << unique_id << " not found!" << endl;
  123. } else {
  124. // Get plugin index
  125. unsigned long plugin_index = m_IDLookup[unique_id];
  126. PluginInfo *pi = &(m_Plugins[plugin_index]);
  127. LibraryInfo *li = &(m_Libraries[pi->LibraryIndex]);
  128. // Decrement reference counter for library, and unload if last
  129. if (li->RefCount > 0) {
  130. li->RefCount--;
  131. if (li->RefCount == 0) {
  132. dlclose(li->Handle);
  133. // Need to unset all plugin descriptors that may have been
  134. // set from this library
  135. // Plugins in library will be a contiguous block, so we
  136. // just check each direction from given plugin
  137. unsigned long i = plugin_index - 1;
  138. while (m_Plugins[i].LibraryIndex == pi->LibraryIndex) {
  139. m_Plugins[i--].Descriptor = NULL;
  140. }
  141. i = plugin_index + 1;
  142. while (m_Plugins[i].LibraryIndex == pi->LibraryIndex) {
  143. m_Plugins[i++].Descriptor = NULL;
  144. }
  145. }
  146. }
  147. }
  148. }
  149. unsigned long
  150. LADSPAInfo::GetIDFromFilenameAndLabel(std::string filename,
  151. std::string label)
  152. {
  153. bool library_loaded = false;
  154. if (m_FilenameLookup.find(filename) == m_FilenameLookup.end()) {
  155. cerr << "LADSPA Library " << filename << " not found!" << endl;
  156. return 0;
  157. }
  158. unsigned long library_index = m_FilenameLookup[filename];
  159. if (!(m_Libraries[library_index].Handle)) library_loaded = true;
  160. LADSPA_Descriptor_Function desc_func = GetDescriptorFunctionForLibrary(library_index);
  161. if (!desc_func) {
  162. return 0;
  163. }
  164. // Search for label in library
  165. const LADSPA_Descriptor *desc;
  166. for (unsigned long i = 0; (desc = desc_func(i)) != NULL; i++) {
  167. string l = desc->Label;
  168. if (l == label) {
  169. // If we had to load the library, unload it
  170. unsigned long id = desc->UniqueID;
  171. if (library_loaded) {
  172. dlclose(m_Libraries[library_index].Handle);
  173. m_Libraries[library_index].Handle = NULL;
  174. }
  175. return id;
  176. }
  177. }
  178. cerr << "Plugin " << label << " not found in library " << filename << endl;
  179. return 0;
  180. }
  181. const vector<LADSPAInfo::PluginEntry>
  182. LADSPAInfo::GetPluginList(void)
  183. {
  184. return m_OrderedPluginList;
  185. }
  186. unsigned long
  187. LADSPAInfo::GetPluginListEntryByID(unsigned long unique_id)
  188. {
  189. unsigned long j = 0;
  190. for (vector<PluginEntry>::iterator i = m_OrderedPluginList.begin();
  191. i != m_OrderedPluginList.end(); i++, j++) {
  192. if (i->UniqueID == unique_id) return j;
  193. }
  194. return m_OrderedPluginList.size();
  195. }
  196. // ****************************************************************************
  197. // ** Private Member Functions **
  198. // ****************************************************************************
  199. // Unload any loaded DLLs and clear vectors etc
  200. void
  201. LADSPAInfo::CleanUp(void)
  202. {
  203. m_IDLookup.clear();
  204. m_FilenameLookup.clear();
  205. m_Plugins.clear();
  206. // Unload loaded dlls
  207. for (vector<LibraryInfo>::iterator i = m_Libraries.begin();
  208. i != m_Libraries.end(); i++) {
  209. if (i->Handle) dlclose(i->Handle);
  210. }
  211. m_Libraries.clear();
  212. m_Paths.clear();
  213. m_OrderedPluginList.clear();
  214. m_MaxInputPortCount = 0;
  215. if (m_ExtraPaths) {
  216. free(m_ExtraPaths);
  217. m_ExtraPaths = NULL;
  218. }
  219. }
  220. void
  221. LADSPAInfo::ScanPathList(const char *path_list)
  222. {
  223. const char *start;
  224. const char *end;
  225. int extra;
  226. char *temp;
  227. start = path_list;
  228. while (*start != '\0') {
  229. while (*start == ':') start++;
  230. end = start;
  231. while (*end != ':' && *end != '\0') end++;
  232. if (end - start > 0) {
  233. extra = (*(end - 1) == '/') ? 0 : 1;
  234. temp = (char *)malloc(end - start + 1 + extra);
  235. if (temp) {
  236. strncpy(temp, start, end - start);
  237. if (extra == 1) temp[end - start] = '/';
  238. temp[end - start + extra] = '\0';
  239. ExaminePath(temp);
  240. free(temp);
  241. }
  242. }
  243. start = end;
  244. }
  245. }
  246. // Check given path:
  247. // 1. Exists
  248. // 2. Is a directory
  249. // 3. Contains at least one LADSPA Plugin library
  250. //
  251. // If so, add the path to the Paths vector, and examine
  252. // contents, adding all valid LADSPA plugin library info
  253. // to the Libraries and Plugins vectors.
  254. void
  255. LADSPAInfo::ExaminePath(const char *path)
  256. {
  257. DIR *dp;
  258. struct dirent *ep;
  259. struct stat sb;
  260. void *handle;
  261. LADSPA_Descriptor_Function desc_func;
  262. const LADSPA_Descriptor *desc;
  263. bool path_added;
  264. unsigned long path_index;
  265. bool library_added;
  266. string fullpath;
  267. vector<string> temp_names;
  268. vector<string>::iterator p = find(m_Paths.begin(), m_Paths.end(), path);
  269. path_added = !(p == m_Paths.end());
  270. path_index = p - m_Paths.begin();
  271. dp = opendir(path);
  272. if (!dp) {
  273. cerr << "WARNING: Could not open path " << path << endl;
  274. } else {
  275. while ((ep = readdir(dp))) {
  276. // Need full path
  277. fullpath = path;
  278. fullpath.append(ep->d_name);
  279. // Stat file to get type
  280. if (!stat(fullpath.c_str(), &sb)) {
  281. // We only want regular files
  282. if (S_ISREG(sb.st_mode)) {
  283. // We're not fussed about resolving symbols yet, since we are just
  284. // checking libraries.
  285. handle = dlopen(fullpath.c_str(), RTLD_LAZY);
  286. if (!handle) {
  287. cerr << "WARNING: File " << path << ep->d_name
  288. << " could not be examined" << endl;
  289. cerr << "dlerror() output:" << endl;
  290. cerr << dlerror() << endl;
  291. } else {
  292. // It's a DLL, so now see if it's a LADSPA plugin library
  293. desc_func = (LADSPA_Descriptor_Function)dlsym(handle,
  294. "ladspa_descriptor");
  295. if (!desc_func) {
  296. // Is DLL, but not a LADSPA one
  297. cerr << "WARNING: DLL " << path << ep->d_name
  298. << " has no ladspa_descriptor function" << endl;
  299. cerr << "dleror() output:" << endl;
  300. cerr << dlerror() << endl;
  301. } else {
  302. // Got ladspa_descriptor, so we can now get plugin info
  303. library_added = false;
  304. unsigned long i = 0;
  305. desc = desc_func(i);
  306. while (desc) {
  307. // First, check that it's not a dupe
  308. if (m_IDLookup.find(desc->UniqueID) != m_IDLookup.end()) {
  309. unsigned long plugin_index;
  310. unsigned long library_index;
  311. cerr << "WARNING: Duplicated Plugin ID ("
  312. << desc->UniqueID << ") found:" << endl;
  313. plugin_index = m_IDLookup[desc->UniqueID];
  314. library_index = m_Plugins[plugin_index].LibraryIndex;
  315. path_index = m_Libraries[library_index].PathIndex;
  316. cerr << " Plugin " << m_Plugins[plugin_index].Index
  317. << " in library: " << m_Paths[path_index]
  318. << m_Libraries[library_index].Basename
  319. << " [First instance found]" << endl;
  320. cerr << " Plugin " << i << " in library: " << path << ep->d_name
  321. << " [Duplicate not added]" << endl;
  322. } else {
  323. if (CheckPlugin(desc_func)) {
  324. if (!path_added) {
  325. // Add path
  326. m_Paths.push_back(path);
  327. path_added = true;
  328. }
  329. if (!library_added) {
  330. // Add library info
  331. LibraryInfo li;
  332. li.PathIndex = path_index;
  333. li.Basename = ep->d_name;
  334. li.RefCount = 0;
  335. li.Handle = NULL;
  336. m_Libraries.push_back(li);
  337. // Add filename to lookup
  338. m_FilenameLookup[fullpath] = m_Libraries.size() - 1;
  339. library_added = true;
  340. }
  341. // Add plugin info
  342. PluginInfo pi;
  343. pi.LibraryIndex = m_Libraries.size() - 1;
  344. pi.Index = i;
  345. pi.Descriptor = NULL;
  346. m_Plugins.push_back(pi);
  347. // Add to index
  348. m_IDLookup[desc->UniqueID] = m_Plugins.size() - 1;
  349. // Add to ordered list
  350. PluginEntry pe;
  351. pe.UniqueID = desc->UniqueID;
  352. pe.Name = desc->Name;
  353. m_OrderedPluginList.push_back(pe);
  354. // Find number of input ports
  355. unsigned long in_port_count = 0;
  356. for (unsigned long p = 0; p < desc->PortCount; p++) {
  357. if (LADSPA_IS_PORT_INPUT(desc->PortDescriptors[p])) {
  358. in_port_count++;
  359. }
  360. }
  361. if (in_port_count > m_MaxInputPortCount) {
  362. m_MaxInputPortCount = in_port_count;
  363. }
  364. } else {
  365. cerr << "WARNING: Plugin " << desc->UniqueID << " not added" << endl;
  366. }
  367. }
  368. desc = desc_func(++i);
  369. }
  370. }
  371. dlclose(handle);
  372. }
  373. }
  374. }
  375. }
  376. closedir(dp);
  377. }
  378. }
  379. bool
  380. LADSPAInfo::CheckPlugin(const LADSPA_Descriptor_Function desc_func)
  381. {
  382. const LADSPA_Descriptor *desc;
  383. #define test(t, m) { \
  384. if (!(t)) { \
  385. cerr << m << endl; \
  386. return false; \
  387. } \
  388. }
  389. for (unsigned long i = 0; (desc = desc_func(i)) != NULL; i++) {
  390. test(!LADSPA_IS_REALTIME(desc->Properties), "WARNING: Plugin must run real time");
  391. test(desc->instantiate, "WARNING: Plugin has no instatiate function");
  392. test(desc->connect_port, "WARNING: Warning: Plugin has no connect_port funciton");
  393. test(desc->run, "WARNING: Plugin has no run function");
  394. test(!(desc->run_adding != 0 && desc->set_run_adding_gain == 0),
  395. "WARNING: Plugin has run_adding but no set_run_adding_gain");
  396. test(!(desc->run_adding == 0 && desc->set_run_adding_gain != 0),
  397. "WARNING: Plugin has set_run_adding_gain but no run_adding");
  398. test(desc->cleanup, "WARNING: Plugin has no cleanup function");
  399. test(!LADSPA_IS_INPLACE_BROKEN(desc->Properties),
  400. "WARNING: Plugin cannot use in place processing");
  401. test(desc->PortCount, "WARNING: Plugin has no ports");
  402. }
  403. return true;
  404. }
  405. LADSPA_Descriptor_Function
  406. LADSPAInfo::GetDescriptorFunctionForLibrary(unsigned long library_index)
  407. {
  408. LibraryInfo *li = &(m_Libraries[library_index]);
  409. if (!(li->Handle)) {
  410. // Need full path
  411. string fullpath = m_Paths[li->PathIndex];
  412. fullpath.append(li->Basename);
  413. li->Handle = dlopen(fullpath.c_str(), RTLD_NOW);
  414. if (!(li->Handle)) {
  415. // Plugin library changed since last path scan
  416. cerr << "WARNING: Plugin library " << fullpath << " cannot be loaded" << endl;
  417. cerr << "Rescan of plugins recommended" << endl;
  418. cerr << "dleror() output:" << endl;
  419. cerr << dlerror() << endl;
  420. return NULL;
  421. }
  422. }
  423. // Got handle so now verify that it's a LADSPA plugin library
  424. const LADSPA_Descriptor_Function desc_func = (LADSPA_Descriptor_Function)dlsym(li->Handle,
  425. "ladspa_descriptor");
  426. if (!desc_func) {
  427. // Is DLL, but not a LADSPA one (changed since last path scan?)
  428. cerr << "WARNING: DLL " << m_Paths[li->PathIndex] << li->Basename
  429. << " has no ladspa_descriptor function" << endl;
  430. cerr << "Rescan of plugins recommended" << endl;
  431. cerr << "dleror() output:" << endl;
  432. cerr << dlerror() << endl;
  433. // Unload library
  434. dlclose(li->Handle);
  435. return NULL;
  436. }
  437. return desc_func;
  438. }