diff --git a/src/core/libraries/ajm/ajm_at9.cpp b/src/core/libraries/ajm/ajm_at9.cpp index e725f5c8a..7f28988d1 100644 --- a/src/core/libraries/ajm/ajm_at9.cpp +++ b/src/core/libraries/ajm/ajm_at9.cpp @@ -123,14 +123,16 @@ AjmSidebandFormat AjmAt9Decoder::GetFormat() const { .channel_mask = GetChannelMask(u32(m_codec_info.channels)), .sampl_freq = u32(m_codec_info.samplingRate), .sample_encoding = m_format, - .bitrate = u32(m_codec_info.samplingRate * GetPCMSize(m_format) * 8), + .bitrate = u32((m_codec_info.samplingRate * m_codec_info.superframeSize * 8) / + (m_codec_info.framesInSuperframe * m_codec_info.frameSamples)), .reserved = 0, }; } -u32 AjmAt9Decoder::GetNextFrameSize(u32 max_samples) const { - return std::min(u32(m_codec_info.frameSamples), max_samples) * m_codec_info.channels * - GetPCMSize(m_format); +u32 AjmAt9Decoder::GetNextFrameSize(u32 skip_samples, u32 max_samples) const { + skip_samples = std::min({skip_samples, u32(m_codec_info.frameSamples), max_samples}); + return (std::min(u32(m_codec_info.frameSamples), max_samples) - skip_samples) * + m_codec_info.channels * GetPCMSize(m_format); } } // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_at9.h b/src/core/libraries/ajm/ajm_at9.h index 93ec5dcef..671ebf054 100644 --- a/src/core/libraries/ajm/ajm_at9.h +++ b/src/core/libraries/ajm/ajm_at9.h @@ -35,7 +35,7 @@ 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 GetNextFrameSize(u32 max_samples) const override; + u32 GetNextFrameSize(u32 skip_samples, u32 max_samples) const override; std::tuple ProcessData(std::span& input, SparseOutputBuffer& output, AjmSidebandGaplessDecode& gapless, std::optional max_samples) override; diff --git a/src/core/libraries/ajm/ajm_batch.cpp b/src/core/libraries/ajm/ajm_batch.cpp index 5c76beae8..b1cec88b3 100644 --- a/src/core/libraries/ajm/ajm_batch.cpp +++ b/src/core/libraries/ajm/ajm_batch.cpp @@ -135,7 +135,10 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { case Identifier::AjmIdentInputControlBuf: { ASSERT_MSG(!input_control_buffer.has_value(), "Only one instance of input control buffer is allowed per job"); - input_control_buffer = batch_buffer.Consume(); + const auto& buffer = batch_buffer.Consume(); + if (buffer.p_address != nullptr && buffer.size != 0) { + input_control_buffer = buffer; + } break; } case Identifier::AjmIdentControlFlags: @@ -155,19 +158,27 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { case Identifier::AjmIdentInlineBuf: { ASSERT_MSG(!output_control_buffer.has_value(), "Only one instance of inline buffer is allowed per job"); - inline_buffer = batch_buffer.Consume(); + const auto& buffer = batch_buffer.Consume(); + if (buffer.p_address != nullptr && buffer.size != 0) { + inline_buffer = buffer; + } break; } case Identifier::AjmIdentOutputRunBuf: { auto& buffer = batch_buffer.Consume(); u8* p_begin = reinterpret_cast(buffer.p_address); - job.output.buffers.emplace_back(std::span(p_begin, p_begin + buffer.size)); + if (p_begin != nullptr && buffer.size != 0) { + job.output.buffers.emplace_back(std::span(p_begin, p_begin + buffer.size)); + } break; } case Identifier::AjmIdentOutputControlBuf: { ASSERT_MSG(!output_control_buffer.has_value(), "Only one instance of output control buffer is allowed per job"); - output_control_buffer = batch_buffer.Consume(); + const auto& buffer = batch_buffer.Consume(); + if (buffer.p_address != nullptr && buffer.size != 0) { + output_control_buffer = buffer; + } break; } default: diff --git a/src/core/libraries/ajm/ajm_instance.cpp b/src/core/libraries/ajm/ajm_instance.cpp index 5468ab3e3..ecc6ad529 100644 --- a/src/core/libraries/ajm/ajm_instance.cpp +++ b/src/core/libraries/ajm/ajm_instance.cpp @@ -78,8 +78,12 @@ void AjmInstance::ExecuteJob(AjmJob& job) { } if (job.input.gapless_decode.has_value()) { auto& params = job.input.gapless_decode.value(); - m_gapless.total_samples = params.total_samples; - m_gapless.skip_samples = params.skip_samples; + if (params.total_samples != 0) { + m_gapless.total_samples = std::max(params.total_samples, m_gapless.total_samples); + } + if (params.skip_samples != 0) { + m_gapless.skip_samples = std::max(params.skip_samples, m_gapless.skip_samples); + } } if (!job.input.buffer.empty() && !job.output.buffers.empty()) { @@ -90,16 +94,14 @@ void AjmInstance::ExecuteJob(AjmJob& job) { auto in_size = in_buf.size(); auto out_size = out_buf.Size(); while (!in_buf.empty() && !out_buf.IsEmpty() && !IsGaplessEnd()) { - const auto samples_remain = GetNumRemainingSamples(); - if (!HasEnoughSpace(out_buf, samples_remain)) { - if (job.output.p_mframe == nullptr) { - LOG_ERROR(Lib_Ajm, "Single-frame job buffer too small."); + if (!HasEnoughSpace(out_buf)) { + if (job.output.p_mframe == nullptr || frames_decoded == 0) { job.output.p_result->result = ORBIS_AJM_RESULT_NOT_ENOUGH_ROOM; + break; } - break; } const auto [nframes, nsamples] = - m_codec->ProcessData(in_buf, out_buf, m_gapless, samples_remain); + m_codec->ProcessData(in_buf, out_buf, m_gapless, GetNumRemainingSamples()); frames_decoded += nframes; m_total_samples += nsamples; m_gapless_samples += nsamples; @@ -138,10 +140,11 @@ bool AjmInstance::IsGaplessEnd() const { return m_gapless.total_samples != 0 && m_gapless_samples >= m_gapless.total_samples; } -bool AjmInstance::HasEnoughSpace(const SparseOutputBuffer& output, - std::optional opt_samples_remain) const { - const auto remain = opt_samples_remain.value_or(std::numeric_limits::max()); - return output.Size() >= m_codec->GetNextFrameSize(remain); +bool AjmInstance::HasEnoughSpace(const SparseOutputBuffer& output) const { + const auto skip = + m_gapless.skip_samples - std::min(m_gapless.skip_samples, m_gapless.skipped_samples); + const auto remain = GetNumRemainingSamples().value_or(std::numeric_limits::max()); + return output.Size() >= m_codec->GetNextFrameSize(skip, remain); } std::optional AjmInstance::GetNumRemainingSamples() const { diff --git a/src/core/libraries/ajm/ajm_instance.h b/src/core/libraries/ajm/ajm_instance.h index 2974e578d..ff5434eff 100644 --- a/src/core/libraries/ajm/ajm_instance.h +++ b/src/core/libraries/ajm/ajm_instance.h @@ -66,7 +66,7 @@ public: virtual void Reset() = 0; virtual void GetInfo(void* out_info) const = 0; virtual AjmSidebandFormat GetFormat() const = 0; - virtual u32 GetNextFrameSize(u32 max_samples) const = 0; + virtual u32 GetNextFrameSize(u32 skip_samples, u32 max_samples) const = 0; virtual std::tuple ProcessData(std::span& input, SparseOutputBuffer& output, AjmSidebandGaplessDecode& gapless, std::optional max_samples_per_channel) = 0; @@ -80,7 +80,7 @@ public: private: bool IsGaplessEnd() const; - bool HasEnoughSpace(const SparseOutputBuffer& output, std::optional samples_remain) const; + bool HasEnoughSpace(const SparseOutputBuffer& output) const; std::optional GetNumRemainingSamples() const; AjmInstanceFlags m_flags{}; diff --git a/src/core/libraries/ajm/ajm_mp3.cpp b/src/core/libraries/ajm/ajm_mp3.cpp index 469186888..48102de8a 100644 --- a/src/core/libraries/ajm/ajm_mp3.cpp +++ b/src/core/libraries/ajm/ajm_mp3.cpp @@ -219,9 +219,10 @@ std::tuple AjmMp3Decoder::ProcessData(std::span& in_buf, SparseOut return {frames_decoded, samples_decoded / m_codec_context->ch_layout.nb_channels}; } -u32 AjmMp3Decoder::GetNextFrameSize(u32 max_samples) const { - return std::min(m_frame_samples, max_samples) * m_codec_context->ch_layout.nb_channels * - GetPCMSize(m_format); +u32 AjmMp3Decoder::GetNextFrameSize(u32 skip_samples, u32 max_samples) const { + skip_samples = std::min({skip_samples, m_frame_samples, max_samples}); + return (std::min(m_frame_samples, max_samples) - skip_samples) * + m_codec_context->ch_layout.nb_channels * GetPCMSize(m_format); } class BitReader { diff --git a/src/core/libraries/ajm/ajm_mp3.h b/src/core/libraries/ajm/ajm_mp3.h index 062e28cb6..c8e1f27c3 100644 --- a/src/core/libraries/ajm/ajm_mp3.h +++ b/src/core/libraries/ajm/ajm_mp3.h @@ -70,7 +70,7 @@ public: void Initialize(const void* buffer, u32 buffer_size) override {} void GetInfo(void* out_info) const override; AjmSidebandFormat GetFormat() const override; - u32 GetNextFrameSize(u32 max_samples) const override; + u32 GetNextFrameSize(u32 skip_samples, u32 max_samples) const override; std::tuple ProcessData(std::span& input, SparseOutputBuffer& output, AjmSidebandGaplessDecode& gapless, std::optional max_samples_per_channel) override;