From 42fb06d0f1c624d72f98539970117e38025ad50d Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 26 Dec 2024 23:29:03 -0800 Subject: [PATCH] cubeb_audio: Replace output yield loop with condvar. --- src/core/libraries/audio/audioout.cpp | 13 ++++++----- src/core/libraries/audio/audioout.h | 12 ++++++---- src/core/libraries/audio/cubeb_audio.cpp | 29 ++++++++++++------------ 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/core/libraries/audio/audioout.cpp b/src/core/libraries/audio/audioout.cpp index 33e1d3302..89ea1d3f5 100644 --- a/src/core/libraries/audio/audioout.cpp +++ b/src/core/libraries/audio/audioout.cpp @@ -93,7 +93,7 @@ static bool IsFormatFloat(const OrbisAudioOutParamFormat format) { } } -static int GetFormatNumChannels(const OrbisAudioOutParamFormat format) { +static u8 GetFormatNumChannels(const OrbisAudioOutParamFormat format) { switch (format) { case OrbisAudioOutParamFormat::S16Mono: case OrbisAudioOutParamFormat::FloatMono: @@ -403,12 +403,14 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id, } port->type = port_type; - port->samples_num = length; - port->freq = sample_rate; port->format = format; port->is_float = IsFormatFloat(format); - port->channels_num = GetFormatNumChannels(format); port->sample_size = GetFormatSampleSize(format); + port->channels_num = GetFormatNumChannels(format); + port->samples_num = length; + port->frame_size = port->sample_size * port->channels_num; + port->buffer_size = port->frame_size * port->samples_num; + port->freq = sample_rate; port->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB); port->impl = audio->Open(*port); @@ -434,8 +436,7 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr) { return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; } - const size_t data_size = port.samples_num * port.sample_size * port.channels_num; - port.impl->Output(ptr, data_size); + port.impl->Output(ptr, port.buffer_size); return ORBIS_OK; } diff --git a/src/core/libraries/audio/audioout.h b/src/core/libraries/audio/audioout.h index 3471315ba..58c77db99 100644 --- a/src/core/libraries/audio/audioout.h +++ b/src/core/libraries/audio/audioout.h @@ -62,14 +62,16 @@ struct OrbisAudioOutPortState { struct PortOut { std::unique_ptr impl{}; - u32 samples_num; - u32 freq; - OrbisAudioOutParamFormat format; OrbisAudioOutPort type; - int channels_num; + OrbisAudioOutParamFormat format; bool is_float; - std::array volume; u8 sample_size; + u8 channels_num; + u32 samples_num; + u32 frame_size; + u32 buffer_size; + u32 freq; + std::array volume; }; int PS4_SYSV_ABI sceAudioOutDeviceIdOpen(); diff --git a/src/core/libraries/audio/cubeb_audio.cpp b/src/core/libraries/audio/cubeb_audio.cpp index b8058f295..ca0af6b34 100644 --- a/src/core/libraries/audio/cubeb_audio.cpp +++ b/src/core/libraries/audio/cubeb_audio.cpp @@ -1,7 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include +#include +#include #include #include "common/assert.h" @@ -16,8 +17,7 @@ constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buff class CubebPortBackend : public PortBackend { public: CubebPortBackend(cubeb* ctx, const PortOut& port) - : frame_size(port.channels_num * port.sample_size), - buffer(static_cast(port.samples_num * frame_size) * 4) { + : frame_size(port.frame_size), buffer(static_cast(port.buffer_size) * 4) { if (!ctx) { return; } @@ -36,7 +36,7 @@ public: cubeb_stream_params stream_params = { .format = port.is_float ? CUBEB_SAMPLE_FLOAT32LE : CUBEB_SAMPLE_S16LE, .rate = port.freq, - .channels = static_cast(port.channels_num), + .channels = port.channels_num, .layout = get_channel_layout(), .prefs = CUBEB_STREAM_PREF_NONE, }; @@ -75,16 +75,10 @@ public: void Output(void* ptr, size_t size) override { auto* data = static_cast(ptr); - while (size > 0) { - const auto queued = buffer.enqueue(data, static_cast(size)); - size -= queued; - data += queued; - if (size > 0) { - // If the data is too large for the ring buffer, yield execution and give it a - // chance to drain. - std::this_thread::yield(); - } - } + + std::unique_lock lock{buffer_mutex}; + buffer_cv.wait(lock, [&] { return buffer.available_write() >= size; }); + buffer.enqueue(data, static_cast(size)); } void SetVolume(const std::array& ch_volumes) override { @@ -106,7 +100,12 @@ private: auto* stream_data = static_cast(user_data); const auto out_data = static_cast(out); const auto requested_size = static_cast(num_frames * stream_data->frame_size); + + std::unique_lock lock{stream_data->buffer_mutex}; const auto dequeued_size = stream_data->buffer.dequeue(out_data, requested_size); + lock.unlock(); + stream_data->buffer_cv.notify_one(); + if (dequeued_size < requested_size) { // Need to fill remaining space with silence. std::memset(out_data + dequeued_size, 0, requested_size - dequeued_size); @@ -133,6 +132,8 @@ private: size_t frame_size; ring_buffer_base buffer; + std::mutex buffer_mutex; + std::condition_variable buffer_cv; cubeb_stream* stream{}; };