parallelizing-support for SMP-systems
git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@403 0778d3d1-df1d-0410-868b-ea421aaaa00d
This commit is contained in:
@@ -51,14 +51,14 @@ instrument::~instrument()
|
||||
|
||||
|
||||
|
||||
void instrument::play( void )
|
||||
void instrument::play( bool )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void instrument::playNote( notePlayHandle * )
|
||||
void instrument::playNote( notePlayHandle *, bool )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ mixer::mixer( engine * _engine ) :
|
||||
m_readBuf( NULL ),
|
||||
m_writeBuf( NULL ),
|
||||
m_cpuLoad( 0 ),
|
||||
m_parallelizingLevel( 1 ),
|
||||
m_qualityLevel( DEFAULT_QUALITY_LEVEL ),
|
||||
m_masterGain( 1.0f ),
|
||||
m_audioDev( NULL ),
|
||||
@@ -88,6 +89,13 @@ mixer::mixer( engine * _engine ) :
|
||||
QString::number( m_framesPerAudioBuffer ) );
|
||||
}
|
||||
|
||||
if( configManager::inst()->value( "mixer", "parallelizinglevel"
|
||||
).toInt() > 0 )
|
||||
{
|
||||
m_parallelizingLevel =configManager::inst()->value( "mixer",
|
||||
"parallelizinglevel" ).toInt();
|
||||
}
|
||||
|
||||
for( Uint8 i = 0; i < 3; i++ )
|
||||
{
|
||||
m_readBuf = bufferAllocator::alloc<surroundSampleFrame>(
|
||||
@@ -258,22 +266,76 @@ const surroundSampleFrame * mixer::renderNextBuffer( void )
|
||||
// if( criticalXRuns() == FALSE )
|
||||
{
|
||||
csize idx = 0;
|
||||
while( idx < m_playHandles.size() )
|
||||
if( m_parallelizingLevel > 1 )
|
||||
{
|
||||
register playHandle * n = m_playHandles[idx];
|
||||
// delete play-handle if it played completely
|
||||
if( n->done() )
|
||||
// TODO: if not enough play-handles are found which are capable of
|
||||
// parallelizing, create according worker-threads. each of this threads
|
||||
// processes a certain part of our m_playHandles-vector
|
||||
// the question is the queueing model which we can use:
|
||||
// 1) m_playHandles is divided into m_parallelizingLevel sub-vectors
|
||||
// each sub-vector is processed by a worker-thread
|
||||
// 2) create a stack with all play-handles that need to be processed,
|
||||
// save it via a mutex and then let all worker-threads "fetch jobs"
|
||||
// from the stack - this way it's guaranteed the work is
|
||||
// balanced across all worker-threads. this would avoid cases
|
||||
// where the sub-vector of a thread only contains notes that are
|
||||
// done and only need to be deleted.
|
||||
playHandleVector par_hndls;
|
||||
while( idx < m_playHandles.size() )
|
||||
{
|
||||
delete n;
|
||||
m_playHandles.erase( m_playHandles.begin() +
|
||||
idx );
|
||||
}
|
||||
else
|
||||
{
|
||||
// play all uncompletely-played play-handles...
|
||||
n->play();
|
||||
playHandle * n = m_playHandles[idx];
|
||||
if( !n->done() && n->supportsParallelizing() )
|
||||
{
|
||||
n->play( TRUE );
|
||||
par_hndls.push_back( n );
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
idx = 0;
|
||||
while( idx < m_playHandles.size() )
|
||||
{
|
||||
playHandle * n =m_playHandles[idx];
|
||||
if( n->supportsParallelizing() )
|
||||
{
|
||||
++idx;
|
||||
continue;
|
||||
}
|
||||
else if( n->done() )
|
||||
{
|
||||
delete n;
|
||||
m_playHandles.erase(
|
||||
m_playHandles.begin() + idx );
|
||||
}
|
||||
else
|
||||
{
|
||||
n->play();
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
for( playHandleVector::iterator it = par_hndls.begin();
|
||||
it != par_hndls.end(); ++it )
|
||||
{
|
||||
( *it )->waitForWorkerThread();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while( idx < m_playHandles.size() )
|
||||
{
|
||||
register playHandle * n = m_playHandles[idx];
|
||||
// delete play-handle if it played completely
|
||||
if( n->done() )
|
||||
{
|
||||
delete n;
|
||||
m_playHandles.erase(
|
||||
m_playHandles.begin() + idx );
|
||||
}
|
||||
else
|
||||
{
|
||||
n->play();
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eng()->getSongEditor()->processNextBuffer();
|
||||
@@ -283,7 +345,8 @@ const surroundSampleFrame * mixer::renderNextBuffer( void )
|
||||
it != m_audioPorts.end(); ++it )
|
||||
{
|
||||
more_effects = ( *it )->processEffects();
|
||||
if( ( *it )->m_bufferUsage != audioPort::NONE || more_effects )
|
||||
if( ( *it )->m_bufferUsage != audioPort::NONE ||
|
||||
more_effects )
|
||||
{
|
||||
processBuffer( ( *it )->firstBuffer(),
|
||||
( *it )->nextFxChannel() );
|
||||
@@ -324,7 +387,7 @@ void mixer::clear( bool _everything )
|
||||
// parameter _everything is true (which is the case when
|
||||
// clearing song for example)
|
||||
if( _everything == TRUE ||
|
||||
( *it )->type() != playHandle::INSTRUMENT_PLAY_HANDLE )
|
||||
( *it )->type() != playHandle::InstrumentPlayHandle )
|
||||
{
|
||||
m_playHandlesToRemove.push_back( *it );
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ notePlayHandle::notePlayHandle( instrumentTrack * _it,
|
||||
const f_cnt_t _frames,
|
||||
const note & _n,
|
||||
const bool _arp_note ) :
|
||||
playHandle( NOTE_PLAY_HANDLE ),
|
||||
playHandle( NotePlayHandle ),
|
||||
note( NULL, _n.length(), _n.pos(), _n.tone(), _n.octave(),
|
||||
_n.getVolume(), _n.getPanning() ),
|
||||
m_pluginData( NULL ),
|
||||
@@ -121,7 +121,7 @@ notePlayHandle::~notePlayHandle()
|
||||
|
||||
|
||||
|
||||
void notePlayHandle::play( void )
|
||||
void notePlayHandle::play( bool _try_parallelizing )
|
||||
{
|
||||
if( m_muted == TRUE || m_instrumentTrack == NULL )
|
||||
{
|
||||
@@ -137,7 +137,7 @@ void notePlayHandle::play( void )
|
||||
}
|
||||
|
||||
// play note!
|
||||
m_instrumentTrack->playNote( this );
|
||||
m_instrumentTrack->playNote( this, _try_parallelizing );
|
||||
|
||||
if( m_released == TRUE )
|
||||
{
|
||||
@@ -198,7 +198,7 @@ void notePlayHandle::play( void )
|
||||
for( notePlayHandleVector::iterator it = m_subNotes.begin();
|
||||
it != m_subNotes.end(); )
|
||||
{
|
||||
( *it )->play();
|
||||
( *it )->play( _try_parallelizing );
|
||||
if( ( *it )->done() )
|
||||
{
|
||||
delete *it;
|
||||
|
||||
@@ -139,6 +139,21 @@ plugin * plugin::instantiate( const QString & _plugin_name, void * _data )
|
||||
|
||||
|
||||
|
||||
bool plugin::supportsParallelizing( void ) const
|
||||
{
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void plugin::waitForWorkerThread( void )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void plugin::getDescriptorsOfAvailPlugins( vvector<descriptor> & _plugin_descs )
|
||||
{
|
||||
QDir directory( configManager::inst()->pluginDir() );
|
||||
|
||||
@@ -127,7 +127,7 @@ QMap<const engine *, previewTrackContainer *>
|
||||
presetPreviewPlayHandle::presetPreviewPlayHandle(
|
||||
const QString & _preset_file,
|
||||
engine * _engine ) :
|
||||
playHandle( PRESET_PREVIEW_PLAY_HANDLE ),
|
||||
playHandle( PresetPreviewHandle ),
|
||||
engineObject( _engine ),
|
||||
m_previewNote( NULL )
|
||||
{
|
||||
@@ -192,9 +192,9 @@ presetPreviewPlayHandle::~presetPreviewPlayHandle()
|
||||
|
||||
|
||||
|
||||
void presetPreviewPlayHandle::play( void )
|
||||
void presetPreviewPlayHandle::play( bool _try_parallelizing )
|
||||
{
|
||||
m_previewNote->play();
|
||||
m_previewNote->play( _try_parallelizing );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
samplePlayHandle::samplePlayHandle( const QString & _sample_file,
|
||||
engine * _engine ) :
|
||||
playHandle( SAMPLE_PLAY_HANDLE ),
|
||||
playHandle( SamplePlayHandle ),
|
||||
engineObject( _engine ),
|
||||
m_sampleBuffer( new sampleBuffer( eng(), _sample_file ) ),
|
||||
m_doneMayReturnTrue( TRUE ),
|
||||
@@ -55,7 +55,7 @@ samplePlayHandle::samplePlayHandle( const QString & _sample_file,
|
||||
|
||||
|
||||
samplePlayHandle::samplePlayHandle( sampleBuffer * _sample_buffer ) :
|
||||
playHandle( SAMPLE_PLAY_HANDLE ),
|
||||
playHandle( SamplePlayHandle ),
|
||||
engineObject( _sample_buffer->eng() ),
|
||||
m_sampleBuffer( sharedObject::ref( _sample_buffer ) ),
|
||||
m_doneMayReturnTrue( TRUE ),
|
||||
@@ -72,7 +72,7 @@ samplePlayHandle::samplePlayHandle( sampleBuffer * _sample_buffer ) :
|
||||
|
||||
|
||||
samplePlayHandle::samplePlayHandle( sampleTCO * _tco ) :
|
||||
playHandle( SAMPLE_PLAY_HANDLE ),
|
||||
playHandle( SamplePlayHandle ),
|
||||
engineObject( _tco->eng() ),
|
||||
m_sampleBuffer( sharedObject::ref( _tco->getSampleBuffer() ) ),
|
||||
m_doneMayReturnTrue( TRUE ),
|
||||
@@ -89,7 +89,7 @@ samplePlayHandle::samplePlayHandle( sampleTCO * _tco ) :
|
||||
|
||||
|
||||
samplePlayHandle::samplePlayHandle( pattern * _pattern ) :
|
||||
playHandle( SAMPLE_PLAY_HANDLE ),
|
||||
playHandle( SamplePlayHandle ),
|
||||
engineObject( _pattern->eng() ),
|
||||
m_sampleBuffer( sharedObject::ref( _pattern->getFrozenPattern() ) ),
|
||||
m_doneMayReturnTrue( TRUE ),
|
||||
@@ -117,15 +117,15 @@ samplePlayHandle::~samplePlayHandle()
|
||||
|
||||
|
||||
|
||||
void samplePlayHandle::play( void )
|
||||
void samplePlayHandle::play( bool _try_parallelizing )
|
||||
{
|
||||
play( 0 );
|
||||
play( 0, _try_parallelizing );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void samplePlayHandle::play( const fpab_t _frame_base )
|
||||
void samplePlayHandle::play( const fpab_t _frame_base, bool )
|
||||
{
|
||||
if( framesDone() >= totalFrames() )
|
||||
{
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
#include "debug.h"
|
||||
#include "tooltip.h"
|
||||
#include "led_checkbox.h"
|
||||
#include "lcd_spinbox.h"
|
||||
#include "ladspa_manager.h"
|
||||
|
||||
|
||||
@@ -126,7 +127,9 @@ setupDialog::setupDialog( engine * _engine, configTabs _tab_to_open ) :
|
||||
m_disableChActInd( configManager::inst()->value( "ui",
|
||||
"disablechannelactivityindicators" ).toInt() ),
|
||||
m_manualChPiano( configManager::inst()->value( "ui",
|
||||
"manualchannelpiano" ).toInt() )
|
||||
"manualchannelpiano" ).toInt() ),
|
||||
m_parLevel( configManager::inst()->value( "mixer",
|
||||
"parallelizinglevel" ).toInt() )
|
||||
|
||||
{
|
||||
setWindowIcon( embed::getIconPixmap( "setup_general" ) );
|
||||
@@ -453,8 +456,45 @@ setupDialog::setupDialog( engine * _engine, configTabs _tab_to_open ) :
|
||||
|
||||
|
||||
|
||||
tabWidget * smp_supp_tw = new tabWidget( tr( "SMP support" ).toUpper(),
|
||||
performance );
|
||||
smp_supp_tw->setFixedHeight( 180 );
|
||||
|
||||
QLabel * par_level_lbl = new QLabel( tr( "Parallelizing level" ),
|
||||
smp_supp_tw );
|
||||
par_level_lbl->move( 10, 15 );
|
||||
lcdSpinBox * par_level = new lcdSpinBox( 1, 16, 2, smp_supp_tw,
|
||||
tr( "SMP-level" ),
|
||||
eng(), NULL );
|
||||
par_level->setValue( m_parLevel );
|
||||
connect( par_level, SIGNAL( valueChanged( int ) ),
|
||||
this, SLOT( setParallelizingLevel( int ) ) );
|
||||
|
||||
par_level->move( 120, 20 );
|
||||
|
||||
QLabel * smp_help = new QLabel(
|
||||
tr( "If you have a machine with more then one processor "
|
||||
"(e.g. dual-core systems) you should use a "
|
||||
"parallelizing-level above 1 which means that LMMS "
|
||||
"will try to split up sound-processing into several "
|
||||
"threads which should should be run on several cores "
|
||||
"by the underlaying operating-system.\n"
|
||||
"Please note that in some cases parallelizing won't "
|
||||
"work with small buffer-sizes. If you experience "
|
||||
"problems (i.e. lot of xruns), try to increase buffer-"
|
||||
"size." ), smp_supp_tw );
|
||||
smp_help->setFixedSize( performance->width() - 20, 110 );
|
||||
#ifndef QT3
|
||||
smp_help->setWordWrap( TRUE );
|
||||
#else
|
||||
smp_help->setAlignment( Qt::WordBreak );
|
||||
#endif
|
||||
smp_help->move( 10, 55 );
|
||||
|
||||
|
||||
perf_layout->addWidget( ui_fx_tw );
|
||||
perf_layout->addSpacing( 15 );
|
||||
perf_layout->addWidget( smp_supp_tw );
|
||||
perf_layout->addStretch();
|
||||
|
||||
|
||||
@@ -721,6 +761,8 @@ void setupDialog::accept( void )
|
||||
QString::number( m_disableChActInd ) );
|
||||
configManager::inst()->setValue( "ui", "manualchannelpiano",
|
||||
QString::number( m_manualChPiano ) );
|
||||
configManager::inst()->setValue( "mixer", "parallelizinglevel",
|
||||
QString::number( m_parLevel ) );
|
||||
|
||||
configManager::inst()->setWorkingDir( m_workingDir );
|
||||
configManager::inst()->setVSTDir( m_vstDir );
|
||||
@@ -878,6 +920,13 @@ void setupDialog::toggleManualChPiano( bool _enabled )
|
||||
|
||||
|
||||
|
||||
void setupDialog::setParallelizingLevel( int _level )
|
||||
{
|
||||
m_parLevel = _level;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void setupDialog::openWorkingDir( void )
|
||||
{
|
||||
|
||||
@@ -734,7 +734,7 @@ void instrumentTrack::processOutEvent( const midiEvent & _me,
|
||||
|
||||
|
||||
|
||||
void instrumentTrack::playNote( notePlayHandle * _n )
|
||||
void instrumentTrack::playNote( notePlayHandle * _n, bool _try_parallelizing )
|
||||
{
|
||||
// arpeggio- and chord-widget has to do its work -> adding sub-notes
|
||||
// for chords/arpeggios
|
||||
@@ -743,7 +743,7 @@ void instrumentTrack::playNote( notePlayHandle * _n )
|
||||
if( _n->arpBaseNote() == FALSE && m_instrument != NULL )
|
||||
{
|
||||
// all is done, so now lets play the note!
|
||||
m_instrument->playNote( _n );
|
||||
m_instrument->playNote( _n, _try_parallelizing );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1031,7 +1031,7 @@ bool FASTCALL instrumentTrack::play( const midiTime & _start,
|
||||
note_frames,
|
||||
*cur_note );
|
||||
note_play_handle->setBBTrack( bb_track );
|
||||
note_play_handle->play();
|
||||
note_play_handle->play( FALSE );
|
||||
// could we play all within current number of
|
||||
// frames per audio-buffer?
|
||||
if( note_play_handle->done() == FALSE )
|
||||
|
||||
Reference in New Issue
Block a user