/*******************************************************************************/ /* Copyright (C) 2009 Jonathan Moore Liles */ /* */ /* 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; see the file COPYING. If not,write to the Free Software */ /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*******************************************************************************/ /* Filter module. Can host LADPSA Plugins, or can be inherited from to make internal modules with special features and appearance. */ #include "Plugin_Module.H" #include #include #include "util/debug.h" #include #define HAVE_LIBLRDF 1 #include "LADSPAInfo.h" #include #include #include #include #include #include "Engine/Engine.H" static LADSPAInfo *ladspainfo; /* keep this out of the header to avoid spreading ladspa.h dependency */ struct Plugin_Module::ImplementationData { const LADSPA_Descriptor *descriptor; // std::vector m_LADSPABufVec; LADSPA_Handle handle; }; Plugin_Module::Plugin_Module ( int , int , const char *L ) : Module( 50, 50, L ) { init(); end(); } Plugin_Module::~Plugin_Module ( ) { } /* void */ /* Plugin_Module::detect_plugins ( void ) */ /* { */ /* LADPSAInfo *li = new LADSPAInfo(); */ /* } */ #include /* allow the user to pick a plugin */ Plugin_Module * Plugin_Module::pick_plugin ( void ) { /**************/ /* build menu */ /**************/ Fl_Menu_Button *menu = new Fl_Menu_Button( 0, 0, 400, 400 ); menu->type( Fl_Menu_Button::POPUP3 ); Plugin_Module::Plugin_Info *pia = Plugin_Module::discover(); for ( Plugin_Module::Plugin_Info *pi = pia; pi->path; ++pi ) { menu->add(pi->path, 0, NULL, pi, 0 ); } menu->popup(); if ( menu->value() <= 0 ) return NULL; /************************/ /* load selected plugin */ /************************/ Plugin_Module::Plugin_Info *pi = (Plugin_Module::Plugin_Info*)menu->menu()[ menu->value() ].user_data(); Plugin_Module *m = new Plugin_Module( 50, 50 ); // Plugin_Module *plugin = new Plugin_Module(); m->load( pi ); const char *plugin_name = pi->path; char *label = strdup( rindex(plugin_name, '/') + 1 ); m->label( label ); delete[] pia; return m; } void Plugin_Module::init ( void ) { _idata = new Plugin_Module::ImplementationData(); _active = false; _crosswire = true; _instances = 1; // box( FL_ROUNDED_BOX ); // box( FL_NO_BOX ); align( (Fl_Align)FL_ALIGN_CENTER | FL_ALIGN_INSIDE ); color( (Fl_Color)fl_color_average( FL_BLUE, FL_GREEN, 0.5f ) ); int tw, th, tx, ty; bbox( tx, ty, tw, th ); } #include "FL/test_press.H" int Plugin_Module::handle ( int m ) { switch ( m ) { case FL_ENTER: case FL_LEAVE: redraw(); return 1; break; default: return Module::handle( m ); } return 0; } /* There are two possible adaptations that can be made at Plugin_Module input to account for a mismatch between channel configurations. The two scenarios are as follows. 1. The preceding module has fewer outputs than this module has inputs. If the preceding module has 1 output (MONO) then it will be duplicated for this module's addition inputs. If the preceding module has more than one output, then the chain is in error. 2. The preceding module has more outputs than this module has inputs If this module has 1 output (MONO) then it will create the required number of instances of its plugin. Stereo plugins are never run with more than one instance. Mono plugins will have their outputs brought up to stereo for plugins with stereo input. */ int Plugin_Module::can_support_inputs ( int n ) { /* this is the simple case */ if ( plugin_ins() == n ) return plugin_outs(); /* e.g. MONO going into STEREO */ /* we'll duplicate our inputs */ else if ( n < plugin_ins() && 1 == n ) { return plugin_outs(); } /* e.g. STEREO going into MONO */ /* we'll run multiple instances of the plugin */ else if ( n > plugin_ins() && plugin_ins() == 1 && plugin_outs() == 1 ) { return plugin_outs() * n; // instances( i ); } return -1; } bool Plugin_Module::configure_inputs( int n ) { if ( 1 == n && plugin_ins() > 1 ) { _crosswire = true; audio_input.clear(); audio_input.push_back( Port( this, Port::INPUT, Port::AUDIO ) ); } /* audio_input.clear(); */ /* audio_output.clear(); */ /* for ( int i = 0; i < n; ++i ) */ /* { */ /* add_port( Port( Port::INPUT, Port::AUDIO ) ); */ /* } */ /* if ( n > plugin_ins() ) */ /* { */ /* /\* multiple instances *\/ */ /* instances( n / plugin_ins() ); */ /* } */ /* else if ( n < plugin_ins() ) */ /* { */ /* /\* duplication of input *\/ */ /* } */ /* for ( int i = 0; i < plugin_outs() * instances(); ++i ) */ /* { */ /* add_port( Port( this, Port::OUTPUT, Port::AUDIO ) ); */ /* } */ if ( ! _active ) activate(); /* // _plugin->deactivate(); */ /* /\* if ( _plugin->active() ) *\/ */ /* /\* _plugin->activate(); *\/ */ /* FIXME: do controls */ return true; } /* return a list of available plugins */ Plugin_Module::Plugin_Info * Plugin_Module::discover ( void ) { if ( !ladspainfo ) ladspainfo = new LADSPAInfo(); std::vector plugins = ladspainfo->GetMenuList(); Plugin_Info* pi = new Plugin_Info[plugins.size() + 1]; int j = 0; for (std::vector::iterator i=plugins.begin(); i!=plugins.end(); i++, j++) { pi[j].path = i->Name.c_str(); pi[j].id = i->UniqueID; } return pi; } bool Plugin_Module::load ( Plugin_Module::Plugin_Info *pi ) { _idata->descriptor = ladspainfo->GetDescriptorByID( pi->id ); _plugin_ins = _plugin_outs = 0; if ( _idata->descriptor ) { if ( LADSPA_IS_INPLACE_BROKEN( _idata->descriptor->Properties ) ) { WARNING( "Cannot use this plugin because it is incapable of processing audio in-place" ); return false; } else if ( ! LADSPA_IS_HARD_RT_CAPABLE( _idata->descriptor->Properties ) ) { WARNING( "Cannot use this plugin because it is incapable of hard real-time operation" ); return false; } /* FIXME: bogus rate */ if ( ! (_idata->handle = _idata->descriptor->instantiate( _idata->descriptor, engine->sample_rate() ) ) ) { WARNING( "Failed to load plugin" ); return false; } // _idata->descriptor->activate( _idata->handle ); MESSAGE( "Name: %s", _idata->descriptor->Name ); for ( unsigned int i = 0; i < _idata->descriptor->PortCount; ++i ) { if ( LADSPA_IS_PORT_AUDIO( _idata->descriptor->PortDescriptors[i] ) ) { if ( LADSPA_IS_PORT_INPUT( _idata->descriptor->PortDescriptors[i] ) ) { add_port( Port( this, Port::INPUT, Port::AUDIO, _idata->descriptor->PortNames[ i ] ) ); _plugin_ins++; } else if (LADSPA_IS_PORT_OUTPUT(_idata->descriptor->PortDescriptors[i])) { _plugin_outs++; add_port( Port( this, Port::OUTPUT, Port::AUDIO, _idata->descriptor->PortNames[ i ] ) ); } } } MESSAGE( "Plugin has %i inputs and %i outputs", _plugin_ins, _plugin_outs); for ( unsigned int i = 0; i < _idata->descriptor->PortCount; ++i ) { if ( LADSPA_IS_PORT_CONTROL( _idata->descriptor->PortDescriptors[i] ) ) { Port::Direction d; if ( LADSPA_IS_PORT_INPUT( _idata->descriptor->PortDescriptors[i] ) ) { d = Port::INPUT; } else if ( LADSPA_IS_PORT_OUTPUT( _idata->descriptor->PortDescriptors[i] ) ) { d = Port::OUTPUT; } Port p( this, d, Port::CONTROL, _idata->descriptor->PortNames[ i ] ); LADSPA_PortRangeHintDescriptor hd = _idata->descriptor->PortRangeHints[i].HintDescriptor; if ( LADSPA_IS_HINT_BOUNDED_BELOW(hd) ) { p.hints.ranged = true; p.hints.minimum = _idata->descriptor->PortRangeHints[i].LowerBound; } if ( LADSPA_IS_HINT_BOUNDED_ABOVE(hd) ) { p.hints.ranged = true; p.hints.maximum = _idata->descriptor->PortRangeHints[i].UpperBound; } if ( LADSPA_IS_HINT_HAS_DEFAULT(hd) ) { float Max=1.0f, Min=-1.0f, Default=0.0f; int Port=i; // Get the bounding hints for the port LADSPA_PortRangeHintDescriptor HintDesc=_idata->descriptor->PortRangeHints[Port].HintDescriptor; if (LADSPA_IS_HINT_BOUNDED_BELOW(HintDesc)) { Min=_idata->descriptor->PortRangeHints[Port].LowerBound; if (LADSPA_IS_HINT_SAMPLE_RATE(HintDesc)) { Min*=engine->sample_rate(); } } if (LADSPA_IS_HINT_BOUNDED_ABOVE(HintDesc)) { Max=_idata->descriptor->PortRangeHints[Port].UpperBound; if (LADSPA_IS_HINT_SAMPLE_RATE(HintDesc)) { Max*=engine->sample_rate(); } } #ifdef LADSPA_VERSION // We've got a version of the header that supports port defaults if (LADSPA_IS_HINT_HAS_DEFAULT(HintDesc)) { // LADSPA_HINT_DEFAULT_0 is assumed anyway, so we don't check for it if (LADSPA_IS_HINT_DEFAULT_1(HintDesc)) { Default = 1.0f; } else if (LADSPA_IS_HINT_DEFAULT_100(HintDesc)) { Default = 100.0f; } else if (LADSPA_IS_HINT_DEFAULT_440(HintDesc)) { Default = 440.0f; } else { // These hints may be affected by SAMPLERATE, LOGARITHMIC and INTEGER if (LADSPA_IS_HINT_DEFAULT_MINIMUM(HintDesc) && LADSPA_IS_HINT_BOUNDED_BELOW(HintDesc)) { Default=_idata->descriptor->PortRangeHints[Port].LowerBound; } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(HintDesc) && LADSPA_IS_HINT_BOUNDED_ABOVE(HintDesc)) { Default=_idata->descriptor->PortRangeHints[Port].UpperBound; } else if (LADSPA_IS_HINT_BOUNDED_BELOW(HintDesc) && LADSPA_IS_HINT_BOUNDED_ABOVE(HintDesc)) { // These hints require both upper and lower bounds float lp = 0.0f, up = 0.0f; float min = _idata->descriptor->PortRangeHints[Port].LowerBound; float max = _idata->descriptor->PortRangeHints[Port].UpperBound; if (LADSPA_IS_HINT_DEFAULT_LOW(HintDesc)) { lp = 0.75f; up = 0.25f; } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(HintDesc)) { lp = 0.5f; up = 0.5f; } else if (LADSPA_IS_HINT_DEFAULT_HIGH(HintDesc)) { lp = 0.25f; up = 0.75f; } if (LADSPA_IS_HINT_LOGARITHMIC(HintDesc)) { p.hints.type = Port::Hints::LOGARITHMIC; if (min==0.0f || max==0.0f) { // Zero at either end means zero no matter // where hint is at, since: // log(n->0) -> Infinity Default = 0.0f; } else { // Catch negatives bool neg_min = min < 0.0f ? true : false; bool neg_max = max < 0.0f ? true : false; if (!neg_min && !neg_max) { Default = exp(log(min) * lp + log(max) * up); } else if (neg_min && neg_max) { Default = -exp(log(-min) * lp + log(-max) * up); } else { // Logarithmic range has asymptote // so just use linear scale Default = min * lp + max * up; } } } else { Default = min * lp + max * up; } } if (LADSPA_IS_HINT_SAMPLE_RATE(HintDesc)) { Default *= engine->sample_rate(); } if (LADSPA_IS_HINT_INTEGER(HintDesc)) { if ( p.hints.ranged && 0 == p.hints.minimum && 1 == p.hints.maximum ) p.hints.type = Port::Hints::BOOLEAN; else p.hints.type = Port::Hints::INTEGER; Default = floorf(Default); } if (LADSPA_IS_HINT_TOGGLED(HintDesc)){ p.hints.type = Port::Hints::BOOLEAN; } } } #else Default = 0.0f; #endif p.hints.default_value = Default; } float *control_value = new float; *control_value = p.hints.default_value; p.connect_to( control_value ); add_port( p ); _idata->descriptor->connect_port( _idata->handle, i, (LADSPA_Data*)control_input.back().buffer() ); DMESSAGE( "Plugin has control port \"%s\" (default: %f)", _idata->descriptor->PortNames[ i ], p.hints.default_value ); } } } else { WARNING( "Failed to load plugin" ); return false; } return true; } /* const char * */ /* Plugin_Module::name ( void ) const */ /* { */ /* return _idata->descriptor->Name; */ /* } */ void Plugin_Module::set_input_buffer ( int n, void *buf ) { for ( unsigned int i = 0; i < _idata->descriptor->PortCount; ++i ) if ( LADSPA_IS_PORT_INPUT( _idata->descriptor->PortDescriptors[i] ) && LADSPA_IS_PORT_AUDIO( _idata->descriptor->PortDescriptors[i] ) ) if ( n-- == 0 ) _idata->descriptor->connect_port( _idata->handle, i, (LADSPA_Data*)buf ); } void Plugin_Module::set_output_buffer ( int n, void *buf ) { for ( unsigned int i = 0; i < _idata->descriptor->PortCount; ++i ) if ( LADSPA_IS_PORT_OUTPUT( _idata->descriptor->PortDescriptors[i] ) && LADSPA_IS_PORT_AUDIO( _idata->descriptor->PortDescriptors[i] ) ) if ( n-- == 0 ) _idata->descriptor->connect_port( _idata->handle, i, (LADSPA_Data*)buf ); } void Plugin_Module::set_control_buffer ( int n, void *buf ) { for ( unsigned int i = 0; i < _idata->descriptor->PortCount; ++i ) if ( LADSPA_IS_PORT_INPUT( _idata->descriptor->PortDescriptors[i] ) && LADSPA_IS_PORT_CONTROL( _idata->descriptor->PortDescriptors[i] ) ) if ( n-- == 0 ) _idata->descriptor->connect_port( _idata->handle, i, (LADSPA_Data*)buf ); } void Plugin_Module::activate ( void ) { if ( _active ) FATAL( "Attempt to activate already active plugin" ); if ( _idata->descriptor->activate ) _idata->descriptor->activate( _idata->handle ); _active = true; } void Plugin_Module::deactivate( void ) { if ( _idata->descriptor->deactivate ) _idata->descriptor->deactivate( _idata->handle ); _active = false; } void Plugin_Module::process ( ) { if ( _crosswire ) { for ( int i = 0; i < plugin_ins(); ++i ) { set_input_buffer( i, audio_input[0].buffer() ); } } else { for ( unsigned int i = 0; i < audio_input.size(); ++i ) { set_input_buffer( i, audio_input[i].buffer() ); } } for ( unsigned int i = 0; i < audio_output.size(); ++i ) { set_output_buffer( i, audio_output[i].buffer() ); } if ( _active ) { _idata->descriptor->run( _idata->handle, nframes() ); } }