Skip to content

Commit d2d1a3c

Browse files
committed
audio: Format info cleanup.
1 parent 5cebf67 commit d2d1a3c

File tree

3 files changed

+71
-159
lines changed

3 files changed

+71
-159
lines changed

src/core/libraries/audio/audioout.cpp

Lines changed: 35 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <memory>
55
#include <mutex>
66
#include <shared_mutex>
7+
#include <magic_enum/magic_enum.hpp>
78

89
#include "common/assert.h"
910
#include "common/config.h"
@@ -22,111 +23,28 @@ std::array<PortOut, SCE_AUDIO_OUT_NUM_PORTS> ports_out{};
2223

2324
static std::unique_ptr<AudioOutBackend> audio;
2425

25-
static std::string_view GetAudioOutPort(OrbisAudioOutPort port) {
26-
switch (port) {
27-
case OrbisAudioOutPort::Main:
28-
return "MAIN";
29-
case OrbisAudioOutPort::Bgm:
30-
return "BGM";
31-
case OrbisAudioOutPort::Voice:
32-
return "VOICE";
33-
case OrbisAudioOutPort::Personal:
34-
return "PERSONAL";
35-
case OrbisAudioOutPort::Padspk:
36-
return "PADSPK";
37-
case OrbisAudioOutPort::Aux:
38-
return "AUX";
39-
default:
40-
return "INVALID";
41-
}
42-
}
43-
44-
static std::string_view GetAudioOutParamFormat(OrbisAudioOutParamFormat param) {
45-
switch (param) {
46-
case OrbisAudioOutParamFormat::S16Mono:
47-
return "S16_MONO";
48-
case OrbisAudioOutParamFormat::S16Stereo:
49-
return "S16_STEREO";
50-
case OrbisAudioOutParamFormat::S16_8CH:
51-
return "S16_8CH";
52-
case OrbisAudioOutParamFormat::FloatMono:
53-
return "FLOAT_MONO";
54-
case OrbisAudioOutParamFormat::FloatStereo:
55-
return "FLOAT_STEREO";
56-
case OrbisAudioOutParamFormat::Float_8CH:
57-
return "FLOAT_8CH";
58-
case OrbisAudioOutParamFormat::S16_8CH_Std:
59-
return "S16_8CH_STD";
60-
case OrbisAudioOutParamFormat::Float_8CH_Std:
61-
return "FLOAT_8CH_STD";
62-
default:
63-
return "INVALID";
64-
}
65-
}
66-
67-
static std::string_view GetAudioOutParamAttr(OrbisAudioOutParamAttr attr) {
68-
switch (attr) {
69-
case OrbisAudioOutParamAttr::None:
70-
return "NONE";
71-
case OrbisAudioOutParamAttr::Restricted:
72-
return "RESTRICTED";
73-
case OrbisAudioOutParamAttr::MixToMain:
74-
return "MIX_TO_MAIN";
75-
default:
76-
return "INVALID";
77-
}
78-
}
79-
80-
static bool IsFormatFloat(const OrbisAudioOutParamFormat format) {
81-
switch (format) {
82-
case OrbisAudioOutParamFormat::S16Mono:
83-
case OrbisAudioOutParamFormat::S16Stereo:
84-
case OrbisAudioOutParamFormat::S16_8CH:
85-
case OrbisAudioOutParamFormat::S16_8CH_Std:
86-
return false;
87-
case OrbisAudioOutParamFormat::FloatMono:
88-
case OrbisAudioOutParamFormat::FloatStereo:
89-
case OrbisAudioOutParamFormat::Float_8CH:
90-
case OrbisAudioOutParamFormat::Float_8CH_Std:
91-
return true;
92-
default:
93-
UNREACHABLE_MSG("Unknown format");
94-
}
95-
}
96-
97-
static u8 GetFormatNumChannels(const OrbisAudioOutParamFormat format) {
98-
switch (format) {
99-
case OrbisAudioOutParamFormat::S16Mono:
100-
case OrbisAudioOutParamFormat::FloatMono:
101-
return 1;
102-
case OrbisAudioOutParamFormat::S16Stereo:
103-
case OrbisAudioOutParamFormat::FloatStereo:
104-
return 2;
105-
case OrbisAudioOutParamFormat::S16_8CH:
106-
case OrbisAudioOutParamFormat::Float_8CH:
107-
case OrbisAudioOutParamFormat::S16_8CH_Std:
108-
case OrbisAudioOutParamFormat::Float_8CH_Std:
109-
return 8;
110-
default:
111-
UNREACHABLE_MSG("Unknown format");
112-
}
113-
}
114-
115-
static u8 GetFormatSampleSize(const OrbisAudioOutParamFormat format) {
116-
switch (format) {
117-
case OrbisAudioOutParamFormat::S16Mono:
118-
case OrbisAudioOutParamFormat::S16Stereo:
119-
case OrbisAudioOutParamFormat::S16_8CH:
120-
case OrbisAudioOutParamFormat::S16_8CH_Std:
121-
return 2;
122-
case OrbisAudioOutParamFormat::FloatMono:
123-
case OrbisAudioOutParamFormat::FloatStereo:
124-
case OrbisAudioOutParamFormat::Float_8CH:
125-
case OrbisAudioOutParamFormat::Float_8CH_Std:
126-
return 4;
127-
default:
128-
UNREACHABLE_MSG("Unknown format");
129-
}
26+
static AudioFormatInfo GetFormatInfo(const OrbisAudioOutParamFormat format) {
27+
static constexpr std::array<AudioFormatInfo, 8> format_infos = {{
28+
// S16Mono
29+
{false, 2, 1, {0}},
30+
// S16Stereo
31+
{false, 2, 2, {0, 1}},
32+
// S16_8CH
33+
{false, 2, 8, {0, 1, 2, 3, 4, 5, 6, 7}},
34+
// FloatMono
35+
{true, 4, 1, {0}},
36+
// FloatStereo
37+
{true, 4, 2, {0, 1}},
38+
// Float_8CH
39+
{true, 4, 8, {0, 1, 2, 3, 4, 5, 6, 7}},
40+
// S16_8CH_Std
41+
{false, 2, 8, {0, 1, 2, 3, 6, 7, 4, 5}},
42+
// Float_8CH_Std
43+
{true, 4, 8, {0, 1, 2, 3, 6, 7, 4, 5}},
44+
}};
45+
const auto index = static_cast<u32>(format);
46+
ASSERT_MSG(index < format_infos.size(), "Unknown audio format {}", index);
47+
return format_infos[index];
13048
}
13149

13250
int PS4_SYSV_ABI sceAudioOutDeviceIdOpen() {
@@ -268,7 +186,7 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta
268186
case OrbisAudioOutPort::Bgm:
269187
case OrbisAudioOutPort::Voice:
270188
state->output = 1;
271-
state->channel = port.channels_num > 2 ? 2 : port.channels_num;
189+
state->channel = port.format_info.num_channels > 2 ? 2 : port.format_info.num_channels;
272190
break;
273191
case OrbisAudioOutPort::Personal:
274192
case OrbisAudioOutPort::Padspk:
@@ -357,7 +275,7 @@ static void AudioOutputThread(PortOut* port, const std::stop_token& stop) {
357275
}
358276

359277
Common::AccurateTimer timer(
360-
std::chrono::nanoseconds(1000000000ULL * port->buffer_frames / port->freq));
278+
std::chrono::nanoseconds(1000000000ULL * port->buffer_frames / port->sample_rate));
361279
while (true) {
362280
timer.Start();
363281
{
@@ -381,9 +299,9 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
381299
LOG_INFO(Lib_AudioOut,
382300
"id = {} port_type = {} index = {} length = {} sample_rate = {} "
383301
"param_type = {} attr = {}",
384-
user_id, GetAudioOutPort(port_type), index, length, sample_rate,
385-
GetAudioOutParamFormat(param_type.data_format),
386-
GetAudioOutParamAttr(param_type.attributes));
302+
user_id, magic_enum::enum_name(port_type), index, length, sample_rate,
303+
magic_enum::enum_name(param_type.data_format.Value()),
304+
magic_enum::enum_name(param_type.attributes.Value()));
387305
if ((port_type < OrbisAudioOutPort::Main || port_type > OrbisAudioOutPort::Padspk) &&
388306
(port_type != OrbisAudioOutPort::Aux)) {
389307
LOG_ERROR(Lib_AudioOut, "Invalid port type");
@@ -423,19 +341,14 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
423341
}
424342

425343
port->type = port_type;
426-
port->format = format;
427-
port->is_float = IsFormatFloat(format);
428-
port->freq = sample_rate;
429-
port->sample_size = GetFormatSampleSize(format);
430-
port->channels_num = GetFormatNumChannels(format);
431-
port->frame_size = port->sample_size * port->channels_num;
344+
port->format_info = GetFormatInfo(format);
345+
port->sample_rate = sample_rate;
432346
port->buffer_frames = length;
433-
port->buffer_size = port->frame_size * port->buffer_frames;
434347
port->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB);
435348

436349
port->impl = audio->Open(*port);
437350

438-
port->output_buffer = std::malloc(port->buffer_size);
351+
port->output_buffer = std::malloc(port->BufferSize());
439352
port->output_ready = false;
440353
port->output_thread.Run(
441354
[port](const std::stop_token& stop) { AudioOutputThread(&*port, stop); });
@@ -462,7 +375,7 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr) {
462375
std::unique_lock lock{port.output_mutex};
463376
port.output_cv.wait(lock, [&] { return !port.output_ready; });
464377
if (ptr != nullptr) {
465-
std::memcpy(port.output_buffer, ptr, port.buffer_size);
378+
std::memcpy(port.output_buffer, ptr, port.BufferSize());
466379
port.output_ready = true;
467380
}
468381
}
@@ -581,30 +494,9 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) {
581494
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
582495
}
583496

584-
for (int i = 0; i < port.channels_num; i++, flag >>= 1u) {
585-
auto bit = flag & 0x1u;
586-
if (bit == 1) {
587-
int src_index = i;
588-
if (port.format == OrbisAudioOutParamFormat::Float_8CH_Std ||
589-
port.format == OrbisAudioOutParamFormat::S16_8CH_Std) {
590-
switch (i) {
591-
case 4:
592-
src_index = 6;
593-
break;
594-
case 5:
595-
src_index = 7;
596-
break;
597-
case 6:
598-
src_index = 4;
599-
break;
600-
case 7:
601-
src_index = 5;
602-
break;
603-
default:
604-
break;
605-
}
606-
}
607-
port.volume[i] = vol[src_index];
497+
for (int i = 0; i < port.format_info.num_channels; i++, flag >>= 1u) {
498+
if (flag & 0x1u) {
499+
port.volume[i] = vol[i];
608500
}
609501
}
610502

src/core/libraries/audio/audioout.h

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ class PortBackend;
1515

1616
// Main up to 8 ports, BGM 1 port, voice up to 4 ports,
1717
// personal up to 4 ports, padspk up to 5 ports, aux 1 port
18-
constexpr int SCE_AUDIO_OUT_NUM_PORTS = 22;
19-
constexpr int SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value
18+
constexpr s32 SCE_AUDIO_OUT_NUM_PORTS = 22;
19+
constexpr s32 SCE_AUDIO_OUT_VOLUME_0DB = 32768; // max volume value
2020

2121
enum class OrbisAudioOutPort { Main = 0, Bgm = 1, Voice = 2, Personal = 3, Padspk = 4, Aux = 127 };
2222

23-
enum class OrbisAudioOutParamFormat {
23+
enum class OrbisAudioOutParamFormat : u32 {
2424
S16Mono = 0,
2525
S16Stereo = 1,
2626
S16_8CH = 2,
@@ -31,7 +31,7 @@ enum class OrbisAudioOutParamFormat {
3131
Float_8CH_Std = 7
3232
};
3333

34-
enum class OrbisAudioOutParamAttr {
34+
enum class OrbisAudioOutParamAttr : u32 {
3535
None = 0,
3636
Restricted = 1,
3737
MixToMain = 2,
@@ -60,6 +60,19 @@ struct OrbisAudioOutPortState {
6060
u64 reserved64[2];
6161
};
6262

63+
struct AudioFormatInfo {
64+
bool is_float;
65+
u8 sample_size;
66+
u8 num_channels;
67+
/// Layout array remapping channel indices, specified in this order:
68+
/// FL, FR, FC, LFE, BL, BR, SL, SR
69+
std::array<int, 8> channel_layout;
70+
71+
[[nodiscard]] u16 FrameSize() const {
72+
return sample_size * num_channels;
73+
}
74+
};
75+
6376
struct PortOut {
6477
std::unique_ptr<PortBackend> impl{};
6578

@@ -70,15 +83,14 @@ struct PortOut {
7083
Kernel::Thread output_thread{};
7184

7285
OrbisAudioOutPort type;
73-
OrbisAudioOutParamFormat format;
74-
bool is_float;
75-
u32 freq;
76-
u8 sample_size;
77-
u8 channels_num;
78-
u32 frame_size;
86+
AudioFormatInfo format_info;
87+
u32 sample_rate;
7988
u32 buffer_frames;
80-
u32 buffer_size;
81-
std::array<int, 8> volume;
89+
std::array<s32, 8> volume;
90+
91+
[[nodiscard]] u32 BufferSize() const {
92+
return buffer_frames * format_info.FrameSize();
93+
}
8294
};
8395

8496
int PS4_SYSV_ABI sceAudioOutDeviceIdOpen();

src/core/libraries/audio/sdl_audio.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace Libraries::AudioOut {
1414
class SDLPortBackend : public PortBackend {
1515
public:
1616
explicit SDLPortBackend(const PortOut& port)
17-
: frame_size(port.frame_size), buffer_size(port.buffer_size) {
17+
: frame_size(port.format_info.FrameSize()), buffer_size(port.BufferSize()) {
1818
// We want the latency for delivering frames out to be as small as possible,
1919
// so set the sample frames hint to the number of frames per buffer.
2020
const auto samples_num_str = std::to_string(port.buffer_frames);
@@ -23,9 +23,9 @@ class SDLPortBackend : public PortBackend {
2323
samples_num_str, SDL_GetError());
2424
}
2525
const SDL_AudioSpec fmt = {
26-
.format = port.is_float ? SDL_AUDIO_F32LE : SDL_AUDIO_S16LE,
27-
.channels = port.channels_num,
28-
.freq = static_cast<int>(port.freq),
26+
.format = port.format_info.is_float ? SDL_AUDIO_F32LE : SDL_AUDIO_S16LE,
27+
.channels = port.format_info.num_channels,
28+
.freq = static_cast<int>(port.sample_rate),
2929
};
3030
stream =
3131
SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, nullptr, nullptr);
@@ -34,6 +34,14 @@ class SDLPortBackend : public PortBackend {
3434
return;
3535
}
3636
queue_threshold = CalculateQueueThreshold();
37+
if (!SDL_SetAudioStreamInputChannelMap(stream, port.format_info.channel_layout.data(),
38+
port.format_info.num_channels)) {
39+
LOG_ERROR(Lib_AudioOut, "Failed to configure SDL audio stream channel map: {}",
40+
SDL_GetError());
41+
SDL_DestroyAudioStream(stream);
42+
stream = nullptr;
43+
return;
44+
}
3745
if (!SDL_ResumeAudioStreamDevice(stream)) {
3846
LOG_ERROR(Lib_AudioOut, "Failed to resume SDL audio stream: {}", SDL_GetError());
3947
SDL_DestroyAudioStream(stream);

0 commit comments

Comments
 (0)