mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-05 08:52:36 +00:00
audioout: Refactor backend ports into class.
This commit is contained in:
parent
dc0725aa91
commit
a7f8aae2be
@ -202,9 +202,7 @@ set(AUDIO_LIB src/core/libraries/audio/audioin.cpp
|
||||
src/core/libraries/audio/audioout_backend.h
|
||||
src/core/libraries/audio/audioout_error.h
|
||||
src/core/libraries/audio/cubeb_audio.cpp
|
||||
src/core/libraries/audio/cubeb_audio.h
|
||||
src/core/libraries/audio/sdl_audio.cpp
|
||||
src/core/libraries/audio/sdl_audio.h
|
||||
src/core/libraries/ngs2/ngs2.cpp
|
||||
src/core/libraries/ngs2/ngs2.h
|
||||
)
|
||||
|
@ -10,9 +10,8 @@
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/audio/audioout.h"
|
||||
#include "core/libraries/audio/audioout_backend.h"
|
||||
#include "core/libraries/audio/audioout_error.h"
|
||||
#include "core/libraries/audio/cubeb_audio.h"
|
||||
#include "core/libraries/audio/sdl_audio.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::AudioOut {
|
||||
@ -177,13 +176,11 @@ int PS4_SYSV_ABI sceAudioOutClose(s32 handle) {
|
||||
|
||||
std::scoped_lock lock(ports_mutex);
|
||||
auto& port = ports_out.at(handle - 1);
|
||||
if (!port.is_open) {
|
||||
if (!port.impl) {
|
||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
audio->Close(port.impl);
|
||||
port.impl = nullptr;
|
||||
port.is_open = false;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -254,7 +251,7 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta
|
||||
|
||||
std::scoped_lock lock(ports_mutex);
|
||||
const auto& port = ports_out.at(handle - 1);
|
||||
if (!port.is_open) {
|
||||
if (!port.impl) {
|
||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
@ -398,13 +395,13 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
|
||||
}
|
||||
|
||||
std::scoped_lock lock{ports_mutex};
|
||||
const auto port = std::ranges::find(ports_out, false, &PortOut::is_open);
|
||||
const auto port =
|
||||
std::ranges::find_if(ports_out, [&](const PortOut& p) { return p.impl == nullptr; });
|
||||
if (port == ports_out.end()) {
|
||||
LOG_ERROR(Lib_AudioOut, "Audio ports are full");
|
||||
return ORBIS_AUDIO_OUT_ERROR_PORT_FULL;
|
||||
}
|
||||
|
||||
port->is_open = true;
|
||||
port->type = port_type;
|
||||
port->samples_num = length;
|
||||
port->freq = sample_rate;
|
||||
@ -413,8 +410,8 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
|
||||
port->channels_num = GetFormatNumChannels(format);
|
||||
port->sample_size = GetFormatSampleSize(format);
|
||||
port->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB);
|
||||
|
||||
port->impl = audio->Open(*port);
|
||||
|
||||
return std::distance(ports_out.begin(), port) + 1;
|
||||
}
|
||||
|
||||
@ -433,12 +430,12 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr) {
|
||||
}
|
||||
|
||||
auto& port = ports_out.at(handle - 1);
|
||||
if (!port.is_open) {
|
||||
if (!port.impl) {
|
||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
const size_t data_size = port.samples_num * port.sample_size * port.channels_num;
|
||||
audio->Output(port.impl, ptr, data_size);
|
||||
port.impl->Output(ptr, data_size);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -547,7 +544,7 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) {
|
||||
|
||||
std::scoped_lock lock(ports_mutex);
|
||||
auto& port = ports_out.at(handle - 1);
|
||||
if (!port.is_open) {
|
||||
if (!port.impl) {
|
||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||
}
|
||||
|
||||
@ -578,7 +575,7 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) {
|
||||
}
|
||||
}
|
||||
|
||||
audio->SetVolume(port.impl, port.volume);
|
||||
port.impl->SetVolume(port.volume);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -3,12 +3,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include <memory>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
|
||||
namespace Libraries::AudioOut {
|
||||
|
||||
class PortBackend;
|
||||
|
||||
// Main up to 8 ports, BGM 1 port, voice up to 4 ports,
|
||||
// personal up to 4 ports, padspk up to 5 ports, aux 1 port
|
||||
constexpr int SCE_AUDIO_OUT_NUM_PORTS = 22;
|
||||
@ -57,7 +60,8 @@ struct OrbisAudioOutPortState {
|
||||
};
|
||||
|
||||
struct PortOut {
|
||||
void* impl;
|
||||
std::unique_ptr<PortBackend> impl{};
|
||||
|
||||
u32 samples_num;
|
||||
u32 freq;
|
||||
OrbisAudioOutParamFormat format;
|
||||
@ -66,7 +70,6 @@ struct PortOut {
|
||||
bool is_float;
|
||||
std::array<int, 8> volume;
|
||||
u8 sample_size;
|
||||
bool is_open;
|
||||
};
|
||||
|
||||
int PS4_SYSV_ABI sceAudioOutDeviceIdOpen();
|
||||
|
@ -3,19 +3,42 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef struct cubeb cubeb;
|
||||
|
||||
namespace Libraries::AudioOut {
|
||||
|
||||
struct PortOut;
|
||||
|
||||
class PortBackend {
|
||||
public:
|
||||
virtual ~PortBackend() = default;
|
||||
|
||||
virtual void Output(void* ptr, size_t size) = 0;
|
||||
virtual void SetVolume(const std::array<int, 8>& ch_volumes) = 0;
|
||||
};
|
||||
|
||||
class AudioOutBackend {
|
||||
public:
|
||||
AudioOutBackend() = default;
|
||||
virtual ~AudioOutBackend() = default;
|
||||
|
||||
virtual void* Open(PortOut& port) = 0;
|
||||
virtual void Close(void* impl) = 0;
|
||||
virtual void Output(void* impl, void* ptr, size_t size) = 0;
|
||||
virtual void SetVolume(void* impl, const std::array<int, 8>& ch_volumes) = 0;
|
||||
virtual std::unique_ptr<PortBackend> Open(PortOut& port) = 0;
|
||||
};
|
||||
|
||||
class CubebAudioOut final : public AudioOutBackend {
|
||||
public:
|
||||
CubebAudioOut();
|
||||
~CubebAudioOut() override;
|
||||
|
||||
std::unique_ptr<PortBackend> Open(PortOut& port) override;
|
||||
|
||||
private:
|
||||
cubeb* ctx = nullptr;
|
||||
};
|
||||
|
||||
class SDLAudioOut final : public AudioOutBackend {
|
||||
public:
|
||||
std::unique_ptr<PortBackend> Open(PortOut& port) override;
|
||||
};
|
||||
|
||||
} // namespace Libraries::AudioOut
|
||||
|
@ -7,48 +7,49 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/ringbuffer.h"
|
||||
#include "core/libraries/audio/audioout.h"
|
||||
#include "core/libraries/audio/cubeb_audio.h"
|
||||
#include "core/libraries/audio/audioout_backend.h"
|
||||
|
||||
namespace Libraries::AudioOut {
|
||||
|
||||
constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold
|
||||
|
||||
static cubeb_channel_layout GetCubebChannelLayout(int num_channels) {
|
||||
switch (num_channels) {
|
||||
case 1:
|
||||
return CUBEB_LAYOUT_MONO;
|
||||
case 2:
|
||||
return CUBEB_LAYOUT_STEREO;
|
||||
case 8:
|
||||
return CUBEB_LAYOUT_3F4_LFE;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
struct CubebStream {
|
||||
cubeb_stream* stream{};
|
||||
size_t frame_size;
|
||||
ring_buffer_base<u8> buffer;
|
||||
|
||||
CubebStream(cubeb* ctx, const PortOut& port)
|
||||
class CubebPortBackend : public PortBackend {
|
||||
public:
|
||||
CubebPortBackend(cubeb* ctx, const PortOut& port)
|
||||
: frame_size(port.channels_num * port.sample_size),
|
||||
buffer(static_cast<int>(port.samples_num * frame_size) * 4) {
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
const auto get_channel_layout = [&port] -> cubeb_channel_layout {
|
||||
switch (port.channels_num) {
|
||||
case 1:
|
||||
return CUBEB_LAYOUT_MONO;
|
||||
case 2:
|
||||
return CUBEB_LAYOUT_STEREO;
|
||||
case 8:
|
||||
return CUBEB_LAYOUT_3F4_LFE;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
};
|
||||
cubeb_stream_params stream_params = {
|
||||
.format = port.is_float ? CUBEB_SAMPLE_FLOAT32LE : CUBEB_SAMPLE_S16LE,
|
||||
.rate = port.freq,
|
||||
.channels = static_cast<u32>(port.channels_num),
|
||||
.layout = GetCubebChannelLayout(port.channels_num),
|
||||
.layout = get_channel_layout(),
|
||||
.prefs = CUBEB_STREAM_PREF_NONE,
|
||||
};
|
||||
u32 latency_frames = 512;
|
||||
if (const auto ret = cubeb_get_min_latency(ctx, &stream_params, &latency_frames);
|
||||
ret != CUBEB_OK) {
|
||||
LOG_WARNING(Lib_AudioOut, "Could not get minimum cubeb audio latency: {}", ret);
|
||||
LOG_WARNING(Lib_AudioOut,
|
||||
"Could not get minimum cubeb audio latency, falling back to default: {}",
|
||||
ret);
|
||||
}
|
||||
if (const auto ret = cubeb_stream_init(ctx, &stream, "shadPS4", nullptr, nullptr, nullptr,
|
||||
char stream_name[64];
|
||||
snprintf(stream_name, sizeof(stream_name), "shadPS4 stream %p", this);
|
||||
if (const auto ret = cubeb_stream_init(ctx, &stream, stream_name, nullptr, nullptr, nullptr,
|
||||
&stream_params, latency_frames, &DataCallback,
|
||||
&StateCallback, this);
|
||||
ret != CUBEB_OK) {
|
||||
@ -61,7 +62,7 @@ struct CubebStream {
|
||||
}
|
||||
}
|
||||
|
||||
~CubebStream() {
|
||||
~CubebPortBackend() override {
|
||||
if (!stream) {
|
||||
return;
|
||||
}
|
||||
@ -72,7 +73,7 @@ struct CubebStream {
|
||||
stream = nullptr;
|
||||
}
|
||||
|
||||
void Output(void* ptr, size_t size) {
|
||||
void Output(void* ptr, size_t size) override {
|
||||
auto* data = static_cast<u8*>(ptr);
|
||||
while (size > 0) {
|
||||
const auto queued = buffer.enqueue(data, static_cast<int>(size));
|
||||
@ -86,7 +87,7 @@ struct CubebStream {
|
||||
}
|
||||
}
|
||||
|
||||
void SetVolume(const std::array<int, 8>& ch_volumes) {
|
||||
void SetVolume(const std::array<int, 8>& ch_volumes) override {
|
||||
if (!stream) {
|
||||
return;
|
||||
}
|
||||
@ -99,9 +100,10 @@ struct CubebStream {
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static long DataCallback(cubeb_stream* stream, void* user_data, const void* in, void* out,
|
||||
long num_frames) {
|
||||
auto* stream_data = static_cast<CubebStream*>(user_data);
|
||||
auto* stream_data = static_cast<CubebPortBackend*>(user_data);
|
||||
const auto out_data = static_cast<u8*>(out);
|
||||
const auto requested_size = static_cast<int>(num_frames * stream_data->frame_size);
|
||||
const auto dequeued_size = stream_data->buffer.dequeue(out_data, requested_size);
|
||||
@ -128,10 +130,14 @@ struct CubebStream {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t frame_size;
|
||||
ring_buffer_base<u8> buffer;
|
||||
cubeb_stream* stream{};
|
||||
};
|
||||
|
||||
CubebAudioOut::CubebAudioOut() {
|
||||
cubeb_set_log_callback(CUBEB_LOG_NORMAL, LogCallback);
|
||||
cubeb_set_log_callback(CUBEB_LOG_DISABLED, nullptr);
|
||||
if (const auto ret = cubeb_init(&ctx, "shadPS4", nullptr); ret != CUBEB_OK) {
|
||||
LOG_CRITICAL(Lib_AudioOut, "Failed to create cubeb context: {}", ret);
|
||||
}
|
||||
@ -145,38 +151,8 @@ CubebAudioOut::~CubebAudioOut() {
|
||||
ctx = nullptr;
|
||||
}
|
||||
|
||||
void* CubebAudioOut::Open(PortOut& port) {
|
||||
return new CubebStream(ctx, port);
|
||||
}
|
||||
|
||||
void CubebAudioOut::Close(void* impl) {
|
||||
if (!impl) {
|
||||
return;
|
||||
}
|
||||
delete static_cast<CubebStream*>(impl);
|
||||
}
|
||||
|
||||
void CubebAudioOut::Output(void* impl, void* ptr, size_t size) {
|
||||
if (!impl) {
|
||||
return;
|
||||
}
|
||||
static_cast<CubebStream*>(impl)->Output(ptr, size);
|
||||
}
|
||||
|
||||
void CubebAudioOut::SetVolume(void* impl, const std::array<int, 8>& ch_volumes) {
|
||||
if (!impl) {
|
||||
return;
|
||||
}
|
||||
static_cast<CubebStream*>(impl)->SetVolume(ch_volumes);
|
||||
}
|
||||
|
||||
void CubebAudioOut::LogCallback(const char* format, ...) {
|
||||
std::array<char, 512> buffer{};
|
||||
std::va_list args;
|
||||
va_start(args, format);
|
||||
vsnprintf(buffer.data(), buffer.size(), format, args);
|
||||
va_end(args);
|
||||
LOG_DEBUG(Lib_Audio3d, "[cubeb] {}", buffer.data());
|
||||
std::unique_ptr<PortBackend> CubebAudioOut::Open(PortOut& port) {
|
||||
return std::make_unique<CubebPortBackend>(ctx, port);
|
||||
}
|
||||
|
||||
} // namespace Libraries::AudioOut
|
||||
|
@ -1,29 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/audio/audioout_backend.h"
|
||||
|
||||
typedef struct cubeb cubeb;
|
||||
|
||||
namespace Libraries::AudioOut {
|
||||
|
||||
class CubebAudioOut final : public AudioOutBackend {
|
||||
public:
|
||||
CubebAudioOut();
|
||||
~CubebAudioOut();
|
||||
|
||||
void* Open(PortOut& port) override;
|
||||
void Close(void* impl) override;
|
||||
void Output(void* impl, void* ptr, size_t size) override;
|
||||
void SetVolume(void* impl, const std::array<int, 8>& ch_volumes) override;
|
||||
|
||||
private:
|
||||
static void LogCallback(const char* format, ...);
|
||||
|
||||
cubeb* ctx = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Libraries::AudioOut
|
@ -1,45 +1,60 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <SDL3/SDL_audio.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <SDL3/SDL_timer.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/audio/audioout.h"
|
||||
#include "core/libraries/audio/sdl_audio.h"
|
||||
#include "core/libraries/audio/audioout_backend.h"
|
||||
|
||||
namespace Libraries::AudioOut {
|
||||
|
||||
constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold
|
||||
|
||||
void* SDLAudioOut::Open(PortOut& port) {
|
||||
SDL_AudioSpec fmt;
|
||||
SDL_zero(fmt);
|
||||
fmt.format = port.is_float ? SDL_AUDIO_F32 : SDL_AUDIO_S16;
|
||||
fmt.channels = port.channels_num;
|
||||
fmt.freq = static_cast<int>(port.freq);
|
||||
|
||||
auto* stream =
|
||||
SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, nullptr, nullptr);
|
||||
SDL_ResumeAudioStreamDevice(stream);
|
||||
return stream;
|
||||
}
|
||||
|
||||
void SDLAudioOut::Close(void* impl) {
|
||||
SDL_DestroyAudioStream(static_cast<SDL_AudioStream*>(impl));
|
||||
}
|
||||
|
||||
void SDLAudioOut::Output(void* impl, void* ptr, size_t size) {
|
||||
auto* stream = static_cast<SDL_AudioStream*>(impl);
|
||||
SDL_PutAudioStreamData(stream, ptr, size);
|
||||
while (SDL_GetAudioStreamAvailable(stream) > AUDIO_STREAM_BUFFER_THRESHOLD) {
|
||||
SDL_Delay(0);
|
||||
class SDLPortBackend : public PortBackend {
|
||||
public:
|
||||
explicit SDLPortBackend(const PortOut& port) {
|
||||
const SDL_AudioSpec fmt = {
|
||||
.format = port.is_float ? SDL_AUDIO_F32 : SDL_AUDIO_S16,
|
||||
.channels = port.channels_num,
|
||||
.freq = static_cast<int>(port.freq),
|
||||
};
|
||||
stream =
|
||||
SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, nullptr, nullptr);
|
||||
if (stream == nullptr) {
|
||||
LOG_ERROR(Lib_AudioOut, "Failed to create SDL audio stream: {}", SDL_GetError());
|
||||
}
|
||||
SDL_ResumeAudioStreamDevice(stream);
|
||||
}
|
||||
}
|
||||
|
||||
void SDLAudioOut::SetVolume(void* impl, const std::array<int, 8>& ch_volumes) {
|
||||
// Not yet implemented
|
||||
~SDLPortBackend() override {
|
||||
if (stream) {
|
||||
SDL_DestroyAudioStream(stream);
|
||||
stream = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Output(void* ptr, size_t size) override {
|
||||
SDL_PutAudioStreamData(stream, ptr, static_cast<int>(size));
|
||||
while (SDL_GetAudioStreamAvailable(stream) > AUDIO_STREAM_BUFFER_THRESHOLD) {
|
||||
// Yield to allow the stream to drain.
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
void SetVolume(const std::array<int, 8>& ch_volumes) override {
|
||||
// TODO: Not yet implemented
|
||||
}
|
||||
|
||||
private:
|
||||
SDL_AudioStream* stream;
|
||||
};
|
||||
|
||||
std::unique_ptr<PortBackend> SDLAudioOut::Open(PortOut& port) {
|
||||
return std::make_unique<SDLPortBackend>(port);
|
||||
}
|
||||
|
||||
} // namespace Libraries::AudioOut
|
||||
|
@ -1,19 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/audio/audioout_backend.h"
|
||||
|
||||
namespace Libraries::AudioOut {
|
||||
|
||||
class SDLAudioOut final : public AudioOutBackend {
|
||||
public:
|
||||
void* Open(PortOut& port) override;
|
||||
void Close(void* impl) override;
|
||||
void Output(void* impl, void* ptr, size_t size) override;
|
||||
void SetVolume(void* impl, const std::array<int, 8>& ch_volumes) override;
|
||||
};
|
||||
|
||||
} // namespace Libraries::AudioOut
|
Loading…
Reference in New Issue
Block a user