diff --git a/src/arch/Sound/RageSoundDriver_WaveOut.cpp b/src/arch/Sound/RageSoundDriver_WaveOut.cpp index 997c99dfe8..46aee649f9 100644 --- a/src/arch/Sound/RageSoundDriver_WaveOut.cpp +++ b/src/arch/Sound/RageSoundDriver_WaveOut.cpp @@ -27,21 +27,15 @@ namespace { // To achieve this, we use 512 frames per block and determine the number // of blocks and buffer size in frames based on the sample rate. constexpr int kTargetLatency = 118; - - // CHANNELS should be renamed CHANNELS - constexpr int CHANNELS = 2; - - // CHUNKSIZE_FRAMES should be renamed kBlockSizeFrames - constexpr int CHUNKSIZE_FRAMES = 512; - - // BYTES_PER_FRAME should be renamed kBytesPerFrame - constexpr int BYTES_PER_FRAME = CHANNELS * 2; // 16 bit + constexpr int kChannels = 2; + constexpr int kBlockSizeFrames = 512; + constexpr int kBytesPerFrame = kChannels * 2; // 16 bit inline int CalculateNumBlocks( int sampleRate ) { return (sampleRate * kTargetLatency + - (1000 * CHUNKSIZE_FRAMES - 1)) / - (1000 * CHUNKSIZE_FRAMES); + (1000 * kBlockSizeFrames - 1)) / + (1000 * kBlockSizeFrames); } } // namespace @@ -69,42 +63,42 @@ void RageSoundDriver_WaveOut::MixerThread() if( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL) ) LOG->Warn( werr_ssprintf(GetLastError(), "Failed to set sound thread priority") ); - while( !m_bShutdown ) + while( !wo_shutdown_ ) { while( GetData() ) ; - WaitForSingleObject( m_hSoundEvent, 10 ); + WaitForSingleObject( soundevent_handle_, 10 ); } - waveOutReset( m_hWaveOut ); + waveOutReset( waveout_handle_ ); } bool RageSoundDriver_WaveOut::GetData() { /* Look for a free buffer. */ int b; - for( b = 0; b < NUM_CHUNKS; ++b ) - if( m_aBuffers[b].dwFlags & WHDR_DONE ) + for( b = 0; b < wo_num_blocks_; ++b ) + if( wo_buffers_[b].dwFlags & WHDR_DONE ) break; - if( b == NUM_CHUNKS ) + if( b == wo_num_blocks_ ) return false; /* Call the callback. */ - this->Mix( (int16_t *) m_aBuffers[b].lpData, CHUNKSIZE_FRAMES, m_iLastCursorPos, GetPosition() ); + this->Mix( (int16_t *) wo_buffers_[b].lpData, kBlockSizeFrames, wo_last_cursor_position_, GetPosition() ); - MMRESULT ret = waveOutWrite( m_hWaveOut, &m_aBuffers[b], sizeof(m_aBuffers[b]) ); + MMRESULT ret = waveOutWrite( waveout_handle_, &wo_buffers_[b], sizeof(wo_buffers_[b]) ); if( ret != MMSYSERR_NOERROR ) { Init(); - if (b_InitSuccess == false) + if (wo_init_success_ == false) { FAIL_M(wo_ssprintf(ret, "waveOutWrite failed")); } } - /* Increment m_iLastCursorPos. */ - m_iLastCursorPos += CHUNKSIZE_FRAMES; + /* Increment wo_last_cursor_position_. */ + wo_last_cursor_position_ += kBlockSizeFrames; return true; } @@ -119,7 +113,7 @@ int64_t RageSoundDriver_WaveOut::GetPosition() const { MMTIME tm; tm.wType = TIME_SAMPLES; - MMRESULT ret = waveOutGetPosition( m_hWaveOut, &tm, sizeof(tm) ); + MMRESULT ret = waveOutGetPosition( waveout_handle_, &tm, sizeof(tm) ); if( ret != MMSYSERR_NOERROR ) FAIL_M( wo_ssprintf(ret, "waveOutGetPosition failed") ); @@ -127,39 +121,39 @@ int64_t RageSoundDriver_WaveOut::GetPosition() const } RageSoundDriver_WaveOut::RageSoundDriver_WaveOut() - : m_hWaveOut(nullptr) - , m_hSoundEvent(CreateEvent(nullptr, false, true, nullptr)) - , m_iLastCursorPos(0) - , m_iSampleRate(0) - , m_bShutdown(false) - , b_InitSuccess(false) - , NUM_CHUNKS(1) - , BUFFERSIZE_FRAMES(0) - , CHUNKSIZE(0) - , m_aBuffers{} - , MixingThread() + : MixingThread() + , waveout_handle_(nullptr) + , soundevent_handle_(CreateEvent(nullptr, false, true, nullptr)) + , wo_buffers_{} + , wo_samplerate_(0) + , wo_shutdown_(false) + , wo_last_cursor_position_(0) + , wo_init_success_(false) + , wo_frames_per_block_(0) + , wo_num_blocks_(1) + , wo_blocksize_(0) { } RString RageSoundDriver_WaveOut::Init() { - b_InitSuccess = false; - m_iSampleRate = PREFSMAN->m_iSoundPreferredSampleRate; - if( m_iSampleRate == 0 ) + wo_init_success_ = false; + wo_samplerate_ = PREFSMAN->m_iSoundPreferredSampleRate; + if( wo_samplerate_ == 0 ) { - m_iSampleRate = kFallbackSampleRate; + wo_samplerate_ = kFallbackSampleRate; } - NUM_CHUNKS = CalculateNumBlocks( m_iSampleRate ); - BUFFERSIZE_FRAMES = CHUNKSIZE_FRAMES * NUM_CHUNKS; - CHUNKSIZE = CHUNKSIZE_FRAMES * BYTES_PER_FRAME; + wo_num_blocks_ = CalculateNumBlocks( wo_samplerate_ ); + + wo_frames_per_block_ = kBlockSizeFrames * wo_num_blocks_; + wo_blocksize_ = kBlockSizeFrames * kBytesPerFrame; WAVEFORMATEX fmt; fmt.wFormatTag = WAVE_FORMAT_PCM; - fmt.nChannels = CHANNELS; + fmt.nChannels = kChannels; fmt.cbSize = 0; - fmt.nSamplesPerSec = m_iSampleRate; - fmt.wBitsPerSample = 16; + fmt.nSamplesPerSec = wo_samplerate_; fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; @@ -178,7 +172,7 @@ RString RageSoundDriver_WaveOut::Init() MMRESULT ret = MMSYSERR_ERROR; for (UINT id : deviceIds) { - ret = waveOutOpen( &m_hWaveOut, id, &fmt, (DWORD_PTR) m_hSoundEvent, NULL, CALLBACK_EVENT ); + ret = waveOutOpen( &waveout_handle_, id, &fmt, (DWORD_PTR) soundevent_handle_, NULL, CALLBACK_EVENT ); if (ret == MMSYSERR_NOERROR) { break; } @@ -188,28 +182,28 @@ RString RageSoundDriver_WaveOut::Init() } - ZERO( m_aBuffers ); - for(int b = 0; b < NUM_CHUNKS; ++b) + ZERO( wo_buffers_ ); + for(int b = 0; b < wo_num_blocks_; ++b) { - m_aBuffers[b].dwBufferLength = CHUNKSIZE; - m_aBuffers[b].lpData = new char[CHUNKSIZE]; - ret = waveOutPrepareHeader( m_hWaveOut, &m_aBuffers[b], sizeof(m_aBuffers[b]) ); + wo_buffers_[b].dwBufferLength = wo_blocksize_; + wo_buffers_[b].lpData = new char[wo_blocksize_]; + ret = waveOutPrepareHeader( waveout_handle_, &wo_buffers_[b], sizeof(wo_buffers_[b]) ); if( ret != MMSYSERR_NOERROR ) return wo_ssprintf( ret, "waveOutPrepareHeader failed" ); - m_aBuffers[b].dwFlags |= WHDR_DONE; + wo_buffers_[b].dwFlags |= WHDR_DONE; } - LOG->Info( "WaveOut software mixing at %i hz", m_iSampleRate ); + LOG->Info( "WaveOut software mixing at %i hz", wo_samplerate_ ); /* We have a very large writeahead; make sure we have a large enough decode * buffer to recover cleanly from underruns. */ - SetDecodeBufferSize( BUFFERSIZE_FRAMES * 3/2 ); + SetDecodeBufferSize( wo_frames_per_block_ * 3/2 ); StartDecodeThread(); MixingThread.SetName( "Mixer thread" ); MixingThread.Create( MixerThread_start, this ); - b_InitSuccess = true; + wo_init_success_ = true; return RString(); } @@ -218,32 +212,32 @@ RageSoundDriver_WaveOut::~RageSoundDriver_WaveOut() /* Signal the mixing thread to quit. */ if( MixingThread.IsCreated() ) { - m_bShutdown = true; - SetEvent( m_hSoundEvent ); + wo_shutdown_ = true; + SetEvent( soundevent_handle_ ); LOG->Trace( "Shutting down mixer thread ..." ); MixingThread.Wait(); LOG->Trace( "Mixer thread shut down." ); } - if( m_hWaveOut != nullptr ) + if( waveout_handle_ != nullptr ) { - for( int b = 0; b < NUM_CHUNKS && m_aBuffers[b].lpData != nullptr; ++b ) + for( int b = 0; b < wo_num_blocks_ && wo_buffers_[b].lpData != nullptr; ++b ) { - waveOutUnprepareHeader( m_hWaveOut, &m_aBuffers[b], sizeof(m_aBuffers[b]) ); - delete [] m_aBuffers[b].lpData; + waveOutUnprepareHeader( waveout_handle_, &wo_buffers_[b], sizeof(wo_buffers_[b]) ); + delete [] wo_buffers_[b].lpData; } - waveOutClose( m_hWaveOut ); + waveOutClose( waveout_handle_ ); } - CloseHandle( m_hSoundEvent ); + CloseHandle( soundevent_handle_ ); } float RageSoundDriver_WaveOut::GetPlayLatency() const { /* If we have a 1000-byte buffer, and we fill 100 bytes at a time, we * almost always have between 900 and 1000 bytes filled; on average, 950. */ - return (BUFFERSIZE_FRAMES - CHUNKSIZE_FRAMES/2) * (1.0f / m_iSampleRate); + return (wo_frames_per_block_ - kBlockSizeFrames/2) * (1.0f / wo_samplerate_); } /* diff --git a/src/arch/Sound/RageSoundDriver_WaveOut.h b/src/arch/Sound/RageSoundDriver_WaveOut.h index 61632b15cd..4f45822da1 100644 --- a/src/arch/Sound/RageSoundDriver_WaveOut.h +++ b/src/arch/Sound/RageSoundDriver_WaveOut.h @@ -12,12 +12,19 @@ class RageSoundDriver_WaveOut: public RageSoundDriver { public: + // The size of wo_buffers_ is the -maximum- number of blocks we can have. + // We have to pre-allocate this size here. With the default target latency + // of 118 ms, 13 buffers is sufficient for both 44100 and 48000 Hz sample + // rates, but at least 20 buffers are needed if higher sample rates (such + // as 96000 Hz) are used, or the game will crash during initialization. + static const int kMaximumNumBlocks = 32; + RageSoundDriver_WaveOut(); ~RageSoundDriver_WaveOut(); RString Init(); int64_t GetPosition() const; float GetPlayLatency() const; - int GetSampleRate() const { return m_iSampleRate; } + int GetSampleRate() const { return wo_samplerate_; } private: static int MixerThread_start( void *p ); void MixerThread(); @@ -25,16 +32,16 @@ private: bool GetData(); void SetupDecodingThread(); - HWAVEOUT m_hWaveOut; - HANDLE m_hSoundEvent; - WAVEHDR m_aBuffers[32]; // Maximum of 32 output blocks (frame blocks) allowed. - int m_iSampleRate; - bool m_bShutdown; - int m_iLastCursorPos; - bool b_InitSuccess; - int NUM_CHUNKS; // TODO rename wo_num_blocks - int BUFFERSIZE_FRAMES; // TODO rename wo_num_frames_per_block - int CHUNKSIZE; // TODO rename wo_block_size + HWAVEOUT waveout_handle_; + HANDLE soundevent_handle_; + WAVEHDR wo_buffers_[kMaximumNumBlocks]; + int wo_samplerate_; + bool wo_shutdown_; + int wo_last_cursor_position_; + bool wo_init_success_; + int wo_frames_per_block_; + int wo_num_blocks_; + int wo_blocksize_; }; #endif