From 05f14e36829d775329a774772e7bcd25dedff3f3 Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Tue, 23 Dec 2025 12:11:52 +0300 Subject: [PATCH] ajm fixes (#3875) --- src/core/libraries/ajm/ajm.h | 2 +- src/core/libraries/ajm/ajm_at9.cpp | 52 ++++++--- src/core/libraries/ajm/ajm_at9.h | 5 +- src/core/libraries/ajm/ajm_batch.h | 1 + src/core/libraries/ajm/ajm_context.cpp | 11 +- src/core/libraries/ajm/ajm_instance.cpp | 107 ++++++++---------- src/core/libraries/ajm/ajm_instance.h | 14 ++- .../libraries/ajm/ajm_instance_statistics.cpp | 6 +- .../libraries/ajm/ajm_instance_statistics.h | 1 + src/core/libraries/ajm/ajm_mp3.cpp | 44 ++++--- src/core/libraries/ajm/ajm_mp3.h | 6 +- src/core/libraries/ajm/ajm_result.h | 17 +++ 12 files changed, 165 insertions(+), 101 deletions(-) create mode 100644 src/core/libraries/ajm/ajm_result.h diff --git a/src/core/libraries/ajm/ajm.h b/src/core/libraries/ajm/ajm.h index 2c529cd4b..d68a4c0f4 100644 --- a/src/core/libraries/ajm/ajm.h +++ b/src/core/libraries/ajm/ajm.h @@ -133,7 +133,7 @@ struct AjmSidebandGaplessDecode { struct AjmSidebandResampleParameters { float ratio; - uint32_t flags; + u32 flags; }; struct AjmDecAt9InitializeParameters { diff --git a/src/core/libraries/ajm/ajm_at9.cpp b/src/core/libraries/ajm/ajm_at9.cpp index 014d1a4e5..ea7add4f3 100644 --- a/src/core/libraries/ajm/ajm_at9.cpp +++ b/src/core/libraries/ajm/ajm_at9.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "ajm_result.h" #include "common/assert.h" #include "core/libraries/ajm/ajm_at9.h" #include "error_codes.h" @@ -85,8 +86,8 @@ void AjmAt9Decoder::GetInfo(void* out_info) const { auto* info = reinterpret_cast(out_info); info->super_frame_size = m_codec_info.superframeSize; info->frames_in_super_frame = m_codec_info.framesInSuperframe; + info->next_frame_size = m_superframe_bytes_remain; info->frame_samples = m_codec_info.frameSamples; - info->next_frame_size = static_cast(m_handle)->Config.FrameBytes; } u8 g_at9_guid[] = {0xD2, 0x42, 0xE1, 0x47, 0xBA, 0x36, 0x8D, 0x4D, @@ -133,18 +134,22 @@ void AjmAt9Decoder::ParseRIFFHeader(std::span& in_buf, AjmInstanceGapless& g } } -std::tuple AjmAt9Decoder::ProcessData(std::span& in_buf, - SparseOutputBuffer& output, - AjmInstanceGapless& gapless) { - bool is_reset = false; +u32 AjmAt9Decoder::GetMinimumInputSize() const { + return m_superframe_bytes_remain; +} + +DecoderResult AjmAt9Decoder::ProcessData(std::span& in_buf, SparseOutputBuffer& output, + AjmInstanceGapless& gapless) { + DecoderResult result{}; if (True(m_flags & AjmAt9CodecFlags::ParseRiffHeader) && *reinterpret_cast(in_buf.data()) == 'FFIR') { ParseRIFFHeader(in_buf, gapless); - is_reset = true; + result.is_reset = true; } if (!m_is_initialized) { - return {0, 0, is_reset}; + result.result = ORBIS_AJM_RESULT_NOT_INITIALIZED; + return result; } int ret = 0; @@ -166,7 +171,14 @@ std::tuple AjmAt9Decoder::ProcessData(std::span& in_buf, default: UNREACHABLE(); } - ASSERT_MSG(ret == At9Status::ERR_SUCCESS, "Atrac9Decode failed ret = {:#x}", ret); + if (ret != At9Status::ERR_SUCCESS) { + LOG_ERROR(Lib_Ajm, "Atrac9Decode failed ret = {:#x}", ret); + result.result = ORBIS_AJM_RESULT_CODEC_ERROR | ORBIS_AJM_RESULT_FATAL; + result.internal_result = ret; + return result; + } + + result.frames_decoded += 1; in_buf = in_buf.subspan(bytes_used); m_superframe_bytes_remain -= bytes_used; @@ -196,10 +208,10 @@ std::tuple AjmAt9Decoder::ProcessData(std::span& in_buf, UNREACHABLE(); } - const auto samples_written = pcm_written / m_codec_info.channels; - gapless.current.skipped_samples += m_codec_info.frameSamples - samples_written; + result.samples_written = pcm_written / m_codec_info.channels; + gapless.current.skipped_samples += m_codec_info.frameSamples - result.samples_written; if (gapless.init.total_samples != 0) { - gapless.current.total_samples -= samples_written; + gapless.current.total_samples -= result.samples_written; } m_num_frames += 1; @@ -209,9 +221,23 @@ std::tuple AjmAt9Decoder::ProcessData(std::span& in_buf, } m_superframe_bytes_remain = m_codec_info.superframeSize; m_num_frames = 0; + } else if (gapless.IsEnd()) { + // Drain the remaining superframe + std::vector buf(m_codec_info.frameSamples * m_codec_info.channels, 0); + while ((m_num_frames % m_codec_info.framesInSuperframe) != 0) { + ret = Atrac9Decode(m_handle, in_buf.data(), buf.data(), &bytes_used, + True(m_flags & AjmAt9CodecFlags::NonInterleavedOutput)); + in_buf = in_buf.subspan(bytes_used); + m_superframe_bytes_remain -= bytes_used; + result.frames_decoded += 1; + m_num_frames += 1; + } + in_buf = in_buf.subspan(m_superframe_bytes_remain); + m_superframe_bytes_remain = m_codec_info.superframeSize; + m_num_frames = 0; } - return {1, m_codec_info.frameSamples, is_reset}; + return result; } AjmSidebandFormat AjmAt9Decoder::GetFormat() const { @@ -232,7 +258,7 @@ u32 AjmAt9Decoder::GetNextFrameSize(const AjmInstanceGapless& gapless) const { const auto samples = gapless.init.total_samples != 0 ? std::min(gapless.current.total_samples, m_codec_info.frameSamples - skip_samples) - : m_codec_info.frameSamples; + : m_codec_info.frameSamples - skip_samples; return samples * m_codec_info.channels * GetPCMSize(m_format); } diff --git a/src/core/libraries/ajm/ajm_at9.h b/src/core/libraries/ajm/ajm_at9.h index 3262f1aa0..94a718824 100644 --- a/src/core/libraries/ajm/ajm_at9.h +++ b/src/core/libraries/ajm/ajm_at9.h @@ -36,9 +36,10 @@ struct AjmAt9Decoder final : AjmCodec { void Initialize(const void* buffer, u32 buffer_size) override; void GetInfo(void* out_info) const override; AjmSidebandFormat GetFormat() const override; + u32 GetMinimumInputSize() const override; u32 GetNextFrameSize(const AjmInstanceGapless& gapless) const override; - std::tuple ProcessData(std::span& input, SparseOutputBuffer& output, - AjmInstanceGapless& gapless) override; + DecoderResult ProcessData(std::span& input, SparseOutputBuffer& output, + AjmInstanceGapless& gapless) override; private: template diff --git a/src/core/libraries/ajm/ajm_batch.h b/src/core/libraries/ajm/ajm_batch.h index 09daa630d..7f8890898 100644 --- a/src/core/libraries/ajm/ajm_batch.h +++ b/src/core/libraries/ajm/ajm_batch.h @@ -52,6 +52,7 @@ struct AjmBatch { u32 id{}; std::atomic_bool waiting{}; std::atomic_bool canceled{}; + std::atomic_bool processed{}; std::binary_semaphore finished{0}; boost::container::small_vector jobs; diff --git a/src/core/libraries/ajm/ajm_context.cpp b/src/core/libraries/ajm/ajm_context.cpp index 0e2915f32..83d38c5b5 100644 --- a/src/core/libraries/ajm/ajm_context.cpp +++ b/src/core/libraries/ajm/ajm_context.cpp @@ -39,7 +39,12 @@ s32 AjmContext::BatchCancel(const u32 batch_id) { batch = *p_batch; } - batch->canceled = true; + if (batch->processed) { + return ORBIS_OK; + } + + bool expected = false; + batch->canceled.compare_exchange_strong(expected, true); return ORBIS_OK; } @@ -58,7 +63,9 @@ void AjmContext::WorkerThread(std::stop_token stop) { Common::SetCurrentThreadName("shadPS4:AjmWorker"); while (!stop.stop_requested()) { auto batch = batch_queue.PopWait(stop); - if (batch != nullptr) { + if (batch != nullptr && !batch->canceled) { + bool expected = false; + batch->processed.compare_exchange_strong(expected, true); ProcessBatch(batch->id, batch->jobs); batch->finished.release(); } diff --git a/src/core/libraries/ajm/ajm_instance.cpp b/src/core/libraries/ajm/ajm_instance.cpp index c4ea395b9..35685e6a4 100644 --- a/src/core/libraries/ajm/ajm_instance.cpp +++ b/src/core/libraries/ajm/ajm_instance.cpp @@ -1,27 +1,15 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "core/libraries/ajm/ajm_at9.h" -#include "core/libraries/ajm/ajm_instance.h" -#include "core/libraries/ajm/ajm_mp3.h" +#include "ajm_at9.h" +#include "ajm_instance.h" +#include "ajm_mp3.h" +#include "ajm_result.h" #include namespace Libraries::Ajm { -constexpr int ORBIS_AJM_RESULT_NOT_INITIALIZED = 0x00000001; -constexpr int ORBIS_AJM_RESULT_INVALID_DATA = 0x00000002; -constexpr int ORBIS_AJM_RESULT_INVALID_PARAMETER = 0x00000004; -constexpr int ORBIS_AJM_RESULT_PARTIAL_INPUT = 0x00000008; -constexpr int ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM = 0x00000010; -constexpr int ORBIS_AJM_RESULT_STREAM_CHANGE = 0x00000020; -constexpr int ORBIS_AJM_RESULT_TOO_MANY_CHANNELS = 0x00000040; -constexpr int ORBIS_AJM_RESULT_UNSUPPORTED_FLAG = 0x00000080; -constexpr int ORBIS_AJM_RESULT_SIDEBAND_TRUNCATED = 0x00000100; -constexpr int ORBIS_AJM_RESULT_PRIORITY_PASSED = 0x00000200; -constexpr int ORBIS_AJM_RESULT_CODEC_ERROR = 0x40000000; -constexpr int ORBIS_AJM_RESULT_FATAL = 0x80000000; - u8 GetPCMSize(AjmFormatEncoding format) { switch (format) { case AjmFormatEncoding::S16: @@ -60,6 +48,7 @@ void AjmInstance::Reset() { void AjmInstance::ExecuteJob(AjmJob& job) { const auto control_flags = job.flags.control_flags; + job.output.p_result->result = 0; if (True(control_flags & AjmJobControlFlags::Reset)) { LOG_TRACE(Lib_Ajm, "Resetting instance {}", job.instance_id); Reset(); @@ -91,8 +80,7 @@ void AjmInstance::ExecuteJob(AjmJob& job) { m_gapless.current.total_samples -= sample_difference; } else { LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_INVALID_PARAMETER"); - job.output.p_result->result = ORBIS_AJM_RESULT_INVALID_PARAMETER; - return; + job.output.p_result->result |= ORBIS_AJM_RESULT_INVALID_PARAMETER; } } @@ -106,61 +94,59 @@ void AjmInstance::ExecuteJob(AjmJob& job) { m_gapless.current.skip_samples -= sample_difference; } else { LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_INVALID_PARAMETER"); - job.output.p_result->result = ORBIS_AJM_RESULT_INVALID_PARAMETER; - return; + job.output.p_result->result |= ORBIS_AJM_RESULT_INVALID_PARAMETER; } } } - if (!job.input.buffer.empty() && !job.output.buffers.empty()) { - std::span in_buf(job.input.buffer); - SparseOutputBuffer out_buf(job.output.buffers); + std::span in_buf(job.input.buffer); + SparseOutputBuffer out_buf(job.output.buffers); + auto in_size = in_buf.size(); + auto out_size = out_buf.Size(); + u32 frames_decoded = 0; - u32 frames_decoded = 0; - auto in_size = in_buf.size(); - auto out_size = out_buf.Size(); - while (!in_buf.empty() && !out_buf.IsEmpty() && !m_gapless.IsEnd()) { - if (!HasEnoughSpace(out_buf)) { - if (job.output.p_mframe == nullptr || frames_decoded == 0) { - LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM ({} < {})", - out_buf.Size(), m_codec->GetNextFrameSize(m_gapless)); - job.output.p_result->result = ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM; - break; - } - } - - const auto [nframes, nsamples, reset] = - m_codec->ProcessData(in_buf, out_buf, m_gapless); - if (reset) { + if (!job.input.buffer.empty()) { + for (;;) { + if (m_flags.gapless_loop && m_gapless.IsEnd()) { + m_gapless.Reset(); m_total_samples = 0; } - if (!nframes) { - LOG_WARNING(Lib_Ajm, "ORBIS_AJM_RESULT_NOT_INITIALIZED"); - job.output.p_result->result = ORBIS_AJM_RESULT_NOT_INITIALIZED; + if (!HasEnoughSpace(out_buf)) { + LOG_TRACE(Lib_Ajm, "ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM ({} < {})", out_buf.Size(), + m_codec->GetNextFrameSize(m_gapless)); + job.output.p_result->result |= ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM; + } + if (in_buf.size() < m_codec->GetMinimumInputSize()) { + job.output.p_result->result |= ORBIS_AJM_RESULT_PARTIAL_INPUT; + } + if (job.output.p_result->result != 0) { + break; + } + const auto result = m_codec->ProcessData(in_buf, out_buf, m_gapless); + if (result.is_reset) { + m_total_samples = 0; + } else { + m_total_samples += result.samples_written; + } + frames_decoded += result.frames_decoded; + if (result.result != 0) { + job.output.p_result->result |= result.result; + job.output.p_result->internal_result = result.internal_result; break; } - frames_decoded += nframes; - m_total_samples += nsamples; - if (False(job.flags.run_flags & AjmJobRunFlags::MultipleFrames)) { break; } } + } - const auto total_decoded_samples = m_total_samples; - if (m_flags.gapless_loop && m_gapless.IsEnd()) { - in_buf = in_buf.subspan(in_buf.size()); - m_gapless.Reset(); - m_codec->Reset(); - } - if (job.output.p_mframe) { - job.output.p_mframe->num_frames = frames_decoded; - } - if (job.output.p_stream) { - job.output.p_stream->input_consumed = in_size - in_buf.size(); - job.output.p_stream->output_written = out_size - out_buf.Size(); - job.output.p_stream->total_decoded_samples = total_decoded_samples; - } + if (job.output.p_mframe) { + job.output.p_mframe->num_frames = frames_decoded; + } + if (job.output.p_stream) { + job.output.p_stream->input_consumed = in_size - in_buf.size(); + job.output.p_stream->output_written = out_size - out_buf.Size(); + job.output.p_stream->total_decoded_samples = m_total_samples; } if (job.output.p_format != nullptr) { @@ -175,6 +161,9 @@ void AjmInstance::ExecuteJob(AjmJob& job) { } bool AjmInstance::HasEnoughSpace(const SparseOutputBuffer& output) const { + if (m_gapless.IsEnd()) { + return true; + } return output.Size() >= m_codec->GetNextFrameSize(m_gapless); } diff --git a/src/core/libraries/ajm/ajm_instance.h b/src/core/libraries/ajm/ajm_instance.h index ad0a82f29..db53add4d 100644 --- a/src/core/libraries/ajm/ajm_instance.h +++ b/src/core/libraries/ajm/ajm_instance.h @@ -73,6 +73,14 @@ struct AjmInstanceGapless { } }; +struct DecoderResult { + s32 result = 0; + s32 internal_result = 0; + u32 frames_decoded = 0; + u32 samples_written = 0; + bool is_reset = false; +}; + class AjmCodec { public: virtual ~AjmCodec() = default; @@ -81,9 +89,10 @@ public: virtual void Reset() = 0; virtual void GetInfo(void* out_info) const = 0; virtual AjmSidebandFormat GetFormat() const = 0; + virtual u32 GetMinimumInputSize() const = 0; virtual u32 GetNextFrameSize(const AjmInstanceGapless& gapless) const = 0; - virtual std::tuple ProcessData(std::span& input, SparseOutputBuffer& output, - AjmInstanceGapless& gapless) = 0; + virtual DecoderResult ProcessData(std::span& input, SparseOutputBuffer& output, + AjmInstanceGapless& gapless) = 0; }; class AjmInstance { @@ -94,7 +103,6 @@ public: private: bool HasEnoughSpace(const SparseOutputBuffer& output) const; - std::optional GetNumRemainingSamples() const; void Reset(); AjmInstanceFlags m_flags{}; diff --git a/src/core/libraries/ajm/ajm_instance_statistics.cpp b/src/core/libraries/ajm/ajm_instance_statistics.cpp index c0c1af8bb..2e4a65914 100644 --- a/src/core/libraries/ajm/ajm_instance_statistics.cpp +++ b/src/core/libraries/ajm/ajm_instance_statistics.cpp @@ -8,7 +8,7 @@ namespace Libraries::Ajm { void AjmInstanceStatistics::ExecuteJob(AjmJob& job) { if (job.output.p_engine) { - job.output.p_engine->usage_batch = 0.01; + job.output.p_engine->usage_batch = 0.05; const auto ic = job.input.statistics_engine_parameters->interval_count; for (u32 idx = 0; idx < ic; ++idx) { job.output.p_engine->usage_interval[idx] = 0.01; @@ -25,10 +25,12 @@ void AjmInstanceStatistics::ExecuteJob(AjmJob& job) { job.output.p_memory->batch_size = 0x4200; job.output.p_memory->input_size = 0x2000; job.output.p_memory->output_size = 0x2000; - job.output.p_memory->small_size = 0x200; + job.output.p_memory->small_size = 0x400; } } +void AjmInstanceStatistics::Reset() {} + AjmInstanceStatistics& AjmInstanceStatistics::Getinstance() { static AjmInstanceStatistics instance; return instance; diff --git a/src/core/libraries/ajm/ajm_instance_statistics.h b/src/core/libraries/ajm/ajm_instance_statistics.h index ea70c9d56..0ec79aeac 100644 --- a/src/core/libraries/ajm/ajm_instance_statistics.h +++ b/src/core/libraries/ajm/ajm_instance_statistics.h @@ -10,6 +10,7 @@ namespace Libraries::Ajm { class AjmInstanceStatistics { public: void ExecuteJob(AjmJob& job); + void Reset(); static AjmInstanceStatistics& Getinstance(); }; diff --git a/src/core/libraries/ajm/ajm_mp3.cpp b/src/core/libraries/ajm/ajm_mp3.cpp index f17f53d51..f8d77f031 100644 --- a/src/core/libraries/ajm/ajm_mp3.cpp +++ b/src/core/libraries/ajm/ajm_mp3.cpp @@ -122,6 +122,7 @@ void AjmMp3Decoder::Reset() { avcodec_flush_buffers(m_codec_context); m_header.reset(); m_frame_samples = 0; + m_frame_size = 0; } void AjmMp3Decoder::GetInfo(void* out_info) const { @@ -138,16 +139,28 @@ void AjmMp3Decoder::GetInfo(void* out_info) const { } } -std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, - SparseOutputBuffer& output, - AjmInstanceGapless& gapless) { +u32 AjmMp3Decoder::GetMinimumInputSize() const { + // 4 bytes is for mp3 header that contains frame_size + return std::max(m_frame_size, 4); +} + +DecoderResult AjmMp3Decoder::ProcessData(std::span& in_buf, SparseOutputBuffer& output, + AjmInstanceGapless& gapless) { + DecoderResult result{}; AVPacket* pkt = av_packet_alloc(); if ((!m_header.has_value() || m_frame_samples == 0) && in_buf.size() >= 4) { m_header = std::byteswap(*reinterpret_cast(in_buf.data())); AjmDecMp3ParseFrame info{}; - ParseMp3Header(in_buf.data(), in_buf.size(), false, &info); + ParseMp3Header(in_buf.data(), in_buf.size(), true, &info); m_frame_samples = info.samples_per_channel; + m_frame_size = info.frame_size; + gapless.init = { + .total_samples = info.total_samples, + .skip_samples = static_cast(info.encoder_delay), + .skipped_samples = 0, + }; + gapless.current = gapless.init; } int ret = av_parser_parse2(m_parser, m_codec_context, &pkt->data, &pkt->size, in_buf.data(), @@ -155,9 +168,6 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, ASSERT_MSG(ret >= 0, "Error while parsing {}", ret); in_buf = in_buf.subspan(ret); - u32 frames_decoded = 0; - u32 samples_decoded = 0; - if (pkt->size) { // Send the packet with the compressed data to the decoder pkt->pts = m_parser->pts; @@ -177,9 +187,8 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, UNREACHABLE_MSG("Error during decoding"); } frame = ConvertAudioFrame(frame); - samples_decoded += u32(frame->nb_samples); - frames_decoded += 1; + result.frames_decoded += 1; u32 skip_samples = 0; if (gapless.current.skip_samples > 0) { skip_samples = std::min(u16(frame->nb_samples), gapless.current.skip_samples); @@ -211,6 +220,7 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, if (gapless.init.total_samples != 0) { gapless.current.total_samples -= samples; } + result.samples_written += samples; av_frame_free(&frame); } @@ -218,16 +228,16 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, av_packet_free(&pkt); - return {frames_decoded, samples_decoded, false}; + return result; } u32 AjmMp3Decoder::GetNextFrameSize(const AjmInstanceGapless& gapless) const { - const auto max_samples = gapless.init.total_samples != 0 - ? std::min(gapless.current.total_samples, m_frame_samples) - : m_frame_samples; - const auto skip_samples = std::min(u32(gapless.current.skip_samples), max_samples); - return (max_samples - skip_samples) * m_codec_context->ch_layout.nb_channels * - GetPCMSize(m_format); + const auto skip_samples = std::min(gapless.current.skip_samples, m_frame_samples); + const auto samples = + gapless.init.total_samples != 0 + ? std::min(gapless.current.total_samples, m_frame_samples - skip_samples) + : m_frame_samples - skip_samples; + return samples * m_codec_context->ch_layout.nb_channels * GetPCMSize(m_format); } class BitReader { @@ -264,7 +274,7 @@ private: int AjmMp3Decoder::ParseMp3Header(const u8* p_begin, u32 stream_size, int parse_ofl, AjmDecMp3ParseFrame* frame) { - LOG_INFO(Lib_Ajm, "called stream_size = {} parse_ofl = {}", stream_size, parse_ofl); + LOG_TRACE(Lib_Ajm, "called stream_size = {} parse_ofl = {}", stream_size, parse_ofl); if (p_begin == nullptr || stream_size < 4 || frame == nullptr) { return ORBIS_AJM_ERROR_INVALID_PARAMETER; diff --git a/src/core/libraries/ajm/ajm_mp3.h b/src/core/libraries/ajm/ajm_mp3.h index 7ac65fdba..c03d5ba15 100644 --- a/src/core/libraries/ajm/ajm_mp3.h +++ b/src/core/libraries/ajm/ajm_mp3.h @@ -70,9 +70,10 @@ public: void Initialize(const void* buffer, u32 buffer_size) override {} void GetInfo(void* out_info) const override; AjmSidebandFormat GetFormat() const override; + u32 GetMinimumInputSize() const override; u32 GetNextFrameSize(const AjmInstanceGapless& gapless) const override; - std::tuple ProcessData(std::span& input, SparseOutputBuffer& output, - AjmInstanceGapless& gapless) override; + DecoderResult ProcessData(std::span& input, SparseOutputBuffer& output, + AjmInstanceGapless& gapless) override; static int ParseMp3Header(const u8* buf, u32 stream_size, int parse_ofl, AjmDecMp3ParseFrame* frame); @@ -97,6 +98,7 @@ private: SwrContext* m_swr_context = nullptr; std::optional m_header; u32 m_frame_samples = 0; + u32 m_frame_size = 0; }; } // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_result.h b/src/core/libraries/ajm/ajm_result.h new file mode 100644 index 000000000..d4e6d1147 --- /dev/null +++ b/src/core/libraries/ajm/ajm_result.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +constexpr int ORBIS_AJM_RESULT_NOT_INITIALIZED = 0x00000001; +constexpr int ORBIS_AJM_RESULT_INVALID_DATA = 0x00000002; +constexpr int ORBIS_AJM_RESULT_INVALID_PARAMETER = 0x00000004; +constexpr int ORBIS_AJM_RESULT_PARTIAL_INPUT = 0x00000008; +constexpr int ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM = 0x00000010; +constexpr int ORBIS_AJM_RESULT_STREAM_CHANGE = 0x00000020; +constexpr int ORBIS_AJM_RESULT_TOO_MANY_CHANNELS = 0x00000040; +constexpr int ORBIS_AJM_RESULT_UNSUPPORTED_FLAG = 0x00000080; +constexpr int ORBIS_AJM_RESULT_SIDEBAND_TRUNCATED = 0x00000100; +constexpr int ORBIS_AJM_RESULT_PRIORITY_PASSED = 0x00000200; +constexpr int ORBIS_AJM_RESULT_CODEC_ERROR = 0x40000000; +constexpr int ORBIS_AJM_RESULT_FATAL = 0x80000000;