void RageSoundDriver::StartMixing( RageSoundBase *pSound ) { /* Lock available m_Sounds[], and reserve a slot. */ m_SoundListMutex.Lock(); unsigned i; for( i = 0; i < ARRAYLEN(m_Sounds); ++i ) if( m_Sounds[i].m_State == Sound::AVAILABLE ) break; if( i == ARRAYLEN(m_Sounds) ) { m_SoundListMutex.Unlock(); return; } Sound &s = m_Sounds[i]; s.m_State = Sound::BUFFERING; /* We've reserved our slot; we can safely unlock now. Don't hold onto it longer * than needed, since prebuffering might take some time. */ m_SoundListMutex.Unlock(); s.m_pSound = pSound; s.m_StartTime = pSound->GetStartTime(); s.m_Buffer.clear(); /* Initialize the sound buffer. */ int BufferSize = frames_to_buffer; s.Allocate( BufferSize ); // LOG->Trace("StartMixing(%s) (%p)", s.m_pSound->GetLoadedFilePath().c_str(), s.m_pSound ); /* Prebuffer some frames before changing the sound to PLAYING. */ while( s.m_Buffer.num_writable() ) { // LOG->Trace("StartMixing: (#%i) buffering %i (%i writable) (%p)", i, (int) frames_to_buffer, s.buffer.num_writable(), s.m_pSound ); int iWrote = GetDataForSound( s ); if( iWrote < 0 ) break; } s.m_State = Sound::PLAYING; // LOG->Trace("StartMixing: (#%i) finished prebuffering(%s) (%p)", i, s.m_pSound->GetLoadedFilePath().c_str(), s.m_pSound ); }
void RageSoundDriver::DecodeThread() { SetupDecodingThread(); while( !m_bShutdownDecodeThread ) { /* Fill each playing sound, round-robin. */ { int iSampleRate = GetSampleRate(); ASSERT_M( iSampleRate > 0, ssprintf("%i", iSampleRate) ); int iUsecs = 1000000*chunksize() / iSampleRate; usleep( iUsecs ); } LockMut( m_Mutex ); // LOG->Trace("begin mix"); for( unsigned i = 0; i < ARRAYLEN(m_Sounds); ++i ) { if( m_Sounds[i].m_State != Sound::PLAYING ) continue; Sound *pSound = &m_Sounds[i]; CHECKPOINT_M("Processing the sound while buffers are available."); while( pSound->m_Buffer.num_writable() ) { int iWrote = GetDataForSound( *pSound ); if( iWrote == RageSoundReader::WOULD_BLOCK ) break; if( iWrote < 0 ) { /* This sound is finishing. */ pSound->m_State = Sound::STOPPING; break; // LOG->Trace("mixer: (#%i) eof (%p)", i, pSound->m_pSound ); } } } // LOG->Trace("end mix"); } }
void RageSound_Generic_Software::StartMixing( RageSoundBase *snd ) { /* Lock available sounds[], and reserve a slot. */ m_SoundListMutex.Lock(); unsigned i; for( i = 0; i < ARRAYSIZE(sounds); ++i ) if( sounds[i].available ) break; if( i == ARRAYSIZE(sounds) ) { m_SoundListMutex.Unlock(); return; } sound &s = sounds[i]; s.available = false; /* We've reserved our slot; we can safely unlock now. Don't hold onto it longer * than needed, since prebuffering might take some time. */ m_SoundListMutex.Unlock(); s.snd = snd; s.start_time = snd->GetStartTime(); s.sound_id = snd->GetID(); s.volume = snd->GetVolume(); s.buffer.clear(); /* Initialize the sound buffer. */ int BufferSize = frames_to_buffer; /* If a sound is streaming from disk, use a bigger buffer, so we don't underrun * the BGM if a drive seek takes too long. Note that in this case, we'll still * underrun any other sounds that are playing, even if they're preloaded. This * is less of a problem, since the music position isn't based on those. It could * be fixed by having two decoding threads; one for streaming sounds and one for * non-streaming sounds. */ if( s.snd->IsStreamingFromDisk() ) BufferSize = max( BufferSize, min_streaming_buffer_size ); s.Allocate( BufferSize ); // LOG->Trace("StartMixing(%s) (%p)", s.snd->GetLoadedFilePath().c_str(), s.snd ); /* Prebuffer some frames before changing the sound to PLAYING. */ bool ReachedEOF = false; int frames_filled = 0; while( !ReachedEOF && frames_filled < min_fill_frames && frames_filled < BufferSize ) { // LOG->Trace("StartMixing: (#%i) buffering %i (%i writable) (%p)", i, (int) frames_to_buffer, s.buffer.num_writable(), s.snd ); int wrote = GetDataForSound( s ); frames_filled += wrote; if( !wrote ) { // LOG->Trace("StartMixing: XXX hit EOF (%p)", s.snd ); ReachedEOF = true; } } /* If we hit EOF already, while prebuffering, then go right to STOPPING. */ s.state = ReachedEOF? sound::STOPPING: sound::PLAYING; // LOG->Trace("StartMixing: (#%i) finished prebuffering(%s) (%p)", i, s.snd->GetLoadedFilePath().c_str(), s.snd ); }
void RageSound_Generic_Software::DecodeThread() { /* SOUNDMAN will be set once RageSoundManager's ctor returns and * assigns it; we might get here before that happens, though. */ while( !SOUNDMAN && !shutdown_decode_thread ) usleep( 10000 ); SetupDecodingThread(); while( !shutdown_decode_thread ) { /* Fill each playing sound, round-robin. */ usleep( 1000000*chunksize() / GetSampleRate(0) ); LockMut( m_Mutex ); // LOG->Trace("begin mix"); for( unsigned i = 0; i < ARRAYSIZE(sounds); ++i ) { /* The volume can change while the sound is playing; update it. */ if( sounds[i].state == sound::PLAYING || sounds[i].state == sound::STOPPING ) sounds[i].volume = sounds[i].snd->GetVolume(); } /* * If a buffer is low on data, keep filling until it has a reasonable amount. * However, once beyond a certain threshold, clamp the rate at which we fill * it. For example, if the threshold is 4k frames, and we have a 32k frame * buffer, fill the buffer as fast as we can until it reaches 4k frames; but * beyond that point, only fill it at a rate relative to realtime (for example, * at 2x realtime). * * This allows a stream to have a large buffer, for higher reliability, without * causing major CPU bursts when the stream starts or underruns. (Filling 32k * takes more CPU than filling 4k frames, and may cause a gameplay skip.) */ for( unsigned i = 0; i < ARRAYSIZE(sounds); ++i ) { if( sounds[i].state != sound::PLAYING ) continue; sound *pSound = &sounds[i]; CHECKPOINT; int frames_filled = 0; while( pSound->buffer.num_writable() ) { /* If there are more than min_fill_frames available, check for * rate clamping. */ if( pSound->buffer.num_readable()*samples_per_block >= unsigned(min_fill_frames) ) { /* Don't write more than two chunks worth of data in one * iteration. Since we delay for one chunk period per loop, * this means we'll fill at no more than 2x realtime. */ if( frames_filled >= chunksize()*2 ) break; } int wrote = GetDataForSound( *pSound ); if( !wrote ) { /* This sound is finishing. */ pSound->state = sound::STOPPING; break; // LOG->Trace("mixer: (#%i) eof (%p)", i, pSound->snd ); } frames_filled += wrote; } } // LOG->Trace("end mix"); } }