mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-06 17:32:40 +00:00
audio: Format info cleanup.
This commit is contained in:
parent
3a02d2860c
commit
3c86a9906a
@ -4,6 +4,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
@ -22,111 +23,28 @@ std::array<PortOut, SCE_AUDIO_OUT_NUM_PORTS> ports_out{};
|
|||||||
|
|
||||||
static std::unique_ptr<AudioOutBackend> audio;
|
static std::unique_ptr<AudioOutBackend> audio;
|
||||||
|
|
||||||
static std::string_view GetAudioOutPort(OrbisAudioOutPort port) {
|
static AudioFormatInfo GetFormatInfo(const OrbisAudioOutParamFormat format) {
|
||||||
switch (port) {
|
static constexpr std::array<AudioFormatInfo, 8> format_infos = {{
|
||||||
case OrbisAudioOutPort::Main:
|
// S16Mono
|
||||||
return "MAIN";
|
{false, 2, 1, {0}},
|
||||||
case OrbisAudioOutPort::Bgm:
|
// S16Stereo
|
||||||
return "BGM";
|
{false, 2, 2, {0, 1}},
|
||||||
case OrbisAudioOutPort::Voice:
|
// S16_8CH
|
||||||
return "VOICE";
|
{false, 2, 8, {0, 1, 2, 3, 4, 5, 6, 7}},
|
||||||
case OrbisAudioOutPort::Personal:
|
// FloatMono
|
||||||
return "PERSONAL";
|
{true, 4, 1, {0}},
|
||||||
case OrbisAudioOutPort::Padspk:
|
// FloatStereo
|
||||||
return "PADSPK";
|
{true, 4, 2, {0, 1}},
|
||||||
case OrbisAudioOutPort::Aux:
|
// Float_8CH
|
||||||
return "AUX";
|
{true, 4, 8, {0, 1, 2, 3, 4, 5, 6, 7}},
|
||||||
default:
|
// S16_8CH_Std
|
||||||
return "INVALID";
|
{false, 2, 8, {0, 1, 2, 3, 6, 7, 4, 5}},
|
||||||
}
|
// Float_8CH_Std
|
||||||
}
|
{true, 4, 8, {0, 1, 2, 3, 6, 7, 4, 5}},
|
||||||
|
}};
|
||||||
static std::string_view GetAudioOutParamFormat(OrbisAudioOutParamFormat param) {
|
const auto index = static_cast<u32>(format);
|
||||||
switch (param) {
|
ASSERT_MSG(index < format_infos.size(), "Unknown audio format {}", index);
|
||||||
case OrbisAudioOutParamFormat::S16Mono:
|
return format_infos[index];
|
||||||
return "S16_MONO";
|
|
||||||
case OrbisAudioOutParamFormat::S16Stereo:
|
|
||||||
return "S16_STEREO";
|
|
||||||
case OrbisAudioOutParamFormat::S16_8CH:
|
|
||||||
return "S16_8CH";
|
|
||||||
case OrbisAudioOutParamFormat::FloatMono:
|
|
||||||
return "FLOAT_MONO";
|
|
||||||
case OrbisAudioOutParamFormat::FloatStereo:
|
|
||||||
return "FLOAT_STEREO";
|
|
||||||
case OrbisAudioOutParamFormat::Float_8CH:
|
|
||||||
return "FLOAT_8CH";
|
|
||||||
case OrbisAudioOutParamFormat::S16_8CH_Std:
|
|
||||||
return "S16_8CH_STD";
|
|
||||||
case OrbisAudioOutParamFormat::Float_8CH_Std:
|
|
||||||
return "FLOAT_8CH_STD";
|
|
||||||
default:
|
|
||||||
return "INVALID";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string_view GetAudioOutParamAttr(OrbisAudioOutParamAttr attr) {
|
|
||||||
switch (attr) {
|
|
||||||
case OrbisAudioOutParamAttr::None:
|
|
||||||
return "NONE";
|
|
||||||
case OrbisAudioOutParamAttr::Restricted:
|
|
||||||
return "RESTRICTED";
|
|
||||||
case OrbisAudioOutParamAttr::MixToMain:
|
|
||||||
return "MIX_TO_MAIN";
|
|
||||||
default:
|
|
||||||
return "INVALID";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool IsFormatFloat(const OrbisAudioOutParamFormat format) {
|
|
||||||
switch (format) {
|
|
||||||
case OrbisAudioOutParamFormat::S16Mono:
|
|
||||||
case OrbisAudioOutParamFormat::S16Stereo:
|
|
||||||
case OrbisAudioOutParamFormat::S16_8CH:
|
|
||||||
case OrbisAudioOutParamFormat::S16_8CH_Std:
|
|
||||||
return false;
|
|
||||||
case OrbisAudioOutParamFormat::FloatMono:
|
|
||||||
case OrbisAudioOutParamFormat::FloatStereo:
|
|
||||||
case OrbisAudioOutParamFormat::Float_8CH:
|
|
||||||
case OrbisAudioOutParamFormat::Float_8CH_Std:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
UNREACHABLE_MSG("Unknown format");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static u8 GetFormatNumChannels(const OrbisAudioOutParamFormat format) {
|
|
||||||
switch (format) {
|
|
||||||
case OrbisAudioOutParamFormat::S16Mono:
|
|
||||||
case OrbisAudioOutParamFormat::FloatMono:
|
|
||||||
return 1;
|
|
||||||
case OrbisAudioOutParamFormat::S16Stereo:
|
|
||||||
case OrbisAudioOutParamFormat::FloatStereo:
|
|
||||||
return 2;
|
|
||||||
case OrbisAudioOutParamFormat::S16_8CH:
|
|
||||||
case OrbisAudioOutParamFormat::Float_8CH:
|
|
||||||
case OrbisAudioOutParamFormat::S16_8CH_Std:
|
|
||||||
case OrbisAudioOutParamFormat::Float_8CH_Std:
|
|
||||||
return 8;
|
|
||||||
default:
|
|
||||||
UNREACHABLE_MSG("Unknown format");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static u8 GetFormatSampleSize(const OrbisAudioOutParamFormat format) {
|
|
||||||
switch (format) {
|
|
||||||
case OrbisAudioOutParamFormat::S16Mono:
|
|
||||||
case OrbisAudioOutParamFormat::S16Stereo:
|
|
||||||
case OrbisAudioOutParamFormat::S16_8CH:
|
|
||||||
case OrbisAudioOutParamFormat::S16_8CH_Std:
|
|
||||||
return 2;
|
|
||||||
case OrbisAudioOutParamFormat::FloatMono:
|
|
||||||
case OrbisAudioOutParamFormat::FloatStereo:
|
|
||||||
case OrbisAudioOutParamFormat::Float_8CH:
|
|
||||||
case OrbisAudioOutParamFormat::Float_8CH_Std:
|
|
||||||
return 4;
|
|
||||||
default:
|
|
||||||
UNREACHABLE_MSG("Unknown format");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceAudioOutDeviceIdOpen() {
|
int PS4_SYSV_ABI sceAudioOutDeviceIdOpen() {
|
||||||
@ -268,7 +186,7 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta
|
|||||||
case OrbisAudioOutPort::Bgm:
|
case OrbisAudioOutPort::Bgm:
|
||||||
case OrbisAudioOutPort::Voice:
|
case OrbisAudioOutPort::Voice:
|
||||||
state->output = 1;
|
state->output = 1;
|
||||||
state->channel = port.channels_num > 2 ? 2 : port.channels_num;
|
state->channel = port.format_info.num_channels > 2 ? 2 : port.format_info.num_channels;
|
||||||
break;
|
break;
|
||||||
case OrbisAudioOutPort::Personal:
|
case OrbisAudioOutPort::Personal:
|
||||||
case OrbisAudioOutPort::Padspk:
|
case OrbisAudioOutPort::Padspk:
|
||||||
@ -357,7 +275,7 @@ static void AudioOutputThread(PortOut* port, const std::stop_token& stop) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Common::AccurateTimer timer(
|
Common::AccurateTimer timer(
|
||||||
std::chrono::nanoseconds(1000000000ULL * port->buffer_frames / port->freq));
|
std::chrono::nanoseconds(1000000000ULL * port->buffer_frames / port->sample_rate));
|
||||||
while (true) {
|
while (true) {
|
||||||
timer.Start();
|
timer.Start();
|
||||||
{
|
{
|
||||||
@ -381,9 +299,9 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
|
|||||||
LOG_INFO(Lib_AudioOut,
|
LOG_INFO(Lib_AudioOut,
|
||||||
"id = {} port_type = {} index = {} length = {} sample_rate = {} "
|
"id = {} port_type = {} index = {} length = {} sample_rate = {} "
|
||||||
"param_type = {} attr = {}",
|
"param_type = {} attr = {}",
|
||||||
user_id, GetAudioOutPort(port_type), index, length, sample_rate,
|
user_id, magic_enum::enum_name(port_type), index, length, sample_rate,
|
||||||
GetAudioOutParamFormat(param_type.data_format),
|
magic_enum::enum_name(param_type.data_format.Value()),
|
||||||
GetAudioOutParamAttr(param_type.attributes));
|
magic_enum::enum_name(param_type.attributes.Value()));
|
||||||
if ((port_type < OrbisAudioOutPort::Main || port_type > OrbisAudioOutPort::Padspk) &&
|
if ((port_type < OrbisAudioOutPort::Main || port_type > OrbisAudioOutPort::Padspk) &&
|
||||||
(port_type != OrbisAudioOutPort::Aux)) {
|
(port_type != OrbisAudioOutPort::Aux)) {
|
||||||
LOG_ERROR(Lib_AudioOut, "Invalid port type");
|
LOG_ERROR(Lib_AudioOut, "Invalid port type");
|
||||||
@ -423,19 +341,14 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
port->type = port_type;
|
port->type = port_type;
|
||||||
port->format = format;
|
port->format_info = GetFormatInfo(format);
|
||||||
port->is_float = IsFormatFloat(format);
|
port->sample_rate = sample_rate;
|
||||||
port->freq = sample_rate;
|
|
||||||
port->sample_size = GetFormatSampleSize(format);
|
|
||||||
port->channels_num = GetFormatNumChannels(format);
|
|
||||||
port->frame_size = port->sample_size * port->channels_num;
|
|
||||||
port->buffer_frames = length;
|
port->buffer_frames = length;
|
||||||
port->buffer_size = port->frame_size * port->buffer_frames;
|
|
||||||
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);
|
||||||
|
|
||||||
port->output_buffer = std::malloc(port->buffer_size);
|
port->output_buffer = std::malloc(port->BufferSize());
|
||||||
port->output_ready = false;
|
port->output_ready = false;
|
||||||
port->output_thread.Run(
|
port->output_thread.Run(
|
||||||
[port](const std::stop_token& stop) { AudioOutputThread(&*port, stop); });
|
[port](const std::stop_token& stop) { AudioOutputThread(&*port, stop); });
|
||||||
@ -462,7 +375,7 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr) {
|
|||||||
std::unique_lock lock{port.output_mutex};
|
std::unique_lock lock{port.output_mutex};
|
||||||
port.output_cv.wait(lock, [&] { return !port.output_ready; });
|
port.output_cv.wait(lock, [&] { return !port.output_ready; });
|
||||||
if (ptr != nullptr) {
|
if (ptr != nullptr) {
|
||||||
std::memcpy(port.output_buffer, ptr, port.buffer_size);
|
std::memcpy(port.output_buffer, ptr, port.BufferSize());
|
||||||
port.output_ready = true;
|
port.output_ready = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -581,30 +494,9 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) {
|
|||||||
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < port.channels_num; i++, flag >>= 1u) {
|
for (int i = 0; i < port.format_info.num_channels; i++, flag >>= 1u) {
|
||||||
auto bit = flag & 0x1u;
|
if (flag & 0x1u) {
|
||||||
if (bit == 1) {
|
port.volume[i] = vol[i];
|
||||||
int src_index = i;
|
|
||||||
if (port.format == OrbisAudioOutParamFormat::Float_8CH_Std ||
|
|
||||||
port.format == OrbisAudioOutParamFormat::S16_8CH_Std) {
|
|
||||||
switch (i) {
|
|
||||||
case 4:
|
|
||||||
src_index = 6;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
src_index = 7;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
src_index = 4;
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
src_index = 5;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
port.volume[i] = vol[src_index];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,12 +15,12 @@ class PortBackend;
|
|||||||
|
|
||||||
// Main up to 8 ports, BGM 1 port, voice up to 4 ports,
|
// 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
|
// personal up to 4 ports, padspk up to 5 ports, aux 1 port
|
||||||
constexpr int SCE_AUDIO_OUT_NUM_PORTS = 22;
|
constexpr s32 SCE_AUDIO_OUT_NUM_PORTS = 22;
|
||||||
constexpr int SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value
|
constexpr s32 SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value
|
||||||
|
|
||||||
enum class OrbisAudioOutPort { Main = 0, Bgm = 1, Voice = 2, Personal = 3, Padspk = 4, Aux = 127 };
|
enum class OrbisAudioOutPort { Main = 0, Bgm = 1, Voice = 2, Personal = 3, Padspk = 4, Aux = 127 };
|
||||||
|
|
||||||
enum class OrbisAudioOutParamFormat {
|
enum class OrbisAudioOutParamFormat : u32 {
|
||||||
S16Mono = 0,
|
S16Mono = 0,
|
||||||
S16Stereo = 1,
|
S16Stereo = 1,
|
||||||
S16_8CH = 2,
|
S16_8CH = 2,
|
||||||
@ -31,7 +31,7 @@ enum class OrbisAudioOutParamFormat {
|
|||||||
Float_8CH_Std = 7
|
Float_8CH_Std = 7
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class OrbisAudioOutParamAttr {
|
enum class OrbisAudioOutParamAttr : u32 {
|
||||||
None = 0,
|
None = 0,
|
||||||
Restricted = 1,
|
Restricted = 1,
|
||||||
MixToMain = 2,
|
MixToMain = 2,
|
||||||
@ -60,6 +60,19 @@ struct OrbisAudioOutPortState {
|
|||||||
u64 reserved64[2];
|
u64 reserved64[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AudioFormatInfo {
|
||||||
|
bool is_float;
|
||||||
|
u8 sample_size;
|
||||||
|
u8 num_channels;
|
||||||
|
/// Layout array remapping channel indices, specified in this order:
|
||||||
|
/// FL, FR, FC, LFE, BL, BR, SL, SR
|
||||||
|
std::array<int, 8> channel_layout;
|
||||||
|
|
||||||
|
[[nodiscard]] u16 FrameSize() const {
|
||||||
|
return sample_size * num_channels;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct PortOut {
|
struct PortOut {
|
||||||
std::unique_ptr<PortBackend> impl{};
|
std::unique_ptr<PortBackend> impl{};
|
||||||
|
|
||||||
@ -70,15 +83,14 @@ struct PortOut {
|
|||||||
Kernel::Thread output_thread{};
|
Kernel::Thread output_thread{};
|
||||||
|
|
||||||
OrbisAudioOutPort type;
|
OrbisAudioOutPort type;
|
||||||
OrbisAudioOutParamFormat format;
|
AudioFormatInfo format_info;
|
||||||
bool is_float;
|
u32 sample_rate;
|
||||||
u32 freq;
|
|
||||||
u8 sample_size;
|
|
||||||
u8 channels_num;
|
|
||||||
u32 frame_size;
|
|
||||||
u32 buffer_frames;
|
u32 buffer_frames;
|
||||||
u32 buffer_size;
|
std::array<s32, 8> volume;
|
||||||
std::array<int, 8> volume;
|
|
||||||
|
[[nodiscard]] u32 BufferSize() const {
|
||||||
|
return buffer_frames * format_info.FrameSize();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceAudioOutDeviceIdOpen();
|
int PS4_SYSV_ABI sceAudioOutDeviceIdOpen();
|
||||||
|
@ -14,7 +14,7 @@ namespace Libraries::AudioOut {
|
|||||||
class SDLPortBackend : public PortBackend {
|
class SDLPortBackend : public PortBackend {
|
||||||
public:
|
public:
|
||||||
explicit SDLPortBackend(const PortOut& port)
|
explicit SDLPortBackend(const PortOut& port)
|
||||||
: frame_size(port.frame_size), buffer_size(port.buffer_size) {
|
: frame_size(port.format_info.FrameSize()), buffer_size(port.BufferSize()) {
|
||||||
// We want the latency for delivering frames out to be as small as possible,
|
// We want the latency for delivering frames out to be as small as possible,
|
||||||
// so set the sample frames hint to the number of frames per buffer.
|
// so set the sample frames hint to the number of frames per buffer.
|
||||||
const auto samples_num_str = std::to_string(port.buffer_frames);
|
const auto samples_num_str = std::to_string(port.buffer_frames);
|
||||||
@ -23,9 +23,9 @@ public:
|
|||||||
samples_num_str, SDL_GetError());
|
samples_num_str, SDL_GetError());
|
||||||
}
|
}
|
||||||
const SDL_AudioSpec fmt = {
|
const SDL_AudioSpec fmt = {
|
||||||
.format = port.is_float ? SDL_AUDIO_F32LE : SDL_AUDIO_S16LE,
|
.format = port.format_info.is_float ? SDL_AUDIO_F32LE : SDL_AUDIO_S16LE,
|
||||||
.channels = port.channels_num,
|
.channels = port.format_info.num_channels,
|
||||||
.freq = static_cast<int>(port.freq),
|
.freq = static_cast<int>(port.sample_rate),
|
||||||
};
|
};
|
||||||
stream =
|
stream =
|
||||||
SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, nullptr, nullptr);
|
SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, nullptr, nullptr);
|
||||||
@ -34,6 +34,14 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
queue_threshold = CalculateQueueThreshold();
|
queue_threshold = CalculateQueueThreshold();
|
||||||
|
if (!SDL_SetAudioStreamInputChannelMap(stream, port.format_info.channel_layout.data(),
|
||||||
|
port.format_info.num_channels)) {
|
||||||
|
LOG_ERROR(Lib_AudioOut, "Failed to configure SDL audio stream channel map: {}",
|
||||||
|
SDL_GetError());
|
||||||
|
SDL_DestroyAudioStream(stream);
|
||||||
|
stream = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!SDL_ResumeAudioStreamDevice(stream)) {
|
if (!SDL_ResumeAudioStreamDevice(stream)) {
|
||||||
LOG_ERROR(Lib_AudioOut, "Failed to resume SDL audio stream: {}", SDL_GetError());
|
LOG_ERROR(Lib_AudioOut, "Failed to resume SDL audio stream: {}", SDL_GetError());
|
||||||
SDL_DestroyAudioStream(stream);
|
SDL_DestroyAudioStream(stream);
|
||||||
|
Loading…
Reference in New Issue
Block a user