diff --git a/include/Mixer.h b/include/Mixer.h index 8e57f074e36..d491cecdf5f 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -184,9 +184,9 @@ class EXPORT Mixer : public QObject // audio-port-stuff inline void addAudioPort( AudioPort * _port ) { - lock(); + requestChangeInModel(); m_audioPorts.push_back( _port ); - unlock(); + doneChangeInModel(); } void removeAudioPort( AudioPort * _port ); @@ -274,16 +274,6 @@ class EXPORT Mixer : public QObject // methods needed by other threads to alter knob values, waveforms, etc - void lock() - { - m_globalMutex.lock(); - } - - void unlock() - { - m_globalMutex.unlock(); - } - void lockInputFrames() { m_inputFramesMutex.lock(); @@ -429,7 +419,6 @@ class EXPORT Mixer : public QObject QString m_midiClientName; // mutexes - QMutex m_globalMutex; QMutex m_inputFramesMutex; QMutex m_playHandleMutex; // mutex used only for adding playhandles QMutex m_playHandleRemovalMutex; diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index c210af2ef90..1bae6f8ecb4 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -151,19 +151,16 @@ class EXPORT SampleBuffer : public QObject, public sharedObject void setLoopStartFrame( f_cnt_t _start ) { - QWriteLocker writeLocker(&m_varLock); m_loopStartFrame = _start; } void setLoopEndFrame( f_cnt_t _end ) { - QWriteLocker writeLocker(&m_varLock); m_loopEndFrame = _end; } void setAllPointFrames( f_cnt_t _start, f_cnt_t _end, f_cnt_t _loopstart, f_cnt_t _loopend ) { - QWriteLocker writeLocker(&m_varLock); m_startFrame = _start; m_endFrame = _end; m_loopStartFrame = _loopstart; @@ -202,13 +199,11 @@ class EXPORT SampleBuffer : public QObject, public sharedObject inline void setFrequency( float _freq ) { - QWriteLocker writeLocker(&m_varLock); m_frequency = _freq; } inline void setSampleRate( sample_rate_t _rate ) { - QWriteLocker writeLocker(&m_varLock); m_sampleRate = _rate; } @@ -224,31 +219,37 @@ class EXPORT SampleBuffer : public QObject, public sharedObject QString & toBase64( QString & _dst ) const; - static SampleBuffer * resample( sampleFrame * _data, - const f_cnt_t _frames, - const sample_rate_t _src_sr, + // protect calls from the GUI to this function with dataReadLock() and + // dataUnlock() + SampleBuffer * resample( const sample_rate_t _src_sr, const sample_rate_t _dst_sr ); - static inline SampleBuffer * resample( SampleBuffer * _buf, - const sample_rate_t _src_sr, - const sample_rate_t _dst_sr ) - { - return resample( _buf->m_data, _buf->m_frames, _src_sr, - _dst_sr ); - } - void normalizeSampleRate( const sample_rate_t _src_sr, bool _keep_settings = false ); + // protect calls from the GUI to this function with dataReadLock() and + // dataUnlock(), out of loops for efficiency inline sample_t userWaveSample( const float _sample ) const { - const float frame = _sample * m_frames; - f_cnt_t f1 = static_cast( frame ) % m_frames; + f_cnt_t frames = m_frames; + sampleFrame * data = m_data; + const float frame = _sample * frames; + f_cnt_t f1 = static_cast( frame ) % frames; if( f1 < 0 ) { - f1 += m_frames; + f1 += frames; } - return linearInterpolate( m_data[f1][0], m_data[ (f1 + 1) % m_frames ][0], fraction( frame ) ); + return linearInterpolate( data[f1][0], data[ (f1 + 1) % frames ][0], fraction( frame ) ); + } + + void dataReadLock() + { + m_varLock.lockForRead(); + } + + void dataUnlock() + { + m_varLock.unlock(); } static QString tryToMakeRelative( const QString & _file ); diff --git a/include/SampleRecordHandle.h b/include/SampleRecordHandle.h index 76bfabf181e..b2ead8ae834 100644 --- a/include/SampleRecordHandle.h +++ b/include/SampleRecordHandle.h @@ -31,9 +31,9 @@ #include "MidiTime.h" #include "PlayHandle.h" -#include "SampleBuffer.h" class BBTrack; +class SampleBuffer; class SampleTCO; class Track; diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index 999e21b18ba..d9f2e0c5a3b 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -43,6 +43,7 @@ #include "Mixer.h" #include "NotePlayHandle.h" #include "Knob.h" +#include "SampleBuffer.h" #include "Song.h" #include "ConfigManager.h" #include "endian_handling.h" diff --git a/plugins/GigPlayer/GigPlayer.h b/plugins/GigPlayer/GigPlayer.h index e637fcd6ac4..08a3b184cf5 100644 --- a/plugins/GigPlayer/GigPlayer.h +++ b/plugins/GigPlayer/GigPlayer.h @@ -30,6 +30,7 @@ #include #include #include +#include #include "Instrument.h" #include "PixmapButton.h" @@ -37,7 +38,6 @@ #include "Knob.h" #include "LcdSpinBox.h" #include "LedCheckbox.h" -#include "SampleBuffer.h" #include "MemoryManager.h" #include "gig.h" diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index 86e732face9..a8854502fe1 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -36,6 +36,7 @@ #include "Mixer.h" #include "NotePlayHandle.h" #include "Knob.h" +#include "SampleBuffer.h" #include "Song.h" #include "patches_dialog.h" diff --git a/plugins/sf2_player/sf2_player.h b/plugins/sf2_player/sf2_player.h index 2a6995c01d6..8996b3e14d4 100644 --- a/plugins/sf2_player/sf2_player.h +++ b/plugins/sf2_player/sf2_player.h @@ -28,6 +28,7 @@ #define SF2_PLAYER_H #include +#include #include "Instrument.h" #include "PixmapButton.h" @@ -36,7 +37,6 @@ #include "LcdSpinBox.h" #include "LedCheckbox.h" #include "fluidsynth.h" -#include "SampleBuffer.h" #include "MemoryManager.h" class sf2InstrumentView; diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 7725d71dc83..856739f1498 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -644,7 +644,7 @@ void VestigeInstrumentView::openPlugin() { return; } - Engine::mixer()->lock(); + Engine::mixer()->requestChangeInModel(); if (m_vi->p_subWindow != NULL) { delete m_vi->p_subWindow; @@ -652,7 +652,7 @@ void VestigeInstrumentView::openPlugin() } m_vi->loadFile( ofd.selectedFiles()[0] ); - Engine::mixer()->unlock(); + Engine::mixer()->doneChangeInModel(); if( m_vi->m_plugin && m_vi->m_plugin->pluginWidget() ) { m_vi->m_plugin->pluginWidget()->setWindowIcon( diff --git a/src/core/EffectChain.cpp b/src/core/EffectChain.cpp index f06cc04aa90..d42ed1c6dc0 100644 --- a/src/core/EffectChain.cpp +++ b/src/core/EffectChain.cpp @@ -122,9 +122,9 @@ void EffectChain::loadSettings( const QDomElement & _this ) void EffectChain::appendEffect( Effect * _effect ) { - Engine::mixer()->lock(); + Engine::mixer()->requestChangeInModel(); m_effects.append( _effect ); - Engine::mixer()->unlock(); + Engine::mixer()->doneChangeInModel(); emit dataChanged(); } @@ -134,21 +134,18 @@ void EffectChain::appendEffect( Effect * _effect ) void EffectChain::removeEffect( Effect * _effect ) { - Engine::mixer()->lock(); + Engine::mixer()->requestChangeInModel(); Effect ** found = qFind( m_effects.begin(), m_effects.end(), _effect ); if( found == m_effects.end() ) { - goto fail; + Engine::mixer()->doneChangeInModel(); + return; } m_effects.erase( found ); - Engine::mixer()->unlock(); + Engine::mixer()->doneChangeInModel(); emit dataChanged(); - return; - -fail: - Engine::mixer()->unlock(); } @@ -252,7 +249,7 @@ void EffectChain::clear() { emit aboutToClear(); - Engine::mixer()->lock(); + Engine::mixer()->requestChangeInModel(); m_enabledModel.setValue( false ); for( int i = 0; i < m_effects.count(); ++i ) @@ -261,5 +258,5 @@ void EffectChain::clear() } m_effects.clear(); - Engine::mixer()->unlock(); + Engine::mixer()->doneChangeInModel(); } diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 95fcc424aa9..f1d8abd120e 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -285,8 +285,8 @@ void FxMixer::toggledSolo() void FxMixer::deleteChannel( int index ) { - // lock the mixer so channel deletion is performed between mixer rounds - Engine::mixer()->lock(); + // channel deletion is performed between mixer rounds + Engine::mixer()->requestChangeInModel(); FxChannel * ch = m_fxChannels[index]; @@ -347,7 +347,7 @@ void FxMixer::deleteChannel( int index ) } } - Engine::mixer()->unlock(); + Engine::mixer()->doneChangeInModel(); } diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 8789d27e906..dac521f5c4e 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -75,7 +75,6 @@ Mixer::Mixer( bool renderOnly ) : m_audioDev( NULL ), m_oldAudioDev( NULL ), m_audioDevStartFailed( false ), - m_globalMutex( QMutex::Recursive ), m_profiler(), m_metronomeActive(false), m_changesSignal( false ), @@ -386,10 +385,6 @@ const surroundSampleFrame * Mixer::renderNextBuffer() } unlockPlayHandleRemoval(); - // now we have to make sure no other thread does anything bad - // while we're acting... - lock(); - // rotate buffers m_writeBuffer = ( m_writeBuffer + 1 ) % m_poolDepth; m_readBuffer = ( m_readBuffer + 1 ) % m_poolDepth; @@ -453,8 +448,6 @@ const surroundSampleFrame * Mixer::renderNextBuffer() // STAGE 3: do master mix in FX mixer fxMixer->masterMix( m_writeBuf ); - unlock(); - emit nextAudioBuffer( m_readBuf ); @@ -481,7 +474,6 @@ const surroundSampleFrame * Mixer::renderNextBuffer() void Mixer::clear() { // TODO: m_midiClient->noteOffAll(); - lock(); lockPlayHandleRemoval(); for( PlayHandleList::Iterator it = m_playHandles.begin(); it != m_playHandles.end(); ++it ) { @@ -493,7 +485,6 @@ void Mixer::clear() } } unlockPlayHandleRemoval(); - unlock(); } @@ -620,9 +611,7 @@ void Mixer::removeAudioPort( AudioPort * _port ) _port ); if( it != m_audioPorts.end() ) { - lock(); m_audioPorts.erase( it ); - unlock(); } } @@ -650,7 +639,7 @@ bool Mixer::addPlayHandle( PlayHandle* handle ) void Mixer::removePlayHandle( PlayHandle * _ph ) { - lockPlayHandleRemoval(); + requestChangeInModel(); // check thread affinity as we must not delete play-handles // which were created in a thread different than mixer thread if( _ph->affinityMatters() && @@ -694,7 +683,7 @@ void Mixer::removePlayHandle( PlayHandle * _ph ) { m_playHandlesToRemove.push_back( _ph ); } - unlockPlayHandleRemoval(); + doneChangeInModel(); } @@ -702,7 +691,7 @@ void Mixer::removePlayHandle( PlayHandle * _ph ) void Mixer::removePlayHandlesOfTypes( Track * _track, const quint8 types ) { - lockPlayHandleRemoval(); + requestChangeInModel(); PlayHandleList::Iterator it = m_playHandles.begin(); while( it != m_playHandles.end() ) { @@ -721,7 +710,7 @@ void Mixer::removePlayHandlesOfTypes( Track * _track, const quint8 types ) ++it; } } - unlockPlayHandleRemoval(); + doneChangeInModel(); } @@ -729,18 +718,13 @@ void Mixer::removePlayHandlesOfTypes( Track * _track, const quint8 types ) bool Mixer::hasNotePlayHandles() { - lock(); - for( PlayHandleList::Iterator it = m_playHandles.begin(); it != m_playHandles.end(); ++it ) { if( (*it)->type() == PlayHandle::TypeNotePlayHandle ) { - unlock(); return true; } } - - unlock(); return false; } diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 12103c2776e..9f83f449cf7 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -170,6 +170,7 @@ void SampleBuffer::update( bool _keep_settings ) const bool lock = ( m_data != NULL ); if( lock ) { + Engine::mixer()->requestChangeInModel(); m_varLock.lockForWrite(); MM_FREE( m_data ); } @@ -270,6 +271,7 @@ void SampleBuffer::update( bool _keep_settings ) if( lock ) { m_varLock.unlock(); + Engine::mixer()->doneChangeInModel(); } emit sampleUpdated(); @@ -354,7 +356,7 @@ void SampleBuffer::normalizeSampleRate( const sample_rate_t _src_sr, // do samplerate-conversion to our default-samplerate if( _src_sr != Engine::mixer()->baseSampleRate() ) { - SampleBuffer * resampled = resample( this, _src_sr, + SampleBuffer * resampled = resample( _src_sr, Engine::mixer()->baseSampleRate() ); MM_FREE( m_data ); m_frames = resampled->frames(); @@ -597,8 +599,6 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, const float _freq, const LoopMode _loopmode ) { - QReadLocker readLocker(&m_varLock); - f_cnt_t startFrame = m_startFrame; f_cnt_t endFrame = m_endFrame; f_cnt_t loopStartFrame = m_loopStartFrame; @@ -1147,12 +1147,12 @@ QString & SampleBuffer::toBase64( QString & _dst ) const -SampleBuffer * SampleBuffer::resample( sampleFrame * _data, - const f_cnt_t _frames, - const sample_rate_t _src_sr, +SampleBuffer * SampleBuffer::resample( const sample_rate_t _src_sr, const sample_rate_t _dst_sr ) { - const f_cnt_t dst_frames = static_cast( _frames / + sampleFrame * data = m_data; + const f_cnt_t frames = m_frames; + const f_cnt_t dst_frames = static_cast( frames / (float) _src_sr * (float) _dst_sr ); SampleBuffer * dst_sb = new SampleBuffer( dst_frames ); sampleFrame * dst_buf = dst_sb->m_origData; @@ -1165,9 +1165,9 @@ SampleBuffer * SampleBuffer::resample( sampleFrame * _data, { SRC_DATA src_data; src_data.end_of_input = 1; - src_data.data_in = _data[0]; + src_data.data_in = data[0]; src_data.data_out = dst_buf[0]; - src_data.input_frames = _frames; + src_data.input_frames = frames; src_data.output_frames = dst_frames; src_data.src_ratio = (double) _dst_sr / _src_sr; if( ( error = src_process( state, &src_data ) ) ) @@ -1355,7 +1355,6 @@ void SampleBuffer::loadFromBase64( const QString & _data ) void SampleBuffer::setStartFrame( const f_cnt_t _s ) { - QWriteLocker writeLocker(&m_varLock); m_startFrame = _s; } @@ -1364,7 +1363,6 @@ void SampleBuffer::setStartFrame( const f_cnt_t _s ) void SampleBuffer::setEndFrame( const f_cnt_t _e ) { - QWriteLocker writeLocker(&m_varLock); m_endFrame = _e; } diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 56d637e0b8f..b471e0939bc 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -790,7 +790,7 @@ void Song::clearProject() } - Engine::mixer()->lock(); + Engine::mixer()->requestChangeInModel(); if( gui && gui->getBBEditor() ) { @@ -831,7 +831,7 @@ void Song::clearProject() AutomationPattern::globalAutomationPattern( &m_masterPitchModel )-> clear(); - Engine::mixer()->unlock(); + Engine::mixer()->doneChangeInModel(); if( gui && gui->getProjectNotes() ) { @@ -963,7 +963,7 @@ void Song::loadProject( const QString & fileName ) DataFile::LocaleHelper localeHelper( DataFile::LocaleHelper::ModeLoad ); - Engine::mixer()->lock(); + Engine::mixer()->requestChangeInModel(); // get the header information from the DOM m_tempoModel.loadSettings( dataFile.head(), "bpm" ); @@ -1050,7 +1050,7 @@ void Song::loadProject( const QString & fileName ) AutomationPattern::resolveAllIDs(); - Engine::mixer()->unlock(); + Engine::mixer()->doneChangeInModel(); ConfigManager::inst()->addRecentlyOpenedProject( fileName ); diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index e4ebabbae92..d3eb22132be 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -534,7 +534,7 @@ void FileBrowserTreeWidget::mouseReleaseEvent(QMouseEvent * me ) void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it ) { - Engine::mixer()->lock(); + Engine::mixer()->requestChangeInModel(); switch( f->handling() ) { case FileItem::LoadAsProject: @@ -582,7 +582,7 @@ void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it ) break; } - Engine::mixer()->unlock(); + Engine::mixer()->doneChangeInModel(); } @@ -604,12 +604,10 @@ void FileBrowserTreeWidget::activateListItem(QTreeWidgetItem * item, } else if( f->handling() != FileItem::NotSupported ) { -// engine::mixer()->lock(); InstrumentTrack * it = dynamic_cast( Track::create( Track::InstrumentTrack, Engine::getBBTrackContainer() ) ); handleFile( f, it ); -// engine::mixer()->unlock(); } } @@ -621,11 +619,9 @@ void FileBrowserTreeWidget::openInNewInstrumentTrack( TrackContainer* tc ) if( m_contextMenuItem->handling() == FileItem::LoadAsPreset || m_contextMenuItem->handling() == FileItem::LoadByPlugin ) { -// engine::mixer()->lock(); InstrumentTrack * it = dynamic_cast( Track::create( Track::InstrumentTrack, tc ) ); handleFile( m_contextMenuItem, it ); -// engine::mixer()->unlock(); } } diff --git a/src/gui/widgets/EnvelopeAndLfoView.cpp b/src/gui/widgets/EnvelopeAndLfoView.cpp index 5c364c19bd2..c6598d4f5b3 100644 --- a/src/gui/widgets/EnvelopeAndLfoView.cpp +++ b/src/gui/widgets/EnvelopeAndLfoView.cpp @@ -508,6 +508,8 @@ void EnvelopeAndLfoView::paintEvent( QPaintEvent * ) osc_frames *= 100.0f; } + // userWaveSample() may be used, called out of loop for efficiency + m_params->m_userWave.dataReadLock(); float old_y = 0; for( int x = 0; x <= LFO_GRAPH_W; ++x ) { @@ -558,6 +560,7 @@ void EnvelopeAndLfoView::paintEvent( QPaintEvent * ) graph_y_base + cur_y ) ); old_y = cur_y; } + m_params->m_userWave.dataUnlock(); p.setPen( QColor( 201, 201, 225 ) ); int ms_per_osc = static_cast( SECS_PER_LFO_OSCILLATION * diff --git a/src/gui/widgets/Graph.cpp b/src/gui/widgets/Graph.cpp index 3fea035cde1..ead7ee86c59 100644 --- a/src/gui/widgets/Graph.cpp +++ b/src/gui/widgets/Graph.cpp @@ -585,11 +585,13 @@ QString graphModel::setWaveToUser() QString fileName = sampleBuffer->openAndSetWaveformFile(); if( fileName.isEmpty() == false ) { + sampleBuffer->dataReadLock(); for( int i = 0; i < length(); i++ ) { m_samples[i] = sampleBuffer->userWaveSample( i / static_cast( length() ) ); } + sampleBuffer->dataUnlock(); } sharedObject::unref( sampleBuffer );