From 8be23ca4fc5ad9005c86354a865be302cab3bc34 Mon Sep 17 00:00:00 2001 From: angel Date: Sat, 6 Dec 2025 23:06:32 -0600 Subject: [PATCH] feat: add M4A/AAC support and update application version to dev43 --- README.md | 1 + include/file.h | 4 +- include/m4a.h | 21 ++++++++ include/main.h | 2 +- source/file.c | 50 +++++++++++------ source/m4a.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++ source/playback.c | 20 ++++--- 7 files changed, 204 insertions(+), 27 deletions(-) create mode 100644 include/m4a.h create mode 100644 source/m4a.c diff --git a/README.md b/README.md index bb71d96..f531a5e 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ The latest 3DSX/CIA/3DS download can be found on the +#include +#include +#include +#include <3ds.h> + +#include "error.h" +#include "m4a.h" +#include "playback.h" + +/* TODO: Integrate proper AAC decoder library (libfaad2, fdk-aac, or minimp4) */ +/* For now, this is a stub implementation */ + +static size_t* buffSize; +static uint32_t rate = 44100; +static uint8_t channels = 2; + +static int initM4a(const char* file); +static uint32_t rateM4a(void); +static uint8_t channelM4a(void); +static uint64_t decodeM4a(void* buffer); +static void exitM4a(void); +static size_t getFileSamplesM4a(void); + +/** + * Set decoder parameters for M4A/AAC. + * + * \param decoder Structure to store parameters. + */ +void setM4a(struct decoder_fn* decoder) +{ + decoder->init = &initM4a; + decoder->rate = &rateM4a; + decoder->channels = &channelM4a; + buffSize = &(decoder->buffSize); + decoder->decode = &decodeM4a; + decoder->exit = &exitM4a; + decoder->getFileSamples = &getFileSamplesM4a; +} + +/** + * Check if a file is an M4A/AAC/ALAC file. + * + * \param file File location. + * \return 0 on success, -1 on failure. + */ +int isM4a(const char* file) +{ + FILE* ftest = fopen(file, "rb"); + uint32_t fileSig; + uint32_t ftypSig; + + if(ftest == NULL) + return -1; + + /* Read first 4 bytes (should be size of ftyp atom) */ + if(fread(&fileSig, 4, 1, ftest) == 0) + { + fclose(ftest); + return -1; + } + + /* Read next 4 bytes (should be 'ftyp') */ + if(fread(&ftypSig, 4, 1, ftest) == 0) + { + fclose(ftest); + return -1; + } + + fclose(ftest); + + /* Check for 'ftyp' signature (0x70797466 in little-endian) */ + if(ftypSig == 0x70797466) + return 0; + + return -1; +} + +static int initM4a(const char* file) +{ + (void)file; + + /* TODO: Initialize AAC decoder */ + /* This requires: + * 1. Parse MP4 container to find AAC audio track + * 2. Extract decoder config (sample rate, channels, etc.) + * 3. Initialize AAC decoder with config + */ + + /* Set default values for now */ + rate = 44100; + channels = 2; + *buffSize = rate * channels * sizeof(int16_t); + + errno = FILE_NOT_SUPPORTED; + return -1; +} + +static uint32_t rateM4a(void) +{ + return rate; +} + +static uint8_t channelM4a(void) +{ + return channels; +} + +static uint64_t decodeM4a(void* buffer) +{ + (void)buffer; + + /* TODO: Decode AAC frame */ + /* This requires: + * 1. Read next AAC frame from MP4 container + * 2. Decode AAC frame to PCM samples + * 3. Write PCM samples to buffer + * 4. Return number of samples decoded + */ + + return 0; +} + +static void exitM4a(void) +{ + /* TODO: Clean up AAC decoder */ +} + +static size_t getFileSamplesM4a(void) +{ + /* TODO: Calculate total samples from MP4 metadata */ + return 0; +} diff --git a/source/playback.c b/source/playback.c index 3d9a00f..8210fcf 100644 --- a/source/playback.c +++ b/source/playback.c @@ -7,6 +7,7 @@ #include "error.h" #include "file.h" #include "flac.h" +#include "m4a.h" #include "mp3.h" #include "opus.h" #include "playback.h" @@ -98,15 +99,18 @@ void playFile(void* infoIn) setVorbis(&decoder); break; - case FILE_TYPE_SID: - setSid(&decoder); - break; + case FILE_TYPE_SID: + setSid(&decoder); + break; + + case FILE_TYPE_M4A: + case FILE_TYPE_AAC: + setM4a(&decoder); + break; - default: - goto err; - } - - if(ndspInit() < 0) + default: + goto err; + } if(ndspInit() < 0) { errno = NDSP_INIT_FAIL; goto err;