diff --git a/.editorconfig b/.editorconfig index 7c6f14d27f..20d5c776b5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -29,6 +29,10 @@ charset = utf-8-bom [Windows/{aboutbox.rc,version.rc}] charset = utf-8 +[ext/at3_standalone/**.{cpp,h}] +indent_style = space +indent_size = 4 + [libretro/**.{cpp,h}] indent_style = space indent_size = 3 diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index 7bf9e7fa45..9aae6db15b 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -147,7 +147,7 @@ struct InputBuffer { struct Atrac; int __AtracSetContext(Atrac *atrac); -void _AtracGenerateContext(Atrac *atrac); +static void _AtracWriteContextToPSPMem(Atrac *atrac); struct AtracLoopInfo { int cuePointID; @@ -419,7 +419,7 @@ struct Atrac { InputBuffer first_; InputBuffer second_; - PSPPointer context_; + PSPPointer context_; void ForceSeekToSample(int sample) { if (decoder_) { @@ -511,22 +511,31 @@ struct AtracResetBufferInfo { const int PSP_NUM_ATRAC_IDS = 6; static bool atracInited = true; -static Atrac *atracIDs[PSP_NUM_ATRAC_IDS]; -static u32 atracIDTypes[PSP_NUM_ATRAC_IDS]; +static Atrac *atracContexts[PSP_NUM_ATRAC_IDS]; +static u32 atracContextTypes[PSP_NUM_ATRAC_IDS]; static int atracLibVersion = 0; static u32 atracLibCrc = 0; void __AtracInit() { + _assert_(sizeof(SceAtracContext) == 256); + atracInited = true; - memset(atracIDs, 0, sizeof(atracIDs)); + memset(atracContexts, 0, sizeof(atracContexts)); // Start with 2 of each in this order. - atracIDTypes[0] = PSP_MODE_AT_3_PLUS; - atracIDTypes[1] = PSP_MODE_AT_3_PLUS; - atracIDTypes[2] = PSP_MODE_AT_3; - atracIDTypes[3] = PSP_MODE_AT_3; - atracIDTypes[4] = 0; - atracIDTypes[5] = 0; + atracContextTypes[0] = PSP_MODE_AT_3_PLUS; + atracContextTypes[1] = PSP_MODE_AT_3_PLUS; + atracContextTypes[2] = PSP_MODE_AT_3; + atracContextTypes[3] = PSP_MODE_AT_3; + atracContextTypes[4] = 0; + atracContextTypes[5] = 0; +} + +void __AtracShutdown() { + for (size_t i = 0; i < ARRAY_SIZE(atracContexts); ++i) { + delete atracContexts[i]; + atracContexts[i] = nullptr; + } } void __AtracLoadModule(int version, u32 crc) { @@ -542,16 +551,16 @@ void __AtracDoState(PointerWrap &p) { Do(p, atracInited); for (int i = 0; i < PSP_NUM_ATRAC_IDS; ++i) { - bool valid = atracIDs[i] != NULL; + bool valid = atracContexts[i] != nullptr; Do(p, valid); if (valid) { - Do(p, atracIDs[i]); + Do(p, atracContexts[i]); } else { - delete atracIDs[i]; - atracIDs[i] = NULL; + delete atracContexts[i]; + atracContexts[i] = nullptr; } } - DoArray(p, atracIDTypes, PSP_NUM_ATRAC_IDS); + DoArray(p, atracContextTypes, PSP_NUM_ATRAC_IDS); if (s < 2) { atracLibVersion = 0; atracLibCrc = 0; @@ -562,18 +571,11 @@ void __AtracDoState(PointerWrap &p) { } } -void __AtracShutdown() { - for (size_t i = 0; i < ARRAY_SIZE(atracIDs); ++i) { - delete atracIDs[i]; - atracIDs[i] = NULL; - } -} - static Atrac *getAtrac(int atracID) { if (atracID < 0 || atracID >= PSP_NUM_ATRAC_IDS) { - return NULL; + return nullptr; } - Atrac *atrac = atracIDs[atracID]; + Atrac *atrac = atracContexts[atracID]; if (atrac && atrac->context_.IsValid()) { // Read in any changes from the game to the context. @@ -586,27 +588,24 @@ static Atrac *getAtrac(int atracID) { } static int createAtrac(Atrac *atrac) { - for (int i = 0; i < (int)ARRAY_SIZE(atracIDs); ++i) { - if (atracIDTypes[i] == atrac->codecType_ && atracIDs[i] == 0) { - atracIDs[i] = atrac; + for (int i = 0; i < (int)ARRAY_SIZE(atracContexts); ++i) { + if (atracContextTypes[i] == atrac->codecType_ && atracContexts[i] == 0) { + atracContexts[i] = atrac; atrac->atracID_ = i; return i; } } - return ATRAC_ERROR_NO_ATRACID; } static int deleteAtrac(int atracID) { if (atracID >= 0 && atracID < PSP_NUM_ATRAC_IDS) { - if (atracIDs[atracID] != nullptr) { - delete atracIDs[atracID]; - atracIDs[atracID] = nullptr; - + if (atracContexts[atracID] != nullptr) { + delete atracContexts[atracID]; + atracContexts[atracID] = nullptr; return 0; } } - return ATRAC_ERROR_BAD_ATRACID; } @@ -921,7 +920,7 @@ u32 _AtracAddStreamData(int atracID, u32 bufPtr, u32 bytesToAdd) { atrac->first_.fileoffset += addbytes; if (atrac->context_.IsValid()) { // refresh context_ - _AtracGenerateContext(atrac); + _AtracWriteContextToPSPMem(atrac); } return 0; } @@ -1042,7 +1041,7 @@ static u32 sceAtracAddStreamData(int atracID, u32 bytesToAdd) { if (atrac->bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) atrac->bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED; if (atrac->context_.IsValid()) { - _AtracGenerateContext(atrac); + _AtracWriteContextToPSPMem(atrac); } } @@ -1078,7 +1077,7 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3 *finish = 1; if (atrac->context_.IsValid()) { // refresh context_ - _AtracGenerateContext(atrac); + _AtracWriteContextToPSPMem(atrac); } return ATRAC_ERROR_ALL_DATA_DECODED; } @@ -1198,7 +1197,7 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3 *remains = atrac->RemainingFrames(); if (atrac->context_.IsValid()) { // refresh context_ - _AtracGenerateContext(atrac); + _AtracWriteContextToPSPMem(atrac); } return 0; } @@ -1212,12 +1211,12 @@ static u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 int ret = _AtracDecodeData(atracID, Memory::GetPointerWrite(outAddr), outAddr, &numSamples, &finish, &remains); if (ret != (int)ATRAC_ERROR_BAD_ATRACID && ret != (int)ATRAC_ERROR_NO_DATA) { if (Memory::IsValidAddress(numSamplesAddr)) - Memory::Write_U32(numSamples, numSamplesAddr); + Memory::WriteUnchecked_U32(numSamples, numSamplesAddr); if (Memory::IsValidAddress(finishFlagAddr)) - Memory::Write_U32(finish, finishFlagAddr); + Memory::WriteUnchecked_U32(finish, finishFlagAddr); // On error, no remaining frame value is written. if (ret == 0 && Memory::IsValidAddress(remainAddr)) - Memory::Write_U32(remains, remainAddr); + Memory::WriteUnchecked_U32(remains, remainAddr); } DEBUG_LOG(ME, "%08x=sceAtracDecodeData(%i, %08x, %08x[%08x], %08x[%08x], %08x[%d])", ret, atracID, outAddr, numSamplesAddr, numSamples, @@ -1636,7 +1635,7 @@ static u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFi } if (atrac->context_.IsValid()) { - _AtracGenerateContext(atrac); + _AtracWriteContextToPSPMem(atrac); } return hleDelayResult(hleLogSuccessInfoI(ME, 0), "reset play pos", 3000); } @@ -1793,7 +1792,7 @@ static u32 sceAtracSetData(int atracID, u32 buffer, u32 bufferSize) { return ret; } - if (atrac->codecType_ != atracIDTypes[atracID]) { + if (atrac->codecType_ != atracContextTypes[atracID]) { // TODO: Should this not change the buffer size? return hleReportError(ME, ATRAC_ERROR_WRONG_CODECTYPE, "atracID uses different codec type than data"); } @@ -1871,20 +1870,20 @@ static u32 sceAtracSetLoopNum(int atracID, int loopNum) { atrac->loopEndSample_ = atrac->endSample_ + atrac->firstSampleOffset_ + atrac->FirstOffsetExtra(); } if (atrac->context_.IsValid()) { - _AtracGenerateContext(atrac); + _AtracWriteContextToPSPMem(atrac); } return hleLogSuccessI(ME, 0); } static int sceAtracReinit(int at3Count, int at3plusCount) { for (int i = 0; i < PSP_NUM_ATRAC_IDS; ++i) { - if (atracIDs[i] != NULL) { + if (atracContexts[i] != nullptr) { ERROR_LOG_REPORT(ME, "sceAtracReinit(%d, %d): cannot reinit while IDs in use", at3Count, at3plusCount); return SCE_KERNEL_ERROR_BUSY; } } - memset(atracIDTypes, 0, sizeof(atracIDTypes)); + memset(atracContextTypes, 0, sizeof(atracContextTypes)); int next = 0; int space = PSP_NUM_ATRAC_IDS; @@ -1900,13 +1899,13 @@ static int sceAtracReinit(int at3Count, int at3plusCount) { for (int i = 0; i < at3plusCount; ++i) { space -= 2; if (space >= 0) { - atracIDTypes[next++] = PSP_MODE_AT_3_PLUS; + atracContextTypes[next++] = PSP_MODE_AT_3_PLUS; } } for (int i = 0; i < at3Count; ++i) { space -= 1; if (space >= 0) { - atracIDTypes[next++] = PSP_MODE_AT_3; + atracContextTypes[next++] = PSP_MODE_AT_3; } } @@ -1929,7 +1928,7 @@ static int sceAtracGetOutputChannel(int atracID, u32 outputChanPtr) { return err; } if (Memory::IsValidAddress(outputChanPtr)) { - Memory::Write_U32(atrac->outputChannels_, outputChanPtr); + Memory::WriteUnchecked_U32(atrac->outputChannels_, outputChanPtr); return hleLogSuccessI(ME, 0); } else { return hleLogError(ME, 0, "invalid address"); @@ -2065,13 +2064,15 @@ static int sceAtracSetAA3DataAndGetID(u32 buffer, u32 bufferSize, u32 fileSize, return _AtracSetData(atracID, buffer, bufferSize, bufferSize, true); } +// Used by SasAudio's AT3 integration. int _AtracGetIDByContext(u32 contextAddr) { int atracID = (int)Memory::Read_U32(contextAddr + 0xfc); return atracID; } -void _AtracGenerateContext(Atrac *atrac) { - SceAtracId *context = atrac->context_; +void _AtracWriteContextToPSPMem(Atrac *atrac) { + // context points into PSP memory. + SceAtracContext *context = atrac->context_; context->info.buffer = atrac->first_.addr; context->info.bufferByte = atrac->bufferMaxSize_; context->info.secondBuffer = atrac->second_.addr; @@ -2102,7 +2103,7 @@ void _AtracGenerateContext(Atrac *atrac) { u8 *buf = (u8 *)context; *(u32_le *)(buf + 0xfc) = atrac->atracID_; - NotifyMemInfo(MemBlockFlags::WRITE, atrac->context_.ptr, sizeof(SceAtracId), "AtracContext"); + NotifyMemInfo(MemBlockFlags::WRITE, atrac->context_.ptr, sizeof(SceAtracContext), "AtracContext"); } static u32 _sceAtracGetContextAddress(int atracID) { @@ -2113,17 +2114,17 @@ static u32 _sceAtracGetContextAddress(int atracID) { } if (!atrac->context_.IsValid()) { // allocate a new context_ - u32 contextsize = 256; - atrac->context_ = kernelMemory.Alloc(contextsize, false, StringFromFormat("AtracCtx/%d", atracID).c_str()); + u32 contextSize = sizeof(SceAtracContext); + // Note that Alloc can increase contextSize to the "grain" size. + atrac->context_ = kernelMemory.Alloc(contextSize, false, StringFromFormat("AtracCtx/%d", atracID).c_str()); if (atrac->context_.IsValid()) - Memory::Memset(atrac->context_.ptr, 0, 256, "AtracContextClear"); - + Memory::Memset(atrac->context_.ptr, 0, contextSize, "AtracContextClear"); WARN_LOG(ME, "%08x=_sceAtracGetContextAddress(%i): allocated new context", atrac->context_.ptr, atracID); } else WARN_LOG(ME, "%08x=_sceAtracGetContextAddress(%i)", atrac->context_.ptr, atracID); if (atrac->context_.IsValid()) - _AtracGenerateContext(atrac); + _AtracWriteContextToPSPMem(atrac); return atrac->context_.ptr; } @@ -2147,27 +2148,6 @@ static const At3HeaderMap at3HeaderMap[] = { { 0x00C0, 2, 1 }, // 66 kbps stereo }; -static bool initAT3Decoder(Atrac *atrac) { - atrac->bitrate_ = (atrac->bytesPerFrame_ * 352800) / 1000; - atrac->bitrate_ = (atrac->bitrate_ + 511) >> 10; - atrac->jointStereo_ = false; - - // See if we can match the actual jointStereo value. - for (size_t i = 0; i < ARRAY_SIZE(at3HeaderMap); ++i) { - if (at3HeaderMap[i].Matches(atrac)) { - atrac->jointStereo_ = at3HeaderMap[i].jointStereo; - return true; - } - } - return false; -} - -static void initAT3plusDecoder(Atrac *atrac) { - atrac->bitrate_ = (atrac->bytesPerFrame_ * 352800) / 1000; - atrac->bitrate_ = ((atrac->bitrate_ >> 11) + 8) & 0xFFFFFFF0; - atrac->jointStereo_ = false; -} - static int sceAtracLowLevelInitDecoder(int atracID, u32 paramsAddr) { Atrac *atrac = getAtrac(atracID); if (!atrac) { @@ -2195,12 +2175,26 @@ static int sceAtracLowLevelInitDecoder(int atracID, u32 paramsAddr) { const char *channelName = atrac->channels_ == 1 ? "mono" : "stereo"; if (atrac->codecType_ == PSP_MODE_AT_3) { - if (!initAT3Decoder(atrac)) { + atrac->bitrate_ = (atrac->bytesPerFrame_ * 352800) / 1000; + atrac->bitrate_ = (atrac->bitrate_ + 511) >> 10; + atrac->jointStereo_ = false; + + // See if we can match the actual jointStereo value. + bool found = false; + for (size_t i = 0; i < ARRAY_SIZE(at3HeaderMap); ++i) { + if (at3HeaderMap[i].Matches(atrac)) { + atrac->jointStereo_ = at3HeaderMap[i].jointStereo; + found = true; + } + } + if (!found) { ERROR_LOG_REPORT(ME, "AT3 header map lacks entry for bpf: %i channels: %i", atrac->bytesPerFrame_, atrac->channels_); // TODO: Should we return an error code for these values? } } else if (atrac->codecType_ == PSP_MODE_AT_3_PLUS) { - initAT3plusDecoder(atrac); + atrac->bitrate_ = (atrac->bytesPerFrame_ * 352800) / 1000; + atrac->bitrate_ = ((atrac->bitrate_ >> 11) + 8) & 0xFFFFFFF0; + atrac->jointStereo_ = false; } atrac->dataOff_ = 0; @@ -2211,7 +2205,7 @@ static int sceAtracLowLevelInitDecoder(int atracID, u32 paramsAddr) { int ret = __AtracSetContext(atrac); if (atrac->context_.IsValid()) { - _AtracGenerateContext(atrac); + _AtracWriteContextToPSPMem(atrac); } if (ret < 0) { diff --git a/Core/HLE/sceAtrac.h b/Core/HLE/sceAtrac.h index cc2c14e943..344f8c26f4 100644 --- a/Core/HLE/sceAtrac.h +++ b/Core/HLE/sceAtrac.h @@ -77,7 +77,7 @@ struct SceAtracIdInfo { u8 unk[56]; }; -struct SceAtracId { +struct SceAtracContext { // size 128 SceAudiocodecCodec codec; // size 128 diff --git a/Core/HW/Atrac3Standalone.cpp b/Core/HW/Atrac3Standalone.cpp index 2b2ec92f3c..b109effd44 100644 --- a/Core/HW/Atrac3Standalone.cpp +++ b/Core/HW/Atrac3Standalone.cpp @@ -46,7 +46,7 @@ public: return codecOpen_; } - void FlushBuffers() { + void FlushBuffers() override { if (at3Ctx_) { atrac3_flush_buffers(at3Ctx_); } diff --git a/Core/HW/SimpleAudioDec.cpp b/Core/HW/SimpleAudioDec.cpp index a6555b8095..abf7722d1d 100644 --- a/Core/HW/SimpleAudioDec.cpp +++ b/Core/HW/SimpleAudioDec.cpp @@ -55,7 +55,9 @@ extern "C" { // AAC decoder candidates: // * https://github.com/mstorsjo/fdk-aac/tree/master - +// h.264 decoder candidates: +// * https://github.com/meerkat-cv/h264_decoder +// * https://github.com/shengbinmeng/ffmpeg-h264-dec // minimp3-based decoder. class MiniMp3Audio : public AudioDecoder { @@ -93,10 +95,11 @@ private: }; // FFMPEG-based decoder. TODO: Replace with individual codecs. -class SimpleAudio : public AudioDecoder { +// Based on http://ffmpeg.org/doxygen/trunk/doc_2examples_2decoding_encoding_8c-example.html#_a13 +class FFmpegAudioDecoder : public AudioDecoder { public: - SimpleAudio(PSPAudioType audioType, int sampleRateHz = 44100, int channels = 2); - ~SimpleAudio(); + FFmpegAudioDecoder(PSPAudioType audioType, int sampleRateHz = 44100, int channels = 2); + ~FFmpegAudioDecoder(); bool Decode(const uint8_t* inbuf, int inbytes, int *inbytesConsumed, uint8_t *outbuf, int *outbytes) override; bool IsOK() const override { @@ -142,7 +145,7 @@ AudioDecoder *CreateAudioDecoder(PSPAudioType audioType, int sampleRateHz, int c return CreateAtrac3PlusAudio(channels, blockAlign); default: // Only AAC falls back to FFMPEG now. - return new SimpleAudio(audioType, sampleRateHz, channels); + return new FFmpegAudioDecoder(audioType, sampleRateHz, channels); } } @@ -165,7 +168,7 @@ static int GetAudioCodecID(int audioType) { #endif // USE_FFMPEG } -SimpleAudio::SimpleAudio(PSPAudioType audioType, int sampleRateHz, int channels) +FFmpegAudioDecoder::FFmpegAudioDecoder(PSPAudioType audioType, int sampleRateHz, int channels) : audioType(audioType), sample_rate_(sampleRateHz), channels_(channels) { #ifdef USE_FFMPEG @@ -205,7 +208,7 @@ SimpleAudio::SimpleAudio(PSPAudioType audioType, int sampleRateHz, int channels) #endif // USE_FFMPEG } -bool SimpleAudio::OpenCodec(int block_align) { +bool FFmpegAudioDecoder::OpenCodec(int block_align) { #ifdef USE_FFMPEG // Some versions of FFmpeg require this set. May be set in SetExtraData(), but optional. // When decoding, we decode by packet, so we know the size. @@ -226,7 +229,7 @@ bool SimpleAudio::OpenCodec(int block_align) { #endif // USE_FFMPEG } -void SimpleAudio::SetChannels(int channels) { +void FFmpegAudioDecoder::SetChannels(int channels) { if (channels_ == channels) { // Do nothing, already set. return; @@ -243,7 +246,7 @@ void SimpleAudio::SetChannels(int channels) { #endif } -SimpleAudio::~SimpleAudio() { +FFmpegAudioDecoder::~FFmpegAudioDecoder() { #ifdef USE_FFMPEG swr_free(&swrCtx_); av_frame_free(&frame_); @@ -261,7 +264,7 @@ SimpleAudio::~SimpleAudio() { } // Decodes a single input frame. -bool SimpleAudio::Decode(const uint8_t *inbuf, int inbytes, int *inbytesConsumed, uint8_t *outbuf, int *outbytes) { +bool FFmpegAudioDecoder::Decode(const uint8_t *inbuf, int inbytes, int *inbytesConsumed, uint8_t *outbuf, int *outbytes) { #ifdef USE_FFMPEG if (!codecOpen_) { OpenCodec(inbytes); @@ -368,7 +371,7 @@ void AudioClose(AudioDecoder **ctx) { #endif // USE_FFMPEG } -void AudioClose(SimpleAudio **ctx) { +void AudioClose(FFmpegAudioDecoder **ctx) { #ifdef USE_FFMPEG delete *ctx; *ctx = 0; @@ -408,10 +411,6 @@ AuCtx::~AuCtx() { } size_t AuCtx::FindNextMp3Sync() { - if (decoder->GetAudioType() != PSP_CODEC_MP3) { - return 0; - } - for (size_t i = 0; i < sourcebuff.size() - 2; ++i) { if ((sourcebuff[i] & 0xFF) == 0xFF && (sourcebuff[i + 1] & 0xC0) == 0xC0) { return i; @@ -432,7 +431,10 @@ u32 AuCtx::AuDecode(u32 pcmAddr) { // Decode a single frame in sourcebuff and output into PCMBuf. if (!sourcebuff.empty()) { // FFmpeg doesn't seem to search for a sync for us, so let's do that. - int nextSync = (int)FindNextMp3Sync(); + int nextSync = 0; + if (decoder->GetAudioType() == PSP_CODEC_MP3) { + nextSync = (int)FindNextMp3Sync(); + } int inbytesConsumed = 0; decoder->Decode(&sourcebuff[nextSync], (int)sourcebuff.size() - nextSync, &inbytesConsumed, outbuf, &outpcmbufsize); @@ -546,18 +548,18 @@ u32 AuCtx::AuGetInfoToAddStreamData(u32 bufPtr, u32 sizePtr, u32 srcPosPtr) { // we can recharge AuBuf from its beginning if (readsize != 0) { if (Memory::IsValidAddress(bufPtr)) - Memory::Write_U32(AuBuf + offset, bufPtr); + Memory::WriteUnchecked_U32(AuBuf + offset, bufPtr); if (Memory::IsValidAddress(sizePtr)) - Memory::Write_U32(readsize, sizePtr); + Memory::WriteUnchecked_U32(readsize, sizePtr); if (Memory::IsValidAddress(srcPosPtr)) - Memory::Write_U32(readPos, srcPosPtr); + Memory::WriteUnchecked_U32(readPos, srcPosPtr); } else { if (Memory::IsValidAddress(bufPtr)) - Memory::Write_U32(0, bufPtr); + Memory::WriteUnchecked_U32(0, bufPtr); if (Memory::IsValidAddress(sizePtr)) - Memory::Write_U32(0, sizePtr); + Memory::WriteUnchecked_U32(0, sizePtr); if (Memory::IsValidAddress(srcPosPtr)) - Memory::Write_U32(0, srcPosPtr); + Memory::WriteUnchecked_U32(0, srcPosPtr); } // Just for old save states. diff --git a/Core/HW/SimpleAudioDec.h b/Core/HW/SimpleAudioDec.h index d000850133..904b8f927f 100644 --- a/Core/HW/SimpleAudioDec.h +++ b/Core/HW/SimpleAudioDec.h @@ -20,11 +20,8 @@ #include "Core/HW/MediaEngine.h" #include "Core/HLE/sceAudio.h" -// Wraps FFMPEG for audio decoding in a nice interface. // Decodes packet by packet - does NOT demux. -// Based on http://ffmpeg.org/doxygen/trunk/doc_2examples_2decoding_encoding_8c-example.html#_a13 - // audioType enum PSPAudioType { PSP_CODEC_AT3PLUS = 0x00001000, diff --git a/ext/at3_standalone/at3_decoders.h b/ext/at3_standalone/at3_decoders.h index 97c9145e68..019ebe306d 100644 --- a/ext/at3_standalone/at3_decoders.h +++ b/ext/at3_standalone/at3_decoders.h @@ -2,6 +2,11 @@ #include +// Notes +// +// Performance-wise, these are OK. +// For Atrac3+, the bottleneck is two functions: decode_qu_spectra and ff_atrac3p_ipqf. At least the latter is quite SIMD-able. + // The full external API for the standalone Atrac3/3+ decoder. struct ATRAC3Context; diff --git a/ext/at3_standalone/atrac3.cpp b/ext/at3_standalone/atrac3.cpp index 7b2ca124eb..fe0e71c841 100644 --- a/ext/at3_standalone/atrac3.cpp +++ b/ext/at3_standalone/atrac3.cpp @@ -140,7 +140,7 @@ static void imlt(ATRAC3Context *q, float *input, float *output, int odd_band) FFSWAP(float, input[i], input[255 - i]); } - q->mdct_ctx.imdct_calc(&q->mdct_ctx, output, input); + imdct_calc(&q->mdct_ctx, output, input); /* Perform windowing on the output. */ vector_fmul(output, output, mdct_window, MDCT_SIZE); diff --git a/ext/at3_standalone/atrac3plusdsp.cpp b/ext/at3_standalone/atrac3plusdsp.cpp index fcdb9caecb..004515819c 100644 --- a/ext/at3_standalone/atrac3plusdsp.cpp +++ b/ext/at3_standalone/atrac3plusdsp.cpp @@ -25,6 +25,22 @@ * DSP functions for ATRAC3+ decoder. */ +#include "ppsspp_config.h" + +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) + +#include + +#elif PPSSPP_ARCH(ARM_NEON) + +#if defined(_MSC_VER) && PPSSPP_ARCH(ARM64) +#include +#else +#include +#endif + +#endif + #include #include @@ -466,7 +482,7 @@ void ff_atrac3p_imdct(FFTContext *mdct_ctx, float *pIn, for (i = 0; i < ATRAC3P_SUBBAND_SAMPLES / 2; i++) FFSWAP(float, pIn[i], pIn[ATRAC3P_SUBBAND_SAMPLES - 1 - i]); - mdct_ctx->imdct_calc(mdct_ctx, pOut, pIn); + imdct_calc(mdct_ctx, pOut, pIn); /* Perform windowing on the output. * ATRAC3+ uses two different MDCT windows: @@ -612,24 +628,72 @@ void ff_atrac3p_ipqf(FFTContext *dct_ctx, Atrac3pIPQFChannelCtx *hist, idct_in[sb] = in[sb * ATRAC3P_SUBBAND_SAMPLES + s]; /* Calculate the sine and cosine part of the PQF using IDCT-IV */ - dct_ctx->imdct_half(dct_ctx, idct_out, idct_in); + imdct_half(dct_ctx, idct_out, idct_in); /* append the result to the history */ + const int hist_pos = hist->pos; for (i = 0; i < 8; i++) { - hist->buf1[hist->pos][i] = idct_out[i + 8]; - hist->buf2[hist->pos][i] = idct_out[7 - i]; + hist->buf1[hist_pos][i] = idct_out[i + 8]; + } + for (i = 0; i < 8; i++) { + hist->buf2[hist_pos][i] = idct_out[7 - i]; } pos_now = hist->pos; pos_next = mod23_lut[pos_now + 2]; // pos_next = (pos_now + 1) % 23; for (t = 0; t < ATRAC3P_PQF_FIR_LEN; t++) { + const float *buf1 = hist->buf1[pos_now]; + const float *buf2 = hist->buf2[pos_next]; + const float *coeffs1 = ipqf_coeffs1[t]; + const float *coeffs2 = ipqf_coeffs2[t]; + + float *outp = out + s * 16; +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) + auto _mm_reverse = [](__m128 x) -> __m128 { + return _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)); + }; + _mm_storeu_ps(outp, _mm_add_ps(_mm_loadu_ps(outp), _mm_add_ps( + _mm_mul_ps(_mm_loadu_ps(buf1), _mm_loadu_ps(coeffs1)), + _mm_mul_ps(_mm_loadu_ps(buf2), _mm_loadu_ps(coeffs2))))); + _mm_storeu_ps(outp + 4, _mm_add_ps(_mm_loadu_ps(outp + 4), _mm_add_ps( + _mm_mul_ps(_mm_loadu_ps(buf1 + 4), _mm_loadu_ps(coeffs1 + 4)), + _mm_mul_ps(_mm_loadu_ps(buf2 + 4), _mm_loadu_ps(coeffs2 + 4))))); + + _mm_storeu_ps(outp + 8, _mm_add_ps(_mm_loadu_ps(outp + 8), _mm_add_ps( + _mm_mul_ps(_mm_reverse(_mm_loadu_ps(buf1 + 4)), _mm_loadu_ps(coeffs1 + 8)), + _mm_mul_ps(_mm_reverse(_mm_loadu_ps(buf2 + 4)), _mm_loadu_ps(coeffs2 + 8))))); + _mm_storeu_ps(outp + 12, _mm_add_ps(_mm_loadu_ps(outp + 12), _mm_add_ps( + _mm_mul_ps(_mm_reverse(_mm_loadu_ps(buf1)), _mm_loadu_ps(coeffs1 + 12)), + _mm_mul_ps(_mm_reverse(_mm_loadu_ps(buf2)), _mm_loadu_ps(coeffs2 + 12))))); +#elif PPSSPP_ARCH(ARM_NEON) + auto vreverseq_f32 = [](float32x4_t x) -> float32x4_t { + float32x4_t rev = vrev64q_f32(x); + float32x2_t high = vget_high_f32(rev); //{4,3} + float32x2_t low = vget_low_f32(rev); //{1,2} + return vcombine_f32(high, low); //{4,3,2,1} + }; + vst1q_f32(outp, vaddq_f32(vld1q_f32(outp), vaddq_f32( + vmulq_f32(vld1q_f32(buf1), vld1q_f32(coeffs1)), + vmulq_f32(vld1q_f32(buf2), vld1q_f32(coeffs2))))); + vst1q_f32(outp + 4, vaddq_f32(vld1q_f32(outp + 4), vaddq_f32( + vmulq_f32(vld1q_f32(buf1 + 4), vld1q_f32(coeffs1 + 4)), + vmulq_f32(vld1q_f32(buf2 + 4), vld1q_f32(coeffs2 + 4))))); + + vst1q_f32(outp + 8, vaddq_f32(vld1q_f32(outp + 8), vaddq_f32( + vmulq_f32(vreverseq_f32(vld1q_f32(buf1 + 4)), vld1q_f32(coeffs1 + 8)), + vmulq_f32(vreverseq_f32(vld1q_f32(buf2 + 4)), vld1q_f32(coeffs2 + 8))))); + vst1q_f32(outp + 12, vaddq_f32(vld1q_f32(outp + 12), vaddq_f32( + vmulq_f32(vreverseq_f32(vld1q_f32(buf1)), vld1q_f32(coeffs1 + 12)), + vmulq_f32(vreverseq_f32(vld1q_f32(buf2)), vld1q_f32(coeffs2 + 12))))); +#else for (i = 0; i < 8; i++) { - out[s * 16 + i + 0] += hist->buf1[pos_now][i] * ipqf_coeffs1[t][i] + - hist->buf2[pos_next][i] * ipqf_coeffs2[t][i]; - out[s * 16 + i + 8] += hist->buf1[pos_now][7 - i] * ipqf_coeffs1[t][i + 8] + - hist->buf2[pos_next][7 - i] * ipqf_coeffs2[t][i + 8]; + outp[i] += buf1[i] * coeffs1[i] + buf2[i] * coeffs2[i]; } + for (i = 0; i < 8; i++) { + outp[i + 8] += buf1[7 - i] * coeffs1[i + 8] + buf2[7 - i] * coeffs2[i + 8]; + } +#endif pos_now = mod23_lut[pos_next + 2]; // pos_now = (pos_now + 2) % 23; pos_next = mod23_lut[pos_now + 2]; // pos_next = (pos_next + 2) % 23; diff --git a/ext/at3_standalone/compat.h b/ext/at3_standalone/compat.h index 6c4a4ac45b..bd3e398b32 100644 --- a/ext/at3_standalone/compat.h +++ b/ext/at3_standalone/compat.h @@ -18,6 +18,9 @@ #define AV_HAVE_FAST_UNALIGNED 0 #define AV_INPUT_BUFFER_PADDING_SIZE 32 +// TODO: This should work but doesn't?? +// #define BITSTREAM_READER_LE + #define LOCAL_ALIGNED(bits, type, name, subscript) type name subscript #define av_restrict #define av_alias diff --git a/ext/at3_standalone/fft.cpp b/ext/at3_standalone/fft.cpp index 961c43b55d..184275f870 100644 --- a/ext/at3_standalone/fft.cpp +++ b/ext/at3_standalone/fft.cpp @@ -35,9 +35,9 @@ #define sqrthalf (float)M_SQRT1_2 -void ff_imdct_calc_c(FFTContext *s, FFTSample *output, const FFTSample *input); -void ff_imdct_half_c(FFTContext *s, FFTSample *output, const FFTSample *input); -void ff_mdct_calc_c(FFTContext *s, FFTSample *output, const FFTSample *input); +void imdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input); +void imdct_half(FFTContext *s, FFTSample *output, const FFTSample *input); +void mdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input); /* cos(2*pi*x/n) for 0<=x<=n/4, followed by its reverse */ COSTABLE(16); @@ -71,8 +71,8 @@ static FFTSample * const av_cos_tabs[] = { av_cos_65536, }; -static void fft_permute_c(FFTContext *s, FFTComplex *z); -static void fft_calc_c(FFTContext *s, FFTComplex *z); +void fft_permute(FFTContext *s, FFTComplex *z); +void fft_calc(FFTContext *s, FFTComplex *z); static int split_radix_permutation(int i, int n, int inverse) { @@ -130,11 +130,6 @@ int ff_fft_init(FFTContext *s, int nbits, int inverse) goto fail; s->inverse = inverse; - s->fft_permute = fft_permute_c; - s->fft_calc = fft_calc_c; - s->imdct_calc = ff_imdct_calc_c; - s->imdct_half = ff_imdct_half_c; - s->mdct_calc = ff_mdct_calc_c; for(j=4; j<=nbits; j++) { ff_init_ff_cos_tabs(j); } @@ -152,7 +147,7 @@ int ff_fft_init(FFTContext *s, int nbits, int inverse) return -1; } -static void fft_permute_c(FFTContext *s, FFTComplex *z) +void fft_permute(FFTContext *s, FFTComplex *z) { int j, np; const uint16_t *revtab = s->revtab; @@ -315,12 +310,11 @@ static void (* const fft_dispatch[])(FFTComplex*) = { fft2048, fft4096, fft8192, fft16384, fft32768, fft65536, }; -static void fft_calc_c(FFTContext *s, FFTComplex *z) +void fft_calc(FFTContext *s, FFTComplex *z) { fft_dispatch[s->nbits-2](z); } - #include #include @@ -383,7 +377,7 @@ fail: * @param output N/2 samples * @param input N/2 samples */ -void ff_imdct_half_c(FFTContext *s, FFTSample *output, const FFTSample *input) +void imdct_half(FFTContext *s, FFTSample *output, const FFTSample *input) { int k, n8, n4, n2, n, j; const uint16_t *revtab = s->revtab; @@ -406,7 +400,7 @@ void ff_imdct_half_c(FFTContext *s, FFTSample *output, const FFTSample *input) in1 += 2; in2 -= 2; } - s->fft_calc(s, z); + fft_calc(s, z); /* post rotation + reordering */ for (k = 0; k < n8; k++) { @@ -425,14 +419,14 @@ void ff_imdct_half_c(FFTContext *s, FFTSample *output, const FFTSample *input) * @param output N samples * @param input N/2 samples */ -void ff_imdct_calc_c(FFTContext *s, FFTSample *output, const FFTSample *input) +void imdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input) { int k; int n = 1 << s->mdct_bits; int n2 = n >> 1; int n4 = n >> 2; - ff_imdct_half_c(s, output + n4, input); + imdct_half(s, output + n4, input); for (k = 0; k < n4; k++) { output[k] = -output[n2 - k - 1]; @@ -445,7 +439,7 @@ void ff_imdct_calc_c(FFTContext *s, FFTSample *output, const FFTSample *input) * @param input N samples * @param out N/2 samples */ -void ff_mdct_calc_c(FFTContext *s, FFTSample *out, const FFTSample *input) +void mdct_calc(FFTContext *s, FFTSample *out, const FFTSample *input) { int i, j, n, n8, n4, n2, n3; FFTDouble re, im; @@ -473,7 +467,7 @@ void ff_mdct_calc_c(FFTContext *s, FFTSample *out, const FFTSample *input) CMUL(x[j].re, x[j].im, re, im, -tcos[n8 + i], tsin[n8 + i]); } - s->fft_calc(s, x); + fft_calc(s, x); /* post rotation */ for (i = 0; i < n8; i++) { diff --git a/ext/at3_standalone/fft.h b/ext/at3_standalone/fft.h index 3c9e413bb1..d4dfd940ec 100644 --- a/ext/at3_standalone/fft.h +++ b/ext/at3_standalone/fft.h @@ -57,21 +57,23 @@ struct FFTContext { /* pre/post rotation tables */ FFTSample *tcos; FFTSample *tsin; - /** - * Do the permutation needed BEFORE calling fft_calc(). - */ - void (*fft_permute)(struct FFTContext *s, FFTComplex *z); - /** - * Do a complex FFT with the parameters defined in ff_fft_init(). The - * input data must be permuted before. No 1.0/sqrt(n) normalization is done. - */ - void (*fft_calc)(struct FFTContext *s, FFTComplex *z); - void (*imdct_calc)(struct FFTContext *s, FFTSample *output, const FFTSample *input); - void (*imdct_half)(struct FFTContext *s, FFTSample *output, const FFTSample *input); - void (*mdct_calc)(struct FFTContext *s, FFTSample *output, const FFTSample *input); + enum mdct_permutation_type mdct_permutation; }; +/** + * Do the permutation needed BEFORE calling fft_calc(). + */ +void fft_permute(struct FFTContext *s, FFTComplex *z); +/** + * Do a complex FFT with the parameters defined in ff_fft_init(). The + * input data must be permuted before. No 1.0/sqrt(n) normalization is done. + */ +void fft_calc(struct FFTContext *s, FFTComplex *z); +void imdct_calc(struct FFTContext *s, FFTSample *output, const FFTSample *input); +void imdct_half(struct FFTContext *s, FFTSample *output, const FFTSample *input); +void mdct_calc(struct FFTContext *s, FFTSample *output, const FFTSample *input); + #define COSTABLE(size) \ DECLARE_ALIGNED(32, FFTSample, av_cos_##size)[size/2]