ajm: Attempt to add gapless support

This commit is contained in:
IndecisiveTurtle 2024-10-28 00:31:56 +02:00
parent 32f5dafaf7
commit 18c883d8b2
5 changed files with 71 additions and 37 deletions

View file

@ -1,15 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <condition_variable>
#include <mutex>
#include <numeric>
#include <semaphore>
#include <boost/container/small_vector.hpp>
#include <magic_enum.hpp>
#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<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{};
}
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);

View file

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <vector>
#include <fmt/format.h>
#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<const AjmDecAt9InitializeParameters*>(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<u32, u32, u32> 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<Atrac9Handle*>(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<s16> 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

View file

@ -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;

View file

@ -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<u32, u32, u32> 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,

View file

@ -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;
}