diff --git a/SpiralSound/Plugins/LADSPAPlugin/LADSPAInfo.C b/SpiralSound/Plugins/LADSPAPlugin/LADSPAInfo.C new file mode 100644 index 0000000..41f78ea --- /dev/null +++ b/SpiralSound/Plugins/LADSPAPlugin/LADSPAInfo.C @@ -0,0 +1,469 @@ +/* + LADSPAInfo.C - Class for indexing information on LADSPA Plugins + + Copyright (C) 2002 Mike Rawes + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "LADSPAInfo.h" + +using namespace std; + +LADSPAInfo::LADSPAInfo() +{ + RescanPlugins(); +} + +LADSPAInfo::~LADSPAInfo() +{ + CleanUp(); +} + +void +LADSPAInfo::RescanPlugins(void) +{ + char *ladspa_path; + char *start; + char *end; + int extra; + char *temp; + +// Clear out what we've got + CleanUp(); + +// Get $LADPSA_PATH, if available + ladspa_path = getenv("LADSPA_PATH"); + if (!ladspa_path) { + + // Oops + cerr << "WARNING: No LADSPA Path Set" << endl; + } + +// Extract path elements and add path + if (ladspa_path) { + start = ladspa_path; + while (*start != '\0') { + while (*start == ':') start++; + end = start; + while (*end != ':' && *end != '\0') end++; + + if (end - start > 0) { + extra = (*(end - 1) == '/') ? 0 : 1; + temp = (char *)malloc(end - start + 1 + extra); + if (temp) { + strncpy(temp, start, end - start); + if (extra == 1) temp[end - start] = '/'; + temp[end - start + extra] = '\0'; + + ExaminePath(temp); + + free(temp); + } + } + start = end; + } + } + +// Do we have any plugins now? + if (m_Plugins.size() == 0) { + cerr << "WARNING: No plugins found in given LADSPA_PATH" << endl; + } else { + cerr << m_Plugins.size() << " plugins found in " << m_Libraries.size() << " libraries" << endl; + } + +// No last loaded library or plugin + m_LastLoadedLibraryIndex = m_Libraries.size(); + m_LastLoadedPluginIndex = m_Plugins.size(); + +// Sort list by name + sort(m_OrderedPluginList.begin(), m_OrderedPluginList.end(), PluginEntrySortAsc()); +} + +void +LADSPAInfo::UnloadAllLibraries(void) +{ + for (vector::iterator i = m_Libraries.begin(); + i != m_Libraries.end(); i++) { + if (i->Handle) dlclose(i->Handle); + } + +// No last loaded library or plugin + m_LastLoadedLibraryIndex = m_Libraries.size(); + m_LastLoadedPluginIndex = m_Plugins.size(); +} + +void +LADSPAInfo::UnloadLibraryByID(unsigned long unique_id) +{ + if (m_IDLookup.find(unique_id) == m_IDLookup.end()) { + cerr << "LADSPA Plugin ID " << unique_id << " not found!" << endl; + } else { + + // Get plugin index + unsigned long plugin_index = m_IDLookup[unique_id]; + UnloadLibraryByPlugin(plugin_index); + } +} + +const LADSPA_Descriptor * +LADSPAInfo::GetDescriptorByID(unsigned long unique_id, + bool unload_previous_library) +{ + if (m_IDLookup.find(unique_id) == m_IDLookup.end()) { + cerr << "LADSPA Plugin ID " << unique_id << " not found!" << endl; + return NULL; + } + +// Got plugin index + unsigned long plugin_index = m_IDLookup[unique_id]; + + PluginInfo *pi = &(m_Plugins[plugin_index]); + if (!(pi->Descriptor)) { + LADSPA_Descriptor_Function desc_func = GetDescriptorFunctionForLibrary(pi->LibraryIndex); + + if (desc_func) { + pi->Descriptor = desc_func(pi->Index); + + // Unload previously loaded library (if different) + if ((m_LastLoadedLibraryIndex != m_Libraries.size()) && + (m_LastLoadedLibraryIndex != pi->LibraryIndex)) { + UnloadLibraryByPlugin(plugin_index); + } + } + } + return pi->Descriptor; +} + +unsigned long +LADSPAInfo::GetIDFromFilenameAndLabel(std::string filename, + std::string label) +{ + bool library_loaded = false; + + if (m_FilenameLookup.find(filename) == m_FilenameLookup.end()) { + cerr << "LADSPA Library " << filename << " not found!" << endl; + return 0; + } + + unsigned long library_index = m_FilenameLookup[filename]; + + if (!(m_Libraries[library_index].Handle)) library_loaded = true; + + LADSPA_Descriptor_Function desc_func = GetDescriptorFunctionForLibrary(library_index); + + if (!desc_func) { + return 0; + } + +// Search for label in library + const LADSPA_Descriptor *desc; + for (unsigned long i = 0; (desc = desc_func(i)) != NULL; i++) { + string l = desc->Label; + if (l == label) { + // If we had to load the library, unload it + unsigned long id = desc->UniqueID; + if (library_loaded) { + dlclose(m_Libraries[library_index].Handle); + m_Libraries[library_index].Handle = NULL; + } + return id; + } + } + + cerr << "Plugin " << label << " not found in library " << filename << endl; + return 0; +} + +const vector +LADSPAInfo::GetPluginList(void) +{ + return m_OrderedPluginList; +} + +// **************************************************************************** +// ** Private Member Functions ** +// **************************************************************************** + +// Unload any loaded DLLs and clear vectors etc +void +LADSPAInfo::CleanUp(void) +{ + m_IDLookup.clear(); + m_Plugins.clear(); + +// Unload loaded dlls + for (vector::iterator i = m_Libraries.begin(); + i != m_Libraries.end(); i++) { + if (i->Handle) dlclose(i->Handle); + } + + m_Libraries.clear(); + m_Paths.clear(); + + m_OrderedPluginList.clear(); + m_MaxInputPortCount = 0; +} + +// Check given path: +// 1. Exists +// 2. Is a directory +// 3. Contains at least one LADSPA Plugin library +// +// If so, add the path to the Paths vector, and examine +// contents, adding all valid LADSPA plugin library info +// to the Libraries and Plugins vectors. +void +LADSPAInfo::ExaminePath(const char *path) +{ + DIR *dp; + struct dirent *ep; + void *handle; + LADSPA_Descriptor_Function desc_func; + const LADSPA_Descriptor *desc; + bool path_added = false; + bool library_added; + string fullpath; + + dp = opendir(path); + if (!dp) { + cerr << "WARNING: Could not open path " << path << endl; + } else { + while ((ep = readdir(dp))) { + + // Ignore ./ and ../ + if (!((strlen(ep->d_name) == 2 && ep->d_name[0] == '.' && ep->d_name[1] == '.') || + (strlen(ep->d_name) == 1 && ep->d_name[0] == '.'))) { + + // Need full path + fullpath = path; + fullpath.append(ep->d_name); + + // We're not fussed about resolving symbols yet, since we are just + // checking libraries. + handle = dlopen(fullpath.c_str(), RTLD_LAZY); + + if (!handle) { + cerr << "WARNING: File " << path << ep->d_name + << " could not be examined" << endl; + cerr << "dleror() output:" << endl; + cerr << dlerror() << endl; + } else { + + // It's a DLL, so now see if it's a LADSPA plugin library + desc_func = (LADSPA_Descriptor_Function)dlsym(handle, + "ladspa_descriptor"); + if (!desc_func) { + + // Is DLL, but not a LADSPA one + cerr << "WARNING: DLL " << path << ep->d_name + << " has no ladspa_descriptor function" << endl; + cerr << "dleror() output:" << endl; + cerr << dlerror() << endl; + } else { + + // Got ladspa_descriptor, so we can now get plugin info + library_added = false; + unsigned long i = 0; + desc = desc_func(i); + while (desc) { + + // First, check that it's not a dupe + if (m_IDLookup.find(desc->UniqueID) != m_IDLookup.end()) { + unsigned long plugin_index; + unsigned long library_index; + unsigned long path_index; + + cerr << "WARNING: Duplicated Plugin ID (" + << desc->UniqueID << ") found:" << endl; + + plugin_index = m_IDLookup[desc->UniqueID]; + library_index = m_Plugins[plugin_index].LibraryIndex; + path_index = m_Libraries[library_index].PathIndex; + + cerr << " Plugin " << m_Plugins[plugin_index].Index + << " in library: " << m_Paths[path_index] + << m_Libraries[library_index].Basename + << " [First instance found]" << endl; + cerr << " Plugin " << i << " in library: " << path << ep->d_name + << " [Duplicate not added]" << endl; + } else { + if (CheckPlugin(desc_func)) { + if (!path_added) { + + // Add path + m_Paths.push_back(path); + path_added = true; + } + if (!library_added) { + + // Add library info + LibraryInfo li; + li.PathIndex = m_Paths.size() - 1; + li.Basename = ep->d_name; + li.Handle = NULL; + m_Libraries.push_back(li); + + // Add filename to lookup + m_FilenameLookup[fullpath] = m_Libraries.size() - 1; + + library_added = true; + } + + // Add plugin info + PluginInfo pi; + pi.LibraryIndex = m_Libraries.size() - 1; + pi.Index = i; + pi.Descriptor = NULL; + m_Plugins.push_back(pi); + + // Add to index + m_IDLookup[desc->UniqueID] = m_Plugins.size() - 1; + + // Add to ordered list + PluginEntry pe; + pe.UniqueID = desc->UniqueID; + pe.Name = desc->Name; + m_OrderedPluginList.push_back(pe); + + // Find number of input ports + unsigned long in_port_count = 0; + for (unsigned long p = 0; p < desc->PortCount; p++) { + if (LADSPA_IS_PORT_INPUT(desc->PortDescriptors[p])) { + in_port_count++; + } + } + if (in_port_count > m_MaxInputPortCount) { + m_MaxInputPortCount = in_port_count; + } + } else { + cerr << "WARNING: Plugin " << desc->UniqueID << " not added" << endl; + } + } + + desc = desc_func(++i); + } + } + dlclose(handle); + } + } + } + closedir(dp); + } +} + +bool +LADSPAInfo::CheckPlugin(const LADSPA_Descriptor_Function desc_func) +{ + const LADSPA_Descriptor *desc; + +#define test(t, m) { \ + if (!(t)) { \ + cerr << m << endl; \ + return false; \ + } \ +} + for (unsigned long i = 0; (desc = desc_func(i)) != NULL; i++) { + test(!LADSPA_IS_REALTIME(desc->Properties), "WARNING: Plugin must run real time"); + test(desc->instantiate, "WARNING: Plugin has no instatiate function"); + test(desc->connect_port, "WARNING: Warning: Plugin has no connect_port funciton"); + test(desc->run, "WARNING: Plugin has no run function"); + test(!(desc->run_adding != 0 && desc->set_run_adding_gain == 0), + "WARNING: Plugin has run_adding but no set_run_adding_gain"); + test(!(desc->run_adding == 0 && desc->set_run_adding_gain != 0), + "WARNING: Plugin has set_run_adding_gain but no run_adding"); + test(desc->cleanup, "WARNING: Plugin has no cleanup function"); + test(!LADSPA_IS_INPLACE_BROKEN(desc->Properties), + "WARNING: Plugin cannot use in place processing"); + test(desc->PortCount, "WARNING: Plugin has no ports"); + } + return true; +} + +LADSPA_Descriptor_Function +LADSPAInfo::GetDescriptorFunctionForLibrary(unsigned long library_index) +{ + LibraryInfo *li = &(m_Libraries[library_index]); + if (!(li->Handle)) { + + // Need full path + string fullpath = m_Paths[li->PathIndex]; + fullpath.append(li->Basename); + + li->Handle = dlopen(fullpath.c_str(), RTLD_NOW); + if (!(li->Handle)) { + + // Plugin library changed since last path scan + cerr << "WARNING: Plugin library " << fullpath << " cannot be loaded" << endl; + cerr << "Rescan of plugins recommended" << endl; + cerr << "dleror() output:" << endl; + cerr << dlerror() << endl; + return NULL; + } + } + +// Got handle so now verify that it's a LADSPA plugin library + const LADSPA_Descriptor_Function desc_func = (LADSPA_Descriptor_Function)dlsym(li->Handle, + "ladspa_descriptor"); + if (!desc_func) { + + // Is DLL, but not a LADSPA one (changed since last path scan?) + cerr << "WARNING: DLL " << m_Paths[li->PathIndex] << li->Basename + << " has no ladspa_descriptor function" << endl; + cerr << "Rescan of plugins recommended" << endl; + cerr << "dleror() output:" << endl; + cerr << dlerror() << endl; + + // Unload library + dlclose(li->Handle); + return NULL; + } + + return desc_func; +} + +void +LADSPAInfo::UnloadLibraryByPlugin(unsigned long plugin_index) +{ +// Unload library corresponding to given plugin index + PluginInfo *pi = &(m_Plugins[plugin_index]); + LibraryInfo *li = &(m_Libraries[pi->LibraryIndex]); + if (li->Handle) dlclose(li->Handle); + +// Need to unset all plugin descriptors that may have been +// set from this library +// Plugins in library will be a contiguous block, so we +// just check each direction from given plugin + unsigned long i = plugin_index - 1; + while (m_Plugins[i].LibraryIndex == pi->LibraryIndex) { + m_Plugins[i--].Descriptor = NULL; + } + i = plugin_index + 1; + while (m_Plugins[i].LibraryIndex == pi->LibraryIndex) { + m_Plugins[i++].Descriptor = NULL; + } +} diff --git a/SpiralSound/Plugins/LADSPAPlugin/LADSPAInfo.h b/SpiralSound/Plugins/LADSPAPlugin/LADSPAInfo.h new file mode 100644 index 0000000..fc5a5b1 --- /dev/null +++ b/SpiralSound/Plugins/LADSPAPlugin/LADSPAInfo.h @@ -0,0 +1,121 @@ +/* + LADSPAInfo.h - Header file for LADSPA Plugin info class + + Copyright (C) 2002 Mike Rawes + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __ladspa_info_h__ +#define __ladspa_info_h__ + +#include +#include +#include +#include + +class LADSPAInfo +{ +public: + // Examine all valid paths in $LADSPA_PATH, and add + // basic plugin information for later lookup, instantiation + // and so on. + LADSPAInfo(); + + // Unload all loaded plugins and clean up + ~LADSPAInfo(); + + // Rescan all paths in $LADSPA_PATH, as per constructor + // This will also unload all libraries + void RescanPlugins(void); + + // Unload all dlopened libraries + void UnloadAllLibraries(void); + + // Unload library containing plugin id + void UnloadLibraryByID(unsigned long unique_id); + + // Get descriptor of plugin in list, loading library if necessary + // and optionally unloading previously loaded library (if different + // from library containing requested plugin. Phew!) + const LADSPA_Descriptor *GetDescriptorByID(unsigned long unique_id, + bool unload_previous_library); + + unsigned long GetIDFromFilenameAndLabel(std::string filename, + std::string label); + + struct PluginEntry + { + unsigned long UniqueID; + std::string Name; + }; + + const std::vector GetPluginList(void); + unsigned long GetMaxInputPortCount(void) { return m_MaxInputPortCount; } + +private: + void CleanUp(void); + void ExaminePath(const char *path); + bool CheckPlugin(LADSPA_Descriptor_Function desc_func); + LADSPA_Descriptor_Function GetDescriptorFunctionForLibrary(unsigned long library_index); + void UnloadLibraryByPlugin(unsigned long plugin_index); + + struct LibraryInfo + { + unsigned long PathIndex; // Index of path in m_Paths + std::string Basename; // Filename + void *Handle; // DLL Handle, NULL + }; + + struct PluginInfo + { + unsigned long LibraryIndex; // Index of library in m_Libraries + unsigned long Index; // Plugin index in library + const LADSPA_Descriptor *Descriptor; // Descriptor, NULL + }; + + typedef std::map > IDMap; + + typedef std::map > StringMap; + + // For sorting vectors of PluginEntries + struct PluginEntrySortAsc + { + bool operator()(const PluginEntry &begin, const PluginEntry &end) + { + return begin.Name < end.Name; + } + }; + + std::vector m_Paths; + std::vector m_Libraries; + std::vector m_Plugins; + + unsigned long m_LastLoadedLibraryIndex; + unsigned long m_LastLoadedPluginIndex; + + IDMap m_IDLookup; + StringMap m_FilenameLookup; + + std::vector m_OrderedPluginList; + + unsigned long m_MaxInputPortCount; +}; + +#endif // __ladspa_info_h__