diff --git a/README.md b/README.md index 69ee64b13..e43a2408d 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ The following firmware modules are supported and must be placed in shadPS4's `sy | libSceCesCs.sprx | libSceFont.sprx | libSceFontFt.sprx | libSceFreeTypeOt.sprx | | libSceJpegDec.sprx | libSceJpegEnc.sprx | libSceJson.sprx | libSceJson2.sprx | | libSceLibcInternal.sprx | libSceNgs2.sprx | libScePngEnc.sprx | libSceRtc.sprx | -| libSceUlt.sprx | | | | +| libSceUlt.sprx | libSceAudiodec.sprx | | | > [!Caution] diff --git a/src/core/libraries/ajm/ajm.cpp b/src/core/libraries/ajm/ajm.cpp index b64bb47fd..2bec1bf0f 100644 --- a/src/core/libraries/ajm/ajm.cpp +++ b/src/core/libraries/ajm/ajm.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" @@ -34,7 +34,7 @@ u32 GetChannelMask(u32 num_channels) { case 8: return ORBIS_AJM_CHANNELMASK_7POINT1; default: - UNREACHABLE(); + UNREACHABLE_MSG("Unexpected number of channels: {}", num_channels); } } diff --git a/src/core/libraries/ajm/ajm_aac.cpp b/src/core/libraries/ajm/ajm_aac.cpp index b96394b72..061b77890 100644 --- a/src/core/libraries/ajm/ajm_aac.cpp +++ b/src/core/libraries/ajm/ajm_aac.cpp @@ -5,20 +5,45 @@ #include "ajm_aac.h" #include "ajm_result.h" +#include // using this internal header to manually configure the decoder in RAW mode #include "externals/aacdec/fdk-aac/libAACdec/src/aacdecoder.h" -#include -#include +#include // std::transform +#include // std::back_inserter +#include namespace Libraries::Ajm { +std::span AjmAacDecoder::GetOuputPcm(u32 skipped_pcm, u32 max_pcm) const { + const auto pcm_data = std::span(m_pcm_buffer).subspan(skipped_pcm); + return pcm_data.subspan(0, std::min(pcm_data.size(), max_pcm)); +} + +template <> +size_t AjmAacDecoder::WriteOutputSamples(SparseOutputBuffer& out, std::span pcm) { + if (pcm.empty()) { + return 0; + } + + m_resample_buffer.clear(); + constexpr float inv_scale = 1.0f / std::numeric_limits::max(); + std::transform(pcm.begin(), pcm.end(), std::back_inserter(m_resample_buffer), + [](auto sample) { return float(sample) * inv_scale; }); + + return out.Write(std::span(m_resample_buffer)); +} + AjmAacDecoder::AjmAacDecoder(AjmFormatEncoding format, AjmAacCodecFlags flags, u32 channels) - : m_format(format), m_flags(flags), m_channels(channels), m_pcm_buffer(2048 * 8), - m_skip_frames(True(flags & AjmAacCodecFlags::EnableNondelayOutput) ? 0 : 2) {} + : m_format(format), m_flags(flags), m_channels(channels), m_pcm_buffer(1024 * 8), + m_skip_frames(True(flags & AjmAacCodecFlags::EnableNondelayOutput) ? 0 : 2) { + m_resample_buffer.reserve(m_pcm_buffer.size()); +} AjmAacDecoder::~AjmAacDecoder() { - aacDecoder_Close(m_decoder); + if (m_decoder) { + aacDecoder_Close(m_decoder); + } } TRANSPORT_TYPE TransportTypeFromConfigType(ConfigType config_type) { @@ -98,7 +123,7 @@ AjmSidebandFormat AjmAacDecoder::GetFormat() const { .num_channels = static_cast(info->numChannels), .channel_mask = GetChannelMask(info->numChannels), .sampl_freq = static_cast(info->sampleRate), - .sample_encoding = m_format, // AjmFormatEncoding + .sample_encoding = m_format, .bitrate = static_cast(info->bitRate), }; } @@ -130,8 +155,7 @@ DecoderResult AjmAacDecoder::ProcessData(std::span& input, SparseOutputBuffe const UINT sizes[] = {static_cast(input.size())}; UINT valid = sizes[0]; aacDecoder_Fill(m_decoder, buffers, sizes, &valid); - auto ret = aacDecoder_DecodeFrame(m_decoder, reinterpret_cast(m_pcm_buffer.data()), - m_pcm_buffer.size() / 2, 0); + auto ret = aacDecoder_DecodeFrame(m_decoder, m_pcm_buffer.data(), m_pcm_buffer.size(), 0); switch (ret) { case AAC_DEC_OK: @@ -167,16 +191,16 @@ DecoderResult AjmAacDecoder::ProcessData(std::span& input, SparseOutputBuffe gapless.init.total_samples != 0 ? gapless.current.total_samples : info->aacSamplesPerFrame; size_t pcm_written = 0; + auto pcm = GetOuputPcm(skip_samples * info->numChannels, max_samples * info->numChannels); switch (m_format) { case AjmFormatEncoding::S16: - pcm_written = WriteOutputSamples(output, skip_samples * info->numChannels, - max_samples * info->numChannels); + pcm_written = output.Write(pcm); break; case AjmFormatEncoding::S32: UNREACHABLE_MSG("NOT IMPLEMENTED"); break; case AjmFormatEncoding::Float: - UNREACHABLE_MSG("NOT IMPLEMENTED"); + pcm_written = WriteOutputSamples(output, pcm); break; default: UNREACHABLE(); @@ -191,4 +215,4 @@ DecoderResult AjmAacDecoder::ProcessData(std::span& input, SparseOutputBuffe return result; } -} // namespace Libraries::Ajm +} // namespace Libraries::Ajm \ No newline at end of file diff --git a/src/core/libraries/ajm/ajm_aac.h b/src/core/libraries/ajm/ajm_aac.h index 7ca8ecbf8..4ff55d843 100644 --- a/src/core/libraries/ajm/ajm_aac.h +++ b/src/core/libraries/ajm/ajm_aac.h @@ -52,22 +52,18 @@ private: }; template - size_t WriteOutputSamples(SparseOutputBuffer& output, u32 skipped_pcm, u32 max_pcm) { - std::span pcm_data{reinterpret_cast(m_pcm_buffer.data()), - m_pcm_buffer.size() / sizeof(T)}; - pcm_data = pcm_data.subspan(skipped_pcm); - const auto pcm_size = std::min(u32(pcm_data.size()), max_pcm); - return output.Write(pcm_data.subspan(0, pcm_size)); - } + size_t WriteOutputSamples(SparseOutputBuffer& output, std::span pcm); + std::span GetOuputPcm(u32 skipped_pcm, u32 max_pcm) const; const AjmFormatEncoding m_format; const AjmAacCodecFlags m_flags; const u32 m_channels; - std::vector m_pcm_buffer; + std::vector m_pcm_buffer; + std::vector m_resample_buffer; u32 m_skip_frames = 0; InitializeParameters m_init_params = {}; AAC_DECODER_INSTANCE* m_decoder = nullptr; }; -} // namespace Libraries::Ajm +} // namespace Libraries::Ajm \ No newline at end of file diff --git a/src/emulator.cpp b/src/emulator.cpp index 44f8b0e72..263bd9c2b 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -532,6 +532,7 @@ void Emulator::LoadSystemModules(const std::string& game_serial) { {"libSceJson2.sprx", nullptr}, {"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterLib}, {"libSceCesCs.sprx", nullptr}, + {"libSceAudiodec.sprx", nullptr}, {"libSceFont.sprx", &Libraries::Font::RegisterlibSceFont}, {"libSceFontFt.sprx", &Libraries::FontFt::RegisterlibSceFontFt}, {"libSceFreeTypeOt.sprx", nullptr}});