Mixer refactor (#4894)

Co-authored-by: Kevin Zander <veratil@gmail.com>
This commit is contained in:
Alexandre Almeida
2020-12-10 22:46:03 -03:00
committed by GitHub
parent 2cb797353b
commit 28ee260e28
2 changed files with 131 additions and 129 deletions

View File

@@ -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;

View File

@@ -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();
}