Merge pull request #19054 from hrydgard/more-audio-dec-cleanup

More audio decoding cleanup, some optimization
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2024-04-13 11:25:39 +02:00
commit 4aeea7b84f
12 files changed
+213 -148

No files matched your search

+4
View File
@@ -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
+75 -81
View File
@@ -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<SceAtracId> context_;
PSPPointer<SceAtracContext> 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) {
+1 -1
View File
@@ -77,7 +77,7 @@ struct SceAtracIdInfo {
u8 unk[56];
};
struct SceAtracId {
struct SceAtracContext {
// size 128
SceAudiocodecCodec codec;
// size 128
+1 -1
View File
@@ -46,7 +46,7 @@ public:
return codecOpen_;
}
void FlushBuffers() {
void FlushBuffers() override {
if (at3Ctx_) {
atrac3_flush_buffers(at3Ctx_);
}
+24 -22
View File
@@ -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.
-3
View File
@@ -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,
+5
View File
@@ -2,6 +2,11 @@
#include <cstdint>
// 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;
+1 -1
View File
@@ -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);
+72 -8
View File
@@ -25,6 +25,22 @@
* DSP functions for ATRAC3+ decoder.
*/
#include "ppsspp_config.h"
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
#include <emmintrin.h>
#elif PPSSPP_ARCH(ARM_NEON)
#if defined(_MSC_VER) && PPSSPP_ARCH(ARM64)
#include <arm64_neon.h>
#else
#include <arm_neon.h>
#endif
#endif
#include <math.h>
#include <string.h>
@@ -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;
+3
View File
@@ -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
+13 -19
View File
@@ -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 <stdlib.h>
#include <string.h>
@@ -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++) {
+14 -12
View File
@@ -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]