diff --git a/rpcs3/Emu/Audio/AudioBackend.h b/rpcs3/Emu/Audio/AudioBackend.h index ad8fed2eaf..43a8adc23f 100644 --- a/rpcs3/Emu/Audio/AudioBackend.h +++ b/rpcs3/Emu/Audio/AudioBackend.h @@ -8,7 +8,6 @@ enum : u32 DEFAULT_AUDIO_SAMPLING_RATE = 48000, MAX_AUDIO_BUFFERS = 64, AUDIO_BUFFER_SAMPLES = 256, - AUDIO_MIN_LATENCY = 512, AUDIO_MAX_CHANNELS = 8, }; diff --git a/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp b/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp index 4faf5dc1b8..94a535a141 100644 --- a/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp +++ b/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp @@ -69,6 +69,7 @@ void CubebBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChanne m_sampling_rate = freq; m_sample_size = sample_size; m_channels = ch_cnt; + full_sample_size = get_channels() * get_sample_size(); cubeb_stream_params stream_param{}; stream_param.format = get_convert_to_s16() ? CUBEB_SAMPLE_S16NE : CUBEB_SAMPLE_FLOAT32NE; @@ -93,7 +94,8 @@ void CubebBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChanne Cubeb.error("cubeb_get_min_latency() failed: 0x%08x", err); } - if (int err = cubeb_stream_init(m_ctx, &m_stream, "Main stream", nullptr, nullptr, nullptr, &stream_param, std::max(AUDIO_MIN_LATENCY, min_latency), data_cb, state_cb, this)) + const u32 stream_latency = std::max(static_cast(AUDIO_MIN_LATENCY * get_sampling_rate()), min_latency); + if (int err = cubeb_stream_init(m_ctx, &m_stream, "Main stream", nullptr, nullptr, nullptr, &stream_param, stream_latency, data_cb, state_cb, this)) { m_stream = nullptr; Cubeb.error("cubeb_stream_init() failed: 0x%08x", err); @@ -110,6 +112,11 @@ void CubebBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChanne { Cubeb.error("cubeb_stream_set_volume() failed: 0x%08x", err); } + + if (int err = cubeb_stream_start(m_stream)) + { + Cubeb.error("cubeb_stream_start() failed: 0x%08x", err); + } } void CubebBackend::CloseUnlocked() @@ -125,7 +132,7 @@ void CubebBackend::CloseUnlocked() m_playing = false; m_stream = nullptr; - memset(m_last_sample, 0, sizeof(m_last_sample)); + m_last_sample.fill(0); } void CubebBackend::Close() @@ -138,24 +145,15 @@ void CubebBackend::Play() { if (m_playing) return; - if (int err = cubeb_stream_start(m_stream)) - { - Cubeb.error("cubeb_stream_start() failed: 0x%08x", err); - } - std::lock_guard lock(m_cb_mutex); m_playing = true; } void CubebBackend::Pause() { - if (int err = cubeb_stream_stop(m_stream)) - { - Cubeb.error("cubeb_stream_stop() failed: 0x%08x", err); - } - std::lock_guard lock(m_cb_mutex); m_playing = false; + m_last_sample.fill(0); } bool CubebBackend::IsPlaying() @@ -171,18 +169,13 @@ void CubebBackend::SetWriteCallback(std::function cb) f64 CubebBackend::GetCallbackFrameLen() { - cubeb_stream_params stream_param{}; - stream_param.format = get_convert_to_s16() ? CUBEB_SAMPLE_S16NE : CUBEB_SAMPLE_FLOAT32NE; - stream_param.rate = get_sampling_rate(); - stream_param.channels = get_channels(); - - u32 min_latency{}; - if (int err = cubeb_get_min_latency(m_ctx, &stream_param, &min_latency)) + u32 stream_latency{}; + if (int err = cubeb_stream_get_latency(m_stream, &stream_latency)) { - Cubeb.error("cubeb_get_min_latency() failed: 0x%08x", err); + Cubeb.error("cubeb_stream_get_latency() failed: 0x%08x", err); } - return static_cast(std::max(AUDIO_MIN_LATENCY, min_latency)) / get_sampling_rate(); + return std::max(AUDIO_MIN_LATENCY, static_cast(stream_latency) / get_sampling_rate()); } long CubebBackend::data_cb(cubeb_stream* /* stream */, void* user_ptr, void const* /* input_buffer */, void* output_buffer, long nframes) @@ -192,21 +185,27 @@ long CubebBackend::data_cb(cubeb_stream* /* stream */, void* user_ptr, void cons if (nframes && lock.try_lock() && cubeb->m_write_callback && cubeb->m_playing) { - const u32 sample_size = cubeb->get_sample_size() * cubeb->get_channels(); - const u32 bytes_req = nframes * cubeb->get_sample_size() * cubeb->get_channels(); + const u32 sample_size = cubeb->full_sample_size.observe(); + const u32 bytes_req = nframes * sample_size; u32 written = std::min(cubeb->m_write_callback(bytes_req, output_buffer), bytes_req); written -= written % sample_size; if (written >= sample_size) { - memcpy(cubeb->m_last_sample, static_cast(output_buffer) + written - sample_size, sample_size); + memcpy(cubeb->m_last_sample.data(), static_cast(output_buffer) + written - sample_size, sample_size); } for (u32 i = written; i < bytes_req; i += sample_size) { - memcpy(static_cast(output_buffer) + i, cubeb->m_last_sample, sample_size); + memcpy(static_cast(output_buffer) + i, cubeb->m_last_sample.data(), sample_size); } } + else + { + // Stream parameters are modified only after stream_destroy. stream_destroy will return + // only after this callback returns, so it's safe to access full_sample_size here. + memset(output_buffer, 0, nframes * cubeb->full_sample_size.observe()); + } return nframes; } diff --git a/rpcs3/Emu/Audio/Cubeb/CubebBackend.h b/rpcs3/Emu/Audio/Cubeb/CubebBackend.h index c1be026ef2..cebe784373 100644 --- a/rpcs3/Emu/Audio/Cubeb/CubebBackend.h +++ b/rpcs3/Emu/Audio/Cubeb/CubebBackend.h @@ -32,6 +32,8 @@ public: bool IsPlaying() override; private: + static constexpr f64 AUDIO_MIN_LATENCY = 512.0 / 48000; // 10ms + cubeb* m_ctx = nullptr; cubeb_stream* m_stream = nullptr; #ifdef _WIN32 @@ -40,7 +42,8 @@ private: shared_mutex m_cb_mutex{}; std::function m_write_callback{}; - u8 m_last_sample[sizeof(float) * static_cast(AudioChannelCnt::SURROUND_7_1)]{}; + std::array(AudioChannelCnt::SURROUND_7_1)> m_last_sample{}; + atomic_t full_sample_size = 0; bool m_playing = false; atomic_t m_reset_req = false; diff --git a/rpcs3/Emu/Audio/FAudio/FAudioBackend.cpp b/rpcs3/Emu/Audio/FAudio/FAudioBackend.cpp index 5e07400bbe..a50d4bc733 100644 --- a/rpcs3/Emu/Audio/FAudio/FAudioBackend.cpp +++ b/rpcs3/Emu/Audio/FAudio/FAudioBackend.cpp @@ -102,6 +102,7 @@ void FAudioBackend::Pause() std::lock_guard lock(m_cb_mutex); m_playing = false; + m_last_sample.fill(0); } void FAudioBackend::CloseUnlocked() @@ -119,7 +120,7 @@ void FAudioBackend::CloseUnlocked() m_source_voice = nullptr; m_data_buf = nullptr; m_data_buf_len = 0; - memset(m_last_sample, 0, sizeof(m_last_sample)); + m_last_sample.fill(0); } void FAudioBackend::Close() @@ -233,12 +234,12 @@ void FAudioBackend::OnVoiceProcessingPassStart_func(FAudioVoiceCallback *cb_obj, if (written >= sample_size) { - memcpy(faudio->m_last_sample, faudio->m_data_buf.get() + written - sample_size, sample_size); + memcpy(faudio->m_last_sample.data(), faudio->m_data_buf.get() + written - sample_size, sample_size); } for (u32 i = written; i < BytesRequired; i += sample_size) { - memcpy(faudio->m_data_buf.get() + i, faudio->m_last_sample, sample_size); + memcpy(faudio->m_data_buf.get() + i, faudio->m_last_sample.data(), sample_size); } FAudioBuffer buffer{}; diff --git a/rpcs3/Emu/Audio/FAudio/FAudioBackend.h b/rpcs3/Emu/Audio/FAudio/FAudioBackend.h index 07c5e8b205..91011d8811 100644 --- a/rpcs3/Emu/Audio/FAudio/FAudioBackend.h +++ b/rpcs3/Emu/Audio/FAudio/FAudioBackend.h @@ -45,7 +45,7 @@ private: std::function m_write_callback{}; std::unique_ptr m_data_buf{}; u64 m_data_buf_len = 0; - u8 m_last_sample[sizeof(float) * static_cast(AudioChannelCnt::SURROUND_7_1)]{}; + std::array(AudioChannelCnt::SURROUND_7_1)> m_last_sample{}; bool m_playing = false; atomic_t m_reset_req = false; diff --git a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp index 3ecfe43362..76a5fbf169 100644 --- a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp +++ b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp @@ -124,7 +124,7 @@ void XAudio2Backend::CloseUnlocked() m_playing = false; m_data_buf = nullptr; m_data_buf_len = 0; - memset(m_last_sample, 0, sizeof(m_last_sample)); + m_last_sample.fill(0); } void XAudio2Backend::Close() @@ -156,6 +156,7 @@ void XAudio2Backend::Pause() std::lock_guard lock(m_cb_mutex); m_playing = false; + m_last_sample.fill(0); } void XAudio2Backend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) @@ -261,12 +262,12 @@ void XAudio2Backend::OnVoiceProcessingPassStart(UINT32 BytesRequired) if (written >= sample_size) { - memcpy(m_last_sample, m_data_buf.get() + written - sample_size, sample_size); + memcpy(m_last_sample.data(), m_data_buf.get() + written - sample_size, sample_size); } for (u32 i = written; i < BytesRequired; i += sample_size) { - memcpy(m_data_buf.get() + i, m_last_sample, sample_size); + memcpy(m_data_buf.get() + i, m_last_sample.data(), sample_size); } XAUDIO2_BUFFER buffer{}; diff --git a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h index acd916dac2..6be3bc70c4 100644 --- a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h +++ b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h @@ -48,7 +48,7 @@ private: std::function m_write_callback{}; std::unique_ptr m_data_buf{}; u64 m_data_buf_len = 0; - u8 m_last_sample[sizeof(float) * static_cast(AudioChannelCnt::SURROUND_7_1)]{}; + std::array(AudioChannelCnt::SURROUND_7_1)> m_last_sample{}; bool m_playing = false; atomic_t m_reset_req = false;