From 45c4b7b824de19fbdc3bbbc3907dd6bb81151483 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 15 Sep 2009 17:53:59 -0700 Subject: [PATCH 01/25] Mixer sends work in the backend The backend code of mixer uses mixer sends to compute effects. --- include/FxMixer.h | 20 +++++- src/core/FxMixer.cpp | 147 +++++++++++++++++++++++++++++++------------ src/core/mixer.cpp | 27 ++------ 3 files changed, 128 insertions(+), 66 deletions(-) diff --git a/include/FxMixer.h b/include/FxMixer.h index 2a6bd84e0..12d4af42a 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -40,8 +40,10 @@ struct FxChannel ~FxChannel(); EffectChain m_fxChain; - bool m_used; + + // set to true if any effect in the channel is enabled and running bool m_stillRunning; + float m_peakLeft; float m_peakRight; sampleFrame * m_buffer; @@ -50,6 +52,11 @@ struct FxChannel QString m_name; QMutex m_lock; + // pointers to other channels that this one sends to + QVector m_sends; + + // pointers to other channels that send to this one + QVector m_receives; } ; @@ -86,11 +93,20 @@ public: return NULL; } + // make the output of channel fromChannel go to the input of channel toChannel + void createChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel); + + // delete the connection made by createChannelSend + void deleteChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel); + + // does fromChannel send its output to the input of toChannel? + bool channelSendsTo(fx_ch_t fromChannel, fx_ch_t toChannel); + + private: FxChannel * m_fxChannels[NumFxChannels+1]; // +1 = master - friend class mixerWorkerThread; friend class FxMixerView; diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index c7cfff902..ce8351cad 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -33,7 +33,6 @@ FxChannel::FxChannel( Model * _parent ) : m_fxChain( NULL ), - m_used( false ), m_stillRunning( false ), m_peakLeft( 0.0f ), m_peakRight( 0.0f ), @@ -64,10 +63,19 @@ FxMixer::FxMixer() : JournallingObject(), Model( NULL ) { - for( int i = 0; i < NumFxChannels+1; ++i ) + // create master channel + m_fxChannels[0] = new FxChannel(this); + + // create the rest of the channels + for( int i = 1; i < NumFxChannels+1; ++i ) { + // create new channel m_fxChannels[i] = new FxChannel( this ); + + // send the channel into master + createChannelSend(i, 0); } + // reset name etc. clear(); } @@ -85,6 +93,64 @@ FxMixer::~FxMixer() +void FxMixer::createChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel) +{ + // first make sure the send doesn't already exist + if( ! channelSendsTo(fromChannel, toChannel) ) + { + // add to from's sends + m_fxChannels[fromChannel]->m_sends.push_back(toChannel); + + // add to to's receives + m_fxChannels[toChannel]->m_receives.push_back(fromChannel); + } +} + + + +// delete the connection made by createChannelSend +void FxMixer::deleteChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel) +{ + // delete the send + FxChannel * from = m_fxChannels[fromChannel]; + FxChannel * to = m_fxChannels[toChannel]; + + // find and delete the send entry + for(int i=0; im_sends.size(); ++i) { + if( from->m_sends[i] == toChannel ) + { + // delete this index + from->m_sends.remove(i); + break; + } + } + + // find and delete the receive entry + for(int i=0; im_receives.size(); ++i) + { + if( to->m_receives[i] == fromChannel ) + { + // delete this index + to->m_receives.remove(i); + break; + } + } +} + + + +// does fromChannel send its output to the input of toChannel? +bool FxMixer::channelSendsTo(fx_ch_t fromChannel, fx_ch_t toChannel) +{ + FxChannel * from = m_fxChannels[fromChannel]; + for(int i=0; im_sends.size(); ++i){ + if( from->m_sends[i] == toChannel ) + return true; + } + return false; +} + + void FxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch ) { @@ -93,7 +159,6 @@ void FxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch ) m_fxChannels[_ch]->m_lock.lock(); CPU::bufMix( m_fxChannels[_ch]->m_buffer, _buf, engine::getMixer()->framesPerPeriod() ); - m_fxChannels[_ch]->m_used = true; m_fxChannels[_ch]->m_lock.unlock(); } } @@ -103,32 +168,49 @@ void FxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch ) void FxMixer::processChannel( fx_ch_t _ch, sampleFrame * _buf ) { - if( m_fxChannels[_ch]->m_muteModel.value() == false && - ( m_fxChannels[_ch]->m_used || - m_fxChannels[_ch]->m_stillRunning || - _ch == 0 ) ) + const fpp_t fpp = engine::getMixer()->framesPerPeriod(); + FxChannel * thisCh = m_fxChannels[_ch]; + if( ! thisCh->m_muteModel.value() ) { + // do mixer sends. loop through the channels that send to this one + for( int i = 0; i < thisCh->m_receives.size(); ++i) + { + fx_ch_t senderIndex = thisCh->m_receives[i]; + FxChannel * sender = m_fxChannels[senderIndex]; + + // compute the sending channel + processChannel( senderIndex ); + + // mix it with this one + sampleFrame * ch_buf = sender->m_buffer; + const float v = sender->m_volumeModel.value(); + for( f_cnt_t f = 0; f < fpp; ++f ) + { + _buf[f][0] += ch_buf[f][0] * v; + _buf[f][1] += ch_buf[f][1] * v; + } + engine::getMixer()->clearAudioBuffer( ch_buf, + engine::getMixer()->framesPerPeriod() ); + } + + if( _buf == NULL ) { - _buf = m_fxChannels[_ch]->m_buffer; + _buf = thisCh->m_buffer; } - const fpp_t f = engine::getMixer()->framesPerPeriod(); - m_fxChannels[_ch]->m_fxChain.startRunning(); - m_fxChannels[_ch]->m_stillRunning = - m_fxChannels[_ch]->m_fxChain.processAudioBuffer( - _buf, f ); - m_fxChannels[_ch]->m_peakLeft = - engine::getMixer()->peakValueLeft( _buf, f ) * - m_fxChannels[_ch]->m_volumeModel.value(); - m_fxChannels[_ch]->m_peakRight = - engine::getMixer()->peakValueRight( _buf, f ) * - m_fxChannels[_ch]->m_volumeModel.value(); - m_fxChannels[_ch]->m_used = true; + const float v = thisCh->m_volumeModel.value(); + + thisCh->m_fxChain.startRunning(); + thisCh->m_stillRunning = thisCh-> + m_fxChain.processAudioBuffer( _buf, fpp); + thisCh->m_peakLeft = + engine::getMixer()->peakValueLeft( _buf, fpp ) * v; + thisCh->m_peakRight = + engine::getMixer()->peakValueRight( _buf, fpp ) * v; } else { - m_fxChannels[_ch]->m_peakLeft = - m_fxChannels[_ch]->m_peakRight = 0.0f; + thisCh->m_peakLeft = thisCh->m_peakRight = 0.0f; } } @@ -149,31 +231,14 @@ void FxMixer::masterMix( sampleFrame * _buf ) const int fpp = engine::getMixer()->framesPerPeriod(); memcpy( _buf, m_fxChannels[0]->m_buffer, sizeof( sampleFrame ) * fpp ); - for( int i = 1; i < NumFxChannels+1; ++i ) - { - if( m_fxChannels[i]->m_used ) - { - sampleFrame * ch_buf = m_fxChannels[i]->m_buffer; - const float v = m_fxChannels[i]->m_volumeModel.value(); - for( f_cnt_t f = 0; f < fpp; ++f ) - { - _buf[f][0] += ch_buf[f][0] * v; - _buf[f][1] += ch_buf[f][1] * v; - } - engine::getMixer()->clearAudioBuffer( ch_buf, - engine::getMixer()->framesPerPeriod() ); - m_fxChannels[i]->m_used = false; - } - } - processChannel( 0, _buf ); - if( m_fxChannels[0]->m_muteModel.value() ) + /*if( m_fxChannels[0]->m_muteModel.value() ) { engine::getMixer()->clearAudioBuffer( _buf, engine::getMixer()->framesPerPeriod() ); return; - } + }*/ const float v = m_fxChannels[0]->m_volumeModel.value(); for( f_cnt_t f = 0; f < engine::getMixer()->framesPerPeriod(); ++f ) diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index b82d6886f..35fdb62cd 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -143,16 +143,6 @@ public: private: virtual void run() { -#if 0 -#ifdef LMMS_BUILD_LINUX -#ifdef LMMS_HAVE_PTHREAD_H - cpu_set_t mask; - CPU_ZERO( &mask ); - CPU_SET( m_workerNum, &mask ); - pthread_setaffinity_np( pthread_self(), sizeof( mask ), &mask ); -#endif -#endif -#endif QMutex m; while( m_quit == false ) { @@ -618,15 +608,17 @@ sampleFrameA * mixer::renderNextBuffer() // STAGE 3: process effects in FX mixer - FILL_JOB_QUEUE_PARAM(QVector,__fx_channel_jobs, + /*FILL_JOB_QUEUE_PARAM(QVector,__fx_channel_jobs, MixerWorkerThread::EffectChannel,1); START_JOBS(); - WAIT_FOR_JOBS(); + WAIT_FOR_JOBS();*/ // STAGE 4: do master mix in FX mixer engine::fxMixer()->masterMix( m_writeBuf ); + WAIT_FOR_JOBS(); + unlock(); @@ -1137,17 +1129,6 @@ void mixer::fifoWriter::finish() void mixer::fifoWriter::run() { -#if 0 -#ifdef LMMS_BUILD_LINUX -#ifdef LMMS_HAVE_PTHREAD_H - cpu_set_t mask; - CPU_ZERO( &mask ); - CPU_SET( 0, &mask ); - pthread_setaffinity_np( pthread_self(), sizeof( mask ), &mask ); -#endif -#endif -#endif - const fpp_t frames = m_mixer->framesPerPeriod(); while( m_writing ) { From 89d5be7855cc0dc9b621a5aca987960a8ee754af Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 16 Sep 2009 09:41:57 -0700 Subject: [PATCH 02/25] Used FloatModels as the backend for mixer sends Can add new channels in the mixer, and sends are implemented. Instruments are hardcoded at 10. FL Import is hardcoded at 64. --- data/themes/default/send_bg_arrow.png | Bin 0 -> 267 bytes include/FxMixer.h | 30 ++-- include/FxMixerView.h | 12 +- include/classic_style.h | 2 +- include/cusis_style.h | 2 +- include/lmms_style.h | 2 +- plugins/flp_import/FlpImport.cpp | 14 +- src/core/FxMixer.cpp | 130 +++++++++------ src/core/mixer.cpp | 9 - src/gui/FxMixerView.cpp | 231 +++++++++++++++----------- src/gui/classic_style.cpp | 12 +- src/gui/cusis_style.cpp | 2 +- src/tracks/InstrumentTrack.cpp | 2 +- 13 files changed, 263 insertions(+), 185 deletions(-) create mode 100644 data/themes/default/send_bg_arrow.png diff --git a/data/themes/default/send_bg_arrow.png b/data/themes/default/send_bg_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..fde514da62ab29e40dd44632fa9757edc65ea2bf GIT binary patch literal 267 zcmeAS@N?(olHy`uVBq!ia0vp^GC*v>!3HGX8O+!Mq!^2X+?^QKos)S9lbCYGe8D3oWGWGJ|M`UZqI@`(c#rFptIhD02GdvhT#gQ9@T#a1hhrhnN^ z6J9q9r|YM%30?VLcJ%XR!L+5@BC}?1jO;tj$b<#dExmd%wr>8#%YrW!aH%zNtx7W5 m;Us&8>3@Lu?N?uVKQPJ7l)NGl%6T1VBZH@_pUXO@geCy^?oqG+ literal 0 HcmV?d00001 diff --git a/include/FxMixer.h b/include/FxMixer.h index 12d4af42a..116dbcde0 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -31,7 +31,6 @@ #include "JournallingObject.h" -const int NumFxChannels = 64; struct FxChannel @@ -54,6 +53,7 @@ struct FxChannel // pointers to other channels that this one sends to QVector m_sends; + QVector m_sendAmount; // pointers to other channels that send to this one QVector m_receives; @@ -86,26 +86,36 @@ public: FxChannel * effectChannel( int _ch ) { - if( _ch >= 0 && _ch <= NumFxChannels ) - { - return m_fxChannels[_ch]; - } - return NULL; + return m_fxChannels[_ch]; } // make the output of channel fromChannel go to the input of channel toChannel - void createChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel); + // it is safe to call even if the send already exists + void createChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel, + float amount = 1.0f); // delete the connection made by createChannelSend void deleteChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel); - // does fromChannel send its output to the input of toChannel? - bool channelSendsTo(fx_ch_t fromChannel, fx_ch_t toChannel); + // return the FloatModel of fromChannel sending its output to the input of + // toChannel. NULL if there is no send. + FloatModel * channelSendModel(fx_ch_t fromChannel, fx_ch_t toChannel); + // add a new channel to the Fx Mixer. + // returns the index of the channel that was just added + int createChannel(); + // reset a channel's name, fx, sends, etc + void clearChannel(fx_ch_t channelIndex); + + inline fx_ch_t numChannels() const + { + return m_fxChannels.size(); + } private: - FxChannel * m_fxChannels[NumFxChannels+1]; // +1 = master + // the fx channels in the mixer. index 0 is always master. + QVector m_fxChannels; friend class mixerWorkerThread; friend class FxMixerView; diff --git a/include/FxMixerView.h b/include/FxMixerView.h index dab34caaf..297ab5061 100644 --- a/include/FxMixerView.h +++ b/include/FxMixerView.h @@ -26,6 +26,8 @@ #define _FX_MIXER_VIEW_H #include +#include +#include #include "FxMixer.h" #include "ModelView.h" @@ -61,7 +63,7 @@ public: private slots: void updateFaders(); - + void addNewChannel(); private: struct FxChannelView @@ -72,13 +74,15 @@ private: fader * m_fader; } ; - FxChannelView m_fxChannelViews[NumFxChannels+1]; + QVector m_fxChannelViews; QStackedLayout * m_fxRacksLayout; - QStackedLayout * m_fxLineBanks; - QButtonGroup * m_bankButtons; FxLine * m_currentFxLine; + QScrollArea * channelArea; + QHBoxLayout * chLayout; + + void addFxLine(int i, QWidget * parent, QLayout * layout); } ; #endif diff --git a/include/classic_style.h b/include/classic_style.h index ccd1b5482..629960133 100644 --- a/include/classic_style.h +++ b/include/classic_style.h @@ -59,7 +59,7 @@ public: // LMMS Stuff virtual void drawFxLine(QPainter * _painter, const QWidget *_fxLine, - const QString & _name, bool _active); + const QString & _name, bool _active, bool _sendToThis); virtual void drawTrackContentBackground(QPainter * _painter, const QSize & _size, const int _pixelsPerTact); diff --git a/include/cusis_style.h b/include/cusis_style.h index 3e359cfd1..f000b520f 100644 --- a/include/cusis_style.h +++ b/include/cusis_style.h @@ -66,7 +66,7 @@ public: virtual void unpolish( QWidget * widget ); virtual void drawFxLine( QPainter * _painter, const QWidget *_fxLine, - const QString & _name, bool _active ); + const QString & _name, bool _active, bool _sendToThis ); virtual void drawTrackContentBackground( QPainter * _painter, const QSize & _size, const int _pixelsPerTact ); diff --git a/include/lmms_style.h b/include/lmms_style.h index eb6b0635c..3018e27c0 100644 --- a/include/lmms_style.h +++ b/include/lmms_style.h @@ -91,7 +91,7 @@ public: virtual void drawFxLine(QPainter * _painter, const QWidget *_fxLine, - const QString & _name, bool _active) = 0; + const QString & _name, bool _active, bool _sendToThis) = 0; virtual void drawTrackContentBackground(QPainter * _painter, const QSize & _size, const int _pixelsPerTact) = 0; diff --git a/plugins/flp_import/FlpImport.cpp b/plugins/flp_import/FlpImport.cpp index 1c36b5c4b..b243c9427 100644 --- a/plugins/flp_import/FlpImport.cpp +++ b/plugins/flp_import/FlpImport.cpp @@ -104,7 +104,7 @@ extern QString outstring; } - +const int NumFLFxChannels = 64; static void dump_mem( const void * buffer, uint n_bytes ) { @@ -542,7 +542,7 @@ struct FL_Project int currentPattern; int activeEditPattern; - FL_EffectChannel effectChannels[NumFxChannels+1]; + FL_EffectChannel effectChannels[NumFLFxChannels+1]; int currentEffectChannel; QString projectNotes; @@ -1022,7 +1022,7 @@ bool FlpImport::tryFLPImport( trackContainer * _tc ) break; case FLP_EffectChannelMuted: -if( p.currentEffectChannel <= NumFxChannels ) +if( p.currentEffectChannel <= NumFLFxChannels ) { p.effectChannels[p.currentEffectChannel].isMuted = ( data & 0x08 ) > 0 ? false : true; @@ -1274,7 +1274,7 @@ if( p.currentEffectChannel <= NumFxChannels ) case FLP_Text_EffectChanName: ++p.currentEffectChannel; - if( p.currentEffectChannel <= NumFxChannels ) + if( p.currentEffectChannel <= NumFLFxChannels ) { p.effectChannels[p.currentEffectChannel].name = text; } @@ -1497,7 +1497,7 @@ if( p.currentEffectChannel <= NumFxChannels ) const int param = pi[i*3+1] & 0xffff; const int ch = ( pi[i*3+1] >> 22 ) & 0x7f; - if( ch < 0 || ch > NumFxChannels ) + if( ch < 0 || ch > NumFLFxChannels ) { continue; } @@ -1797,7 +1797,7 @@ p->putValue( jt->pos, value, false ); } } - for( int fx_ch = 0; fx_ch <= NumFxChannels ; ++fx_ch ) + for( int fx_ch = 0; fx_ch <= NumFLFxChannels ; ++fx_ch ) { FxChannel * ch = engine::fxMixer()->effectChannel( fx_ch ); if( !ch ) @@ -1857,7 +1857,7 @@ p->putValue( jt->pos, value, false ); break; } if( effName.isEmpty() || it->fxChannel < 0 || - it->fxChannel > NumFxChannels ) + it->fxChannel > NumFLFxChannels ) { continue; } diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index ce8351cad..53c1152a7 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -61,49 +61,64 @@ FxChannel::~FxChannel() FxMixer::FxMixer() : JournallingObject(), - Model( NULL ) + Model( NULL ), + m_fxChannels() { // create master channel - m_fxChannels[0] = new FxChannel(this); - - // create the rest of the channels - for( int i = 1; i < NumFxChannels+1; ++i ) - { - // create new channel - m_fxChannels[i] = new FxChannel( this ); - - // send the channel into master - createChannelSend(i, 0); - } - - // reset name etc. - clear(); + createChannel(); } - FxMixer::~FxMixer() { - for( int i = 0; i < NumFxChannels+1; ++i ) + for( int i = 0; i < m_fxChannels.size(); ++i ) { + delete m_fxChannels[i]->m_sendAmount[i]; delete m_fxChannels[i]; } } -void FxMixer::createChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel) +int FxMixer::createChannel() { - // first make sure the send doesn't already exist - if( ! channelSendsTo(fromChannel, toChannel) ) - { - // add to from's sends - m_fxChannels[fromChannel]->m_sends.push_back(toChannel); + // create new channel + m_fxChannels.push_back(new FxChannel( this )); - // add to to's receives - m_fxChannels[toChannel]->m_receives.push_back(fromChannel); + // reset channel state + int index = m_fxChannels.size() - 1; + clearChannel(index); + + return index; +} + + + +void FxMixer::createChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel, + float amount) +{ + // find the existing connection + FxChannel * from = m_fxChannels[fromChannel]; + for(int i=0; im_sends.size(); ++i){ + if( from->m_sends[i] == toChannel ) + { + // simply adjust the amount + from->m_sendAmount[i]->setValue(amount); + return; + } } + + // connection does not exist. create a new one + + // add to from's sends + from->m_sends.push_back(toChannel); + from->m_sendAmount.push_back(new FloatModel(amount, 0, 1, 0.001, NULL, + tr("Amount to send"))); + + // add to to's receives + m_fxChannels[toChannel]->m_receives.push_back(fromChannel); + } @@ -120,6 +135,8 @@ void FxMixer::deleteChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel) if( from->m_sends[i] == toChannel ) { // delete this index + delete from->m_sendAmount[i]; + from->m_sendAmount.remove(i); from->m_sends.remove(i); break; } @@ -139,15 +156,15 @@ void FxMixer::deleteChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel) -// does fromChannel send its output to the input of toChannel? -bool FxMixer::channelSendsTo(fx_ch_t fromChannel, fx_ch_t toChannel) +// how much does fromChannel send its output to the input of toChannel? +FloatModel * FxMixer::channelSendModel(fx_ch_t fromChannel, fx_ch_t toChannel) { FxChannel * from = m_fxChannels[fromChannel]; for(int i=0; im_sends.size(); ++i){ if( from->m_sends[i] == toChannel ) - return true; + return from->m_sendAmount[i]; } - return false; + return NULL; } @@ -182,12 +199,13 @@ void FxMixer::processChannel( fx_ch_t _ch, sampleFrame * _buf ) processChannel( senderIndex ); // mix it with this one + float amt = channelSendModel(senderIndex, _ch)->value(); sampleFrame * ch_buf = sender->m_buffer; const float v = sender->m_volumeModel.value(); for( f_cnt_t f = 0; f < fpp; ++f ) { - _buf[f][0] += ch_buf[f][0] * v; - _buf[f][1] += ch_buf[f][1] * v; + _buf[f][0] += ch_buf[f][0] * v * amt; + _buf[f][1] += ch_buf[f][1] * v * amt; } engine::getMixer()->clearAudioBuffer( ch_buf, engine::getMixer()->framesPerPeriod() ); @@ -233,13 +251,6 @@ void FxMixer::masterMix( sampleFrame * _buf ) processChannel( 0, _buf ); - /*if( m_fxChannels[0]->m_muteModel.value() ) - { - engine::getMixer()->clearAudioBuffer( _buf, - engine::getMixer()->framesPerPeriod() ); - return; - }*/ - const float v = m_fxChannels[0]->m_volumeModel.value(); for( f_cnt_t f = 0; f < engine::getMixer()->framesPerPeriod(); ++f ) { @@ -256,25 +267,46 @@ void FxMixer::masterMix( sampleFrame * _buf ) void FxMixer::clear() { - for( int i = 0; i <= NumFxChannels; ++i ) + for( int i = 0; i < m_fxChannels.size(); ++i ) { - m_fxChannels[i]->m_fxChain.clear(); - m_fxChannels[i]->m_volumeModel.setValue( 1.0f ); - m_fxChannels[i]->m_muteModel.setValue( false ); - m_fxChannels[i]->m_name = ( i == 0 ) ? - tr( "Master" ) : tr( "FX %1" ).arg( i ); - m_fxChannels[i]->m_volumeModel.setDisplayName( - m_fxChannels[i]->m_name ); - + clearChannel(i); } } +void FxMixer::clearChannel(fx_ch_t index) +{ + FxChannel * ch = m_fxChannels[index]; + ch->m_fxChain.clear(); + ch->m_volumeModel.setValue( 1.0f ); + ch->m_muteModel.setValue( false ); + ch->m_name = ( index == 0 ) ? tr( "Master" ) : tr( "FX %1" ).arg( index ); + ch->m_volumeModel.setDisplayName(ch->m_name ); + // send only to master + if( index > 0) + { + // delete existing sends + for( int i=0; im_sends.size(); ++i) + { + deleteChannelSend(index, ch->m_sends[i]); + } + + // add send to master + createChannelSend(index, 0); + } + + // delete receives + for( int i=0; im_receives.size(); ++i) + { + deleteChannelSend(ch->m_receives[i], index); + } + +} void FxMixer::saveSettings( QDomDocument & _doc, QDomElement & _this ) { - for( int i = 0; i <= NumFxChannels; ++i ) + for( int i = 0; i < m_fxChannels.size(); ++i ) { QDomElement fxch = _doc.createElement( QString( "fxchannel" ) ); _this.appendChild( fxch ); @@ -295,7 +327,7 @@ void FxMixer::loadSettings( const QDomElement & _this ) { clear(); QDomNode node = _this.firstChild(); - for( int i = 0; i <= NumFxChannels; ++i ) + for( int i = 0; i <= 64; ++i ) // TODO make this work { QDomElement fxch = node.toElement(); int num = fxch.attribute( "num" ).toInt(); diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index 35fdb62cd..819ef0be2 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -61,10 +61,6 @@ #endif -static QVector __fx_channel_jobs( NumFxChannels ); - - - class MixerWorkerThread : public QThread { public: @@ -284,11 +280,6 @@ mixer::mixer() : clearAudioBuffer( m_inputBuffer[i], m_inputBufferSize[i] ); } - for( int i = 1; i < NumFxChannels+1; ++i ) - { - __fx_channel_jobs[i-1] = (fx_ch_t) i; - } - // just rendering? if( !engine::hasGUI() ) { diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index bde8e5a04..3be23bcfe 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -22,6 +22,8 @@ * */ +#include + #include #include #include @@ -31,9 +33,12 @@ #include #include #include +#include + #include "FxMixerView.h" #include "fader.h" +#include "knob.h" #include "EffectRackView.h" #include "engine.h" #include "embed.h" @@ -44,26 +49,40 @@ #include "pixmap_button.h" +class SendIndicator : public QWidget +{ + public: + SendIndicator( QWidget * _parent ) : + QWidget( _parent ) + { + setFixedSize(23, 16); + } + +}; class FxLine : public QWidget { public: - FxLine( QWidget * _parent, FxMixerView * _mv, QString & _name ) : + FxLine( QWidget * _parent, FxMixerView * _mv, QString & _name, + int _channelIndex) : QWidget( _parent ), + m_channelIndex( _channelIndex ), m_mv( _mv ), m_name( _name ) { - setFixedSize( 32, 232 ); + setFixedSize( 32, 287 ); setAttribute( Qt::WA_OpaquePaintEvent, true ); setCursor( QCursor( embed::getIconPixmap( "hand" ), 0, 0 ) ); } virtual void paintEvent( QPaintEvent * ) { + bool sendToThis = engine::fxMixer()->channelSendModel( + m_mv->currentFxLine()->m_channelIndex, m_channelIndex) != NULL; QPainter painter; painter.begin( this ); - engine::getLmmsStyle()->drawFxLine( &painter, - this, m_name, m_mv->currentFxLine() == this ); + engine::getLmmsStyle()->drawFxLine( &painter, this, m_name, + m_mv->currentFxLine() == this, sendToThis ); painter.end(); } @@ -87,7 +106,8 @@ public: } } - + knob * m_sendKnob; + int m_channelIndex; private: FxMixerView * m_mv; QString & m_name; @@ -114,10 +134,6 @@ FxMixerView::FxMixerView() : setWindowTitle( tr( "FX-Mixer" ) ); setWindowIcon( embed::getIconPixmap( "fx_mixer" ) ); - m_fxLineBanks = new QStackedLayout; - m_fxLineBanks->setSpacing( 0 ); - m_fxLineBanks->setMargin( 1 ); - m_fxRacksLayout = new QStackedLayout; m_fxRacksLayout->setSpacing( 0 ); m_fxRacksLayout->setMargin( 0 ); @@ -128,93 +144,40 @@ FxMixerView::FxMixerView() : ml->setSpacing( 0 ); ml->addSpacing( 6 ); + m_fxChannelViews.resize(m->numChannels()); + channelArea = new QScrollArea(this); + chLayout = new QHBoxLayout(channelArea); - QHBoxLayout * banks[NumFxChannels/16]; - for( int i = 0; i < NumFxChannels/16; ++i ) + // add master channel + FxChannelView * masterView = &m_fxChannelViews[0]; + addFxLine(0, this, ml); + ml->addSpacing(5); + QSize fxLineSize = masterView->m_fxLine->size(); + + chLayout->setSizeConstraint(QLayout::SetMinimumSize); + channelArea->setWidgetResizable(true); + + // add mixer channels + for( int i = 1; i < m_fxChannelViews.size(); ++i ) { - QWidget * w = new QWidget( this ); - banks[i] = new QHBoxLayout( w ); - banks[i]->setMargin( 5 ); - banks[i]->setSpacing( 1 ); - m_fxLineBanks->addWidget( w ); + addFxLine(i, channelArea, chLayout); } + // add the scrolling section to the main layout + ml->addLayout(chLayout); - for( int i = 0; i < NumFxChannels+1; ++i ) - { - FxChannelView * cv = &m_fxChannelViews[i]; - if( i == 0 ) - { - cv->m_fxLine = new FxLine( NULL, this, - m->m_fxChannels[i]->m_name ); - ml->addWidget( cv->m_fxLine ); - ml->addSpacing( 10 ); - } - else - { - const int bank = (i-1) / 16; - cv->m_fxLine = new FxLine( NULL, this, - m->m_fxChannels[i]->m_name ); - banks[bank]->addWidget( cv->m_fxLine ); - } - lcdSpinBox * l = new lcdSpinBox( 2, cv->m_fxLine ); - l->model()->setRange( i, i ); - l->model()->setValue( i ); - l->move( 2, 4 ); - l->setMarginWidth( 1 ); + // show the add new effect channel button + QPushButton * newChannelBtn = new QPushButton("new", this ); + newChannelBtn->setFont(QFont("sans-serif", 10, 1, false)); + newChannelBtn->setFixedSize(fxLineSize); + connect( newChannelBtn, SIGNAL(clicked()), this, SLOT(addNewChannel())); + ml->addWidget( newChannelBtn ); - - cv->m_fader = new fader( &m->m_fxChannels[i]->m_volumeModel, - tr( "FX Fader %1" ).arg( i ), - cv->m_fxLine ); - cv->m_fader->move( 15-cv->m_fader->width()/2, - cv->m_fxLine->height()- - cv->m_fader->height()-5 ); - - cv->m_muteBtn = new pixmapButton( cv->m_fxLine, tr( "Mute" ) ); - cv->m_muteBtn->setModel( &m->m_fxChannels[i]->m_muteModel ); - cv->m_muteBtn->setActiveGraphic( - embed::getIconPixmap( "led_off" ) ); - cv->m_muteBtn->setInactiveGraphic( - embed::getIconPixmap( "led_green" ) ); - cv->m_muteBtn->setCheckable( true ); - cv->m_muteBtn->move( 9, cv->m_fader->y()-16); - toolTip::add( cv->m_muteBtn, tr( "Mute this FX channel" ) ); - - cv->m_rackView = new EffectRackView( - &m->m_fxChannels[i]->m_fxChain, this ); - m_fxRacksLayout->addWidget( cv->m_rackView ); - if( i == 0 ) - { - QVBoxLayout * l = new QVBoxLayout; - l->addSpacing( 10 ); - QButtonGroup * g = new QButtonGroup( this ); - m_bankButtons = g; - g->setExclusive( true ); - for( int j = 0; j < 4; ++j ) - { - QToolButton * btn = new QToolButton; - btn->setText( QString( 'A'+j ) ); - btn->setCheckable( true ); - btn->setSizePolicy( QSizePolicy::Preferred, - QSizePolicy::Expanding ); - l->addWidget( btn ); - g->addButton( btn, j ); - btn->setChecked( j == 0); - } - l->addSpacing( 10 ); - ml->addLayout( l ); - connect( g, SIGNAL( buttonClicked( int ) ), - m_fxLineBanks, SLOT( setCurrentIndex( int ) ) ); - } - } - - ml->addLayout( m_fxLineBanks ); ml->addLayout( m_fxRacksLayout ); + setLayout( ml ); updateGeometry(); - m_fxLineBanks->setCurrentIndex( 0 ); setCurrentFxLine( m_fxChannelViews[0].m_fxLine ); // timer for updating faders @@ -226,10 +189,9 @@ FxMixerView::FxMixerView() : QMdiSubWindow * subWin = engine::mainWindow()->workspace()->addSubWindow( this ); Qt::WindowFlags flags = subWin->windowFlags(); - flags |= Qt::MSWindowsFixedSizeDialogHint; flags &= ~Qt::WindowMaximizeButtonHint; subWin->setWindowFlags( flags ); - subWin->layout()->setSizeConstraint(QLayout::SetFixedSize); + subWin->layout()->setSizeConstraint(QLayout::SetMinimumSize); parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false ); parentWidget()->move( 5, 310 ); @@ -239,6 +201,54 @@ FxMixerView::FxMixerView() : } +void FxMixerView::addFxLine(int i, QWidget * parent, QLayout * layout) +{ + FxMixer * m = engine::fxMixer(); + + FxChannelView * cv = &m_fxChannelViews[i]; + + cv->m_fxLine = new FxLine( parent, this, + m->m_fxChannels[i]->m_name, i ); + layout->addWidget(cv->m_fxLine); + + // mixer sends knob + cv->m_fxLine->m_sendKnob = new knob(0, cv->m_fxLine, + tr("Channel send amount")); + cv->m_fxLine->m_sendKnob->move(0, 22); + cv->m_fxLine->m_sendKnob->setVisible(false); + + // send light indicator + + + // channel number + lcdSpinBox * l = new lcdSpinBox( 2, cv->m_fxLine ); + l->model()->setRange( i, i ); + l->model()->setValue( i ); + l->move( 2, 58 ); + l->setMarginWidth( 1 ); + + + cv->m_fader = new fader( &m->m_fxChannels[i]->m_volumeModel, + tr( "FX Fader %1" ).arg( i ), + cv->m_fxLine ); + cv->m_fader->move( 15-cv->m_fader->width()/2, + cv->m_fxLine->height()- + cv->m_fader->height()-5 ); + + cv->m_muteBtn = new pixmapButton( cv->m_fxLine, tr( "Mute" ) ); + cv->m_muteBtn->setModel( &m->m_fxChannels[i]->m_muteModel ); + cv->m_muteBtn->setActiveGraphic( + embed::getIconPixmap( "led_off" ) ); + cv->m_muteBtn->setInactiveGraphic( + embed::getIconPixmap( "led_green" ) ); + cv->m_muteBtn->setCheckable( true ); + cv->m_muteBtn->move( 9, cv->m_fader->y()-16); + toolTip::add( cv->m_muteBtn, tr( "Mute this FX channel" ) ); + + cv->m_rackView = new EffectRackView( + &m->m_fxChannels[i]->m_fxChain, this ); + m_fxRacksLayout->addWidget( cv->m_rackView ); +} FxMixerView::~FxMixerView() @@ -247,6 +257,18 @@ FxMixerView::~FxMixerView() +void FxMixerView::addNewChannel() +{ + // add new fx mixer channel and redraw the form. + FxMixer * mix = engine::fxMixer(); + + int newChannelIndex = mix->createChannel(); + m_fxChannelViews.push_back(FxChannelView()); + + addFxLine(newChannelIndex, channelArea, chLayout); +} + + void FxMixerView::saveSettings( QDomDocument & _doc, QDomElement & _this ) { @@ -266,13 +288,29 @@ void FxMixerView::loadSettings( const QDomElement & _this ) void FxMixerView::setCurrentFxLine( FxLine * _line ) { + FxMixer * mix = engine::fxMixer(); + + // select m_currentFxLine = _line; - for( int i = 0; i < NumFxChannels+1; ++i ) + m_fxRacksLayout->setCurrentIndex( _line->m_channelIndex ); + + // set up send knob + for(int i = 0; i < m_fxChannelViews.size(); ++i) { - if( m_fxChannelViews[i].m_fxLine == _line ) + // does current channel send to this channel? + FloatModel * sendModel = mix->channelSendModel(_line->m_channelIndex, i); + if( sendModel == NULL ) { - m_fxRacksLayout->setCurrentIndex( i ); + // does not send, hide send knob + m_fxChannelViews[i].m_fxLine->m_sendKnob->setVisible(false); } + else + { + // it does send, show knob and connect + m_fxChannelViews[i].m_fxLine->m_sendKnob->setVisible(true); + m_fxChannelViews[i].m_fxLine->m_sendKnob->setModel(sendModel); + } + m_fxChannelViews[i].m_fxLine->update(); } } @@ -281,12 +319,7 @@ void FxMixerView::setCurrentFxLine( FxLine * _line ) void FxMixerView::setCurrentFxLine( int _line ) { - if ( _line >= 0 && _line < NumFxChannels+1 ) - { - setCurrentFxLine( m_fxChannelViews[_line].m_fxLine ); - - m_bankButtons->button( (_line-1) / 16 )->click(); - } + setCurrentFxLine( m_fxChannelViews[_line].m_fxLine ); } @@ -294,7 +327,7 @@ void FxMixerView::setCurrentFxLine( int _line ) void FxMixerView::clear() { - for( int i = 0; i <= NumFxChannels; ++i ) + for( int i = 0; i < m_fxChannelViews.size(); ++i ) { m_fxChannelViews[i].m_rackView->clearViews(); } @@ -306,7 +339,7 @@ void FxMixerView::clear() void FxMixerView::updateFaders() { FxMixer * m = engine::fxMixer(); - for( int i = 0; i < NumFxChannels+1; ++i ) + for( int i = 0; i < m_fxChannelViews.size(); ++i ) { const float opl = m_fxChannelViews[i].m_fader->getPeak_L(); const float opr = m_fxChannelViews[i].m_fader->getPeak_R(); diff --git a/src/gui/classic_style.cpp b/src/gui/classic_style.cpp index 602fcdf78..9160c9028 100644 --- a/src/gui/classic_style.cpp +++ b/src/gui/classic_style.cpp @@ -279,7 +279,7 @@ int ClassicStyle::pixelMetric( PixelMetric _metric, void ClassicStyle::drawFxLine( QPainter * _painter, const QWidget *_fxLine, - const QString & _name, bool _active ) + const QString & _name, bool _active, bool _sendToThis ) { int width = _fxLine->rect().width(); int height = _fxLine->rect().height(); @@ -293,10 +293,18 @@ void ClassicStyle::drawFxLine( QPainter * _painter, const QWidget *_fxLine, p->setPen( QColor( 20, 24, 32 ) ); p->drawRect( 0, 0, width-1, height-1 ); + // draw the mixer send background + if( _sendToThis ) + { + p->drawPixmap(2, 0, 28, 56, + embed::getIconPixmap("send_bg_arrow", 28, 56)); + } + + // draw the channel name p->rotate( -90 ); p->setPen( _active ? QColor( 0, 255, 0 ) : Qt::white ); p->setFont( pointSizeF( _fxLine->font(), 7.5f ) ); - p->drawText( -90, 20, _name ); + p->drawText( -145, 20, _name ); } void ClassicStyle::drawTrackContentBackground(QPainter * _painter, diff --git a/src/gui/cusis_style.cpp b/src/gui/cusis_style.cpp index 87a739a27..a78d14762 100644 --- a/src/gui/cusis_style.cpp +++ b/src/gui/cusis_style.cpp @@ -881,7 +881,7 @@ int CusisStyle::pixelMetric( PixelMetric _metric, const QStyleOption * _option, void CusisStyle::drawFxLine( QPainter * _painter, const QWidget *_fxLine, - const QString & _name, bool _active ) + const QString & _name, bool _active, bool _sendToThis ) { int width = _fxLine->rect().width(); int height = _fxLine->rect().height(); diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index cbddc519e..f56d6487b 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -103,7 +103,7 @@ InstrumentTrack::InstrumentTrack( trackContainer * _tc ) : this, tr( "Panning" ) ), m_pitchModel( 0, -100, 100, 1, this, tr( "Pitch" ) ), m_pitchRangeModel( 1, 1, 24, this, tr( "Pitch range" ) ), - m_effectChannelModel( 0, 0, NumFxChannels, this, tr( "FX channel" ) ), + m_effectChannelModel( 0, 0, 10, this, tr( "FX channel" ) ), // change this so it's a combo box, all the channels and then new. m_instrument( NULL ), m_soundShaping( this ), m_arpeggiator( this ), From 289f004c281bc835ad0b593da16fe36ab42051b5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 16 Sep 2009 17:43:28 -0700 Subject: [PATCH 03/25] Pretty interface in default theme for fx sends Created a user interface so you can add and remove FX sends as well as tweak how much you are sending. The gui does not yet prevent the user from creating endless render loops. Additionally, there seems to be some issues to work out regarding rendering with multiple sends. --- data/themes/default/mixer_send_off.png | Bin 0 -> 467 bytes data/themes/default/mixer_send_on.png | Bin 0 -> 471 bytes include/FxLine.h | 38 ++++ include/FxMixerView.h | 70 +++++--- include/SendButtonIndicator.h | 32 ++++ src/core/FxMixer.cpp | 15 +- src/gui/FxLine.cpp | 77 +++++++++ src/gui/FxMixerView.cpp | 231 ++++++++----------------- src/gui/SendButtonIndicator.cpp | 53 ++++++ 9 files changed, 324 insertions(+), 192 deletions(-) create mode 100644 data/themes/default/mixer_send_off.png create mode 100644 data/themes/default/mixer_send_on.png create mode 100644 include/FxLine.h create mode 100644 include/SendButtonIndicator.h create mode 100644 src/gui/FxLine.cpp create mode 100644 src/gui/SendButtonIndicator.cpp diff --git a/data/themes/default/mixer_send_off.png b/data/themes/default/mixer_send_off.png new file mode 100644 index 0000000000000000000000000000000000000000..6f426c36f9599a1fd8dbef8de6a5577d8bd17a86 GIT binary patch literal 467 zcmeAS@N?(olHy`uVBq!ia0vp^;y^6G!3HG%>OYYHQjEnx?oJHr&dIz4a@dl*-CY>| zgW!U_%O?XxI14-?iy0WWg+Z8+Vb&aw9`+JXUsv{pv8=Oo-U3d7N_4%_Vs2;6ltAbUXgxcsrj+STZmA#r#x`+VakQr%=69w}#N>KWdkoG$usOe!NJm`PLE-Y4eAX_o^#) zJU<}%Tzq=`8c(k5D{T8hgc2^izxdWfvfrNZ#8d7m-md+HR^958ToxOiTB@|OhB4vU z)S#m#y5Ak`5&v(|sw-B&;AQZ1^>bP0 Hl+XkKQoFyV literal 0 HcmV?d00001 diff --git a/data/themes/default/mixer_send_on.png b/data/themes/default/mixer_send_on.png new file mode 100644 index 0000000000000000000000000000000000000000..6861c7acdf717cd4e6b164956346fa7413447423 GIT binary patch literal 471 zcmeAS@N?(olHy`uVBq!ia0vp^;y^6G!3HG%>OYYHQjEnx?oJHr&dIz4a@dl*-CY>| zgW!U_%O?XxI14-?iy0WWg+Z8+Vb&aw9`+JXUsv{wc6x=<11Hv2mfEF_vdAc};Se#DYvmx@kJ##OEFf;GL6%QjA zxtW=n4{tlXUvgUh@3SBN39IC7_j#K3;pD%rMFMKmL{6L%cyj32gUNrVCY}z-V*`R6 zQF?rAY__IpZD$J1*b*7lIvy5Z|Cf}Z{U9M=a(YriN{Wl2fb5#vzuOOzd3YmbCL~-8NJ^Tq>yP;1M`!m--mCf1etospCLf+?{(r}`jvqdH zLg4l3V+B92#k|p^cO=b!v%Y|bc^{t+Pxkje$1*LIt7aG* z8yme?ZERRjyvg8=-9PsH4HB6~#vclejcR^1&HSElaM9{xJq9w*jO6^-+9c$|M5Zr} zK62v3i3iE&PZ=a%)fZ{5-L&7Rz)9J}yo+~HnaOW{{lkpCf(e%sr~M281~G%DtDnm{ Hr-UW|EQGns literal 0 HcmV?d00001 diff --git a/include/FxLine.h b/include/FxLine.h new file mode 100644 index 000000000..1700ebba3 --- /dev/null +++ b/include/FxLine.h @@ -0,0 +1,38 @@ +#ifndef FXLINE_H +#define FXLINE_H + +#include +#include + +#include "knob.h" +#include "SendButtonIndicator.h" + +class FxMixerView; +class SendButtonIndicator; + +class FxLine : public QWidget +{ + Q_OBJECT +public: + FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex); + + virtual void paintEvent( QPaintEvent * ); + virtual void mousePressEvent( QMouseEvent * ); + virtual void mouseDoubleClickEvent( QMouseEvent * ); + + inline int channelIndex() { return m_channelIndex; } + + knob * m_sendKnob; + SendButtonIndicator * m_sendBtn; + + +private: + FxMixerView * m_mv; + + + int m_channelIndex; + +} ; + + +#endif // FXLINE_H diff --git a/include/FxMixerView.h b/include/FxMixerView.h index 297ab5061..461379728 100644 --- a/include/FxMixerView.h +++ b/include/FxMixerView.h @@ -29,60 +29,74 @@ #include #include +#include "FxLine.h" #include "FxMixer.h" #include "ModelView.h" +#include "engine.h" +#include "fader.h" +#include "pixmap_button.h" +#include "tooltip.h" +#include "embed.h" +#include "EffectRackView.h" class QStackedLayout; class QButtonGroup; -class fader; class FxLine; -class EffectRackView; -class pixmapButton; - class FxMixerView : public QWidget, public ModelView, public SerializingObjectHook { Q_OBJECT public: - FxMixerView(); - virtual ~FxMixerView(); - - virtual void saveSettings( QDomDocument & _doc, QDomElement & _this ); - virtual void loadSettings( const QDomElement & _this ); - - FxLine * currentFxLine() - { - return m_currentFxLine; - } - void setCurrentFxLine( FxLine * _line ); - void setCurrentFxLine( int _line ); - - void clear(); - - -private slots: - void updateFaders(); - void addNewChannel(); - -private: struct FxChannelView { + FxChannelView(QWidget * _parent, FxMixerView * _mv, int _chIndex ); + FxLine * m_fxLine; EffectRackView * m_rackView; pixmapButton * m_muteBtn; fader * m_fader; } ; - QVector m_fxChannelViews; + + FxMixerView(); + virtual ~FxMixerView(); + + virtual void saveSettings( QDomDocument & _doc, QDomElement & _this ); + virtual void loadSettings( const QDomElement & _this ); + + inline FxLine * currentFxLine() + { + return m_currentFxLine; + } + + inline FxChannelView * channelView(int index) + { + return m_fxChannelViews[index]; + } + + void setCurrentFxLine( FxLine * _line ); + void setCurrentFxLine( int _line ); + + void clear(); + + + // display the send button and knob correctly + void updateFxLine(int i); + +private slots: + void updateFaders(); + void addNewChannel(); + +private: + + QVector m_fxChannelViews; QStackedLayout * m_fxRacksLayout; FxLine * m_currentFxLine; QScrollArea * channelArea; QHBoxLayout * chLayout; - - void addFxLine(int i, QWidget * parent, QLayout * layout); } ; #endif diff --git a/include/SendButtonIndicator.h b/include/SendButtonIndicator.h new file mode 100644 index 000000000..f2c7ef44d --- /dev/null +++ b/include/SendButtonIndicator.h @@ -0,0 +1,32 @@ +#ifndef SENDBUTTONINDICATOR_H +#define SENDBUTTONINDICATOR_H + +#include +#include +#include + +#include "FxLine.h" +#include "FxMixerView.h" + +class FxLine; +class FxMixerView; + +class SendButtonIndicator : public QLabel { + public: + SendButtonIndicator( QWidget * _parent, FxLine * _owner, + FxMixerView * _mv); + + virtual void mousePressEvent( QMouseEvent * e ); + void updateLightStatus(); + + private: + + FxLine * m_parent; + FxMixerView * m_mv; + QPixmap qpmOn; + QPixmap qpmOff; + + FloatModel * getSendModel(); +}; + +#endif // SENDBUTTONINDICATOR_H diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 53c1152a7..b2dd852e2 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -74,7 +74,10 @@ FxMixer::~FxMixer() { for( int i = 0; i < m_fxChannels.size(); ++i ) { - delete m_fxChannels[i]->m_sendAmount[i]; + for( int j = 0; j < m_fxChannels[i]->m_sendAmount.size(); ++j) + { + delete m_fxChannels[i]->m_sendAmount[j]; + } delete m_fxChannels[i]; } } @@ -187,6 +190,11 @@ void FxMixer::processChannel( fx_ch_t _ch, sampleFrame * _buf ) { const fpp_t fpp = engine::getMixer()->framesPerPeriod(); FxChannel * thisCh = m_fxChannels[_ch]; + if( _buf == NULL ) + { + _buf = thisCh->m_buffer; + } + if( ! thisCh->m_muteModel.value() ) { // do mixer sends. loop through the channels that send to this one @@ -212,10 +220,7 @@ void FxMixer::processChannel( fx_ch_t _ch, sampleFrame * _buf ) } - if( _buf == NULL ) - { - _buf = thisCh->m_buffer; - } + const float v = thisCh->m_volumeModel.value(); thisCh->m_fxChain.startRunning(); diff --git a/src/gui/FxLine.cpp b/src/gui/FxLine.cpp new file mode 100644 index 000000000..fe92ba66b --- /dev/null +++ b/src/gui/FxLine.cpp @@ -0,0 +1,77 @@ +#include "FxLine.h" + +#include +#include +#include +#include + +#include "FxMixer.h" +#include "FxMixerView.h" +#include "embed.h" +#include "engine.h" +#include "lcd_spinbox.h" +#include "SendButtonIndicator.h" + +FxLine::FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex) : + QWidget( _parent ), + m_mv( _mv ), + m_channelIndex( _channelIndex ) +{ + setFixedSize( 32, 287 ); + setAttribute( Qt::WA_OpaquePaintEvent, true ); + setCursor( QCursor( embed::getIconPixmap( "hand" ), 0, 0 ) ); + + // mixer sends knob + m_sendKnob = new knob(0, this, tr("Channel send amount")); + m_sendKnob->move(0, 22); + m_sendKnob->setVisible(false); + + // send button indicator + m_sendBtn = new SendButtonIndicator(this, this, m_mv); + m_sendBtn->setPixmap(embed::getIconPixmap("mixer_send_off", 23, 16)); + m_sendBtn->move(4,4); + + // channel number + lcdSpinBox * l = new lcdSpinBox( 2, this ); + l->model()->setRange( m_channelIndex, m_channelIndex ); + l->model()->setValue( m_channelIndex ); + l->move( 2, 58 ); + l->setMarginWidth( 1 ); +} + + +void FxLine::paintEvent( QPaintEvent * ) +{ + FxMixer * mix = engine::fxMixer(); + bool sendToThis = mix->channelSendModel( + m_mv->currentFxLine()->m_channelIndex, m_channelIndex) != NULL; + QPainter painter; + painter.begin( this ); + engine::getLmmsStyle()->drawFxLine( &painter, this, + mix->effectChannel(m_channelIndex)->m_name, + m_mv->currentFxLine() == this, sendToThis ); + painter.end(); +} + +void FxLine::mousePressEvent( QMouseEvent * ) +{ + m_mv->setCurrentFxLine( this ); +} + +void FxLine::mouseDoubleClickEvent( QMouseEvent * ) +{ + bool ok; + FxMixer * mix = engine::fxMixer(); + QString new_name = QInputDialog::getText( this, + FxMixerView::tr( "Rename FX channel" ), + FxMixerView::tr( "Enter the new name for this " + "FX channel" ), + QLineEdit::Normal, mix->effectChannel(m_channelIndex)->m_name, &ok ); + if( ok && !new_name.isEmpty() ) + { + mix->effectChannel(m_channelIndex)->m_name = new_name; + update(); + } +} + +#include "moc_FxLine.cxx" diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 3be23bcfe..cf2d5e72a 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -23,6 +23,7 @@ */ #include +#include #include #include @@ -35,87 +36,13 @@ #include #include - #include "FxMixerView.h" -#include "fader.h" #include "knob.h" -#include "EffectRackView.h" #include "engine.h" #include "embed.h" #include "MainWindow.h" #include "lcd_spinbox.h" #include "gui_templates.h" -#include "tooltip.h" -#include "pixmap_button.h" - - -class SendIndicator : public QWidget -{ - public: - SendIndicator( QWidget * _parent ) : - QWidget( _parent ) - { - setFixedSize(23, 16); - } - -}; - -class FxLine : public QWidget -{ -public: - FxLine( QWidget * _parent, FxMixerView * _mv, QString & _name, - int _channelIndex) : - QWidget( _parent ), - m_channelIndex( _channelIndex ), - m_mv( _mv ), - m_name( _name ) - { - setFixedSize( 32, 287 ); - setAttribute( Qt::WA_OpaquePaintEvent, true ); - setCursor( QCursor( embed::getIconPixmap( "hand" ), 0, 0 ) ); - } - - virtual void paintEvent( QPaintEvent * ) - { - bool sendToThis = engine::fxMixer()->channelSendModel( - m_mv->currentFxLine()->m_channelIndex, m_channelIndex) != NULL; - QPainter painter; - painter.begin( this ); - engine::getLmmsStyle()->drawFxLine( &painter, this, m_name, - m_mv->currentFxLine() == this, sendToThis ); - painter.end(); - } - - virtual void mousePressEvent( QMouseEvent * ) - { - m_mv->setCurrentFxLine( this ); - } - - virtual void mouseDoubleClickEvent( QMouseEvent * ) - { - bool ok; - QString new_name = QInputDialog::getText( this, - FxMixerView::tr( "Rename FX channel" ), - FxMixerView::tr( "Enter the new name for this " - "FX channel" ), - QLineEdit::Normal, m_name, &ok ); - if( ok && !new_name.isEmpty() ) - { - m_name = new_name; - update(); - } - } - - knob * m_sendKnob; - int m_channelIndex; -private: - FxMixerView * m_mv; - QString & m_name; - -} ; - - - FxMixerView::FxMixerView() : QWidget(), @@ -144,13 +71,16 @@ FxMixerView::FxMixerView() : ml->setSpacing( 0 ); ml->addSpacing( 6 ); - m_fxChannelViews.resize(m->numChannels()); channelArea = new QScrollArea(this); chLayout = new QHBoxLayout(channelArea); // add master channel - FxChannelView * masterView = &m_fxChannelViews[0]; - addFxLine(0, this, ml); + m_fxChannelViews.resize(m->numChannels()); + m_fxChannelViews[0] = new FxChannelView(this, this, 0); + FxChannelView * masterView = m_fxChannelViews[0]; + m_fxRacksLayout->addWidget( masterView->m_rackView ); + + ml->addWidget(masterView->m_fxLine); ml->addSpacing(5); QSize fxLineSize = masterView->m_fxLine->size(); @@ -160,7 +90,9 @@ FxMixerView::FxMixerView() : // add mixer channels for( int i = 1; i < m_fxChannelViews.size(); ++i ) { - addFxLine(i, channelArea, chLayout); + m_fxChannelViews[i] = new FxChannelView(channelArea, this, i); + chLayout->addWidget(m_fxChannelViews[i]->m_fxLine); + m_fxRacksLayout->addWidget( m_fxChannelViews[i]->m_rackView ); } // add the scrolling section to the main layout ml->addLayout(chLayout); @@ -178,7 +110,7 @@ FxMixerView::FxMixerView() : setLayout( ml ); updateGeometry(); - setCurrentFxLine( m_fxChannelViews[0].m_fxLine ); + setCurrentFxLine( m_fxChannelViews[0]->m_fxLine ); // timer for updating faders connect( engine::mainWindow(), SIGNAL( periodicUpdate() ), @@ -200,57 +132,6 @@ FxMixerView::FxMixerView() : setModel( m ); } - -void FxMixerView::addFxLine(int i, QWidget * parent, QLayout * layout) -{ - FxMixer * m = engine::fxMixer(); - - FxChannelView * cv = &m_fxChannelViews[i]; - - cv->m_fxLine = new FxLine( parent, this, - m->m_fxChannels[i]->m_name, i ); - layout->addWidget(cv->m_fxLine); - - // mixer sends knob - cv->m_fxLine->m_sendKnob = new knob(0, cv->m_fxLine, - tr("Channel send amount")); - cv->m_fxLine->m_sendKnob->move(0, 22); - cv->m_fxLine->m_sendKnob->setVisible(false); - - // send light indicator - - - // channel number - lcdSpinBox * l = new lcdSpinBox( 2, cv->m_fxLine ); - l->model()->setRange( i, i ); - l->model()->setValue( i ); - l->move( 2, 58 ); - l->setMarginWidth( 1 ); - - - cv->m_fader = new fader( &m->m_fxChannels[i]->m_volumeModel, - tr( "FX Fader %1" ).arg( i ), - cv->m_fxLine ); - cv->m_fader->move( 15-cv->m_fader->width()/2, - cv->m_fxLine->height()- - cv->m_fader->height()-5 ); - - cv->m_muteBtn = new pixmapButton( cv->m_fxLine, tr( "Mute" ) ); - cv->m_muteBtn->setModel( &m->m_fxChannels[i]->m_muteModel ); - cv->m_muteBtn->setActiveGraphic( - embed::getIconPixmap( "led_off" ) ); - cv->m_muteBtn->setInactiveGraphic( - embed::getIconPixmap( "led_green" ) ); - cv->m_muteBtn->setCheckable( true ); - cv->m_muteBtn->move( 9, cv->m_fader->y()-16); - toolTip::add( cv->m_muteBtn, tr( "Mute this FX channel" ) ); - - cv->m_rackView = new EffectRackView( - &m->m_fxChannels[i]->m_fxChain, this ); - m_fxRacksLayout->addWidget( cv->m_rackView ); -} - - FxMixerView::~FxMixerView() { } @@ -263,9 +144,10 @@ void FxMixerView::addNewChannel() FxMixer * mix = engine::fxMixer(); int newChannelIndex = mix->createChannel(); - m_fxChannelViews.push_back(FxChannelView()); - - addFxLine(newChannelIndex, channelArea, chLayout); + m_fxChannelViews.push_back(new FxChannelView(channelArea, this, + newChannelIndex)); + chLayout->addWidget(m_fxChannelViews[newChannelIndex]->m_fxLine); + m_fxRacksLayout->addWidget( m_fxChannelViews[newChannelIndex]->m_rackView ); } @@ -284,42 +166,73 @@ void FxMixerView::loadSettings( const QDomElement & _this ) } +FxMixerView::FxChannelView::FxChannelView(QWidget * _parent, FxMixerView * _mv, + int _chIndex ) +{ + m_fxLine = new FxLine(_parent, _mv, _chIndex); + + FxMixer * m = engine::fxMixer(); + m_fader = new fader( &m->effectChannel(_chIndex)->m_volumeModel, + tr( "FX Fader %1" ).arg( _chIndex ), m_fxLine ); + m_fader->move( 15-m_fader->width()/2, + m_fxLine->height()- + m_fader->height()-5 ); + + m_muteBtn = new pixmapButton( m_fxLine, tr( "Mute" ) ); + m_muteBtn->setModel( &m->effectChannel(_chIndex)->m_muteModel ); + m_muteBtn->setActiveGraphic( + embed::getIconPixmap( "led_off" ) ); + m_muteBtn->setInactiveGraphic( + embed::getIconPixmap( "led_green" ) ); + m_muteBtn->setCheckable( true ); + m_muteBtn->move( 9, m_fader->y()-16); + toolTip::add( m_muteBtn, tr( "Mute this FX channel" ) ); + + m_rackView = new EffectRackView( + &m->m_fxChannels[_chIndex]->m_fxChain, _mv ); +} void FxMixerView::setCurrentFxLine( FxLine * _line ) { - FxMixer * mix = engine::fxMixer(); - // select m_currentFxLine = _line; - m_fxRacksLayout->setCurrentIndex( _line->m_channelIndex ); + m_fxRacksLayout->setCurrentIndex( _line->channelIndex() ); // set up send knob for(int i = 0; i < m_fxChannelViews.size(); ++i) { - // does current channel send to this channel? - FloatModel * sendModel = mix->channelSendModel(_line->m_channelIndex, i); - if( sendModel == NULL ) - { - // does not send, hide send knob - m_fxChannelViews[i].m_fxLine->m_sendKnob->setVisible(false); - } - else - { - // it does send, show knob and connect - m_fxChannelViews[i].m_fxLine->m_sendKnob->setVisible(true); - m_fxChannelViews[i].m_fxLine->m_sendKnob->setModel(sendModel); - } - - m_fxChannelViews[i].m_fxLine->update(); + updateFxLine(i); } } +void FxMixerView::updateFxLine(int i) +{ + FxMixer * mix = engine::fxMixer(); + + // does current channel send to this channel? + FloatModel * sendModel = mix->channelSendModel(m_currentFxLine->channelIndex(), i); + if( sendModel == NULL ) + { + // does not send, hide send knob + m_fxChannelViews[i]->m_fxLine->m_sendKnob->setVisible(false); + } + else + { + // it does send, show knob and connect + m_fxChannelViews[i]->m_fxLine->m_sendKnob->setVisible(true); + m_fxChannelViews[i]->m_fxLine->m_sendKnob->setModel(sendModel); + } + + + m_fxChannelViews[i]->m_fxLine->update(); + m_fxChannelViews[i]->m_fxLine->m_sendBtn->updateLightStatus(); +} void FxMixerView::setCurrentFxLine( int _line ) { - setCurrentFxLine( m_fxChannelViews[_line].m_fxLine ); + setCurrentFxLine( m_fxChannelViews[_line]->m_fxLine ); } @@ -329,7 +242,7 @@ void FxMixerView::clear() { for( int i = 0; i < m_fxChannelViews.size(); ++i ) { - m_fxChannelViews[i].m_rackView->clearViews(); + m_fxChannelViews[i]->m_rackView->clearViews(); } } @@ -341,26 +254,26 @@ void FxMixerView::updateFaders() FxMixer * m = engine::fxMixer(); for( int i = 0; i < m_fxChannelViews.size(); ++i ) { - const float opl = m_fxChannelViews[i].m_fader->getPeak_L(); - const float opr = m_fxChannelViews[i].m_fader->getPeak_R(); + const float opl = m_fxChannelViews[i]->m_fader->getPeak_L(); + const float opr = m_fxChannelViews[i]->m_fader->getPeak_R(); const float fall_off = 1.2; if( m->m_fxChannels[i]->m_peakLeft > opl ) { - m_fxChannelViews[i].m_fader->setPeak_L( + m_fxChannelViews[i]->m_fader->setPeak_L( m->m_fxChannels[i]->m_peakLeft ); } else { - m_fxChannelViews[i].m_fader->setPeak_L( opl/fall_off ); + m_fxChannelViews[i]->m_fader->setPeak_L( opl/fall_off ); } if( m->m_fxChannels[i]->m_peakRight > opr ) { - m_fxChannelViews[i].m_fader->setPeak_R( + m_fxChannelViews[i]->m_fader->setPeak_R( m->m_fxChannels[i]->m_peakRight ); } else { - m_fxChannelViews[i].m_fader->setPeak_R( opr/fall_off ); + m_fxChannelViews[i]->m_fader->setPeak_R( opr/fall_off ); } } } diff --git a/src/gui/SendButtonIndicator.cpp b/src/gui/SendButtonIndicator.cpp new file mode 100644 index 000000000..a932f136a --- /dev/null +++ b/src/gui/SendButtonIndicator.cpp @@ -0,0 +1,53 @@ +#include "SendButtonIndicator.h" + +#include "engine.h" +#include "FxMixer.h" +#include "Model.h" + +SendButtonIndicator:: SendButtonIndicator( QWidget * _parent, FxLine * _owner, + FxMixerView * _mv) : + QLabel( _parent ), + m_parent( _owner ), + m_mv( _mv ) +{ + qpmOff = embed::getIconPixmap("mixer_send_off", 23, 16); + qpmOn = embed::getIconPixmap("mixer_send_on", 23, 16); + + // don't do any initializing yet, because the FxMixerView and FxLine + // that were passed to this constructor are not done with their constructors + // yet. + +} + +void SendButtonIndicator::mousePressEvent( QMouseEvent * e ) +{ + FxMixer * mix = engine::fxMixer(); + int from = m_mv->currentFxLine()->channelIndex(); + int to = m_parent->channelIndex(); + FloatModel * sendModel = mix->channelSendModel(from, to); + if( sendModel == NULL ) + { + // not sending. create a mixer send. + mix->createChannelSend( from, to ); + } + else + { + // sending. delete the mixer send. + mix->deleteChannelSend( from, to ); + } + + m_mv->updateFxLine(m_parent->channelIndex()); + updateLightStatus(); +} + +FloatModel * SendButtonIndicator::getSendModel() +{ + FxMixer * mix = engine::fxMixer(); + return mix->channelSendModel( + m_mv->currentFxLine()->channelIndex(), m_parent->channelIndex()); +} + +void SendButtonIndicator::updateLightStatus() +{ + setPixmap( getSendModel() == NULL ? qpmOff : qpmOn ); +} From ce7891b7bd62ade0843eef924307626a82a2de02 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 16 Sep 2009 20:26:47 -0700 Subject: [PATCH 04/25] Fix mixer sends rendering in the backend Fixed: Buffers was cleared too early resulting in some combinations of sends not working. --- src/core/FxMixer.cpp | 2 -- src/core/mixer.cpp | 13 +++++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index b2dd852e2..03aa9b2da 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -215,8 +215,6 @@ void FxMixer::processChannel( fx_ch_t _ch, sampleFrame * _buf ) _buf[f][0] += ch_buf[f][0] * v * amt; _buf[f][1] += ch_buf[f][1] * v * amt; } - engine::getMixer()->clearAudioBuffer( ch_buf, - engine::getMixer()->framesPerPeriod() ); } diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index 819ef0be2..b33e4c49f 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -503,6 +503,8 @@ sampleFrameA * mixer::renderNextBuffer() MicroTimer timer; static song::playPos last_metro_pos = -1; + FxMixer * fxm = engine::fxMixer(); + song::playPos p = engine::getSong()->getPlayPos( song::Mode_PlayPattern ); if( engine::getSong()->playMode() == song::Mode_PlayPattern && @@ -556,7 +558,7 @@ sampleFrameA * mixer::renderNextBuffer() clearAudioBuffer( m_writeBuf, m_framesPerPeriod ); // prepare master mix (clear internal buffers etc.) - engine::fxMixer()->prepareMasterMix(); + fxm->prepareMasterMix(); // create play-handles for new notes, samples etc. engine::getSong()->processNextBuffer(); @@ -606,10 +608,17 @@ sampleFrameA * mixer::renderNextBuffer() // STAGE 4: do master mix in FX mixer - engine::fxMixer()->masterMix( m_writeBuf ); + fxm->masterMix( m_writeBuf ); WAIT_FOR_JOBS(); + // clear all channel buffers + for( int i = 0; i < fxm->numChannels(); ++i) + { + engine::getMixer()->clearAudioBuffer( fxm->effectChannel(i)->m_buffer, + engine::getMixer()->framesPerPeriod() ); + } + unlock(); From 504a03f2cf1a1a5f571dd5cdad361073210d9fb1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 Sep 2009 19:16:19 -0700 Subject: [PATCH 05/25] prevent infinite mixing loops Prevent infinite mixing loops by disabling the send button for a channel line when clicking it would cause an infinite loop. --- include/FxMixer.h | 4 ++++ src/core/FxMixer.cpp | 20 ++++++++++++++++++++ src/gui/FxMixerView.cpp | 23 +++++++++++++++-------- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/include/FxMixer.h b/include/FxMixer.h index 116dbcde0..b53045015 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -97,6 +97,10 @@ public: // delete the connection made by createChannelSend void deleteChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel); + // determine if adding a send from sendFrom to + // sendTo would result in an infinite mixer loop. + bool isInfiniteLoop(fx_ch_t fromChannel, fx_ch_t toChannel); + // return the FloatModel of fromChannel sending its output to the input of // toChannel. NULL if there is no send. FloatModel * channelSendModel(fx_ch_t fromChannel, fx_ch_t toChannel); diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 03aa9b2da..8306b28c4 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -158,6 +158,26 @@ void FxMixer::deleteChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel) } +bool FxMixer::isInfiniteLoop(fx_ch_t sendFrom, fx_ch_t sendTo) { + // can't send master to anything + if( sendFrom == 0 ) return true; + + // can't send channel to itself + if( sendFrom == sendTo ) return true; + + // follow sendTo's outputs recursively looking for something that sends + // to sendFrom + for(int i=0; im_sends.size(); ++i) + { + if( isInfiniteLoop( sendFrom, m_fxChannels[sendTo]->m_sends[i] ) ) + { + return true; + } + } + + return false; +} + // how much does fromChannel send its output to the input of toChannel? FloatModel * FxMixer::channelSendModel(fx_ch_t fromChannel, fx_ch_t toChannel) diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index cf2d5e72a..f1d591fc8 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -148,6 +148,8 @@ void FxMixerView::addNewChannel() newChannelIndex)); chLayout->addWidget(m_fxChannelViews[newChannelIndex]->m_fxLine); m_fxRacksLayout->addWidget( m_fxChannelViews[newChannelIndex]->m_rackView ); + + updateFxLine(newChannelIndex); } @@ -207,29 +209,34 @@ void FxMixerView::setCurrentFxLine( FxLine * _line ) } -void FxMixerView::updateFxLine(int i) +void FxMixerView::updateFxLine(int index) { FxMixer * mix = engine::fxMixer(); // does current channel send to this channel? - FloatModel * sendModel = mix->channelSendModel(m_currentFxLine->channelIndex(), i); + int selIndex = m_currentFxLine->channelIndex(); + FxLine * thisLine = m_fxChannelViews[index]->m_fxLine; + FloatModel * sendModel = mix->channelSendModel(selIndex, index); if( sendModel == NULL ) { // does not send, hide send knob - m_fxChannelViews[i]->m_fxLine->m_sendKnob->setVisible(false); + thisLine->m_sendKnob->setVisible(false); } else { // it does send, show knob and connect - m_fxChannelViews[i]->m_fxLine->m_sendKnob->setVisible(true); - m_fxChannelViews[i]->m_fxLine->m_sendKnob->setModel(sendModel); + thisLine->m_sendKnob->setVisible(true); + thisLine->m_sendKnob->setModel(sendModel); } - - m_fxChannelViews[i]->m_fxLine->update(); - m_fxChannelViews[i]->m_fxLine->m_sendBtn->updateLightStatus(); + // disable the send button if it would cause an infinite loop + thisLine->m_sendBtn->setVisible(! mix->isInfiniteLoop(selIndex, index)); + thisLine->m_sendBtn->updateLightStatus(); + thisLine->update(); } + + void FxMixerView::setCurrentFxLine( int _line ) { setCurrentFxLine( m_fxChannelViews[_line]->m_fxLine ); From d68d53b83a8158a3c46360dfbf0c2102504a5782 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 Sep 2009 20:54:30 -0700 Subject: [PATCH 06/25] Scrollbar for the fx mixer channels Still need to fix up the rest of the fx mixer --- include/FxMixerView.h | 1 + src/gui/FxMixerView.cpp | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/include/FxMixerView.h b/include/FxMixerView.h index 461379728..f752672c9 100644 --- a/include/FxMixerView.h +++ b/include/FxMixerView.h @@ -97,6 +97,7 @@ private: QScrollArea * channelArea; QHBoxLayout * chLayout; + QWidget * m_channelAreaWidget; } ; #endif diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index f1d591fc8..f41c9bba4 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -56,7 +56,9 @@ FxMixerView::FxMixerView() : //pal.setColor( QPalette::Background, QColor( 72, 76, 88 ) ); //setPalette( pal ); setAutoFillBackground( true ); - setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum ); + setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ); + setFixedSize(600, 300); + setWindowTitle( tr( "FX-Mixer" ) ); setWindowIcon( embed::getIconPixmap( "fx_mixer" ) ); @@ -64,6 +66,7 @@ FxMixerView::FxMixerView() : m_fxRacksLayout = new QStackedLayout; m_fxRacksLayout->setSpacing( 0 ); m_fxRacksLayout->setMargin( 0 ); + //m_fxRacksLayout->setAlignment(Qt::AlignRight); // main-layout QHBoxLayout * ml = new QHBoxLayout; @@ -71,8 +74,8 @@ FxMixerView::FxMixerView() : ml->setSpacing( 0 ); ml->addSpacing( 6 ); - channelArea = new QScrollArea(this); - chLayout = new QHBoxLayout(channelArea); + m_channelAreaWidget = new QWidget; + chLayout = new QHBoxLayout(m_channelAreaWidget); // add master channel m_fxChannelViews.resize(m->numChannels()); @@ -85,17 +88,20 @@ FxMixerView::FxMixerView() : QSize fxLineSize = masterView->m_fxLine->size(); chLayout->setSizeConstraint(QLayout::SetMinimumSize); - channelArea->setWidgetResizable(true); // add mixer channels for( int i = 1; i < m_fxChannelViews.size(); ++i ) { - m_fxChannelViews[i] = new FxChannelView(channelArea, this, i); + m_fxChannelViews[i] = new FxChannelView(m_channelAreaWidget, this, i); chLayout->addWidget(m_fxChannelViews[i]->m_fxLine); m_fxRacksLayout->addWidget( m_fxChannelViews[i]->m_rackView ); } // add the scrolling section to the main layout - ml->addLayout(chLayout); + m_channelAreaWidget->setLayout(chLayout); + channelArea = new QScrollArea(this); + channelArea->setWidget(m_channelAreaWidget); + //channelArea-> get rid of padding + ml->addWidget(channelArea); // show the add new effect channel button QPushButton * newChannelBtn = new QPushButton("new", this ); @@ -123,7 +129,7 @@ FxMixerView::FxMixerView() : Qt::WindowFlags flags = subWin->windowFlags(); flags &= ~Qt::WindowMaximizeButtonHint; subWin->setWindowFlags( flags ); - subWin->layout()->setSizeConstraint(QLayout::SetMinimumSize); + //subWin->layout()->setSizeConstraint(QLayout::SetMinimumSize); parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false ); parentWidget()->move( 5, 310 ); @@ -144,7 +150,7 @@ void FxMixerView::addNewChannel() FxMixer * mix = engine::fxMixer(); int newChannelIndex = mix->createChannel(); - m_fxChannelViews.push_back(new FxChannelView(channelArea, this, + m_fxChannelViews.push_back(new FxChannelView(m_channelAreaWidget, this, newChannelIndex)); chLayout->addWidget(m_fxChannelViews[newChannelIndex]->m_fxLine); m_fxRacksLayout->addWidget( m_fxChannelViews[newChannelIndex]->m_rackView ); From db6164ca91f671cba81e3c603a99db1176a44db6 Mon Sep 17 00:00:00 2001 From: Paul Giblock Date: Wed, 30 Sep 2009 03:12:25 -0400 Subject: [PATCH 07/25] Requested improvements to new FxMixerView$ * lock fx mixer height Done. channelArea->setFixedHeight and proper sizeConstraints * width: pick a good min. size. keep max. size off. Done. Set to 6 fx-lines. * effects chain should align to the right Done. * get rid of padding in mixer Done. setSpacing and setMargin on chLayout * scroll area so that vert scrollbar is never seen. Done. setVerticalScrollBarPolicy and proper height calculation * Get rid of scroll bar area border Done. FrameStyle. The biggest change, however, was removing the multiple EffectRackViews that were being used. Now just a single EffectRackView exists and it is shared by all models. --- include/FxMixerView.h | 5 +-- src/gui/FxMixerView.cpp | 62 +++++++++++++----------------- src/gui/widgets/EffectRackView.cpp | 2 +- 3 files changed, 30 insertions(+), 39 deletions(-) diff --git a/include/FxMixerView.h b/include/FxMixerView.h index f752672c9..5c4c2e0c8 100644 --- a/include/FxMixerView.h +++ b/include/FxMixerView.h @@ -39,7 +39,6 @@ #include "embed.h" #include "EffectRackView.h" -class QStackedLayout; class QButtonGroup; class FxLine; @@ -53,7 +52,7 @@ public: FxChannelView(QWidget * _parent, FxMixerView * _mv, int _chIndex ); FxLine * m_fxLine; - EffectRackView * m_rackView; + //EffectRackView * m_rackView; pixmapButton * m_muteBtn; fader * m_fader; } ; @@ -92,12 +91,12 @@ private: QVector m_fxChannelViews; - QStackedLayout * m_fxRacksLayout; FxLine * m_currentFxLine; QScrollArea * channelArea; QHBoxLayout * chLayout; QWidget * m_channelAreaWidget; + EffectRackView * m_rackView; } ; #endif diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index f41c9bba4..63a23c891 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include "FxMixerView.h" #include "knob.h" @@ -52,55 +53,51 @@ FxMixerView::FxMixerView() : FxMixer * m = engine::fxMixer(); m->setHook( this ); - QPalette pal = palette(); + //QPalette pal = palette(); //pal.setColor( QPalette::Background, QColor( 72, 76, 88 ) ); //setPalette( pal ); setAutoFillBackground( true ); - setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ); - setFixedSize(600, 300); - setWindowTitle( tr( "FX-Mixer" ) ); setWindowIcon( embed::getIconPixmap( "fx_mixer" ) ); - m_fxRacksLayout = new QStackedLayout; - m_fxRacksLayout->setSpacing( 0 ); - m_fxRacksLayout->setMargin( 0 ); - //m_fxRacksLayout->setAlignment(Qt::AlignRight); - // main-layout QHBoxLayout * ml = new QHBoxLayout; - ml->setMargin( 0 ); - ml->setSpacing( 0 ); - ml->addSpacing( 6 ); + //ml->setMargin( 0 ); + //ml->setSpacing( 0 ); + //ml->addSpacing( 6 ); + // Channel area m_channelAreaWidget = new QWidget; chLayout = new QHBoxLayout(m_channelAreaWidget); + chLayout->setSizeConstraint(QLayout::SetMinimumSize); + chLayout->setSpacing( 0 ); + chLayout->setMargin( 0 ); + m_channelAreaWidget->setLayout(chLayout); // add master channel m_fxChannelViews.resize(m->numChannels()); m_fxChannelViews[0] = new FxChannelView(this, this, 0); + FxChannelView * masterView = m_fxChannelViews[0]; - m_fxRacksLayout->addWidget( masterView->m_rackView ); + ml->addWidget( masterView->m_fxLine, 0, Qt::AlignTop ); - ml->addWidget(masterView->m_fxLine); - ml->addSpacing(5); QSize fxLineSize = masterView->m_fxLine->size(); - chLayout->setSizeConstraint(QLayout::SetMinimumSize); - // add mixer channels for( int i = 1; i < m_fxChannelViews.size(); ++i ) { m_fxChannelViews[i] = new FxChannelView(m_channelAreaWidget, this, i); chLayout->addWidget(m_fxChannelViews[i]->m_fxLine); - m_fxRacksLayout->addWidget( m_fxChannelViews[i]->m_rackView ); } // add the scrolling section to the main layout - m_channelAreaWidget->setLayout(chLayout); channelArea = new QScrollArea(this); channelArea->setWidget(m_channelAreaWidget); - //channelArea-> get rid of padding + channelArea->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + channelArea->setFrameStyle( QFrame::NoFrame ); + channelArea->setMinimumWidth( fxLineSize.width() * 6 ); + channelArea->setFixedHeight( fxLineSize.height() + + style()->pixelMetric( QStyle::PM_ScrollBarExtent ) ); ml->addWidget(channelArea); // show the add new effect channel button @@ -108,16 +105,17 @@ FxMixerView::FxMixerView() : newChannelBtn->setFont(QFont("sans-serif", 10, 1, false)); newChannelBtn->setFixedSize(fxLineSize); connect( newChannelBtn, SIGNAL(clicked()), this, SLOT(addNewChannel())); - ml->addWidget( newChannelBtn ); - - ml->addLayout( m_fxRacksLayout ); + ml->addWidget( newChannelBtn, 0, Qt::AlignTop ); + + // Create EffectRack and set initial index to master channel + m_rackView = new EffectRackView( &m->m_fxChannels[0]->m_fxChain, this ); + ml->addWidget( m_rackView, 0, Qt::AlignTop ); + setCurrentFxLine( m_fxChannelViews[0]->m_fxLine ); setLayout( ml ); updateGeometry(); - setCurrentFxLine( m_fxChannelViews[0]->m_fxLine ); - // timer for updating faders connect( engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( updateFaders() ) ); @@ -129,7 +127,8 @@ FxMixerView::FxMixerView() : Qt::WindowFlags flags = subWin->windowFlags(); flags &= ~Qt::WindowMaximizeButtonHint; subWin->setWindowFlags( flags ); - //subWin->layout()->setSizeConstraint(QLayout::SetMinimumSize); + layout()->setSizeConstraint( QLayout::SetMinAndMaxSize ); + subWin->layout()->setSizeConstraint( QLayout::SetMinAndMaxSize ); parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false ); parentWidget()->move( 5, 310 ); @@ -153,7 +152,6 @@ void FxMixerView::addNewChannel() m_fxChannelViews.push_back(new FxChannelView(m_channelAreaWidget, this, newChannelIndex)); chLayout->addWidget(m_fxChannelViews[newChannelIndex]->m_fxLine); - m_fxRacksLayout->addWidget( m_fxChannelViews[newChannelIndex]->m_rackView ); updateFxLine(newChannelIndex); } @@ -195,9 +193,6 @@ FxMixerView::FxChannelView::FxChannelView(QWidget * _parent, FxMixerView * _mv, m_muteBtn->setCheckable( true ); m_muteBtn->move( 9, m_fader->y()-16); toolTip::add( m_muteBtn, tr( "Mute this FX channel" ) ); - - m_rackView = new EffectRackView( - &m->m_fxChannels[_chIndex]->m_fxChain, _mv ); } @@ -205,7 +200,7 @@ void FxMixerView::setCurrentFxLine( FxLine * _line ) { // select m_currentFxLine = _line; - m_fxRacksLayout->setCurrentIndex( _line->channelIndex() ); + m_rackView->setModel( &engine::fxMixer()->m_fxChannels[_line->channelIndex()]->m_fxChain ); // set up send knob for(int i = 0; i < m_fxChannelViews.size(); ++i) @@ -253,10 +248,7 @@ void FxMixerView::setCurrentFxLine( int _line ) void FxMixerView::clear() { - for( int i = 0; i < m_fxChannelViews.size(); ++i ) - { - m_fxChannelViews[i]->m_rackView->clearViews(); - } + m_rackView->clearViews(); } diff --git a/src/gui/widgets/EffectRackView.cpp b/src/gui/widgets/EffectRackView.cpp index f78c3d7e1..bd59656b6 100644 --- a/src/gui/widgets/EffectRackView.cpp +++ b/src/gui/widgets/EffectRackView.cpp @@ -43,7 +43,7 @@ EffectRackView::EffectRackView( EffectChain * _model, QWidget * _parent ) : m_mainLayout = new QVBoxLayout( this ); m_mainLayout->setSpacing( 0 ); - m_mainLayout->setMargin( 5 ); + m_mainLayout->setMargin( 0 ); m_effectsGroupBox = new groupBox( tr( "EFFECTS CHAIN" ) ); m_mainLayout->addWidget( m_effectsGroupBox ); From e09c12687a87165a8e70364c6c7f6d62e4d211d7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 Sep 2009 03:45:08 -0700 Subject: [PATCH 08/25] Ability to delete mixer channels in the FX Mixer Users can now delete mixer channels from the FX mixer, and it doesn't mess up the instrument send channels. --- include/FxLine.h | 2 +- include/FxMixer.h | 7 +++ include/FxMixerView.h | 10 +++-- src/core/FxMixer.cpp | 95 +++++++++++++++++++++++++++++++++++++++++ src/gui/FxMixerView.cpp | 55 ++++++++++++++++++++++-- 5 files changed, 161 insertions(+), 8 deletions(-) diff --git a/include/FxLine.h b/include/FxLine.h index 1700ebba3..70def8637 100644 --- a/include/FxLine.h +++ b/include/FxLine.h @@ -21,11 +21,11 @@ public: virtual void mouseDoubleClickEvent( QMouseEvent * ); inline int channelIndex() { return m_channelIndex; } + inline void setChannelIndex(int index) { m_channelIndex = index; } knob * m_sendKnob; SendButtonIndicator * m_sendBtn; - private: FxMixerView * m_mv; diff --git a/include/FxMixer.h b/include/FxMixer.h index b53045015..19caa1479 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -109,6 +109,13 @@ public: // returns the index of the channel that was just added int createChannel(); + // delete a channel from the FX mixer. + void deleteChannel(int index); + + // re-arrange channels + void moveChannelLeft(int index); + void moveChannelRight(int index); + // reset a channel's name, fx, sends, etc void clearChannel(fx_ch_t channelIndex); diff --git a/include/FxMixerView.h b/include/FxMixerView.h index 5c4c2e0c8..05c5874bf 100644 --- a/include/FxMixerView.h +++ b/include/FxMixerView.h @@ -52,15 +52,16 @@ public: FxChannelView(QWidget * _parent, FxMixerView * _mv, int _chIndex ); FxLine * m_fxLine; - //EffectRackView * m_rackView; pixmapButton * m_muteBtn; fader * m_fader; - } ; + }; FxMixerView(); virtual ~FxMixerView(); + virtual void keyPressEvent(QKeyEvent * e); + virtual void saveSettings( QDomDocument & _doc, QDomElement & _this ); virtual void loadSettings( const QDomElement & _this ); @@ -81,7 +82,10 @@ public: // display the send button and knob correctly - void updateFxLine(int i); + void updateFxLine(int index); + + // notify the view that an fx channel was deleted + void deleteChannel(int index); private slots: void updateFaders(); diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 8306b28c4..68fba6037 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -30,6 +30,9 @@ #include "Effect.h" #include "song.h" +#include "InstrumentTrack.h" +#include "bb_track_container.h" + FxChannel::FxChannel( Model * _parent ) : m_fxChain( NULL ), @@ -97,6 +100,98 @@ int FxMixer::createChannel() } +void FxMixer::deleteChannel(int index) +{ + // go through every instrument and adjust for the channel index change + QVector songTrackList = engine::getSong()->tracks(); + QVector bbTrackList = engine::getBBTrackContainer()->tracks(); + + QVector trackLists[] = {songTrackList, bbTrackList}; + for(int tl=0; tl<2; ++tl) + { + QVector trackList = trackLists[tl]; + for(int i=0; itype() == track::InstrumentTrack ) + { + InstrumentTrack * inst = (InstrumentTrack *) trackList[i]; + int val = inst->effectChannelModel()->value(0); + if( val == index ) + { + // we are deleting this track's fx send + // send to master + inst->effectChannelModel()->setValue(0); + } + else if( val > index ) + { + // subtract 1 to make up for the missing channel + inst->effectChannelModel()->setValue(val-1); + } + + } + } + } + + // delete all of this channel's sends and receives + for(int i=0; im_sends.size(); ++i) + { + deleteChannelSend(index, m_fxChannels[index]->m_sends[i]); + } + for(int i=0; im_receives.size(); ++i) + { + deleteChannelSend(m_fxChannels[index]->m_receives[i], index); + } + + for(int i=0; im_sends.size(); ++j) + { + if( m_fxChannels[i]->m_sends[j] > index ) + { + // subtract 1 to make up for the missing channel + --m_fxChannels[i]->m_sends[j]; + } + } + for(int j=0; jm_receives.size(); ++j) + { + if( m_fxChannels[i]->m_receives[j] > index ) + { + // subtract 1 to make up for the missing channel + --m_fxChannels[i]->m_receives[j]; + } + } + + } + + // actually delete the channel + m_fxChannels.remove(index); +} + + + +void FxMixer::moveChannelLeft(int index) +{ + // can't move master or first channel + if( index <= 1 ) + { + return; + } + + // channels to swap + int a = index - 1, b = index; + + // go through every instrument and adjust for the channel index change +} + + + +void FxMixer::moveChannelRight(int index) +{ + moveChannelLeft(index+1); +} + + void FxMixer::createChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel, float amount) diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 63a23c891..e3fa74212 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -63,9 +64,6 @@ FxMixerView::FxMixerView() : // main-layout QHBoxLayout * ml = new QHBoxLayout; - //ml->setMargin( 0 ); - //ml->setSpacing( 0 ); - //ml->addSpacing( 6 ); // Channel area m_channelAreaWidget = new QWidget; @@ -237,6 +235,56 @@ void FxMixerView::updateFxLine(int index) } +void FxMixerView::deleteChannel(int index) +{ + // remember selected line + int selLine = m_currentFxLine->channelIndex(); + + // can't delete master + if( index == 0 ) + return; + + // delete the real channel + engine::fxMixer()->deleteChannel(index); + + // delete the view + chLayout->removeWidget(m_fxChannelViews[index]->m_fxLine); + delete m_fxChannelViews[index]->m_fader; + delete m_fxChannelViews[index]->m_muteBtn; + delete m_fxChannelViews[index]->m_fxLine; + delete m_fxChannelViews[index]; + + // make sure every channel knows what index it is + for(int i=0; i index ) + { + m_fxChannelViews[i]->m_fxLine->setChannelIndex(i-1); + } + } + m_fxChannelViews.remove(index); + + // select the next channel + if( selLine >= m_fxChannelViews.size() ) + { + selLine = m_fxChannelViews.size()-1; + } + setCurrentFxLine(selLine); + +} + + +void FxMixerView::keyPressEvent(QKeyEvent * e) +{ + switch(e->key()) + { + case Qt::Key_Delete: + deleteChannel(m_currentFxLine->channelIndex()); + break; + } +} + + void FxMixerView::setCurrentFxLine( int _line ) { @@ -245,7 +293,6 @@ void FxMixerView::setCurrentFxLine( int _line ) - void FxMixerView::clear() { m_rackView->clearViews(); From 33753495bd064f7453a2eb4cb50af5c5be686ac9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 Sep 2009 06:53:23 -0700 Subject: [PATCH 09/25] Ability to re-order mixer channels In the Fx Mixer View, you can select a channel and press Alt+Left and Alt+Right respectively to re-order Fx Channels. This should be made more easily available in the GUI eventually. --- include/FxMixerView.h | 4 ++ src/core/FxMixer.cpp | 59 +++++++++++++++++++++++++++- src/gui/FxMixerView.cpp | 86 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 143 insertions(+), 6 deletions(-) diff --git a/include/FxMixerView.h b/include/FxMixerView.h index 05c5874bf..736c70615 100644 --- a/include/FxMixerView.h +++ b/include/FxMixerView.h @@ -87,6 +87,10 @@ public: // notify the view that an fx channel was deleted void deleteChannel(int index); + // move the channel to the left or right + void moveChannelLeft(int index); + void moveChannelRight(int index); + private slots: void updateFaders(); void addNewChannel(); diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 68fba6037..488fb09d9 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -173,7 +173,7 @@ void FxMixer::deleteChannel(int index) void FxMixer::moveChannelLeft(int index) { // can't move master or first channel - if( index <= 1 ) + if( index <= 1 || index >= m_fxChannels.size() ) { return; } @@ -182,6 +182,63 @@ void FxMixer::moveChannelLeft(int index) int a = index - 1, b = index; // go through every instrument and adjust for the channel index change + QVector songTrackList = engine::getSong()->tracks(); + QVector bbTrackList = engine::getBBTrackContainer()->tracks(); + + QVector trackLists[] = {songTrackList, bbTrackList}; + for(int tl=0; tl<2; ++tl) + { + QVector trackList = trackLists[tl]; + for(int i=0; itype() == track::InstrumentTrack ) + { + InstrumentTrack * inst = (InstrumentTrack *) trackList[i]; + int val = inst->effectChannelModel()->value(0); + if( val == a ) + { + inst->effectChannelModel()->setValue(b); + } + else if( val == b ) + { + inst->effectChannelModel()->setValue(a); + } + + } + } + } + + for(int i=0; im_sends.size(); ++j) + { + if( m_fxChannels[i]->m_sends[j] == a ) + { + m_fxChannels[i]->m_sends[j] = b; + } + else if( m_fxChannels[i]->m_sends[j] == b ) + { + m_fxChannels[i]->m_sends[j] = a; + } + } + for(int j=0; jm_receives.size(); ++j) + { + if( m_fxChannels[i]->m_receives[j] == a ) + { + m_fxChannels[i]->m_receives[j] = b; + } + else if( m_fxChannels[i]->m_receives[j] == b ) + { + m_fxChannels[i]->m_receives[j] = a; + } + } + } + + // actually do the swap + FxChannel * tmpChannel = m_fxChannels[a]; + m_fxChannels[a] = m_fxChannels[b]; + m_fxChannels[b] = tmpChannel; } diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index e3fa74212..70745ef8c 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -89,7 +89,22 @@ FxMixerView::FxMixerView() : chLayout->addWidget(m_fxChannelViews[i]->m_fxLine); } // add the scrolling section to the main layout - channelArea = new QScrollArea(this); + + // class for scroll area to pass key presses down + class ChannelArea : public QScrollArea + { + public: + ChannelArea(QWidget * parent, FxMixerView * mv) : + QScrollArea(parent), m_mv(mv) {} + ~ChannelArea() {} + virtual void keyPressEvent(QKeyEvent * e) + { + m_mv->keyPressEvent(e); + } + private: + FxMixerView * m_mv; + }; + channelArea = new ChannelArea(this, this); channelArea->setWidget(m_channelAreaWidget); channelArea->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); channelArea->setFrameStyle( QFrame::NoFrame ); @@ -237,13 +252,12 @@ void FxMixerView::updateFxLine(int index) void FxMixerView::deleteChannel(int index) { + // can't delete master + if( index == 0 ) return; + // remember selected line int selLine = m_currentFxLine->channelIndex(); - // can't delete master - if( index == 0 ) - return; - // delete the real channel engine::fxMixer()->deleteChannel(index); @@ -253,6 +267,7 @@ void FxMixerView::deleteChannel(int index) delete m_fxChannelViews[index]->m_muteBtn; delete m_fxChannelViews[index]->m_fxLine; delete m_fxChannelViews[index]; + m_channelAreaWidget->adjustSize(); // make sure every channel knows what index it is for(int i=0; i= m_fxChannelViews.size() ) return; + + int selIndex = m_currentFxLine->channelIndex(); + + FxMixer * mix = engine::fxMixer(); + mix->moveChannelLeft(index); + + // refresh the two mixer views + for( int i = index-1; i <= index; ++i ) + { + // delete the mixer view + int replaceIndex = chLayout->indexOf(m_fxChannelViews[i]->m_fxLine); + + chLayout->removeWidget(m_fxChannelViews[i]->m_fxLine); + delete m_fxChannelViews[i]->m_fader; + delete m_fxChannelViews[i]->m_muteBtn; + delete m_fxChannelViews[i]->m_fxLine; + delete m_fxChannelViews[i]; + + // add it again + m_fxChannelViews[i] = new FxChannelView(m_channelAreaWidget, this, i); + chLayout->insertWidget(replaceIndex, m_fxChannelViews[i]->m_fxLine); + } + + // keep selected channel + if( selIndex == index ) + { + selIndex = index-1; + } + else if( selIndex == index - 1 ) + { + selIndex = index; + } + setCurrentFxLine(selIndex); +} + + + +void FxMixerView::moveChannelRight(int index) +{ + moveChannelLeft(index+1); +} + + + void FxMixerView::keyPressEvent(QKeyEvent * e) { switch(e->key()) @@ -281,6 +345,18 @@ void FxMixerView::keyPressEvent(QKeyEvent * e) case Qt::Key_Delete: deleteChannel(m_currentFxLine->channelIndex()); break; + case Qt::Key_Left: + if( e->modifiers() & Qt::AltModifier ) + { + moveChannelLeft( m_currentFxLine->channelIndex() ); + } + break; + case Qt::Key_Right: + if( e->modifiers() & Qt::AltModifier ) + { + moveChannelRight( m_currentFxLine->channelIndex() ); + } + break; } } From dd28a654b548a8b27c4875c25e72341a5a64fb50 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 Sep 2009 17:04:28 -0700 Subject: [PATCH 10/25] Channel selector has a max range of num channels When you add and remove channels, the range of the L.E.D. channel selector is correct. --- include/AutomatableModel.h | 19 +++++++++++++++---- include/FxMixerView.h | 2 ++ include/SendButtonIndicator.h | 4 ++-- src/core/AutomatableModel.cpp | 4 ++-- src/gui/FxLine.cpp | 6 +++--- src/gui/FxMixerView.cpp | 32 ++++++++++++++++++++++++++++++-- 6 files changed, 54 insertions(+), 13 deletions(-) diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index e3c4a9859..6bd68187a 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -225,6 +225,9 @@ protected: float fittedValue( float _value ) const; + float m_minValue; + float m_maxValue; + float m_value; private: void linkModel( AutomatableModel * _model ); @@ -232,10 +235,7 @@ private: DataType m_dataType; - float m_value; float m_initValue; - float m_minValue; - float m_maxValue; float m_step; float m_range; @@ -281,7 +281,18 @@ signals: { \ return AutomatableModel::maxValue(); \ } \ - + \ + inline void setMinValue(type val) \ + { \ + m_minValue = val; \ + if( m_value < m_minValue ) m_value = m_minValue; \ + } \ + \ + inline void setMaxValue(type val) \ + { \ + m_maxValue = val; \ + if( m_value > m_maxValue ) m_value = m_maxValue; \ + } // some typed AutomatableModel-definitions diff --git a/include/FxMixerView.h b/include/FxMixerView.h index 736c70615..cff856659 100644 --- a/include/FxMixerView.h +++ b/include/FxMixerView.h @@ -105,6 +105,8 @@ private: QHBoxLayout * chLayout; QWidget * m_channelAreaWidget; EffectRackView * m_rackView; + + void updateMaxChannelSelector(); } ; #endif diff --git a/include/SendButtonIndicator.h b/include/SendButtonIndicator.h index f2c7ef44d..2b0819791 100644 --- a/include/SendButtonIndicator.h +++ b/include/SendButtonIndicator.h @@ -1,9 +1,9 @@ #ifndef SENDBUTTONINDICATOR_H #define SENDBUTTONINDICATOR_H -#include #include -#include +#include +#include #include "FxLine.h" #include "FxMixerView.h" diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index 2864f266c..7762edcb8 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -44,11 +44,11 @@ AutomatableModel::AutomatableModel( DataType _type, const QString & _display_name, bool _default_constructed ) : Model( _parent, _display_name, _default_constructed ), + m_minValue( _min ), + m_maxValue( _max ), m_dataType( _type ), m_value( _val ), m_initValue( _val ), - m_minValue( _min ), - m_maxValue( _max ), m_step( _step ), m_range( _max - _min ), m_journalEntryReady( false ), diff --git a/src/gui/FxLine.cpp b/src/gui/FxLine.cpp index fe92ba66b..5d57a6b0f 100644 --- a/src/gui/FxLine.cpp +++ b/src/gui/FxLine.cpp @@ -1,9 +1,9 @@ #include "FxLine.h" #include -#include -#include -#include +#include +#include +#include #include "FxMixer.h" #include "FxMixerView.h" diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 70745ef8c..70832c71b 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -24,7 +24,6 @@ #include #include -#include #include #include @@ -37,6 +36,7 @@ #include #include #include +#include #include "FxMixerView.h" #include "knob.h" @@ -45,6 +45,9 @@ #include "MainWindow.h" #include "lcd_spinbox.h" #include "gui_templates.h" +#include "InstrumentTrack.h" +#include "song.h" +#include "bb_track_container.h" FxMixerView::FxMixerView() : QWidget(), @@ -90,7 +93,7 @@ FxMixerView::FxMixerView() : } // add the scrolling section to the main layout - // class for scroll area to pass key presses down + // class solely for scroll area to pass key presses down class ChannelArea : public QScrollArea { public: @@ -167,9 +170,33 @@ void FxMixerView::addNewChannel() chLayout->addWidget(m_fxChannelViews[newChannelIndex]->m_fxLine); updateFxLine(newChannelIndex); + + updateMaxChannelSelector(); } +void FxMixerView::updateMaxChannelSelector() +{ + // update the and max. channel number for every instrument + QVector songTrackList = engine::getSong()->tracks(); + QVector bbTrackList = engine::getBBTrackContainer()->tracks(); + + QVector trackLists[] = {songTrackList, bbTrackList}; + for(int tl=0; tl<2; ++tl) + { + QVector trackList = trackLists[tl]; + for(int i=0; itype() == track::InstrumentTrack ) + { + InstrumentTrack * inst = (InstrumentTrack *) trackList[i]; + inst->effectChannelModel()->setMaxValue( + m_fxChannelViews.size()-1); + } + } + } +} + void FxMixerView::saveSettings( QDomDocument & _doc, QDomElement & _this ) { @@ -286,6 +313,7 @@ void FxMixerView::deleteChannel(int index) } setCurrentFxLine(selLine); + updateMaxChannelSelector(); } From 333df687e6e21a838cd1145b269cf522fe5109dd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 1 Oct 2009 02:45:32 -0700 Subject: [PATCH 11/25] Ability to save mixer sends to disk with the mmp LMMS can load projects with the old mixer and new projects with the new mixer. By "new mixer", I simply mean not hardcoded to 64 channels. --- include/AutomatableModel.h | 12 ------ include/FxLine.h | 5 ++- include/FxMixer.h | 9 +++-- include/FxMixerView.h | 4 ++ src/core/FxMixer.cpp | 83 ++++++++++++++++++++++++++++++++------ src/core/song.cpp | 22 +++++++--- src/gui/FxLine.cpp | 27 ++++++++++--- src/gui/FxMixerView.cpp | 32 +++++++++++++-- 8 files changed, 152 insertions(+), 42 deletions(-) diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 6bd68187a..0f9308975 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -280,18 +280,6 @@ signals: inline type maxValue() const \ { \ return AutomatableModel::maxValue(); \ - } \ - \ - inline void setMinValue(type val) \ - { \ - m_minValue = val; \ - if( m_value < m_minValue ) m_value = m_minValue; \ - } \ - \ - inline void setMaxValue(type val) \ - { \ - m_maxValue = val; \ - if( m_value > m_maxValue ) m_value = m_maxValue; \ } // some typed AutomatableModel-definitions diff --git a/include/FxLine.h b/include/FxLine.h index 70def8637..89e0321f3 100644 --- a/include/FxLine.h +++ b/include/FxLine.h @@ -5,6 +5,7 @@ #include #include "knob.h" +#include "lcd_spinbox.h" #include "SendButtonIndicator.h" class FxMixerView; @@ -15,19 +16,21 @@ class FxLine : public QWidget Q_OBJECT public: FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex); + ~FxLine(); virtual void paintEvent( QPaintEvent * ); virtual void mousePressEvent( QMouseEvent * ); virtual void mouseDoubleClickEvent( QMouseEvent * ); inline int channelIndex() { return m_channelIndex; } - inline void setChannelIndex(int index) { m_channelIndex = index; } + void setChannelIndex(int index); knob * m_sendKnob; SendButtonIndicator * m_sendBtn; private: FxMixerView * m_mv; + lcdSpinBox * m_lcd; int m_channelIndex; diff --git a/include/FxMixer.h b/include/FxMixer.h index 19caa1479..b9f11ca3e 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -73,9 +73,6 @@ public: void prepareMasterMix(); void masterMix( sampleFrame * _buf ); - - void clear(); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); virtual void loadSettings( const QDomElement & _this ); @@ -112,6 +109,9 @@ public: // delete a channel from the FX mixer. void deleteChannel(int index); + // delete all the mixer channels except master and remove all effects + void clear(); + // re-arrange channels void moveChannelLeft(int index); void moveChannelRight(int index); @@ -128,6 +128,9 @@ private: // the fx channels in the mixer. index 0 is always master. QVector m_fxChannels; + + void allocateChannelsTo(int num); + friend class mixerWorkerThread; friend class FxMixerView; diff --git a/include/FxMixerView.h b/include/FxMixerView.h index cff856659..1dc08a6e5 100644 --- a/include/FxMixerView.h +++ b/include/FxMixerView.h @@ -91,6 +91,10 @@ public: void moveChannelLeft(int index); void moveChannelRight(int index); + // make sure the display syncs up with the fx mixer. + // useful for loading projects + void refreshDisplay(); + private slots: void updateFaders(); void addNewChannel(); diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 488fb09d9..e28deda3b 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -442,13 +442,16 @@ void FxMixer::masterMix( sampleFrame * _buf ) void FxMixer::clear() { - for( int i = 0; i < m_fxChannels.size(); ++i ) + while( m_fxChannels.size() > 1 ) { - clearChannel(i); + deleteChannel(1); } + + clearChannel(0); } + void FxMixer::clearChannel(fx_ch_t index) { FxChannel * ch = m_fxChannels[index]; @@ -483,38 +486,94 @@ void FxMixer::saveSettings( QDomDocument & _doc, QDomElement & _this ) { for( int i = 0; i < m_fxChannels.size(); ++i ) { + FxChannel * ch = m_fxChannels[i]; + QDomElement fxch = _doc.createElement( QString( "fxchannel" ) ); _this.appendChild( fxch ); - m_fxChannels[i]->m_fxChain.saveState( _doc, fxch ); - m_fxChannels[i]->m_volumeModel.saveSettings( _doc, fxch, - "volume" ); - m_fxChannels[i]->m_muteModel.saveSettings( _doc, fxch, - "muted" ); + + ch->m_fxChain.saveState( _doc, fxch ); + ch->m_volumeModel.saveSettings( _doc, fxch, "volume" ); + ch->m_muteModel.saveSettings( _doc, fxch, "muted" ); fxch.setAttribute( "num", i ); - fxch.setAttribute( "name", m_fxChannels[i]->m_name ); + fxch.setAttribute( "name", ch->m_name ); + + // add the channel sends + for( int si = 0; si < ch->m_sends.size(); ++si ) + { + QDomElement sendsDom = _doc.createElement( QString( "send" ) ); + fxch.appendChild( sendsDom ); + + sendsDom.setAttribute( "channel", ch->m_sends[si] ); + ch->m_sendAmount[si]->saveSettings( _doc, sendsDom, "amount"); + } } } +void FxMixer::allocateChannelsTo(int num) +{ + while( num > m_fxChannels.size() - 1 ) + { + createChannel(); + + // delete the default send to master + deleteChannelSend(m_fxChannels.size()-1, 0); + } +} void FxMixer::loadSettings( const QDomElement & _this ) { clear(); QDomNode node = _this.firstChild(); - for( int i = 0; i <= 64; ++i ) // TODO make this work + bool thereIsASend = false; + + while( ! node.isNull() ) { QDomElement fxch = node.toElement(); + + // index of the channel we are about to load int num = fxch.attribute( "num" ).toInt(); - m_fxChannels[num]->m_fxChain.restoreState( - fxch.firstChildElement( - m_fxChannels[num]->m_fxChain.nodeName() ) ); + + // allocate enough channels + allocateChannelsTo( num ); + m_fxChannels[num]->m_volumeModel.loadSettings( fxch, "volume" ); m_fxChannels[num]->m_muteModel.loadSettings( fxch, "muted" ); m_fxChannels[num]->m_name = fxch.attribute( "name" ); + + m_fxChannels[num]->m_fxChain.restoreState( fxch.firstChildElement( + m_fxChannels[num]->m_fxChain.nodeName() ) ); + + // mixer sends + QDomNodeList chData = fxch.childNodes(); + for( unsigned int i=0; inodeName() ) + { + engine::fxMixer()->restoreState( node.toElement() ); + + // refresh FxMixerView + engine::fxMixerView()->refreshDisplay(); + } + + node = node.nextSibling(); + } + + node = mmp.content().firstChild(); + while( !node.isNull() ) { if( node.isElement() ) @@ -917,10 +934,6 @@ void song::loadProject( const QString & _file_name ) { restoreControllerStates( node.toElement() ); } - else if( node.nodeName() == engine::fxMixer()->nodeName() ) - { - engine::fxMixer()->restoreState( node.toElement() ); - } else if( engine::hasGUI() ) { if( node.nodeName() == @@ -973,7 +986,6 @@ void song::loadProject( const QString & _file_name ) // resolve all IDs so that autoModels are automated automationPattern::resolveAllIDs(); - engine::getMixer()->unlock(); configManager::inst()->addRecentlyOpenedProject( _file_name ); diff --git a/src/gui/FxLine.cpp b/src/gui/FxLine.cpp index 5d57a6b0f..172d30b17 100644 --- a/src/gui/FxLine.cpp +++ b/src/gui/FxLine.cpp @@ -9,7 +9,6 @@ #include "FxMixerView.h" #include "embed.h" #include "engine.h" -#include "lcd_spinbox.h" #include "SendButtonIndicator.h" FxLine::FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex) : @@ -32,11 +31,27 @@ FxLine::FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex) : m_sendBtn->move(4,4); // channel number - lcdSpinBox * l = new lcdSpinBox( 2, this ); - l->model()->setRange( m_channelIndex, m_channelIndex ); - l->model()->setValue( m_channelIndex ); - l->move( 2, 58 ); - l->setMarginWidth( 1 ); + m_lcd = new lcdSpinBox( 2, this ); + m_lcd->model()->setRange( m_channelIndex, m_channelIndex ); + m_lcd->model()->setValue( m_channelIndex ); + m_lcd->move( 2, 58 ); + m_lcd->setMarginWidth( 1 ); +} + +FxLine::~FxLine() +{ + delete m_sendKnob; + delete m_sendBtn; + delete m_lcd; +} + + +void FxLine::setChannelIndex(int index) { + m_channelIndex = index; + + m_lcd->model()->setRange( m_channelIndex, m_channelIndex ); + m_lcd->model()->setValue( m_channelIndex ); + m_lcd->update(); } diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 70832c71b..ad844ec4f 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -91,8 +91,8 @@ FxMixerView::FxMixerView() : m_fxChannelViews[i] = new FxChannelView(m_channelAreaWidget, this, i); chLayout->addWidget(m_fxChannelViews[i]->m_fxLine); } - // add the scrolling section to the main layout + // add the scrolling section to the main layout // class solely for scroll area to pass key presses down class ChannelArea : public QScrollArea { @@ -175,6 +175,32 @@ void FxMixerView::addNewChannel() } +void FxMixerView::refreshDisplay() +{ + // delete all views and re-add them + for( int i = 1; iremoveWidget(m_fxChannelViews[i]->m_fxLine); + delete m_fxChannelViews[i]->m_fader; + delete m_fxChannelViews[i]->m_muteBtn; + delete m_fxChannelViews[i]->m_fxLine; + delete m_fxChannelViews[i]; + } + m_channelAreaWidget->adjustSize(); + + // re-add the views + m_fxChannelViews.resize(engine::fxMixer()->numChannels()); + for( int i = 1; i < m_fxChannelViews.size(); ++i ) + { + m_fxChannelViews[i] = new FxChannelView(m_channelAreaWidget, this, i); + chLayout->addWidget(m_fxChannelViews[i]->m_fxLine); + } + + // fix master +//TODO +} + + void FxMixerView::updateMaxChannelSelector() { // update the and max. channel number for every instrument @@ -190,8 +216,8 @@ void FxMixerView::updateMaxChannelSelector() if( trackList[i]->type() == track::InstrumentTrack ) { InstrumentTrack * inst = (InstrumentTrack *) trackList[i]; - inst->effectChannelModel()->setMaxValue( - m_fxChannelViews.size()-1); + inst->effectChannelModel()->setRange(0, + m_fxChannelViews.size()-1,1); } } } From 275bf5bb0e5ebe124c813195859c177fbe1fddbf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 1 Oct 2009 03:40:20 -0700 Subject: [PATCH 12/25] Fix FL Import with new mixer --- plugins/flp_import/FlpImport.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/flp_import/FlpImport.cpp b/plugins/flp_import/FlpImport.cpp index b243c9427..5cebea7b3 100644 --- a/plugins/flp_import/FlpImport.cpp +++ b/plugins/flp_import/FlpImport.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "FlpImport.h" #include "note_play_handle.h" @@ -40,6 +41,7 @@ #include "Effect.h" #include "engine.h" #include "FxMixer.h" +#include "FxMixerView.h" #include "group_box.h" #include "Instrument.h" #include "InstrumentTrack.h" @@ -903,7 +905,6 @@ bool FlpImport::tryFLPImport( trackContainer * _tc ) const bool is_journ = engine::projectJournal()->isJournalling(); engine::projectJournal()->setJournalling( false ); - while( file().atEnd() == false ) { FLP_Events ev = static_cast( readByte() ); @@ -1558,9 +1559,15 @@ else // now create a project from FL_Project data structure - engine::getSong()->clearProject(); + // configure the mixer + for( int i=0; icreateChannel(); + } + engine::fxMixerView()->refreshDisplay(); + // set global parameters engine::getSong()->setMasterVolume( p.mainVolume ); engine::getSong()->setMasterPitch( p.mainPitch ); From 23e33010376852f8af8f67cc1d1eea0a01b29009 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 1 Oct 2009 03:45:04 -0700 Subject: [PATCH 13/25] FxMixerView - Left and right to select channels --- src/gui/FxMixerView.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index ad844ec4f..0cb3f1938 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -404,12 +404,22 @@ void FxMixerView::keyPressEvent(QKeyEvent * e) { moveChannelLeft( m_currentFxLine->channelIndex() ); } + else + { + // select channel to the left + setCurrentFxLine( m_currentFxLine->channelIndex()-1 ); + } break; case Qt::Key_Right: if( e->modifiers() & Qt::AltModifier ) { moveChannelRight( m_currentFxLine->channelIndex() ); } + else + { + // select channel to the right + setCurrentFxLine( m_currentFxLine->channelIndex()+1 ); + } break; } } @@ -418,7 +428,10 @@ void FxMixerView::keyPressEvent(QKeyEvent * e) void FxMixerView::setCurrentFxLine( int _line ) { - setCurrentFxLine( m_fxChannelViews[_line]->m_fxLine ); + if( _line >= 0 && _line < m_fxChannelViews.size() ) + { + setCurrentFxLine( m_fxChannelViews[_line]->m_fxLine ); + } } From 1c9b24afb783a1efd6da3171f332918583b4e438 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 1 Oct 2009 11:37:46 -0700 Subject: [PATCH 14/25] Fixed a mixer bug regarding deleting channels --- src/core/FxMixer.cpp | 3 +++ src/gui/widgets/knob.cpp | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index e28deda3b..43a254660 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -102,6 +102,8 @@ int FxMixer::createChannel() void FxMixer::deleteChannel(int index) { + m_fxChannels[index]->m_lock.lock(); + // go through every instrument and adjust for the channel index change QVector songTrackList = engine::getSong()->tracks(); QVector bbTrackList = engine::getBBTrackContainer()->tracks(); @@ -165,6 +167,7 @@ void FxMixer::deleteChannel(int index) } // actually delete the channel + delete m_fxChannels[index]; m_fxChannels.remove(index); } diff --git a/src/gui/widgets/knob.cpp b/src/gui/widgets/knob.cpp index 31a772c1b..72a57c735 100644 --- a/src/gui/widgets/knob.cpp +++ b/src/gui/widgets/knob.cpp @@ -492,7 +492,11 @@ void knob::mouseMoveEvent( QMouseEvent * _me ) void knob::mouseReleaseEvent( QMouseEvent * /* _me*/ ) { - model()->addJournalEntryFromOldToCurVal(); + AutomatableModel * thisModel = model(); + if( thisModel ) + { + thisModel->addJournalEntryFromOldToCurVal(); + } m_buttonPressed = false; From abfdb6a74d68272558e360d98789e1c11bbd9eb4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 1 Oct 2009 17:42:14 -0700 Subject: [PATCH 15/25] Fixed bug - Instruments had wrong channel models Instruments were initialized with hardcoded 0-10 for min/max channel selector range. Fixed. --- src/core/FxMixer.cpp | 2 +- src/gui/FxMixerView.cpp | 6 +++--- src/tracks/InstrumentTrack.cpp | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 43a254660..7cf27b8c3 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -512,7 +512,7 @@ void FxMixer::saveSettings( QDomDocument & _doc, QDomElement & _this ) } } - +// make sure we have at least num channels void FxMixer::allocateChannelsTo(int num) { while( num > m_fxChannels.size() - 1 ) diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 0cb3f1938..b61bcbf7b 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -196,14 +196,13 @@ void FxMixerView::refreshDisplay() chLayout->addWidget(m_fxChannelViews[i]->m_fxLine); } - // fix master -//TODO + updateMaxChannelSelector(); } +// update the and max. channel number for every instrument void FxMixerView::updateMaxChannelSelector() { - // update the and max. channel number for every instrument QVector songTrackList = engine::getSong()->tracks(); QVector bbTrackList = engine::getBBTrackContainer()->tracks(); @@ -439,6 +438,7 @@ void FxMixerView::setCurrentFxLine( int _line ) void FxMixerView::clear() { m_rackView->clearViews(); + refreshDisplay(); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index f56d6487b..252980448 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -103,13 +103,14 @@ InstrumentTrack::InstrumentTrack( trackContainer * _tc ) : this, tr( "Panning" ) ), m_pitchModel( 0, -100, 100, 1, this, tr( "Pitch" ) ), m_pitchRangeModel( 1, 1, 24, this, tr( "Pitch range" ) ), - m_effectChannelModel( 0, 0, 10, this, tr( "FX channel" ) ), // change this so it's a combo box, all the channels and then new. + m_effectChannelModel( 0, 0, 0, this, tr( "FX channel" ) ), // change this so it's a combo box, all the channels and then new. m_instrument( NULL ), m_soundShaping( this ), m_arpeggiator( this ), m_chordCreator( this ), m_piano( this ) { + m_effectChannelModel.setRange( 0, engine::fxMixer()->numChannels()-1, 1); connect( baseNoteModel(), SIGNAL( dataChanged() ), this, SLOT( updateBaseNote() ) ); connect( &m_pitchModel, SIGNAL( dataChanged() ), From 3fa96a576c621a2ac9f470c557ec3f5e2f80df16 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Sun, 11 Oct 2009 01:36:55 +0200 Subject: [PATCH 16/25] Mixer: rewrote/reorganized job queueing for worker threads In Mixer, the old C-macro based code has been replaced by an OOP-like design. Management of job queue now happens via some static member methods of MixerWorkerThread. All the moved code still needs to be splitted into some new files but here's a first dirty version. All objects that are intended to be processed by MixerWorkerThreads have to inherit ThreadableJob (name of class is subject of change). One can add jobs to the job queue even if the queue is already being processed. This is merely important for multithreading with upcoming FX sends support. --- include/AudioPort.h | 9 +- include/mixer.h | 177 +++++++++++++++++++++++++++- include/play_handle.h | 18 ++- src/core/FxMixer.cpp | 7 ++ src/core/audio/AudioPort.cpp | 17 ++- src/core/mixer.cpp | 220 +---------------------------------- 6 files changed, 224 insertions(+), 224 deletions(-) diff --git a/include/AudioPort.h b/include/AudioPort.h index 7157f729b..d84dd179f 100644 --- a/include/AudioPort.h +++ b/include/AudioPort.h @@ -33,7 +33,7 @@ class EffectChain; -class AudioPort +class AudioPort : public ThreadableJob { public: AudioPort( const QString & _name, bool _has_effect_chain = true ); @@ -109,6 +109,13 @@ public: bool processEffects(); + // ThreadableJob stuff + virtual void doProcessing( sampleFrame * ); + virtual bool requiresProcessing() const + { + return true; + } + enum bufferUsages { diff --git a/include/mixer.h b/include/mixer.h index 3562db3f1..81be6af7f 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -62,11 +62,46 @@ const Keys BaseKey = Key_A; const Octaves BaseOctave = DefaultOctave; -#include "play_handle.h" - class MixerWorkerThread; +// TODO: move to ThreadableJob.h +class ThreadableJob +{ +public: + ThreadableJob() : + m_done( false ) + { + } + + void reset() + { + m_done = false; + } + + bool process( sampleFrame * _working_buffer ) + { + if( m_done.fetchAndStoreOrdered( true ) == false ) + { + doProcessing( _working_buffer ); + return true; + } + return false; + } + + virtual bool requiresProcessing() const = 0; + + +private: + virtual void doProcessing( sampleFrame * _working_buffer ) = 0; + + QAtomicInt m_done; + +} ; + + +#include "play_handle.h" + class EXPORT mixer : public QObject { @@ -466,4 +501,142 @@ private: } ; +// TODO: move to MixerWorkerThread.h / MixerWorkerThread.cpp +#include "Cpu.h" +#include "engine.h" + +class MixerWorkerThread : public QThread +{ +public: + struct JobQueue + { +#define JOB_QUEUE_SIZE 1024 + JobQueue() : + queueSize( 0 ), + itemsDone( 0 ) + { + for( int i = 0; i < JOB_QUEUE_SIZE; ++i ) + { + items[i] = NULL; + } + } + + ThreadableJob * items[JOB_QUEUE_SIZE]; + QAtomicInt queueSize; + QAtomicInt itemsDone; + } ; + + static JobQueue s_jobQueue; + + MixerWorkerThread( int _worker_num, mixer * _mixer ) : + QThread( _mixer ), + m_workingBuf( CPU::allocFrames( _mixer->framesPerPeriod() ) ), + m_workerNum( _worker_num ), + m_quit( false ), + m_mixer( _mixer ), + m_queueReadyWaitCond( &m_mixer->m_queueReadyWaitCond ) + { + } + + virtual ~MixerWorkerThread() + { + CPU::freeFrames( m_workingBuf ); + } + + virtual void quit() + { + m_quit = true; + } + + void processJobQueue() + { + for( int i = 0; i < s_jobQueue.queueSize; ++i ) + { + // returns true if ThreadableJob was not processed before + if( s_jobQueue.items[i]->process( m_workingBuf ) ) + { + s_jobQueue.itemsDone.fetchAndAddOrdered( 1 ); + } + } + } + + template + static void fillJobQueue( const T & _vec ) + { + s_jobQueue.queueSize = 0; + s_jobQueue.itemsDone = 0; + for( typename T::ConstIterator it = _vec.begin(); it != _vec.end(); ++it ) + { + addJob( *it ); + } + } + + static void addJob( ThreadableJob * _job ) + { + if( _job->requiresProcessing() ) + { + _job->reset(); + s_jobQueue.items[s_jobQueue.queueSize.fetchAndAddOrdered(1)] = _job; + } + } + + +// define a pause instruction for spinlock-loop - merely useful on +// HyperThreading systems with just one physical core (e.g. Intel Atom) +#ifdef LMMS_HOST_X86 +#define SPINLOCK_PAUSE() asm( "pause" ) +#else +#ifdef LMMS_HOST_X86_64 +#define SPINLOCK_PAUSE() asm( "pause" ) +#else +#define SPINLOCK_PAUSE() +#endif +#endif + + static void startJobs() + { + // TODO: this is dirty! + engine::getMixer()->m_queueReadyWaitCond.wakeAll(); + } + + static void waitForJobs() + { + // TODO: this is dirty! + mixer * m = engine::getMixer(); + m->m_workers[m->m_numWorkers]->processJobQueue(); + while( s_jobQueue.itemsDone < s_jobQueue.queueSize ) + { + SPINLOCK_PAUSE(); + } + } + + static void startAndWaitForJobs() + { + startJobs(); + waitForJobs(); + } + + +private: + virtual void run() + { + QMutex m; + while( m_quit == false ) + { + m.lock(); + m_queueReadyWaitCond->wait( &m ); + processJobQueue(); + m.unlock(); + } + } + + sampleFrame * m_workingBuf; + int m_workerNum; + volatile bool m_quit; + mixer * m_mixer; + QWaitCondition * m_queueReadyWaitCond; + +} ; + + #endif diff --git a/include/play_handle.h b/include/play_handle.h index 365da3f19..1a49fb540 100644 --- a/include/play_handle.h +++ b/include/play_handle.h @@ -25,15 +25,12 @@ #ifndef _PLAY_HANDLE_H #define _PLAY_HANDLE_H -#include -#include - -#include "lmms_basics.h" +#include "mixer.h" class track; -class playHandle +class playHandle : public ThreadableJob { public: enum Types @@ -71,6 +68,17 @@ public: return m_type; } + // required for ThreadableJob + virtual void doProcessing( sampleFrame * _working_buffer ) + { + play( _working_buffer ); + } + + virtual bool requiresProcessing() const + { + return !done(); + } + virtual void play( sampleFrame * _working_buffer ) = 0; virtual bool done() const = 0; diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 7cf27b8c3..844f8fd7d 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -438,6 +438,13 @@ void FxMixer::masterMix( sampleFrame * _buf ) m_fxChannels[0]->m_peakLeft *= engine::getMixer()->masterGain(); m_fxChannels[0]->m_peakRight *= engine::getMixer()->masterGain(); + + // clear all channel buffers + for( int i = 0; i < numChannels(); ++i) + { + engine::getMixer()->clearAudioBuffer( m_fxChannels[i]->m_buffer, + engine::getMixer()->framesPerPeriod() ); + } } diff --git a/src/core/audio/AudioPort.cpp b/src/core/audio/AudioPort.cpp index 2b4301178..d9dc86b06 100644 --- a/src/core/audio/AudioPort.cpp +++ b/src/core/audio/AudioPort.cpp @@ -24,9 +24,10 @@ #include "AudioPort.h" #include "AudioDevice.h" -#include "EffectChain.h" -#include "engine.h" #include "Cpu.h" +#include "EffectChain.h" +#include "FxMixer.h" +#include "engine.h" AudioPort::AudioPort( const QString & _name, bool _has_effect_chain ) : @@ -123,3 +124,15 @@ bool AudioPort::processEffects() } + + +void AudioPort::doProcessing( sampleFrame * ) +{ + const bool me = processEffects(); + if( me || m_bufferUsage != NoUsage ) + { + engine::fxMixer()->mixToChannel( firstBuffer(), nextFxChannel() ); + nextPeriod(); + } +} + diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index b33e4c49f..f8e0c38b0 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -61,197 +61,11 @@ #endif -class MixerWorkerThread : public QThread -{ -public: - enum JobTypes - { - InvalidJob, - PlayHandle, - AudioPortEffects, - EffectChannel, - NumJobTypes - } ; - - struct JobQueueItem - { - JobQueueItem() : - type( InvalidJob ), - job( NULL ), - param( 0 ), - done( false ) - { - } - JobQueueItem( JobTypes _type, void * _job, int _param = 0 ) : - type( _type ), - job( _job ), - param( _param ), - done( false ) - { - } - - JobTypes type; - void * job; - int param; - - QAtomicInt done; - - } ; - - struct JobQueue - { -#define JOB_QUEUE_SIZE 1024 - JobQueue() : - queueSize( 0 ) - { - } - - JobQueueItem items[JOB_QUEUE_SIZE]; - int queueSize; - QAtomicInt itemsDone; - } ; - - static JobQueue s_jobQueue; - - MixerWorkerThread( int _worker_num, mixer * _mixer ) : - QThread( _mixer ), - m_workingBuf( CPU::allocFrames( _mixer->framesPerPeriod() ) ), - m_workerNum( _worker_num ), - m_quit( false ), - m_mixer( _mixer ), - m_queueReadyWaitCond( &m_mixer->m_queueReadyWaitCond ) - { - } - - virtual ~MixerWorkerThread() - { - CPU::freeFrames( m_workingBuf ); - } - - virtual void quit() - { - m_quit = true; - } - - void processJobQueue(); - - -private: - virtual void run() - { - QMutex m; - while( m_quit == false ) - { - m.lock(); - m_queueReadyWaitCond->wait( &m ); - processJobQueue(); - m.unlock(); - } - } - - sampleFrame * m_workingBuf; - int m_workerNum; - volatile bool m_quit; - mixer * m_mixer; - QWaitCondition * m_queueReadyWaitCond; - -} ; - MixerWorkerThread::JobQueue MixerWorkerThread::s_jobQueue; -void MixerWorkerThread::processJobQueue() -{ - for( int i = 0; i < s_jobQueue.queueSize; ++i ) - { - JobQueueItem * it = &s_jobQueue.items[i]; - if( it->done.fetchAndStoreOrdered( 1 ) == 0 ) - { - switch( it->type ) - { - case PlayHandle: - ( (playHandle *) it->job )-> - play( m_workingBuf ); - break; - case AudioPortEffects: - { - AudioPort * a = (AudioPort *) it->job; - const bool me = a->processEffects(); - if( me || a->m_bufferUsage != AudioPort::NoUsage ) - { - engine::fxMixer()->mixToChannel( a->firstBuffer(), - a->nextFxChannel() ); - a->nextPeriod(); - } - } - break; - case EffectChannel: - engine::fxMixer()->processChannel( (fx_ch_t) it->param ); - break; - default: - break; - } - s_jobQueue.itemsDone.fetchAndAddOrdered( 1 ); - } - } -} - -#define FILL_JOB_QUEUE_BEGIN(_vec_type,_vec,_condition) \ - MixerWorkerThread::s_jobQueue.queueSize = 0; \ - MixerWorkerThread::s_jobQueue.itemsDone = 0; \ - for( _vec_type::Iterator it = _vec.begin(); \ - it != _vec.end(); ++it ) \ - { \ - if( _condition ) \ - { - -#define FILL_JOB_QUEUE_END() \ - ++MixerWorkerThread::s_jobQueue.queueSize; \ - } \ - } - -#define FILL_JOB_QUEUE(_vec_type,_vec,_job_type,_condition) \ - FILL_JOB_QUEUE_BEGIN(_vec_type,_vec,_condition) \ - MixerWorkerThread::s_jobQueue.items \ - [MixerWorkerThread::s_jobQueue.queueSize] = \ - MixerWorkerThread::JobQueueItem( _job_type, \ - (void *) *it ); \ - FILL_JOB_QUEUE_END() - -#define FILL_JOB_QUEUE_PARAM(_vec_type,_vec,_job_type,_condition) \ - FILL_JOB_QUEUE_BEGIN(_vec_type,_vec,_condition) \ - MixerWorkerThread::s_jobQueue.items \ - [MixerWorkerThread::s_jobQueue.queueSize] = \ - MixerWorkerThread::JobQueueItem( _job_type, \ - NULL, *it ); \ - FILL_JOB_QUEUE_END() - -#define START_JOBS() \ - m_queueReadyWaitCond.wakeAll(); - -// define a pause instruction for spinlock-loop - merely useful on -// HyperThreading systems with just one physical core (e.g. Intel Atom) -#ifdef LMMS_HOST_X86 -#define SPINLOCK_PAUSE() asm( "pause" ) -#else -#ifdef LMMS_HOST_X86_64 -#define SPINLOCK_PAUSE() asm( "pause" ) -#else -#define SPINLOCK_PAUSE() -#endif -#endif - -#define WAIT_FOR_JOBS() \ - m_workers[m_numWorkers]->processJobQueue(); \ - while( MixerWorkerThread::s_jobQueue.itemsDone < \ - MixerWorkerThread::s_jobQueue.queueSize ) \ - { \ - SPINLOCK_PAUSE(); \ - } \ - - mixer::mixer() : @@ -347,7 +161,7 @@ mixer::~mixer() { m_workers[w]->quit(); } - START_JOBS(); + MixerWorkerThread::startJobs(); for( int w = 0; w < m_numWorkers; ++w ) { m_workers[w]->wait( 500 ); @@ -565,11 +379,8 @@ sampleFrameA * mixer::renderNextBuffer() // STAGE 1: run and render all play handles - FILL_JOB_QUEUE(PlayHandleList,m_playHandles, - MixerWorkerThread::PlayHandle, - !( *it )->done()); - START_JOBS(); - WAIT_FOR_JOBS(); + MixerWorkerThread::fillJobQueue( m_playHandles ); + MixerWorkerThread::startAndWaitForJobs(); // removed all play handles which are done for( PlayHandleList::Iterator it = m_playHandles.begin(); @@ -594,31 +405,12 @@ sampleFrameA * mixer::renderNextBuffer() // STAGE 2: process effects of all instrument- and sampletracks - FILL_JOB_QUEUE(QVector,m_audioPorts, - MixerWorkerThread::AudioPortEffects,1); - START_JOBS(); - WAIT_FOR_JOBS(); + MixerWorkerThread::fillJobQueue >( m_audioPorts ); + MixerWorkerThread::startAndWaitForJobs(); - - // STAGE 3: process effects in FX mixer - /*FILL_JOB_QUEUE_PARAM(QVector,__fx_channel_jobs, - MixerWorkerThread::EffectChannel,1); - START_JOBS(); - WAIT_FOR_JOBS();*/ - - - // STAGE 4: do master mix in FX mixer + // STAGE 3: do master mix in FX mixer fxm->masterMix( m_writeBuf ); - WAIT_FOR_JOBS(); - - // clear all channel buffers - for( int i = 0; i < fxm->numChannels(); ++i) - { - engine::getMixer()->clearAudioBuffer( fxm->effectChannel(i)->m_buffer, - engine::getMixer()->framesPerPeriod() ); - } - unlock(); From f6f4414c989e55a867045a2575252b8b78289942 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 12 Oct 2009 01:09:35 -0700 Subject: [PATCH 17/25] NOT WORKING! Fx Mixer uses job threads The FxMixer now uses job threads to accomplish its mixing. It's theoretically efficient, but there is a horrible thread bug preventing the code from working. I've spent 5 hours debugging and need some external help! --- include/FxMixer.h | 49 ++++++++------ include/mixer.h | 21 ++++-- src/core/FxMixer.cpp | 149 ++++++++++++++++++++++++++++++------------- 3 files changed, 147 insertions(+), 72 deletions(-) diff --git a/include/FxMixer.h b/include/FxMixer.h index b9f11ca3e..d4e4df6a3 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -33,31 +33,38 @@ -struct FxChannel +class FxChannel : public ThreadableJob { - FxChannel( Model * _parent ); - ~FxChannel(); + public: + FxChannel( Model * _parent ); + ~FxChannel(); - EffectChain m_fxChain; + EffectChain m_fxChain; - // set to true if any effect in the channel is enabled and running - bool m_stillRunning; + // set to true if any effect in the channel is enabled and running + bool m_stillRunning; - float m_peakLeft; - float m_peakRight; - sampleFrame * m_buffer; - BoolModel m_muteModel; - FloatModel m_volumeModel; - QString m_name; - QMutex m_lock; + float m_peakLeft; + float m_peakRight; + sampleFrame * m_buffer; + BoolModel m_muteModel; + FloatModel m_volumeModel; + QString m_name; + QMutex m_lock; + int m_channelIndex; // what channel index are we - // pointers to other channels that this one sends to - QVector m_sends; - QVector m_sendAmount; + // pointers to other channels that this one sends to + QVector m_sends; + QVector m_sendAmount; - // pointers to other channels that send to this one - QVector m_receives; -} ; + // pointers to other channels that send to this one + QVector m_receives; + + virtual bool requiresProcessing() const { return true; } + + private: + virtual void doProcessing( sampleFrame * _working_buffer ); +}; @@ -128,9 +135,11 @@ private: // the fx channels in the mixer. index 0 is always master. QVector m_fxChannels; - + // make sure we have at least num channels void allocateChannelsTo(int num); + void addChannelLeaf( int _ch, sampleFrame * _buf ); + friend class mixerWorkerThread; friend class FxMixerView; diff --git a/include/mixer.h b/include/mixer.h index 81be6af7f..cb6b65aaa 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -69,21 +69,31 @@ class MixerWorkerThread; class ThreadableJob { public: + + enum ProcessingState + { + Unstarted, + Queued, + InProgress, + Done + }; + ThreadableJob() : - m_done( false ) + m_state( ThreadableJob::Unstarted ) { } void reset() { - m_done = false; + m_state = ThreadableJob::Unstarted; } bool process( sampleFrame * _working_buffer ) { - if( m_done.fetchAndStoreOrdered( true ) == false ) + if( m_state.testAndSetOrdered( Queued, InProgress ) ) { doProcessing( _working_buffer ); + m_state = Done; return true; } return false; @@ -91,12 +101,11 @@ public: virtual bool requiresProcessing() const = 0; + QAtomicInt m_state; private: virtual void doProcessing( sampleFrame * _working_buffer ) = 0; - QAtomicInt m_done; - } ; @@ -575,7 +584,7 @@ public: { if( _job->requiresProcessing() ) { - _job->reset(); + _job->m_state = ThreadableJob::Queued; s_jobQueue.items[s_jobQueue.queueSize.fetchAndAddOrdered(1)] = _job; } } diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 844f8fd7d..66c65537a 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -59,6 +59,75 @@ FxChannel::~FxChannel() +void FxChannel::doProcessing(sampleFrame * _buf) +{ + FxMixer * fxm = engine::fxMixer(); + + const fpp_t fpp = engine::getMixer()->framesPerPeriod(); + if( _buf == NULL ) + { + _buf = m_buffer; + } + + if( ! m_muteModel.value() ) + { + // do mixer sends. loop through the channels that send to this one + for( int i = 0; i < m_receives.size(); ++i) + { + fx_ch_t senderIndex = m_receives[i]; + FxChannel * sender = fxm->effectChannel(senderIndex); + + // mix it with this one + float amt = fxm->channelSendModel(senderIndex, + m_channelIndex)->value(); + sampleFrame * ch_buf = sender->m_buffer; + const float v = sender->m_volumeModel.value(); + for( f_cnt_t f = 0; f < fpp; ++f ) + { + _buf[f][0] += ch_buf[f][0] * v * amt; + _buf[f][1] += ch_buf[f][1] * v * amt; + } + } + + const float v = m_volumeModel.value(); + + m_fxChain.startRunning(); + m_stillRunning = m_fxChain.processAudioBuffer( _buf, fpp); + m_peakLeft = engine::getMixer()->peakValueLeft( _buf, fpp ) * v; + m_peakRight = engine::getMixer()->peakValueRight( _buf, fpp ) * v; + } + else + { + m_peakLeft = m_peakRight = 0.0f; + } + + m_state = ThreadableJob::Done; + + // check if any of its parents are now able to be processed + for(int i=0; ieffectChannel(m_sends[i]); + if( parent->m_state == ThreadableJob::Unstarted ) + { + bool everyLeafDone = true; + for( int j=0; jm_receives.size(); ++j ) + { + if( fxm->effectChannel(parent->m_receives[j])->m_state != + ThreadableJob::Done ) + { + everyLeafDone = false; + break; + } + } + if( everyLeafDone ) + { + MixerWorkerThread::addJob(parent); + } + } + } + +} @@ -363,51 +432,8 @@ void FxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch ) void FxMixer::processChannel( fx_ch_t _ch, sampleFrame * _buf ) { - const fpp_t fpp = engine::getMixer()->framesPerPeriod(); - FxChannel * thisCh = m_fxChannels[_ch]; - if( _buf == NULL ) - { - _buf = thisCh->m_buffer; - } + m_fxChannels[_ch]->process(_buf); - if( ! thisCh->m_muteModel.value() ) - { - // do mixer sends. loop through the channels that send to this one - for( int i = 0; i < thisCh->m_receives.size(); ++i) - { - fx_ch_t senderIndex = thisCh->m_receives[i]; - FxChannel * sender = m_fxChannels[senderIndex]; - - // compute the sending channel - processChannel( senderIndex ); - - // mix it with this one - float amt = channelSendModel(senderIndex, _ch)->value(); - sampleFrame * ch_buf = sender->m_buffer; - const float v = sender->m_volumeModel.value(); - for( f_cnt_t f = 0; f < fpp; ++f ) - { - _buf[f][0] += ch_buf[f][0] * v * amt; - _buf[f][1] += ch_buf[f][1] * v * amt; - } - } - - - - const float v = thisCh->m_volumeModel.value(); - - thisCh->m_fxChain.startRunning(); - thisCh->m_stillRunning = thisCh-> - m_fxChain.processAudioBuffer( _buf, fpp); - thisCh->m_peakLeft = - engine::getMixer()->peakValueLeft( _buf, fpp ) * v; - thisCh->m_peakRight = - engine::getMixer()->peakValueRight( _buf, fpp ) * v; - } - else - { - thisCh->m_peakLeft = thisCh->m_peakRight = 0.0f; - } } @@ -421,13 +447,42 @@ void FxMixer::prepareMasterMix() +void FxMixer::addChannelLeaf( int _ch, sampleFrame * _buf ) +{ + FxChannel * thisCh = m_fxChannels[_ch]; + + // remember what channel number we are, 'cause we need it later + thisCh->m_channelIndex = _ch; + + int numDeps = thisCh->m_receives.size(); + if( numDeps > 0 ) + { + for(int i=0; im_receives[i], _buf ); + } + } + else + { + // add this channel to job list + MixerWorkerThread::addJob( thisCh ); + } + +} + + void FxMixer::masterMix( sampleFrame * _buf ) { const int fpp = engine::getMixer()->framesPerPeriod(); memcpy( _buf, m_fxChannels[0]->m_buffer, sizeof( sampleFrame ) * fpp ); - processChannel( 0, _buf ); + // recursively loop through channel dependency chain + // and add all channels to job list that have no dependencies + // when the channel completes it will check its parent to see if it needs + // to be processed. + addChannelLeaf( 0, _buf ); + MixerWorkerThread::startAndWaitForJobs(); const float v = m_fxChannels[0]->m_volumeModel.value(); for( f_cnt_t f = 0; f < engine::getMixer()->framesPerPeriod(); ++f ) @@ -439,11 +494,13 @@ void FxMixer::masterMix( sampleFrame * _buf ) m_fxChannels[0]->m_peakLeft *= engine::getMixer()->masterGain(); m_fxChannels[0]->m_peakRight *= engine::getMixer()->masterGain(); - // clear all channel buffers + // clear all channel buffers and + // reset channel process state for( int i = 0; i < numChannels(); ++i) { engine::getMixer()->clearAudioBuffer( m_fxChannels[i]->m_buffer, engine::getMixer()->framesPerPeriod() ); + m_fxChannels[i]->reset(); } } From 95eb60f05fc2dc190e8c8e84ffcbcbba7fad8e7c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 13 Oct 2009 02:21:13 -0700 Subject: [PATCH 18/25] Fixed the audio/visual screwups With the help of Toby, effects in the channels work and the peak displays correctly. However the freeze-up bug is still wreaking havoc. --- include/FxMixer.h | 1 + include/mixer.h | 9 +++++++-- src/core/FxMixer.cpp | 27 ++++++++++++++++++++------- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/include/FxMixer.h b/include/FxMixer.h index d4e4df6a3..3d76903d5 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -52,6 +52,7 @@ class FxChannel : public ThreadableJob QString m_name; QMutex m_lock; int m_channelIndex; // what channel index are we + bool m_queued; // are we queued up for rendering yet? // pointers to other channels that this one sends to QVector m_sends; diff --git a/include/mixer.h b/include/mixer.h index cb6b65aaa..d992920c6 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -569,11 +569,16 @@ public: } } - template - static void fillJobQueue( const T & _vec ) + static void resetJobQueue() { s_jobQueue.queueSize = 0; s_jobQueue.itemsDone = 0; + } + + template + static void fillJobQueue( const T & _vec ) + { + resetJobQueue(); for( typename T::ConstIterator it = _vec.begin(); it != _vec.end(); ++it ) { addJob( *it ); diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 66c65537a..4c3e17007 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -43,7 +43,8 @@ FxChannel::FxChannel( Model * _parent ) : m_muteModel( false, _parent ), m_volumeModel( 1.0, 0.0, 2.0, 0.01, _parent ), m_name(), - m_lock() + m_lock(), + m_queued( false ) { engine::getMixer()->clearAudioBuffer( m_buffer, engine::getMixer()->framesPerPeriod() ); @@ -62,12 +63,16 @@ FxChannel::~FxChannel() void FxChannel::doProcessing(sampleFrame * _buf) { FxMixer * fxm = engine::fxMixer(); - const fpp_t fpp = engine::getMixer()->framesPerPeriod(); - if( _buf == NULL ) - { - _buf = m_buffer; - } + + // ignore the passed _buf + // always use m_buffer + // this is just an auxilliary buffer if doProcessing() + // needs one for processing while running + // particularly important for playHandles, so Instruments + // can operate on this buffer the whole time + // this improves cache hit rate + _buf = m_buffer; if( ! m_muteModel.value() ) { @@ -454,6 +459,10 @@ void FxMixer::addChannelLeaf( int _ch, sampleFrame * _buf ) // remember what channel number we are, 'cause we need it later thisCh->m_channelIndex = _ch; + // if we're muted or this channel is seen already, discount it + if( thisCh->m_muteModel.value() || thisCh->m_queued ) + return; + int numDeps = thisCh->m_receives.size(); if( numDeps > 0 ) { @@ -465,6 +474,7 @@ void FxMixer::addChannelLeaf( int _ch, sampleFrame * _buf ) else { // add this channel to job list + thisCh->m_queued = true; MixerWorkerThread::addJob( thisCh ); } @@ -475,15 +485,17 @@ void FxMixer::addChannelLeaf( int _ch, sampleFrame * _buf ) void FxMixer::masterMix( sampleFrame * _buf ) { const int fpp = engine::getMixer()->framesPerPeriod(); - memcpy( _buf, m_fxChannels[0]->m_buffer, sizeof( sampleFrame ) * fpp ); // recursively loop through channel dependency chain // and add all channels to job list that have no dependencies // when the channel completes it will check its parent to see if it needs // to be processed. + MixerWorkerThread::resetJobQueue(); addChannelLeaf( 0, _buf ); MixerWorkerThread::startAndWaitForJobs(); + memcpy( _buf, m_fxChannels[0]->m_buffer, sizeof( sampleFrame ) * fpp ); + const float v = m_fxChannels[0]->m_volumeModel.value(); for( f_cnt_t f = 0; f < engine::getMixer()->framesPerPeriod(); ++f ) { @@ -501,6 +513,7 @@ void FxMixer::masterMix( sampleFrame * _buf ) engine::getMixer()->clearAudioBuffer( m_fxChannels[i]->m_buffer, engine::getMixer()->framesPerPeriod() ); m_fxChannels[i]->reset(); + m_fxChannels[i]->m_queued = false; } } From 45a2f81eaa94a39ef619dfdb2aaaab46dba98d5c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 13 Oct 2009 03:14:23 -0700 Subject: [PATCH 19/25] Rough fix for freeze-up bug Added startJobs() to the waitForJobs() loop, and don't give up in masterMix() until status of master channel is Done. --- include/mixer.h | 2 +- src/core/FxMixer.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/mixer.h b/include/mixer.h index d992920c6..d6db2cb12 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -620,7 +620,7 @@ public: m->m_workers[m->m_numWorkers]->processJobQueue(); while( s_jobQueue.itemsDone < s_jobQueue.queueSize ) { - SPINLOCK_PAUSE(); + startJobs(); } } diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 4c3e17007..1e8a255e6 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -492,7 +492,10 @@ void FxMixer::masterMix( sampleFrame * _buf ) // to be processed. MixerWorkerThread::resetJobQueue(); addChannelLeaf( 0, _buf ); - MixerWorkerThread::startAndWaitForJobs(); + while( m_fxChannels[0]->m_state != ThreadableJob::Done ) + { + MixerWorkerThread::startAndWaitForJobs(); + } memcpy( _buf, m_fxChannels[0]->m_buffer, sizeof( sampleFrame ) * fpp ); From a9d24d34f26ffe63306650979bbd571d1052c7a0 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Wed, 14 Oct 2009 01:22:31 +0200 Subject: [PATCH 20/25] Mixer/FxMixer: separated MixerWorkerThread and ThreadableJob into files Declarations and implementations of MixerWorkerThread and ThreadableJob have been moved into separate source files. Furthermore there were some improvements to MixerWorkerThreads. MixerWorkerThread::processJobQueue() does not return until the job queue completely has been processed. This way each thread can "help" to finish processing the queue and does not get back to sleep until all of the work is done. Management of the queue is now done via an array of QAtomicPointers. Items that are non-NULL still need to be processed while NULL-items were taken from the queue (i.e. in progress or done). Thus we do not need to deal with ThreadableJob-states within MixerWorkerThread anymore. --- include/AudioPort.h | 1 - include/FxMixer.h | 1 - include/MixerWorkerThread.h | 97 +++++++++++++++++ include/ThreadableJob.h | 84 +++++++++++++++ include/mixer.h | 190 +-------------------------------- include/play_handle.h | 1 + src/core/FxMixer.cpp | 23 ++-- src/core/MixerWorkerThread.cpp | 138 ++++++++++++++++++++++++ src/core/mixer.cpp | 8 +- 9 files changed, 329 insertions(+), 214 deletions(-) create mode 100644 include/MixerWorkerThread.h create mode 100644 include/ThreadableJob.h create mode 100644 src/core/MixerWorkerThread.cpp diff --git a/include/AudioPort.h b/include/AudioPort.h index d84dd179f..6ac7e8294 100644 --- a/include/AudioPort.h +++ b/include/AudioPort.h @@ -27,7 +27,6 @@ #include #include -#include #include "mixer.h" diff --git a/include/FxMixer.h b/include/FxMixer.h index 3d76903d5..21fe100f3 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -76,7 +76,6 @@ public: virtual ~FxMixer(); void mixToChannel( const sampleFrame * _buf, fx_ch_t _ch ); - void processChannel( fx_ch_t _ch, sampleFrame * _buf = NULL ); void prepareMasterMix(); void masterMix( sampleFrame * _buf ); diff --git a/include/MixerWorkerThread.h b/include/MixerWorkerThread.h new file mode 100644 index 000000000..03e195e60 --- /dev/null +++ b/include/MixerWorkerThread.h @@ -0,0 +1,97 @@ +/* + * MixerWorkerThread.h - declaration of class MixerWorkerThread + * + * Copyright (c) 2009 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * 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. + * + */ + +#ifndef _MIXER_WORKER_THREAD_H +#define _MIXER_WORKER_THREAD_H + +#include +#include +#include + +#include "mixer.h" + + +class MixerWorkerThread : public QThread +{ +public: + struct JobQueue + { +#define JOB_QUEUE_SIZE 1024 + JobQueue() : + items(), + queueSize( 0 ), + itemsDone( 0 ) + { + } + + QAtomicPointer items[JOB_QUEUE_SIZE]; + QAtomicInt queueSize; + QAtomicInt itemsDone; + } ; + + static JobQueue s_jobQueue; + + MixerWorkerThread( int _worker_num, mixer * _mixer ); + virtual ~MixerWorkerThread(); + + virtual void quit(); + + void processJobQueue(); + static void resetJobQueue(); + + template + static void fillJobQueue( const T & _vec ) + { + resetJobQueue(); + for( typename T::ConstIterator it = _vec.begin(); it != _vec.end(); ++it ) + { + addJob( *it ); + } + } + + static void addJob( ThreadableJob * _job ); + + static void startJobs(); + static void waitForJobs(); + + static void startAndWaitForJobs() + { + startJobs(); + waitForJobs(); + } + + +private: + virtual void run(); + + sampleFrame * m_workingBuf; + int m_workerNum; + volatile bool m_quit; + mixer * m_mixer; + QWaitCondition * m_queueReadyWaitCond; + +} ; + + +#endif diff --git a/include/ThreadableJob.h b/include/ThreadableJob.h new file mode 100644 index 000000000..bade2baa5 --- /dev/null +++ b/include/ThreadableJob.h @@ -0,0 +1,84 @@ +/* + * ThreadableJob.h - declaration of class ThreadableJob + * + * Copyright (c) 2009 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * 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. + * + */ + +#ifndef _THREADABLE_JOB_H +#define _THREADABLE_JOB_H + +#include + +#include "mixer.h" + + +class ThreadableJob +{ +public: + + enum ProcessingState + { + Unstarted, + Queued, + InProgress, + Done + }; + + ThreadableJob() : + m_state( ThreadableJob::Unstarted ) + { + } + + inline ProcessingState state() const + { + return static_cast( (int) m_state ); + } + + inline void reset() + { + m_state = Unstarted; + } + + inline void queue() + { + m_state = Queued; + } + + void process( sampleFrame * _working_buffer ) + { + if( m_state.testAndSetOrdered( Queued, InProgress ) ) + { + doProcessing( _working_buffer ); + m_state = Done; + } + } + + virtual bool requiresProcessing() const = 0; + + +protected: + virtual void doProcessing( sampleFrame * _working_buffer ) = 0; + + QAtomicInt m_state; + +} ; + +#endif diff --git a/include/mixer.h b/include/mixer.h index d6db2cb12..8c539a0c8 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -62,53 +62,9 @@ const Keys BaseKey = Key_A; const Octaves BaseOctave = DefaultOctave; - class MixerWorkerThread; -// TODO: move to ThreadableJob.h -class ThreadableJob -{ -public: - - enum ProcessingState - { - Unstarted, - Queued, - InProgress, - Done - }; - - ThreadableJob() : - m_state( ThreadableJob::Unstarted ) - { - } - - void reset() - { - m_state = ThreadableJob::Unstarted; - } - - bool process( sampleFrame * _working_buffer ) - { - if( m_state.testAndSetOrdered( Queued, InProgress ) ) - { - doProcessing( _working_buffer ); - m_state = Done; - return true; - } - return false; - } - - virtual bool requiresProcessing() const = 0; - - QAtomicInt m_state; - -private: - virtual void doProcessing( sampleFrame * _working_buffer ) = 0; - -} ; - - +#include "ThreadableJob.h" #include "play_handle.h" @@ -509,148 +465,4 @@ private: } ; - -// TODO: move to MixerWorkerThread.h / MixerWorkerThread.cpp -#include "Cpu.h" -#include "engine.h" - -class MixerWorkerThread : public QThread -{ -public: - struct JobQueue - { -#define JOB_QUEUE_SIZE 1024 - JobQueue() : - queueSize( 0 ), - itemsDone( 0 ) - { - for( int i = 0; i < JOB_QUEUE_SIZE; ++i ) - { - items[i] = NULL; - } - } - - ThreadableJob * items[JOB_QUEUE_SIZE]; - QAtomicInt queueSize; - QAtomicInt itemsDone; - } ; - - static JobQueue s_jobQueue; - - MixerWorkerThread( int _worker_num, mixer * _mixer ) : - QThread( _mixer ), - m_workingBuf( CPU::allocFrames( _mixer->framesPerPeriod() ) ), - m_workerNum( _worker_num ), - m_quit( false ), - m_mixer( _mixer ), - m_queueReadyWaitCond( &m_mixer->m_queueReadyWaitCond ) - { - } - - virtual ~MixerWorkerThread() - { - CPU::freeFrames( m_workingBuf ); - } - - virtual void quit() - { - m_quit = true; - } - - void processJobQueue() - { - for( int i = 0; i < s_jobQueue.queueSize; ++i ) - { - // returns true if ThreadableJob was not processed before - if( s_jobQueue.items[i]->process( m_workingBuf ) ) - { - s_jobQueue.itemsDone.fetchAndAddOrdered( 1 ); - } - } - } - - static void resetJobQueue() - { - s_jobQueue.queueSize = 0; - s_jobQueue.itemsDone = 0; - } - - template - static void fillJobQueue( const T & _vec ) - { - resetJobQueue(); - for( typename T::ConstIterator it = _vec.begin(); it != _vec.end(); ++it ) - { - addJob( *it ); - } - } - - static void addJob( ThreadableJob * _job ) - { - if( _job->requiresProcessing() ) - { - _job->m_state = ThreadableJob::Queued; - s_jobQueue.items[s_jobQueue.queueSize.fetchAndAddOrdered(1)] = _job; - } - } - - -// define a pause instruction for spinlock-loop - merely useful on -// HyperThreading systems with just one physical core (e.g. Intel Atom) -#ifdef LMMS_HOST_X86 -#define SPINLOCK_PAUSE() asm( "pause" ) -#else -#ifdef LMMS_HOST_X86_64 -#define SPINLOCK_PAUSE() asm( "pause" ) -#else -#define SPINLOCK_PAUSE() -#endif -#endif - - static void startJobs() - { - // TODO: this is dirty! - engine::getMixer()->m_queueReadyWaitCond.wakeAll(); - } - - static void waitForJobs() - { - // TODO: this is dirty! - mixer * m = engine::getMixer(); - m->m_workers[m->m_numWorkers]->processJobQueue(); - while( s_jobQueue.itemsDone < s_jobQueue.queueSize ) - { - startJobs(); - } - } - - static void startAndWaitForJobs() - { - startJobs(); - waitForJobs(); - } - - -private: - virtual void run() - { - QMutex m; - while( m_quit == false ) - { - m.lock(); - m_queueReadyWaitCond->wait( &m ); - processJobQueue(); - m.unlock(); - } - } - - sampleFrame * m_workingBuf; - int m_workerNum; - volatile bool m_quit; - mixer * m_mixer; - QWaitCondition * m_queueReadyWaitCond; - -} ; - - #endif diff --git a/include/play_handle.h b/include/play_handle.h index 1a49fb540..26a8f1058 100644 --- a/include/play_handle.h +++ b/include/play_handle.h @@ -25,6 +25,7 @@ #ifndef _PLAY_HANDLE_H #define _PLAY_HANDLE_H +#include "ThreadableJob.h" #include "mixer.h" class track; diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 1e8a255e6..e936d33ba 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -22,10 +22,10 @@ * */ - #include #include "FxMixer.h" +#include "MixerWorkerThread.h" #include "Cpu.h" #include "Effect.h" #include "song.h" @@ -60,7 +60,7 @@ FxChannel::~FxChannel() -void FxChannel::doProcessing(sampleFrame * _buf) +void FxChannel::doProcessing( sampleFrame * _buf ) { FxMixer * fxm = engine::fxMixer(); const fpp_t fpp = engine::getMixer()->framesPerPeriod(); @@ -97,7 +97,7 @@ void FxChannel::doProcessing(sampleFrame * _buf) const float v = m_volumeModel.value(); m_fxChain.startRunning(); - m_stillRunning = m_fxChain.processAudioBuffer( _buf, fpp); + m_stillRunning = m_fxChain.processAudioBuffer( _buf, fpp ); m_peakLeft = engine::getMixer()->peakValueLeft( _buf, fpp ) * v; m_peakRight = engine::getMixer()->peakValueRight( _buf, fpp ) * v; } @@ -113,13 +113,13 @@ void FxChannel::doProcessing(sampleFrame * _buf) { // if parent.unstarted and every parent.leaf.done: FxChannel * parent = fxm->effectChannel(m_sends[i]); - if( parent->m_state == ThreadableJob::Unstarted ) + if( parent->state() == ThreadableJob::Unstarted ) { bool everyLeafDone = true; for( int j=0; jm_receives.size(); ++j ) { - if( fxm->effectChannel(parent->m_receives[j])->m_state != - ThreadableJob::Done ) + if( fxm->effectChannel( parent->m_receives[j] )->state() != + ThreadableJob::Done ) { everyLeafDone = false; break; @@ -435,15 +435,6 @@ void FxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch ) -void FxMixer::processChannel( fx_ch_t _ch, sampleFrame * _buf ) -{ - m_fxChannels[_ch]->process(_buf); - -} - - - - void FxMixer::prepareMasterMix() { engine::getMixer()->clearAudioBuffer( m_fxChannels[0]->m_buffer, @@ -492,7 +483,7 @@ void FxMixer::masterMix( sampleFrame * _buf ) // to be processed. MixerWorkerThread::resetJobQueue(); addChannelLeaf( 0, _buf ); - while( m_fxChannels[0]->m_state != ThreadableJob::Done ) + while( m_fxChannels[0]->state() != ThreadableJob::Done ) { MixerWorkerThread::startAndWaitForJobs(); } diff --git a/src/core/MixerWorkerThread.cpp b/src/core/MixerWorkerThread.cpp new file mode 100644 index 000000000..539b32245 --- /dev/null +++ b/src/core/MixerWorkerThread.cpp @@ -0,0 +1,138 @@ +/* + * MixerWorkerThread.cpp - implementation of MixerWorkerThread + * + * Copyright (c) 2009 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * 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 "MixerWorkerThread.h" +#include "Cpu.h" +#include "engine.h" +#include "mixer.h" + + +MixerWorkerThread::JobQueue MixerWorkerThread::s_jobQueue; + + +MixerWorkerThread::MixerWorkerThread( int _worker_num, mixer * _mixer ) : + QThread( _mixer ), + m_workingBuf( CPU::allocFrames( _mixer->framesPerPeriod() ) ), + m_workerNum( _worker_num ), + m_quit( false ), + m_mixer( _mixer ), + m_queueReadyWaitCond( &m_mixer->m_queueReadyWaitCond ) +{ + resetJobQueue(); +} + + + + +MixerWorkerThread::~MixerWorkerThread() +{ + CPU::freeFrames( m_workingBuf ); +} + + + + +void MixerWorkerThread::quit() +{ + m_quit = true; +} + + + + +void MixerWorkerThread::processJobQueue() +{ + while( s_jobQueue.itemsDone != s_jobQueue.queueSize ) + { + for( int i = 0; i < s_jobQueue.queueSize; ++i ) + { + ThreadableJob * job = + s_jobQueue.items[i].fetchAndStoreOrdered( NULL ); + if( job ) + { + job->process( m_workingBuf ); + s_jobQueue.itemsDone.fetchAndAddOrdered( 1 ); + } + } + } +} + + + + +void MixerWorkerThread::resetJobQueue() +{ + s_jobQueue.queueSize = 0; + s_jobQueue.itemsDone = 0; +} + + + + +void MixerWorkerThread::addJob( ThreadableJob * _job ) +{ + if( _job->requiresProcessing() ) + { + // update job state + _job->queue(); + // actually queue the job via atomic operations + s_jobQueue.items[s_jobQueue.queueSize.fetchAndAddOrdered(1)] = _job; + } +} + + + + +void MixerWorkerThread::startJobs() +{ + // TODO: this is dirty! + engine::getMixer()->m_queueReadyWaitCond.wakeAll(); +} + + + + +void MixerWorkerThread::waitForJobs() +{ + // TODO: this is dirty! + mixer * m = engine::getMixer(); + m->m_workers[m->m_numWorkers]->processJobQueue(); +} + + + + +void MixerWorkerThread::run() +{ + QMutex m; + while( m_quit == false ) + { + m.lock(); + m_queueReadyWaitCond->wait( &m ); + processJobQueue(); + m.unlock(); + } +} + + diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index f8e0c38b0..dab8ee496 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -26,6 +26,7 @@ #include "mixer.h" #include "FxMixer.h" +#include "MixerWorkerThread.h" #include "play_handle.h" #include "song.h" #include "templates.h" @@ -61,13 +62,6 @@ #endif - -MixerWorkerThread::JobQueue MixerWorkerThread::s_jobQueue; - - - - - mixer::mixer() : m_framesPerPeriod( DEFAULT_BUFFER_SIZE ), m_workingBuf( NULL ), From 05b1325c09d344834d8c243b198871a7e2e95b72 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 19 Oct 2009 01:03:22 +0200 Subject: [PATCH 21/25] Song: do not refresh FxMixerView when loading song in console mode Calling FxMixerView::refreshDisplay causes LMMS to crash when running in console mode. Therefore explicitely check GUI mode before calling this function. --- src/core/song.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/song.cpp b/src/core/song.cpp index 0d5e37c08..6b3ff3cc5 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -912,8 +912,11 @@ void song::loadProject( const QString & _file_name ) { engine::fxMixer()->restoreState( node.toElement() ); - // refresh FxMixerView - engine::fxMixerView()->refreshDisplay(); + if( engine::hasGUI() ) + { + // refresh FxMixerView + engine::fxMixerView()->refreshDisplay(); + } } node = node.nextSibling(); From 4f5d31f862e72103b701515f2b7e40c72b8cf1ac Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 19 Oct 2009 01:04:18 +0200 Subject: [PATCH 22/25] MixerWorkerThread: exit outer loop in processJobQueue() if finished Exit the outer loop of processJobQueue() Only as soon as we went through the job queue without having processed at least one job. In MixerWorkerThread::waitForJobs() re-introduced "pause" instruction on x86 and x86_64 giving better performance on HyperThreading systems. These improvements however still do not solve all performance and multithreading issues. --- src/core/MixerWorkerThread.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/core/MixerWorkerThread.cpp b/src/core/MixerWorkerThread.cpp index 539b32245..8941b1a56 100644 --- a/src/core/MixerWorkerThread.cpp +++ b/src/core/MixerWorkerThread.cpp @@ -63,8 +63,10 @@ void MixerWorkerThread::quit() void MixerWorkerThread::processJobQueue() { - while( s_jobQueue.itemsDone != s_jobQueue.queueSize ) + bool processedJob = true; + while( processedJob && (int) s_jobQueue.itemsDone < (int) s_jobQueue.queueSize ) { + processedJob = false; for( int i = 0; i < s_jobQueue.queueSize; ++i ) { ThreadableJob * job = @@ -72,6 +74,7 @@ void MixerWorkerThread::processJobQueue() if( job ) { job->process( m_workingBuf ); + processedJob = true; s_jobQueue.itemsDone.fetchAndAddOrdered( 1 ); } } @@ -118,6 +121,13 @@ void MixerWorkerThread::waitForJobs() // TODO: this is dirty! mixer * m = engine::getMixer(); m->m_workers[m->m_numWorkers]->processJobQueue(); + + while( (int) s_jobQueue.itemsDone < (int) s_jobQueue.queueSize ) + { +#if defined(LMMS_HOST_X86) || defined(LMMS_HOST_X86_64) + asm( "pause" ); +#endif + } } From ff03ddb8e4902e6bf5fd5c6d2a970dad851dedf1 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Sat, 24 Oct 2009 18:59:00 +0200 Subject: [PATCH 23/25] MixerWorkerThread: added job queue modes and improved class structure JobQueues can now operate in JobQueue::Static and JobQueue::Dynamic mode. In static mode it operates the way it always used to while in dynamic mode a changing job queue is supported. This is particularly important for FX mixer sends. There were also heavy improvements regarding the overall structure and functionality of MixerWorkerThread and MixerWorkerThread::JobQueue. There's now a clean distinction between multi-threaded processing and actual (thread-safe) job queue processing. MixerWorkerThread does not need to be a friend class of Mixer anymore. --- include/MixerWorkerThread.h | 80 ++++++++++------ include/mixer.h | 2 - src/core/FxMixer.cpp | 2 +- src/core/MixerWorkerThread.cpp | 167 ++++++++++++++++++--------------- src/core/mixer.cpp | 8 +- 5 files changed, 146 insertions(+), 113 deletions(-) diff --git a/include/MixerWorkerThread.h b/include/MixerWorkerThread.h index 03e195e60..09a54ba92 100644 --- a/include/MixerWorkerThread.h +++ b/include/MixerWorkerThread.h @@ -27,7 +27,6 @@ #include #include -#include #include "mixer.h" @@ -35,61 +34,82 @@ class MixerWorkerThread : public QThread { public: - struct JobQueue + // internal representation of the job queue - all functions are thread-safe + class JobQueue { -#define JOB_QUEUE_SIZE 1024 + public: + enum OperationMode + { + Static, // no jobs added while processing queue + Dynamic // jobs can be added while processing queue + } ; + JobQueue() : - items(), - queueSize( 0 ), - itemsDone( 0 ) + m_items(), + m_queueSize( 0 ), + m_itemsDone( 0 ), + m_opMode( Static ) { } - QAtomicPointer items[JOB_QUEUE_SIZE]; - QAtomicInt queueSize; - QAtomicInt itemsDone; + void reset( OperationMode _opMode ); + + void addJob( ThreadableJob * _job ); + + void run( sampleFrame * _buffer ); + void wait(); + + private: +#define JOB_QUEUE_SIZE 1024 + QAtomicPointer m_items[JOB_QUEUE_SIZE]; + QAtomicInt m_queueSize; + QAtomicInt m_itemsDone; + OperationMode m_opMode; + } ; - static JobQueue s_jobQueue; - MixerWorkerThread( int _worker_num, mixer * _mixer ); + MixerWorkerThread( mixer * _mixer ); virtual ~MixerWorkerThread(); virtual void quit(); - void processJobQueue(); - static void resetJobQueue(); - - template - static void fillJobQueue( const T & _vec ) + static void resetJobQueue( JobQueue::OperationMode _opMode = + JobQueue::Static ) { - resetJobQueue(); + globalJobQueue.reset( _opMode ); + } + + static void addJob( ThreadableJob * _job ) + { + globalJobQueue.addJob( _job ); + } + + // a convenient helper function allowing to pass a container with pointers + // to ThreadableJob objects + template + static void fillJobQueue( const T & _vec, + JobQueue::OperationMode _opMode = JobQueue::Static ) + { + resetJobQueue( _opMode ); for( typename T::ConstIterator it = _vec.begin(); it != _vec.end(); ++it ) { addJob( *it ); } } - static void addJob( ThreadableJob * _job ); - - static void startJobs(); - static void waitForJobs(); - - static void startAndWaitForJobs() - { - startJobs(); - waitForJobs(); - } + static void startAndWaitForJobs(); private: virtual void run(); + static JobQueue globalJobQueue; + static QWaitCondition * queueReadyWaitCond; + static QList workerThreads; + sampleFrame * m_workingBuf; - int m_workerNum; volatile bool m_quit; - mixer * m_mixer; - QWaitCondition * m_queueReadyWaitCond; } ; diff --git a/include/mixer.h b/include/mixer.h index 8c539a0c8..a92b16924 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -433,7 +433,6 @@ private: int m_cpuLoad; QVector m_workers; int m_numWorkers; - QWaitCondition m_queueReadyWaitCond; PlayHandleList m_playHandles; @@ -461,7 +460,6 @@ private: friend class engine; - friend class MixerWorkerThread; } ; diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index e936d33ba..13c7d626a 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -481,7 +481,7 @@ void FxMixer::masterMix( sampleFrame * _buf ) // and add all channels to job list that have no dependencies // when the channel completes it will check its parent to see if it needs // to be processed. - MixerWorkerThread::resetJobQueue(); + MixerWorkerThread::resetJobQueue( MixerWorkerThread::JobQueue::Dynamic ); addChannelLeaf( 0, _buf ); while( m_fxChannels[0]->state() != ThreadableJob::Done ) { diff --git a/src/core/MixerWorkerThread.cpp b/src/core/MixerWorkerThread.cpp index 8941b1a56..f9d2e3e95 100644 --- a/src/core/MixerWorkerThread.cpp +++ b/src/core/MixerWorkerThread.cpp @@ -28,17 +28,92 @@ #include "mixer.h" -MixerWorkerThread::JobQueue MixerWorkerThread::s_jobQueue; +MixerWorkerThread::JobQueue MixerWorkerThread::globalJobQueue; +QWaitCondition * MixerWorkerThread::queueReadyWaitCond = NULL; +QList MixerWorkerThread::workerThreads; -MixerWorkerThread::MixerWorkerThread( int _worker_num, mixer * _mixer ) : + +// implementation of internal JobQueue +void MixerWorkerThread::JobQueue::reset( OperationMode _opMode ) +{ + m_queueSize = 0; + m_itemsDone = 0; + m_opMode = _opMode; +} + + + + +void MixerWorkerThread::JobQueue::addJob( ThreadableJob * _job ) +{ + if( _job->requiresProcessing() ) + { + // update job state + _job->queue(); + // actually queue the job via atomic operations + m_items[m_queueSize.fetchAndAddOrdered(1)] = _job; + } +} + + + +void MixerWorkerThread::JobQueue::run( sampleFrame * _buffer ) +{ + bool processedJob = true; + while( processedJob && (int) m_itemsDone < (int) m_queueSize ) + { + processedJob = false; + for( int i = 0; i < m_queueSize; ++i ) + { + ThreadableJob * job = m_items[i].fetchAndStoreOrdered( NULL ); + if( job ) + { + job->process( _buffer ); + processedJob = true; + m_itemsDone.fetchAndAddOrdered( 1 ); + } + } + // always exit loop if we're not in dynamic mode + processedJob = processedJob && ( m_opMode == Dynamic ); + } +} + + + + +void MixerWorkerThread::JobQueue::wait() +{ + while( (int) m_itemsDone < (int) m_queueSize ) + { +#if defined(LMMS_HOST_X86) || defined(LMMS_HOST_X86_64) + asm( "pause" ); +#endif + } +} + + + + + +// implementation of worker threads + +MixerWorkerThread::MixerWorkerThread( mixer * _mixer ) : QThread( _mixer ), m_workingBuf( CPU::allocFrames( _mixer->framesPerPeriod() ) ), - m_workerNum( _worker_num ), - m_quit( false ), - m_mixer( _mixer ), - m_queueReadyWaitCond( &m_mixer->m_queueReadyWaitCond ) + m_quit( false ) { + // initialize global static data + if( queueReadyWaitCond == NULL ) + { + queueReadyWaitCond = new QWaitCondition; + } + + // keep track of all instantiated worker threads - this is used for + // processing the last worker thread "inline", see comments in + // MixerWorkerThread::startAndWaitForJobs() for details + workerThreads << this; + resetJobQueue(); } @@ -48,6 +123,8 @@ MixerWorkerThread::MixerWorkerThread( int _worker_num, mixer * _mixer ) : MixerWorkerThread::~MixerWorkerThread() { CPU::freeFrames( m_workingBuf ); + + workerThreads.removeAll( this ); } @@ -56,78 +133,20 @@ MixerWorkerThread::~MixerWorkerThread() void MixerWorkerThread::quit() { m_quit = true; + resetJobQueue(); } -void MixerWorkerThread::processJobQueue() +void MixerWorkerThread::startAndWaitForJobs() { - bool processedJob = true; - while( processedJob && (int) s_jobQueue.itemsDone < (int) s_jobQueue.queueSize ) - { - processedJob = false; - for( int i = 0; i < s_jobQueue.queueSize; ++i ) - { - ThreadableJob * job = - s_jobQueue.items[i].fetchAndStoreOrdered( NULL ); - if( job ) - { - job->process( m_workingBuf ); - processedJob = true; - s_jobQueue.itemsDone.fetchAndAddOrdered( 1 ); - } - } - } -} - - - - -void MixerWorkerThread::resetJobQueue() -{ - s_jobQueue.queueSize = 0; - s_jobQueue.itemsDone = 0; -} - - - - -void MixerWorkerThread::addJob( ThreadableJob * _job ) -{ - if( _job->requiresProcessing() ) - { - // update job state - _job->queue(); - // actually queue the job via atomic operations - s_jobQueue.items[s_jobQueue.queueSize.fetchAndAddOrdered(1)] = _job; - } -} - - - - -void MixerWorkerThread::startJobs() -{ - // TODO: this is dirty! - engine::getMixer()->m_queueReadyWaitCond.wakeAll(); -} - - - - -void MixerWorkerThread::waitForJobs() -{ - // TODO: this is dirty! - mixer * m = engine::getMixer(); - m->m_workers[m->m_numWorkers]->processJobQueue(); - - while( (int) s_jobQueue.itemsDone < (int) s_jobQueue.queueSize ) - { -#if defined(LMMS_HOST_X86) || defined(LMMS_HOST_X86_64) - asm( "pause" ); -#endif - } + queueReadyWaitCond->wakeAll(); + // The last worker-thread is never started. Instead it's processed "inline" + // i.e. within the global Mixer thread. This way we can reduce latencies + // that otherwise would be caused by synchronizing with another thread. + globalJobQueue.run( workerThreads.last()->m_workingBuf ); + globalJobQueue.wait(); } @@ -139,8 +158,8 @@ void MixerWorkerThread::run() while( m_quit == false ) { m.lock(); - m_queueReadyWaitCond->wait( &m ); - processJobQueue(); + queueReadyWaitCond->wait( &m ); + globalJobQueue.run( m_workingBuf ); m.unlock(); } } diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index dab8ee496..ac6663930 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -72,7 +72,6 @@ mixer::mixer() : m_cpuLoad( 0 ), m_workers(), m_numWorkers( QThread::idealThreadCount()-1 ), - m_queueReadyWaitCond(), m_qualitySettings( qualitySettings::Mode_Draft ), m_masterGain( 1.0f ), m_audioDev( NULL ), @@ -130,7 +129,7 @@ mixer::mixer() : for( int i = 0; i < m_numWorkers+1; ++i ) { - MixerWorkerThread * wt = new MixerWorkerThread( i, this ); + MixerWorkerThread * wt = new MixerWorkerThread( this ); if( i < m_numWorkers ) { wt->start( QThread::TimeCriticalPriority ); @@ -148,14 +147,11 @@ mixer::mixer() : mixer::~mixer() { - // distribute an empty job-queue so that worker-threads - // get out of their processing-loop - MixerWorkerThread::s_jobQueue.queueSize = 0; for( int w = 0; w < m_numWorkers; ++w ) { m_workers[w]->quit(); } - MixerWorkerThread::startJobs(); + MixerWorkerThread::startAndWaitForJobs(); for( int w = 0; w < m_numWorkers; ++w ) { m_workers[w]->wait( 500 ); From aff378983408481cdbf2f6234a8027a24102b625 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 26 Oct 2009 00:00:37 +0100 Subject: [PATCH 24/25] Cpu: added BufMixCoeff + SSE implementation Added another buffer operation BufMixCoeff allowing to mix a certain buffer to another at a given amount (coeff). CpuX86 has been extended by an according SSE implementation. --- include/Cpu.h | 4 ++++ src/core/Cpu.cpp | 22 ++++++++++++++++++++++ src/core/CpuX86.c | 23 +++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/include/Cpu.h b/include/Cpu.h index f4e956690..b4ad59ba0 100644 --- a/include/Cpu.h +++ b/include/Cpu.h @@ -56,6 +56,9 @@ typedef void (*BufApplyGainFunc)( sampleFrameA * RP _dst, typedef void (*BufMixFunc)( sampleFrameA * RP _dst, const sampleFrameA * RP _src, int _frames ); +typedef void (*BufMixCoeffFunc)( sampleFrameA * RP _dst, + const sampleFrameA * RP _src, + float _coeff, int _frames ); typedef void (*BufMixLRCoeffFunc)( sampleFrameA * RP _dst, const sampleFrameA * RP _src, float _left, float _right, @@ -81,6 +84,7 @@ extern MemCpyFunc memCpy; extern MemClearFunc memClear; extern BufApplyGainFunc bufApplyGain; extern BufMixFunc bufMix; +extern BufMixCoeffFunc bufMixCoeff; extern BufMixLRCoeffFunc bufMixLRCoeff; extern UnalignedBufMixLRCoeffFunc unalignedBufMixLRCoeff; extern BufWetDryMixFunc bufWetDryMix; diff --git a/src/core/Cpu.cpp b/src/core/Cpu.cpp index 7e72e5a8f..79f6ab539 100644 --- a/src/core/Cpu.cpp +++ b/src/core/Cpu.cpp @@ -179,6 +179,25 @@ void bufMixNoOpt( sampleFrameA * RP _dst, const sampleFrameA * RP _src, +void bufMixCoeffNoOpt( sampleFrameA * RP _dst, const sampleFrameA * RP _src, + float _coeff, int _frames ) +{ + for( int i = 0; i < _frames; ) + { + _dst[i+0][0] += _src[i+0][0]*_coeff; + _dst[i+0][1] += _src[i+0][1]*_coeff; + _dst[i+1][0] += _src[i+1][0]*_coeff; + _dst[i+1][1] += _src[i+1][1]*_coeff; + _dst[i+2][0] += _src[i+2][0]*_coeff; + _dst[i+2][1] += _src[i+2][1]*_coeff; + _dst[i+3][0] += _src[i+3][0]*_coeff; + _dst[i+3][1] += _src[i+3][1]*_coeff; + i += 4; + } +} + + + void bufMixLRCoeffNoOpt( sampleFrameA * RP _dst, const sampleFrameA * RP _src, float _left, float _right, int _frames ) @@ -306,6 +325,7 @@ MemCpyFunc memCpy = memCpyNoOpt; MemClearFunc memClear = memClearNoOpt; BufApplyGainFunc bufApplyGain = bufApplyGainNoOpt; BufMixFunc bufMix = bufMixNoOpt; +BufMixCoeffFunc bufMixCoeff = bufMixCoeffNoOpt; BufMixLRCoeffFunc bufMixLRCoeff = bufMixLRCoeffNoOpt; UnalignedBufMixLRCoeffFunc unalignedBufMixLRCoeff = unalignedBufMixLRCoeffNoOpt; BufWetDryMixFunc bufWetDryMix = bufWetDryMixNoOpt; @@ -337,6 +357,7 @@ void memCpySSE( void * RP _dst, const void * RP _src, int _size ); void memClearSSE( void * RP _dst, int _size ); void bufApplyGainSSE( sampleFrameA * RP _dst, float _gain, int _frames ); void bufMixSSE( sampleFrameA * RP _dst, const sampleFrameA * RP _src, int _frames ); +void bufMixCoeffSSE( sampleFrameA * RP _dst, const sampleFrameA * RP _src, float _coeff, int _frames ); void bufMixLRCoeffSSE( sampleFrameA * RP _dst, const sampleFrameA * RP _src, float _left, float _right, int _frames ); void unalignedBufMixLRCoeffSSE( sampleFrame * RP _dst, const sampleFrame * RP _src, const float _left, const float _right, int _frames ); void bufWetDryMixSSE( sampleFrameA * RP _dst, const sampleFrameA * RP _src, float _wet, float _dry, int _frames ); @@ -447,6 +468,7 @@ void init() memClear = memClearSSE; bufApplyGain = bufApplyGainSSE; bufMix = bufMixSSE; + bufMixCoeff = bufMixCoeffSSE; bufMixLRCoeff = bufMixLRCoeffSSE; unalignedBufMixLRCoeff = unalignedBufMixLRCoeffSSE; bufWetDryMix = bufWetDryMixSSE; diff --git a/src/core/CpuX86.c b/src/core/CpuX86.c index 4c5e7217c..6c9c5a1c0 100644 --- a/src/core/CpuX86.c +++ b/src/core/CpuX86.c @@ -229,6 +229,29 @@ void bufMixSSE( sampleFrameA * RP _dst, const sampleFrameA * RP _src, } +void bufMixCoeffSSE( sampleFrameA * RP _dst, const sampleFrameA * RP _src, + float _coeff, int _frames ) +{ + int i; + + PREFETCH_READ(_src); + PREFETCH_WRITE(_dst); + + for( i = 0; i < _frames; ) + { + _dst[i+0][0] += _src[i+0][0]*_coeff; + _dst[i+0][1] += _src[i+0][1]*_coeff; + _dst[i+1][0] += _src[i+1][0]*_coeff; + _dst[i+1][1] += _src[i+1][1]*_coeff; + _dst[i+2][0] += _src[i+2][0]*_coeff; + _dst[i+2][1] += _src[i+2][1]*_coeff; + _dst[i+3][0] += _src[i+3][0]*_coeff; + _dst[i+3][1] += _src[i+3][1]*_coeff; + i += 4; + } +} + + void bufMixLRCoeffSSE( sampleFrameA * RP _dst, const sampleFrameA * RP _src, float _left, float _right, int _frames ) From 2262c0097322fc1eb5e2e6f9d7201f21a7a7005f Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 26 Oct 2009 00:12:06 +0100 Subject: [PATCH 25/25] FxMixer: use new CPU::bufMixCoeff() When mixing FX channel to another use new CPU::bufMixCoeff() function instead of implementing an own (slower) mixing loop. --- src/core/FxMixer.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 13c7d626a..ce43d7196 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -85,13 +85,8 @@ void FxChannel::doProcessing( sampleFrame * _buf ) // mix it with this one float amt = fxm->channelSendModel(senderIndex, m_channelIndex)->value(); - sampleFrame * ch_buf = sender->m_buffer; - const float v = sender->m_volumeModel.value(); - for( f_cnt_t f = 0; f < fpp; ++f ) - { - _buf[f][0] += ch_buf[f][0] * v * amt; - _buf[f][1] += ch_buf[f][1] * v * amt; - } + CPU::bufMixCoeff( _buf, sender->m_buffer, + sender->m_volumeModel.value() * amt, fpp ); } const float v = m_volumeModel.value();