diff --git a/rpcsx/iodev/ajm.cpp b/rpcsx/iodev/ajm.cpp index 1fe621772..2ed668349 100644 --- a/rpcsx/iodev/ajm.cpp +++ b/rpcsx/iodev/ajm.cpp @@ -7,7 +7,9 @@ #include "orbis/thread/Thread.hpp" #include "orbis/utils/Logs.hpp" #include +#include #include +#include #include #include extern "C" { @@ -24,15 +26,25 @@ extern "C" { struct AjmFile : orbis::File {}; -namespace ajm { -orbis::uint32_t batchId = 1; +struct AjmDevice : IoDevice { + // TODO: remove when moving input and temp buffers to instance + orbis::shared_mutex mtx; + // TODO: move to intsance + orbis::kvector inputBuffer; + orbis::uint32_t inputSize = 0; + orbis::kvector tempBuffer; -orbis::uint32_t at9InstanceId = 0; -orbis::uint32_t mp3InstanceId = 0; -orbis::uint32_t aacInstanceId = 0; -orbis::uint32_t unimplementedInstanceId = 0; -orbis::kmap instanceMap; -} // namespace ajm + orbis::uint32_t batchId = 1; // temp + + orbis::uint32_t at9InstanceId = 0; + orbis::uint32_t mp3InstanceId = 0; + orbis::uint32_t aacInstanceId = 0; + orbis::uint32_t unimplementedInstanceId = 0; + orbis::kmap instanceMap; + orbis::ErrorCode open(orbis::Ref *file, const char *path, + std::uint32_t flags, std::uint32_t mode, + orbis::Thread *thread) override; +}; AVSampleFormat ajmToAvFormat(AJMFormat ajmFormat) { switch (ajmFormat) { @@ -47,9 +59,49 @@ AVSampleFormat ajmToAvFormat(AJMFormat ajmFormat) { } } +void reset(Instance *instance) { + instance->gapless.skipSamples = 0; + instance->gapless.totalSamples = 0; + instance->gapless.totalSkippedSamples = 0; + instance->processedSamples = 0; +} + +void resetAt9(Instance *instance) { + if (instance->at9.configData) { + Atrac9ReleaseHandle(instance->at9.handle); + instance->at9.estimatedSizeUsed = 0; + instance->at9.superFrameSize = 0; + instance->at9.framesInSuperframe = 0; + instance->at9.frameSamples = 0; + instance->at9.sampleRate = 0; + instance->at9.superFrameDataIdx = 0; + instance->at9.inputChannels = 0; + instance->at9.superFrameDataLeft = 0; + instance->at9.handle = Atrac9GetHandle(); + int err = Atrac9InitDecoder(instance->at9.handle, + (uint8_t *)&instance->at9.configData); + if (err < 0) { + ORBIS_LOG_FATAL("AT9 Init Decoder error", err); + std::abort(); + } + Atrac9CodecInfo pCodecInfo; + Atrac9GetCodecInfo(instance->at9.handle, &pCodecInfo); + + instance->at9.frameSamples = pCodecInfo.frameSamples; + instance->at9.inputChannels = pCodecInfo.channels; + instance->at9.framesInSuperframe = pCodecInfo.framesInSuperframe; + instance->at9.superFrameDataIdx = 0; + instance->at9.superFrameSize = pCodecInfo.superframeSize; + instance->at9.superFrameDataLeft = pCodecInfo.superframeSize; + instance->at9.sampleRate = pCodecInfo.samplingRate; + } +} + static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, void *argp, orbis::Thread *thread) { + auto device = static_cast(file->device.get()); + std::lock_guard lock(device->mtx); // 0xc0288900 - finalize // 0xc0288903 - module register // 0xc0288904 - module unregister @@ -67,14 +119,16 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, orbis::uint32_t instanceId; }; auto args = reinterpret_cast(argp); - auto it = ajm::instanceMap.find(args->instanceId); - if (it != ajm::instanceMap.end()) { - auto &instance = ajm::instanceMap[args->instanceId]; + auto it = device->instanceMap.find(args->instanceId); + if (it != device->instanceMap.end()) { + auto &instance = device->instanceMap[args->instanceId]; if (instance.resampler) { swr_free(&instance.resampler); + } + if (instance.codecCtx) { avcodec_free_context(&instance.codecCtx); } - ajm::instanceMap.erase(args->instanceId); + device->instanceMap.erase(it); } args->result = 0; } @@ -98,11 +152,11 @@ 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 + ajm::at9InstanceId++; + args->instanceId = codecOffset + device->at9InstanceId++; } else if (codecId == AJM_CODEC_MP3) { - args->instanceId = codecOffset + ajm::mp3InstanceId++; + args->instanceId = codecOffset + device->mp3InstanceId++; } else if (codecId == AJM_CODEC_AAC) { - args->instanceId = codecOffset + ajm::aacInstanceId++; + args->instanceId = codecOffset + device->aacInstanceId++; } Instance instance; instance.codec = codecId; @@ -111,6 +165,15 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, instance.outputFormat = AJMFormat((args->flags & ~7) & 0b11); if (codecId == AJM_CODEC_At9) { instance.at9.handle = Atrac9GetHandle(); + if (instance.outputFormat == AJM_FORMAT_S16) { + instance.at9.outputFormat = kAtrac9FormatS16; + } else if (instance.outputFormat == AJM_FORMAT_S32) { + instance.at9.outputFormat = kAtrac9FormatS32; + } else if (instance.outputFormat == AJM_FORMAT_FLOAT) { + instance.at9.outputFormat = kAtrac9FormatF32; + } else { + // TODO: throw error + } } if (codecId == AJM_CODEC_AAC || codecId == AJM_CODEC_MP3) { const AVCodec *codec = avcodec_find_decoder( @@ -132,12 +195,12 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, instance.codecCtx = codecCtx; } - ajm::instanceMap.insert({ + device->instanceMap.insert({ args->instanceId, instance, }); } else { - args->instanceId = codecOffset + ajm::unimplementedInstanceId++; + args->instanceId = codecOffset + device->unimplementedInstanceId++; } ORBIS_LOG_ERROR(__FUNCTION__, request, args->result, args->unk0, args->flags, args->codec, args->instanceId); @@ -153,12 +216,12 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, }; auto args = reinterpret_cast(argp); args->result = 0; - args->batchId = ajm::batchId; - ORBIS_LOG_ERROR(__FUNCTION__, request, args->result, args->unk0, - args->pBatch, args->batchSize, args->priority, - args->batchError, args->batchId); - ajm::batchId += 1; - thread->where(); + args->batchId = device->batchId; + // ORBIS_LOG_ERROR(__FUNCTION__, request, args->result, args->unk0, + // args->pBatch, args->batchSize, args->priority, + // args->batchError, args->batchId); + device->batchId += 1; + // thread->where(); auto ptr = args->pBatch; auto endPtr = args->pBatch + args->batchSize; @@ -169,16 +232,17 @@ 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 = ajm::instanceMap[instanceId]; - ORBIS_LOG_TODO("instance info", instanceId); + auto &instance = device->instanceMap[instanceId]; RunJob runJob{}; + device->inputSize = 0; while (jobPtr < endJobPtr) { auto typed = (OpcodeHeader *)jobPtr; switch (typed->getOpcode()) { case Opcode::ReturnAddress: { - ReturnAddress *ra = (ReturnAddress *)jobPtr; - ORBIS_LOG_ERROR(__FUNCTION__, request, "return address", ra->opcode, - ra->unk, ra->returnAddress); + // ReturnAddress *ra = (ReturnAddress *)jobPtr; + // ORBIS_LOG_ERROR(__FUNCTION__, request, "return address", + // ra->opcode, + // ra->unk, ra->returnAddress); jobPtr += sizeof(ReturnAddress); break; } @@ -193,13 +257,10 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, ctrl->commandId, ctrl->flagsHi, ctrl->flagsLo, ctrl->sidebandInputSize, ctrl->sidebandOutputSize); 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"); + reset(&instance); + if (instance.codec == AJM_CODEC_At9) { + resetAt9(&instance); + } } if (ctrl->getFlags() & CONTROL_INITIALIZE) { @@ -210,35 +271,9 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, }; InitalizeBuffer *initializeBuffer = (InitalizeBuffer *)ctrl->pSidebandInput; - Atrac9ReleaseHandle(instance.at9.handle); - instance.at9.estimatedSizeUsed = 0; - instance.at9.superFrameSize = 0; - instance.at9.framesInSuperframe = 0; - instance.at9.frameSamples = 0; - instance.at9.sampleRate = 0; - instance.at9.superFrameDataIdx = 0; - instance.at9.inputChannels = 0; - instance.at9.superFrameDataLeft = 0; - instance.at9.handle = Atrac9GetHandle(); - int err = Atrac9InitDecoder( - instance.at9.handle, - reinterpret_cast(&initializeBuffer->configData)); - if (err < 0) { - ORBIS_LOG_FATAL("AT9 Init Decoder error", err); - rx::hexdump({(std::byte *)ctrl->pSidebandInput, - ctrl->sidebandInputSize}); - std::abort(); - } - Atrac9CodecInfo pCodecInfo; - Atrac9GetCodecInfo(instance.at9.handle, &pCodecInfo); - - instance.at9.frameSamples = pCodecInfo.frameSamples; - instance.at9.inputChannels = pCodecInfo.channels; - instance.at9.framesInSuperframe = pCodecInfo.framesInSuperframe; - instance.at9.superFrameDataIdx = 0; - instance.at9.superFrameSize = pCodecInfo.superframeSize; - instance.at9.superFrameDataLeft = pCodecInfo.superframeSize; - instance.at9.sampleRate = pCodecInfo.samplingRate; + instance.at9.configData = initializeBuffer->configData; + reset(&instance); + resetAt9(&instance); orbis::uint32_t maxChannels = instance.maxChannels == AJM_CHANNEL_DEFAULT @@ -248,48 +283,12 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, instance.at9.inputChannels > maxChannels ? maxChannels : instance.at9.inputChannels; - if (instance.resampler) { - swr_free(&instance.resampler); - instance.resampler = NULL; - } - if (instance.at9.inputChannels != outputChannels || - instance.outputFormat != AJM_FORMAT_S16) { - instance.resampler = swr_alloc(); - if (!instance.resampler) { - ORBIS_LOG_FATAL("Could not allocate resampler context"); - std::abort(); - } - - AVChannelLayout inputChLayout; - av_channel_layout_default(&inputChLayout, - instance.at9.inputChannels); - - AVChannelLayout outputChLayout; - av_channel_layout_default(&outputChLayout, outputChannels); - - av_opt_set_chlayout(instance.resampler, "in_chlayout", - &inputChLayout, 0); - av_opt_set_chlayout(instance.resampler, "out_chlayout", - &outputChLayout, 0); - av_opt_set_int(instance.resampler, "in_sample_rate", - pCodecInfo.samplingRate, 0); - av_opt_set_int(instance.resampler, "out_sample_rate", - pCodecInfo.samplingRate, 0); - av_opt_set_sample_fmt(instance.resampler, "in_sample_fmt", - ajmToAvFormat(AJM_FORMAT_S16), 0); - av_opt_set_sample_fmt(instance.resampler, "out_sample_fmt", - ajmToAvFormat(instance.outputFormat), 0); - if (swr_init(instance.resampler) < 0) { - ORBIS_LOG_FATAL( - "Failed to initialize the resampling context"); - std::abort(); - } - } - ORBIS_LOG_TODO("CONTROL_INITIALIZE", pCodecInfo.channels, - pCodecInfo.samplingRate, pCodecInfo.frameSamples, - pCodecInfo.superframeSize, maxChannels, + // TODO: check max channels + ORBIS_LOG_TODO("CONTROL_INITIALIZE", instance.at9.inputChannels, + instance.at9.sampleRate, instance.at9.frameSamples, + instance.at9.superFrameSize, maxChannels, outputChannels, initializeBuffer->configData, - &initializeBuffer->configData); + (orbis::uint32_t)instance.outputFormat); } else if (instance.codec == AJM_CODEC_AAC) { struct InitalizeBuffer { orbis::uint32_t headerIndex; @@ -312,40 +311,44 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, InitalizeBuffer *initializeBuffer = (InitalizeBuffer *)ctrl->pSidebandInput; if (initializeBuffer->totalSamples > 0) { - instance.gaplessTotalSamples = initializeBuffer->totalSamples; + instance.gapless.totalSamples = initializeBuffer->totalSamples; } if (initializeBuffer->skipSamples > 0) { - instance.gaplessSkipSamples = initializeBuffer->skipSamples; + instance.gapless.skipSamples = initializeBuffer->skipSamples; } ORBIS_LOG_TODO("SIDEBAND_GAPLESS_DECODE", - instance.gaplessSkipSamples, - instance.gaplessTotalSamples); + instance.gapless.skipSamples, + instance.gapless.totalSamples); } jobPtr += sizeof(BatchJobControlBufferRa); break; } case Opcode::RunBufferRa: { BatchJobInputBufferRa *job = (BatchJobInputBufferRa *)jobPtr; - ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobInputBufferRa", - job->opcode, job->szInputSize, job->pInput); - runJob.pInput = job->pInput; - runJob.inputSize = job->szInputSize; + // ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobInputBufferRa", + // job->opcode, job->szInputSize, job->pInput); + // We don't support split buffers for now, so ignore new buffers + if (device->inputSize == 0) { + std::memcpy(device->inputBuffer.data(), job->pInput, + job->szInputSize); + device->inputSize += job->szInputSize; + } // rx::hexdump({(std::byte*) job->pInput, job->szInputSize}); jobPtr += sizeof(BatchJobInputBufferRa); break; } case Opcode::Flags: { BatchJobFlagsRa *job = (BatchJobFlagsRa *)jobPtr; - ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobFlagsRa", - job->flagsHi, job->flagsLo); - runJob.flags = ((uint64_t)job->flagsHi << 0x1a) | job->flagsLo; + // ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobFlagsRa", + // job->flagsHi, job->flagsLo); + runJob.flags = ((orbis::uint64_t)job->flagsHi << 0x1a) | job->flagsLo; jobPtr += sizeof(BatchJobFlagsRa); break; } case Opcode::JobBufferOutputRa: { BatchJobOutputBufferRa *job = (BatchJobOutputBufferRa *)jobPtr; - ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobOutputBufferRa", - job->opcode, job->outputSize, job->pOutput); + // ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobOutputBufferRa", + // job->opcode, job->outputSize, job->pOutput); runJob.pOutput = job->pOutput; runJob.outputSize = job->outputSize; jobPtr += sizeof(BatchJobOutputBufferRa); @@ -353,8 +356,8 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, } case Opcode::JobBufferSidebandRa: { BatchJobSidebandBufferRa *job = (BatchJobSidebandBufferRa *)jobPtr; - ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobSidebandBufferRa", - job->opcode, job->sidebandSize, job->pSideband); + // ORBIS_LOG_ERROR(__FUNCTION__, request, "BatchJobSidebandBufferRa", + // job->opcode, job->sidebandSize, job->pSideband); runJob.pSideband = job->pSideband; runJob.sidebandSize = job->sidebandSize; jobPtr += sizeof(BatchJobSidebandBufferRa); @@ -373,13 +376,14 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, if (runJob.flags & SIDEBAND_STREAM) { AJMSidebandStream *stream = reinterpret_cast(runJob.pSideband + 8); - stream->inputSize = runJob.inputSize; + stream->inputSize = device->inputSize; stream->outputSize = runJob.outputSize; } } else if (!runJob.control) { - orbis::uint32_t maxChannels = - instance.maxChannels == AJM_CHANNEL_DEFAULT ? 2 - : instance.maxChannels; + // orbis::uint32_t maxChannels = + // instance.maxChannels == AJM_CHANNEL_DEFAULT ? 2 + // : + // instance.maxChannels; AJMSidebandResult *result = reinterpret_cast(runJob.pSideband); result->result = 0; @@ -388,37 +392,41 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, 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) { - // TODO: initialize if not - if (instance.at9.frameSamples == 0 && - instance.codec == AJM_CODEC_At9) { + orbis::uint32_t samplesCount = 0; + if (device->inputSize != 0 && runJob.outputSize != 0) { + do { + if (instance.codec == AJM_CODEC_At9 && + instance.at9.frameSamples == 0) { + break; + } + if (inputReaded >= device->inputSize || + outputWritten >= runJob.outputSize) { break; } - if (instance.codec == AJM_CODEC_At9) { - 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 - : instance.outputFormat), - 0); - orbis::uint8_t *tempBuffer = - instance.resampler ? (uint8_t *)av_malloc(outputBufferSize) - : reinterpret_cast( - runJob.pOutput + outputWritten); + AVFrame *frame = av_frame_alloc(); + rx::atScopeExit _free_frame([&] { av_frame_free(&frame); }); + + orbis::uint32_t readed = 0; + orbis::uint32_t outputBufferSize = 0; + if (instance.codec == AJM_CODEC_At9) { orbis::int32_t bytesUsed = 0; - int err = - Atrac9Decode(instance.at9.handle, runJob.pInput + inputReaded, - tempBuffer, kAtrac9FormatS16, &bytesUsed); + outputBufferSize = av_samples_get_buffer_size( + nullptr, instance.at9.inputChannels, + instance.at9.frameSamples, + ajmToAvFormat(instance.outputFormat), 0); + int err = Atrac9Decode(instance.at9.handle, + &device->inputBuffer[inputReaded], + device->tempBuffer.data(), + instance.at9.outputFormat, &bytesUsed); if (err != ERR_SUCCESS) { - ORBIS_LOG_FATAL("Could not decode frame", err); + rx::hexdump({(std::byte *)&device->inputBuffer[inputReaded], + device->inputSize}); + ORBIS_LOG_FATAL("Could not decode frame", err, + instance.at9.estimatedSizeUsed, + instance.at9.superFrameSize, + instance.at9.frameSamples, instance.at9.handle, + inputReaded, outputWritten); std::abort(); } instance.at9.estimatedSizeUsed = @@ -432,80 +440,27 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, instance.at9.superFrameDataIdx = 0; instance.at9.superFrameDataLeft = instance.at9.superFrameSize; } - - ORBIS_LOG_TODO("used size", bytesUsed, - instance.at9.estimatedSizeUsed); - // TODO: possible memory leak because "genius" code to avoiding - // memory copying - framesProcessed += 1; - 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); - continue; - } 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); - continue; - } - } - if (instance.resampler) { - auto outputBuffer = reinterpret_cast( - runJob.pOutput + outputWritten); - - int nb_samples = - swr_convert(instance.resampler, &outputBuffer, - instance.at9.frameSamples, &tempBuffer, - instance.at9.frameSamples); - if (nb_samples < 0) { - ORBIS_LOG_FATAL("Error while resampling", nb_samples); - std::abort(); - } - av_freep(&tempBuffer); - } - channels = instance.at9.inputChannels; - sampleRate = instance.at9.sampleRate; - inputReaded += instance.at9.estimatedSizeUsed; - // outputWritten += std::max((orbis::uint32_t)outputBufferSize, - // runJob.outputSize); - outputWritten += outputBufferSize; - instance.processedSamples += instance.at9.frameSamples; + samplesCount = instance.at9.frameSamples; + readed = instance.at9.estimatedSizeUsed; + instance.lastDecode.channels = + AJMChannels(instance.at9.inputChannels); + instance.lastDecode.sampleRate = instance.at9.sampleRate; + // ORBIS_LOG_TODO("at9 decode", instance.at9.estimatedSizeUsed, + // instance.at9.superFrameDataLeft, + // instance.at9.superFrameDataIdx, + // instance.at9.framesInSuperframe); } else if (instance.codec == AJM_CODEC_MP3) { - ORBIS_LOG_FATAL("Pre get mp3 data size info", runJob.inputSize, - runJob.outputSize, runJob.sidebandSize, - runJob.flags); - auto realInputSize = - get_mp3_data_size((uint8_t *)(runJob.pInput + inputReaded)); - if (realInputSize == 0) { - realInputSize = runJob.inputSize; + auto frameSize = get_mp3_data_size( + (orbis::uint8_t *)(&device->inputBuffer[inputReaded])); + if (frameSize == 0) { + frameSize = device->inputSize; } else { - realInputSize = std::min(realInputSize, runJob.inputSize); + frameSize = std::min(frameSize, device->inputSize); } - - if (inputReaded + realInputSize > runJob.inputSize) { - break; - } - - // rx::hexdump( - // {(std::byte *)(runJob.pInput + inputReaded), - // realInputSize}); - 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 = (orbis::uint8_t *)(runJob.pInput + inputReaded); - pkt->size = realInputSize; + pkt->data = (orbis::uint8_t *)(&device->inputBuffer[inputReaded]); + pkt->size = frameSize; int ret = avcodec_send_packet(instance.codecCtx, pkt); if (ret < 0) { ORBIS_LOG_FATAL("Error sending packet for decoding", ret); @@ -516,49 +471,62 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, ORBIS_LOG_FATAL("Error during decoding"); std::abort(); } + outputBufferSize = av_samples_get_buffer_size( + nullptr, frame->ch_layout.nb_channels, frame->nb_samples, + ajmToAvFormat(instance.outputFormat), 0); + samplesCount = frame->nb_samples; + readed = frameSize; + instance.lastDecode.channels = + AJMChannels(frame->ch_layout.nb_channels); + instance.lastDecode.sampleRate = frame->sample_rate; + } else if (instance.codec == AJM_CODEC_AAC) { + AVPacket *pkt = av_packet_alloc(); + rx::atScopeExit _free_pkt([&] { av_packet_free(&pkt); }); + pkt->data = (orbis::uint8_t *)(&device->inputBuffer[inputReaded]); + pkt->size = device->inputSize; - 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"); + // HACK: to avoid writing a bunch of useless calls + // we simply call this method directly (but it can be very + // unstable) + int gotFrame; + int len = + ffcodec(instance.codecCtx->codec) + ->cb.decode(instance.codecCtx, frame, &gotFrame, pkt); + if (len < 0) { + ORBIS_LOG_FATAL("Error during decoding"); std::abort(); } + outputBufferSize = av_samples_get_buffer_size( + nullptr, frame->ch_layout.nb_channels, frame->nb_samples, + ajmToAvFormat(instance.outputFormat), 0); + samplesCount = frame->nb_samples; + readed = len; + instance.lastDecode.channels = + AJMChannels(frame->ch_layout.nb_channels); + instance.lastDecode.sampleRate = frame->sample_rate; + } + framesProcessed += 1; + inputReaded += readed; - 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, + if (instance.gapless.skipSamples > 0 || + instance.gapless.totalSamples > 0) { + if (instance.gapless.skipSamples > + instance.gapless.totalSkippedSamples || + instance.processedSamples > instance.gapless.totalSamples) { + instance.gapless.totalSkippedSamples += samplesCount; + continue; + } + } + // at least three codecs outputs in float + // and mp3 support sample rate resample (TODO), so made resampling + // with swr + if (instance.codec != AJM_CODEC_At9) { + auto resampler = swr_alloc(); + AVChannelLayout chLayout; + av_channel_layout_default(&chLayout, frame->ch_layout.nb_channels); - - AVChannelLayout outputChLayout; - av_channel_layout_default(&outputChLayout, outputChannels); - - av_opt_set_chlayout(resampler, "in_chlayout", &inputChLayout, 0); - av_opt_set_chlayout(resampler, "out_chlayout", &outputChLayout, - 0); + av_opt_set_chlayout(resampler, "in_chlayout", &chLayout, 0); + av_opt_set_chlayout(resampler, "out_chlayout", &chLayout, 0); av_opt_set_int(resampler, "in_sample_rate", frame->sample_rate, 0); av_opt_set_int(resampler, "out_sample_rate", frame->sample_rate, @@ -571,168 +539,29 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, ORBIS_LOG_FATAL("Failed to initialize the resampling context"); std::abort(); } - - 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); - if (outputBufferSize < 0) { - ORBIS_LOG_FATAL("Could not allocate output buffer"); - std::abort(); - } - ORBIS_LOG_TODO("output buffer info", frame->ch_layout.nb_channels, - frame->nb_samples, (int32_t)instance.outputFormat, - outputBufferSize, - instance.gaplessTotalSkippedSamples, - instance.processedSamples); - - if (outputWritten + outputBufferSize > runJob.outputSize) { - ORBIS_LOG_TODO("overwriting", outputWritten, outputBufferSize, - outputWritten + outputBufferSize, - runJob.outputSize); - break; - } - + orbis::uint8_t *outputBuffer = + reinterpret_cast(device->tempBuffer.data()); int nb_samples = swr_convert(resampler, &outputBuffer, frame->nb_samples, - (const uint8_t **)frame->data, frame->nb_samples); + (const orbis::uint8_t **)frame->extended_data, + frame->nb_samples); if (nb_samples < 0) { ORBIS_LOG_FATAL("Error while converting"); std::abort(); } - - memcpy(runJob.pOutput + outputWritten, outputBuffer, - outputBufferSize); - channels = frame->ch_layout.nb_channels; - 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); - } 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; - - // HACK: to avoid writing a bunch of useless calls - // we simply call this method directly (but it can be very - // unstable) - int gotFrame; - int len = - ffcodec(instance.codecCtx->codec) - ->cb.decode(instance.codecCtx, frame, &gotFrame, pkt); - - orbis::uint32_t 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.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(); - } - - AVChannelLayout inputChLayout; - av_channel_layout_default(&inputChLayout, - frame->ch_layout.nb_channels); - - AVChannelLayout outputChLayout; - av_channel_layout_default(&outputChLayout, outputChannels); - - av_opt_set_chlayout(resampler, "in_chlayout", &inputChLayout, 0); - av_opt_set_chlayout(resampler, "out_chlayout", &outputChLayout, - 0); - av_opt_set_int(resampler, "in_sample_rate", - instance.aac.sampleRate, 0); - av_opt_set_int(resampler, "out_sample_rate", - instance.aac.sampleRate, 0); - av_opt_set_sample_fmt(resampler, "in_sample_fmt", - ajmToAvFormat(AJM_FORMAT_FLOAT), 0); - av_opt_set_sample_fmt(resampler, "out_sample_fmt", - ajmToAvFormat(instance.outputFormat), 0); - if (swr_init(resampler) < 0) { - ORBIS_LOG_FATAL("Failed to initialize the resampling context"); - std::abort(); - } - - 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); - if (outputBufferSize < 0) { - ORBIS_LOG_FATAL("Could not allocate output buffer"); - std::abort(); - } - - int nb_samples = - swr_convert(resampler, &outputBuffer, frame->nb_samples, - frame->extended_data, frame->nb_samples); - if (nb_samples < 0) { - ORBIS_LOG_FATAL("Error while converting"); - std::abort(); - } - - memcpy(runJob.pOutput + outputWritten, outputBuffer, - outputBufferSize); - channels = frame->ch_layout.nb_channels; - sampleRate = frame->sample_rate; - inputReaded += len; - outputWritten += outputBufferSize; - framesProcessed += 1; - instance.processedSamples += frame->nb_samples; - // av_frame_free(&frame); - // av_packet_free(&pkt); - // swr_free(&resampler); } - if (!(runJob.flags & RUN_MULTIPLE_FRAMES)) { - break; - } - } + memcpy(runJob.pOutput + outputWritten, device->tempBuffer.data(), + outputBufferSize); + outputWritten += outputBufferSize; + instance.processedSamples += samplesCount; + } while ((runJob.flags & RUN_MULTIPLE_FRAMES) != 0); } orbis::int64_t currentSize = sizeof(AJMSidebandResult); if (runJob.flags & SIDEBAND_STREAM) { - ORBIS_LOG_TODO("SIDEBAND_STREAM", currentSize, inputReaded, - outputWritten, instance.processedSamples); + // ORBIS_LOG_TODO("SIDEBAND_STREAM", currentSize, inputReaded, + // outputWritten, instance.processedSamples); AJMSidebandStream *stream = reinterpret_cast( runJob.pSideband + currentSize); stream->inputSize = inputReaded; @@ -742,28 +571,29 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, } if (runJob.flags & SIDEBAND_FORMAT) { - ORBIS_LOG_TODO("SIDEBAND_FORMAT", currentSize); + // ORBIS_LOG_TODO("SIDEBAND_FORMAT", currentSize); AJMSidebandFormat *format = reinterpret_cast( runJob.pSideband + currentSize); - format->channels = AJMChannels(channels); - format->sampleRate = sampleRate; - format->sampleFormat = AJM_FORMAT_FLOAT; + format->channels = AJMChannels(instance.lastDecode.channels); + format->sampleRate = instance.lastDecode.sampleRate; + format->sampleFormat = instance.outputFormat; + // TODO: channel mask and bitrate currentSize += sizeof(AJMSidebandFormat); } if (runJob.flags & SIDEBAND_GAPLESS_DECODE) { - ORBIS_LOG_TODO("SIDEBAND_GAPLESS_DECODE", currentSize); + // 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; + gapless->skipSamples = instance.gapless.skipSamples; + gapless->totalSamples = instance.gapless.totalSamples; + gapless->totalSkippedSamples = instance.gapless.totalSkippedSamples; currentSize += sizeof(AJMSidebandGaplessDecode); } if (runJob.flags & RUN_GET_CODEC_INFO) { - ORBIS_LOG_TODO("RUN_GET_CODEC_INFO"); + // ORBIS_LOG_TODO("RUN_GET_CODEC_INFO"); if (instance.codec == AJM_CODEC_At9) { AJMAt9CodecInfoSideband *info = reinterpret_cast(runJob.pSideband + @@ -788,7 +618,7 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, } if (runJob.flags & RUN_MULTIPLE_FRAMES) { - ORBIS_LOG_TODO("RUN_MULTIPLE_FRAMES", currentSize); + // ORBIS_LOG_TODO("RUN_MULTIPLE_FRAMES", framesProcessed); AJMSidebandMultipleFrames *multipleFrames = reinterpret_cast(runJob.pSideband + currentSize); @@ -807,9 +637,9 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, }; auto args = reinterpret_cast(argp); args->unk0 = 0; - ORBIS_LOG_ERROR(__FUNCTION__, request, args->unk0, args->unk1, - args->batchId, args->timeout, args->batchError); - thread->where(); + // ORBIS_LOG_ERROR(__FUNCTION__, request, args->unk0, args->unk1, + // args->batchId, args->timeout, args->batchError); + // thread->where(); } else { ORBIS_LOG_FATAL("Unhandled AJM ioctl", request); thread->where(); @@ -821,17 +651,18 @@ static const orbis::FileOps fileOps = { .ioctl = ajm_ioctl, }; -struct AjmDevice : IoDevice { - orbis::ErrorCode open(orbis::Ref *file, const char *path, - std::uint32_t flags, std::uint32_t mode, - orbis::Thread *thread) override { - auto newFile = orbis::knew(); - newFile->ops = &fileOps; - newFile->device = this; +orbis::ErrorCode AjmDevice::open(orbis::Ref *file, + const char *path, std::uint32_t flags, + std::uint32_t mode, orbis::Thread *thread) { + auto newFile = orbis::knew(); + newFile->ops = &fileOps; + newFile->device = this; - *file = newFile; - return {}; - } -}; + inputBuffer.reserve(32 * 1024); + tempBuffer.reserve(32 * 1024); + + *file = newFile; + return {}; +} IoDevice *createAjmCharacterDevice() { return orbis::knew(); } diff --git a/rpcsx/iodev/ajm.hpp b/rpcsx/iodev/ajm.hpp index fd3133e90..8ec3a7a65 100644 --- a/rpcsx/iodev/ajm.hpp +++ b/rpcsx/iodev/ajm.hpp @@ -1,7 +1,8 @@ #pragma once +#include "libatrac9/libatrac9.h" #include "orbis-config.hpp" -#include "orbis/utils/Logs.hpp" +// #include "orbis/utils/Logs.hpp" #include extern "C" { #include @@ -29,7 +30,7 @@ struct OpcodeHeader { orbis::uint32_t opcode; Opcode getOpcode() const { - ORBIS_LOG_ERROR(__FUNCTION__, opcode); + // ORBIS_LOG_ERROR(__FUNCTION__, opcode); if (auto loType = static_cast(opcode & 0xf); loType == Opcode::ReturnAddress || loType == Opcode::Flags) { return loType; @@ -49,23 +50,21 @@ static_assert(sizeof(ReturnAddress) == 0x10); struct BatchJobControlBufferRa { orbis::uint32_t opcode; orbis::uint32_t sidebandInputSize; - std::byte* pSidebandInput; + std::byte *pSidebandInput; orbis::uint32_t flagsHi; orbis::uint32_t flagsLo; orbis::uint32_t commandId; orbis::uint32_t sidebandOutputSize; - std::byte* pSidebandOutput; + std::byte *pSidebandOutput; - std::uint64_t getFlags() { - return ((uint64_t)flagsHi << 0x1a) | flagsLo; - } + std::uint64_t getFlags() { return ((uint64_t)flagsHi << 0x1a) | flagsLo; } }; static_assert(sizeof(BatchJobControlBufferRa) == 0x28); struct BatchJobInputBufferRa { orbis::uint32_t opcode; orbis::uint32_t szInputSize; - std::byte* pInput; + std::byte *pInput; }; static_assert(sizeof(BatchJobInputBufferRa) == 0x10); @@ -79,25 +78,23 @@ static_assert(sizeof(BatchJobFlagsRa) == 0x8); struct BatchJobOutputBufferRa { orbis::uint32_t opcode; orbis::uint32_t outputSize; - std::byte* pOutput; + std::byte *pOutput; }; static_assert(sizeof(BatchJobOutputBufferRa) == 0x10); struct BatchJobSidebandBufferRa { orbis::uint32_t opcode; orbis::uint32_t sidebandSize; - std::byte* pSideband; + std::byte *pSideband; }; static_assert(sizeof(BatchJobSidebandBufferRa) == 0x10); struct RunJob { orbis::uint64_t flags; - orbis::uint32_t inputSize; - std::byte* pInput; orbis::uint32_t outputSize; - std::byte* pOutput; + std::byte *pOutput; orbis::uint32_t sidebandSize; - std::byte* pSideband; + std::byte *pSideband; bool control; }; @@ -198,10 +195,10 @@ uint32_t get_mp3_data_size(const uint8_t *data) { ((bps * static_cast(bitrate)) / static_cast(samprate)) + ((pad) ? slot_size : 0); - ORBIS_LOG_TODO("get_mp3_data_size", (uint16_t)ver, (uint16_t)lyr, - (uint16_t)pad, (uint16_t)brx, (uint16_t)srx, bitrate, samprate, - samples, (uint16_t)slot_size, bps, fsize, - static_cast(fsize)); + // ORBIS_LOG_TODO(__FUNCTION__, (uint16_t)ver, (uint16_t)lyr, + // (uint16_t)pad, (uint16_t)brx, (uint16_t)srx, bitrate, samprate, + // samples, (uint16_t)slot_size, bps, fsize, + // static_cast(fsize)); // Frame sizes are truncated integers return static_cast(fsize); @@ -245,6 +242,8 @@ struct At9Instance { orbis::uint32_t superFrameSize{}; orbis::uint32_t estimatedSizeUsed{}; orbis::uint32_t sampleRate{}; + Atrac9Format outputFormat{}; + orbis::uint32_t configData; }; struct AACInstance { @@ -252,20 +251,10 @@ struct AACInstance { orbis::uint32_t sampleRate; }; -struct Instance { - AJMCodecs codec; - AJMChannels maxChannels; - AJMFormat outputFormat; - At9Instance at9; - AACInstance aac; - AVCodecContext *codecCtx; - SwrContext *resampler; - orbis::uint32_t lastBatchId; - // TODO: use AJMSidebandGaplessDecode for these variables - orbis::uint32_t gaplessTotalSamples; - orbis::uint16_t gaplessSkipSamples; - orbis::uint16_t gaplessTotalSkippedSamples; - orbis::uint32_t processedSamples; +struct AJMSidebandGaplessDecode { + orbis::uint32_t totalSamples; + orbis::uint16_t skipSamples; + orbis::uint16_t totalSkippedSamples; }; struct AJMSidebandResult { @@ -293,13 +282,6 @@ struct AJMSidebandFormat { uint32_t unk1; }; - -struct AJMSidebandGaplessDecode { - orbis::uint32_t totalSamples; - orbis::uint16_t skipSamples; - orbis::uint16_t totalSkippedSamples; -}; - struct AJMAt9CodecInfoSideband { orbis::uint32_t superFrameSize; orbis::uint32_t framesInSuperFrame; @@ -325,6 +307,21 @@ struct AJMAACCodecInfoSideband { orbis::uint32_t unk0; }; +struct Instance { + AJMCodecs codec; + AJMChannels maxChannels; + AJMFormat outputFormat; + At9Instance at9; + AACInstance aac; + AVCodecContext *codecCtx; + SwrContext *resampler; + orbis::uint32_t lastBatchId; + // TODO: use AJMSidebandGaplessDecode for these variables + AJMSidebandGaplessDecode gapless; + orbis::uint32_t processedSamples; + AJMSidebandFormat lastDecode; +}; + enum ControlFlags { CONTROL_INITIALIZE = 0x4000, CONTROL_RESET = 0x2000,