Mixer refactor (#4894)
Co-authored-by: Kevin Zander <veratil@gmail.com>
This commit is contained in:
committed by
GitHub
parent
2cb797353b
commit
28ee260e28
@@ -98,9 +98,9 @@ public:
|
||||
Interpolation interpolation;
|
||||
Oversampling oversampling;
|
||||
|
||||
qualitySettings( Mode _m )
|
||||
qualitySettings(Mode m)
|
||||
{
|
||||
switch( _m )
|
||||
switch (m)
|
||||
{
|
||||
case Mode_Draft:
|
||||
interpolation = Interpolation_Linear;
|
||||
@@ -118,9 +118,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
qualitySettings( Interpolation _i, Oversampling _o ) :
|
||||
interpolation( _i ),
|
||||
oversampling( _o )
|
||||
qualitySettings(Interpolation i, Oversampling o) :
|
||||
interpolation(i),
|
||||
oversampling(o)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -186,14 +186,14 @@ public:
|
||||
|
||||
|
||||
// audio-port-stuff
|
||||
inline void addAudioPort( AudioPort * _port )
|
||||
inline void addAudioPort(AudioPort * port)
|
||||
{
|
||||
requestChangeInModel();
|
||||
m_audioPorts.push_back( _port );
|
||||
m_audioPorts.push_back(port);
|
||||
doneChangeInModel();
|
||||
}
|
||||
|
||||
void removeAudioPort( AudioPort * _port );
|
||||
void removeAudioPort(AudioPort * port);
|
||||
|
||||
|
||||
// MIDI-client-stuff
|
||||
@@ -218,7 +218,7 @@ public:
|
||||
return m_playHandles;
|
||||
}
|
||||
|
||||
void removePlayHandlesOfTypes( Track * _track, const quint8 types );
|
||||
void removePlayHandlesOfTypes(Track * track, const quint8 types);
|
||||
|
||||
|
||||
// methods providing information for other classes
|
||||
@@ -255,23 +255,23 @@ public:
|
||||
return m_masterGain;
|
||||
}
|
||||
|
||||
inline void setMasterGain( const float _mo )
|
||||
inline void setMasterGain(const float mo)
|
||||
{
|
||||
m_masterGain = _mo;
|
||||
m_masterGain = mo;
|
||||
}
|
||||
|
||||
|
||||
static inline sample_t clip( const sample_t _s )
|
||||
static inline sample_t clip(const sample_t s)
|
||||
{
|
||||
if( _s > 1.0f )
|
||||
if (s > 1.0f)
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
else if( _s < -1.0f )
|
||||
else if (s < -1.0f)
|
||||
{
|
||||
return -1.0f;
|
||||
}
|
||||
return _s;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -281,7 +281,7 @@ public:
|
||||
sample_t left;
|
||||
sample_t right;
|
||||
};
|
||||
StereoSample getPeakValues(sampleFrame * _ab, const f_cnt_t _frames) const;
|
||||
StereoSample getPeakValues(sampleFrame * ab, const f_cnt_t _frames) const;
|
||||
|
||||
|
||||
bool criticalXRuns() const;
|
||||
@@ -308,7 +308,7 @@ public:
|
||||
return hasFifoWriter() ? m_fifo->read() : renderNextBuffer();
|
||||
}
|
||||
|
||||
void changeQuality( const struct qualitySettings & _qs );
|
||||
void changeQuality(const struct qualitySettings & qs);
|
||||
|
||||
inline bool isMetronomeActive() const { return m_metronomeActive; }
|
||||
inline void setMetronomeActive(bool value = true) { m_metronomeActive = value; }
|
||||
@@ -333,7 +333,7 @@ private:
|
||||
class fifoWriter : public QThread
|
||||
{
|
||||
public:
|
||||
fifoWriter( Mixer * _mixer, fifo * _fifo );
|
||||
fifoWriter(Mixer * mixer, fifo * _fifo);
|
||||
|
||||
void finish();
|
||||
|
||||
@@ -353,7 +353,7 @@ private:
|
||||
Mixer( bool renderOnly );
|
||||
virtual ~Mixer();
|
||||
|
||||
void startProcessing( bool _needs_fifo = true );
|
||||
void startProcessing(bool needsFifo = true);
|
||||
void stopProcessing();
|
||||
|
||||
|
||||
@@ -363,6 +363,10 @@ private:
|
||||
|
||||
const surroundSampleFrame * renderNextBuffer();
|
||||
|
||||
void swapBuffers();
|
||||
|
||||
void handleMetronome();
|
||||
|
||||
void clearInternal();
|
||||
|
||||
//! Called by the audio thread to give control to other threads,
|
||||
@@ -381,13 +385,8 @@ private:
|
||||
int m_inputBufferRead;
|
||||
int m_inputBufferWrite;
|
||||
|
||||
surroundSampleFrame * m_readBuf;
|
||||
surroundSampleFrame * m_writeBuf;
|
||||
|
||||
QVector<surroundSampleFrame *> m_bufferPool;
|
||||
int m_readBuffer;
|
||||
int m_writeBuffer;
|
||||
int m_poolDepth;
|
||||
surroundSampleFrame * m_outputBufferRead;
|
||||
surroundSampleFrame * m_outputBufferWrite;
|
||||
|
||||
// worker thread stuff
|
||||
QVector<MixerWorkerThread *> m_workers;
|
||||
|
||||
@@ -74,8 +74,8 @@ Mixer::Mixer( bool renderOnly ) :
|
||||
m_framesPerPeriod( DEFAULT_BUFFER_SIZE ),
|
||||
m_inputBufferRead( 0 ),
|
||||
m_inputBufferWrite( 1 ),
|
||||
m_readBuf( NULL ),
|
||||
m_writeBuf( NULL ),
|
||||
m_outputBufferRead(nullptr),
|
||||
m_outputBufferWrite(nullptr),
|
||||
m_workers(),
|
||||
m_numWorkers( QThread::idealThreadCount()-1 ),
|
||||
m_newPlayHandles( PlayHandle::MaxNumber ),
|
||||
@@ -136,15 +136,12 @@ Mixer::Mixer( bool renderOnly ) :
|
||||
// now that framesPerPeriod is fixed initialize global BufferManager
|
||||
BufferManager::init( m_framesPerPeriod );
|
||||
|
||||
for( int i = 0; i < 3; i++ )
|
||||
{
|
||||
m_readBuf = (surroundSampleFrame*)
|
||||
MemoryHelper::alignedMalloc( m_framesPerPeriod *
|
||||
sizeof( surroundSampleFrame ) );
|
||||
int outputBufferSize = m_framesPerPeriod * sizeof(surroundSampleFrame);
|
||||
m_outputBufferRead = static_cast<surroundSampleFrame *>(MemoryHelper::alignedMalloc(outputBufferSize));
|
||||
m_outputBufferWrite = static_cast<surroundSampleFrame *>(MemoryHelper::alignedMalloc(outputBufferSize));
|
||||
|
||||
BufferManager::clear( m_readBuf, m_framesPerPeriod );
|
||||
m_bufferPool.push_back( m_readBuf );
|
||||
}
|
||||
BufferManager::clear(m_outputBufferRead, m_framesPerPeriod);
|
||||
BufferManager::clear(m_outputBufferWrite, m_framesPerPeriod);
|
||||
|
||||
for( int i = 0; i < m_numWorkers+1; ++i )
|
||||
{
|
||||
@@ -155,10 +152,6 @@ Mixer::Mixer( bool renderOnly ) :
|
||||
}
|
||||
m_workers.push_back( wt );
|
||||
}
|
||||
|
||||
m_poolDepth = 2;
|
||||
m_readBuffer = 0;
|
||||
m_writeBuffer = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -189,10 +182,8 @@ Mixer::~Mixer()
|
||||
delete m_midiClient;
|
||||
delete m_audioDev;
|
||||
|
||||
for( int i = 0; i < 3; i++ )
|
||||
{
|
||||
MemoryHelper::alignedFree( m_bufferPool[i] );
|
||||
}
|
||||
MemoryHelper::alignedFree(m_outputBufferRead);
|
||||
MemoryHelper::alignedFree(m_outputBufferWrite);
|
||||
|
||||
for( int i = 0; i < 2; ++i )
|
||||
{
|
||||
@@ -220,9 +211,9 @@ void Mixer::initDevices()
|
||||
|
||||
|
||||
|
||||
void Mixer::startProcessing( bool _needs_fifo )
|
||||
void Mixer::startProcessing(bool needsFifo)
|
||||
{
|
||||
if( _needs_fifo )
|
||||
if (needsFifo)
|
||||
{
|
||||
m_fifoWriter = new fifoWriter( this, m_fifo );
|
||||
m_fifoWriter->start( QThread::HighPriority );
|
||||
@@ -345,42 +336,6 @@ const surroundSampleFrame * Mixer::renderNextBuffer()
|
||||
|
||||
s_renderingThread = true;
|
||||
|
||||
static Song::PlayPos last_metro_pos = -1;
|
||||
|
||||
Song *song = Engine::getSong();
|
||||
|
||||
Song::PlayModes currentPlayMode = song->playMode();
|
||||
Song::PlayPos p = song->getPlayPos( currentPlayMode );
|
||||
|
||||
bool playModeSupportsMetronome = currentPlayMode == Song::Mode_PlayPattern ||
|
||||
currentPlayMode == Song::Mode_PlaySong ||
|
||||
currentPlayMode == Song::Mode_PlayBB;
|
||||
|
||||
if( playModeSupportsMetronome && m_metronomeActive && !song->isExporting() &&
|
||||
!song->isPaused() && p != last_metro_pos &&
|
||||
// Stop crash with metronome if empty project
|
||||
Engine::getSong()->countTracks() )
|
||||
{
|
||||
tick_t ticksPerBar = TimePos::ticksPerBar();
|
||||
if ( p.getTicks() % ( ticksPerBar / 1 ) == 0 )
|
||||
{
|
||||
addPlayHandle( new SamplePlayHandle( "misc/metronome02.ogg" ) );
|
||||
}
|
||||
else if ( p.getTicks() % ( ticksPerBar /
|
||||
song->getTimeSigModel().getNumerator() ) == 0 )
|
||||
{
|
||||
addPlayHandle( new SamplePlayHandle( "misc/metronome01.ogg" ) );
|
||||
}
|
||||
last_metro_pos = p;
|
||||
}
|
||||
|
||||
// swap buffer
|
||||
m_inputBufferWrite = ( m_inputBufferWrite + 1 ) % 2;
|
||||
m_inputBufferRead = ( m_inputBufferRead + 1 ) % 2;
|
||||
|
||||
// clear new write buffer
|
||||
m_inputBufferFrames[ m_inputBufferWrite ] = 0;
|
||||
|
||||
if( m_clearSignal )
|
||||
{
|
||||
m_clearSignal = false;
|
||||
@@ -409,22 +364,16 @@ const surroundSampleFrame * Mixer::renderNextBuffer()
|
||||
it_rem = m_playHandlesToRemove.erase( it_rem );
|
||||
}
|
||||
|
||||
// rotate buffers
|
||||
m_writeBuffer = ( m_writeBuffer + 1 ) % m_poolDepth;
|
||||
m_readBuffer = ( m_readBuffer + 1 ) % m_poolDepth;
|
||||
|
||||
m_writeBuf = m_bufferPool[m_writeBuffer];
|
||||
m_readBuf = m_bufferPool[m_readBuffer];
|
||||
|
||||
// clear last audio-buffer
|
||||
BufferManager::clear( m_writeBuf, m_framesPerPeriod );
|
||||
swapBuffers();
|
||||
|
||||
// prepare master mix (clear internal buffers etc.)
|
||||
FxMixer * fxMixer = Engine::fxMixer();
|
||||
fxMixer->prepareMasterMix();
|
||||
|
||||
handleMetronome();
|
||||
|
||||
// create play-handles for new notes, samples etc.
|
||||
song->processNextBuffer();
|
||||
Engine::getSong()->processNextBuffer();
|
||||
|
||||
// add all play-handles that have to be added
|
||||
for( LocklessListElement * e = m_newPlayHandles.popList(); e; )
|
||||
@@ -471,10 +420,10 @@ const surroundSampleFrame * Mixer::renderNextBuffer()
|
||||
|
||||
|
||||
// STAGE 3: do master mix in FX mixer
|
||||
fxMixer->masterMix( m_writeBuf );
|
||||
fxMixer->masterMix(m_outputBufferWrite);
|
||||
|
||||
|
||||
emit nextAudioBuffer( m_readBuf );
|
||||
emit nextAudioBuffer(m_outputBufferRead);
|
||||
|
||||
runChangesInModel();
|
||||
|
||||
@@ -487,12 +436,71 @@ const surroundSampleFrame * Mixer::renderNextBuffer()
|
||||
|
||||
m_profiler.finishPeriod( processingSampleRate(), m_framesPerPeriod );
|
||||
|
||||
return m_readBuf;
|
||||
return m_outputBufferRead;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Mixer::swapBuffers()
|
||||
{
|
||||
m_inputBufferWrite = (m_inputBufferWrite + 1) % 2;
|
||||
m_inputBufferRead = (m_inputBufferRead + 1) % 2;
|
||||
m_inputBufferFrames[m_inputBufferWrite] = 0;
|
||||
|
||||
std::swap(m_outputBufferRead, m_outputBufferWrite);
|
||||
BufferManager::clear(m_outputBufferWrite, m_framesPerPeriod);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Mixer::handleMetronome()
|
||||
{
|
||||
static tick_t lastMetroTicks = -1;
|
||||
|
||||
Song * song = Engine::getSong();
|
||||
Song::PlayModes currentPlayMode = song->playMode();
|
||||
|
||||
bool metronomeSupported =
|
||||
currentPlayMode == Song::Mode_PlayPattern
|
||||
|| currentPlayMode == Song::Mode_PlaySong
|
||||
|| currentPlayMode == Song::Mode_PlayBB;
|
||||
|
||||
if (!metronomeSupported || !m_metronomeActive || song->isExporting())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// stop crash with metronome if empty project
|
||||
if (song->countTracks() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
tick_t ticks = song->getPlayPos(currentPlayMode).getTicks();
|
||||
tick_t ticksPerBar = TimePos::ticksPerBar();
|
||||
int numerator = song->getTimeSigModel().getNumerator();
|
||||
|
||||
if (ticks == lastMetroTicks)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ticks % (ticksPerBar / 1) == 0)
|
||||
{
|
||||
addPlayHandle(new SamplePlayHandle("misc/metronome02.ogg"));
|
||||
}
|
||||
else if (ticks % (ticksPerBar / numerator) == 0)
|
||||
{
|
||||
addPlayHandle(new SamplePlayHandle("misc/metronome01.ogg"));
|
||||
}
|
||||
|
||||
lastMetroTicks = ticks;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Mixer::clear()
|
||||
{
|
||||
m_clearSignal = true;
|
||||
@@ -520,29 +528,28 @@ void Mixer::clearNewPlayHandles()
|
||||
void Mixer::clearInternal()
|
||||
{
|
||||
// TODO: m_midiClient->noteOffAll();
|
||||
for( PlayHandleList::Iterator it = m_playHandles.begin(); it != m_playHandles.end(); ++it )
|
||||
for (auto ph : m_playHandles)
|
||||
{
|
||||
// we must not delete instrument-play-handles as they exist
|
||||
// during the whole lifetime of an instrument
|
||||
if( ( *it )->type() != PlayHandle::TypeInstrumentPlayHandle )
|
||||
if (ph->type() != PlayHandle::TypeInstrumentPlayHandle)
|
||||
{
|
||||
m_playHandlesToRemove.push_back( *it );
|
||||
m_playHandlesToRemove.push_back(ph);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Mixer::StereoSample Mixer::getPeakValues(sampleFrame * _ab, const f_cnt_t _frames) const
|
||||
Mixer::StereoSample Mixer::getPeakValues(sampleFrame * ab, const f_cnt_t frames) const
|
||||
{
|
||||
sample_t peakLeft = 0.0f;
|
||||
sample_t peakRight = 0.0f;
|
||||
|
||||
for( f_cnt_t f = 0; f < _frames; ++f )
|
||||
for (f_cnt_t f = 0; f < frames; ++f)
|
||||
{
|
||||
float const absLeft = qAbs( _ab[f][0] );
|
||||
float const absRight = qAbs( _ab[f][1] );
|
||||
float const absLeft = qAbs(ab[f][0]);
|
||||
float const absRight = qAbs(ab[f][1]);
|
||||
if (absLeft > peakLeft)
|
||||
{
|
||||
peakLeft = absLeft;
|
||||
@@ -560,12 +567,12 @@ Mixer::StereoSample Mixer::getPeakValues(sampleFrame * _ab, const f_cnt_t _frame
|
||||
|
||||
|
||||
|
||||
void Mixer::changeQuality( const struct qualitySettings & _qs )
|
||||
void Mixer::changeQuality(const struct qualitySettings & qs)
|
||||
{
|
||||
// don't delete the audio-device
|
||||
stopProcessing();
|
||||
|
||||
m_qualitySettings = _qs;
|
||||
m_qualitySettings = qs;
|
||||
m_audioDev->applyQualitySettings();
|
||||
|
||||
emit sampleRateChanged();
|
||||
@@ -649,15 +656,14 @@ void Mixer::restoreAudioDevice()
|
||||
|
||||
|
||||
|
||||
void Mixer::removeAudioPort( AudioPort * _port )
|
||||
void Mixer::removeAudioPort(AudioPort * port)
|
||||
{
|
||||
requestChangeInModel();
|
||||
QVector<AudioPort *>::Iterator it = std::find( m_audioPorts.begin(),
|
||||
m_audioPorts.end(),
|
||||
_port );
|
||||
if( it != m_audioPorts.end() )
|
||||
|
||||
QVector<AudioPort *>::Iterator it = std::find(m_audioPorts.begin(), m_audioPorts.end(), port);
|
||||
if (it != m_audioPorts.end())
|
||||
{
|
||||
m_audioPorts.erase( it );
|
||||
m_audioPorts.erase(it);
|
||||
}
|
||||
doneChangeInModel();
|
||||
}
|
||||
@@ -682,22 +688,21 @@ bool Mixer::addPlayHandle( PlayHandle* handle )
|
||||
}
|
||||
|
||||
|
||||
void Mixer::removePlayHandle( PlayHandle * _ph )
|
||||
void Mixer::removePlayHandle(PlayHandle * ph)
|
||||
{
|
||||
requestChangeInModel();
|
||||
// check thread affinity as we must not delete play-handles
|
||||
// which were created in a thread different than mixer thread
|
||||
if( _ph->affinityMatters() &&
|
||||
_ph->affinity() == QThread::currentThread() )
|
||||
if (ph->affinityMatters() && ph->affinity() == QThread::currentThread())
|
||||
{
|
||||
_ph->audioPort()->removePlayHandle( _ph );
|
||||
ph->audioPort()->removePlayHandle(ph);
|
||||
bool removedFromList = false;
|
||||
// Check m_newPlayHandles first because doing it the other way around
|
||||
// creates a race condition
|
||||
for( LocklessListElement * e = m_newPlayHandles.first(),
|
||||
* ePrev = NULL; e; ePrev = e, e = e->next )
|
||||
{
|
||||
if( e->value == _ph )
|
||||
if (e->value == ph)
|
||||
{
|
||||
if( ePrev )
|
||||
{
|
||||
@@ -713,11 +718,10 @@ void Mixer::removePlayHandle( PlayHandle * _ph )
|
||||
}
|
||||
}
|
||||
// Now check m_playHandles
|
||||
PlayHandleList::Iterator it = std::find( m_playHandles.begin(),
|
||||
m_playHandles.end(), _ph );
|
||||
if( it != m_playHandles.end() )
|
||||
PlayHandleList::Iterator it = std::find(m_playHandles.begin(), m_playHandles.end(), ph);
|
||||
if (it != m_playHandles.end())
|
||||
{
|
||||
m_playHandles.erase( it );
|
||||
m_playHandles.erase(it);
|
||||
removedFromList = true;
|
||||
}
|
||||
// Only deleting PlayHandles that were actually found in the list
|
||||
@@ -725,16 +729,16 @@ void Mixer::removePlayHandle( PlayHandle * _ph )
|
||||
// (See tobydox's 2008 commit 4583e48)
|
||||
if ( removedFromList )
|
||||
{
|
||||
if( _ph->type() == PlayHandle::TypeNotePlayHandle )
|
||||
if (ph->type() == PlayHandle::TypeNotePlayHandle)
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*) _ph );
|
||||
NotePlayHandleManager::release(dynamic_cast<NotePlayHandle*>(ph));
|
||||
}
|
||||
else delete _ph;
|
||||
else { delete ph; }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_playHandlesToRemove.push_back( _ph );
|
||||
m_playHandlesToRemove.push_back(ph);
|
||||
}
|
||||
doneChangeInModel();
|
||||
}
|
||||
@@ -742,13 +746,13 @@ void Mixer::removePlayHandle( PlayHandle * _ph )
|
||||
|
||||
|
||||
|
||||
void Mixer::removePlayHandlesOfTypes( Track * _track, const quint8 types )
|
||||
void Mixer::removePlayHandlesOfTypes(Track * track, const quint8 types)
|
||||
{
|
||||
requestChangeInModel();
|
||||
PlayHandleList::Iterator it = m_playHandles.begin();
|
||||
while( it != m_playHandles.end() )
|
||||
{
|
||||
if( ( *it )->isFromTrack( _track ) && ( ( *it )->type() & types ) )
|
||||
if ((*it)->isFromTrack(track) && ((*it)->type() & types))
|
||||
{
|
||||
( *it )->audioPort()->removePlayHandle( ( *it ) );
|
||||
if( ( *it )->type() == PlayHandle::TypeNotePlayHandle )
|
||||
@@ -780,7 +784,7 @@ void Mixer::requestChangeInModel()
|
||||
|
||||
m_doChangesMutex.lock();
|
||||
m_waitChangesMutex.lock();
|
||||
if ( m_isProcessing && !m_waitingForWrite && !m_changesSignal )
|
||||
if (m_isProcessing && !m_waitingForWrite && !m_changesSignal)
|
||||
{
|
||||
m_changesSignal = true;
|
||||
m_changesRequestCondition.wait( &m_waitChangesMutex );
|
||||
@@ -1282,4 +1286,3 @@ void Mixer::fifoWriter::write( surroundSampleFrame * buffer )
|
||||
m_mixer->m_waitingForWrite = false;
|
||||
m_mixer->m_doChangesMutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user