123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611 |
- /*
- * LadspaEffect.cpp - class for processing LADSPA effects
- *
- * Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
- * Copyright (c) 2009-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
- *
- * This file is part of LMMS - https://lmms.io
- *
- * 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 COPYING); if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- */
- #include <QtCore/QVarLengthArray>
- #include <QMessageBox>
- #include "LadspaEffect.h"
- #include "DataFile.h"
- #include "AudioDevice.h"
- #include "ConfigManager.h"
- #include "Ladspa2LMMS.h"
- #include "LadspaControl.h"
- #include "LadspaSubPluginFeatures.h"
- #include "Mixer.h"
- #include "EffectChain.h"
- #include "AutomationPattern.h"
- #include "ControllerConnection.h"
- #include "MemoryManager.h"
- #include "ValueBuffer.h"
- #include "Song.h"
- #include "embed.h"
- #include "plugin_export.h"
- extern "C"
- {
- Plugin::Descriptor PLUGIN_EXPORT ladspaeffect_plugin_descriptor =
- {
- STRINGIFY( PLUGIN_NAME ),
- "LADSPA",
- QT_TRANSLATE_NOOP( "PluginBrowser",
- "plugin for using arbitrary LADSPA-effects "
- "inside LMMS." ),
- "Danny McRae <khjklujn/at/users.sourceforge.net>",
- 0x0100,
- Plugin::Effect,
- new PluginPixmapLoader("logo"),
- NULL,
- new LadspaSubPluginFeatures( Plugin::Effect )
- } ;
- }
- LadspaEffect::LadspaEffect( Model * _parent,
- const Descriptor::SubPluginFeatures::Key * _key ) :
- Effect( &ladspaeffect_plugin_descriptor, _parent, _key ),
- m_controls( NULL ),
- m_maxSampleRate( 0 ),
- m_key( LadspaSubPluginFeatures::subPluginKeyToLadspaKey( _key ) )
- {
- Ladspa2LMMS * manager = Engine::getLADSPAManager();
- if( manager->getDescription( m_key ) == NULL )
- {
- Engine::getSong()->collectError(tr( "Unknown LADSPA plugin %1 requested." ).arg(
- m_key.second ) );
- setOkay( false );
- return;
- }
- setDisplayName( manager->getShortName( m_key ) );
- pluginInstantiation();
- connect( Engine::mixer(), SIGNAL( sampleRateChanged() ),
- this, SLOT( changeSampleRate() ) );
- }
- LadspaEffect::~LadspaEffect()
- {
- pluginDestruction();
- }
- void LadspaEffect::changeSampleRate()
- {
- DataFile dataFile( DataFile::EffectSettings );
- m_controls->saveState( dataFile, dataFile.content() );
- LadspaControls * controls = m_controls;
- m_controls = NULL;
- m_pluginMutex.lock();
- pluginDestruction();
- pluginInstantiation();
- m_pluginMutex.unlock();
- controls->effectModelChanged( m_controls );
- delete controls;
- m_controls->restoreState( dataFile.content().firstChild().toElement() );
- // the IDs of re-created controls have been saved and now need to be
- // resolved again
- AutomationPattern::resolveAllIDs();
- }
- bool LadspaEffect::processAudioBuffer( sampleFrame * _buf,
- const fpp_t _frames )
- {
- m_pluginMutex.lock();
- if( !isOkay() || dontRun() || !isRunning() || !isEnabled() )
- {
- m_pluginMutex.unlock();
- return( false );
- }
- int frames = _frames;
- sampleFrame * o_buf = NULL;
- QVarLengthArray<sample_t> sBuf(_frames * DEFAULT_CHANNELS);
- if( m_maxSampleRate < Engine::mixer()->processingSampleRate() )
- {
- o_buf = _buf;
- _buf = reinterpret_cast<sampleFrame*>(sBuf.data());
- sampleDown( o_buf, _buf, m_maxSampleRate );
- frames = _frames * m_maxSampleRate /
- Engine::mixer()->processingSampleRate();
- }
- // Copy the LMMS audio buffer to the LADSPA input buffer and initialize
- // the control ports.
- ch_cnt_t channel = 0;
- for( ch_cnt_t proc = 0; proc < processorCount(); ++proc )
- {
- for( int port = 0; port < m_portCount; ++port )
- {
- port_desc_t * pp = m_ports.at( proc ).at( port );
- switch( pp->rate )
- {
- case CHANNEL_IN:
- for( fpp_t frame = 0;
- frame < frames; ++frame )
- {
- pp->buffer[frame] =
- _buf[frame][channel];
- }
- ++channel;
- break;
- case AUDIO_RATE_INPUT:
- {
- ValueBuffer * vb = pp->control->valueBuffer();
- if( vb )
- {
- memcpy( pp->buffer, vb->values(), frames * sizeof(float) );
- }
- else
- {
- pp->value = static_cast<LADSPA_Data>(
- pp->control->value() / pp->scale );
- // This only supports control rate ports, so the audio rates are
- // treated as though they were control rate by setting the
- // port buffer to all the same value.
- for( fpp_t frame = 0;
- frame < frames; ++frame )
- {
- pp->buffer[frame] =
- pp->value;
- }
- }
- break;
- }
- case CONTROL_RATE_INPUT:
- if( pp->control == NULL )
- {
- break;
- }
- pp->value = static_cast<LADSPA_Data>(
- pp->control->value() / pp->scale );
- pp->buffer[0] =
- pp->value;
- break;
- case CHANNEL_OUT:
- case AUDIO_RATE_OUTPUT:
- case CONTROL_RATE_OUTPUT:
- break;
- default:
- break;
- }
- }
- }
- // Process the buffers.
- for( ch_cnt_t proc = 0; proc < processorCount(); ++proc )
- {
- (m_descriptor->run)( m_handles[proc], frames );
- }
- // Copy the LADSPA output buffers to the LMMS buffer.
- double out_sum = 0.0;
- channel = 0;
- const float d = dryLevel();
- const float w = wetLevel();
- for( ch_cnt_t proc = 0; proc < processorCount(); ++proc )
- {
- for( int port = 0; port < m_portCount; ++port )
- {
- port_desc_t * pp = m_ports.at( proc ).at( port );
- switch( pp->rate )
- {
- case CHANNEL_IN:
- case AUDIO_RATE_INPUT:
- case CONTROL_RATE_INPUT:
- break;
- case CHANNEL_OUT:
- for( fpp_t frame = 0;
- frame < frames; ++frame )
- {
- _buf[frame][channel] = d * _buf[frame][channel] + w * pp->buffer[frame];
- out_sum += _buf[frame][channel] * _buf[frame][channel];
- }
- ++channel;
- break;
- case AUDIO_RATE_OUTPUT:
- case CONTROL_RATE_OUTPUT:
- break;
- default:
- break;
- }
- }
- }
- if( o_buf != NULL )
- {
- sampleBack( _buf, o_buf, m_maxSampleRate );
- }
- checkGate( out_sum / frames );
- bool is_running = isRunning();
- m_pluginMutex.unlock();
- return( is_running );
- }
- void LadspaEffect::setControl( int _control, LADSPA_Data _value )
- {
- if( !isOkay() )
- {
- return;
- }
- m_portControls[_control]->value = _value;
- }
- void LadspaEffect::pluginInstantiation()
- {
- m_maxSampleRate = maxSamplerate( displayName() );
- Ladspa2LMMS * manager = Engine::getLADSPAManager();
- // Calculate how many processing units are needed.
- const ch_cnt_t lmms_chnls = Engine::mixer()->audioDev()->channels();
- int effect_channels = manager->getDescription( m_key )->inputChannels;
- setProcessorCount( lmms_chnls / effect_channels );
- // get inPlaceBroken property
- m_inPlaceBroken = manager->isInplaceBroken( m_key );
- // Categorize the ports, and create the buffers.
- m_portCount = manager->getPortCount( m_key );
- int inputch = 0;
- int outputch = 0;
- LADSPA_Data * inbuf [2];
- inbuf[0] = NULL;
- inbuf[1] = NULL;
- for( ch_cnt_t proc = 0; proc < processorCount(); proc++ )
- {
- multi_proc_t ports;
- for( int port = 0; port < m_portCount; port++ )
- {
- port_desc_t * p = new PortDescription;
- p->name = manager->getPortName( m_key, port );
- p->proc = proc;
- p->port_id = port;
- p->control = NULL;
- p->buffer = NULL;
- // Determine the port's category.
- if( manager->isPortAudio( m_key, port ) )
- {
- if( p->name.toUpper().contains( "IN" ) &&
- manager->isPortInput( m_key, port ) )
- {
- p->rate = CHANNEL_IN;
- p->buffer = MM_ALLOC( LADSPA_Data, Engine::mixer()->framesPerPeriod() );
- inbuf[ inputch ] = p->buffer;
- inputch++;
- }
- else if( p->name.toUpper().contains( "OUT" ) &&
- manager->isPortOutput( m_key, port ) )
- {
- p->rate = CHANNEL_OUT;
- if( ! m_inPlaceBroken && inbuf[ outputch ] )
- {
- p->buffer = inbuf[ outputch ];
- outputch++;
- }
- else
- {
- p->buffer = MM_ALLOC( LADSPA_Data, Engine::mixer()->framesPerPeriod() );
- m_inPlaceBroken = true;
- }
- }
- else if( manager->isPortInput( m_key, port ) )
- {
- p->rate = AUDIO_RATE_INPUT;
- p->buffer = MM_ALLOC( LADSPA_Data, Engine::mixer()->framesPerPeriod() );
- }
- else
- {
- p->rate = AUDIO_RATE_OUTPUT;
- p->buffer = MM_ALLOC( LADSPA_Data, Engine::mixer()->framesPerPeriod() );
- }
- }
- else
- {
- p->buffer = MM_ALLOC( LADSPA_Data, 1 );
- if( manager->isPortInput( m_key, port ) )
- {
- p->rate = CONTROL_RATE_INPUT;
- }
- else
- {
- p->rate = CONTROL_RATE_OUTPUT;
- }
- }
- p->scale = 1.0f;
- if( manager->isEnum( m_key, port ) )
- {
- p->data_type = ENUM;
- }
- else if( manager->isPortToggled( m_key, port ) )
- {
- p->data_type = TOGGLED;
- }
- else if( manager->isInteger( m_key, port ) )
- {
- p->data_type = INTEGER;
- }
- else if( p->name.toUpper().contains( "(SECONDS)" ) )
- {
- p->data_type = TIME;
- p->scale = 1000.0f;
- int loc = p->name.toUpper().indexOf(
- "(SECONDS)" );
- p->name.replace( loc, 9, "(ms)" );
- }
- else if( p->name.toUpper().contains( "(S)" ) )
- {
- p->data_type = TIME;
- p->scale = 1000.0f;
- int loc = p->name.toUpper().indexOf( "(S)" );
- p->name.replace( loc, 3, "(ms)" );
- }
- else if( p->name.toUpper().contains( "(MS)" ) )
- {
- p->data_type = TIME;
- int loc = p->name.toUpper().indexOf( "(MS)" );
- p->name.replace( loc, 4, "(ms)" );
- }
- else
- {
- p->data_type = FLOATING;
- }
- // Get the range and default values.
- p->max = manager->getUpperBound( m_key, port );
- if( p->max == NOHINT )
- {
- p->max = p->name.toUpper() == "GAIN" ? 10.0f :
- 1.0f;
- }
- if( manager->areHintsSampleRateDependent(
- m_key, port ) )
- {
- p->max *= m_maxSampleRate;
- }
- p->min = manager->getLowerBound( m_key, port );
- if( p->min == NOHINT )
- {
- p->min = 0.0f;
- }
- if( manager->areHintsSampleRateDependent(
- m_key, port ) )
- {
- p->min *= m_maxSampleRate;
- }
- p->def = manager->getDefaultSetting( m_key, port );
- if( p->def == NOHINT )
- {
- if( p->data_type != TOGGLED )
- {
- p->def = ( p->min + p->max ) / 2.0f;
- }
- else
- {
- p->def = 1.0f;
- }
- }
- else if( manager->areHintsSampleRateDependent( m_key, port ) )
- {
- p->def *= m_maxSampleRate;
- }
- p->max *= p->scale;
- p->min *= p->scale;
- p->def *= p->scale;
- p->value = p->def;
- p->suggests_logscale = manager->isLogarithmic( m_key, port );
- ports.append( p );
- // For convenience, keep a separate list of the ports that are used
- // to control the processors.
- if( p->rate == AUDIO_RATE_INPUT ||
- p->rate == CONTROL_RATE_INPUT )
- {
- p->control_id = m_portControls.count();
- m_portControls.append( p );
- }
- }
- m_ports.append( ports );
- }
- // Instantiate the processing units.
- m_descriptor = manager->getDescriptor( m_key );
- if( m_descriptor == NULL )
- {
- QMessageBox::warning( 0, "Effect",
- "Can't get LADSPA descriptor function: " + m_key.second,
- QMessageBox::Ok, QMessageBox::NoButton );
- setOkay( false );
- return;
- }
- if( m_descriptor->run == NULL )
- {
- QMessageBox::warning( 0, "Effect",
- "Plugin has no processor: " + m_key.second,
- QMessageBox::Ok, QMessageBox::NoButton );
- setDontRun( true );
- }
- for( ch_cnt_t proc = 0; proc < processorCount(); proc++ )
- {
- LADSPA_Handle effect = manager->instantiate( m_key,
- m_maxSampleRate );
- if( effect == NULL )
- {
- QMessageBox::warning( 0, "Effect",
- "Can't get LADSPA instance: " + m_key.second,
- QMessageBox::Ok, QMessageBox::NoButton );
- setOkay( false );
- return;
- }
- m_handles.append( effect );
- }
- // Connect the ports.
- for( ch_cnt_t proc = 0; proc < processorCount(); proc++ )
- {
- for( int port = 0; port < m_portCount; port++ )
- {
- port_desc_t * pp = m_ports.at( proc ).at( port );
- if( !manager->connectPort( m_key,
- m_handles[proc],
- port,
- pp->buffer ) )
- {
- QMessageBox::warning( 0, "Effect",
- "Failed to connect port: " + m_key.second,
- QMessageBox::Ok, QMessageBox::NoButton );
- setDontRun( true );
- return;
- }
- }
- }
- // Activate the processing units.
- for( ch_cnt_t proc = 0; proc < processorCount(); proc++ )
- {
- manager->activate( m_key, m_handles[proc] );
- }
- m_controls = new LadspaControls( this );
- }
- void LadspaEffect::pluginDestruction()
- {
- if( !isOkay() )
- {
- return;
- }
- delete m_controls;
- for( ch_cnt_t proc = 0; proc < processorCount(); proc++ )
- {
- Ladspa2LMMS * manager = Engine::getLADSPAManager();
- manager->deactivate( m_key, m_handles[proc] );
- manager->cleanup( m_key, m_handles[proc] );
- for( int port = 0; port < m_portCount; port++ )
- {
- port_desc_t * pp = m_ports.at( proc ).at( port );
- if( m_inPlaceBroken || pp->rate != CHANNEL_OUT )
- {
- if( pp->buffer) MM_FREE( pp->buffer );
- }
- delete pp;
- }
- m_ports[proc].clear();
- }
- m_ports.clear();
- m_handles.clear();
- m_portControls.clear();
- }
- static QMap<QString, sample_rate_t> __buggy_plugins;
- sample_rate_t LadspaEffect::maxSamplerate( const QString & _name )
- {
- if( __buggy_plugins.isEmpty() )
- {
- __buggy_plugins["C* AmpVTS"] = 88200;
- __buggy_plugins["Chorus2"] = 44100;
- __buggy_plugins["Notch Filter"] = 96000;
- __buggy_plugins["TAP Reflector"] = 192000;
- }
- if( __buggy_plugins.contains( _name ) )
- {
- return( __buggy_plugins[_name] );
- }
- return( Engine::mixer()->processingSampleRate() );
- }
- extern "C"
- {
- // necessary for getting instance out of shared lib
- PLUGIN_EXPORT Plugin * lmms_plugin_main( Model * _parent, void * _data )
- {
- return new LadspaEffect( _parent,
- static_cast<const Plugin::Descriptor::SubPluginFeatures::Key *>(
- _data ) );
- }
- }
|