From edda876d65f19e594dbfaa67d6a215ddb832adc3 Mon Sep 17 00:00:00 2001 From: TheGameratorT Date: Mon, 6 Jul 2020 14:12:22 +0100 Subject: [PATCH] Add Mario Kart DS code. --- MarioKartDS/declarations.h | 82 ++++ MarioKartDS/source/NWAVPlayer.c | 523 +++++++++++++++++++++++ MarioKartDS/source/NWAVPlayer.h | 79 ++++ MarioKartDS/source/NWAVPlayer_mkds.c | 186 ++++++++ MarioKartDS/source/NWAVPlayer_mkds_asm.s | 37 ++ MarioKartDS/source/snd.s | 10 + MarioKartDS/symbols9.x | 23 + NSMBDS/declarations.h | 22 +- README.md | 10 +- 9 files changed, 961 insertions(+), 11 deletions(-) create mode 100644 MarioKartDS/declarations.h create mode 100644 MarioKartDS/source/NWAVPlayer.c create mode 100644 MarioKartDS/source/NWAVPlayer.h create mode 100644 MarioKartDS/source/NWAVPlayer_mkds.c create mode 100644 MarioKartDS/source/NWAVPlayer_mkds_asm.s create mode 100644 MarioKartDS/source/snd.s create mode 100644 MarioKartDS/symbols9.x diff --git a/MarioKartDS/declarations.h b/MarioKartDS/declarations.h new file mode 100644 index 0000000..4869e55 --- /dev/null +++ b/MarioKartDS/declarations.h @@ -0,0 +1,82 @@ +/*===============================================================\ +| Simplified definitions of which declarations are required. | +| This is meant for the code to be compilable without the SDK. | +\===============================================================*/ + +#define FX_Mul(v1, v2) FX_MulFunc(v1, v2) +#define FX32_SHIFT 12 +#define MATH_CLAMP(x, low, high) ( ( (x) > (high) ) ? (high) : ( ( (x) < (low) ) ? (low) : (x) ) ) + +typedef u8 FSFile[72]; +typedef u8 OSThread[200]; +typedef u8 OSMessageQueue[32]; + +typedef void *OSMessage; +typedef void (*SNDAlarmHandler)(void*); + +typedef enum +{ + SND_CHANNEL_DATASHIFT_NONE, + SND_CHANNEL_DATASHIFT_1BIT, + SND_CHANNEL_DATASHIFT_2BIT, + SND_CHANNEL_DATASHIFT_4BIT +} SNDChannelDataShift; + +typedef enum +{ + SND_WAVE_FORMAT_PCM8, + SND_WAVE_FORMAT_PCM16, + SND_WAVE_FORMAT_ADPCM, + SND_WAVE_FORMAT_PSG, + SND_WAVE_FORMAT_NOISE = SND_WAVE_FORMAT_PSG +} SNDWaveFormat; + +typedef enum +{ + SND_CHANNEL_LOOP_MANUAL, + SND_CHANNEL_LOOP_REPEAT, + SND_CHANNEL_LOOP_1SHOT +} SNDChannelLoop; + +typedef enum +{ + FS_SEEK_SET, + FS_SEEK_CUR, + FS_SEEK_END +} FSSeekFileMode; + +extern "C" +{ + void OS_Panic(); + void OS_WakeupThreadDirect(OSThread *thread); + void OS_CreateThread(OSThread *thread, void (*func)(void *), void *arg, void *stack, u32 stackSize, u32 prio); + bool OS_ReceiveMessage(OSMessageQueue *mq, OSMessage *msg, s32 flags); + bool OS_SendMessage(OSMessageQueue *mq, OSMessage msg, s32 flags); + void OS_InitMessageQueue(OSMessageQueue *mq, OSMessage *msgArray, s32 msgCount); + + void MI_CpuFill8(void *dest, u8 data, u32 size); + static inline void MI_CpuClear8(void *dest, u32 size) { + MI_CpuFill8(dest, 0, size); + } + + void SND_SetupChannelPcm(int chNo, SNDWaveFormat format, const void *dataAddr, SNDChannelLoop loop, int loopStart, int dataLen, int volume, SNDChannelDataShift shift, int timer, int pan); + void SND_SetChannelVolume(u32 chBitMask, int volume, SNDChannelDataShift shift); + void SND_LockChannel(u32 chBitMask, u32 flags); + void SND_SetupAlarm(int alarmNo, u32 tick, u32 period, SNDAlarmHandler handler, void *arg); + void SND_StopTimer(u32 chBitMask, u32 capBitMask, u32 alarmBitMask, u32 flags); + void SND_StartTimer(u32 chBitMask, u32 capBitMask, u32 alarmBitMask, u32 flags); + void SND_SetChannelTimer(u32 chBitMask, int timer); + + bool FS_SeekFile(FSFile *p_file, s32 offset, FSSeekFileMode origin); + s32 FS_ReadFile(FSFile *p_file, void *dst, s32 len); + bool FS_CloseFile(FSFile *p_file); + bool FS_OpenFileFast(FSFile* p_file, void* archivePtr, int file_id); + void FS_InitFile(FSFile *p_file); + + fx32 FX_MulFunc(fx32 v1, fx32 v2); + + //Custom FX function. + static inline s64 FX_MulInline64(s64 a1, s64 a2) { + return ((s64)a1 * (s64)a2 + 0x800) >> FX32_SHIFT; + } +} diff --git a/MarioKartDS/source/NWAVPlayer.c b/MarioKartDS/source/NWAVPlayer.c new file mode 100644 index 0000000..a9946d6 --- /dev/null +++ b/MarioKartDS/source/NWAVPlayer.c @@ -0,0 +1,523 @@ +/*============================================================\ +| This file was made by TheGameratorT. | +| | +| You may modify this file and use it for whatever you want | +| just be sure to credit me (TheGameratorT). | +| | +| Hope you like it just as much as I had fun coding this! | +| | +| --------------------------------------------------------- | +| | +| NWAV player core. | +| This is main code that allows for music playback on DS. | +\============================================================*/ + +#include "NWAVPlayer.h" + +#define CHANNEL_L_NUM 6 +#define CHANNEL_R_NUM 7 +#define ALARM_NUM 1 +#define STREAM_THREAD_PRIO 12 +#define THREAD_STACK_SIZE 1024 +#define STRM_BUF_PAGESIZE (64 * 32) +#define STRM_BUF_PAGECOUNT 2 +#define STRM_BUF_SIZE (STRM_BUF_PAGESIZE * STRM_BUF_PAGECOUNT) + +#define MATH_CLAMP(x, low, high) ( ( (x) > (high) ) ? (high) : ( ( (x) < (low) ) ? (low) : (x) ) ) + +typedef struct NWAVEventInfo +{ + int eventID; + int sample; +} NWAVEventInfo; + +typedef struct NWAVHeader +{ + int magic; + int fileSize; + int sampleRate; + int loopStart; + int loopEnd; + SNDWaveFormat format : 8; + u8 stereo; + u8 numEvents; + u8 padding; +} NWAVHeader; + +typedef struct NWAVStream +{ + FSFile file; + NWAVHeader header; + + u8 isPlaying; + u8 isPaused; + s8 pitchIndex; + u8 reserved; + + fx32 speed; + int playRate; + int volume; + + int chMask; + int bufPage; + int musicEnd; + int musicCursor; + int samplesPerUpdate; + int targetVolume; + int fadeDec; + int fadeFrame; + int lastPos; + u8 loops; + u8 stopMode; + u8 chCount; + u8 bytesPerSample; + + int eventIDBlockSize; + int eventBlockSize; + + NWAVEventHandler eventHandler; +} NWAVStream; + +static NWAVStream strm = { + .isPlaying = false, + .isPaused = true, + .speed = 0x1000, + .volume = 127, + .pitchIndex = -1 +}; + +static const int headerSize = sizeof(NWAVHeader); + +static u8 strmThreadStack[THREAD_STACK_SIZE]; +static OSThread strmThread; +static OSMessageQueue msgQ; +static OSMessage msgBuf[1]; + +static NWAVEventInfo* events; + +typedef u8(*pStrmBufT)[2][STRM_BUF_SIZE]; +static pStrmBufT pStrmBuf; + +static void update(NWAVStream* strm); + +static void SoundAlarmHandler(void* arg) +{ + OS_SendMessage(&msgQ, (OSMessage)arg, OS_MESSAGE_NOBLOCK); +} + +static int alignSample(NWAVStream* strm, int pos) +{ + if (strm->header.stereo) + { + int sampleAlign = strm->samplesPerUpdate; + int unaligned = (pos % sampleAlign); + + if (unaligned > (sampleAlign / 2)) + pos += (sampleAlign - unaligned); + else + pos -= unaligned; + } + return pos; +} + +static void seek(NWAVStream* strm, int pos, bool sample) +{ + if (sample) + { + pos = alignSample(strm, pos); + strm->musicCursor = pos; + + pos *= strm->chCount; + pos *= strm->bytesPerSample; + pos += headerSize; + pos += strm->eventBlockSize; + } + + FS_SeekFile(&strm->file, pos, FS_SEEK_SET); +} + +static void prepareBuffer(NWAVStream* strm) +{ + strm->bufPage = 0; + update(strm); + update(strm); +} + +bool NWAV_GetPaused() { return strm.isPaused; } +void NWAV_SetPaused(bool paused) +{ + if (strm.isPlaying && strm.isPaused != paused) + { + if (paused) + { + SND_StopTimer(strm.chMask, 0, 1 << ALARM_NUM, 0); + } + else + { + prepareBuffer(&strm); + SND_StartTimer(strm.chMask, 0, 1 << ALARM_NUM, 0); + } + strm.isPaused = paused; + } +} + +int NWAV_GetVolume() { return strm.volume; } +void NWAV_SetVolume(int volume, int frames) +{ + if (strm.volume == volume) + return; + + if (frames == 0) + { + SND_SetChannelVolume(strm.chMask, volume, SND_CHANNEL_DATASHIFT_NONE); + strm.volume = volume; + } + else + { + int volumeDiff = strm.volume - volume; + int remove = volumeDiff > 0 ? 1 : -1; + + strm.fadeDec = (volumeDiff / frames) + remove; + strm.fadeFrame = frames; + strm.targetVolume = volume; + } +} + +//For internal use +static void stop(NWAVStream* strm, int frames, bool waitForUpdate) +{ + if (!strm->isPlaying) + return; + + if (frames || waitForUpdate) + { + if (frames) + NWAV_SetVolume(0, frames); + strm->stopMode = waitForUpdate + 1; + } + else + { + NWAV_SetPaused(true); + strm->isPlaying = false; + + strm->lastPos = strm->musicCursor; + + FS_CloseFile(&strm->file); + if (strm->header.numEvents) + free(events); + free(pStrmBuf); + } +} + +//For external use +void NWAV_Stop(int frames) +{ + stop(&strm, frames, false); +} + +void NWAV_SetEventHandler(NWAVEventHandler func) { strm.eventHandler = func; } +static void updateEvents(NWAVStream* strm) +{ + if (!strm->eventHandler) + return; + + for (int i = 0; i < strm->header.numEvents; i++) + { + NWAVEventInfo info = events[i]; + if (info.sample > strm->musicCursor && + info.sample < strm->musicCursor + strm->samplesPerUpdate) + { + strm->eventHandler(info.eventID); + } + } +} + +static int pitchForIndex[] = { + 0xF00, + 0xE00, + 0xF00, + 0x1000, + 0x1100, + 0x1200, + 0x1100, + 0x1000 +}; + +//Sets the music as faster, but does not care to align the buffer. +//WARNING: Be extremely cautious. +static void setSpeedFast(NWAVStream* strm, fx32 speed) +{ + fx32 sampleRate = strm->header.sampleRate << FX32_SHIFT; + int rate = FX_Mul(sampleRate, speed) >> FX32_SHIFT; + + s32 timerValue = SND_TIMER_CLOCK / rate; + SND_SetChannelTimer(strm->chMask, timerValue); +} + +//Used to switch pitch per frame update. +//WARNING: Somewhat misaligned with the buffer. +static void setAndAlternatePitch(NWAVStream* strm) +{ + setSpeedFast(strm, pitchForIndex[strm->pitchIndex]); + strm->pitchIndex++; + if (strm->pitchIndex > 7) + strm->pitchIndex = 0; +} + +void NWAV_SetPitchWaving(bool waving, fx32 returnSpeed) +{ + if (strm.isPlaying) + { + if (waving) + { + NWAV_SetSpeed(0x1000); + strm.pitchIndex = 0; + } + else + { + NWAV_SetSpeed(returnSpeed); + strm.pitchIndex = -1; + } + } +} + +//Updates music settings each frame. +bool NWAV_UpdatePerFrame() +{ + if (strm.isPlaying) + { + if (strm.pitchIndex != -1) + setAndAlternatePitch(&strm); + + if (strm.fadeFrame) + { + int newVolume = strm.volume - strm.fadeDec; + newVolume = MATH_CLAMP(newVolume, 0, 127); + + NWAV_SetVolume(newVolume, 0); + strm.fadeFrame--; + + if (strm.fadeFrame == 0) + { + NWAV_SetVolume(strm.targetVolume, 0); + if (strm.stopMode == 1) + { + stop(&strm, 0, false); + return false; + } + } + } + } + return true; +} + +static void updateCheckEnd(NWAVStream* strm, pStrmBufT pBuf, int len) +{ + int leftOver = STRM_BUF_PAGESIZE - len; + if (strm->loops) + { + if (strm->musicCursor > strm->header.loopEnd) + { + seek(strm, strm->header.loopStart, true); + if (leftOver > 0) + { + for (int i = 0; i < strm->chCount; i++) + FS_ReadFile(&strm->file, &(*pBuf)[i][len], leftOver); + } + seek(strm, strm->header.loopStart + (leftOver / strm->bytesPerSample), true); + } + } + else + { + if (strm->musicCursor > strm->musicEnd) + { + if (leftOver > 0) + { + for (int i = 0; i < strm->chCount; i++) + MI_CpuClear8(&(*pBuf)[i][len], leftOver); + } + stop(strm, 0, true); + } + } +} + +static void update(NWAVStream* strm) +{ + //Check for delayed stop. + if (strm->stopMode == 2) + { + stop(strm, 0, false); + return; + } + + //Get buffer page and swap. + pStrmBufT pBuf = (pStrmBufT)(*(*pStrmBuf) + (STRM_BUF_PAGESIZE * strm->bufPage)); + + //Get read length. + int len = STRM_BUF_PAGESIZE; + int limit = strm->loops ? strm->header.loopEnd : strm->musicEnd; + int remain = (limit - strm->musicCursor) * strm->bytesPerSample; + if (remain < len) + len = remain; + + //Read the data to the buffer. + for (int ch = 0; ch < strm->chCount; ch++) + FS_ReadFile(&strm->file, (*pBuf)[ch], len); + + strm->musicCursor += strm->samplesPerUpdate; + + updateCheckEnd(strm, pBuf, len); + updateEvents(strm); + + strm->bufPage++; + if (strm->bufPage == STRM_BUF_PAGECOUNT) + strm->bufPage = 0; +} + +static void setup(NWAVStream* strm) +{ + s32 timerValue = SND_TIMER_CLOCK / strm->playRate; + u32 alarmPeriod = timerValue * strm->samplesPerUpdate / 32; + + for (int i = 0; i < strm->chCount; i++) + { + bool left = i == 0; + SND_SetupChannelPcm( + left ? CHANNEL_L_NUM : CHANNEL_R_NUM, + strm->header.format, + left ? (*pStrmBuf)[0] : (*pStrmBuf)[1], + SND_CHANNEL_LOOP_REPEAT, + 0, + STRM_BUF_SIZE / sizeof(u32), + strm->volume, + SND_CHANNEL_DATASHIFT_NONE, + timerValue, + !strm->header.stereo ? 64 : (left ? 0 : 127) + ); + } + SND_SetupAlarm(ALARM_NUM, alarmPeriod, alarmPeriod, SoundAlarmHandler, strm); +} + +static void updateStreamInfo(NWAVStream* strm) +{ + bool notPaused = !strm->isPaused; + + if (notPaused) + NWAV_SetPaused(true); + + setup(strm); + + if (notPaused) + NWAV_SetPaused(false); +} + +fx32 NWAV_GetSpeed() { return strm.speed; } +void NWAV_SetSpeed(fx32 speed) +{ + fx32 sampleRate = strm.header.sampleRate << FX32_SHIFT; + strm.playRate = FX_Mul(sampleRate, speed) >> FX32_SHIFT; + strm.speed = speed; + + updateStreamInfo(&strm); +} + +static void loadEvents(NWAVStream* strm) +{ + events = (NWAVEventInfo*)malloc(sizeof(NWAVEventInfo) * strm->header.numEvents); + for (int i = 0; i < strm->header.numEvents; i++) + { + int val = 0; + FS_ReadFile(&strm->file, &val, 1); + events[i].eventID = val; + } + seek(strm, headerSize + strm->eventIDBlockSize, false); + for (int i = 0; i < strm->header.numEvents; i++) + { + int val; + FS_ReadFile(&strm->file, &val, 4); + events[i].sample = val; + } +} + +void NWAV_Play(int fileID, fx32 speed, int volume, fx32 resume) +{ + if (strm.isPlaying) + stop(&strm, 0, false); + + FS_InitFile(&strm.file); + if (!FS_OpenFileFast(&strm.file, (void*)0x216F36C, fileID)) + OS_Panic(); + + FS_ReadFile(&strm.file, &strm.header, headerSize); + + //Setup play + if (speed != 0) + strm.speed = speed; + if (volume != 0) + strm.volume = volume; + + //Reset variables + strm.loops = strm.header.loopEnd != 0; + strm.fadeDec = 0; + strm.fadeFrame = 0; + strm.stopMode = 0; + strm.chCount = strm.header.stereo ? 2 : 1; + strm.bytesPerSample = strm.header.format ? 2 : 1; + strm.samplesPerUpdate = (STRM_BUF_PAGESIZE / strm.bytesPerSample); + strm.chMask = 1 << CHANNEL_L_NUM | ((1 << CHANNEL_R_NUM) * strm.header.stereo); + + if (strm.header.numEvents) + { + int unalignedEvents = (strm.header.numEvents % 4); + strm.eventIDBlockSize = strm.header.numEvents + (4 - unalignedEvents); + strm.eventBlockSize = strm.eventIDBlockSize + (strm.header.numEvents * 4); + loadEvents(&strm); + } + else + { + strm.eventIDBlockSize = 0; + strm.eventBlockSize = 0; + } + + strm.musicEnd = (((strm.header.fileSize - headerSize - strm.eventBlockSize) / strm.chCount) / strm.bytesPerSample); + + pStrmBuf = (pStrmBufT)malloc(STRM_BUF_SIZE * strm.chCount); + + int resumePos = 0; + if (resume) + resumePos = FX_MulInline64((s64)strm.lastPos << FX32_SHIFT, resume) >> FX32_SHIFT; + + seek(&strm, resumePos, true); + NWAV_SetSpeed(strm.speed); + + strm.isPlaying = true; + strm.isPaused = true; + NWAV_SetPaused(false); +} + +static void StrmThread(void* arg) +{ + OSMessage message; + + while (true) + { + OS_ReceiveMessage(&msgQ, &message, OS_MESSAGE_BLOCK); + update((NWAVStream*)message); + } +} + +void NWAV_Init() +{ + //Lock the channels. + SND_LockChannel(1 << CHANNEL_L_NUM | 1 << CHANNEL_R_NUM, 0); + + //Startup stream thread. + OS_InitMessageQueue(&msgQ, msgBuf, 1); + OS_CreateThread(&strmThread, + StrmThread, + NULL, + &strmThreadStack[THREAD_STACK_SIZE], + THREAD_STACK_SIZE, + STREAM_THREAD_PRIO); + OS_WakeupThreadDirect(&strmThread); +} diff --git a/MarioKartDS/source/NWAVPlayer.h b/MarioKartDS/source/NWAVPlayer.h new file mode 100644 index 0000000..033d186 --- /dev/null +++ b/MarioKartDS/source/NWAVPlayer.h @@ -0,0 +1,79 @@ +/*============================================================\ +| This file was made by TheGameratorT. | +| | +| You may modify this file and use it for whatever you want | +| just be sure to credit me (TheGameratorT). | +| | +| Hope you like it just as much as I had fun coding this! | +| | +| --------------------------------------------------------- | +| | +| NWAV player core declarations. | +| This is the header that allows the engine code to be | +| implemented into the game and be accessed externally. | +\============================================================*/ + +#ifndef _NWAVPLAYER_H +#define _NWAVPLAYER_H + +#define NWAV 0x5641574E + +#include "mkds.h" + +#ifdef __cplusplus +extern "C" { +#endif + + //The function type of the function that will handle the events. + typedef void(*NWAVEventHandler)(int); + + /// Plays a music. + /// The file ID of the music file to play. + /// Sets the speed to be played. (0 = do not change) + /// Sets the volume to be played. (0 = do not change) + /// The speed scale related to the music playing. (0 = do not resume) + void NWAV_Play(int fileID, fx32 speed, int volume, fx32 resume); + + /// Stops the music playing. + /// Number of frames where the volume shift occurs. + void NWAV_Stop(int frames); + + /// Gets the music volume. + /// The music volume. + int NWAV_GetVolume(); + + /// Sets the music volume. + /// The target volume. Value range = [0, 127] + /// Number of frames where the volume shift occurs. (0 = instant change) + void NWAV_SetVolume(int volume, int frames); + + /// Gets the music speed. + /// The current music speed. + fx32 NWAV_GetSpeed(); + + /// Sets the music speed. + /// The target speed for the music to be played at. + void NWAV_SetSpeed(fx32 tempo); + + /// Sets either if the music pitch waves up and down. + /// True if should wave. False otherwise. + /// The destination speed when the waving is stopped. + void NWAV_SetPitchWaving(bool waving, fx32 returnSpeed); + + /// Gets if the music is paused. + /// True if the music is paused. False otherwise. + bool NWAV_GetPaused(); + + /// Sets if the music is paused. + /// Sets the music as paused when true, unpauses when false. + void NWAV_SetPaused(bool paused); + + /// Sets the event handler function. + /// The function pointer of the event handler. + void NWAV_SetEventHandler(NWAVEventHandler func); + +#ifdef __cplusplus +} +#endif + +#endif //!_NWAVPLAYER_H diff --git a/MarioKartDS/source/NWAVPlayer_mkds.c b/MarioKartDS/source/NWAVPlayer_mkds.c new file mode 100644 index 0000000..cf20f0d --- /dev/null +++ b/MarioKartDS/source/NWAVPlayer_mkds.c @@ -0,0 +1,186 @@ +/*============================================================\ +| This file was made by TheGameratorT. | +| | +| You may modify this file and use it for whatever you want | +| just be sure to credit me (TheGameratorT). | +| | +| Hope you like it just as much as I had fun coding this! | +| | +| --------------------------------------------------------- | +| | +| NWAV player integration into Mario Kart DS. | +| This is where the wav player is installed in the game. | +\============================================================*/ + +#include "NWAVPlayer.h" + +#ifdef __INTELLISENSE__ +#define __attribute__(x) +#endif + +/*=============================================================\ +| NWAV player integration into MKDS. | +| This is where the wav player is installed in the game. | +\=============================================================*/ + +void NWAV_MainEventHandler(int eventID) +{ + switch (eventID) + { + case 0: + break; + default: + break; + } +} + +/*=============================================================\ +| MKDS playback control interface replacement. | +| This is where the Nitro WAV player is controlled. | +\=============================================================*/ + +//#define NWAV_HAS_HQ_TEMPO +//#define NWAV_HAS_HQ_PITCH + +#define NWAV_FIRST_ID 611 +#define NWAV_TABLE_SIZE 76 +#define NWAV_HQ_TEMPO_OFFSET NWAV_TABLE_SIZE +#ifdef NWAV_HAS_HQ_TEMPO +#define NWAV_HQ_PITCH_OFFSET (NWAV_HQ_TEMPO_OFFSET * 2) +#else +#define NWAV_HQ_PITCH_OFFSET NWAV_TABLE_SIZE +#endif + +static int curWav = 0; +static int pitchReturnVar = 0; +#ifdef NWAV_HAS_HQ_PITCH +static bool fastTempoMode = false; +#endif +static bool isPitchTrackPlaying = false; + +//Backup the original functions +__attribute__((naked)) static void SEQ_Play(int seqID, int volume, void* handle) { asm("STMFD SP!, {R4-R6,LR}\nB 0x0210D7E0"); } + +static bool GetIfSequenced(int wavID) +{ + FSFile file; + FS_InitFile(&file); + if (FS_OpenFileFast(&file, (void*)0x216F36C, wavID)) + { + int magic; + int readSize = FS_ReadFile(&file, &magic, 4); + if (readSize) + { + if (magic == NWAV) + { + FS_CloseFile(&file); + return false; + } + } + FS_CloseFile(&file); + } + return true; +} + +void NWAVh_Play(int seqID, int volume, void* handle) +{ + int wavID = NWAV_FIRST_ID + seqID; + + if (GetIfSequenced(wavID)) + { + NWAV_Stop(0); + SEQ_Play(seqID, volume, handle); + } + else + { + NNS_SndPlayerStopSeq(handle, 0); + NWAV_Play(wavID, 0x1000, 127, 0); + +#ifdef NWAV_HAS_HQ_PITCH + pitchReturnVar = wavID; + fastTempoMode = false; +#else + pitchReturnVar = 0x1000; +#endif + isPitchTrackPlaying = false; + + curWav = wavID; + } +} + +void NWAVh_Stop(void* handle, int fadeFrame) +{ + NNS_SndPlayerStopSeq(handle, fadeFrame); //Keep SSEQ support + NWAV_Stop(fadeFrame); +} + +void NWAVh_MoveVolume(void* handle, int targetVolume, int frames) +{ + NNS_SndPlayerMoveVolume(handle, targetVolume, frames); //Keep SSEQ support + NWAV_SetVolume(targetVolume, frames); +} + +void NWAVh_SetTempo(void* handle, int ratio) +{ + NNS_SndPlayerSetTempoRatio(handle, ratio); //Keep SSEQ support + +#ifdef NWAV_HAS_HQ_TEMPO + if (ratio == 300) + { + int wavID = curWav + NWAV_HQ_TEMPO_OFFSET; + if (!isPitchTrackPlaying) + NWAV_Play(wavID, 0x1000, 127, 0); + pitchReturnVar = wavID; + fastTempoMode = true; + } + else + { + if (!isPitchTrackPlaying) + NWAV_Play(curWav, 0x1000, 127, 0); + pitchReturnVar = curWav; + fastTempoMode = false; + } +#else + int new_ratio = ratio << 4; + if (!isPitchTrackPlaying) + NWAV_SetSpeed(new_ratio); + pitchReturnVar = new_ratio; +#endif +} + +void NWAVh_SetPitch(void* handle, u16 trackBitMask, int pitch) +{ + NNS_SndPlayerSetTrackPitch(handle, trackBitMask, pitch); //Keep SSEQ support + + //Pitch Value 0 = Normal + //Pitch Value 64 = Final Lap + if (!isPitchTrackPlaying) + { + if (pitch != 0 && pitch != 64) + { +#ifdef NWAV_HAS_HQ_PITCH + int wavID = curWav + NWAV_HQ_PITCH_OFFSET; + int resume = fastTempoMode ? 0x12C0/*((300 / 256) * 0x1000)*/ : 0x1000; + NWAV_Play(wavID, 0x1000, 127, resume); +#else + NWAV_SetPitchWaving(true, 0); +#endif + isPitchTrackPlaying = true; + } + } + else + { + if (pitch == 0 || pitch == 64) + { +#ifdef NWAV_HAS_HQ_PITCH + int resume = fastTempoMode ? 0xD40/*(0x1000 - (((300 / 256) * 0x1000) - 0x1000))*/ : 0x1000; + NWAV_Play(pitchReturnVar, 0x1000, 127, resume); +#else + NWAV_SetPitchWaving(false, pitchReturnVar); +#endif + isPitchTrackPlaying = false; + } + } +} + +// To do: Prevent loading SSEQs that are replaced by NWAVs. diff --git a/MarioKartDS/source/NWAVPlayer_mkds_asm.s b/MarioKartDS/source/NWAVPlayer_mkds_asm.s new file mode 100644 index 0000000..6afd1b7 --- /dev/null +++ b/MarioKartDS/source/NWAVPlayer_mkds_asm.s @@ -0,0 +1,37 @@ +@Hook initialization +ansub_0210D8A8: + BL NNS_SndInit + BL NWAV_Init + LDR R0, =NWAV_MainEventHandler + BL NWAV_SetEventHandler + B 0x0210D8AC + +@Hook fade update +ansub_0210D7C8: + BL NNS_SndMain + BL NWAV_UpdatePerFrame + B 0x0210D7CC + +@Hook play seq +ansub_0210D7DC: + B NWAVh_Play + +@Hook stop seq +arepl_0210DA14: +arepl_0210DA7C: +arepl_0210DBB4: + B NWAVh_Stop + +@Hook move seq volume +arepl_0210DAEC: +arepl_0210DBEC: + B NWAVh_MoveVolume + +arepl_02106828: +arepl_02106CE8: +arepl_02106E30: +arepl_0210FB3C: + B NWAVh_SetTempo + +arepl_0210FE90: + B NWAVh_SetPitch \ No newline at end of file diff --git a/MarioKartDS/source/snd.s b/MarioKartDS/source/snd.s new file mode 100644 index 0000000..94e2c85 --- /dev/null +++ b/MarioKartDS/source/snd.s @@ -0,0 +1,10 @@ +.global SND_SetChannelTimer +SND_SetChannelTimer: + STMFD SP!, {R3,LR} + MOV R2, R1 + MOV R3, #0 + MOV R1, R0 + MOV R0, #0x13 + STR R3, [SP] + BL PushCommand_impl + LDMFD SP!, {R3,PC} \ No newline at end of file diff --git a/MarioKartDS/symbols9.x b/MarioKartDS/symbols9.x new file mode 100644 index 0000000..a2761bd --- /dev/null +++ b/MarioKartDS/symbols9.x @@ -0,0 +1,23 @@ +OS_Panic = 0x200FD1C; +OS_WakeupThreadDirect = 0x200DF68; +OS_CreateThread = 0x200E28C; +OS_ReceiveMessage = 0x200EA3C; +OS_SendMessage = 0x200EAE8; +OS_InitMessageQueue = 0x200EB90; + +MI_CpuFill8 = 0x214D168; + +SND_SetupChannelPcm = 0x2020A14; +SND_SetChannelVolume = 0x2020A6C; +SND_LockChannel = 0x2020ACC; +SND_SetupAlarm = 0x2020B2C; +SND_StopTimer = 0x2020BBC; +SND_StartTimer = 0x2020C2C; + +FS_SeekFile = 0x2004840; +FS_ReadFile = 0x20048AC; +FS_CloseFile = 0x20049E8; +FS_OpenFileFast = 0x2004A80; +FS_InitFile = 0x2004D80; + +FX_MulFunc = 0x21488E8; \ No newline at end of file diff --git a/NSMBDS/declarations.h b/NSMBDS/declarations.h index f2c3687..50598b3 100644 --- a/NSMBDS/declarations.h +++ b/NSMBDS/declarations.h @@ -1,14 +1,19 @@ -/*=============================================================\ -| Simplified definitions of which declarations are required. | -\=============================================================*/ +/*===============================================================\ +| Simplified definitions of which declarations are required. | +| This is meant for the code to be compilable without the SDK. | +\===============================================================*/ +#define FX32_CAST(x) ((fx32)x) +#define FX32_SHIFT 12 #define MATH_CLAMP(x, low, high) ( ( (x) > (high) ) ? (high) : ( ( (x) < (low) ) ? (low) : (x) ) ) +typedef u8 FSFile[72]; typedef u8 OSThread[200]; typedef u8 OSMessageQueue[32]; -typedef void *OSMessage; +typedef void *OSMessage; typedef void (*SNDAlarmHandler)(void*); + typedef enum { SND_CHANNEL_DATASHIFT_NONE, @@ -16,6 +21,7 @@ typedef enum SND_CHANNEL_DATASHIFT_2BIT, SND_CHANNEL_DATASHIFT_4BIT } SNDChannelDataShift; + typedef enum { SND_WAVE_FORMAT_PCM8, @@ -24,6 +30,7 @@ typedef enum SND_WAVE_FORMAT_PSG, SND_WAVE_FORMAT_NOISE = SND_WAVE_FORMAT_PSG } SNDWaveFormat; + typedef enum { SND_CHANNEL_LOOP_MANUAL, @@ -31,7 +38,6 @@ typedef enum SND_CHANNEL_LOOP_1SHOT } SNDChannelLoop; -typedef u8 FSFile[72]; typedef enum { FS_SEEK_SET, @@ -65,6 +71,10 @@ extern "C" bool FS_CloseFile(FSFile *p_file); bool FS_OpenFileFast(FSFile* p_file, void* archivePtr, int file_id); void FS_InitFile(FSFile *p_file); + + static inline fx32 FX_MulInline(fx32 v1, fx32 v2) { + return FX32_CAST(((s64)(v1)*v2 + 0x800LL) >> FX32_SHIFT); + } } -int getPlayerCount(); +int getPlayerCount(); //Temporary while the NSMB-ASMReference doesn't have documentation on it. diff --git a/README.md b/README.md index 6b4dd4c..052040f 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,11 @@ This player which relies on a custom WAV format which I named NWAV, allows strea # Supported Games -| Game | State | Patch Requirements | -| -------------------------|--------------|--------------------| -| New Super Mario Bros. DS | Almost done | [NSMB-ASMReference](https://github.com/Overblade/NSMB-ASMReference) | -| Mario Kart DS | Not finished | | -| Super Mario 64 DS | Not started | | +| Game | State | Patch Requirements | +| -------------------------|-------------|--------------------| +| New Super Mario Bros. DS | Almost done | [NSMB-ASMReference](https://github.com/Overblade/NSMB-ASMReference) | +| Mario Kart DS | Almost done | | +| Super Mario 64 DS | Not started | | # Supported WAV formats The source WAV file that is fed to the converter must follow these specifications: