/* SpiralSound * Copyleft (C) 2001 David Griffiths * * 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 "LADSPAPlugin.h" #include "LADSPAPluginGUI.h" #include #include #include #include "SpiralIcon.xpm" #include "utils.h" #include //////////////////////////////////////////// /* FIXME: No matter what, I can't let this as it!! */ static LADSPAPlugin * lg = NULL; void describePluginLibrary(const char * pcFullFilename, void * pvPluginHandle, LADSPA_Descriptor_Function pfDescriptorFunction) { const LADSPA_Descriptor * psDescriptor; long lIndex; unsigned long lPluginIndex; unsigned long lPortIndex; unsigned long lLength; LADSPA_PortRangeHintDescriptor iHintDescriptor; LADSPA_Data fBound; #define testcond(c,s) { \ if (!(c)) { \ cerr << (s); \ failure = 1; \ } \ } for (lIndex = 0; (psDescriptor = pfDescriptorFunction(lIndex)) != NULL; lIndex++) { int failure = 0; testcond(!LADSPA_IS_REALTIME(psDescriptor->Properties), "ERROR: PLUGIN MUST RUN REAL TIME.\n"); testcond(psDescriptor->instantiate, "ERROR: PLUGIN HAS NO INSTANTIATE FUNCTION.\n"); testcond(psDescriptor->connect_port, "ERROR: PLUGIN HAS NO CONNECT_PORT FUNCTION.\n"); testcond(psDescriptor->run, "ERROR: PLUGIN HAS NO RUN FUNCTION.\n"); testcond(!(psDescriptor->run_adding != 0 && psDescriptor->set_run_adding_gain == 0), "ERROR: PLUGIN HAS RUN_ADDING FUNCTION BUT NOT SET_RUN_ADDING_GAIN.\n"); testcond(!(psDescriptor->run_adding == 0 && psDescriptor->set_run_adding_gain != 0), "ERROR: PLUGIN HAS SET_RUN_ADDING_GAIN FUNCTION BUT NOT RUN_ADDING.\n"); testcond(psDescriptor->cleanup, "ERROR: PLUGIN HAS NO CLEANUP FUNCTION.\n"); testcond(!LADSPA_IS_INPLACE_BROKEN(psDescriptor->Properties), "ERROR: THIS PLUGIN CANNOT USE IN-PLACE PROCESSING.\n"); testcond(psDescriptor->PortCount, "ERROR: PLUGIN HAS NO PORTS.\n"); if (!failure) { LPluginInfo pi; pi.Filename = pcFullFilename; pi.Label = psDescriptor->Label; pi.Name = psDescriptor->Name; pi.InputPortCount = getPortCountByType(psDescriptor, LADSPA_PORT_INPUT); // ARGH! I really can't stand this ugly hack lg->m_LADSPAList.push_back(pi); } else { cerr << "Plugin ignored...\n\n"; } } dlclose(pvPluginHandle); } void LADSPAPlugin::LoadPluginList(void) { m_LADSPAList.clear(); m_CurrentPlugin.Name = ""; m_CurrentPlugin.Filename = ""; m_CurrentPlugin.Label = ""; lg = this; LADSPAPluginSearch(describePluginLibrary); lg = NULL; sort(m_LADSPAList.begin(), m_LADSPAList.end(), LPluginInfoSortAsc()); } //////////////////////////////////////////////////// extern "C" { SpiralPlugin* CreateInstance() { return new LADSPAPlugin; } char** GetIcon() { return SpiralIcon_xpm; } int GetID() { return 0x0016; } } /////////////////////////////////////////////////////// LADSPAPlugin::LADSPAPlugin() : PlugHandle(0), PlugDesc(NULL), m_Gain(1.0f), m_Amped(false) { m_Version=3; m_PluginInfo.Name="LADSPA"; m_PluginInfo.Width=600; m_PluginInfo.Height=300; m_PluginInfo.NumInputs=0; m_PluginInfo.NumOutputs=1; m_PluginInfo.PortTips.push_back("Nuffink yet"); m_MaxInputPortCount = 0; m_InputPortCount = 0; LoadPluginList(); // Examine plugin list and find highest input port count for (vector::iterator i = m_LADSPAList.begin(); i != m_LADSPAList.end(); i++) { if ((*i).InputPortCount > m_MaxInputPortCount) m_MaxInputPortCount = (*i).InputPortCount; } // For receiving from GUI m_AudioCH->Register("SetGain",&(m_InData.Gain)); m_AudioCH->Register("SetAmped",&(m_InData.Amped)); m_AudioCH->RegisterData("SetPluginIndex", ChannelHandler::INPUT,&(m_InData.PluginIndex),sizeof(m_InData.PluginIndex)); // For sending to GUI m_AudioCH->RegisterData("GetName",ChannelHandler::OUTPUT,m_Name,256); m_AudioCH->RegisterData("GetMaker",ChannelHandler::OUTPUT,m_Maker,256); m_AudioCH->RegisterData("GetMaxInputPortCount",ChannelHandler::OUTPUT,&(m_MaxInputPortCount),sizeof(m_MaxInputPortCount)); m_AudioCH->RegisterData("GetInputPortCount",ChannelHandler::OUTPUT,&(m_InputPortCount),sizeof(m_InputPortCount)); m_OutData.InputPortNames = (char *)malloc(256 * m_MaxInputPortCount); m_OutData.InputPortRanges = (PortRange *)malloc(sizeof(PortRange) * m_MaxInputPortCount); m_InData.InputPortRanges = (PortRange *)malloc(sizeof(PortRange) * m_MaxInputPortCount); if (m_OutData.InputPortNames && m_OutData.InputPortRanges && m_InData.InputPortRanges) { m_AudioCH->RegisterData("GetInputPortNames", ChannelHandler::OUTPUT, m_OutData.InputPortNames, 256 * m_MaxInputPortCount); m_AudioCH->RegisterData("GetInputPortRanges", ChannelHandler::OUTPUT, m_OutData.InputPortRanges, sizeof(PortRange) * m_MaxInputPortCount); m_AudioCH->RegisterData("SetInputPortRanges", ChannelHandler::INPUT, m_InData.InputPortRanges, sizeof(PortRange) * m_MaxInputPortCount); } else { cerr<<"Memory allocation error"<BUFSIZE]; m_LADSPABufVec.push_back(NewPort); return Info; } SpiralGUIType *LADSPAPlugin::CreateGUI() { return new LADSPAPluginGUI(m_PluginInfo.Width, m_PluginInfo.Height, this, m_AudioCH, m_HostInfo, m_LADSPAList); } void LADSPAPlugin::Execute() { if (PlugDesc) { // convert inputs if exist (zero if not) for (int n=0; nBUFSIZE; i++) { m_LADSPABufVec[n][i]=Offset+(GetInput(n,i)*0.5f+0.5f)*Scale; //cerr<BUFSIZE; i++) { m_LADSPABufVec[n][i]=GetInput(n,i); } } // Update the GUI outputs with the first value in the buffer //((LADSPAPluginGUI*)m_GUI)->UpdatePortDisplay(n,m_LADSPABufVec[n][0]); } else // zero { for (int i=0; iBUFSIZE; i++) m_LADSPABufVec[n][i]=0; } } // run plugin PlugDesc->run(PlugInstHandle,m_HostInfo->BUFSIZE); // convert outputs for (int n=0; nBUFSIZE; i++) { SetOutput(n,i,m_LADSPABufVec[n+m_PluginInfo.NumInputs][i]*m_Gain*10); } } else*/ { for (int i=0; iBUFSIZE; i++) { SetOutput(n,i,m_LADSPABufVec[n+m_PluginInfo.NumInputs][i]*m_Gain); } } } } } void LADSPAPlugin::ExecuteCommands() { if (m_AudioCH->IsCommandWaiting()) { switch(m_AudioCH->GetCommand()) { case (SETRANGES) : SetPortInfo(); break; case (SELECTPLUGIN) : UpdatePlugin(m_InData.PluginIndex); break; }; } } void LADSPAPlugin::StreamOut(ostream &s) { s<::iterator i=m_PortMin.begin(); i!=m_PortMin.end(); i++) { s<<*i<<" "; } for (vector::iterator i=m_PortMax.begin(); i!=m_PortMax.end(); i++) { s<<*i<<" "; } for (vector::iterator i=m_PortClamp.begin(); i!=m_PortClamp.end(); i++) { s<<*i<<" "; } } break; case 2: // Here for consistency - should never actually happen, as // version is always 3! { s<::iterator i=m_PortMin.begin(); i!=m_PortMin.end(); i++) { s<<*i<<" "; } for (vector::iterator i=m_PortMax.begin(); i!=m_PortMax.end(); i++) { s<<*i<<" "; } } break; case 1: { s<>version; switch (version) { case 3: { s>>m_Gain; string Filename,Label; s>>Filename>>Label; int PortCount; s>>PortCount; float min,max; bool clamp; for (int n=0; n>min; m_PortMin.push_back(min); } for (int n=0; n>max; m_PortMax.push_back(max); } for (int n=0; n>clamp; m_PortClamp.push_back(clamp); } if (Filename!="None") { UpdatePlugin(Filename.c_str(), Label.c_str(), false); } m_CurrentPlugin.Ports.reserve(PortCount); for (int n=0; n>m_Gain; string Filename,Label; s>>Filename>>Label; int PortCount; s>>PortCount; float min,max; for (int n=0; n>min; m_PortMin.push_back(min); } for (int n=0; n>max; m_PortMax.push_back(max); } for (int n=0; n>m_Gain; string Filename,Label; s>>Filename>>Label; if (Filename!="None") { UpdatePlugin(Filename.c_str(), Label.c_str()); } } break; } } bool LADSPAPlugin::UpdatePlugin(int n) { return UpdatePlugin(m_LADSPAList[n].Filename.c_str(),m_LADSPAList[n].Label.c_str()); } bool LADSPAPlugin::UpdatePlugin(const char * filename, const char * label, bool PortClampReset) { // first call with same info, to clear the ports UpdatePluginInfoWithHost(); if (PlugHandle) { if (PlugDesc->deactivate) PlugDesc->deactivate(PlugInstHandle); PlugDesc->cleanup(PlugInstHandle); unloadLADSPAPluginLibrary(PlugHandle); PlugHandle = 0; } if ((PlugHandle = loadLADSPAPluginLibrary(filename))) { if (!(PlugDesc = findLADSPAPluginDescriptor(PlugHandle, filename, label))) { unloadLADSPAPluginLibrary(PlugHandle); PlugHandle = 0; } else { /* Now we can instantiate the LADSPA Plugin and wire it to the datas bytes */ if (!(PlugInstHandle = PlugDesc->instantiate(PlugDesc, m_HostInfo->SAMPLERATE))) { cerr << "LADSPA Plugin error to instantiate...\n"; unloadLADSPAPluginLibrary(PlugHandle); PlugDesc = 0; PlugHandle = 0; return 0; } m_PluginInfo.NumInputs=getPortCountByType(PlugDesc, LADSPA_PORT_INPUT); m_PluginInfo.NumOutputs=getPortCountByType(PlugDesc, LADSPA_PORT_OUTPUT); ///////////////////////////////// // LADSPA Buffers for(vector::iterator i=m_LADSPABufVec.begin(); i!=m_LADSPABufVec.end(); i++) { if (*i) delete[] (*i); } m_LADSPABufVec.clear(); unsigned long c=0; for (unsigned int n=0; nPortCount; n++) { if (LADSPA_IS_PORT_INPUT(PlugDesc->PortDescriptors[n])) { LADSPA_Data *NewPort = new LADSPA_Data[m_HostInfo->BUFSIZE]; m_LADSPABufVec.push_back(NewPort); PlugDesc->connect_port(PlugInstHandle, n, m_LADSPABufVec[c]); m_PortID.push_back(n); c++; } } for (unsigned int n=0; nPortCount; n++) { if (LADSPA_IS_PORT_OUTPUT(PlugDesc->PortDescriptors[n])) { LADSPA_Data *NewPort = new LADSPA_Data[m_HostInfo->BUFSIZE]; m_LADSPABufVec.push_back(NewPort); PlugDesc->connect_port(PlugInstHandle, n, m_LADSPABufVec[c]); m_PortID.push_back(n); c++; } } // activate the plugin now if (PlugDesc->activate) PlugDesc->activate(PlugInstHandle); ///////////////////////////////// // SSM Buffers // Clear i/o buffers RemoveAllInputs(); RemoveAllOutputs(); // Reallocate the i/o buffers required for (int n=0; nName; m_CurrentPlugin.Maker=PlugDesc->Maker; m_CurrentPlugin.Filename=filename; m_CurrentPlugin.Label=label; m_CurrentPlugin.Ports.clear(); m_PluginInfo.PortTips.clear(); string desc; c=0; for (unsigned int i = 0; i < PlugDesc->PortCount; i++) { if (LADSPA_IS_PORT_INPUT(PlugDesc->PortDescriptors[i])) { desc = string(PlugDesc->PortNames[i]) + (LADSPA_IS_PORT_CONTROL(PlugDesc->PortDescriptors[i]) ? " (CV)" : " (AU)"); m_PluginInfo.PortTips.push_back(desc.c_str()); LPluginInfo::LPortDetails PortDetails; PortDetails.Name=m_PluginInfo.PortTips[c].c_str(); m_CurrentPlugin.Ports.push_back(PortDetails); c++; } } for (unsigned int i = 0; i < PlugDesc->PortCount; i++) { if (LADSPA_IS_PORT_OUTPUT(PlugDesc->PortDescriptors[i])) { desc = string(PlugDesc->PortNames[i]) + (LADSPA_IS_PORT_CONTROL(PlugDesc->PortDescriptors[i]) ? " (CV)" : " (AU)"); m_PluginInfo.PortTips.push_back(desc.c_str()); } } UpdatePluginInfoWithHost(); if (PortClampReset) { m_PortMin.clear(); m_PortMax.clear(); m_PortClamp.clear(); for (int n=0; nPortRangeHints[Port].HintDescriptor; if (LADSPA_IS_HINT_BOUNDED_BELOW(HintDesc)) { Min=PlugDesc->PortRangeHints[Port].LowerBound; if (LADSPA_IS_HINT_SAMPLE_RATE(HintDesc)) { Min*=m_HostInfo->SAMPLERATE; } } if (LADSPA_IS_HINT_BOUNDED_ABOVE(HintDesc)) { Max=PlugDesc->PortRangeHints[Port].UpperBound; if (LADSPA_IS_HINT_SAMPLE_RATE(HintDesc)) { Max*=m_HostInfo->SAMPLERATE; } } m_PortMin.push_back(Min); m_PortMax.push_back(Max); // PortClamp defaults to true m_PortClamp.push_back(true); m_CurrentPlugin.Ports[n].Min=Min; m_CurrentPlugin.Ports[n].Max=Max; m_CurrentPlugin.Ports[n].Clamped=true; } } m_InputPortCount = m_PluginInfo.NumInputs; int lbl_length; char *lbl_start; lbl_length = m_CurrentPlugin.Name.size(); lbl_length = lbl_length > 255 ? 255 : lbl_length; strncpy(m_Name, m_CurrentPlugin.Name.substr(0, lbl_length).c_str(), lbl_length); m_Name[lbl_length] = '\0'; lbl_length = m_CurrentPlugin.Maker.size(); lbl_length = lbl_length > 255 ? 255 : lbl_length; strncpy(m_Maker, m_CurrentPlugin.Maker.substr(0, lbl_length).c_str(), lbl_length); m_Maker[lbl_length] = '\0'; lbl_start = m_OutData.InputPortNames; for (unsigned long n = 0; n < m_InputPortCount; n++) { lbl_length = m_CurrentPlugin.Ports[n].Name.size(); lbl_length = lbl_length > 255 ? 255 : lbl_length; strncpy(lbl_start, m_CurrentPlugin.Ports[n].Name.substr(0, lbl_length).c_str(), lbl_length); lbl_start[lbl_length] = '\0'; lbl_start += 256; m_OutData.InputPortRanges[n].Min = m_CurrentPlugin.Ports[n].Min; m_OutData.InputPortRanges[n].Max = m_CurrentPlugin.Ports[n].Max; m_OutData.InputPortRanges[n].Clamp = m_CurrentPlugin.Ports[n].Clamped; } return true; } } cerr << "Error loading LADSPA Plugin.\n"; return false; } void LADSPAPlugin::SetPortInfo(void) { for (unsigned long n = 0; n < m_InputPortCount; n++) { m_PortMin[n] = m_InData.InputPortRanges[n].Min; m_PortMax[n] = m_InData.InputPortRanges[n].Max; m_PortClamp[n] = m_InData.InputPortRanges[n].Clamp; } }