diff --git a/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.cpp b/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.cpp index 906b5a2e9e..7db965c3e3 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.cpp +++ b/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.cpp @@ -66,25 +66,52 @@ bool RageSound_ALSA9::stream::GetData(bool init) static Sint16 *buf = NULL; if ( !buf ) buf = new Sint16[max_writeahead*samples_per_frame]; + char *cbuf = (char*) buf; - unsigned len = frames_to_fill*bytes_per_frame; + const int play_pos = pcm->GetPlayPos(); + const int cur_play_pos = pcm->GetPosition(); + + int len = frames_to_fill*bytes_per_frame; /* It might be INACTIVE, when we're prebuffering. We just don't want to * fill anything in STOPPING; in that case, we just clear the audio buffer. */ if( state != STOPPING ) { - const unsigned got = snd->GetPCM( (char *) buf, len, pcm->GetPlayPos() ); + int bytes_read = 0; + int bytes_left = len; - if( got < len ) + /* Does the sound have a start time? */ + if( !start_time.IsZero() ) + { + /* If the sound is supposed to start at a time past this buffer, insert silence. */ + const int iFramesUntilThisBuffer = play_pos - cur_play_pos; + const float fSecondsBeforeStart = -start_time.Ago(); + const int iFramesBeforeStart = int(fSecondsBeforeStart * pcm->GetSampleRate()); + const int iSilentFramesInThisBuffer = iFramesBeforeStart-iFramesUntilThisBuffer; + const int iSilentBytesInThisBuffer = clamp( iSilentFramesInThisBuffer * bytes_per_frame, 0, bytes_left ); + + memset( cbuf+bytes_read, 0, iSilentBytesInThisBuffer ); + bytes_read += iSilentBytesInThisBuffer; + bytes_left -= iSilentBytesInThisBuffer; + + if( !iSilentBytesInThisBuffer ) + start_time.SetZero(); + } + + int got = snd->GetPCM( cbuf+bytes_read, len-bytes_read, play_pos + (bytes_read/bytes_per_frame) ); + bytes_read += got; + bytes_left -= got; + + if( bytes_left > 0 ) { /* Fill the remainder of the buffer with silence. */ - memset( buf+got, 0, len-got ); + memset( cbuf+bytes_read, 0, bytes_left ); /* STOPPING tells the mixer thread to release the stream once str->flush_bufs * buffers have been flushed. */ state = STOPPING; /* Flush two buffers worth of data. */ - flush_pos = pcm->GetPlayPos(); + flush_pos = pcm->GetPlayPos() + (bytes_read / bytes_per_frame); } } else { /* Silence the buffer. */ @@ -121,6 +148,7 @@ void RageSound_ALSA9::StartMixing(RageSound *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]->start_time = snd->GetStartTime(); /* Pre-buffer the stream. */ stream_pool[i]->GetData(true); diff --git a/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.h b/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.h index 17b9cf9ccf..a557d86084 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.h +++ b/stepmania/src/arch/Sound/RageSoundDriver_ALSA9.h @@ -18,6 +18,7 @@ private: /* Sound object that's playing on this stream, or NULL if this * channel is available: */ RageSound *snd; + RageTimer start_time; enum { INACTIVE, diff --git a/stepmania/src/arch/Sound/RageSoundDriver_ALSA9_Software.h b/stepmania/src/arch/Sound/RageSoundDriver_ALSA9_Software.h index 332477e1c2..ec9d93a69d 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_ALSA9_Software.h +++ b/stepmania/src/arch/Sound/RageSoundDriver_ALSA9_Software.h @@ -13,6 +13,7 @@ private: struct sound { RageSound *snd; + RageTimer start_time; bool stopping; int flush_pos; /* state == STOPPING only */ sound() { snd = NULL; stopping=false; }