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 <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/assert.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::string_view GetAudioOutPort(OrbisAudioOutPort port) {
|
||||
switch (port) {
|
||||
case OrbisAudioOutPort::Main:
|
||||
return "MAIN";
|
||||
case OrbisAudioOutPort::Bgm:
|
||||
return "BGM";
|
||||
case OrbisAudioOutPort::Voice:
|
||||
return "VOICE";
|
||||
case OrbisAudioOutPort::Personal:
|
||||
return "PERSONAL";
|
||||
case OrbisAudioOutPort::Padspk:
|
||||
return "PADSPK";
|
||||
case OrbisAudioOutPort::Aux:
|
||||
return "AUX";
|
||||
default:
|
||||
return "INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string_view GetAudioOutParamFormat(OrbisAudioOutParamFormat param) {
|
||||
switch (param) {
|
||||
case OrbisAudioOutParamFormat::S16Mono:
|
||||
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");
|
||||
}
|
||||
static AudioFormatInfo GetFormatInfo(const OrbisAudioOutParamFormat format) {
|
||||
static constexpr std::array<AudioFormatInfo, 8> format_infos = {{
|
||||
// S16Mono
|
||||
{false, 2, 1, {0}},
|
||||
// S16Stereo
|
||||
{false, 2, 2, {0, 1}},
|
||||
// S16_8CH
|
||||
{false, 2, 8, {0, 1, 2, 3, 4, 5, 6, 7}},
|
||||
// FloatMono
|
||||
{true, 4, 1, {0}},
|
||||
// FloatStereo
|
||||
{true, 4, 2, {0, 1}},
|
||||
// Float_8CH
|
||||
{true, 4, 8, {0, 1, 2, 3, 4, 5, 6, 7}},
|
||||
// S16_8CH_Std
|
||||
{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}},
|
||||
}};
|
||||
const auto index = static_cast<u32>(format);
|
||||
ASSERT_MSG(index < format_infos.size(), "Unknown audio format {}", index);
|
||||
return format_infos[index];
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceAudioOutDeviceIdOpen() {
|
||||
@ -268,7 +186,7 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta
|
||||
case OrbisAudioOutPort::Bgm:
|
||||
case OrbisAudioOutPort::Voice:
|
||||
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;
|
||||
case OrbisAudioOutPort::Personal:
|
||||
case OrbisAudioOutPort::Padspk:
|
||||
@ -357,7 +275,7 @@ static void AudioOutputThread(PortOut* port, const std::stop_token& stop) {
|
||||
}
|
||||
|
||||
Common::AccurateTimer timer(
|
||||
std::chrono::nanoseconds(1000000000ULL * port->buffer_frames / port->freq));
|
||||
std::chrono::nanoseconds(1000000000ULL * port->buffer_frames / port->sample_rate));
|
||||
while (true) {
|
||||
timer.Start();
|
||||
{
|
||||
@ -381,9 +299,9 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
|
||||
LOG_INFO(Lib_AudioOut,
|
||||
"id = {} port_type = {} index = {} length = {} sample_rate = {} "
|
||||
"param_type = {} attr = {}",
|
||||
user_id, GetAudioOutPort(port_type), index, length, sample_rate,
|
||||
GetAudioOutParamFormat(param_type.data_format),
|
||||
GetAudioOutParamAttr(param_type.attributes));
|
||||
user_id, magic_enum::enum_name(port_type), index, length, sample_rate,
|
||||
magic_enum::enum_name(param_type.data_format.Value()),
|
||||
magic_enum::enum_name(param_type.attributes.Value()));
|
||||
if ((port_type < OrbisAudioOutPort::Main || port_type > OrbisAudioOutPort::Padspk) &&
|
||||
(port_type != OrbisAudioOutPort::Aux)) {
|
||||
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->format = format;
|
||||
port->is_float = IsFormatFloat(format);
|
||||
port->freq = sample_rate;
|
||||
port->sample_size = GetFormatSampleSize(format);
|
||||
port->channels_num = GetFormatNumChannels(format);
|
||||
port->frame_size = port->sample_size * port->channels_num;
|
||||
port->format_info = GetFormatInfo(format);
|
||||
port->sample_rate = sample_rate;
|
||||
port->buffer_frames = length;
|
||||
port->buffer_size = port->frame_size * port->buffer_frames;
|
||||
port->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB);
|
||||
|
||||
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_thread.Run(
|
||||
[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};
|
||||
port.output_cv.wait(lock, [&] { return !port.output_ready; });
|
||||
if (ptr != nullptr) {
|
||||
std::memcpy(port.output_buffer, ptr, port.buffer_size);
|
||||
std::memcpy(port.output_buffer, ptr, port.BufferSize());
|
||||
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;
|
||||
}
|
||||
|
||||
for (int i = 0; i < port.channels_num; i++, flag >>= 1u) {
|
||||
auto bit = flag & 0x1u;
|
||||
if (bit == 1) {
|
||||
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];
|
||||
for (int i = 0; i < port.format_info.num_channels; i++, flag >>= 1u) {
|
||||
if (flag & 0x1u) {
|
||||
port.volume[i] = vol[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,12 +15,12 @@ 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;
|
||||
constexpr int SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value
|
||||
constexpr s32 SCE_AUDIO_OUT_NUM_PORTS = 22;
|
||||
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 OrbisAudioOutParamFormat {
|
||||
enum class OrbisAudioOutParamFormat : u32 {
|
||||
S16Mono = 0,
|
||||
S16Stereo = 1,
|
||||
S16_8CH = 2,
|
||||
@ -31,7 +31,7 @@ enum class OrbisAudioOutParamFormat {
|
||||
Float_8CH_Std = 7
|
||||
};
|
||||
|
||||
enum class OrbisAudioOutParamAttr {
|
||||
enum class OrbisAudioOutParamAttr : u32 {
|
||||
None = 0,
|
||||
Restricted = 1,
|
||||
MixToMain = 2,
|
||||
@ -60,6 +60,19 @@ struct OrbisAudioOutPortState {
|
||||
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 {
|
||||
std::unique_ptr<PortBackend> impl{};
|
||||
|
||||
@ -70,15 +83,14 @@ struct PortOut {
|
||||
Kernel::Thread output_thread{};
|
||||
|
||||
OrbisAudioOutPort type;
|
||||
OrbisAudioOutParamFormat format;
|
||||
bool is_float;
|
||||
u32 freq;
|
||||
u8 sample_size;
|
||||
u8 channels_num;
|
||||
u32 frame_size;
|
||||
AudioFormatInfo format_info;
|
||||
u32 sample_rate;
|
||||
u32 buffer_frames;
|
||||
u32 buffer_size;
|
||||
std::array<int, 8> volume;
|
||||
std::array<s32, 8> volume;
|
||||
|
||||
[[nodiscard]] u32 BufferSize() const {
|
||||
return buffer_frames * format_info.FrameSize();
|
||||
}
|
||||
};
|
||||
|
||||
int PS4_SYSV_ABI sceAudioOutDeviceIdOpen();
|
||||
|
@ -14,7 +14,7 @@ namespace Libraries::AudioOut {
|
||||
class SDLPortBackend : public PortBackend {
|
||||
public:
|
||||
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,
|
||||
// so set the sample frames hint to the number of frames per buffer.
|
||||
const auto samples_num_str = std::to_string(port.buffer_frames);
|
||||
@ -23,9 +23,9 @@ public:
|
||||
samples_num_str, SDL_GetError());
|
||||
}
|
||||
const SDL_AudioSpec fmt = {
|
||||
.format = port.is_float ? SDL_AUDIO_F32LE : SDL_AUDIO_S16LE,
|
||||
.channels = port.channels_num,
|
||||
.freq = static_cast<int>(port.freq),
|
||||
.format = port.format_info.is_float ? SDL_AUDIO_F32LE : SDL_AUDIO_S16LE,
|
||||
.channels = port.format_info.num_channels,
|
||||
.freq = static_cast<int>(port.sample_rate),
|
||||
};
|
||||
stream =
|
||||
SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, nullptr, nullptr);
|
||||
@ -34,6 +34,14 @@ public:
|
||||
return;
|
||||
}
|
||||
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)) {
|
||||
LOG_ERROR(Lib_AudioOut, "Failed to resume SDL audio stream: {}", SDL_GetError());
|
||||
SDL_DestroyAudioStream(stream);
|
||||
|
Loading…
Reference in New Issue
Block a user