diff --git a/include/AudioPort.h b/include/AudioPort.h index 8d70f8306..0971878d4 100644 --- a/include/AudioPort.h +++ b/include/AudioPort.h @@ -31,6 +31,7 @@ #include "Mixer.h" #include "MemoryManager.h" +#include "PlayHandle.h" class EffectChain; @@ -41,38 +42,21 @@ public: AudioPort( const QString & _name, bool _has_effect_chain = true ); virtual ~AudioPort(); - inline sampleFrame * firstBuffer() + inline sampleFrame * buffer() { - return m_firstBuffer; + return m_portBuffer; } - inline sampleFrame * secondBuffer() + inline void lockBuffer() { - return m_secondBuffer; + m_portBufferLock.lock(); } - inline void lockFirstBuffer() + inline void unlockBuffer() { - m_firstBufferLock.lock(); + m_portBufferLock.unlock(); } - inline void lockSecondBuffer() - { - m_secondBufferLock.lock(); - } - - inline void unlockFirstBuffer() - { - m_firstBufferLock.unlock(); - } - - inline void unlockSecondBuffer() - { - m_secondBufferLock.unlock(); - } - - void nextPeriod(); - // indicate whether JACK & Co should provide output-buffer at ext. port inline bool extOutputEnabled() const @@ -112,28 +96,20 @@ public: bool processEffects(); // ThreadableJob stuff - virtual void doProcessing( sampleFrame * ); + virtual void doProcessing(); virtual bool requiresProcessing() const { return true; } - - enum bufferUsages - { - NoUsage, - FirstBuffer, - BothBuffers - } ; - + void addPlayHandle( PlayHandle * handle ); + void removePlayHandle( PlayHandle * handle ); private: - volatile bufferUsages m_bufferUsage; + volatile bool m_bufferUsage; - sampleFrame * m_firstBuffer; - sampleFrame * m_secondBuffer; - QMutex m_firstBufferLock; - QMutex m_secondBufferLock; + sampleFrame * m_portBuffer; + QMutex m_portBufferLock; bool m_extOutputEnabled; fx_ch_t m_nextFxChannel; @@ -142,6 +118,8 @@ private: EffectChain * m_effects; + PlayHandleList m_playHandles; + QMutex m_playHandleLock; friend class Mixer; friend class MixerWorkerThread; diff --git a/include/BufferManager.h b/include/BufferManager.h index f2905b194..f605e38b8 100644 --- a/include/BufferManager.h +++ b/include/BufferManager.h @@ -34,8 +34,8 @@ #include -const int BM_INITIAL_BUFFERS = 256; -const int BM_INCREMENT = 16; +const int BM_INITIAL_BUFFERS = 512; +//const int BM_INCREMENT = 64; class BufferManager { @@ -43,12 +43,16 @@ public: static void init(); static sampleFrame * acquire(); static void release( sampleFrame * buf ); - static void extend( int c ); + static void refresh(); +// static void extend( int c ); private: static sampleFrame ** s_available; static QAtomicInt s_availableIndex; - static QReadWriteLock s_mutex; + + static sampleFrame ** s_released; + static QAtomicInt s_releasedIndex; +// static QReadWriteLock s_mutex; static int s_size; }; diff --git a/include/FxMixer.h b/include/FxMixer.h index be1d2486e..25c1a8c06 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -76,7 +76,7 @@ class FxChannel : public ThreadableJob void processed(); private: - virtual void doProcessing( sampleFrame * _working_buffer ); + virtual void doProcessing(); }; diff --git a/include/InstrumentPlayHandle.h b/include/InstrumentPlayHandle.h index 331695a7e..1b4d3d12a 100644 --- a/include/InstrumentPlayHandle.h +++ b/include/InstrumentPlayHandle.h @@ -33,11 +33,7 @@ class InstrumentPlayHandle : public PlayHandle { public: - InstrumentPlayHandle( Instrument* instrument ) : - PlayHandle( TypeInstrumentPlayHandle ), - m_instrument( instrument ) - { - } + InstrumentPlayHandle( Instrument * instrument, InstrumentTrack* instrumentTrack ); virtual ~InstrumentPlayHandle() { @@ -88,6 +84,7 @@ public: private: Instrument* m_instrument; + InstrumentTrack * m_instrumentTrack; } ; diff --git a/include/Mixer.h b/include/Mixer.h index d9de461eb..a17e7e7ed 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -321,11 +321,6 @@ public: } // audio-buffer-mgm - void bufferToPort( const sampleFrame * _buf, - const fpp_t _frames, - stereoVolumeVector _volume_vector, - AudioPort * _port ); - static void clearAudioBuffer( sampleFrame * _ab, const f_cnt_t _frames, const f_cnt_t _offset = 0 ); diff --git a/include/MixerWorkerThread.h b/include/MixerWorkerThread.h index 51da6a91e..5f5c18c84 100644 --- a/include/MixerWorkerThread.h +++ b/include/MixerWorkerThread.h @@ -63,7 +63,7 @@ public: void addJob( ThreadableJob * _job ); - void run( sampleFrame * _buffer ); + void run(); void wait(); private: @@ -115,7 +115,6 @@ private: static QWaitCondition * queueReadyWaitCond; static QList workerThreads; - sampleFrame * m_workingBuf; volatile bool m_quit; } ; diff --git a/include/PlayHandle.h b/include/PlayHandle.h index 22849c9d0..51130d21a 100644 --- a/include/PlayHandle.h +++ b/include/PlayHandle.h @@ -33,7 +33,7 @@ #include "lmms_basics.h" class track; - +class AudioPort; class PlayHandle : public ThreadableJob { @@ -48,12 +48,7 @@ public: } ; typedef Types Type; - PlayHandle( const Type type, f_cnt_t offset = 0 ) : - m_type( type ), - m_offset( offset ), - m_affinity( QThread::currentThread() ) - { - } + PlayHandle( const Type type, f_cnt_t offset = 0 ); PlayHandle & operator = ( PlayHandle & p ) { @@ -63,9 +58,7 @@ public: return *this; } - virtual ~PlayHandle() - { - } + virtual ~PlayHandle(); virtual bool affinityMatters() const { @@ -83,10 +76,7 @@ public: } // required for ThreadableJob - virtual void doProcessing( sampleFrame* buffer ) - { - play( buffer ); - } + virtual void doProcessing(); virtual bool requiresProcessing() const { @@ -106,7 +96,7 @@ public: return m_processingLock.tryLock(); } virtual void play( sampleFrame* buffer ) = 0; - virtual bool isFinished( void ) const = 0; + virtual bool isFinished() const = 0; // returns the frameoffset at the start of the playhandle, // ie. how many empty frames should be inserted at the start of the first period @@ -123,12 +113,41 @@ public: virtual bool isFromTrack( const track * _track ) const = 0; + bool usesBuffer() const + { + return m_usesBuffer; + } + + void setUsesBuffer( const bool b ) + { + m_usesBuffer = b; + } + + AudioPort * audioPort() + { + return m_audioPort; + } + + void setAudioPort( AudioPort * port ) + { + m_audioPort = port; + } + + void releaseBuffer(); + + sampleFrame * buffer() + { + return m_playHandleBuffer; + } private: Type m_type; f_cnt_t m_offset; QThread* m_affinity; QMutex m_processingLock; + sampleFrame * m_playHandleBuffer; + bool m_usesBuffer; + AudioPort * m_audioPort; } ; diff --git a/include/PresetPreviewPlayHandle.h b/include/PresetPreviewPlayHandle.h index 673fa1f5f..731a830cf 100644 --- a/include/PresetPreviewPlayHandle.h +++ b/include/PresetPreviewPlayHandle.h @@ -23,8 +23,8 @@ * */ -#ifndef _PRESET_PREVIEW_PLAY_HANDLE_H -#define _PRESET_PREVIEW_PLAY_HANDLE_H +#ifndef PRESET_PREVIEW_PLAY_HANDLE_H +#define PRESET_PREVIEW_PLAY_HANDLE_H #include "NotePlayHandle.h" diff --git a/include/SamplePlayHandle.h b/include/SamplePlayHandle.h index 61bee7f50..9721f04b4 100644 --- a/include/SamplePlayHandle.h +++ b/include/SamplePlayHandle.h @@ -82,7 +82,6 @@ private: f_cnt_t m_frame; SampleBuffer::handleState m_state; - AudioPort * m_audioPort; const bool m_ownAudioPort; FloatModel m_defaultVolumeModel; diff --git a/include/ThreadableJob.h b/include/ThreadableJob.h index c3da4c547..f116a9324 100644 --- a/include/ThreadableJob.h +++ b/include/ThreadableJob.h @@ -22,8 +22,8 @@ * */ -#ifndef _THREADABLE_JOB_H -#define _THREADABLE_JOB_H +#ifndef THREADABLE_JOB_H +#define THREADABLE_JOB_H #include @@ -67,11 +67,11 @@ public: m_state = Done; } - void process( sampleFrame* workingBuffer = NULL ) + void process() { if( m_state.testAndSetOrdered( Queued, InProgress ) ) { - doProcessing( workingBuffer ); + doProcessing(); m_state = Done; } } @@ -80,7 +80,7 @@ public: protected: - virtual void doProcessing( sampleFrame* workingBuffer) = 0; + virtual void doProcessing() = 0; QAtomicInt m_state; diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index b5e89499d..306273b11 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -353,7 +353,7 @@ lb302Synth::lb302Synth( InstrumentTrack * _instrumentTrack ) : filterChanged(); - InstrumentPlayHandle * iph = new InstrumentPlayHandle( this ); + InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrumentTrack ); engine::mixer()->addPlayHandle( iph ); } diff --git a/plugins/opl2/opl2instrument.cpp b/plugins/opl2/opl2instrument.cpp index 786fe2483..4f1e810ef 100644 --- a/plugins/opl2/opl2instrument.cpp +++ b/plugins/opl2/opl2instrument.cpp @@ -138,7 +138,7 @@ opl2instrument::opl2instrument( InstrumentTrack * _instrument_track ) : trem_depth_mdl(false, this, tr( "Tremolo Depth" ) ) { // Connect the plugin to the mixer... - InstrumentPlayHandle * iph = new InstrumentPlayHandle( this ); + InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrument_track ); engine::mixer()->addPlayHandle( iph ); // Voices are laid out in a funny way... diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index e6264cc26..b6aefc123 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -118,7 +118,7 @@ sf2Instrument::sf2Instrument( InstrumentTrack * _instrument_track ) : // everytime we load a new soundfont. m_synth = new_fluid_synth( m_settings ); - InstrumentPlayHandle * iph = new InstrumentPlayHandle( this ); + InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrument_track ); engine::mixer()->addPlayHandle( iph ); loadFile( ConfigManager::inst()->defaultSoundfont() ); diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index f59a2906b..7a165d6ac 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -82,7 +82,7 @@ vestigeInstrument::vestigeInstrument( InstrumentTrack * _instrument_track ) : p_subWindow( NULL ) { // now we need a play-handle which cares for calling play() - InstrumentPlayHandle * iph = new InstrumentPlayHandle( this ); + InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrument_track ); engine::mixer()->addPlayHandle( iph ); } diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index 00d3277df..91b8f43b5 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -129,7 +129,7 @@ ZynAddSubFxInstrument::ZynAddSubFxInstrument( connect( &m_resBandwidthModel, SIGNAL( dataChanged() ), this, SLOT( updateResBandwidth() ) ); // now we need a play-handle which cares for calling play() - InstrumentPlayHandle * iph = new InstrumentPlayHandle( this ); + InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrumentTrack ); engine::mixer()->addPlayHandle( iph ); connect( engine::mixer(), SIGNAL( sampleRateChanged() ), diff --git a/src/core/BufferManager.cpp b/src/core/BufferManager.cpp index 1646caf81..f5925acfc 100644 --- a/src/core/BufferManager.cpp +++ b/src/core/BufferManager.cpp @@ -28,13 +28,16 @@ sampleFrame ** BufferManager::s_available; QAtomicInt BufferManager::s_availableIndex = 0; -QReadWriteLock BufferManager::s_mutex; +sampleFrame ** BufferManager::s_released; +QAtomicInt BufferManager::s_releasedIndex = 0; +//QReadWriteLock BufferManager::s_mutex; int BufferManager::s_size; void BufferManager::init() { s_available = MM_ALLOC( sampleFrame*, BM_INITIAL_BUFFERS ); + s_released = MM_ALLOC( sampleFrame*, BM_INITIAL_BUFFERS ); int c = engine::mixer()->framesPerPeriod() * BM_INITIAL_BUFFERS; sampleFrame * b = MM_ALLOC( sampleFrame, c ); @@ -53,29 +56,42 @@ sampleFrame * BufferManager::acquire() { if( s_availableIndex < 0 ) { - s_mutex.lockForWrite(); - if( s_availableIndex < 0 ) extend( BM_INCREMENT ); - s_mutex.unlock(); + qFatal( "BufferManager: out of buffers" ); } - s_mutex.lockForRead(); - sampleFrame * b = s_available[ s_availableIndex.fetchAndAddOrdered( -1 ) ]; + int i = s_availableIndex.fetchAndAddOrdered( -1 ); + sampleFrame * b = s_available[ i ]; - //qDebug( "acquired buffer: %p - index %d", b, int(s_availableIndex) ); - s_mutex.unlock(); + //qDebug( "acquired buffer: %p - index %d", b, i ); return b; } void BufferManager::release( sampleFrame * buf ) { - s_mutex.lockForRead(); - s_available[ s_availableIndex.fetchAndAddOrdered( 1 ) + 1 ] = buf; - //qDebug( "released buffer: %p - index %d", buf, int(s_availableIndex) ); - s_mutex.unlock(); + int i = s_releasedIndex.fetchAndAddOrdered( 1 ); + s_released[ i ] = buf; + //qDebug( "released buffer: %p - index %d", buf, i ); } +void BufferManager::refresh() // non-threadsafe, hence it's called periodically from mixer at a time when no other threads can interfere +{ + if( s_releasedIndex == 0 ) return; + //qDebug( "refresh: %d buffers", int( s_releasedIndex ) ); + + int j = s_availableIndex; + for( int i = 0; i < s_releasedIndex; ++i ) + { + ++j; + s_available[ j ] = s_released[ i ]; + } + s_availableIndex = j; + s_releasedIndex = 0; +} + + +/* // non-extensible for now void BufferManager::extend( int c ) { s_size += c; @@ -91,4 +107,4 @@ void BufferManager::extend( int c ) s_available[ s_availableIndex.fetchAndAddOrdered( 1 ) + 1 ] = b; b += engine::mixer()->framesPerPeriod(); } -} +}*/ diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 9c52700d1..fcd1cc959 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -116,19 +116,10 @@ void FxChannel::unmuteForSolo() -void FxChannel::doProcessing( sampleFrame * _buf ) +void FxChannel::doProcessing() { const fpp_t fpp = engine::mixer()->framesPerPeriod(); - // 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_muted == false ) { foreach( FxRoute * senderRoute, m_receives ) @@ -150,21 +141,21 @@ void FxChannel::doProcessing( sampleFrame * _buf ) if( ! volBuf && ! sendBuf ) // neither volume nor send has sample-exact data... { const float v = sender->m_volumeModel.value() * sendModel->value(); - MixHelpers::addMultiplied( _buf, ch_buf, v, fpp ); + MixHelpers::addMultiplied( m_buffer, ch_buf, v, fpp ); } else if( volBuf && sendBuf ) // both volume and send have sample-exact data { - MixHelpers::addMultipliedByBuffers( _buf, ch_buf, volBuf, sendBuf, fpp ); + MixHelpers::addMultipliedByBuffers( m_buffer, ch_buf, volBuf, sendBuf, fpp ); } else if( volBuf ) // volume has sample-exact data but send does not { const float v = sendModel->value(); - MixHelpers::addMultipliedByBuffer( _buf, ch_buf, v, volBuf, fpp ); + MixHelpers::addMultipliedByBuffer( m_buffer, ch_buf, v, volBuf, fpp ); } else // vice versa { const float v = sender->m_volumeModel.value(); - MixHelpers::addMultipliedByBuffer( _buf, ch_buf, v, sendBuf, fpp ); + MixHelpers::addMultipliedByBuffer( m_buffer, ch_buf, v, sendBuf, fpp ); } m_hasInput = true; } diff --git a/src/core/InstrumentPlayHandle.cpp b/src/core/InstrumentPlayHandle.cpp new file mode 100644 index 000000000..11a4fb5b8 --- /dev/null +++ b/src/core/InstrumentPlayHandle.cpp @@ -0,0 +1,35 @@ +/* + * InstrumentPlayHandle.cpp - play-handle for driving an instrument + * + * Copyright (c) 2005-2014 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 "InstrumentPlayHandle.h" +#include "InstrumentTrack.h" + +InstrumentPlayHandle::InstrumentPlayHandle( Instrument * instrument, InstrumentTrack* instrumentTrack ) : + PlayHandle( TypeInstrumentPlayHandle ), + m_instrument( instrument ), + m_instrumentTrack( instrumentTrack ) +{ + setAudioPort( instrumentTrack->audioPort() ); +} diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index e2b80ba42..74abde8c7 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -57,7 +57,7 @@ #include "MidiDummy.h" #include "MemoryHelper.h" - +#include "BufferManager.h" @@ -355,6 +355,7 @@ const surroundSampleFrame * Mixer::renderNextBuffer() if( it != m_playHandles.end() ) { + ( *it )->audioPort()->removePlayHandle( ( *it ) ); if( ( *it )->type() == PlayHandle::TypeNotePlayHandle ) { NotePlayHandleManager::release( (NotePlayHandle*) *it ); @@ -410,6 +411,7 @@ const surroundSampleFrame * Mixer::renderNextBuffer() } if( ( *it )->isFinished() ) { + ( *it )->audioPort()->removePlayHandle( ( *it ) ); if( ( *it )->type() == PlayHandle::TypeNotePlayHandle ) { NotePlayHandleManager::release( (NotePlayHandle*) *it ); @@ -424,7 +426,6 @@ const surroundSampleFrame * Mixer::renderNextBuffer() } unlockPlayHandleRemoval(); - // STAGE 2: process effects of all instrument- and sampletracks MixerWorkerThread::fillJobQueue >( m_audioPorts ); MixerWorkerThread::startAndWaitForJobs(); @@ -442,6 +443,9 @@ const surroundSampleFrame * Mixer::renderNextBuffer() EnvelopeAndLfoParameters::instances()->trigger(); Controller::triggerFrameCounter(); AutomatableModel::incrementPeriodCounter(); + + // refresh buffer pool + BufferManager::refresh(); m_profiler.finishPeriod( processingSampleRate(), m_framesPerPeriod ); @@ -472,45 +476,6 @@ void Mixer::clear() -void Mixer::bufferToPort( const sampleFrame * buf, - const fpp_t frames, - stereoVolumeVector vv, - AudioPort * port ) -{ - const int loop1_frame = qMin( frames, m_framesPerPeriod ); - - port->lockFirstBuffer(); - MixHelpers::addMultipliedStereo( port->firstBuffer(), // dst - buf, // src - vv.vol[0], vv.vol[1], // coeff left/right - loop1_frame ); // frame count - port->unlockFirstBuffer(); - - if( frames > m_framesPerPeriod ) - { - port->lockSecondBuffer(); - - const fpp_t framesLeft = qMin( frames - m_framesPerPeriod, m_framesPerPeriod ); - - MixHelpers::addMultipliedStereo( port->secondBuffer(), // dst - buf + m_framesPerPeriod, // src - vv.vol[0], vv.vol[1], // coeff left/right - framesLeft ); // frame count - - // we used both buffers so set flags - port->m_bufferUsage = AudioPort::BothBuffers; - port->unlockSecondBuffer(); - } - else if( port->m_bufferUsage == AudioPort::NoUsage ) - { - // only first buffer touched - port->m_bufferUsage = AudioPort::FirstBuffer; - } -} - - - - void Mixer::clearAudioBuffer( sampleFrame * _ab, const f_cnt_t _frames, const f_cnt_t _offset ) { @@ -665,7 +630,8 @@ bool Mixer::addPlayHandle( PlayHandle* handle ) if( criticalXRuns() == false ) { m_playHandleMutex.lock(); - m_newPlayHandles.append( handle ); + m_newPlayHandles.append( handle ); + handle->audioPort()->addPlayHandle( handle ); m_playHandleMutex.unlock(); return true; } @@ -688,6 +654,7 @@ void Mixer::removePlayHandle( PlayHandle * _ph ) _ph->affinity() == QThread::currentThread() ) { lockPlayHandleRemoval(); + _ph->audioPort()->removePlayHandle( _ph ); PlayHandleList::Iterator it = qFind( m_playHandles.begin(), m_playHandles.end(), _ph ); @@ -719,6 +686,7 @@ void Mixer::removePlayHandles( track * _track, bool removeIPHs ) { if( ( *it )->isFromTrack( _track ) && ( removeIPHs || ( *it )->type() != PlayHandle::TypeInstrumentPlayHandle ) ) { + ( *it )->audioPort()->removePlayHandle( ( *it ) ); if( ( *it )->type() == PlayHandle::TypeNotePlayHandle ) { NotePlayHandleManager::release( (NotePlayHandle*) *it ); diff --git a/src/core/MixerWorkerThread.cpp b/src/core/MixerWorkerThread.cpp index c82f9e035..9cc65c4c4 100644 --- a/src/core/MixerWorkerThread.cpp +++ b/src/core/MixerWorkerThread.cpp @@ -56,7 +56,7 @@ void MixerWorkerThread::JobQueue::addJob( ThreadableJob * _job ) -void MixerWorkerThread::JobQueue::run( sampleFrame * _buffer ) +void MixerWorkerThread::JobQueue::run() { bool processedJob = true; while( processedJob && (int) m_itemsDone < (int) m_queueSize ) @@ -67,7 +67,7 @@ void MixerWorkerThread::JobQueue::run( sampleFrame * _buffer ) ThreadableJob * job = m_items[i].fetchAndStoreOrdered( NULL ); if( job ) { - job->process( _buffer ); + job->process(); processedJob = true; m_itemsDone.fetchAndAddOrdered( 1 ); } @@ -98,7 +98,6 @@ void MixerWorkerThread::JobQueue::wait() MixerWorkerThread::MixerWorkerThread( Mixer* mixer ) : QThread( mixer ), - m_workingBuf( new sampleFrame[mixer->framesPerPeriod()] ), m_quit( false ) { // initialize global static data @@ -120,8 +119,6 @@ MixerWorkerThread::MixerWorkerThread( Mixer* mixer ) : MixerWorkerThread::~MixerWorkerThread() { - delete[] m_workingBuf; - workerThreads.removeAll( this ); } @@ -143,7 +140,7 @@ void MixerWorkerThread::startAndWaitForJobs() // 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.run(); globalJobQueue.wait(); } @@ -166,7 +163,7 @@ void MixerWorkerThread::run() { m.lock(); queueReadyWaitCond->wait( &m ); - globalJobQueue.run( m_workingBuf ); + globalJobQueue.run(); m.unlock(); } } diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 21e1e93f2..c4f3971a3 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -93,6 +93,8 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack, parent->m_hadChildren = true; m_bbTrack = parent->m_bbTrack; + + parent->setUsesBuffer( false ); } updateFrequency(); @@ -115,6 +117,13 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack, MidiTime::fromFrames( offset(), engine::framesPerTick() ), offset() ); } + + if( m_instrumentTrack->instrument()->flags() & Instrument::IsSingleStreamed ) + { + setUsesBuffer( false ); + } + + setAudioPort( instrumentTrack->audioPort() ); unlock(); } @@ -148,6 +157,9 @@ void NotePlayHandle::done() m_subNotes.clear(); delete m_filter; + + if( buffer() ) releaseBuffer(); + unlock(); } diff --git a/src/core/PlayHandle.cpp b/src/core/PlayHandle.cpp new file mode 100644 index 000000000..b32a19c94 --- /dev/null +++ b/src/core/PlayHandle.cpp @@ -0,0 +1,62 @@ +/* + * PlayHandle.cpp - base class PlayHandle - core of rendering engine + * + * Copyright (c) 2004-2014 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 "PlayHandle.h" +#include "BufferManager.h" + + +PlayHandle::PlayHandle( const Type type, f_cnt_t offset ) : + m_type( type ), + m_offset( offset ), + m_affinity( QThread::currentThread() ), + m_playHandleBuffer( NULL ), + m_usesBuffer( true ) +{ +} + + +PlayHandle::~PlayHandle() +{ +} + + +void PlayHandle::doProcessing() +{ + if( m_usesBuffer ) + { + if( ! m_playHandleBuffer ) m_playHandleBuffer = BufferManager::acquire(); + play( m_playHandleBuffer ); + } + else + { + play( m_playHandleBuffer ); + } +} + + +void PlayHandle::releaseBuffer() +{ + BufferManager::release( m_playHandleBuffer ); + m_playHandleBuffer = NULL; +} diff --git a/src/core/PresetPreviewPlayHandle.cpp b/src/core/PresetPreviewPlayHandle.cpp index 4847b8e97..c6d5c7132 100644 --- a/src/core/PresetPreviewPlayHandle.cpp +++ b/src/core/PresetPreviewPlayHandle.cpp @@ -165,6 +165,7 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, typeInfo::max() / 2, note( 0, 0, DefaultKey, 100 ) ); + setAudioPort( s_previewTC->previewInstrumentTrack()->audioPort() ); s_previewTC->setPreviewNote( m_previewNote ); diff --git a/src/core/SamplePlayHandle.cpp b/src/core/SamplePlayHandle.cpp index 423f88784..188002ed2 100644 --- a/src/core/SamplePlayHandle.cpp +++ b/src/core/SamplePlayHandle.cpp @@ -38,13 +38,13 @@ SamplePlayHandle::SamplePlayHandle( const QString& sampleFile ) : m_sampleBuffer( new SampleBuffer( sampleFile ) ), m_doneMayReturnTrue( true ), m_frame( 0 ), - m_audioPort( new AudioPort( "SamplePlayHandle", false ) ), m_ownAudioPort( true ), m_defaultVolumeModel( DefaultVolume, MinVolume, MaxVolume, 1 ), m_volumeModel( &m_defaultVolumeModel ), m_track( NULL ), m_bbTrack( NULL ) { + setAudioPort( new AudioPort( "SamplePlayHandle", false ) ); } @@ -55,13 +55,13 @@ SamplePlayHandle::SamplePlayHandle( SampleBuffer* sampleBuffer ) : m_sampleBuffer( sharedObject::ref( sampleBuffer ) ), m_doneMayReturnTrue( true ), m_frame( 0 ), - m_audioPort( new AudioPort( "SamplePlayHandle", false ) ), m_ownAudioPort( true ), m_defaultVolumeModel( DefaultVolume, MinVolume, MaxVolume, 1 ), m_volumeModel( &m_defaultVolumeModel ), m_track( NULL ), m_bbTrack( NULL ) { + setAudioPort( new AudioPort( "SamplePlayHandle", false ) ); } @@ -72,13 +72,13 @@ SamplePlayHandle::SamplePlayHandle( SampleTCO* tco ) : m_sampleBuffer( sharedObject::ref( tco->sampleBuffer() ) ), m_doneMayReturnTrue( true ), m_frame( 0 ), - m_audioPort( ( (SampleTrack *)tco->getTrack() )->audioPort() ), m_ownAudioPort( false ), m_defaultVolumeModel( DefaultVolume, MinVolume, MaxVolume, 1 ), m_volumeModel( &m_defaultVolumeModel ), m_track( tco->getTrack() ), m_bbTrack( NULL ) { + setAudioPort( ( (SampleTrack *)tco->getTrack() )->audioPort() ); } @@ -89,7 +89,7 @@ SamplePlayHandle::~SamplePlayHandle() sharedObject::unref( m_sampleBuffer ); if( m_ownAudioPort ) { - delete m_audioPort; + delete audioPort(); } } @@ -119,13 +119,11 @@ void SamplePlayHandle::play( sampleFrame * buffer ) if( !( m_track && m_track->isMuted() ) && !( m_bbTrack && m_bbTrack->isMuted() ) ) { - stereoVolumeVector v = +/* stereoVolumeVector v = { { m_volumeModel->value() / DefaultVolume, - m_volumeModel->value() / DefaultVolume } }; + m_volumeModel->value() / DefaultVolume } };*/ m_sampleBuffer->play( workingBuffer, &m_state, frames, BaseFreq ); - engine::mixer()->bufferToPort( buffer, fpp, - v, m_audioPort ); } m_frame += frames; diff --git a/src/core/audio/AudioPort.cpp b/src/core/audio/AudioPort.cpp index 26c1d7074..93b447f0a 100644 --- a/src/core/audio/AudioPort.cpp +++ b/src/core/audio/AudioPort.cpp @@ -27,20 +27,18 @@ #include "EffectChain.h" #include "FxMixer.h" #include "engine.h" +#include "MixHelpers.h" +#include "BufferManager.h" AudioPort::AudioPort( const QString & _name, bool _has_effect_chain ) : - m_bufferUsage( NoUsage ), - m_firstBuffer( new sampleFrame[engine::mixer()->framesPerPeriod()] ), - m_secondBuffer( new sampleFrame[ - engine::mixer()->framesPerPeriod()] ), + m_bufferUsage( false ), + m_portBuffer( NULL ), m_extOutputEnabled( false ), m_nextFxChannel( 0 ), m_name( "unnamed port" ), m_effects( _has_effect_chain ? new EffectChain( NULL ) : NULL ) { - engine::mixer()->clearAudioBuffer( m_firstBuffer, engine::mixer()->framesPerPeriod() ); - engine::mixer()->clearAudioBuffer( m_secondBuffer, engine::mixer()->framesPerPeriod() ); engine::mixer()->addAudioPort( this ); setExtOutputEnabled( true ); } @@ -52,31 +50,12 @@ AudioPort::~AudioPort() { setExtOutputEnabled( false ); engine::mixer()->removeAudioPort( this ); - delete[] m_firstBuffer; - delete[] m_secondBuffer; delete m_effects; } -void AudioPort::nextPeriod() -{ - m_firstBufferLock.lock(); - engine::mixer()->clearAudioBuffer( m_firstBuffer, engine::mixer()->framesPerPeriod() ); - qSwap( m_firstBuffer, m_secondBuffer ); - - // this is how we decrease state of buffer-usage ;-) - m_bufferUsage = ( m_bufferUsage != NoUsage ) ? - ( ( m_bufferUsage == FirstBuffer ) ? - NoUsage : FirstBuffer ) : NoUsage; - - m_firstBufferLock.unlock(); -} - - - - void AudioPort::setExtOutputEnabled( bool _enabled ) { if( _enabled != m_extOutputEnabled ) @@ -109,23 +88,64 @@ bool AudioPort::processEffects() { if( m_effects ) { - lockFirstBuffer(); - bool hasInputNoise = m_bufferUsage != NoUsage; - bool more = m_effects->processAudioBuffer( m_firstBuffer, engine::mixer()->framesPerPeriod(), hasInputNoise ); - unlockFirstBuffer(); + bool more = m_effects->processAudioBuffer( m_portBuffer, engine::mixer()->framesPerPeriod(), m_bufferUsage ); return more; } return false; } -void AudioPort::doProcessing( sampleFrame * ) +void AudioPort::doProcessing() { - const bool me = processEffects(); - if( me || m_bufferUsage != NoUsage ) + const fpp_t fpp = engine::mixer()->framesPerPeriod(); + + if( m_playHandles.isEmpty() ) return; // skip processing if no playhandles are connected + + m_portBuffer = BufferManager::acquire(); // get buffer for processing + + engine::mixer()->clearAudioBuffer( m_portBuffer, fpp ); // clear the audioport buffer so we can use it + + //qDebug( "Playhandles: %d", m_playHandles.size() ); + foreach( PlayHandle * ph, m_playHandles ) // now we mix all playhandle buffers into the audioport buffer { - engine::fxMixer()->mixToChannel( firstBuffer(), nextFxChannel() ); - nextPeriod(); + if( ph->buffer() ) + { + if( ph->usesBuffer() ) + { + m_bufferUsage = true; + MixHelpers::add( m_portBuffer, ph->buffer(), fpp ); + } + ph->releaseBuffer(); // gets rid of playhandle's buffer and sets + // pointer to null, so if it doesn't get re-acquired we know to skip it next time + } } + + const bool me = processEffects(); + if( me || m_bufferUsage ) + { + engine::fxMixer()->mixToChannel( m_portBuffer, m_nextFxChannel ); + m_bufferUsage = false; + } + + BufferManager::release( m_portBuffer ); // release buffer, we don't need it anymore } + +void AudioPort::addPlayHandle( PlayHandle * handle ) +{ + m_playHandleLock.lock(); + m_playHandles.append( handle ); + m_playHandleLock.unlock(); +} + + +void AudioPort::removePlayHandle( PlayHandle * handle ) +{ + m_playHandleLock.lock(); + PlayHandleList::Iterator it = qFind( m_playHandles.begin(), m_playHandles.end(), handle ); + if( it != m_playHandles.end() ) + { + m_playHandles.erase( it ); + } + m_playHandleLock.unlock(); +} diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 98bdd817f..bbbe8ba1b 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -237,7 +237,7 @@ void InstrumentTrack::processAudioBuffer( sampleFrame* buf, const fpp_t frames, } } - engine::mixer()->bufferToPort( buf, frames, panningToVolumeVector( panning, v_scale ), &m_audioPort ); + //engine::mixer()->bufferToPort( buf, frames, panningToVolumeVector( panning, v_scale ), &m_audioPort ); }