buffer size, chunk size cleanup
use poll() many systems (even 2.4) can now handle much smaller writeaheads with software mixing
This commit is contained in:
@@ -29,6 +29,7 @@ FUNC(int, snd_pcm_hw_params_set_channels, (snd_pcm_t *pcm, snd_pcm_hw_params_t *
|
|||||||
FUNC(int, snd_pcm_hw_params_set_format, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val));
|
FUNC(int, snd_pcm_hw_params_set_format, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val));
|
||||||
FUNC(int, snd_pcm_hw_params_set_rate_near, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir));
|
FUNC(int, snd_pcm_hw_params_set_rate_near, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir));
|
||||||
FUNC(int, snd_pcm_hw_params_set_buffer_size_near, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val));
|
FUNC(int, snd_pcm_hw_params_set_buffer_size_near, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val));
|
||||||
|
FUNC(int, snd_pcm_hw_params_set_period_size_near, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir));
|
||||||
FUNC(int, snd_pcm_status, (snd_pcm_t *pcm, snd_pcm_status_t *status));
|
FUNC(int, snd_pcm_status, (snd_pcm_t *pcm, snd_pcm_status_t *status));
|
||||||
FUNC(snd_pcm_uframes_t, snd_pcm_status_get_avail, (const snd_pcm_status_t *obj));
|
FUNC(snd_pcm_uframes_t, snd_pcm_status_get_avail, (const snd_pcm_status_t *obj));
|
||||||
FUNC(size_t, snd_pcm_status_sizeof, (void));
|
FUNC(size_t, snd_pcm_status_sizeof, (void));
|
||||||
@@ -45,10 +46,12 @@ FUNC(snd_pcm_sframes_t, snd_pcm_mmap_writei, (snd_pcm_t *pcm, const void *buffer
|
|||||||
FUNC(int, snd_pcm_open, (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode));
|
FUNC(int, snd_pcm_open, (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode));
|
||||||
FUNC(int, snd_pcm_prepare, (snd_pcm_t *pcm));
|
FUNC(int, snd_pcm_prepare, (snd_pcm_t *pcm));
|
||||||
FUNC(int, snd_pcm_resume, (snd_pcm_t *pcm));
|
FUNC(int, snd_pcm_resume, (snd_pcm_t *pcm));
|
||||||
|
FUNC(int, snd_pcm_wait, (snd_pcm_t *pcm, int timeout));
|
||||||
FUNC(int, snd_pcm_sw_params, (snd_pcm_t *pcm, snd_pcm_sw_params_t *params));
|
FUNC(int, snd_pcm_sw_params, (snd_pcm_t *pcm, snd_pcm_sw_params_t *params));
|
||||||
FUNC(int, snd_pcm_sw_params_current, (snd_pcm_t *pcm, snd_pcm_sw_params_t *params));
|
FUNC(int, snd_pcm_sw_params_current, (snd_pcm_t *pcm, snd_pcm_sw_params_t *params));
|
||||||
FUNC(int, snd_pcm_sw_params_get_boundary, (const snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val));
|
FUNC(int, snd_pcm_sw_params_get_boundary, (const snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val));
|
||||||
FUNC(int, snd_pcm_sw_params_get_xfer_align, (const snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val));
|
FUNC(int, snd_pcm_sw_params_set_xfer_align, (snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val));
|
||||||
FUNC(int, snd_pcm_sw_params_set_stop_threshold, (snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val));
|
FUNC(int, snd_pcm_sw_params_set_stop_threshold, (snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val));
|
||||||
FUNC(int, snd_pcm_sw_params_get_avail_min, (snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val));
|
FUNC(int, snd_pcm_sw_params_get_avail_min, (snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val));
|
||||||
|
FUNC(int, snd_pcm_sw_params_set_avail_min, (snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val));
|
||||||
|
|
||||||
|
|||||||
@@ -68,10 +68,18 @@ bool Alsa9Buf::SetHWParams()
|
|||||||
if( samplerate_set_explicitly && (int) rate != samplerate )
|
if( samplerate_set_explicitly && (int) rate != samplerate )
|
||||||
LOG->Warn("Alsa9Buf::SetHWParams: Couldn't get %ihz (got %ihz instead)", samplerate, rate);
|
LOG->Warn("Alsa9Buf::SetHWParams: Couldn't get %ihz (got %ihz instead)", samplerate, rate);
|
||||||
|
|
||||||
snd_pcm_uframes_t buffersize = 1024*32;
|
/* Set the buffersize to the writeahead, and then copy back the actual value
|
||||||
err = dsnd_pcm_hw_params_set_buffer_size_near( pcm, hwparams, &buffersize );
|
* we got. */
|
||||||
|
err = dsnd_pcm_hw_params_set_buffer_size_near( pcm, hwparams, &writeahead );
|
||||||
ALSA_CHECK("dsnd_pcm_hw_params_set_buffer_size_near");
|
ALSA_CHECK("dsnd_pcm_hw_params_set_buffer_size_near");
|
||||||
|
|
||||||
|
/* The period size is roughly equivalent to what we call the chunksize. */
|
||||||
|
int dir;
|
||||||
|
err = dsnd_pcm_hw_params_set_period_size_near( pcm, hwparams, &chunksize, &dir );
|
||||||
|
ALSA_CHECK("dsnd_pcm_hw_params_set_period_size_near");
|
||||||
|
|
||||||
|
// LOG->Info("asked for %i period, got %i", chunksize, period_size);
|
||||||
|
|
||||||
/* write the hardware parameters to the device */
|
/* write the hardware parameters to the device */
|
||||||
err = dsnd_pcm_hw_params( pcm, hwparams );
|
err = dsnd_pcm_hw_params( pcm, hwparams );
|
||||||
ALSA_CHECK("dsnd_pcm_hw_params");
|
ALSA_CHECK("dsnd_pcm_hw_params");
|
||||||
@@ -85,20 +93,17 @@ bool Alsa9Buf::SetSWParams()
|
|||||||
dsnd_pcm_sw_params_alloca( &swparams );
|
dsnd_pcm_sw_params_alloca( &swparams );
|
||||||
dsnd_pcm_sw_params_current( pcm, swparams );
|
dsnd_pcm_sw_params_current( pcm, swparams );
|
||||||
|
|
||||||
int err = dsnd_pcm_sw_params_get_xfer_align( swparams, &xfer_align );
|
int err = dsnd_pcm_sw_params_set_xfer_align( pcm, swparams, 1 );
|
||||||
ALSA_ASSERT("dsnd_pcm_sw_params_get_xfer_align");
|
ALSA_ASSERT("dsnd_pcm_sw_params_set_xfer_align");
|
||||||
|
|
||||||
snd_pcm_uframes_t avail_min;
|
/* chunksize has been set to the period size. Set avail_min to the period
|
||||||
err = dsnd_pcm_sw_params_get_avail_min( swparams, &avail_min );
|
* size, too, so poll() wakes up once per chunk. */
|
||||||
ALSA_ASSERT("dsnd_pcm_sw_params_get_avail_min");
|
err = dsnd_pcm_sw_params_set_avail_min( pcm, swparams, chunksize );
|
||||||
|
ALSA_ASSERT("dsnd_pcm_sw_params_set_avail_min");
|
||||||
|
|
||||||
/* So far, I havn't seen a case where avail_min > xfer_align. If that happens,
|
/* If this fails, we might have bound dsnd_pcm_sw_params_set_avail_min to
|
||||||
* the GetNumFramesToFill will need updating. */
|
|
||||||
ASSERT( avail_min <= xfer_align );
|
|
||||||
|
|
||||||
/* If this fails, we might have bound dsnd_pcm_sw_params_get_xfer_align to
|
|
||||||
* the old SW API. */
|
* the old SW API. */
|
||||||
ASSERT( err <= 0 );
|
// ASSERT( err <= 0 );
|
||||||
|
|
||||||
/* Disable SND_PCM_STATE_XRUN. */
|
/* Disable SND_PCM_STATE_XRUN. */
|
||||||
snd_pcm_uframes_t boundary = 0;
|
snd_pcm_uframes_t boundary = 0;
|
||||||
@@ -220,6 +225,8 @@ Alsa9Buf::Alsa9Buf( hw hardware, int channels_ )
|
|||||||
samplebits = 16;
|
samplebits = 16;
|
||||||
last_cursor_pos = 0;
|
last_cursor_pos = 0;
|
||||||
samplerate_set_explicitly = false;
|
samplerate_set_explicitly = false;
|
||||||
|
writeahead = 8192;
|
||||||
|
chunksize = 1024;
|
||||||
|
|
||||||
/* Open the device. */
|
/* Open the device. */
|
||||||
int err;
|
int err;
|
||||||
@@ -237,7 +244,6 @@ Alsa9Buf::Alsa9Buf( hw hardware, int channels_ )
|
|||||||
}
|
}
|
||||||
|
|
||||||
SetSWParams();
|
SetSWParams();
|
||||||
total_frames = dsnd_pcm_avail_update(pcm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Alsa9Buf::~Alsa9Buf()
|
Alsa9Buf::~Alsa9Buf()
|
||||||
@@ -249,17 +255,15 @@ Alsa9Buf::~Alsa9Buf()
|
|||||||
/* Don't fill the buffer any more than than "writeahead" frames. Prefer to
|
/* Don't fill the buffer any more than than "writeahead" frames. Prefer to
|
||||||
* write "chunksize" frames at a time. (These numbers are hints; if the
|
* write "chunksize" frames at a time. (These numbers are hints; if the
|
||||||
* hardware parameters require it, they can be ignored.) */
|
* hardware parameters require it, they can be ignored.) */
|
||||||
int Alsa9Buf::GetNumFramesToFill( snd_pcm_sframes_t writeahead, snd_pcm_sframes_t chunksize )
|
int Alsa9Buf::GetNumFramesToFill()
|
||||||
{
|
{
|
||||||
/* We have to write at least xfer_align bytes. */
|
|
||||||
chunksize = max( chunksize, xfer_align );
|
|
||||||
|
|
||||||
/* Make sure we can write ahead at least two chunks. Otherwise, we'll only
|
/* Make sure we can write ahead at least two chunks. Otherwise, we'll only
|
||||||
* fill one chunk ahead, and underrun. */
|
* fill one chunk ahead, and underrun. */
|
||||||
writeahead = max( writeahead, chunksize*2 );
|
int ActualWriteahead = max( writeahead, chunksize*2 );
|
||||||
|
|
||||||
snd_pcm_sframes_t avail_frames = dsnd_pcm_avail_update(pcm);
|
snd_pcm_sframes_t avail_frames = dsnd_pcm_avail_update(pcm);
|
||||||
|
|
||||||
|
int total_frames = writeahead;
|
||||||
if( avail_frames > total_frames )
|
if( avail_frames > total_frames )
|
||||||
{
|
{
|
||||||
/* underrun */
|
/* underrun */
|
||||||
@@ -292,20 +296,26 @@ int Alsa9Buf::GetNumFramesToFill( snd_pcm_sframes_t writeahead, snd_pcm_sframes_
|
|||||||
const snd_pcm_sframes_t filled_frames = max( 0l, total_frames - avail_frames );
|
const snd_pcm_sframes_t filled_frames = max( 0l, total_frames - avail_frames );
|
||||||
|
|
||||||
/* Number of frames that don't have data, that are within the writeahead: */
|
/* Number of frames that don't have data, that are within the writeahead: */
|
||||||
snd_pcm_sframes_t unfilled_frames = clamp( writeahead - filled_frames, 0l, (snd_pcm_sframes_t)writeahead );
|
snd_pcm_sframes_t unfilled_frames = clamp( ActualWriteahead - filled_frames, 0l, (snd_pcm_sframes_t)ActualWriteahead );
|
||||||
|
|
||||||
/* If we have less than a chunk empty, don't fill at all. Otherwise, we'll
|
/* If we have less than a chunk empty, don't fill at all. Otherwise, we'll
|
||||||
* spend a lot of CPU filling in partial chunks, instead of waiting for some
|
* spend a lot of CPU filling in partial chunks, instead of waiting for some
|
||||||
* sound to play and then filling a whole chunk at once. */
|
* sound to play and then filling a whole chunk at once. */
|
||||||
if( unfilled_frames < chunksize )
|
if( unfilled_frames < (int) chunksize )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* We must always return a multiple of xfer_align. This might cause less than chunksize
|
return chunksize;
|
||||||
* to be returned; that's OK. */
|
}
|
||||||
snd_pcm_sframes_t frames_to_fill = chunksize;
|
|
||||||
frames_to_fill -= frames_to_fill % xfer_align;
|
|
||||||
|
|
||||||
return frames_to_fill;
|
bool Alsa9Buf::WaitUntilFramesCanBeFilled( int timeout_ms )
|
||||||
|
{
|
||||||
|
int err = snd_pcm_wait( pcm, timeout_ms );
|
||||||
|
/* EINTR is normal; don't warn. */
|
||||||
|
if( err == -EINTR )
|
||||||
|
return false;
|
||||||
|
ALSA_ASSERT("snd_pcm_wait");
|
||||||
|
|
||||||
|
return err == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Alsa9Buf::Write( const Sint16 *buffer, int frames )
|
void Alsa9Buf::Write( const Sint16 *buffer, int frames )
|
||||||
@@ -430,3 +440,19 @@ CString Alsa9Buf::GetHardwareID( CString name )
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Alsa9Buf::SetWriteahead( snd_pcm_sframes_t frames )
|
||||||
|
{
|
||||||
|
writeahead = frames;
|
||||||
|
SetHWParams();
|
||||||
|
SetSWParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Alsa9Buf::SetChunksize( snd_pcm_sframes_t frames )
|
||||||
|
{
|
||||||
|
chunksize = frames;
|
||||||
|
|
||||||
|
SetHWParams();
|
||||||
|
SetSWParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ private:
|
|||||||
int64_t last_cursor_pos;
|
int64_t last_cursor_pos;
|
||||||
bool samplerate_set_explicitly;
|
bool samplerate_set_explicitly;
|
||||||
|
|
||||||
snd_pcm_sframes_t total_frames;
|
snd_pcm_uframes_t writeahead, chunksize;
|
||||||
snd_pcm_uframes_t xfer_align;
|
|
||||||
|
|
||||||
snd_pcm_t *pcm;
|
snd_pcm_t *pcm;
|
||||||
|
|
||||||
@@ -37,7 +36,8 @@ public:
|
|||||||
Alsa9Buf( hw hardware, int channels );
|
Alsa9Buf( hw hardware, int channels );
|
||||||
~Alsa9Buf();
|
~Alsa9Buf();
|
||||||
|
|
||||||
int GetNumFramesToFill( snd_pcm_sframes_t writeahead, snd_pcm_sframes_t chunksize );
|
int GetNumFramesToFill();
|
||||||
|
bool WaitUntilFramesCanBeFilled( int timeout_ms );
|
||||||
void Write( const Sint16 *buffer, int frames );
|
void Write( const Sint16 *buffer, int frames );
|
||||||
unsigned FindSampleRate( unsigned rate );
|
unsigned FindSampleRate( unsigned rate );
|
||||||
|
|
||||||
@@ -47,6 +47,9 @@ public:
|
|||||||
void SetSampleRate(int hz);
|
void SetSampleRate(int hz);
|
||||||
int GetSampleRate() const { return samplerate; }
|
int GetSampleRate() const { return samplerate; }
|
||||||
|
|
||||||
|
void SetWriteahead( snd_pcm_sframes_t frames );
|
||||||
|
void SetChunksize( snd_pcm_sframes_t frames );
|
||||||
|
|
||||||
int64_t GetPosition() const;
|
int64_t GetPosition() const;
|
||||||
int64_t GetPlayPos() const { return last_cursor_pos; }
|
int64_t GetPlayPos() const { return last_cursor_pos; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ void RageSound_ALSA9::MixerThread()
|
|||||||
continue; /* inactive */
|
continue; /* inactive */
|
||||||
|
|
||||||
bool bEOF = false;
|
bool bEOF = false;
|
||||||
while( !shutdown && stream_pool[i]->GetData(false, bEOF) && !bEOF )
|
while( !shutdown && stream_pool[i]->GetData(bEOF) && !bEOF )
|
||||||
;
|
;
|
||||||
|
|
||||||
if( bEOF )
|
if( bEOF )
|
||||||
@@ -68,8 +68,6 @@ void RageSound_ALSA9::MixerThread()
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
int ps = stream_pool[i]->pcm->GetPosition();
|
int ps = stream_pool[i]->pcm->GetPosition();
|
||||||
LOG->Trace("fl #%i: pos %i to %i",
|
|
||||||
i, ps, (int) stream_pool[i]->flush_pos);
|
|
||||||
if( ps < stream_pool[i]->flush_pos )
|
if( ps < stream_pool[i]->flush_pos )
|
||||||
continue; /* stopping but still flushing */
|
continue; /* stopping but still flushing */
|
||||||
|
|
||||||
@@ -86,11 +84,11 @@ RageSound_ALSA9::stream::~stream()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the number of frames processed */
|
/* Returns the number of frames processed */
|
||||||
bool RageSound_ALSA9::stream::GetData( bool init, bool &bEOF )
|
bool RageSound_ALSA9::stream::GetData( bool &bEOF )
|
||||||
{
|
{
|
||||||
bEOF = false;
|
bEOF = false;
|
||||||
|
|
||||||
int frames_to_fill = pcm->GetNumFramesToFill( max_writeahead, init? max_writeahead:chunksize );
|
int frames_to_fill = pcm->GetNumFramesToFill();
|
||||||
if( frames_to_fill < chunksize )
|
if( frames_to_fill < chunksize )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -181,11 +179,15 @@ void RageSound_ALSA9::StartMixing(RageSoundBase *snd)
|
|||||||
/* Give the stream to the playing sound and remove it from the pool. */
|
/* Give the stream to the playing sound and remove it from the pool. */
|
||||||
stream_pool[i]->snd = snd;
|
stream_pool[i]->snd = snd;
|
||||||
stream_pool[i]->pcm->SetSampleRate( snd->GetSampleRate() );
|
stream_pool[i]->pcm->SetSampleRate( snd->GetSampleRate() );
|
||||||
|
stream_pool[i]->pcm->SetChunksize( chunksize );
|
||||||
|
stream_pool[i]->pcm->SetWriteahead( max_writeahead );
|
||||||
stream_pool[i]->start_time = snd->GetStartTime();
|
stream_pool[i]->start_time = snd->GetStartTime();
|
||||||
|
|
||||||
/* Pre-buffer the stream, and start it immediately. */
|
/* Pre-buffer the stream, and start it immediately. */
|
||||||
bool bEOF;
|
bool bEOF;
|
||||||
stream_pool[i]->GetData( true, bEOF );
|
while( stream_pool[i]->GetData(bEOF) && !bEOF )
|
||||||
|
;
|
||||||
|
|
||||||
stream_pool[i]->pcm->Play();
|
stream_pool[i]->pcm->Play();
|
||||||
|
|
||||||
/* If bEOF is true, we actually finished the whole file in the prebuffering
|
/* If bEOF is true, we actually finished the whole file in the prebuffering
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ private:
|
|||||||
|
|
||||||
int64_t flush_pos; /* state == STOPPING only */
|
int64_t flush_pos; /* state == STOPPING only */
|
||||||
|
|
||||||
bool GetData( bool init, bool &bEOF );
|
bool GetData( bool &bEOF );
|
||||||
|
|
||||||
stream() { pcm = NULL; snd = NULL; state=INACTIVE; }
|
stream() { pcm = NULL; snd = NULL; state=INACTIVE; }
|
||||||
~stream();
|
~stream();
|
||||||
|
|||||||
@@ -41,28 +41,19 @@ void RageSound_ALSA9_Software::MixerThread()
|
|||||||
|
|
||||||
setpriority( PRIO_PROCESS, 0, -15 );
|
setpriority( PRIO_PROCESS, 0, -15 );
|
||||||
|
|
||||||
// RageTimer UnderrunTest;
|
|
||||||
while(!shutdown)
|
while(!shutdown)
|
||||||
{
|
{
|
||||||
while( !shutdown && GetData() )
|
while( !shutdown && GetData() )
|
||||||
;
|
;
|
||||||
const float delay_ms = 1000 * float(max_writeahead) / samplerate;
|
|
||||||
SDL_Delay( int(delay_ms) / 4 );
|
|
||||||
|
|
||||||
// if( UnderrunTest.PeekDeltaTime() > 10 )
|
pcm->WaitUntilFramesCanBeFilled( 100 );
|
||||||
// {
|
|
||||||
// UnderrunTest.GetDeltaTime();
|
|
||||||
// SDL_Delay( 250 );
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the number of frames processed */
|
/* Returns the number of frames processed */
|
||||||
bool RageSound_ALSA9_Software::GetData()
|
bool RageSound_ALSA9_Software::GetData()
|
||||||
{
|
{
|
||||||
const int chunksize = max_writeahead / num_chunks;
|
const int frames_to_fill = pcm->GetNumFramesToFill();
|
||||||
|
|
||||||
const int frames_to_fill = pcm->GetNumFramesToFill( max_writeahead, chunksize );
|
|
||||||
if( frames_to_fill <= 0 )
|
if( frames_to_fill <= 0 )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -116,6 +107,9 @@ try {
|
|||||||
pcm->SetSampleRate( samplerate );
|
pcm->SetSampleRate( samplerate );
|
||||||
LOG->Info( "ALSA: Software mixing at %ihz", samplerate );
|
LOG->Info( "ALSA: Software mixing at %ihz", samplerate );
|
||||||
|
|
||||||
|
pcm->SetWriteahead( max_writeahead );
|
||||||
|
pcm->SetChunksize( max_writeahead / num_chunks );
|
||||||
|
|
||||||
StartDecodeThread();
|
StartDecodeThread();
|
||||||
|
|
||||||
MixingThread.SetName( "RageSound_ALSA9_Software" );
|
MixingThread.SetName( "RageSound_ALSA9_Software" );
|
||||||
|
|||||||
Reference in New Issue
Block a user