diff --git a/stepmania/src/arch/Sound/ALSA9Functions.h b/stepmania/src/arch/Sound/ALSA9Functions.h index d6209949c9..f39fe845b3 100644 --- a/stepmania/src/arch/Sound/ALSA9Functions.h +++ b/stepmania/src/arch/Sound/ALSA9Functions.h @@ -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_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_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(snd_pcm_uframes_t, snd_pcm_status_get_avail, (const snd_pcm_status_t *obj)); 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_prepare, (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_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_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_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)); diff --git a/stepmania/src/arch/Sound/ALSA9Helpers.cpp b/stepmania/src/arch/Sound/ALSA9Helpers.cpp index c6c4e8d8a9..74a013a3a0 100644 --- a/stepmania/src/arch/Sound/ALSA9Helpers.cpp +++ b/stepmania/src/arch/Sound/ALSA9Helpers.cpp @@ -68,10 +68,18 @@ bool Alsa9Buf::SetHWParams() if( samplerate_set_explicitly && (int) rate != samplerate ) LOG->Warn("Alsa9Buf::SetHWParams: Couldn't get %ihz (got %ihz instead)", samplerate, rate); - snd_pcm_uframes_t buffersize = 1024*32; - err = dsnd_pcm_hw_params_set_buffer_size_near( pcm, hwparams, &buffersize ); + /* Set the buffersize to the writeahead, and then copy back the actual value + * we got. */ + err = dsnd_pcm_hw_params_set_buffer_size_near( pcm, hwparams, &writeahead ); 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 */ err = dsnd_pcm_hw_params( pcm, hwparams ); ALSA_CHECK("dsnd_pcm_hw_params"); @@ -85,20 +93,17 @@ bool Alsa9Buf::SetSWParams() dsnd_pcm_sw_params_alloca( &swparams ); dsnd_pcm_sw_params_current( pcm, swparams ); - int err = dsnd_pcm_sw_params_get_xfer_align( swparams, &xfer_align ); - ALSA_ASSERT("dsnd_pcm_sw_params_get_xfer_align"); + int err = dsnd_pcm_sw_params_set_xfer_align( pcm, swparams, 1 ); + ALSA_ASSERT("dsnd_pcm_sw_params_set_xfer_align"); - snd_pcm_uframes_t avail_min; - err = dsnd_pcm_sw_params_get_avail_min( swparams, &avail_min ); - ALSA_ASSERT("dsnd_pcm_sw_params_get_avail_min"); + /* chunksize has been set to the period size. Set avail_min to the period + * size, too, so poll() wakes up once per chunk. */ + 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, - * 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 + /* If this fails, we might have bound dsnd_pcm_sw_params_set_avail_min to * the old SW API. */ - ASSERT( err <= 0 ); +// ASSERT( err <= 0 ); /* Disable SND_PCM_STATE_XRUN. */ snd_pcm_uframes_t boundary = 0; @@ -220,6 +225,8 @@ Alsa9Buf::Alsa9Buf( hw hardware, int channels_ ) samplebits = 16; last_cursor_pos = 0; samplerate_set_explicitly = false; + writeahead = 8192; + chunksize = 1024; /* Open the device. */ int err; @@ -237,7 +244,6 @@ Alsa9Buf::Alsa9Buf( hw hardware, int channels_ ) } SetSWParams(); - total_frames = dsnd_pcm_avail_update(pcm); } Alsa9Buf::~Alsa9Buf() @@ -249,17 +255,15 @@ Alsa9Buf::~Alsa9Buf() /* 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 * 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 * 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); + int total_frames = writeahead; if( avail_frames > total_frames ) { /* 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 ); /* 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 * 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. */ - if( unfilled_frames < chunksize ) + if( unfilled_frames < (int) chunksize ) return 0; - /* We must always return a multiple of xfer_align. This might cause less than chunksize - * to be returned; that's OK. */ - snd_pcm_sframes_t frames_to_fill = chunksize; - frames_to_fill -= frames_to_fill % xfer_align; + return chunksize; +} - 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 ) @@ -430,3 +440,19 @@ CString Alsa9Buf::GetHardwareID( CString name ) 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(); +} + diff --git a/stepmania/src/arch/Sound/ALSA9Helpers.h b/stepmania/src/arch/Sound/ALSA9Helpers.h index aab02b8cb9..62f79a77c8 100644 --- a/stepmania/src/arch/Sound/ALSA9Helpers.h +++ b/stepmania/src/arch/Sound/ALSA9Helpers.h @@ -15,8 +15,7 @@ private: int64_t last_cursor_pos; bool samplerate_set_explicitly; - snd_pcm_sframes_t total_frames; - snd_pcm_uframes_t xfer_align; + snd_pcm_uframes_t writeahead, chunksize; snd_pcm_t *pcm; @@ -37,7 +36,8 @@ public: Alsa9Buf( hw hardware, int channels ); ~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 ); unsigned FindSampleRate( unsigned rate ); @@ -47,6 +47,9 @@ public: void SetSampleRate(int hz); 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 GetPlayPos() const { return last_cursor_pos; } }; diff --git a/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.cpp b/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.cpp index b15d670f51..2489758d5e 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.cpp +++ b/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.cpp @@ -46,7 +46,7 @@ void RageSound_ALSA9::MixerThread() continue; /* inactive */ bool bEOF = false; - while( !shutdown && stream_pool[i]->GetData(false, bEOF) && !bEOF ) + while( !shutdown && stream_pool[i]->GetData(bEOF) && !bEOF ) ; if( bEOF ) @@ -68,8 +68,6 @@ void RageSound_ALSA9::MixerThread() continue; 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 ) continue; /* stopping but still flushing */ @@ -86,11 +84,11 @@ RageSound_ALSA9::stream::~stream() } /* Returns the number of frames processed */ -bool RageSound_ALSA9::stream::GetData( bool init, bool &bEOF ) +bool RageSound_ALSA9::stream::GetData( bool &bEOF ) { 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 ) 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. */ stream_pool[i]->snd = snd; 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(); /* Pre-buffer the stream, and start it immediately. */ bool bEOF; - stream_pool[i]->GetData( true, bEOF ); + while( stream_pool[i]->GetData(bEOF) && !bEOF ) + ; + stream_pool[i]->pcm->Play(); /* If bEOF is true, we actually finished the whole file in the prebuffering diff --git a/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.h b/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.h index 7054571d7b..0c6cd5162c 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.h +++ b/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.h @@ -37,7 +37,7 @@ private: 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(); diff --git a/stepmania/src/arch/Sound/RageSoundDriver_ALSA9_Software.cpp b/stepmania/src/arch/Sound/RageSoundDriver_ALSA9_Software.cpp index 5bcec60959..24f83b57f6 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_ALSA9_Software.cpp +++ b/stepmania/src/arch/Sound/RageSoundDriver_ALSA9_Software.cpp @@ -41,28 +41,19 @@ void RageSound_ALSA9_Software::MixerThread() setpriority( PRIO_PROCESS, 0, -15 ); -// RageTimer UnderrunTest; while(!shutdown) { while( !shutdown && GetData() ) ; - const float delay_ms = 1000 * float(max_writeahead) / samplerate; - SDL_Delay( int(delay_ms) / 4 ); -// if( UnderrunTest.PeekDeltaTime() > 10 ) -// { -// UnderrunTest.GetDeltaTime(); -// SDL_Delay( 250 ); -// } + pcm->WaitUntilFramesCanBeFilled( 100 ); } } /* Returns the number of frames processed */ bool RageSound_ALSA9_Software::GetData() { - const int chunksize = max_writeahead / num_chunks; - - const int frames_to_fill = pcm->GetNumFramesToFill( max_writeahead, chunksize ); + const int frames_to_fill = pcm->GetNumFramesToFill(); if( frames_to_fill <= 0 ) return false; @@ -116,6 +107,9 @@ try { pcm->SetSampleRate( samplerate ); LOG->Info( "ALSA: Software mixing at %ihz", samplerate ); + pcm->SetWriteahead( max_writeahead ); + pcm->SetChunksize( max_writeahead / num_chunks ); + StartDecodeThread(); MixingThread.SetName( "RageSound_ALSA9_Software" );