mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2024-11-23 03:09:55 +00:00
AJM: Added some missing features (#1489)
* AJM: Added support for different PCM formats * updated libatrac9 ref * remove log * Add support for non-interleaved flag * Added support for output sideband format query
This commit is contained in:
parent
f98b9f7726
commit
46ac48c311
2
externals/LibAtrac9
vendored
2
externals/LibAtrac9
vendored
@ -1 +1 @@
|
||||
Subproject commit 82767fe38823c32536726ea798f392b0b49e66b9
|
||||
Subproject commit 3acdcdc78f129c2e6145331ff650fa76dd88d62c
|
@ -13,8 +13,31 @@
|
||||
|
||||
namespace Libraries::Ajm {
|
||||
|
||||
constexpr int ORBIS_AJM_CHANNELMASK_MONO = 0x0004;
|
||||
constexpr int ORBIS_AJM_CHANNELMASK_STEREO = 0x0003;
|
||||
constexpr int ORBIS_AJM_CHANNELMASK_QUAD = 0x0033;
|
||||
constexpr int ORBIS_AJM_CHANNELMASK_5POINT1 = 0x060F;
|
||||
constexpr int ORBIS_AJM_CHANNELMASK_7POINT1 = 0x063F;
|
||||
|
||||
static std::unique_ptr<AjmContext> context{};
|
||||
|
||||
u32 GetChannelMask(u32 num_channels) {
|
||||
switch (num_channels) {
|
||||
case 1:
|
||||
return ORBIS_AJM_CHANNELMASK_MONO;
|
||||
case 2:
|
||||
return ORBIS_AJM_CHANNELMASK_STEREO;
|
||||
case 4:
|
||||
return ORBIS_AJM_CHANNELMASK_QUAD;
|
||||
case 6:
|
||||
return ORBIS_AJM_CHANNELMASK_5POINT1;
|
||||
case 8:
|
||||
return ORBIS_AJM_CHANNELMASK_7POINT1;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceAjmBatchCancel(const u32 context_id, const u32 batch_id) {
|
||||
LOG_INFO(Lib_Ajm, "called context_id = {} batch_id = {}", context_id, batch_id);
|
||||
return context->BatchCancel(batch_id);
|
||||
|
@ -137,9 +137,12 @@ union AjmInstanceFlags {
|
||||
u64 codec : 28;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(AjmInstanceFlags) == 8);
|
||||
|
||||
struct AjmDecMp3ParseFrame;
|
||||
|
||||
u32 GetChannelMask(u32 num_channels);
|
||||
|
||||
int PS4_SYSV_ABI sceAjmBatchCancel(const u32 context_id, const u32 batch_id);
|
||||
int PS4_SYSV_ABI sceAjmBatchErrorDump();
|
||||
void* PS4_SYSV_ABI sceAjmBatchJobControlBufferRa(void* p_buffer, u32 instance_id, u64 flags,
|
||||
|
@ -14,8 +14,8 @@ extern "C" {
|
||||
|
||||
namespace Libraries::Ajm {
|
||||
|
||||
AjmAt9Decoder::AjmAt9Decoder() {
|
||||
m_handle = Atrac9GetHandle();
|
||||
AjmAt9Decoder::AjmAt9Decoder(AjmFormatEncoding format, AjmAt9CodecFlags flags)
|
||||
: m_format(format), m_flags(flags), m_handle(Atrac9GetHandle()) {
|
||||
ASSERT_MSG(m_handle, "Atrac9GetHandle failed");
|
||||
AjmAt9Decoder::Reset();
|
||||
}
|
||||
@ -40,7 +40,20 @@ void AjmAt9Decoder::Initialize(const void* buffer, u32 buffer_size) {
|
||||
const auto params = reinterpret_cast<const AjmDecAt9InitializeParameters*>(buffer);
|
||||
std::memcpy(m_config_data, params->config_data, ORBIS_AT9_CONFIG_DATA_SIZE);
|
||||
AjmAt9Decoder::Reset();
|
||||
m_pcm_buffer.resize(m_codec_info.frameSamples * m_codec_info.channels, 0);
|
||||
m_pcm_buffer.resize(m_codec_info.frameSamples * m_codec_info.channels * GetPointCodeSize(), 0);
|
||||
}
|
||||
|
||||
u8 AjmAt9Decoder::GetPointCodeSize() {
|
||||
switch (m_format) {
|
||||
case AjmFormatEncoding::S16:
|
||||
return sizeof(s16);
|
||||
case AjmFormatEncoding::S32:
|
||||
return sizeof(s32);
|
||||
case AjmFormatEncoding::Float:
|
||||
return sizeof(float);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void AjmAt9Decoder::GetInfo(void* out_info) {
|
||||
@ -53,28 +66,56 @@ void AjmAt9Decoder::GetInfo(void* out_info) {
|
||||
|
||||
std::tuple<u32, u32> AjmAt9Decoder::ProcessData(std::span<u8>& in_buf, SparseOutputBuffer& output,
|
||||
AjmSidebandGaplessDecode& gapless,
|
||||
u32 max_samples_per_channel) {
|
||||
std::optional<u32> max_samples_per_channel) {
|
||||
int ret = 0;
|
||||
int bytes_used = 0;
|
||||
u32 ret = Atrac9Decode(m_handle, in_buf.data(), m_pcm_buffer.data(), &bytes_used);
|
||||
switch (m_format) {
|
||||
case AjmFormatEncoding::S16:
|
||||
ret = Atrac9Decode(m_handle, in_buf.data(), reinterpret_cast<s16*>(m_pcm_buffer.data()),
|
||||
&bytes_used, True(m_flags & AjmAt9CodecFlags::NonInterleavedOutput));
|
||||
break;
|
||||
case AjmFormatEncoding::S32:
|
||||
ret = Atrac9DecodeS32(m_handle, in_buf.data(), reinterpret_cast<s32*>(m_pcm_buffer.data()),
|
||||
&bytes_used, True(m_flags & AjmAt9CodecFlags::NonInterleavedOutput));
|
||||
break;
|
||||
case AjmFormatEncoding::Float:
|
||||
ret =
|
||||
Atrac9DecodeF32(m_handle, in_buf.data(), reinterpret_cast<float*>(m_pcm_buffer.data()),
|
||||
&bytes_used, True(m_flags & AjmAt9CodecFlags::NonInterleavedOutput));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
ASSERT_MSG(ret == At9Status::ERR_SUCCESS, "Atrac9Decode failed ret = {:#x}", ret);
|
||||
in_buf = in_buf.subspan(bytes_used);
|
||||
|
||||
m_superframe_bytes_remain -= bytes_used;
|
||||
std::span<s16> pcm_data{m_pcm_buffer};
|
||||
|
||||
u32 skipped_samples = 0;
|
||||
if (gapless.skipped_samples < gapless.skip_samples) {
|
||||
const auto skipped_samples = std::min(u32(m_codec_info.frameSamples),
|
||||
skipped_samples = std::min(u32(m_codec_info.frameSamples),
|
||||
u32(gapless.skip_samples - gapless.skipped_samples));
|
||||
gapless.skipped_samples += skipped_samples;
|
||||
pcm_data = pcm_data.subspan(skipped_samples * m_codec_info.channels);
|
||||
}
|
||||
|
||||
const auto max_samples = max_samples_per_channel == std::numeric_limits<u32>::max()
|
||||
? max_samples_per_channel
|
||||
: max_samples_per_channel * m_codec_info.channels;
|
||||
const auto max_samples = max_samples_per_channel.has_value()
|
||||
? max_samples_per_channel.value() * m_codec_info.channels
|
||||
: std::numeric_limits<u32>::max();
|
||||
|
||||
const auto pcm_size = std::min(u32(pcm_data.size()), max_samples);
|
||||
const auto written = output.Write(pcm_data.subspan(0, pcm_size));
|
||||
size_t samples_written = 0;
|
||||
switch (m_format) {
|
||||
case AjmFormatEncoding::S16:
|
||||
samples_written = WriteOutputSamples<s16>(output, skipped_samples, max_samples);
|
||||
break;
|
||||
case AjmFormatEncoding::S32:
|
||||
samples_written = WriteOutputSamples<s32>(output, skipped_samples, max_samples);
|
||||
break;
|
||||
case AjmFormatEncoding::Float:
|
||||
samples_written = WriteOutputSamples<float>(output, skipped_samples, max_samples);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
m_num_frames += 1;
|
||||
if ((m_num_frames % m_codec_info.framesInSuperframe) == 0) {
|
||||
@ -85,7 +126,18 @@ std::tuple<u32, u32> AjmAt9Decoder::ProcessData(std::span<u8>& in_buf, SparseOut
|
||||
m_num_frames = 0;
|
||||
}
|
||||
|
||||
return {1, (written / m_codec_info.channels) / sizeof(s16)};
|
||||
return {1, samples_written / m_codec_info.channels};
|
||||
}
|
||||
|
||||
AjmSidebandFormat AjmAt9Decoder::GetFormat() {
|
||||
return AjmSidebandFormat{
|
||||
.num_channels = u32(m_codec_info.channels),
|
||||
.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 * GetPointCodeSize() * 8),
|
||||
.reserved = 0,
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Libraries::Ajm
|
||||
|
@ -8,10 +8,18 @@
|
||||
|
||||
#include "libatrac9.h"
|
||||
|
||||
#include <span>
|
||||
|
||||
namespace Libraries::Ajm {
|
||||
|
||||
constexpr s32 ORBIS_AJM_DEC_AT9_MAX_CHANNELS = 8;
|
||||
|
||||
enum AjmAt9CodecFlags : u32 {
|
||||
ParseRiffHeader = 1 << 0,
|
||||
NonInterleavedOutput = 1 << 8,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(AjmAt9CodecFlags)
|
||||
|
||||
struct AjmSidebandDecAt9CodecInfo {
|
||||
u32 super_frame_size;
|
||||
u32 frames_in_super_frame;
|
||||
@ -20,22 +28,37 @@ struct AjmSidebandDecAt9CodecInfo {
|
||||
};
|
||||
|
||||
struct AjmAt9Decoder final : AjmCodec {
|
||||
explicit AjmAt9Decoder();
|
||||
explicit AjmAt9Decoder(AjmFormatEncoding format, AjmAt9CodecFlags flags);
|
||||
~AjmAt9Decoder() override;
|
||||
|
||||
void Reset() override;
|
||||
void Initialize(const void* buffer, u32 buffer_size) override;
|
||||
void GetInfo(void* out_info) override;
|
||||
AjmSidebandFormat GetFormat() override;
|
||||
std::tuple<u32, u32> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
|
||||
AjmSidebandGaplessDecode& gapless, u32 max_samples) override;
|
||||
AjmSidebandGaplessDecode& gapless,
|
||||
std::optional<u32> max_samples) override;
|
||||
|
||||
private:
|
||||
u8 GetPointCodeSize();
|
||||
|
||||
template <class T>
|
||||
size_t WriteOutputSamples(SparseOutputBuffer& output, u32 skipped_samples, u32 max_samples) {
|
||||
std::span<T> pcm_data{reinterpret_cast<T*>(m_pcm_buffer.data()),
|
||||
m_pcm_buffer.size() / sizeof(T)};
|
||||
pcm_data = pcm_data.subspan(skipped_samples * m_codec_info.channels);
|
||||
const auto pcm_size = std::min(u32(pcm_data.size()), max_samples);
|
||||
return output.Write(pcm_data.subspan(0, pcm_size));
|
||||
}
|
||||
|
||||
const AjmFormatEncoding m_format;
|
||||
const AjmAt9CodecFlags m_flags;
|
||||
void* m_handle{};
|
||||
u8 m_config_data[ORBIS_AT9_CONFIG_DATA_SIZE]{};
|
||||
u32 m_superframe_bytes_remain{};
|
||||
u32 m_num_frames{};
|
||||
Atrac9CodecInfo m_codec_info{};
|
||||
std::vector<s16> m_pcm_buffer;
|
||||
std::vector<u8> m_pcm_buffer;
|
||||
};
|
||||
|
||||
} // namespace Libraries::Ajm
|
||||
|
@ -149,7 +149,6 @@ s32 AjmContext::InstanceCreate(AjmCodecType codec_type, AjmInstanceFlags flags,
|
||||
if (!IsRegistered(codec_type)) {
|
||||
return ORBIS_AJM_ERROR_CODEC_NOT_REGISTERED;
|
||||
}
|
||||
ASSERT_MSG(flags.format == 0, "Only signed 16-bit PCM output is supported currently!");
|
||||
std::optional<u32> opt_index;
|
||||
{
|
||||
std::unique_lock lock(instances_mutex);
|
||||
|
@ -9,14 +9,28 @@
|
||||
|
||||
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;
|
||||
|
||||
AjmInstance::AjmInstance(AjmCodecType codec_type, AjmInstanceFlags flags) : m_flags(flags) {
|
||||
switch (codec_type) {
|
||||
case AjmCodecType::At9Dec: {
|
||||
m_codec = std::make_unique<AjmAt9Decoder>();
|
||||
m_codec = std::make_unique<AjmAt9Decoder>(AjmFormatEncoding(flags.format),
|
||||
AjmAt9CodecFlags(flags.codec));
|
||||
break;
|
||||
}
|
||||
case AjmCodecType::Mp3Dec: {
|
||||
m_codec = std::make_unique<AjmMp3Decoder>();
|
||||
m_codec = std::make_unique<AjmMp3Decoder>(AjmFormatEncoding(flags.format));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -62,9 +76,10 @@ 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 u32 samples_remain = m_gapless.total_samples != 0
|
||||
? m_gapless.total_samples - m_gapless_samples
|
||||
: std::numeric_limits<u32>::max();
|
||||
const auto samples_remain =
|
||||
m_gapless.total_samples != 0
|
||||
? std::optional<u32>{m_gapless.total_samples - m_gapless_samples}
|
||||
: std::optional<u32>{};
|
||||
const auto [nframes, nsamples] =
|
||||
m_codec->ProcessData(in_buf, out_buf, m_gapless, samples_remain);
|
||||
frames_decoded += nframes;
|
||||
@ -87,6 +102,9 @@ void AjmInstance::ExecuteJob(AjmJob& job) {
|
||||
m_gapless.skipped_samples = 0;
|
||||
m_codec->Reset();
|
||||
}
|
||||
if (job.output.p_format != nullptr) {
|
||||
*job.output.p_format = m_codec->GetFormat();
|
||||
}
|
||||
if (job.output.p_gapless_decode != nullptr) {
|
||||
*job.output.p_gapless_decode = m_gapless;
|
||||
}
|
||||
|
@ -14,19 +14,6 @@
|
||||
|
||||
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;
|
||||
|
||||
class SparseOutputBuffer {
|
||||
public:
|
||||
SparseOutputBuffer(std::span<std::span<u8>> chunks)
|
||||
@ -34,18 +21,19 @@ public:
|
||||
|
||||
template <class T>
|
||||
size_t Write(std::span<T> pcm) {
|
||||
size_t bytes_written = 0;
|
||||
size_t samples_written = 0;
|
||||
while (!pcm.empty() && !IsEmpty()) {
|
||||
auto size = std::min(pcm.size() * sizeof(T), m_current->size());
|
||||
std::memcpy(m_current->data(), pcm.data(), size);
|
||||
bytes_written += size;
|
||||
pcm = pcm.subspan(size / sizeof(T));
|
||||
const auto nsamples = size / sizeof(T);
|
||||
samples_written += nsamples;
|
||||
pcm = pcm.subspan(nsamples);
|
||||
*m_current = m_current->subspan(size);
|
||||
if (m_current->empty()) {
|
||||
++m_current;
|
||||
}
|
||||
}
|
||||
return bytes_written;
|
||||
return samples_written;
|
||||
}
|
||||
|
||||
bool IsEmpty() {
|
||||
@ -65,11 +53,6 @@ private:
|
||||
std::span<std::span<u8>>::iterator m_current;
|
||||
};
|
||||
|
||||
struct DecodeResult {
|
||||
u32 bytes_consumed{};
|
||||
u32 bytes_written{};
|
||||
};
|
||||
|
||||
class AjmCodec {
|
||||
public:
|
||||
virtual ~AjmCodec() = default;
|
||||
@ -77,9 +60,10 @@ public:
|
||||
virtual void Initialize(const void* buffer, u32 buffer_size) = 0;
|
||||
virtual void Reset() = 0;
|
||||
virtual void GetInfo(void* out_info) = 0;
|
||||
virtual AjmSidebandFormat GetFormat() = 0;
|
||||
virtual std::tuple<u32, u32> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
|
||||
AjmSidebandGaplessDecode& gapless,
|
||||
u32 max_samples) = 0;
|
||||
std::optional<u32> max_samples_per_channel) = 0;
|
||||
};
|
||||
|
||||
class AjmInstance {
|
||||
@ -100,9 +84,6 @@ private:
|
||||
u32 m_total_samples{};
|
||||
|
||||
std::unique_ptr<AjmCodec> m_codec;
|
||||
|
||||
// AjmCodecType codec_type;
|
||||
// u32 index{};
|
||||
};
|
||||
|
||||
} // namespace Libraries::Ajm
|
||||
|
@ -30,9 +30,27 @@ static constexpr std::array<s32, 2> UnkTable = {0x48, 0x90};
|
||||
|
||||
SwrContext* swr_context{};
|
||||
|
||||
AVFrame* ConvertAudioFrame(AVFrame* frame) {
|
||||
static AVSampleFormat AjmToAVSampleFormat(AjmFormatEncoding format) {
|
||||
switch (format) {
|
||||
case AjmFormatEncoding::S16:
|
||||
return AV_SAMPLE_FMT_S16;
|
||||
case AjmFormatEncoding::S32:
|
||||
return AV_SAMPLE_FMT_S32;
|
||||
case AjmFormatEncoding::Float:
|
||||
return AV_SAMPLE_FMT_FLT;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
AVFrame* AjmMp3Decoder::ConvertAudioFrame(AVFrame* frame) {
|
||||
AVSampleFormat format = AjmToAVSampleFormat(m_format);
|
||||
if (frame->format == format) {
|
||||
return frame;
|
||||
}
|
||||
|
||||
auto pcm16_frame = av_frame_clone(frame);
|
||||
pcm16_frame->format = AV_SAMPLE_FMT_S16;
|
||||
pcm16_frame->format = format;
|
||||
|
||||
if (swr_context) {
|
||||
swr_free(&swr_context);
|
||||
@ -40,9 +58,9 @@ AVFrame* ConvertAudioFrame(AVFrame* frame) {
|
||||
}
|
||||
AVChannelLayout in_ch_layout = frame->ch_layout;
|
||||
AVChannelLayout out_ch_layout = pcm16_frame->ch_layout;
|
||||
swr_alloc_set_opts2(&swr_context, &out_ch_layout, AV_SAMPLE_FMT_S16, frame->sample_rate,
|
||||
&in_ch_layout, AVSampleFormat(frame->format), frame->sample_rate, 0,
|
||||
nullptr);
|
||||
swr_alloc_set_opts2(&swr_context, &out_ch_layout, AVSampleFormat(pcm16_frame->format),
|
||||
frame->sample_rate, &in_ch_layout, AVSampleFormat(frame->format),
|
||||
frame->sample_rate, 0, nullptr);
|
||||
swr_init(swr_context);
|
||||
const auto res = swr_convert_frame(swr_context, pcm16_frame, frame);
|
||||
if (res < 0) {
|
||||
@ -53,11 +71,9 @@ AVFrame* ConvertAudioFrame(AVFrame* frame) {
|
||||
return pcm16_frame;
|
||||
}
|
||||
|
||||
AjmMp3Decoder::AjmMp3Decoder() {
|
||||
m_codec = avcodec_find_decoder(AV_CODEC_ID_MP3);
|
||||
ASSERT_MSG(m_codec, "MP3 m_codec not found");
|
||||
m_parser = av_parser_init(m_codec->id);
|
||||
ASSERT_MSG(m_parser, "Parser not found");
|
||||
AjmMp3Decoder::AjmMp3Decoder(AjmFormatEncoding format)
|
||||
: m_format(format), m_codec(avcodec_find_decoder(AV_CODEC_ID_MP3)),
|
||||
m_parser(av_parser_init(m_codec->id)) {
|
||||
AjmMp3Decoder::Reset();
|
||||
}
|
||||
|
||||
@ -81,7 +97,7 @@ void AjmMp3Decoder::GetInfo(void* out_info) {
|
||||
|
||||
std::tuple<u32, u32> AjmMp3Decoder::ProcessData(std::span<u8>& in_buf, SparseOutputBuffer& output,
|
||||
AjmSidebandGaplessDecode& gapless,
|
||||
u32 max_samples) {
|
||||
std::optional<u32> max_samples_per_channel) {
|
||||
AVPacket* pkt = av_packet_alloc();
|
||||
|
||||
int ret = av_parser_parse2(m_parser, m_codec_context, &pkt->data, &pkt->size, in_buf.data(),
|
||||
@ -109,24 +125,37 @@ std::tuple<u32, u32> AjmMp3Decoder::ProcessData(std::span<u8>& in_buf, SparseOut
|
||||
} else if (ret < 0) {
|
||||
UNREACHABLE_MSG("Error during decoding");
|
||||
}
|
||||
if (frame->format != AV_SAMPLE_FMT_S16) {
|
||||
frame = ConvertAudioFrame(frame);
|
||||
}
|
||||
|
||||
frames_decoded += 1;
|
||||
samples_decoded += frame->nb_samples;
|
||||
const auto size = frame->ch_layout.nb_channels * frame->nb_samples * sizeof(u16);
|
||||
std::span<s16> pcm_data(reinterpret_cast<s16*>(frame->data[0]), size >> 1);
|
||||
u32 skipped_samples = 0;
|
||||
if (gapless.skipped_samples < gapless.skip_samples) {
|
||||
const auto skipped_samples = std::min(
|
||||
u32(frame->nb_samples), u32(gapless.skip_samples - gapless.skipped_samples));
|
||||
skipped_samples = std::min(u32(frame->nb_samples),
|
||||
u32(gapless.skip_samples - gapless.skipped_samples));
|
||||
gapless.skipped_samples += skipped_samples;
|
||||
pcm_data = pcm_data.subspan(skipped_samples * frame->ch_layout.nb_channels);
|
||||
samples_decoded -= skipped_samples;
|
||||
}
|
||||
|
||||
const auto pcm_size = std::min(u32(pcm_data.size()), max_samples);
|
||||
output.Write(pcm_data.subspan(0, pcm_size));
|
||||
const auto max_samples =
|
||||
max_samples_per_channel.has_value()
|
||||
? max_samples_per_channel.value() * frame->ch_layout.nb_channels
|
||||
: std::numeric_limits<u32>::max();
|
||||
|
||||
switch (m_format) {
|
||||
case AjmFormatEncoding::S16:
|
||||
samples_decoded +=
|
||||
WriteOutputSamples<s16>(frame, output, skipped_samples, max_samples);
|
||||
break;
|
||||
case AjmFormatEncoding::S32:
|
||||
samples_decoded +=
|
||||
WriteOutputSamples<s32>(frame, output, skipped_samples, max_samples);
|
||||
break;
|
||||
case AjmFormatEncoding::Float:
|
||||
samples_decoded +=
|
||||
WriteOutputSamples<float>(frame, output, skipped_samples, max_samples);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
av_frame_free(&frame);
|
||||
}
|
||||
@ -163,4 +192,9 @@ int AjmMp3Decoder::ParseMp3Header(const u8* buf, u32 stream_size, int parse_ofl,
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
AjmSidebandFormat AjmMp3Decoder::GetFormat() {
|
||||
LOG_ERROR(Lib_Ajm, "Unimplemented");
|
||||
return AjmSidebandFormat{};
|
||||
};
|
||||
|
||||
} // namespace Libraries::Ajm
|
||||
|
@ -7,11 +7,7 @@
|
||||
#include "core/libraries/ajm/ajm_instance.h"
|
||||
|
||||
extern "C" {
|
||||
struct AVCodec;
|
||||
struct AVCodecContext;
|
||||
struct AVCodecParserContext;
|
||||
struct AVFrame;
|
||||
struct AVPacket;
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
namespace Libraries::Ajm {
|
||||
@ -54,19 +50,35 @@ struct AjmSidebandDecMp3CodecInfo {
|
||||
|
||||
class AjmMp3Decoder : public AjmCodec {
|
||||
public:
|
||||
explicit AjmMp3Decoder();
|
||||
explicit AjmMp3Decoder(AjmFormatEncoding format);
|
||||
~AjmMp3Decoder() override;
|
||||
|
||||
void Reset() override;
|
||||
void Initialize(const void* buffer, u32 buffer_size) override {}
|
||||
void GetInfo(void* out_info) override;
|
||||
AjmSidebandFormat GetFormat() override;
|
||||
std::tuple<u32, u32> ProcessData(std::span<u8>& input, SparseOutputBuffer& output,
|
||||
AjmSidebandGaplessDecode& gapless, u32 max_samples) override;
|
||||
AjmSidebandGaplessDecode& gapless,
|
||||
std::optional<u32> max_samples_per_channel) override;
|
||||
|
||||
static int ParseMp3Header(const u8* buf, u32 stream_size, int parse_ofl,
|
||||
AjmDecMp3ParseFrame* frame);
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
size_t WriteOutputSamples(AVFrame* frame, SparseOutputBuffer& output, u32 skipped_samples,
|
||||
u32 max_samples) {
|
||||
const auto size = frame->ch_layout.nb_channels * frame->nb_samples * sizeof(T);
|
||||
std::span<T> pcm_data(reinterpret_cast<T*>(frame->data[0]), size >> 1);
|
||||
pcm_data = pcm_data.subspan(skipped_samples * frame->ch_layout.nb_channels);
|
||||
const auto pcm_size = std::min(u32(pcm_data.size()), max_samples);
|
||||
const auto samples_written = output.Write(pcm_data.subspan(0, pcm_size));
|
||||
return samples_written / frame->ch_layout.nb_channels;
|
||||
}
|
||||
|
||||
AVFrame* ConvertAudioFrame(AVFrame* frame);
|
||||
|
||||
const AjmFormatEncoding m_format;
|
||||
const AVCodec* m_codec = nullptr;
|
||||
AVCodecContext* m_codec_context = nullptr;
|
||||
AVCodecParserContext* m_parser = nullptr;
|
||||
|
Loading…
Reference in New Issue
Block a user