ajm: Attempt to add gapless support

This commit is contained in:
IndecisiveTurtle 2024-10-28 00:31:56 +02:00 committed by Vladislav Mikhalin
commit d41bb9c57c
5 changed files with 71 additions and 37 deletions

View file

@ -1,15 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <condition_variable>
#include <mutex> #include <mutex>
#include <numeric> #include <numeric>
#include <semaphore>
#include <boost/container/small_vector.hpp> #include <boost/container/small_vector.hpp>
#include <magic_enum.hpp> #include <magic_enum.hpp>
#include "common/alignment.h" #include "common/alignment.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scope_exit.h"
#include "core/libraries/ajm/ajm.h" #include "core/libraries/ajm/ajm.h"
#include "core/libraries/ajm/ajm_at9.h" #include "core/libraries/ajm/ajm_at9.h"
#include "core/libraries/ajm/ajm_error.h" #include "core/libraries/ajm/ajm_error.h"
@ -40,9 +41,7 @@ struct BatchInfo {
u16 instance{}; u16 instance{};
u16 offset_in_qwords{}; // Needed for AjmBatchError? u16 offset_in_qwords{}; // Needed for AjmBatchError?
bool waiting{}; bool waiting{};
bool finished{}; std::binary_semaphore finished{0};
std::mutex mtx;
std::condition_variable cv;
int result{}; int result{};
}; };
@ -377,11 +376,11 @@ int PS4_SYSV_ABI sceAjmBatchStartBuffer(u32 context, const u8* batch, u32 batch_
// Perform operation requested by control flags. // Perform operation requested by control flags.
const auto control_flags = job_flags.value().control_flags; const auto control_flags = job_flags.value().control_flags;
if (True(control_flags & AjmJobControlFlags::Reset)) { if (True(control_flags & AjmJobControlFlags::Reset)) {
LOG_TRACE(Lib_Ajm, "Resetting instance {}", instance); LOG_INFO(Lib_Ajm, "Resetting instance {}", instance);
p_instance->Reset(); p_instance->Reset();
} }
if (True(control_flags & AjmJobControlFlags::Initialize)) { 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(), ASSERT_MSG(input_control_buffer.has_value(),
"Initialize called without control buffer"); "Initialize called without control buffer");
const auto& in_buffer = input_control_buffer.value(); 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)) { if (True(sideband_flags & AjmJobSidebandFlags::GaplessDecode)) {
LOG_ERROR(Lib_Ajm, "SIDEBAND_GAPLESS_DECODE is not implemented"); LOG_ERROR(Lib_Ajm, "SIDEBAND_GAPLESS_DECODE is not implemented");
p_gapless_decode = &AjmBufferExtract<AjmSidebandGaplessDecode>(p_sideband); p_gapless_decode = &AjmBufferExtract<AjmSidebandGaplessDecode>(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{}; *p_gapless_decode = AjmSidebandGaplessDecode{};
} }
const auto run_flags = job_flags.value().run_flags; 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; p_result->internal_result = 0;
} }
batch_info->finished = true; batch_info->finished.release();
return ORBIS_OK; return ORBIS_OK;
} }
@ -480,14 +490,14 @@ int PS4_SYSV_ABI sceAjmBatchWait(const u32 context, const u32 batch_id, const u3
if (batch->waiting) { if (batch->waiting) {
return ORBIS_AJM_ERROR_BUSY; return ORBIS_AJM_ERROR_BUSY;
} }
batch->waiting = true;
{ batch->waiting = true;
std::unique_lock lk{batch->mtx}; SCOPE_EXIT {
if (!batch->cv.wait_for(lk, std::chrono::milliseconds(timeout), batch->waiting = false;
[&] { return batch->finished; })) { };
return ORBIS_AJM_ERROR_IN_PROGRESS;
} if (!batch->finished.try_acquire_for(std::chrono::milliseconds(timeout))) {
return ORBIS_AJM_ERROR_IN_PROGRESS;
} }
dev->batches.erase(dev->batches.begin() + batch_id); dev->batches.erase(dev->batches.begin() + batch_id);

View file

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <vector>
#include <fmt/format.h> #include <fmt/format.h>
#include "common/assert.h" #include "common/assert.h"
#include "core/libraries/ajm/ajm_at9.h" #include "core/libraries/ajm/ajm_at9.h"
@ -25,17 +26,25 @@ AjmAt9Decoder::~AjmAt9Decoder() {
} }
void AjmAt9Decoder::Reset() { void AjmAt9Decoder::Reset() {
num_frames = 0;
decoded_samples = 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) { void AjmAt9Decoder::Initialize(const void* buffer, u32 buffer_size) {
Atrac9ReleaseHandle(handle);
handle = Atrac9GetHandle();
ASSERT_MSG(buffer_size == sizeof(AjmDecAt9InitializeParameters), ASSERT_MSG(buffer_size == sizeof(AjmDecAt9InitializeParameters),
"Incorrect At9 initialization buffer size {}", buffer_size); "Incorrect At9 initialization buffer size {}", buffer_size);
const auto params = reinterpret_cast<const AjmDecAt9InitializeParameters*>(buffer); const auto params = reinterpret_cast<const AjmDecAt9InitializeParameters*>(buffer);
std::memcpy(config_data, params->config_data, SCE_AT9_CONFIG_DATA_SIZE); std::memcpy(config_data, params->config_data, SCE_AT9_CONFIG_DATA_SIZE);
Atrac9InitDecoder(handle, config_data); AjmAt9Decoder::Reset();
} }
void AjmAt9Decoder::GetCodecInfo(void* out_info) { void AjmAt9Decoder::GetCodecInfo(void* out_info) {
@ -51,36 +60,48 @@ void AjmAt9Decoder::GetCodecInfo(void* out_info) {
std::tuple<u32, u32, u32> AjmAt9Decoder::Decode(const u8* in_buf, u32 in_size, u8* out_buf, std::tuple<u32, u32, u32> AjmAt9Decoder::Decode(const u8* in_buf, u32 in_size, u8* out_buf,
u32 out_size) { u32 out_size) {
if (in_size <= 0 || out_size <= 0) {
return std::tuple(in_size, out_size, 0);
}
const auto decoder_handle = static_cast<Atrac9Handle*>(handle); const auto decoder_handle = static_cast<Atrac9Handle*>(handle);
Atrac9CodecInfo codec_info; Atrac9CodecInfo codec_info;
Atrac9GetCodecInfo(handle, &codec_info); Atrac9GetCodecInfo(handle, &codec_info);
int bytes_used = 0; 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); const auto written_size = codec_info.channels * codec_info.frameSamples * sizeof(u16);
for (frame_count = 0; std::vector<s16> pcm_buffer(written_size >> 1);
frame_count < decoder_handle->Config.FramesPerSuperframe && in_size > 0 && out_size > 0; while (ShouldDecode()) {
frame_count++) { u32 ret = Atrac9Decode(decoder_handle, in_buf, pcm_buffer.data(), &bytes_used);
u32 ret = Atrac9Decode(decoder_handle, in_buf, (short*)out_buf, &bytes_used);
ASSERT_MSG(ret == At9Status::ERR_SUCCESS, "Atrac9Decode failed ret = {:#x}", ret); ASSERT_MSG(ret == At9Status::ERR_SUCCESS, "Atrac9Decode failed ret = {:#x}", ret);
in_buf += bytes_used; in_buf += bytes_used;
in_size -= bytes_used; in_size -= bytes_used;
num_frames++;
bytes_remain -= bytes_used; bytes_remain -= bytes_used;
out_buf += written_size; if (gapless.skip_samples != 0) {
out_size -= written_size; gapless.skip_samples -= decoder_handle->Config.FrameSamples;
decoded_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_index);
return std::tuple(in_size, out_size, num_frames);
LOG_TRACE(Lib_Ajm, "Decoded {} samples, frame count: {}", decoded_samples, frame_count);
return std::tuple(in_size, out_size, frame_count);
} }
} // namespace Libraries::Ajm } // namespace Libraries::Ajm

View file

@ -62,10 +62,13 @@ struct AjmSidebandGaplessDecode {
struct AjmInstance { struct AjmInstance {
AjmCodecType codec_type; AjmCodecType codec_type;
u32 decoded_samples{};
AjmFormatEncoding fmt{}; AjmFormatEncoding fmt{};
u32 num_channels{}; u32 num_channels{};
u32 index{}; u32 index{};
u32 bytes_remain{};
u32 num_frames{};
u32 decoded_samples{};
AjmSidebandGaplessDecode gapless{};
explicit AjmInstance() = default; explicit AjmInstance() = default;
virtual ~AjmInstance() = default; virtual ~AjmInstance() = default;

View file

@ -74,11 +74,11 @@ void AjmMp3Decoder::Reset() {
int ret = avcodec_open2(c, codec, nullptr); int ret = avcodec_open2(c, codec, nullptr);
ASSERT_MSG(ret >= 0, "Could not open codec"); ASSERT_MSG(ret >= 0, "Could not open codec");
decoded_samples = 0; decoded_samples = 0;
num_frames = 0;
} }
std::tuple<u32, u32, u32> AjmMp3Decoder::Decode(const u8* buf, u32 in_size, u8* out_buf, std::tuple<u32, u32, u32> AjmMp3Decoder::Decode(const u8* buf, u32 in_size, u8* out_buf,
u32 out_size) { u32 out_size) {
u32 num_frames = 0;
AVPacket* pkt = av_packet_alloc(); AVPacket* pkt = av_packet_alloc();
while (in_size > 0 && out_size > 0) { while (in_size > 0 && out_size > 0) {
int ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, buf, in_size, AV_NOPTS_VALUE, int ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, buf, in_size, AV_NOPTS_VALUE,

View file

@ -582,7 +582,7 @@ int PS4_SYSV_ABI sceHttpUriUnescape() {
} }
int PS4_SYSV_ABI sceHttpWaitRequest() { int PS4_SYSV_ABI sceHttpWaitRequest() {
LOG_ERROR(Lib_Http, "(STUBBED) called"); // LOG_ERROR(Lib_Http, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }