From 8073b49bea24d36bc236ae83afa8a0c086c17c62 Mon Sep 17 00:00:00 2001 From: Nikita Savyolov Date: Sun, 20 Oct 2024 14:20:44 +0300 Subject: [PATCH] rpcsx: ajm gapless decode wip --- rpcsx/iodev/ajm.cpp | 247 +++++++++++++++++++++++++++++++++----------- rpcsx/iodev/ajm.hpp | 106 +++++++++++-------- 2 files changed, 252 insertions(+), 101 deletions(-) diff --git a/rpcsx/iodev/ajm.cpp b/rpcsx/iodev/ajm.cpp index e2e2c97..71f6147 100644 --- a/rpcsx/iodev/ajm.cpp +++ b/rpcsx/iodev/ajm.cpp @@ -8,6 +8,7 @@ #include "orbis/utils/Logs.hpp" #include #include +#include #include extern "C" { #include @@ -23,13 +24,15 @@ extern "C" { struct AjmFile : orbis::File {}; -uint batchId = 1; +namespace ajm { +orbis::uint32_t batchId = 1; orbis::uint32_t at9InstanceId = 0; orbis::uint32_t mp3InstanceId = 0; orbis::uint32_t aacInstanceId = 0; orbis::uint32_t unimplementedInstanceId = 0; -std::map instanceMap; +orbis::kmap instanceMap; +} // namespace ajm AVSampleFormat ajmToAvFormat(AJMFormat ajmFormat) { switch (ajmFormat) { @@ -64,14 +67,14 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, orbis::uint32_t instanceId; }; auto args = reinterpret_cast(argp); - auto it = instanceMap.find(args->instanceId); - if (it != instanceMap.end()) { - auto &instance = instanceMap[args->instanceId]; + auto it = ajm::instanceMap.find(args->instanceId); + if (it != ajm::instanceMap.end()) { + auto &instance = ajm::instanceMap[args->instanceId]; if (instance.resampler) { swr_free(&instance.resampler); avcodec_free_context(&instance.codecCtx); } - instanceMap.erase(args->instanceId); + ajm::instanceMap.erase(args->instanceId); } args->result = 0; } @@ -95,15 +98,15 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, if (codecId >= 0 && codecId <= 2) { args->result = 0; if (codecId == AJM_CODEC_At9) { - args->instanceId = codecOffset + at9InstanceId++; + args->instanceId = codecOffset + ajm::at9InstanceId++; } else if (codecId == AJM_CODEC_MP3) { - args->instanceId = codecOffset + mp3InstanceId++; + args->instanceId = codecOffset + ajm::mp3InstanceId++; } else if (codecId == AJM_CODEC_AAC) { - args->instanceId = codecOffset + aacInstanceId++; + args->instanceId = codecOffset + ajm::aacInstanceId++; } Instance instance; instance.codec = codecId; - instance.outputChannels = + instance.maxChannels = AJMChannels(((args->flags & ~7) & (0xFF & ~0b11)) >> 3); instance.outputFormat = AJMFormat((args->flags & ~7) & 0b11); if (codecId == AJM_CODEC_At9) { @@ -129,12 +132,12 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, instance.codecCtx = codecCtx; } - instanceMap.insert({ + ajm::instanceMap.insert({ args->instanceId, instance, }); } else { - args->instanceId = codecOffset + unimplementedInstanceId++; + args->instanceId = codecOffset + ajm::unimplementedInstanceId++; } ORBIS_LOG_ERROR(__FUNCTION__, request, args->result, args->unk0, args->flags, args->codec, args->instanceId); @@ -150,11 +153,11 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, }; auto args = reinterpret_cast(argp); args->result = 0; - args->batchId = batchId; + args->batchId = ajm::batchId; ORBIS_LOG_ERROR(__FUNCTION__, request, args->result, args->unk0, args->pBatch, args->batchSize, args->priority, args->batchError, args->batchId); - batchId += 1; + ajm::batchId += 1; auto ptr = args->pBatch; auto endPtr = args->pBatch + args->batchSize; @@ -165,7 +168,7 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, auto jobPtr = ptr + sizeof(InstructionHeader); auto endJobPtr = ptr + header->len; // TODO: handle unimplemented codecs, so auto create instance for now - auto &instance = instanceMap[instanceId]; + auto &instance = ajm::instanceMap[instanceId]; RunJob runJob; while (jobPtr < endJobPtr) { auto typed = (OpcodeHeader *)jobPtr; @@ -187,7 +190,17 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, ORBIS_LOG_ERROR(__FUNCTION__, request, "control buffer", ctrl->opcode, ctrl->commandId, ctrl->flagsHi, ctrl->flagsLo, ctrl->sidebandInputSize, ctrl->sidebandOutputSize); - if ((ctrl->flagsLo & ~7) & CONTROL_INITIALIZE) { + if (ctrl->getFlags() & CONTROL_RESET) { + // instance.outputChannels = AJMChannels(0); + // instance.outputFormat = AJMFormat(0); + instance.gaplessSkipSamples = 0; + instance.gaplessTotalSamples = 0; + instance.gaplessTotalSkippedSamples = 0; + instance.processedSamples = 0; + ORBIS_LOG_TODO("CONTROL_RESET"); + } + + if (ctrl->getFlags() & CONTROL_INITIALIZE) { if (instance.codec == AJM_CODEC_At9) { struct InitalizeBuffer { orbis::uint32_t configData; @@ -215,10 +228,14 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, instance.at9.superFrameDataLeft = pCodecInfo.superframeSize; instance.at9.sampleRate = pCodecInfo.samplingRate; + orbis::uint32_t maxChannels = + instance.maxChannels == AJM_CHANNEL_DEFAULT + ? 2 + : instance.maxChannels; orbis::uint32_t outputChannels = - instance.outputChannels == AJM_DEFAULT - ? instance.at9.inputChannels - : instance.outputChannels; + instance.at9.inputChannels > maxChannels + ? maxChannels + : instance.at9.inputChannels; if (instance.at9.inputChannels != outputChannels || instance.outputFormat != AJM_FORMAT_S16) { instance.resampler = swr_alloc(); @@ -265,6 +282,24 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, AACFreq[initializeBuffer->sampleRateIndex]; } } + if (ctrl->getFlags() & SIDEBAND_GAPLESS_DECODE) { + struct InitalizeBuffer { + orbis::uint32_t totalSamples; + orbis::uint16_t skipSamples; + orbis::uint16_t totalSkippedSamples; + }; + InitalizeBuffer *initializeBuffer = + (InitalizeBuffer *)ctrl->pSidebandInput; + if (initializeBuffer->totalSamples > 0) { + instance.gaplessTotalSamples = initializeBuffer->totalSamples; + } + if (initializeBuffer->skipSamples > 0) { + instance.gaplessSkipSamples = initializeBuffer->skipSamples; + } + ORBIS_LOG_TODO("SIDEBAND_GAPLESS_DECODE", + instance.gaplessSkipSamples, + instance.gaplessTotalSamples); + } jobPtr += sizeof(BatchJobControlBufferRa); break; } @@ -288,9 +323,9 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, case Opcode::JobBufferOutputRa: { BatchJobOutputBufferRa *job = (BatchJobOutputBufferRa *)jobPtr; ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobOutputBufferRa", - job->opcode, job->szOutputSize, job->pOutput); + job->opcode, job->outputSize, job->pOutput); runJob.pOutput = job->pOutput; - runJob.outputSize = job->szOutputSize; + runJob.outputSize = job->outputSize; jobPtr += sizeof(BatchJobOutputBufferRa); break; } @@ -320,19 +355,19 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, stream->outputSize = runJob.outputSize; } } else if (!runJob.control) { - orbis::uint32_t outputChannels = instance.outputChannels == AJM_DEFAULT - ? 2 - : instance.outputChannels; + orbis::uint32_t maxChannels = + instance.maxChannels == AJM_CHANNEL_DEFAULT ? 2 + : instance.maxChannels; AJMSidebandResult *result = reinterpret_cast(runJob.pSideband); result->result = 0; result->codecResult = 0; - uint32_t inputReaded = 0; - uint32_t outputWritten = 0; - uint32_t framesProcessed = 0; - uint32_t channels = 0; - uint32_t sampleRate = 0; + orbis::uint32_t inputReaded = 0; + orbis::uint32_t outputWritten = 0; + orbis::uint32_t framesProcessed = 0; + orbis::uint32_t channels = 0; + orbis::uint32_t sampleRate = 0; if (runJob.inputSize != 0 && runJob.outputSize != 0) { while (inputReaded < runJob.inputSize && outputWritten < runJob.outputSize) { @@ -342,9 +377,10 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, break; } if (instance.codec == AJM_CODEC_At9) { - outputChannels = instance.outputChannels == AJM_DEFAULT - ? instance.at9.inputChannels - : instance.outputChannels; + orbis::uint32_t outputChannels = + instance.at9.inputChannels > maxChannels + ? maxChannels + : instance.at9.inputChannels; orbis::int32_t outputBufferSize = av_samples_get_buffer_size( nullptr, outputChannels, instance.at9.frameSamples, ajmToAvFormat(instance.resampler ? AJM_FORMAT_S16 @@ -363,6 +399,40 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, ORBIS_LOG_FATAL("Could not decode frame", err); std::abort(); } + instance.at9.estimatedSizeUsed = + static_cast(bytesUsed); + instance.at9.superFrameDataLeft -= bytesUsed; + instance.at9.superFrameDataIdx++; + if (instance.at9.superFrameDataIdx == + instance.at9.framesInSuperframe) { + instance.at9.estimatedSizeUsed += + instance.at9.superFrameDataLeft; + instance.at9.superFrameDataIdx = 0; + instance.at9.superFrameDataLeft = instance.at9.superFrameSize; + } + // TODO: possible memory leak because "genius" code to avoiding memory + // copying + if (instance.gaplessSkipSamples > 0 || + instance.gaplessTotalSamples > 0) { + if (instance.gaplessSkipSamples > + instance.gaplessTotalSkippedSamples) { + instance.gaplessTotalSkippedSamples += + instance.at9.frameSamples; + inputReaded += instance.at9.estimatedSizeUsed; + ORBIS_LOG_TODO("skip frame", + instance.gaplessTotalSkippedSamples); + break; + } else if (instance.processedSamples > + instance.gaplessTotalSamples) { + instance.gaplessTotalSkippedSamples += + instance.at9.frameSamples; + inputReaded += instance.at9.estimatedSizeUsed; + ORBIS_LOG_TODO( + "skip output", instance.gaplessTotalSkippedSamples, + instance.processedSamples, instance.gaplessTotalSamples); + break; + } + } if (instance.resampler) { auto outputBuffer = reinterpret_cast( runJob.pOutput + outputWritten); @@ -377,21 +447,11 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, } av_freep(&tempBuffer); } - instance.at9.estimatedSizeUsed = static_cast(bytesUsed); - instance.at9.superFrameDataLeft -= bytesUsed; - instance.at9.superFrameDataIdx++; - if (instance.at9.superFrameDataIdx == - instance.at9.framesInSuperframe) { - instance.at9.estimatedSizeUsed += - instance.at9.superFrameDataLeft; - instance.at9.superFrameDataIdx = 0; - instance.at9.superFrameDataLeft = instance.at9.superFrameSize; - } channels = instance.at9.inputChannels; sampleRate = instance.at9.sampleRate; inputReaded += instance.at9.estimatedSizeUsed; - outputWritten += - std::max((uint32_t)outputBufferSize, runJob.outputSize); + outputWritten += std::max((orbis::uint32_t)outputBufferSize, + runJob.outputSize); framesProcessed += 1; } else if (instance.codec == AJM_CODEC_MP3) { ORBIS_LOG_FATAL("Pre get mp3 data size info", runJob.inputSize, @@ -414,8 +474,10 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, // realInputSize}); AVPacket *pkt = av_packet_alloc(); + rx::atScopeExit _free_pkt([&] { av_packet_free(&pkt); }); AVFrame *frame = av_frame_alloc(); - pkt->data = (uint8_t *)(runJob.pInput + inputReaded); + rx::atScopeExit _free_frame([&] { av_frame_free(&frame); }); + pkt->data = (orbis::uint8_t *)(runJob.pInput + inputReaded); pkt->size = realInputSize; int ret = avcodec_send_packet(instance.codecCtx, pkt); if (ret < 0) { @@ -428,12 +490,38 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, std::abort(); } + if (instance.gaplessSkipSamples > 0 || + instance.gaplessTotalSamples > 0) { + if (instance.gaplessSkipSamples > + instance.gaplessTotalSkippedSamples) { + instance.gaplessTotalSkippedSamples += frame->nb_samples; + inputReaded += realInputSize; + ORBIS_LOG_TODO("skip frame", + instance.gaplessTotalSkippedSamples); + break; + } else if (instance.processedSamples > + instance.gaplessTotalSamples) { + instance.gaplessTotalSkippedSamples += frame->nb_samples; + inputReaded += realInputSize; + ORBIS_LOG_TODO( + "skip output", instance.gaplessTotalSkippedSamples, + instance.processedSamples, instance.gaplessTotalSamples); + break; + } + } + auto resampler = swr_alloc(); + rx::atScopeExit _free_resampler([&] { swr_free(&resampler); }); if (!resampler) { ORBIS_LOG_FATAL("Could not allocate resampler context"); std::abort(); } + orbis::uint32_t outputChannels = + (orbis::uint32_t)frame->ch_layout.nb_channels > maxChannels + ? maxChannels + : frame->ch_layout.nb_channels; + AVChannelLayout inputChLayout; av_channel_layout_default(&inputChLayout, frame->ch_layout.nb_channels); @@ -458,6 +546,8 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, } uint8_t *outputBuffer = NULL; + rx::atScopeExit _free_outputBuffer( + [&] { av_freep(&outputBuffer); }); int outputBufferSize = av_samples_alloc( &outputBuffer, NULL, frame->ch_layout.nb_channels, frame->nb_samples, ajmToAvFormat(instance.outputFormat), 0); @@ -467,7 +557,9 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, } ORBIS_LOG_TODO("output buffer info", frame->ch_layout.nb_channels, frame->nb_samples, (int32_t)instance.outputFormat, - outputBufferSize); + outputBufferSize, + instance.gaplessTotalSkippedSamples, + instance.processedSamples); if (outputWritten + outputBufferSize > runJob.outputSize) { ORBIS_LOG_TODO("overwriting", outputWritten, outputBufferSize, @@ -490,14 +582,17 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, sampleRate = frame->sample_rate; inputReaded += realInputSize; outputWritten += outputBufferSize; + instance.processedSamples += frame->nb_samples; framesProcessed += 1; - av_freep(&outputBuffer); - swr_free(&resampler); - av_frame_free(&frame); - av_packet_free(&pkt); + // av_freep(&outputBuffer); + // swr_free(&resampler); + // av_frame_free(&frame); + // av_packet_free(&pkt); } else if (instance.codec == AJM_CODEC_AAC) { AVPacket *pkt = av_packet_alloc(); + rx::atScopeExit _free_pkt([&] { av_packet_free(&pkt); }); AVFrame *frame = av_frame_alloc(); + rx::atScopeExit _free_frame([&] { av_frame_free(&frame); }); pkt->data = (uint8_t *)runJob.pInput + inputReaded; pkt->size = runJob.inputSize; @@ -510,16 +605,37 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, ->cb.decode(instance.codecCtx, frame, &gotFrame, pkt); orbis::uint32_t outputChannels = - instance.outputChannels == AJM_DEFAULT - ? frame->ch_layout.nb_channels - : instance.outputChannels; + (orbis::uint32_t)frame->ch_layout.nb_channels > maxChannels + ? maxChannels + : frame->ch_layout.nb_channels; ORBIS_LOG_TODO("aac decode", len, gotFrame, frame->ch_layout.nb_channels, frame->sample_rate, instance.aac.sampleRate, outputChannels, - (orbis::uint32_t)instance.outputChannels); + (orbis::uint32_t)instance.maxChannels); + + if (instance.gaplessSkipSamples > 0 || + instance.gaplessTotalSamples > 0) { + if (instance.gaplessSkipSamples > + instance.gaplessTotalSkippedSamples) { + instance.gaplessTotalSkippedSamples += frame->nb_samples; + inputReaded += len; + ORBIS_LOG_TODO("skip frame", + instance.gaplessTotalSkippedSamples); + break; + } else if (instance.processedSamples > + instance.gaplessTotalSamples) { + instance.gaplessTotalSkippedSamples += frame->nb_samples; + inputReaded += len; + ORBIS_LOG_TODO( + "skip output", instance.gaplessTotalSkippedSamples, + instance.processedSamples, instance.gaplessTotalSamples); + break; + } + } auto resampler = swr_alloc(); + rx::atScopeExit _free_resampler([&] { swr_free(&resampler); }); if (!resampler) { ORBIS_LOG_FATAL("Could not allocate resampler context"); std::abort(); @@ -549,6 +665,8 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, } uint8_t *outputBuffer = NULL; + rx::atScopeExit _free_outputBuffer( + [&] { av_freep(&outputBuffer); }); int outputBufferSize = av_samples_alloc( &outputBuffer, NULL, outputChannels, frame->nb_samples, ajmToAvFormat(instance.outputFormat), 0); @@ -572,9 +690,10 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, inputReaded += len; outputWritten += outputBufferSize; framesProcessed += 1; - av_frame_free(&frame); - av_packet_free(&pkt); - swr_free(&resampler); + instance.processedSamples += frame->nb_samples; + // av_frame_free(&frame); + // av_packet_free(&pkt); + // swr_free(&resampler); } if (!(runJob.flags & RUN_MULTIPLE_FRAMES)) { break; @@ -586,11 +705,12 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, if (runJob.flags & SIDEBAND_STREAM) { ORBIS_LOG_TODO("SIDEBAND_STREAM", currentSize, inputReaded, - outputWritten); + outputWritten, instance.processedSamples); AJMSidebandStream *stream = reinterpret_cast( runJob.pSideband + currentSize); stream->inputSize = inputReaded; stream->outputSize = outputWritten; + stream->decodedSamples = instance.processedSamples; currentSize += sizeof(AJMSidebandStream); } @@ -604,6 +724,17 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, currentSize += sizeof(AJMSidebandFormat); } + if (runJob.flags & SIDEBAND_GAPLESS_DECODE) { + ORBIS_LOG_TODO("SIDEBAND_GAPLESS_DECODE", currentSize); + AJMSidebandGaplessDecode *gapless = + reinterpret_cast(runJob.pSideband + + currentSize); + gapless->skipSamples = instance.gaplessSkipSamples; + gapless->totalSamples = instance.gaplessTotalSamples; + gapless->totalSkippedSamples = instance.gaplessTotalSkippedSamples; + currentSize += sizeof(AJMSidebandGaplessDecode); + } + if (runJob.flags & RUN_GET_CODEC_INFO) { ORBIS_LOG_TODO("RUN_GET_CODEC_INFO"); if (instance.codec == AJM_CODEC_At9) { diff --git a/rpcsx/iodev/ajm.hpp b/rpcsx/iodev/ajm.hpp index b5f9cbb..0f5a3ac 100644 --- a/rpcsx/iodev/ajm.hpp +++ b/rpcsx/iodev/ajm.hpp @@ -1,3 +1,5 @@ +#pragma once + #include "orbis-config.hpp" #include "orbis/utils/Logs.hpp" #include @@ -16,14 +18,14 @@ enum class Opcode : std::uint8_t { JobBufferSidebandRa = 18, }; -typedef struct InstructionHeader { +struct InstructionHeader { orbis::uint32_t id; orbis::uint32_t len; -} InstructionHeader; +}; static_assert(sizeof(InstructionHeader) == 0x8); -typedef struct OpcodeHeader { +struct OpcodeHeader { orbis::uint32_t opcode; Opcode getOpcode() const { @@ -35,16 +37,16 @@ typedef struct OpcodeHeader { return static_cast(opcode & 0x1f); } -} OpcodeHeader; +}; -typedef struct ReturnAddress { +struct ReturnAddress { orbis::uint32_t opcode; orbis::uint32_t unk; // 0, padding? orbis::ptr returnAddress; -} ReturnAddress; +}; static_assert(sizeof(ReturnAddress) == 0x10); -typedef struct BatchJobControlBufferRa { +struct BatchJobControlBufferRa { orbis::uint32_t opcode; orbis::uint32_t sidebandInputSize; std::byte* pSidebandInput; @@ -53,38 +55,42 @@ typedef struct BatchJobControlBufferRa { orbis::uint32_t commandId; orbis::uint32_t sidebandOutputSize; std::byte* pSidebandOutput; -} BatchJobControlBufferRa; + + std::uint64_t getFlags() { + return ((uint64_t)flagsHi << 0x1a) | flagsLo; + } +}; static_assert(sizeof(BatchJobControlBufferRa) == 0x28); -typedef struct BatchJobInputBufferRa { +struct BatchJobInputBufferRa { orbis::uint32_t opcode; orbis::uint32_t szInputSize; std::byte* pInput; -} BatchJobInputBufferRa; +}; static_assert(sizeof(BatchJobInputBufferRa) == 0x10); -typedef struct BatchJobFlagsRa { +struct BatchJobFlagsRa { orbis::uint32_t flagsHi; orbis::uint32_t flagsLo; -} BatchJobFlagsRa; +}; static_assert(sizeof(BatchJobFlagsRa) == 0x8); -typedef struct BatchJobOutputBufferRa { +struct BatchJobOutputBufferRa { orbis::uint32_t opcode; - orbis::uint32_t szOutputSize; + orbis::uint32_t outputSize; std::byte* pOutput; -} BatchJobOutputBufferRa; +}; static_assert(sizeof(BatchJobOutputBufferRa) == 0x10); -typedef struct BatchJobSidebandBufferRa { +struct BatchJobSidebandBufferRa { orbis::uint32_t opcode; orbis::uint32_t sidebandSize; std::byte* pSideband; -} BatchJobSidebandBufferRa; +}; static_assert(sizeof(BatchJobSidebandBufferRa) == 0x10); -typedef struct RunJob { +struct RunJob { orbis::uint64_t flags; orbis::uint32_t inputSize; std::byte* pInput; @@ -93,7 +99,7 @@ typedef struct RunJob { orbis::uint32_t sidebandSize; std::byte* pSideband; bool control; -} RunJob; +}; // Thanks to mystical SirNickity with 1 post // https://hydrogenaud.io/index.php?topic=85125.msg747716#msg747716 @@ -213,7 +219,7 @@ enum AJMCodecs : orbis::uint32_t { }; enum AJMChannels : orbis::uint32_t { - AJM_DEFAULT = 0, + AJM_CHANNEL_DEFAULT = 0, AJM_CHANNEL_1 = 1, AJM_CHANNEL_2 = 2, AJM_CHANNEL_3 = 3, @@ -229,7 +235,7 @@ enum AJMFormat : orbis::uint32_t { AJM_FORMAT_FLOAT = 2 }; -typedef struct At9Instance { +struct At9Instance { orbis::ptr handle; orbis::uint32_t inputChannels; orbis::uint32_t framesInSuperframe; @@ -239,57 +245,69 @@ typedef struct At9Instance { orbis::uint32_t superFrameSize; orbis::uint32_t estimatedSizeUsed; orbis::uint32_t sampleRate; -} At9Instance; +}; -typedef struct AACInstance { +struct AACInstance { AACHeaderType headerType; orbis::uint32_t sampleRate; -} AACInstance; +}; -typedef struct Instance { +struct Instance { AJMCodecs codec; - AJMChannels outputChannels; + AJMChannels maxChannels; AJMFormat outputFormat; At9Instance at9; AACInstance aac; AVCodecContext *codecCtx; SwrContext *resampler; orbis::uint32_t lastBatchId; -} Instance; + // TODO: use AJMSidebandGaplessDecode for these variables + orbis::uint32_t gaplessTotalSamples; + orbis::uint16_t gaplessSkipSamples; + orbis::uint16_t gaplessTotalSkippedSamples; + orbis::uint32_t processedSamples; +}; -typedef struct AJMSidebandResult { +struct AJMSidebandResult { orbis::int32_t result; orbis::int32_t codecResult; -} AJMSidebandResult; +}; -typedef struct AJMSidebandStream { +struct AJMSidebandStream { orbis::int32_t inputSize; orbis::int32_t outputSize; - orbis::uint64_t unk0; -} AJMSidebandStream; + orbis::uint64_t decodedSamples; +}; -typedef struct AJMSidebandMultipleFrames { +struct AJMSidebandMultipleFrames { orbis::uint32_t framesProcessed; orbis::uint32_t unk0; -} AJMSidebandMultipleFrames; +}; -typedef struct AJMSidebandFormat { +struct AJMSidebandFormat { AJMChannels channels; orbis::uint32_t unk0; // maybe channel mask? orbis::uint32_t sampleRate; AJMFormat sampleFormat; uint32_t bitrate; uint32_t unk1; -} AJMSidebandFormat; +}; -typedef struct AJMAt9CodecInfoSideband { + +struct AJMSidebandGaplessDecode { + orbis::uint32_t totalSamples; + orbis::uint16_t skipSamples; + orbis::uint16_t totalSkippedSamples; +}; + +struct AJMAt9CodecInfoSideband { orbis::uint32_t superFrameSize; orbis::uint32_t framesInSuperFrame; orbis::uint32_t unk0; orbis::uint32_t frameSamples; -} AJMAt9CodecInfoSideband; +}; -typedef struct AJMMP3CodecInfoSideband { +struct AJMMP3CodecInfoSideband { orbis::uint32_t header; orbis::uint8_t unk0; orbis::uint8_t unk1; @@ -300,15 +318,16 @@ typedef struct AJMMP3CodecInfoSideband { orbis::uint16_t unk6; orbis::uint16_t unk7; orbis::uint16_t unk8; -} AJMMP3CodecInfoSideband; +}; -typedef struct AJMAACCodecInfoSideband { +struct AJMAACCodecInfoSideband { orbis::uint32_t heaac; orbis::uint32_t unk0; -} AJMAACCodecInfoSideband; +}; enum ControlFlags { CONTROL_INITIALIZE = 0x4000, + CONTROL_RESET = 0x2000, }; enum RunFlags { @@ -318,5 +337,6 @@ enum RunFlags { enum SidebandFlags { SIDEBAND_STREAM = 0x800000000000, - SIDEBAND_FORMAT = 0x400000000000 + SIDEBAND_FORMAT = 0x400000000000, + SIDEBAND_GAPLESS_DECODE = 0x200000000000, }; \ No newline at end of file