diff --git a/src/DSP_HLE/AACUcode.cpp b/src/DSP_HLE/AACUcode.cpp index 09ffb5d6..1f27c56d 100644 --- a/src/DSP_HLE/AACUcode.cpp +++ b/src/DSP_HLE/AACUcode.cpp @@ -16,9 +16,6 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ -// TODO move faad stuff to Platform -#include - #include "../DSi.h" #include "AACUcode.h" #include "../Platform.h" @@ -32,25 +29,13 @@ using Platform::LogLevel; namespace DSP_HLE { -NeAACDecHandle AACDec; - - AACUcode::AACUcode(melonDS::DSi& dsi, int version) : UcodeBase(dsi) { DSi.RegisterEventFuncs(Event_DSi_DSPHLE, this, {MakeEventThunk(AACUcode, FinishCmd)}); - AACDec = NeAACDecOpen(); - - NeAACDecConfiguration* cfg = NeAACDecGetCurrentConfiguration(AACDec); - cfg->defObjectType = LC; - cfg->defSampleRate = 48000; - cfg->outputFormat = FAAD_FMT_16BIT; - NeAACDecSetConfiguration(AACDec, cfg); - - /*unsigned long freq = 48000; - unsigned char chan = 2; - int res = NeAACDecInit(AACDec, nullptr, 234, &freq, &chan); - printf("init = %d\n", res);*/ + Decoder = Platform::AAC_Init(); + if (!Decoder) + Log(LogLevel::Error, "DSP_HLE: failed to initialize AAC decoder\n"); if (version == -1) Log(LogLevel::Info, "DSP_HLE: initializing AAC decoder ucode (DSi sound app)\n"); @@ -60,7 +45,8 @@ AACUcode::AACUcode(melonDS::DSi& dsi, int version) : UcodeBase(dsi) AACUcode::~AACUcode() { - NeAACDecClose(AACDec); + if (Decoder) + Platform::AAC_DeInit(Decoder); DSi.UnregisterEventFuncs(Event_DSi_DSPHLE); } @@ -74,9 +60,11 @@ void AACUcode::Reset() CmdParamCount = 0; memset(CmdParams, 0, sizeof(CmdParams)); - memset(FrameBuf, 0, sizeof(FrameBuf)); - memset(LeftOutput, 0, sizeof(LeftOutput)); - memset(RightOutput, 0, sizeof(RightOutput)); + memset(InputBuf, 0, sizeof(InputBuf)); + memset(OutputBuf, 0, sizeof(OutputBuf)); + + LastFrequency = -1; + LastChannels = -1; } void AACUcode::DoSavestate(Savestate *file) @@ -135,7 +123,7 @@ void AACUcode::RecvCmdWord() CmdWritten[1] = false; } -int pett = 0; + void AACUcode::CmdDecodeFrame() { u16 framelen = CmdParams[0]; @@ -164,23 +152,6 @@ void AACUcode::CmdDecodeFrame() if ((chan != 1) && (rightaddr == 0)) fail = true; - // check input frequency - // this isn't entirely accurate - // in the ucode, any frequency not within the list below causes an init failure - // but the return value from the init function is ignored - u32 freqlist[9] = {48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000}; - u8 freqnum = 0xF; - for (int i = 0; i < 9; i++) - { - if (freq == freqlist[i]) - { - freqnum = 3 + i; - break; - } - } - if (freqnum == 0xF) - fail = true; - if (fail) { // end the command with return code 1 (invalid parameters) @@ -188,77 +159,65 @@ void AACUcode::CmdDecodeFrame() return; } - printf("AAC: good, freq=%d\n", freqnum); - - // make an ADTS header - /* - * u32 adts[2]; - int freq = 4; - int ch = 2; - int framelen = fs & 0x1FFF; - int resv = 0x7FF; - databotte[0] = 0xFF; - databotte[1] = 0xF1; - databotte[2] = 0x40 | (freq << 2) | (ch >> 2); // freq - databotte[3] = (ch << 6) | (fs >> 11); - databotte[4] = (framelen >> 3); - databotte[5] = (framelen << 5) | (resv >> 6); - databotte[6] = (resv << 2);*/ - u32 totallen = framelen + 7; - u32 rsv = 0x7FF; - FrameBuf[0] = 0xFF; - FrameBuf[1] = 0xF1; - FrameBuf[2] = 0x40 | (freqnum << 2) | (chan >> 2); - FrameBuf[3] = (chan << 6) | (totallen >> 11); - FrameBuf[4] = (totallen >> 3); - FrameBuf[5] = (totallen << 5) | (rsv >> 6); - FrameBuf[6] = (rsv << 2); - -#define databotte FrameBuf - printf("%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - databotte[0],databotte[1],databotte[2],databotte[3], - databotte[4],databotte[5],databotte[6]); - - // read frame data - //ReadARM9Mem((u16*)&FrameBuf[7], frameaddr, framelen); + // TODO more efficient read mechanism? + // ReadARM9Mem() doesn't work well for this for (int i = 0; i < framelen; i++) { - FrameBuf[7+i] = DSi.ARM9Read8(frameaddr + i); + InputBuf[i] = DSi.ARM9Read8(frameaddr + i); } - // init - // TODO only do if config changed! - if (pett < 2) + // NOTE + // the DSi sound app will first send an all-zero frame, then send the actual first AAC frame + // this seems to just be a bug in the sound app + // the AAC ucode will fail to decode the zero frame, but without consequences + // however, third-party AAC decoders do not like zero frames + // so we need to detect this and bail out early + + if ((InputBuf[0] == 0) && (InputBuf[1] == 0) && (InputBuf[2] == 0) && (InputBuf[3] == 0)) { - if (pett == 1) - { - unsigned long _freq = 0; - unsigned char _chan = 0; - int res = NeAACDecInit(AACDec, FrameBuf, totallen, &_freq, &_chan); - printf("init = %d, %ld, %d\n", res, _freq, _chan); - } - pett++; + Log(LogLevel::Warn, "DSP_HLE: skipping zero AAC frame, addr=%08X len=%d\n", frameaddr, framelen); + + DSi.ScheduleEvent(Event_DSi_DSPHLE, false, 512, 0, 2); + return; } - // decode - NeAACDecFrameInfo finfo; - /*void* samplebuf[2] = {LeftOutput, RightOutput}; - NeAACDecDecode2(AACDec, &finfo, FrameBuf, totallen, samplebuf, 1024*4); - printf("decode res = %d %d %d\n", finfo.error, finfo.bytesconsumed, finfo.samples); + // initialize the decoder if needed - WriteARM9Mem((u16*)LeftOutput, leftaddr, 1024*2); - WriteARM9Mem((u16*)RightOutput, rightaddr, 1024*2); // checkme*/ - s16* dataout = (s16*)NeAACDecDecode(AACDec, &finfo, FrameBuf, totallen); - printf("decode res = %p %d %d/%d %d\n", dataout, finfo.error, (int)finfo.bytesconsumed, totallen, (int)finfo.samples); - if (dataout) + if ((freq != LastFrequency) || (chan != LastChannels)) { - for (int i = 0; i < 1024; i++) + if (!Platform::AAC_Configure(Decoder, freq, chan)) { - DSi.ARM9Write16(leftaddr, *dataout++); - DSi.ARM9Write16(rightaddr, *dataout++); - leftaddr += 2; - rightaddr += 2; + Log(LogLevel::Warn, "DSP_HLE: AAC decoder configuration failed, freq=%d chan=%d\n", freq, chan); + + LastFrequency = -1; + LastChannels = -1; + DSi.ScheduleEvent(Event_DSi_DSPHLE, false, 512, 0, 2); + return; } + + LastFrequency = freq; + LastChannels = chan; + } + + // decode the frame + + if (!Platform::AAC_DecodeFrame(Decoder, InputBuf, framelen, OutputBuf, 1024*2*sizeof(s16))) + { + Log(LogLevel::Warn, "DSP_HLE: AAC decoding failed, frame addr=%08X len=%d\n", frameaddr, framelen); + + LastFrequency = -1; + LastChannels = -1; + DSi.ScheduleEvent(Event_DSi_DSPHLE, false, 512, 0, 2); + return; + } + + s16* dataout = OutputBuf; + for (int i = 0; i < 1024; i++) + { + DSi.ARM9Write16(leftaddr, *dataout++); + DSi.ARM9Write16(rightaddr, *dataout++); + leftaddr += 2; + rightaddr += 2; } DSi.ScheduleEvent(Event_DSi_DSPHLE, false, 115000, 0, 0); diff --git a/src/DSP_HLE/AACUcode.h b/src/DSP_HLE/AACUcode.h index 04bd5a6c..d6bc3cf0 100644 --- a/src/DSP_HLE/AACUcode.h +++ b/src/DSP_HLE/AACUcode.h @@ -23,6 +23,7 @@ #include "UcodeBase.h" #include "../Savestate.h" +#include "../Platform.h" namespace melonDS::DSP_HLE { @@ -43,9 +44,11 @@ protected: u8 CmdParamCount; u16 CmdParams[10]; - u8 FrameBuf[1707]; - s16 LeftOutput[1024]; - s16 RightOutput[1024]; + Platform::AACDecoder* Decoder; + u8 InputBuf[1700]; + s16 OutputBuf[1024*2]; + int LastFrequency; + int LastChannels; void RecvCmdWord(); void CmdDecodeFrame(); diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp index fe4d283f..08f41727 100644 --- a/src/DSi_SD.cpp +++ b/src/DSi_SD.cpp @@ -837,7 +837,7 @@ void DSi_MMCStorage::Reset() memset(SCR, 0, 8); *(u32*)&SCR[0] = 0x012A0000; - memset(SD_SSR, 0, 64); + memset(SSR, 0, 64); BlockSize = 0; RWAddress = 0; @@ -855,7 +855,7 @@ void DSi_MMCStorage::DoSavestate(Savestate* file) file->Var32(&OCR); file->Var32(&RCA); file->VarArray(SCR, 8); - file->VarArray(SD_SSR, 64); + file->VarArray(SSR, 64); file->Var32(&BlockSize); file->Var64(&RWAddress); @@ -1014,7 +1014,7 @@ void DSi_MMCStorage::SendACMD(u8 cmd, u32 param) case 13: // get SSR Host->SendResponse(CSR, true); - Host->DataRX(SD_SSR, 64); + Host->DataRX(SSR, 64); return; case 41: // set operating conditions diff --git a/src/DSi_SD.h b/src/DSi_SD.h index 28e2103f..6b9b4fd8 100644 --- a/src/DSi_SD.h +++ b/src/DSi_SD.h @@ -200,7 +200,7 @@ private: u32 OCR; u32 RCA; u8 SCR[8]; - u8 SD_SSR[64]; + u8 SSR[64]; u32 BlockSize; u64 RWAddress; diff --git a/src/Platform.h b/src/Platform.h index 67d1a5b0..317c051b 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -320,6 +320,46 @@ void Camera_Start(int num, void* userdata); void Camera_Stop(int num, void* userdata); void Camera_CaptureFrame(int num, u32* frame, int width, int height, bool yuv, void* userdata); + +// interface for AAC decoding (ie. DSi DSP HLE) + +struct AACDecoder; + +/** + * Initializes an AAC decoder context. + * @return a pointer to an AAC decoder context, or NULL if initialization fails + */ +AACDecoder* AAC_Init(); + +/** + * Deinitializes an AAC decoder context. + * @param dec the context to be freed + */ +void AAC_DeInit(AACDecoder* dec); + +/** + * Configures the AAC decoder with new parameters (sampling frequency, channels). + * @param dec the context to be configured + * @param frequency the sampling frequency + * @param channels the channel setup value (1=mono, 2=stereo) + * @return true if configuration was successful, false if not + */ +bool AAC_Configure(AACDecoder* dec, int frequency, int channels); + +/** + * Decodes an AAC frame. + * Takes a raw AAC frame, without any ADIF or ADTS headers. + * Output is signed PCM6, interleaved. Output length is 1024 samples. + * @param dec the decoder context to use + * @param input the AAC frame to decode + * @param inputlen the length of the AAC frame in bytes + * @param output the buffer to write decoded output into + * @param outputlen the length of the output buffer in bytes + * @return true if decoding was successful, false if not + */ +bool AAC_DecodeFrame(AACDecoder* dec, const void* input, int inputlen, void* output, int outputlen); + + // interface for addon inputs enum KeyType diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 4a69b3fc..244a9d2c 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -34,6 +34,7 @@ set(SOURCES_QT_SDL OSD_shaders.h font.h Platform.cpp + Platform_AAC.cpp QPathInput.h SaveManager.cpp CameraManager.cpp diff --git a/src/frontend/qt_sdl/Platform_AAC.cpp b/src/frontend/qt_sdl/Platform_AAC.cpp new file mode 100644 index 00000000..3f447805 --- /dev/null +++ b/src/frontend/qt_sdl/Platform_AAC.cpp @@ -0,0 +1,98 @@ +/* + Copyright 2016-2025 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include + +#include "Platform.h" + + +namespace melonDS::Platform +{ + +AACDecoder* AAC_Init() +{ + NeAACDecHandle handle = NeAACDecOpen(); + if (!handle) + return nullptr; + + NeAACDecConfiguration* cfg = NeAACDecGetCurrentConfiguration(handle); + cfg->defObjectType = LC; + cfg->outputFormat = FAAD_FMT_16BIT; + if (!NeAACDecSetConfiguration(handle, cfg)) + return nullptr; + + return (AACDecoder*)handle; +} + +void AAC_DeInit(AACDecoder* dec) +{ + NeAACDecHandle handle = (NeAACDecHandle)dec; + NeAACDecClose(handle); +} + +bool AAC_Configure(AACDecoder* dec, int frequency, int channels) +{ + NeAACDecHandle handle = (NeAACDecHandle)dec; + + int freqlist[9] = {48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000}; + u8 freqnum = 3; // default to 48000 + for (int i = 0; i < 9; i++) + { + if (frequency == freqlist[i]) + { + freqnum = 3 + i; + break; + } + } + + channels &= 0xF; + + // produce a MP4 ASC to configure the decoder + + u8 asc[5]; + u32 asclen = sizeof(asc); + asc[0] = 0x10 | (freqnum >> 1); + asc[1] = (freqnum << 7) | (channels << 3); + asc[2] = 0x56; + asc[3] = 0xE5; + asc[4] = 0x00; + + unsigned long freq_out; + u8 chan_out; + if (NeAACDecInit2(handle, asc, asclen, &freq_out, &chan_out) != 0) + return false; + + return true; +} + +bool AAC_DecodeFrame(AACDecoder* dec, const void* input, int inputlen, void* output, int outputlen) +{ + NeAACDecHandle handle = (NeAACDecHandle)dec; + NeAACDecFrameInfo finfo; + + NeAACDecDecode2(handle, &finfo, (u8*)input, inputlen, &output, outputlen); + + if (finfo.error) + return false; + if (finfo.bytesconsumed != inputlen) + return false; + + return true; +} + +}