mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-03-25 20:20:21 -06:00
ajm fixes (#3875)
This commit is contained in:
parent
138425fdf4
commit
05f14e3682
@ -133,7 +133,7 @@ struct AjmSidebandGaplessDecode {
|
||||
|
||||
struct AjmSidebandResampleParameters {
|
||||
float ratio;
|
||||
uint32_t flags;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
struct AjmDecAt9InitializeParameters {
|
||||
|
||||
@ -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<AjmSidebandDecAt9CodecInfo*>(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<Atrac9Handle*>(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<u8>& in_buf, AjmInstanceGapless& g
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<u32, u32, bool> AjmAt9Decoder::ProcessData(std::span<u8>& in_buf,
|
||||
SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) {
|
||||
bool is_reset = false;
|
||||
u32 AjmAt9Decoder::GetMinimumInputSize() const {
|
||||
return m_superframe_bytes_remain;
|
||||
}
|
||||
|
||||
DecoderResult AjmAt9Decoder::ProcessData(std::span<u8>& in_buf, SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) {
|
||||
DecoderResult result{};
|
||||
if (True(m_flags & AjmAt9CodecFlags::ParseRiffHeader) &&
|
||||
*reinterpret_cast<u32*>(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<u32, u32, bool> AjmAt9Decoder::ProcessData(std::span<u8>& 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<u32, u32, bool> AjmAt9Decoder::ProcessData(std::span<u8>& 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<u32, u32, bool> AjmAt9Decoder::ProcessData(std::span<u8>& in_buf,
|
||||
}
|
||||
m_superframe_bytes_remain = m_codec_info.superframeSize;
|
||||
m_num_frames = 0;
|
||||
} else if (gapless.IsEnd()) {
|
||||
// Drain the remaining superframe
|
||||
std::vector<s16> 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<u32>(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);
|
||||
}
|
||||
|
||||
|
||||
@ -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<u32, u32, bool> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) override;
|
||||
DecoderResult ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) override;
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
|
||||
@ -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<AjmJob, 16> jobs;
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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 <magic_enum/magic_enum.hpp>
|
||||
|
||||
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<u8> in_buf(job.input.buffer);
|
||||
SparseOutputBuffer out_buf(job.output.buffers);
|
||||
std::span<u8> 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);
|
||||
}
|
||||
|
||||
|
||||
@ -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<u32, u32, bool> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) = 0;
|
||||
virtual DecoderResult ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) = 0;
|
||||
};
|
||||
|
||||
class AjmInstance {
|
||||
@ -94,7 +103,6 @@ public:
|
||||
|
||||
private:
|
||||
bool HasEnoughSpace(const SparseOutputBuffer& output) const;
|
||||
std::optional<u32> GetNumRemainingSamples() const;
|
||||
void Reset();
|
||||
|
||||
AjmInstanceFlags m_flags{};
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -10,6 +10,7 @@ namespace Libraries::Ajm {
|
||||
class AjmInstanceStatistics {
|
||||
public:
|
||||
void ExecuteJob(AjmJob& job);
|
||||
void Reset();
|
||||
|
||||
static AjmInstanceStatistics& Getinstance();
|
||||
};
|
||||
|
||||
@ -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<u32, u32, bool> AjmMp3Decoder::ProcessData(std::span<u8>& in_buf,
|
||||
SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) {
|
||||
u32 AjmMp3Decoder::GetMinimumInputSize() const {
|
||||
// 4 bytes is for mp3 header that contains frame_size
|
||||
return std::max<u32>(m_frame_size, 4);
|
||||
}
|
||||
|
||||
DecoderResult AjmMp3Decoder::ProcessData(std::span<u8>& 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<u32*>(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<u16>(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<u32, u32, bool> AjmMp3Decoder::ProcessData(std::span<u8>& 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<u32, u32, bool> AjmMp3Decoder::ProcessData(std::span<u8>& 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<u32, u32, bool> AjmMp3Decoder::ProcessData(std::span<u8>& 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<u32, u32, bool> AjmMp3Decoder::ProcessData(std::span<u8>& 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<u32>(gapless.current.skip_samples, m_frame_samples);
|
||||
const auto samples =
|
||||
gapless.init.total_samples != 0
|
||||
? std::min<u32>(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;
|
||||
|
||||
@ -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<u32, u32, bool> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
|
||||
AjmInstanceGapless& gapless) override;
|
||||
DecoderResult ProcessData(std::span<u8>& 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<u32> m_header;
|
||||
u32 m_frame_samples = 0;
|
||||
u32 m_frame_size = 0;
|
||||
};
|
||||
|
||||
} // namespace Libraries::Ajm
|
||||
|
||||
17
src/core/libraries/ajm/ajm_result.h
Normal file
17
src/core/libraries/ajm/ajm_result.h
Normal file
@ -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;
|
||||
Loading…
Reference in New Issue
Block a user