cubeb_audio: Replace output yield loop with condvar.

This commit is contained in:
squidbus 2024-12-26 23:29:03 -08:00
parent a4ef7771df
commit 42fb06d0f1
3 changed files with 29 additions and 25 deletions

View File

@ -93,7 +93,7 @@ static bool IsFormatFloat(const OrbisAudioOutParamFormat format) {
} }
} }
static int GetFormatNumChannels(const OrbisAudioOutParamFormat format) { static u8 GetFormatNumChannels(const OrbisAudioOutParamFormat format) {
switch (format) { switch (format) {
case OrbisAudioOutParamFormat::S16Mono: case OrbisAudioOutParamFormat::S16Mono:
case OrbisAudioOutParamFormat::FloatMono: case OrbisAudioOutParamFormat::FloatMono:
@ -403,12 +403,14 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
} }
port->type = port_type; port->type = port_type;
port->samples_num = length;
port->freq = sample_rate;
port->format = format; port->format = format;
port->is_float = IsFormatFloat(format); port->is_float = IsFormatFloat(format);
port->channels_num = GetFormatNumChannels(format);
port->sample_size = GetFormatSampleSize(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->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB);
port->impl = audio->Open(*port); 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; 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, port.buffer_size);
port.impl->Output(ptr, data_size);
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -62,14 +62,16 @@ struct OrbisAudioOutPortState {
struct PortOut { struct PortOut {
std::unique_ptr<PortBackend> impl{}; std::unique_ptr<PortBackend> impl{};
u32 samples_num;
u32 freq;
OrbisAudioOutParamFormat format;
OrbisAudioOutPort type; OrbisAudioOutPort type;
int channels_num; OrbisAudioOutParamFormat format;
bool is_float; bool is_float;
std::array<int, 8> volume;
u8 sample_size; u8 sample_size;
u8 channels_num;
u32 samples_num;
u32 frame_size;
u32 buffer_size;
u32 freq;
std::array<int, 8> volume;
}; };
int PS4_SYSV_ABI sceAudioOutDeviceIdOpen(); int PS4_SYSV_ABI sceAudioOutDeviceIdOpen();

View File

@ -1,7 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdarg> #include <condition_variable>
#include <mutex>
#include <cubeb/cubeb.h> #include <cubeb/cubeb.h>
#include "common/assert.h" #include "common/assert.h"
@ -16,8 +17,7 @@ constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buff
class CubebPortBackend : public PortBackend { class CubebPortBackend : public PortBackend {
public: public:
CubebPortBackend(cubeb* ctx, const PortOut& port) CubebPortBackend(cubeb* ctx, const PortOut& port)
: frame_size(port.channels_num * port.sample_size), : frame_size(port.frame_size), buffer(static_cast<int>(port.buffer_size) * 4) {
buffer(static_cast<int>(port.samples_num * frame_size) * 4) {
if (!ctx) { if (!ctx) {
return; return;
} }
@ -36,7 +36,7 @@ public:
cubeb_stream_params stream_params = { cubeb_stream_params stream_params = {
.format = port.is_float ? CUBEB_SAMPLE_FLOAT32LE : CUBEB_SAMPLE_S16LE, .format = port.is_float ? CUBEB_SAMPLE_FLOAT32LE : CUBEB_SAMPLE_S16LE,
.rate = port.freq, .rate = port.freq,
.channels = static_cast<u32>(port.channels_num), .channels = port.channels_num,
.layout = get_channel_layout(), .layout = get_channel_layout(),
.prefs = CUBEB_STREAM_PREF_NONE, .prefs = CUBEB_STREAM_PREF_NONE,
}; };
@ -75,16 +75,10 @@ public:
void Output(void* ptr, size_t size) override { void Output(void* ptr, size_t size) override {
auto* data = static_cast<u8*>(ptr); auto* data = static_cast<u8*>(ptr);
while (size > 0) {
const auto queued = buffer.enqueue(data, static_cast<int>(size)); std::unique_lock lock{buffer_mutex};
size -= queued; buffer_cv.wait(lock, [&] { return buffer.available_write() >= size; });
data += queued; buffer.enqueue(data, static_cast<int>(size));
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();
}
}
} }
void SetVolume(const std::array<int, 8>& ch_volumes) override { void SetVolume(const std::array<int, 8>& ch_volumes) override {
@ -106,7 +100,12 @@ private:
auto* stream_data = static_cast<CubebPortBackend*>(user_data); auto* stream_data = static_cast<CubebPortBackend*>(user_data);
const auto out_data = static_cast<u8*>(out); const auto out_data = static_cast<u8*>(out);
const auto requested_size = static_cast<int>(num_frames * stream_data->frame_size); const auto requested_size = static_cast<int>(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); 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) { if (dequeued_size < requested_size) {
// Need to fill remaining space with silence. // Need to fill remaining space with silence.
std::memset(out_data + dequeued_size, 0, requested_size - dequeued_size); std::memset(out_data + dequeued_size, 0, requested_size - dequeued_size);
@ -133,6 +132,8 @@ private:
size_t frame_size; size_t frame_size;
ring_buffer_base<u8> buffer; ring_buffer_base<u8> buffer;
std::mutex buffer_mutex;
std::condition_variable buffer_cv;
cubeb_stream* stream{}; cubeb_stream* stream{};
}; };