* LADSPA-effect-plugin: improved handling of effects which only work at limited samplerates

* audio-port: small optimizations
* mixer: removed obsolete idle-property of worker-threads
* FX-mixer: protect individual buffers of FX-channels from being processed by more than one thread



git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@979 0778d3d1-df1d-0410-868b-ea421aaaa00d
This commit is contained in:
Tobias Doerffel
2008-05-17 15:50:27 +00:00
parent fc63352e55
commit fc1b985fe8
7 changed files with 257 additions and 202 deletions

View File

@@ -1,3 +1,20 @@
2008-05-17 Tobias Doerffel <tobydox/at/users/dot/sourceforge/dot/net>
* plugins/ladspa_effect/ladspa_effect.cpp:
* plugins/ladspa_effect/ladspa_effect.h:
improved handling of effects which only work at limited samplerates
* include/audio_port.h:
* src/core/audio/audio_port.cpp:
small optimizations
* src/core/mixer.cpp:
removed obsolete idle-property of worker-threads
* src/core/fx_mixer.cpp:
protect individual buffers of FX-channels from being processed by more
than one thread
2008-05-17 Paul Giblock <drfaygo/at/gmail/dot/com>
* plugins/stereo_matrix/stereomatrix_controls.cpp:

View File

@@ -43,6 +43,7 @@ public:
{
return( m_firstBuffer );
}
inline sampleFrame * secondBuffer( void )
{
return( m_secondBuffer );
@@ -76,7 +77,8 @@ public:
{
return( m_extOutputEnabled );
}
void FASTCALL setExtOutputEnabled( bool _enabled );
void setExtOutputEnabled( bool _enabled );
// next effect-channel after this audio-port
@@ -105,21 +107,20 @@ public:
void setName( const QString & _new_name );
bool processEffects( void );
enum bufferUsages
{
NoUsage,
FirstBuffer,
BothBuffers
} m_bufferUsage;
inline bool processEffects( void )
{
QMutexLocker m( &m_firstBufferLock );
return( m_effects.processAudioBuffer( m_firstBuffer,
m_frames ) );
}
} ;
private:
volatile bufferUsages m_bufferUsage;
sampleFrame * m_firstBuffer;
sampleFrame * m_secondBuffer;
QMutex m_firstBufferLock;
@@ -131,7 +132,10 @@ private:
QString m_name;
effectChain m_effects;
fpp_t m_frames;
friend class mixer;
friend class mixerWorkerThread;
} ;

View File

@@ -64,7 +64,8 @@ ladspaEffect::ladspaEffect( model * _parent,
const descriptor::subPluginFeatures::key * _key ) :
effect( &ladspaeffect_plugin_descriptor, _parent, _key ),
m_controls( NULL ),
m_effName( "none" ),
m_publicName( "none" ),
m_maxSampleRate( 0 ),
m_key( ladspaSubPluginFeatures::subPluginKeyToLadspaKey( _key ) )
{
ladspa2LMMS * manager = engine::getLADSPAManager();
@@ -76,7 +77,7 @@ ladspaEffect::ladspaEffect( model * _parent,
setOkay( FALSE );
return;
}
setPublicName( manager->getShortName( m_key ) );
pluginInstantiation();
@@ -114,30 +115,200 @@ void ladspaEffect::changeSampleRate( void )
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;
int sr = m_maxSampleRate;
if( sr < engine::getMixer()->processingSampleRate() )
{
o_buf = _buf;
_buf = new sampleFrame[_frames];
sampleDown( o_buf, _buf, sr );
frames = _frames * sr /
engine::getMixer()->processingSampleRate();
}
// Copy the LMMS audio buffer to the LADSPA input buffer and initialize
// the control ports. Need to change this to handle non-in-place-broken
// plugins--would speed things up to use the same buffer for both
// LMMS and LADSPA.
ch_cnt_t channel = 0;
for( ch_cnt_t proc = 0; proc < getProcessorCount(); ++proc )
{
for( int port = 0; port < m_portCount; ++port )
{
switch( m_ports[proc][port]->rate )
{
case CHANNEL_IN:
for( fpp_t frame = 0;
frame < frames; ++frame )
{
m_ports[proc][port]->buffer[frame] =
_buf[frame][channel];
}
++channel;
break;
case AUDIO_RATE_INPUT:
m_ports[proc][port]->value =
static_cast<LADSPA_Data>(
m_ports[proc][port]->control->getValue() /
m_ports[proc][port]->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 )
{
m_ports[proc][port]->buffer[frame] =
m_ports[proc][port]->value;
}
break;
case CONTROL_RATE_INPUT:
if( m_ports[proc][port]->control ==
NULL )
{
break;
}
m_ports[proc][port]->value =
static_cast<LADSPA_Data>(
m_ports[proc][port]->control->getValue() /
m_ports[proc][port]->scale );
m_ports[proc][port]->buffer[0] =
m_ports[proc][port]->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 < getProcessorCount(); ++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 = getDryLevel();
const float w = getWetLevel();
for( ch_cnt_t proc = 0; proc < getProcessorCount(); ++proc )
{
for( int port = 0; port < m_portCount; ++port )
{
switch( m_ports[proc][port]->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 *
m_ports[proc][port]->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, sr );
delete[] _buf;
}
// Check whether we need to continue processing input. Restart the
// counter if the threshold has been exceeded.
if( out_sum / frames <= getGate()+0.000001 )
{
incrementBufferCount();
if( getBufferCount() > getTimeout() )
{
stopRunning();
resetBufferCount();
}
}
else
{
resetBufferCount();
}
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( void )
{
m_maxSampleRate = maxSamplerate( publicName() );
ladspa2LMMS * manager = engine::getLADSPAManager();
// Calculate how many processing units are needed.
const ch_cnt_t lmms_chnls = engine::getMixer()->audioDev()->channels();
m_effectChannels = manager->getDescription( m_key )->inputChannels;
setProcessorCount( lmms_chnls / m_effectChannels );
int effect_channels = manager->getDescription( m_key )->inputChannels;
setProcessorCount( lmms_chnls / effect_channels );
// Categorize the ports, and create the buffers.
m_portCount = manager->getPortCount( m_key );
for( ch_cnt_t proc = 0; proc < getProcessorCount(); proc++ )
{
multi_proc_t ports;
for( Uint16 port = 0; port < m_portCount; port++ )
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;
// Determine the port's category.
if( manager->isPortAudio( m_key, port ) )
{
@@ -227,7 +398,7 @@ void ladspaEffect::pluginInstantiation( void )
if( manager->areHintsSampleRateDependent(
m_key, port ) )
{
p->max *= engine::getMixer()->processingSampleRate();
p->max *= m_maxSampleRate;
}
p->min = manager->getLowerBound( m_key, port );
@@ -239,7 +410,7 @@ void ladspaEffect::pluginInstantiation( void )
if( manager->areHintsSampleRateDependent(
m_key, port ) )
{
p->min *= engine::getMixer()->processingSampleRate();
p->min *= m_maxSampleRate;
}
p->def = manager->getDefaultSetting( m_key, port );
@@ -296,7 +467,7 @@ void ladspaEffect::pluginInstantiation( void )
for( ch_cnt_t proc = 0; proc < getProcessorCount(); proc++ )
{
LADSPA_Handle effect = manager->instantiate( m_key,
engine::getMixer()->processingSampleRate() );
m_maxSampleRate );
if( effect == NULL )
{
QMessageBox::warning( 0, "Effect",
@@ -311,7 +482,7 @@ void ladspaEffect::pluginInstantiation( void )
// Connect the ports.
for( ch_cnt_t proc = 0; proc < getProcessorCount(); proc++ )
{
for( Uint16 port = 0; port < m_portCount; port++ )
for( int port = 0; port < m_portCount; port++ )
{
if( !manager->connectPort( m_key,
m_handles[proc],
@@ -355,7 +526,7 @@ void ladspaEffect::pluginDestruction( void )
ladspa2LMMS * manager = engine::getLADSPAManager();
manager->deactivate( m_key, m_handles[proc] );
manager->cleanup( m_key, m_handles[proc] );
for( Uint16 port = 0; port < m_portCount; port++ )
for( int port = 0; port < m_portCount; port++ )
{
free( m_ports[proc][port]->buffer );
free( m_ports[proc][port] );
@@ -370,171 +541,27 @@ void ladspaEffect::pluginDestruction( void )
bool ladspaEffect::processAudioBuffer( sampleFrame * _buf,
const fpp_t _frames )
static QMap<QString, int> __buggy_plugins;
int ladspaEffect::maxSamplerate( const QString & _name )
{
m_pluginMutex.lock();
if( !isOkay() || dontRun() || !isRunning() || !isEnabled() )
if( __buggy_plugins.isEmpty() )
{
m_pluginMutex.unlock();
return( FALSE );
__buggy_plugins["C * AmpVTS"] = 88200;
__buggy_plugins["Chorus2"] = 44100;
}
sampleFrame * o_buf = NULL;
int frames = _frames;
if( publicName().contains( "C* AmpVTS" ) &&
engine::getMixer()->processingSampleRate() > 88200 )
if( __buggy_plugins.contains( _name ) )
{
o_buf = _buf;
_buf = new sampleFrame[_frames];
sampleDown( o_buf, _buf, 88200 );
frames = _frames * 88200 /
engine::getMixer()->processingSampleRate();
return( __buggy_plugins[_name] );
}
// Copy the LMMS audio buffer to the LADSPA input buffer and initialize
// the control ports. Need to change this to handle non-in-place-broken
// plugins--would speed things up to use the same buffer for both
// LMMS and LADSPA.
ch_cnt_t channel = 0;
for( ch_cnt_t proc = 0; proc < getProcessorCount(); proc++)
{
for( Uint16 port = 0; port < m_portCount; port++ )
{
switch( m_ports[proc][port]->rate )
{
case CHANNEL_IN:
for( fpp_t frame = 0;
frame < frames; frame++ )
{
m_ports[proc][port]->buffer[frame] =
_buf[frame][channel];
}
channel++;
break;
case AUDIO_RATE_INPUT:
m_ports[proc][port]->value =
static_cast<LADSPA_Data>(
m_ports[proc][port]->control->getValue() /
m_ports[proc][port]->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++ )
{
m_ports[proc][port]->buffer[frame] =
m_ports[proc][port]->value;
}
break;
case CONTROL_RATE_INPUT:
if( m_ports[proc][port]->control ==
NULL )
{
break;
}
m_ports[proc][port]->value =
static_cast<LADSPA_Data>(
m_ports[proc][port]->control->getValue() /
m_ports[proc][port]->scale );
m_ports[proc][port]->buffer[0] =
m_ports[proc][port]->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 < getProcessorCount(); 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 = getDryLevel();
const float w = getWetLevel();
for( ch_cnt_t proc = 0; proc < getProcessorCount(); proc++)
{
for( Uint16 port = 0; port < m_portCount; port++ )
{
switch( m_ports[proc][port]->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 *
m_ports[proc][port]->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, 88200 );
delete[] _buf;
}
// Check whether we need to continue processing input. Restart the
// counter if the threshold has been exceeded.
if( out_sum / frames <= getGate()+0.000001 )
{
incrementBufferCount();
if( getBufferCount() > getTimeout() )
{
stopRunning();
resetBufferCount();
}
}
else
{
resetBufferCount();
}
bool is_running = isRunning();
m_pluginMutex.unlock();
return( is_running );
return( engine::getMixer()->processingSampleRate() );
}
void ladspaEffect::setControl( Uint16 _control, LADSPA_Data _value )
{
if( !isOkay() )
{
return;
}
m_portControls[_control]->value = _value;
}
extern "C"
{

View File

@@ -48,7 +48,7 @@ public:
virtual bool processAudioBuffer( sampleFrame * _buf,
const fpp_t _frames );
void setControl( Uint16 _control, LADSPA_Data _data );
void setControl( int _control, LADSPA_Data _data );
virtual effectControls * getControls( void )
{
@@ -62,12 +62,12 @@ public:
virtual inline QString publicName( void ) const
{
return( m_effName );
return( m_publicName );
}
inline void setPublicName( const QString & _name )
{
m_effName = _name;
m_publicName = _name;
}
@@ -79,15 +79,16 @@ private:
void pluginInstantiation( void );
void pluginDestruction( void );
static int maxSamplerate( const QString & _name );
QMutex m_pluginMutex;
ladspaControls * m_controls;
QString m_effName;
QString m_publicName;
int m_maxSampleRate;
ladspa_key_t m_key;
Uint16 m_effectChannels;
Uint16 m_portCount;
fpp_t m_bufferSize;
int m_portCount;
const LADSPA_Descriptor * m_descriptor;
QVector<LADSPA_Handle> m_handles;

View File

@@ -41,8 +41,7 @@ audioPort::audioPort( const QString & _name, track * _track ) :
m_extOutputEnabled( FALSE ),
m_nextFxChannel( 0 ),
m_name( "unnamed port" ),
m_effects( _track ),
m_frames( engine::getMixer()->framesPerPeriod() )
m_effects( _track )
{
engine::getMixer()->clearAudioBuffer( m_firstBuffer,
engine::getMixer()->framesPerPeriod() );
@@ -72,11 +71,13 @@ void audioPort::nextPeriod( void )
engine::getMixer()->clearAudioBuffer( m_firstBuffer,
engine::getMixer()->framesPerPeriod() );
qSwap( m_firstBuffer, m_secondBuffer );
m_firstBufferLock.unlock();
// this is how we decrease state of buffer-usage ;-)
m_bufferUsage = ( m_bufferUsage != NoUsage ) ?
( ( m_bufferUsage == FirstBuffer ) ?
NoUsage : FirstBuffer ) : NoUsage;
m_firstBufferLock.unlock();
}
@@ -108,4 +109,17 @@ void audioPort::setName( const QString & _name )
}
bool audioPort::processEffects( void )
{
lockFirstBuffer();
bool more = m_effects.processAudioBuffer( m_firstBuffer,
engine::getMixer()->framesPerPeriod() );
unlockFirstBuffer();
return( more );
}
#endif

View File

@@ -91,6 +91,7 @@ fxMixer::~fxMixer()
void fxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch )
{
m_fxChannels[_ch]->m_lock.lock();
sampleFrame * buf = m_fxChannels[_ch]->m_buffer;
for( f_cnt_t f = 0; f < engine::getMixer()->framesPerPeriod(); ++f )
{
@@ -98,6 +99,7 @@ void fxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch )
buf[f][1] += _buf[f][1];
}
m_fxChannels[_ch]->m_used = TRUE;
m_fxChannels[_ch]->m_lock.unlock();
}

View File

@@ -152,8 +152,7 @@ public:
m_sem( &m_mixer->m_workerSem ),
m_jobWait( 1 ),
m_jobAccepted( 1 ),
m_jobQueue( NULL ),
m_idle( FALSE )
m_jobQueue( NULL )
{
start( QThread::TimeCriticalPriority );
}
@@ -169,11 +168,6 @@ public:
m_jobAccepted.acquire();
}
inline bool idle( void )
{
return( m_idle );
}
private:
virtual void run( void )
@@ -183,11 +177,9 @@ private:
sizeof( sampleFrame ) );
m_jobWait.acquire();
m_jobAccepted.acquire();
m_idle = TRUE;
while( 1 )
{
m_jobWait.acquire();
m_idle = FALSE;
m_sem->acquire();
m_jobAccepted.release();
for( jobQueueItems::iterator it =
@@ -207,8 +199,8 @@ private:
case AudioPortEffects:
{
audioPort * a = it->audioPortJob;
bool me = a->processEffects();
if( a->m_bufferUsage != audioPort::NoUsage || me )
const bool me = a->processEffects();
if( me || a->m_bufferUsage != audioPort::NoUsage )
{
engine::getFxMixer()->mixToChannel( a->firstBuffer(),
a->nextFxChannel() );
@@ -228,7 +220,6 @@ private:
m_jobQueue->lock.unlock();
}
}
m_idle = TRUE;
m_sem->release();
}
aligned_free( working_buf );
@@ -239,7 +230,6 @@ private:
QSemaphore m_jobWait;
QSemaphore m_jobAccepted;
jobQueue * m_jobQueue;
volatile bool m_idle;
} ;