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.

574 lines
18KB

  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. #ifdef HAVE_LIBLRDF
  29. #include <lrdf.h>
  30. #endif
  31. #include "LADSPAInfo.h"
  32. using namespace std;
  33. LADSPAInfo::LADSPAInfo(bool override,
  34. const char *path_list)
  35. {
  36. if (strlen(path_list) > 0) {
  37. m_ExtraPaths = strdup(path_list);
  38. } else {
  39. m_ExtraPaths = NULL;
  40. }
  41. m_LADSPAPathOverride = override;
  42. #ifdef HAVE_LIBLRDF
  43. lrdf_init();
  44. #endif
  45. RescanPlugins();
  46. }
  47. LADSPAInfo::~LADSPAInfo()
  48. {
  49. CleanUp();
  50. }
  51. void
  52. LADSPAInfo::PrintInfo(void)
  53. {
  54. cout << "LADSPA Plugin Info" << endl;
  55. cout << endl;
  56. cout << " Paths:" << endl;
  57. for (vector<string>::iterator p = m_Paths.begin(); p != m_Paths.end(); p++) {
  58. cout << " " << *p << endl;
  59. }
  60. cout << endl;
  61. cout << " Libraries:" << endl;
  62. for (vector<LibraryInfo>::iterator l = m_Libraries.begin(); l != m_Libraries.end(); l++) {
  63. cout << " " << m_Paths[l->PathIndex] << l->Basename << ": " << "Refs: " << l->RefCount << endl;
  64. }
  65. cout << endl;
  66. cout << " Plugins:" << endl;
  67. for (vector<PluginInfo>::iterator p = m_Plugins.begin(); p != m_Plugins.end(); p++) {
  68. cout << " " << m_Paths[m_Libraries[p->LibraryIndex].PathIndex]
  69. << m_Libraries[p->LibraryIndex].Basename
  70. << ": " << p->Index << endl;
  71. }
  72. }
  73. void
  74. LADSPAInfo::RescanPlugins(void)
  75. {
  76. // Clear out what we've got
  77. CleanUp();
  78. if (!m_LADSPAPathOverride) {
  79. // Get $LADPSA_PATH, if available
  80. char *ladspa_path = getenv("LADSPA_PATH");
  81. if (!ladspa_path) {
  82. // Oops
  83. cerr << "WARNING: LADSPA_PATH environment variable not set" << endl;
  84. }
  85. // Extract path elements and add path
  86. if (ladspa_path) {
  87. ScanPathList(ladspa_path, &LADSPAInfo::ExaminePluginLibrary);
  88. }
  89. }
  90. // Check any supplied extra paths
  91. if (m_ExtraPaths) {
  92. ScanPathList(m_ExtraPaths, &LADSPAInfo::ExaminePluginLibrary);
  93. }
  94. // Do we have any plugins now?
  95. if (m_Plugins.size() == 0) {
  96. cerr << "WARNING: No plugins found" << endl;
  97. } else {
  98. cerr << m_Plugins.size() << " plugins found in " << m_Libraries.size() << " libraries" << endl;
  99. #ifdef HAVE_LIBLRDF
  100. // Got some plugins. Now search for RDF data
  101. char *rdf_path = getenv("LADSPA_RDF_PATH");
  102. if (!rdf_path) {
  103. cerr << "WARNING: LADSPA_RDF_PATH environment variable set" << endl;
  104. } else {
  105. // Examine rdf info
  106. ScanPathList(rdf_path, &LADSPAInfo::ExamineRDFFile);
  107. }
  108. #endif
  109. }
  110. // Sort list by name
  111. sort(m_OrderedPluginList.begin(), m_OrderedPluginList.end(), PluginEntrySortAsc());
  112. // Deal with duplicates by numbering them
  113. for (vector<PluginEntry>::iterator i = m_OrderedPluginList.begin();
  114. i != m_OrderedPluginList.end(); ) {
  115. string name = i->Name;
  116. i++;
  117. unsigned long n = 2;
  118. while ((i != m_OrderedPluginList.end()) && (i->Name == name)) {
  119. stringstream s;
  120. s << n;
  121. i->Name = name + " (" + s.str() + ")";
  122. n++;
  123. i++;
  124. }
  125. }
  126. }
  127. void
  128. LADSPAInfo::UnloadAllLibraries(void)
  129. {
  130. for (vector<LibraryInfo>::iterator i = m_Libraries.begin();
  131. i != m_Libraries.end(); i++) {
  132. if (i->Handle) dlclose(i->Handle);
  133. i->RefCount = 0;
  134. }
  135. }
  136. const LADSPA_Descriptor *
  137. LADSPAInfo::GetDescriptorByID(unsigned long unique_id)
  138. {
  139. if (m_IDLookup.find(unique_id) == m_IDLookup.end()) {
  140. cerr << "LADSPA Plugin ID " << unique_id << " not found!" << endl;
  141. return NULL;
  142. }
  143. // Got plugin index
  144. unsigned long plugin_index = m_IDLookup[unique_id];
  145. PluginInfo *pi = &(m_Plugins[plugin_index]);
  146. LibraryInfo *li = &(m_Libraries[pi->LibraryIndex]);
  147. if (!(pi->Descriptor)) {
  148. LADSPA_Descriptor_Function desc_func = GetDescriptorFunctionForLibrary(pi->LibraryIndex);
  149. if (desc_func) pi->Descriptor = desc_func(pi->Index);
  150. }
  151. if (pi->Descriptor) {
  152. // Success, so increment ref counter for library
  153. li->RefCount++;
  154. }
  155. return pi->Descriptor;
  156. }
  157. void
  158. LADSPAInfo::DiscardDescriptorByID(unsigned long unique_id)
  159. {
  160. if (m_IDLookup.find(unique_id) == m_IDLookup.end()) {
  161. cerr << "LADSPA Plugin ID " << unique_id << " not found!" << endl;
  162. } else {
  163. // Get plugin index
  164. unsigned long plugin_index = m_IDLookup[unique_id];
  165. PluginInfo *pi = &(m_Plugins[plugin_index]);
  166. LibraryInfo *li = &(m_Libraries[pi->LibraryIndex]);
  167. // Decrement reference counter for library, and unload if last
  168. if (li->RefCount > 0) {
  169. li->RefCount--;
  170. if (li->RefCount == 0) {
  171. // Unload and clear library handle
  172. dlclose(li->Handle);
  173. li->Handle = NULL;
  174. }
  175. }
  176. }
  177. }
  178. unsigned long
  179. LADSPAInfo::GetIDFromFilenameAndLabel(std::string filename,
  180. std::string label)
  181. {
  182. bool library_loaded = false;
  183. if (m_FilenameLookup.find(filename) == m_FilenameLookup.end()) {
  184. cerr << "LADSPA Library " << filename << " not found!" << endl;
  185. return 0;
  186. }
  187. unsigned long library_index = m_FilenameLookup[filename];
  188. if (!(m_Libraries[library_index].Handle)) library_loaded = true;
  189. LADSPA_Descriptor_Function desc_func = GetDescriptorFunctionForLibrary(library_index);
  190. if (!desc_func) {
  191. return 0;
  192. }
  193. // Search for label in library
  194. const LADSPA_Descriptor *desc;
  195. for (unsigned long i = 0; (desc = desc_func(i)) != NULL; i++) {
  196. string l = desc->Label;
  197. if (l == label) {
  198. // If we had to load the library, unload it
  199. unsigned long id = desc->UniqueID;
  200. if (library_loaded) {
  201. dlclose(m_Libraries[library_index].Handle);
  202. m_Libraries[library_index].Handle = NULL;
  203. }
  204. return id;
  205. }
  206. }
  207. cerr << "Plugin " << label << " not found in library " << filename << endl;
  208. return 0;
  209. }
  210. const vector<LADSPAInfo::PluginEntry>
  211. LADSPAInfo::GetPluginList(void)
  212. {
  213. return m_OrderedPluginList;
  214. }
  215. unsigned long
  216. LADSPAInfo::GetPluginListEntryByID(unsigned long unique_id)
  217. {
  218. unsigned long j = 0;
  219. for (vector<PluginEntry>::iterator i = m_OrderedPluginList.begin();
  220. i != m_OrderedPluginList.end(); i++, j++) {
  221. if (i->UniqueID == unique_id) return j;
  222. }
  223. return m_OrderedPluginList.size();
  224. }
  225. // ****************************************************************************
  226. // ** Private Member Functions **
  227. // ****************************************************************************
  228. // Unload any loaded DLLs and clear vectors etc
  229. void
  230. LADSPAInfo::CleanUp(void)
  231. {
  232. m_IDLookup.clear();
  233. m_FilenameLookup.clear();
  234. m_Plugins.clear();
  235. // Unload loaded dlls
  236. for (vector<LibraryInfo>::iterator i = m_Libraries.begin();
  237. i != m_Libraries.end(); i++) {
  238. if (i->Handle) dlclose(i->Handle);
  239. }
  240. m_Libraries.clear();
  241. m_Paths.clear();
  242. #ifdef HAVE_LIBLRDF
  243. lrdf_cleanup();
  244. #endif
  245. m_OrderedPluginList.clear();
  246. m_MaxInputPortCount = 0;
  247. if (m_ExtraPaths) {
  248. free(m_ExtraPaths);
  249. m_ExtraPaths = NULL;
  250. }
  251. }
  252. // Given a colon-separated list of paths, examine the contents of each
  253. // path, examining any regular files using the given member function,
  254. // which currently can be:
  255. //
  256. // ExaminePluginLibrary - add plugin library info from plugins
  257. // ExamineRDFFile - add plugin information from .rdf/.rdfs files
  258. void
  259. LADSPAInfo::ScanPathList(const char *path_list,
  260. void (LADSPAInfo::*ExamineFunc)(const string,
  261. const string))
  262. {
  263. const char *start;
  264. const char *end;
  265. int extra;
  266. char *path;
  267. string basename;
  268. DIR *dp;
  269. struct dirent *ep;
  270. struct stat sb;
  271. // This does the same kind of thing as strtok, but strtok won't
  272. // like the const
  273. start = path_list;
  274. while (*start != '\0') {
  275. while (*start == ':') start++;
  276. end = start;
  277. while (*end != ':' && *end != '\0') end++;
  278. if (end - start > 0) {
  279. extra = (*(end - 1) == '/') ? 0 : 1;
  280. path = (char *)malloc(end - start + 1 + extra);
  281. if (path) {
  282. strncpy(path, start, end - start);
  283. if (extra == 1) path[end - start] = '/';
  284. path[end - start + extra] = '\0';
  285. dp = opendir(path);
  286. if (!dp) {
  287. cerr << "WARNING: Could not open path " << path << endl;
  288. } else {
  289. while ((ep = readdir(dp))) {
  290. // Stat file to get type
  291. basename = ep->d_name;
  292. if (!stat((path + basename).c_str(), &sb)) {
  293. // We only want regular files
  294. if (S_ISREG(sb.st_mode)) (*this.*ExamineFunc)(path, basename);
  295. }
  296. }
  297. closedir(dp);
  298. }
  299. free(path);
  300. }
  301. }
  302. start = end;
  303. }
  304. }
  305. // Check given file is a valid LADSPA Plugin library
  306. //
  307. // If so, add path, library and plugin info
  308. // to the m_Paths, m_Libraries and m_Plugins vectors.
  309. void
  310. LADSPAInfo::ExaminePluginLibrary(const string path,
  311. const string basename)
  312. {
  313. void *handle;
  314. LADSPA_Descriptor_Function desc_func;
  315. const LADSPA_Descriptor *desc;
  316. string fullpath = path + basename;
  317. // We're not fussed about resolving symbols yet, since we are just
  318. // checking libraries.
  319. handle = dlopen(fullpath.c_str(), RTLD_LAZY);
  320. if (!handle) {
  321. cerr << "WARNING: File " << fullpath
  322. << " could not be examined" << endl;
  323. cerr << "dlerror() output:" << endl;
  324. cerr << dlerror() << endl;
  325. } else {
  326. // It's a DLL, so now see if it's a LADSPA plugin library
  327. desc_func = (LADSPA_Descriptor_Function)dlsym(handle,
  328. "ladspa_descriptor");
  329. if (!desc_func) {
  330. // Is DLL, but not a LADSPA one
  331. cerr << "WARNING: DLL " << fullpath
  332. << " has no ladspa_descriptor function" << endl;
  333. cerr << "dlerror() output:" << endl;
  334. cerr << dlerror() << endl;
  335. } else {
  336. // Got ladspa_descriptor, so we can now get plugin info
  337. bool library_added = false;
  338. unsigned long i = 0;
  339. desc = desc_func(i);
  340. while (desc) {
  341. // First, check that it's not a dupe
  342. if (m_IDLookup.find(desc->UniqueID) != m_IDLookup.end()) {
  343. unsigned long plugin_index = m_IDLookup[desc->UniqueID];
  344. unsigned long library_index = m_Plugins[plugin_index].LibraryIndex;
  345. unsigned long path_index = m_Libraries[library_index].PathIndex;
  346. cerr << "WARNING: Duplicated Plugin ID ("
  347. << desc->UniqueID << ") found:" << endl;
  348. cerr << " Plugin " << m_Plugins[plugin_index].Index
  349. << " in library: " << m_Paths[path_index]
  350. << m_Libraries[library_index].Basename
  351. << " [First instance found]" << endl;
  352. cerr << " Plugin " << i << " in library: " << fullpath
  353. << " [Duplicate not added]" << endl;
  354. } else {
  355. if (CheckPlugin(desc)) {
  356. // Add path if not already added
  357. unsigned long path_index;
  358. vector<string>::iterator p = find(m_Paths.begin(), m_Paths.end(), path);
  359. if (p == m_Paths.end()) {
  360. path_index = m_Paths.size();
  361. m_Paths.push_back(path);
  362. } else {
  363. path_index = p - m_Paths.begin();
  364. }
  365. // Add library info if not already added
  366. if (!library_added) {
  367. LibraryInfo li;
  368. li.PathIndex = path_index;
  369. li.Basename = basename;
  370. li.RefCount = 0;
  371. li.Handle = NULL;
  372. m_Libraries.push_back(li);
  373. library_added = true;
  374. }
  375. // Add filename to lookup
  376. m_FilenameLookup[fullpath] = m_Libraries.size() - 1;
  377. // Add plugin info
  378. PluginInfo pi;
  379. pi.LibraryIndex = m_Libraries.size() - 1;
  380. pi.Index = i;
  381. pi.Descriptor = NULL;
  382. m_Plugins.push_back(pi);
  383. // Add to index
  384. m_IDLookup[desc->UniqueID] = m_Plugins.size() - 1;
  385. // Add to ordered list
  386. PluginEntry pe;
  387. pe.UniqueID = desc->UniqueID;
  388. pe.Name = desc->Name;
  389. m_OrderedPluginList.push_back(pe);
  390. // Find number of input ports
  391. unsigned long in_port_count = 0;
  392. for (unsigned long p = 0; p < desc->PortCount; p++) {
  393. if (LADSPA_IS_PORT_INPUT(desc->PortDescriptors[p])) {
  394. in_port_count++;
  395. }
  396. }
  397. if (in_port_count > m_MaxInputPortCount) {
  398. m_MaxInputPortCount = in_port_count;
  399. }
  400. } else {
  401. cerr << "WARNING: Plugin " << desc->UniqueID << " not added" << endl;
  402. }
  403. }
  404. desc = desc_func(++i);
  405. }
  406. }
  407. dlclose(handle);
  408. }
  409. }
  410. #ifdef HAVE_LIBLRDF
  411. // Examine given RDF plugin meta-data file
  412. void
  413. LADSPAInfo::ExamineRDFFile(const std::string path,
  414. const std::string basename)
  415. {
  416. string fileuri = "file://" + path + basename;
  417. if (lrdf_read_file(fileuri.c_str())) {
  418. cerr << "WARNING: File " << path + basename << " could not be parsed [Ignored]" << endl;
  419. }
  420. }
  421. #endif
  422. bool
  423. LADSPAInfo::CheckPlugin(const LADSPA_Descriptor *desc)
  424. {
  425. #define test(t, m) { \
  426. if (!(t)) { \
  427. cerr << m << endl; \
  428. return false; \
  429. } \
  430. }
  431. test(!LADSPA_IS_REALTIME(desc->Properties), "WARNING: Plugin must run real time");
  432. test(desc->instantiate, "WARNING: Plugin has no instatiate function");
  433. test(desc->connect_port, "WARNING: Warning: Plugin has no connect_port funciton");
  434. test(desc->run, "WARNING: Plugin has no run function");
  435. test(!(desc->run_adding != 0 && desc->set_run_adding_gain == 0),
  436. "WARNING: Plugin has run_adding but no set_run_adding_gain");
  437. test(!(desc->run_adding == 0 && desc->set_run_adding_gain != 0),
  438. "WARNING: Plugin has set_run_adding_gain but no run_adding");
  439. test(desc->cleanup, "WARNING: Plugin has no cleanup function");
  440. test(!LADSPA_IS_INPLACE_BROKEN(desc->Properties),
  441. "WARNING: Plugin cannot use in place processing");
  442. test(desc->PortCount, "WARNING: Plugin has no ports");
  443. return true;
  444. }
  445. LADSPA_Descriptor_Function
  446. LADSPAInfo::GetDescriptorFunctionForLibrary(unsigned long library_index)
  447. {
  448. LibraryInfo *li = &(m_Libraries[library_index]);
  449. if (!(li->Handle)) {
  450. // Need full path
  451. string fullpath = m_Paths[li->PathIndex];
  452. fullpath.append(li->Basename);
  453. li->Handle = dlopen(fullpath.c_str(), RTLD_NOW);
  454. if (!(li->Handle)) {
  455. // Plugin library changed since last path scan
  456. cerr << "WARNING: Plugin library " << fullpath << " cannot be loaded" << endl;
  457. cerr << "Rescan of plugins recommended" << endl;
  458. cerr << "dlerror() output:" << endl;
  459. cerr << dlerror() << endl;
  460. return NULL;
  461. }
  462. }
  463. // Got handle so now verify that it's a LADSPA plugin library
  464. const LADSPA_Descriptor_Function desc_func = (LADSPA_Descriptor_Function)dlsym(li->Handle,
  465. "ladspa_descriptor");
  466. if (!desc_func) {
  467. // Is DLL, but not a LADSPA one (changed since last path scan?)
  468. cerr << "WARNING: DLL " << m_Paths[li->PathIndex] << li->Basename
  469. << " has no ladspa_descriptor function" << endl;
  470. cerr << "Rescan of plugins recommended" << endl;
  471. cerr << "dlerror() output:" << endl;
  472. cerr << dlerror() << endl;
  473. // Unload library
  474. dlclose(li->Handle);
  475. return NULL;
  476. }
  477. return desc_func;
  478. }