diff --git a/core/libs/prx/libSceAudiodec/Export.cpp b/core/libs/prx/libSceAudiodec/Export.cpp index 4840fde2d0..2f84e5ccd0 100644 --- a/core/libs/prx/libSceAudiodec/Export.cpp +++ b/core/libs/prx/libSceAudiodec/Export.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" #include "prx/libSceAudiodec/src/Codecs.hpp" @@ -34,6 +35,8 @@ constexpr std::int32_t ERROR_M4AAC_INVALID_SAMPLING_FREQ = static_cast(0x807F0302); constexpr std::int32_t ERROR_M4AAC_INVALID_CONFIG_NUMBER = static_cast(0x807F0303); constexpr std::int32_t ERROR_M4AAC_INVALID_MAX_CHANNELS = static_cast(0x807F0304); +constexpr std::int32_t ERROR_M4AAC_INVALID_ENABLE_NONDELAY_OUTPUT = static_cast(0x807F0305); +constexpr std::int32_t ERROR_M4AAC_INVALID_SURROUND_CHANNEL_INTERLEAVE_ORDER = static_cast(0x807F0306); constexpr std::int32_t ERROR_AT9_INVALID_CONFIG_DATA = static_cast(0x807F1000); constexpr std::uint32_t TYPE_AT9 = 1; @@ -136,14 +139,14 @@ void fillMp3Info(AudiodecMp3Info* info, const Audiodec::Mp3Header& header) { } std::int32_t createAac(const AudiodecParamM4aac* param, AudiodecM4aacInfo* info, Instance& instance) { - std::uint32_t extensionMode = 0; - std::uint32_t outputMode = 0; + std::uint32_t enableNondelayOutput = 0; + std::uint32_t surroundChannelInterleaveOrder = 0; const auto* bytes = reinterpret_cast(param); - if (param->ui_size >= 28) std::memcpy(&extensionMode, bytes + 24, sizeof(extensionMode)); - if (extensionMode > 1) return static_cast(0x807F0305); - if (param->ui_size == 32) std::memcpy(&outputMode, bytes + 28, sizeof(outputMode)); - if (outputMode > 2) return static_cast(0x807F0306); - if (extensionMode != 0 || outputMode != 0) throw std::runtime_error("M4AAC extended decoding modes are not implemented"); + if (param->ui_size >= 28) std::memcpy(&enableNondelayOutput, bytes + 24, sizeof(enableNondelayOutput)); + if (enableNondelayOutput > 1) return ERROR_M4AAC_INVALID_ENABLE_NONDELAY_OUTPUT; + if (param->ui_size == 32) std::memcpy(&surroundChannelInterleaveOrder, bytes + 28, sizeof(surroundChannelInterleaveOrder)); + if (surroundChannelInterleaveOrder > 2) return ERROR_M4AAC_INVALID_SURROUND_CHANNEL_INTERLEAVE_ORDER; + if (surroundChannelInterleaveOrder != 0) throw std::runtime_error("M4AAC surround channel interleave order " + std::to_string(surroundChannelInterleaveOrder) + " is not implemented"); if (param->ui_config_number != M4AAC_CONFIG_ADTS && param->ui_config_number != M4AAC_CONFIG_RAW) return ERROR_M4AAC_INVALID_CONFIG_NUMBER; const std::uint32_t sampleRate = Audiodec::AacSampleRate(param->ui_sampling_freq_index); if (param->ui_config_number == M4AAC_CONFIG_RAW && sampleRate == 0) return ERROR_M4AAC_INVALID_SAMPLING_FREQ; diff --git a/core/libs/tests/GuestAudiodec.cpp b/core/libs/tests/GuestAudiodec.cpp index 6461dd9bf4..e43c40addc 100644 --- a/core/libs/tests/GuestAudiodec.cpp +++ b/core/libs/tests/GuestAudiodec.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include static void Require(bool value) { if (!value) std::abort(); } @@ -231,8 +230,14 @@ std::size_t AdtsLength(const std::uint8_t* frame) { return static_cast((frame[3] & 3) << 11 | frame[4] << 3 | frame[5] >> 5); } -void TestAac(bool adts, std::uint32_t maxChannels = 2) { - AudiodecParamM4aac param{sizeof(param), 1, adts ? 1u : 2u, 3, maxChannels, 0}; +struct M4aacExtendedParam { + AudiodecParamM4aac base; + std::uint32_t enableNondelayOutput; + std::uint32_t surroundChannelInterleaveOrder; +}; + +void TestAac(bool adts, std::uint32_t maxChannels = 2, std::uint32_t paramSize = sizeof(AudiodecParamM4aac), std::uint32_t enableNondelayOutput = 0) { + M4aacExtendedParam param{{paramSize, 1, adts ? 1u : 2u, 3, maxChannels, 0}, enableNondelayOutput, 0}; AudiodecM4aacInfo info{sizeof(info), 0, 0, 0, 0}; AudiodecCtrl ctrl{¶m, &info, nullptr, nullptr}; const std::int32_t handle = sceAudiodecCreateDecoder(&ctrl, 3); @@ -419,11 +424,7 @@ int main() { aac.ui_max_channels = 6; aac.ui_enable_heaac = 1; Require(sceAudiodecCreateDecoder(&aacCtrl, 3) == static_cast(0x807F0302)); - struct ExtendedParam { - AudiodecParamM4aac base; - std::uint32_t extensionMode; - std::uint32_t outputMode; - } extended{{32, 1, 1, 3, 2, 0}, 0, 0}; + M4aacExtendedParam extended{{32, 1, 1, 3, 2, 0}, 0, 0}; aacCtrl.pParam = &extended; for (const std::uint32_t size : {25u, 27u, 29u, 31u, 33u}) { extended.base.ui_size = size; @@ -431,19 +432,17 @@ int main() { } for (const std::uint32_t size : {28u, 32u}) { extended.base.ui_size = size; - extended.extensionMode = 2; + extended.enableNondelayOutput = 2; Require(sceAudiodecCreateDecoder(&aacCtrl, 3) == static_cast(0x807F0305)); - extended.extensionMode = 1; - bool threw = false; - try { sceAudiodecCreateDecoder(&aacCtrl, 3); } - catch (const std::runtime_error&) { threw = true; } - Require(threw); - extended.extensionMode = 0; - const auto extendedHandle = sceAudiodecCreateDecoder(&aacCtrl, 3); - Require(extendedHandle > 0); - Require(sceAudiodecDeleteDecoder(extendedHandle) == 0); + for (const std::uint32_t enableNondelayOutput : {0u, 1u}) { + extended.enableNondelayOutput = enableNondelayOutput; + const auto extendedHandle = sceAudiodecCreateDecoder(&aacCtrl, 3); + Require(extendedHandle > 0); + Require(sceAudiodecDeleteDecoder(extendedHandle) == 0); + } + extended.enableNondelayOutput = 0; } - extended.outputMode = 3; + extended.surroundChannelInterleaveOrder = 3; Require(sceAudiodecCreateDecoder(&aacCtrl, 3) == static_cast(0x807F0306)); const std::int32_t handle = sceAudiodecCreateDecoder(&ctrl, 2); @@ -461,6 +460,9 @@ int main() { TestAac(false); TestAac(false, 6); TestAac(false, 8); + TestAac(true, 2, 28, 1); + TestAac(false, 2, 32, 1); + TestAac(false, 8, 28, 1); TestAacRaw51(6); TestAacRaw51(8); for (const bool adts : {true, false}) { diff --git a/docs/dev/debt/libSceAudiodec.md b/docs/dev/debt/libSceAudiodec.md index ac9b492ea4..ea126704da 100644 --- a/docs/dev/debt/libSceAudiodec.md +++ b/docs/dev/debt/libSceAudiodec.md @@ -2,7 +2,7 @@ ### Unknown function info -- [M4AAC extended parameters](../../../core/libs/prx/libSceAudiodec/Export.cpp): 28-byte and 32-byte parameter variants add fields at offsets 24 and 28. Their accepted ranges are 0..1 and 0..2; nonzero modes throw until the corresponding decoder configuration is implemented. Zero extensions use the base decoding configuration. +- [M4AAC extended parameters](../../../core/libs/prx/libSceAudiodec/Export.cpp): 28-byte and 32-byte parameter variants add fields at offsets 24 and 28, named by their range errors (`0x807F0305` `SCE_AUDIODEC_ERROR_M4AAC_INVALID_ENABLE_NONDELAY_OUTPUT`, `0x807F0306` `SCE_AUDIODEC_ERROR_M4AAC_INVALID_SURROUND_CHANNEL_INTERLEAVE_ORDER`, in fpPS4's `ps4_libsceaudiodec.pas`). Non-delay output 1 decodes every access unit to its own PCM from the first one on, as shadPS4's AJM AAC decoder does with `EnableNondelayOutput`; without that flag shadPS4 holds back the first 2 frames. Non-delay output 0 still decodes the same way, with no frame held back: no measurement shows whether the console delays it. Surround channel interleave orders 1 and 2 throw: shadPS4 names two AJM orders (`ExtlExtrLsRs`, `LsRsExtlExtr`) but uses neither, and no source says which value selects which order or which order 0 is. - [sceAudiodecDecode](../../../core/libs/prx/libSceAudiodec/src/Codecs.cpp) (libSceAudiodec) - a raw M4AAC stream carries no channel configuration, so the decoder tries the mono/stereo configuration and then configurations 3 to 7 within `ui_max_channels` until one decodes the first access unit, and keeps that one. No reference documents how the library picks the layout (shadPS4 maps the channel count straight to the configuration; SharpProspero only describes the maximum). With the stereo configuration a mono raw stream decodes as 2 channels, so 2 is reported, not 1 - [sceAudiodecDecode](../../../core/libs/prx/libSceAudiodec/src/Codecs.cpp) (libSceAudiodec) - the HE-AAC handling follows one console measurement of an HE-AAC v2 ADTS stream: SBR output at 48 kHz in one channel with `ui_enable_heaac` 1, the 24 kHz mono core with 0. Not measured: whether the console, like FFmpeg, decodes a stream without SBR data in its first access unit as AAC-LC with `ui_enable_heaac` 1, and what it reports in `ui_sampling_freq` and `ui_heaac` with `ui_enable_heaac` 0 (here the rate of the returned PCM and 0)