add Platform interface for AAC, and make the code nicer

This commit is contained in:
Arisotura 2025-07-31 01:16:55 +02:00
parent b709c89ba0
commit 4166d6848e
7 changed files with 208 additions and 107 deletions

View File

@ -16,9 +16,6 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
// TODO move faad stuff to Platform
#include <neaacdec.h>
#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);

View File

@ -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();

View File

@ -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

View File

@ -200,7 +200,7 @@ private:
u32 OCR;
u32 RCA;
u8 SCR[8];
u8 SD_SSR[64];
u8 SSR[64];
u32 BlockSize;
u64 RWAddress;

View File

@ -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

View File

@ -34,6 +34,7 @@ set(SOURCES_QT_SDL
OSD_shaders.h
font.h
Platform.cpp
Platform_AAC.cpp
QPathInput.h
SaveManager.cpp
CameraManager.cpp

View File

@ -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 <neaacdec.h>
#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;
}
}