diff --git a/ChangeLog b/ChangeLog index 4cc11db9a..bc159b83a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2008-05-17 Tobias Doerffel + + * 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 * plugins/stereo_matrix/stereomatrix_controls.cpp: diff --git a/include/audio_port.h b/include/audio_port.h index 68aec4415..225493a0b 100644 --- a/include/audio_port.h +++ b/include/audio_port.h @@ -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; } ; diff --git a/plugins/ladspa_effect/ladspa_effect.cpp b/plugins/ladspa_effect/ladspa_effect.cpp index dc130b022..b891b34dc 100644 --- a/plugins/ladspa_effect/ladspa_effect.cpp +++ b/plugins/ladspa_effect/ladspa_effect.cpp @@ -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( + 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( + 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 __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( - 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( - 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" { diff --git a/plugins/ladspa_effect/ladspa_effect.h b/plugins/ladspa_effect/ladspa_effect.h index 4c9eb4e9f..d326c0746 100644 --- a/plugins/ladspa_effect/ladspa_effect.h +++ b/plugins/ladspa_effect/ladspa_effect.h @@ -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 m_handles; diff --git a/src/core/audio/audio_port.cpp b/src/core/audio/audio_port.cpp index 46e459fa3..2ee84baf0 100644 --- a/src/core/audio/audio_port.cpp +++ b/src/core/audio/audio_port.cpp @@ -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 diff --git a/src/core/fx_mixer.cpp b/src/core/fx_mixer.cpp index da5b76774..f1b494c19 100644 --- a/src/core/fx_mixer.cpp +++ b/src/core/fx_mixer.cpp @@ -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(); } diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index 0bc252ba7..6fd6886f5 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -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; } ;