From 18c883d8b2069c89c9a9b3551390fdb81910dce5 Mon Sep 17 00:00:00 2001 From: IndecisiveTurtle Date: Mon, 28 Oct 2024 00:31:56 +0200 Subject: [PATCH] ajm: Attempt to add gapless support --- src/core/libraries/ajm/ajm.cpp | 38 +++++++++++------ src/core/libraries/ajm/ajm_at9.cpp | 61 ++++++++++++++++++--------- src/core/libraries/ajm/ajm_instance.h | 5 ++- src/core/libraries/ajm/ajm_mp3.cpp | 2 +- src/core/libraries/network/http.cpp | 2 +- 5 files changed, 71 insertions(+), 37 deletions(-) diff --git a/src/core/libraries/ajm/ajm.cpp b/src/core/libraries/ajm/ajm.cpp index 0d8f16d10..da9c397f2 100644 --- a/src/core/libraries/ajm/ajm.cpp +++ b/src/core/libraries/ajm/ajm.cpp @@ -1,15 +1,16 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include #include +#include #include #include #include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" +#include "common/scope_exit.h" #include "core/libraries/ajm/ajm.h" #include "core/libraries/ajm/ajm_at9.h" #include "core/libraries/ajm/ajm_error.h" @@ -40,9 +41,7 @@ struct BatchInfo { u16 instance{}; u16 offset_in_qwords{}; // Needed for AjmBatchError? bool waiting{}; - bool finished{}; - std::mutex mtx; - std::condition_variable cv; + std::binary_semaphore finished{0}; int result{}; }; @@ -377,11 +376,11 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_ // Perform operation requested by control flags. const auto control_flags = job_flags.value().control_flags; if (True(control_flags & AjmJobControlFlags::Reset)) { - LOG_TRACE(Lib_Ajm, "Resetting instance {}", instance); + LOG_INFO(Lib_Ajm, "Resetting instance {}", instance); p_instance->Reset(); } if (True(control_flags & AjmJobControlFlags::Initialize)) { - LOG_TRACE(Lib_Ajm, "Initializing instance {}", instance); + LOG_INFO(Lib_Ajm, "Initializing instance {}", instance); ASSERT_MSG(input_control_buffer.has_value(), "Initialize called without control buffer"); const auto& in_buffer = input_control_buffer.value(); @@ -417,6 +416,17 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_ if (True(sideband_flags & AjmJobSidebandFlags::GaplessDecode)) { LOG_ERROR(Lib_Ajm, "SIDEBAND_GAPLESS_DECODE is not implemented"); p_gapless_decode = &AjmBufferExtract(p_sideband); + if (input_control_buffer) { + memcpy(&p_instance->gapless, input_control_buffer->p_address, + sizeof(AjmSidebandGaplessDecode)); + LOG_INFO(Lib_Ajm, + "Setting gapless params instance = {}, total_samples = {}, " + "skip_samples = {}", + instance, p_instance->gapless.total_samples, + p_instance->gapless.skip_samples); + } else { + LOG_ERROR(Lib_Ajm, "Requesting gapless structure!"); + } *p_gapless_decode = AjmSidebandGaplessDecode{}; } const auto run_flags = job_flags.value().run_flags; @@ -461,7 +471,7 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_ p_result->internal_result = 0; } - batch_info->finished = true; + batch_info->finished.release(); return ORBIS_OK; } @@ -480,14 +490,14 @@ int PS4_SYSV_ABI sceAjmBatchWait(const u32 context, const u32 batch_id, const u3 if (batch->waiting) { return ORBIS_AJM_ERROR_BUSY; } - batch->waiting = true; - { - std::unique_lock lk{batch->mtx}; - if (!batch->cv.wait_for(lk, std::chrono::milliseconds(timeout), - [&] { return batch->finished; })) { - return ORBIS_AJM_ERROR_IN_PROGRESS; - } + batch->waiting = true; + SCOPE_EXIT { + batch->waiting = false; + }; + + if (!batch->finished.try_acquire_for(std::chrono::milliseconds(timeout))) { + return ORBIS_AJM_ERROR_IN_PROGRESS; } dev->batches.erase(dev->batches.begin() + batch_id); diff --git a/src/core/libraries/ajm/ajm_at9.cpp b/src/core/libraries/ajm/ajm_at9.cpp index 55719aeae..0ac2f7efa 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 #include #include "common/assert.h" #include "core/libraries/ajm/ajm_at9.h" @@ -25,17 +26,25 @@ AjmAt9Decoder::~AjmAt9Decoder() { } void AjmAt9Decoder::Reset() { + num_frames = 0; decoded_samples = 0; + gapless = {}; + + Atrac9ReleaseHandle(handle); + handle = Atrac9GetHandle(); + Atrac9InitDecoder(handle, config_data); + + Atrac9CodecInfo codec_info; + Atrac9GetCodecInfo(handle, &codec_info); + bytes_remain = codec_info.superframeSize; } void AjmAt9Decoder::Initialize(const void* buffer, u32 buffer_size) { - Atrac9ReleaseHandle(handle); - handle = Atrac9GetHandle(); ASSERT_MSG(buffer_size == sizeof(AjmDecAt9InitializeParameters), "Incorrect At9 initialization buffer size {}", buffer_size); const auto params = reinterpret_cast(buffer); std::memcpy(config_data, params->config_data, SCE_AT9_CONFIG_DATA_SIZE); - Atrac9InitDecoder(handle, config_data); + AjmAt9Decoder::Reset(); } void AjmAt9Decoder::GetCodecInfo(void* out_info) { @@ -51,36 +60,48 @@ void AjmAt9Decoder::GetCodecInfo(void* out_info) { std::tuple AjmAt9Decoder::Decode(const u8* in_buf, u32 in_size, u8* out_buf, u32 out_size) { - if (in_size <= 0 || out_size <= 0) { - return std::tuple(in_size, out_size, 0); - } - const auto decoder_handle = static_cast(handle); Atrac9CodecInfo codec_info; Atrac9GetCodecInfo(handle, &codec_info); int bytes_used = 0; - int frame_count = 0; - int bytes_remain = codec_info.superframeSize; + + const auto ShouldDecode = [&] { + if (in_size <= 0 || out_size <= 0) { + return false; + } + if (gapless.total_samples != 0 && gapless.total_samples < decoded_samples) { + return false; + } + return true; + }; const auto written_size = codec_info.channels * codec_info.frameSamples * sizeof(u16); - for (frame_count = 0; - frame_count < decoder_handle->Config.FramesPerSuperframe && in_size > 0 && out_size > 0; - frame_count++) { - u32 ret = Atrac9Decode(decoder_handle, in_buf, (short*)out_buf, &bytes_used); + std::vector pcm_buffer(written_size >> 1); + while (ShouldDecode()) { + u32 ret = Atrac9Decode(decoder_handle, in_buf, pcm_buffer.data(), &bytes_used); ASSERT_MSG(ret == At9Status::ERR_SUCCESS, "Atrac9Decode failed ret = {:#x}", ret); in_buf += bytes_used; in_size -= bytes_used; + num_frames++; bytes_remain -= bytes_used; - out_buf += written_size; - out_size -= written_size; - decoded_samples += decoder_handle->Config.FrameSamples; + if (gapless.skip_samples != 0) { + gapless.skip_samples -= decoder_handle->Config.FrameSamples; + } else { + memcpy(out_buf, pcm_buffer.data(), written_size); + out_buf += written_size; + out_size -= written_size; + decoded_samples += decoder_handle->Config.FrameSamples; + } + if ((num_frames % codec_info.framesInSuperframe) == 0) { + in_buf += bytes_remain; + in_size -= bytes_remain; + bytes_remain = codec_info.superframeSize; + } } - in_size -= bytes_remain; - - LOG_TRACE(Lib_Ajm, "Decoded {} samples, frame count: {}", decoded_samples, frame_count); - return std::tuple(in_size, out_size, frame_count); + LOG_TRACE(Lib_Ajm, "Decoded {} samples, frame count: {}", decoded_samples, frame_index); + return std::tuple(in_size, out_size, num_frames); } } // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_instance.h b/src/core/libraries/ajm/ajm_instance.h index e9de6deb6..de501f059 100644 --- a/src/core/libraries/ajm/ajm_instance.h +++ b/src/core/libraries/ajm/ajm_instance.h @@ -62,10 +62,13 @@ struct AjmSidebandGaplessDecode { struct AjmInstance { AjmCodecType codec_type; - u32 decoded_samples{}; AjmFormatEncoding fmt{}; u32 num_channels{}; u32 index{}; + u32 bytes_remain{}; + u32 num_frames{}; + u32 decoded_samples{}; + AjmSidebandGaplessDecode gapless{}; explicit AjmInstance() = default; virtual ~AjmInstance() = default; diff --git a/src/core/libraries/ajm/ajm_mp3.cpp b/src/core/libraries/ajm/ajm_mp3.cpp index 42d09a600..bed3569d4 100644 --- a/src/core/libraries/ajm/ajm_mp3.cpp +++ b/src/core/libraries/ajm/ajm_mp3.cpp @@ -74,11 +74,11 @@ void AjmMp3Decoder::Reset() { int ret = avcodec_open2(c, codec, nullptr); ASSERT_MSG(ret >= 0, "Could not open codec"); decoded_samples = 0; + num_frames = 0; } std::tuple AjmMp3Decoder::Decode(const u8* buf, u32 in_size, u8* out_buf, u32 out_size) { - u32 num_frames = 0; AVPacket* pkt = av_packet_alloc(); while (in_size > 0 && out_size > 0) { int ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, buf, in_size, AV_NOPTS_VALUE, diff --git a/src/core/libraries/network/http.cpp b/src/core/libraries/network/http.cpp index 8e06c76fa..270d908f9 100644 --- a/src/core/libraries/network/http.cpp +++ b/src/core/libraries/network/http.cpp @@ -582,7 +582,7 @@ int PS4_SYSV_ABI sceHttpUriUnescape() { } int PS4_SYSV_ABI sceHttpWaitRequest() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); + // LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_OK; }