From 2354ee76a3f57f0fa926b98fb8e9adc2957acc49 Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Fri, 25 Oct 2024 22:39:32 -0600 Subject: [PATCH] Another attempt at this thing --- assets/yaml/us/rev1/ast_audio.yaml | 8 + include/sf64audio_provisional.h | 12 +- libultraship | 2 +- src/audio/audio_general.c | 25 +- src/audio/audio_heap.c | 4 +- src/audio/audio_load.c | 71 ++-- src/audio/audio_synthesis.c | 1 + src/audio/audio_thread.c | 69 +++- src/audio/mixer.c | 527 +++++++++++++++++++++++++++++ src/audio/mixer.h | 88 +++++ src/buffers.c | 2 +- src/libultra/io/aisetfreq.c | 1 + src/overlays/ovl_menu/fox_option.c | 1 + src/overlays/ovl_menu/fox_title.c | 1 + src/port/Engine.cpp | 61 ++++ src/port/Engine.h | 5 + src/port/Game.cpp | 5 +- src/port/audio/GameAudio.h | 10 + src/sys/sys_main.c | 7 +- tools/Torch | 2 +- 20 files changed, 842 insertions(+), 60 deletions(-) create mode 100644 assets/yaml/us/rev1/ast_audio.yaml create mode 100644 src/audio/mixer.c create mode 100644 src/audio/mixer.h create mode 100644 src/port/audio/GameAudio.h diff --git a/assets/yaml/us/rev1/ast_audio.yaml b/assets/yaml/us/rev1/ast_audio.yaml new file mode 100644 index 00000000..43d2af2b --- /dev/null +++ b/assets/yaml/us/rev1/ast_audio.yaml @@ -0,0 +1,8 @@ +audio_seq: + { type: BLOB, offset: 0xDEA20, size: 0x3ACF0, symbol: gAudioSeq } + +audio_bank: + { type: BLOB, offset: 0x119710, size: 0x1E020, symbol: gAudioBank } + +audio_table: + { type: BLOB, offset: 0x137730, size: 0x73C580, symbol: gAudioTable } \ No newline at end of file diff --git a/include/sf64audio_provisional.h b/include/sf64audio_provisional.h index 564e63b3..5bf39141 100644 --- a/include/sf64audio_provisional.h +++ b/include/sf64audio_provisional.h @@ -788,7 +788,7 @@ typedef struct { /* 0x01 */ s8 delay; /* 0x02 */ s8 medium; /* 0x04 */ u8* ramAddr; - /* 0x08 */ u32 curDevAddr; + /* 0x08 */ uintptr_t curDevAddr; /* 0x0C */ u8* curRamAddr; /* 0x10 */ u32 bytesRemaining; /* 0x14 */ u32 chunkSize; @@ -803,7 +803,7 @@ typedef struct { /* 0x00 */ u8 medium; /* 0x01 */ u8 seqOrFontId; /* 0x02 */ u8 instId; - /* 0x04 */ u32 curDevAddr; + /* 0x04 */ uintptr_t curDevAddr; /* 0x08 */ u8* curRamAddr; /* 0x0C */ u8* ramAddr; /* 0x10 */ s32 state; @@ -844,7 +844,7 @@ typedef struct { typedef struct SampleDma { /* 0x00 */ u8* ramAddr; - /* 0x04 */ u32 devAddr; + /* 0x04 */ uintptr_t devAddr; /* 0x08 */ u16 sizeUnused; /* 0x0A */ u16 size; /* 0x0C */ u8 unused; @@ -885,8 +885,8 @@ typedef struct { typedef struct { /* 0x00 */ s32 sampleBankId1; /* 0x04 */ s32 sampleBankId2; - /* 0x08 */ s32 baseAddr1; - /* 0x0C */ s32 baseAddr2; + /* 0x08 */ uintptr_t baseAddr1; + /* 0x0C */ uintptr_t baseAddr2; /* 0x10 */ u32 medium1; /* 0x14 */ u32 medium2; } SampleBankRelocInfo; // size = 0x18 @@ -1047,7 +1047,7 @@ void* AudioHeap_AllocPersistentSampleCache(s32 size, s32 fontId, uintptr_t sampl void AudioLoad_DecreaseSampleDmaTtls(void); void AudioLoad_ProcessLoads(s32 resetStatus); void AudioLoad_SyncInitSeqPlayer(s32 playerIdx, s32 seqId, s32 arg2); -void* AudioLoad_DmaSampleData(u32 devAddr, u32 size, u32 arg2, u8* dmaIndexRef, s32 medium); +void* AudioLoad_DmaSampleData(uintptr_t devAddr, u32 size, u32 arg2, u8* dmaIndexRef, s32 medium); void AudioLoad_InitSampleDmaBuffers(s32 numNotes); void AudioLoad_SyncLoadSeqParts(s32 seqId, s32 flags); s32 AudioLoad_SyncLoadInstrument(s32 fontId, s32 instId, s32 drumId); diff --git a/libultraship b/libultraship index e15b3ab1..00c6bcc8 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit e15b3ab12a696a5fe400185966752f0927200b5a +Subproject commit 00c6bcc88286173ce22b8a6656373d3aa5830c62 diff --git a/src/audio/audio_general.c b/src/audio/audio_general.c index c02d106e..70b73fc1 100644 --- a/src/audio/audio_general.c +++ b/src/audio/audio_general.c @@ -1595,7 +1595,7 @@ void Audio_PlayActiveSfx(u8 bankId) { void Audio_KillSfxByBank(u8 bankId) { // LAudioTODO: Stub for now - return; + // return; SfxBankEntry* entry; u8 next = sSfxBanks[bankId][0].next; @@ -1618,7 +1618,8 @@ void Audio_KillSfxByBank(u8 bankId) { void Audio_StopSfxByBankAndSource(u8 bankId, f32* sfxSource) { // LAudioTODO: Stub for now - return; + // return; + SfxBankEntry* entry; u8 curIndex = 0; u8 nextIndex = sSfxBanks[bankId][0].next; @@ -1641,7 +1642,8 @@ void Audio_StopSfxByBankAndSource(u8 bankId, f32* sfxSource) { void Audio_KillSfxByBankAndSource(u8 bankId, f32* sfxSource) { // LAudioTODO: Stub for now - return; + // return; + SfxBankEntry cmp; Audio_StopSfxByBankAndSource(bankId, sfxSource); @@ -1652,7 +1654,8 @@ void Audio_KillSfxByBankAndSource(u8 bankId, f32* sfxSource) { void Audio_KillSfxBySource(f32* sfxSource) { // LAudioTODO: Stub for now - return; + // return; + u8 i; SfxBankEntry cmp; @@ -1665,7 +1668,7 @@ void Audio_KillSfxBySource(f32* sfxSource) { void Audio_KillSfxBySourceAndId(f32* sfxSource, u32 sfxId) { // LAudioTODO: Stub for now - return; + // return; u32 bankId = SFX_BANK(sfxId); u8 next = sSfxBanks[bankId][0].next; @@ -1695,7 +1698,8 @@ void Audio_KillSfxBySourceAndId(f32* sfxSource, u32 sfxId) { void Audio_KillSfxByTokenAndId(u8 token, u32 sfxId) { // LAudioTODO: Stub for now - return; + // return; + u32 bankId = SFX_BANK(sfxId); u8 next = sSfxBanks[bankId][0].next; u8 current = 0; @@ -1723,7 +1727,8 @@ void Audio_KillSfxByTokenAndId(u8 token, u32 sfxId) { void Audio_KillSfxById(u32 sfxId) { // LAudioTODO: Stub for now - return; + // return; + u32 bankId = SFX_BANK(sfxId); u8 next = sSfxBanks[bankId][0].next; u8 current = 0; @@ -1864,7 +1869,8 @@ void Audio_ClearVoice(void) { s32 Audio_GetCurrentVoice(void) { // LAudioTODO: Stub for now - return 0; + // return 0; + if (!IS_SEQUENCE_CHANNEL_VALID(gSeqPlayers[SEQ_PLAYER_VOICE].channels[15])) { return 0; } @@ -1880,7 +1886,8 @@ s32 Audio_GetCurrentVoice(void) { s32 Audio_GetCurrentVoiceStatus(void) { // LAudioTODO: Stub for now - return 1; + // return 1; + SequenceChannel* channel = gSeqPlayers[SEQ_PLAYER_VOICE].channels[15]; SequenceLayer* layer = channel->layers[0]; diff --git a/src/audio/audio_heap.c b/src/audio/audio_heap.c index ddd50d0d..663f5fd7 100644 --- a/src/audio/audio_heap.c +++ b/src/audio/audio_heap.c @@ -147,8 +147,8 @@ void* AudioHeap_Alloc(AudioAllocPool* pool, u32 size) { } void AudioHeap_InitPool(AudioAllocPool* pool, void* ramAddr, u32 size) { - pool->curRamAddr = pool->startRamAddr = (u8*) ALIGN16((u32) ramAddr); - pool->size = size - ((u32) ramAddr & 0xF); + pool->curRamAddr = pool->startRamAddr = (u8*) ramAddr; + pool->size = size - ((uintptr_t) ramAddr & 0xF); pool->numEntries = 0; } diff --git a/src/audio/audio_load.c b/src/audio/audio_load.c index 75a2222a..3cbc7d6d 100644 --- a/src/audio/audio_load.c +++ b/src/audio/audio_load.c @@ -1,6 +1,8 @@ #include "sys.h" #include "sf64dma.h" #include "sf64audio_provisional.h" +#include "assets/ast_audio.h" +#include "port/Engine.h" s32 D_80146D80; s32 PAD_80146D88[2]; @@ -14,23 +16,23 @@ void* AudioLoad_SyncLoad(u32 tableType, u32 id, s32* didAllocate); s32 AudioLoad_GetLoadTableIndex(s32 tableType, u32 entryId); void* AudioLoad_SearchCaches(s32 tableType, s32 id); AudioTable* AudioLoad_GetLoadTable(s32 tableType); -void AudioLoad_SyncDma(u32 devAddr, u8* ramAddr, u32 size, s32 medium); -void AudioLoad_SyncDmaUnkMedium(u32 devAddr, u8* ramAddr, u32 size, s32 unkMediumParam); -s32 AudioLoad_Dma(OSIoMesg* mesg, u32 priority, s32 direction, u32 devAddr, void* ramAddr, u32 size, +void AudioLoad_SyncDma(uintptr_t devAddr, u8* ramAddr, u32 size, s32 medium); +void AudioLoad_SyncDmaUnkMedium(uintptr_t devAddr, u8* ramAddr, u32 size, s32 unkMediumParam); +s32 AudioLoad_Dma(OSIoMesg* mesg, u32 priority, s32 direction, uintptr_t devAddr, void* ramAddr, u32 size, OSMesgQueue* retQueue, s32 medium, const char* dmaType); s32 func_8000FC7C(u32 unkMediumParam, u32* addrPtr); -void func_8000FC8C(s32 unkParam2, u32 addr, u8* ramAddr, u32 size); +void func_8000FC8C(s32 unkParam2, uintptr_t addr, u8* ramAddr, u32 size); void* AudioLoad_AsyncLoadInner(s32 tableType, s32 id, s32 nChunks, s32 retData, OSMesgQueue* retQueue); Sample* AudioLoad_GetFontSample(s32 fontId, s32 instId); void AudioLoad_ProcessSlowLoads(s32 resetStatus); void AudioLoad_DmaSlowCopy(AudioSlowLoad* slowLoad, s32 size); -void AudioLoad_DmaSlowCopyUnkMedium(u32 devAddr, u8* ramAddr, u32 size, s32 unkMediumParam); -AudioAsyncLoad* AudioLoad_StartAsyncLoad(u32 devAddr, u8* ramAddr, u32 size, s32 medium, s32 nChunks, +void AudioLoad_DmaSlowCopyUnkMedium(uintptr_t devAddr, u8* ramAddr, u32 size, s32 unkMediumParam); +AudioAsyncLoad* AudioLoad_StartAsyncLoad(uintptr_t devAddr, u8* ramAddr, u32 size, s32 medium, s32 nChunks, OSMesgQueue* retQueue, u32 retMesg); void AudioLoad_ProcessAsyncLoads(s32 resetStatus); void AudioLoad_ProcessAsyncLoad(AudioAsyncLoad* asyncLoad, s32 resetStatus); void AudioLoad_AsyncDma(AudioAsyncLoad* asyncLoad, u32 size); -void AudioLoad_AsyncDmaUnkMedium(u32 devAddr, u8* ramAddr, u32 size, s32 unkMediumParam); +void AudioLoad_AsyncDmaUnkMedium(uintptr_t devAddr, u8* ramAddr, u32 size, s32 unkMediumParam); void AudioLoad_RelocateSample(TunedSample* tSample, u32 fontDataAddr, SampleBankRelocInfo* relocInfo); s32 AudioLoad_RelocateFontAndPreloadSamples(s32 fontId, u32 fontDataAddr, SampleBankRelocInfo* relocData, s32 isAsync); s32 AudioLoad_ProcessSamplePreloads(s32 resetStatus); @@ -67,12 +69,12 @@ void AudioLoad_DecreaseSampleDmaTtls(void) { static const char devstr00[] = "CAUTION:WAVE CACHE FULL %d"; -void* AudioLoad_DmaSampleData(u32 devAddr, u32 size, u32 arg2, u8* dmaIndexRef, s32 medium) { +void* AudioLoad_DmaSampleData(uintptr_t devAddr, u32 size, u32 arg2, u8* dmaIndexRef, s32 medium) { u32 i; SampleDma* dma; bool hasDma = false; s32 bufferPos; - u32 dmaDevAddr; + uintptr_t dmaDevAddr; s32 sp38; if ((arg2 != 0) || (*dmaIndexRef >= gSampleDmaListSize1)) { @@ -676,7 +678,7 @@ void AudioLoad_RelocateFont(s32 fontId, u32 fontBaseAddr, void* relocData) { gSoundFontList[fontId].instruments = (u32) &fontDataPtrs[1]; } -void AudioLoad_SyncDma(u32 devAddr, u8* ramAddr, u32 size, s32 medium) { +void AudioLoad_SyncDma(uintptr_t devAddr, u8* ramAddr, u32 size, s32 medium) { size = ALIGN16(size); osInvalDCache(ramAddr, size); @@ -698,8 +700,8 @@ void AudioLoad_SyncDma(u32 devAddr, u8* ramAddr, u32 size, s32 medium) { } } -void AudioLoad_SyncDmaUnkMedium(u32 devAddr, u8* ramAddr, u32 size, s32 unkMediumParam) { - s32 addr = devAddr; +void AudioLoad_SyncDmaUnkMedium(uintptr_t devAddr, u8* ramAddr, u32 size, s32 unkMediumParam) { + uintptr_t addr = devAddr; osInvalDCache(ramAddr, size); func_8000FC8C(func_8000FC7C(unkMediumParam, &addr), addr, ramAddr, size); @@ -711,20 +713,20 @@ static const char devstr24[] = "Load Bank BG, Type %d , ID %d\n"; static const char devstr25[] = "get auto\n"; static const char devstr26[] = "get s-auto %x\n"; -s32 AudioLoad_Dma(OSIoMesg* mesg, u32 priority, s32 direction, u32 devAddr, void* ramAddr, u32 size, +s32 AudioLoad_Dma(OSIoMesg* mesg, u32 priority, s32 direction, uintptr_t devAddr, void* ramAddr, u32 size, OSMesgQueue* retQueue, s32 medium, const char* dmaType) { OSPiHandle* handle; - switch (medium) { - case MEDIUM_CART: - handle = osCartRomInit(); - break; - case MEDIUM_DISK_DRIVE: - // handle = osDriveRomInit(); - break; - default: - return 0; - } + // switch (medium) { + // case MEDIUM_CART: + // handle = osCartRomInit(); + // break; + // case MEDIUM_DISK_DRIVE: + // // handle = osDriveRomInit(); + // break; + // default: + // return 0; + // } if (size % 16) { size = ALIGN16(size); @@ -737,7 +739,8 @@ s32 AudioLoad_Dma(OSIoMesg* mesg, u32 priority, s32 direction, u32 devAddr, void mesg->size = size; handle->transferInfo.cmdType = 2; - osEPiStartDma(handle, mesg, direction); + // osEPiStartDma(handle, mesg, direction); + memcpy(ramAddr, (void*) devAddr, size); return 0; } @@ -746,7 +749,7 @@ s32 func_8000FC7C(u32 unkMediumParam, u32* addrPtr) { return 0; } -void func_8000FC8C(s32 unkParam2, u32 addr, u8* ramAddr, u32 size) { +void func_8000FC8C(s32 unkParam2, uintptr_t addr, u8* ramAddr, u32 size) { } void AudioLoad_SyncLoadSimple(u32 tableType, u32 id) { @@ -890,7 +893,7 @@ void AudioLoad_Init(void) { } clearContext = gAudioContextStart; - dwordsLeft = ((u32) gAudioContextEnd - (u32) gAudioContextStart) / 8; + dwordsLeft = ((uintptr_t) gAudioContextEnd - (uintptr_t) gAudioContextStart) / 8; for (; dwordsLeft >= 0; dwordsLeft--) { *clearContext++ = 0; } @@ -938,9 +941,9 @@ void AudioLoad_Init(void) { gSeqFontTable = gSeqFontTableInit; gNumSequences = gSequenceTable->base.numEntries; - AudioLoad_InitTable(gSequenceTable, SEGMENT_ROM_START(audio_seq), gSequenceMedium); - AudioLoad_InitTable(gSoundFontTable, SEGMENT_ROM_START(audio_bank), gSoundFontMedium); - AudioLoad_InitTable(gSampleBankTable, SEGMENT_ROM_START(audio_table), gSampleBankMedium); + AudioLoad_InitTable(gSequenceTable, LOAD_ASSET(gAudioSeq), gSequenceMedium); + AudioLoad_InitTable(gSoundFontTable, LOAD_ASSET(gAudioBank), gSoundFontMedium); + AudioLoad_InitTable(gSampleBankTable, LOAD_ASSET(gAudioTable), gSampleBankMedium); numFonts = gSoundFontTable->base.numEntries; @@ -1104,14 +1107,14 @@ static const char devstr45[] = "===Block BG end\n"; static const char devstr46[] = "Retcode %x\n"; static const char devstr47[] = "Other Type: Not Write ID.\n"; -void AudioLoad_DmaSlowCopyUnkMedium(u32 devAddr, u8* ramAddr, u32 size, s32 unkMediumParam) { - s32 addr = devAddr; +void AudioLoad_DmaSlowCopyUnkMedium(uintptr_t devAddr, u8* ramAddr, u32 size, s32 unkMediumParam) { + uintptr_t addr = devAddr; osInvalDCache(ramAddr, size); func_8000FC8C(func_8000FC7C(unkMediumParam, &addr), addr, ramAddr, size); } -AudioAsyncLoad* AudioLoad_StartAsyncLoad(u32 devAddr, u8* ramAddr, u32 size, s32 medium, s32 nChunks, +AudioAsyncLoad* AudioLoad_StartAsyncLoad(uintptr_t devAddr, u8* ramAddr, u32 size, s32 medium, s32 nChunks, OSMesgQueue* retQueue, u32 retMesg) { AudioAsyncLoad* asyncLoad; s32 i; @@ -1264,8 +1267,8 @@ void AudioLoad_AsyncDma(AudioAsyncLoad* asyncLoad, u32 size) { asyncLoad->medium, "BGCOPY"); } -void AudioLoad_AsyncDmaUnkMedium(u32 devAddr, u8* ramAddr, u32 size, s32 unkMediumParam) { - s32 addr = devAddr; +void AudioLoad_AsyncDmaUnkMedium(uintptr_t devAddr, u8* ramAddr, u32 size, s32 unkMediumParam) { + uintptr_t addr = devAddr; osInvalDCache(ramAddr, size); func_8000FC8C(func_8000FC7C(unkMediumParam, &addr), addr, ramAddr, size); diff --git a/src/audio/audio_synthesis.c b/src/audio/audio_synthesis.c index ed6d361d..5ec7c962 100644 --- a/src/audio/audio_synthesis.c +++ b/src/audio/audio_synthesis.c @@ -1,5 +1,6 @@ #include "sys.h" #include "sf64audio_provisional.h" +#include "audio/mixer.h" s32 D_80145D40; // unused diff --git a/src/audio/audio_thread.c b/src/audio/audio_thread.c index e411411f..fb4144cf 100644 --- a/src/audio/audio_thread.c +++ b/src/audio/audio_thread.c @@ -39,7 +39,74 @@ static const char devstr12[] = "Undefined Port Command %d\n"; static const char devstr13[] = "specchg conjunction error (Msg:%d Cur:%d)\n"; static const char devstr14[] = "Error : Queue is not empty ( %x ) \n"; -SPTask* AudioThread_CreateTask(void) { +void AudioThread_CreateNextAudioBuffer(s16 *samples, u32 num_samples) { + static s32 gMaxAbiCmdCnt = 128; + static SPTask* gWaitingAudioTask = NULL; + u32 sp54; + s32 sp50; + s32 sp4C; + s32 pad48; + OSTask_t* task; + u16* sp40; + s32 pad3C; + OSMesg sp38; + OSMesg sp34; + s32 pad30; + gAudioTaskCountQ++; + if ((gAudioTaskCountQ % gAudioBufferParams.count) != 0) { + return; + } + osSendMesg(gAudioTaskStartQueue, OS_MESG_32(gAudioTaskCountQ), 0); + gAudioTaskIndexQ ^= 1; + gCurAiBuffIndex++; + gCurAiBuffIndex %= 3; + sp4C = (gCurAiBuffIndex + 1) % 3; + sp54 = osAiGetLength() >> 2; + if ((gAudioResetTimer < 16) && (gAiBuffLengths[sp4C] != 0)) { + osAiSetNextBuffer(gAiBuffers[sp4C], gAiBuffLengths[sp4C] * 4); + } + if (gCurAudioFrameDmaCount && gCurAudioFrameDmaCount) {} + gCurAudioFrameDmaCount = 0; + AudioLoad_DecreaseSampleDmaTtls(); + AudioLoad_ProcessLoads(gAudioResetStep); + if (osRecvMesg(&gAudioTaskMesgQueue, &sp38, 0) != -1) { + if (gAudioResetStep == 0) { + gAudioResetStep = 5; + } + gAudioSpecId = sp38.data32; + } + if ((gAudioResetStep != 0) && (AudioHeap_ResetStep() == 0)) { + if (gAudioResetStep == 0) { + osSendMesg(gAudioResetQueue, OS_MESG_32((s32) gAudioSpecId), 0); + } + gWaitingAudioTask = NULL; + return; + } + if (gAudioResetTimer > 16) { + return; + } + if (gAudioResetTimer != 0) { + gAudioResetTimer++; + } + gAudioCurTask = &gAudioRspTasks[gAudioTaskIndexQ]; + gCurAbiCmdBuffer = gAbiCmdBuffs[gAudioTaskIndexQ]; + sp4C = gCurAiBuffIndex; + sp40 = gAiBuffers[sp4C]; + gAiBuffLengths[sp4C] = num_samples; + if (gAiBuffLengths[sp4C] < gAudioBufferParams.minAiBufferLength) { + gAiBuffLengths[sp4C] = gAudioBufferParams.minAiBufferLength; + } + if (gAiBuffLengths[sp4C] > gAudioBufferParams.maxAiBufferLength) { + gAiBuffLengths[sp4C] = gAudioBufferParams.maxAiBufferLength; + } + while (osRecvMesg(gThreadCmdProcQueue, &sp34, 0) != -1) { + AudioThread_ProcessCmds(sp34.data32); + } + gCurAbiCmdBuffer = func_80009B64(gCurAbiCmdBuffer, &sp50, samples, num_samples); + gAudioRandom = osGetCount() * (gAudioRandom + gAudioTaskCountQ); +} + +SPTask* AudioThread_CreateTask() { static s32 gMaxAbiCmdCnt = 128; static SPTask* gWaitingAudioTask = NULL; u32 aiSamplesLeft; diff --git a/src/audio/mixer.c b/src/audio/mixer.c new file mode 100644 index 00000000..8606cc12 --- /dev/null +++ b/src/audio/mixer.c @@ -0,0 +1,527 @@ +#include +#include +#include + +#include "mixer.h" + +#ifndef __clang__ +#pragma GCC optimize ("unroll-loops") +#endif + +#define ROUND_UP_64(v) (((v) + 63) & ~63) +#define ROUND_UP_32(v) (((v) + 31) & ~31) +#define ROUND_UP_16(v) (((v) + 15) & ~15) +#define ROUND_UP_8(v) (((v) + 7) & ~7) +#define ROUND_DOWN_16(v) ((v) & ~0xf) + +#define DMEM_BUF_SIZE (0x1000 - 0x3C0 - 0x40) +#define BUF_U8(a) (rspa.buf.as_u8 + ((a) - 0x3C0)) +#define BUF_S16(a) (rspa.buf.as_s16 + ((a) - 0x3C0) / sizeof(int16_t)) + +static struct { + uint16_t in; + uint16_t out; + uint16_t nbytes; + + uint16_t vol[2]; + uint16_t rate[2]; + uint16_t vol_wet; + uint16_t rate_wet; + + ADPCM_STATE *adpcm_loop_state; + + int16_t adpcm_table[8][2][8]; + + uint16_t filter_count; + int16_t filter[8]; + + union { + int16_t as_s16[DMEM_BUF_SIZE / sizeof(int16_t)]; + uint8_t as_u8[DMEM_BUF_SIZE]; + } buf; +} rspa; + +static int16_t resample_table[64][4] = { + {0x0c39, 0x66ad, 0x0d46, 0xffdf}, {0x0b39, 0x6696, 0x0e5f, 0xffd8}, + {0x0a44, 0x6669, 0x0f83, 0xffd0}, {0x095a, 0x6626, 0x10b4, 0xffc8}, + {0x087d, 0x65cd, 0x11f0, 0xffbf}, {0x07ab, 0x655e, 0x1338, 0xffb6}, + {0x06e4, 0x64d9, 0x148c, 0xffac}, {0x0628, 0x643f, 0x15eb, 0xffa1}, + {0x0577, 0x638f, 0x1756, 0xff96}, {0x04d1, 0x62cb, 0x18cb, 0xff8a}, + {0x0435, 0x61f3, 0x1a4c, 0xff7e}, {0x03a4, 0x6106, 0x1bd7, 0xff71}, + {0x031c, 0x6007, 0x1d6c, 0xff64}, {0x029f, 0x5ef5, 0x1f0b, 0xff56}, + {0x022a, 0x5dd0, 0x20b3, 0xff48}, {0x01be, 0x5c9a, 0x2264, 0xff3a}, + {0x015b, 0x5b53, 0x241e, 0xff2c}, {0x0101, 0x59fc, 0x25e0, 0xff1e}, + {0x00ae, 0x5896, 0x27a9, 0xff10}, {0x0063, 0x5720, 0x297a, 0xff02}, + {0x001f, 0x559d, 0x2b50, 0xfef4}, {0xffe2, 0x540d, 0x2d2c, 0xfee8}, + {0xffac, 0x5270, 0x2f0d, 0xfedb}, {0xff7c, 0x50c7, 0x30f3, 0xfed0}, + {0xff53, 0x4f14, 0x32dc, 0xfec6}, {0xff2e, 0x4d57, 0x34c8, 0xfebd}, + {0xff0f, 0x4b91, 0x36b6, 0xfeb6}, {0xfef5, 0x49c2, 0x38a5, 0xfeb0}, + {0xfedf, 0x47ed, 0x3a95, 0xfeac}, {0xfece, 0x4611, 0x3c85, 0xfeab}, + {0xfec0, 0x4430, 0x3e74, 0xfeac}, {0xfeb6, 0x424a, 0x4060, 0xfeaf}, + {0xfeaf, 0x4060, 0x424a, 0xfeb6}, {0xfeac, 0x3e74, 0x4430, 0xfec0}, + {0xfeab, 0x3c85, 0x4611, 0xfece}, {0xfeac, 0x3a95, 0x47ed, 0xfedf}, + {0xfeb0, 0x38a5, 0x49c2, 0xfef5}, {0xfeb6, 0x36b6, 0x4b91, 0xff0f}, + {0xfebd, 0x34c8, 0x4d57, 0xff2e}, {0xfec6, 0x32dc, 0x4f14, 0xff53}, + {0xfed0, 0x30f3, 0x50c7, 0xff7c}, {0xfedb, 0x2f0d, 0x5270, 0xffac}, + {0xfee8, 0x2d2c, 0x540d, 0xffe2}, {0xfef4, 0x2b50, 0x559d, 0x001f}, + {0xff02, 0x297a, 0x5720, 0x0063}, {0xff10, 0x27a9, 0x5896, 0x00ae}, + {0xff1e, 0x25e0, 0x59fc, 0x0101}, {0xff2c, 0x241e, 0x5b53, 0x015b}, + {0xff3a, 0x2264, 0x5c9a, 0x01be}, {0xff48, 0x20b3, 0x5dd0, 0x022a}, + {0xff56, 0x1f0b, 0x5ef5, 0x029f}, {0xff64, 0x1d6c, 0x6007, 0x031c}, + {0xff71, 0x1bd7, 0x6106, 0x03a4}, {0xff7e, 0x1a4c, 0x61f3, 0x0435}, + {0xff8a, 0x18cb, 0x62cb, 0x04d1}, {0xff96, 0x1756, 0x638f, 0x0577}, + {0xffa1, 0x15eb, 0x643f, 0x0628}, {0xffac, 0x148c, 0x64d9, 0x06e4}, + {0xffb6, 0x1338, 0x655e, 0x07ab}, {0xffbf, 0x11f0, 0x65cd, 0x087d}, + {0xffc8, 0x10b4, 0x6626, 0x095a}, {0xffd0, 0x0f83, 0x6669, 0x0a44}, + {0xffd8, 0x0e5f, 0x6696, 0x0b39}, {0xffdf, 0x0d46, 0x66ad, 0x0c39} +}; + +static inline int16_t clamp16(int32_t v) { + if (v < -0x8000) { + return -0x8000; + } else if (v > 0x7fff) { + return 0x7fff; + } + return (int16_t)v; +} + +static inline int32_t clamp32(int64_t v) { + if (v < -0x7fffffff - 1) { + return -0x7fffffff - 1; + } else if (v > 0x7fffffff) { + return 0x7fffffff; + } + return (int32_t)v; +} + +void aClearBufferImpl(uint16_t addr, int nbytes) { + nbytes = ROUND_UP_16(nbytes); + memset(BUF_U8(addr), 0, nbytes); +} + +void aLoadBufferImpl(const void *source_addr, uint16_t dest_addr, uint16_t nbytes) { + memcpy(BUF_U8(dest_addr), source_addr, ROUND_DOWN_16(nbytes)); +} + +void aSaveBufferImpl(uint16_t source_addr, int16_t *dest_addr, uint16_t nbytes) { + memcpy(dest_addr, BUF_S16(source_addr), ROUND_DOWN_16(nbytes)); +} + +void aLoadADPCMImpl(int num_entries_times_16, const int16_t *book_source_addr) { + memcpy(rspa.adpcm_table, book_source_addr, num_entries_times_16); +} + +void aSetBufferImpl(uint8_t flags, uint16_t in, uint16_t out, uint16_t nbytes) { + rspa.in = in; + rspa.out = out; + rspa.nbytes = nbytes; +} + +void aInterleaveImpl(uint16_t dest, uint16_t left, uint16_t right, uint16_t c) { + int count = ROUND_UP_8(c) / sizeof(int16_t) / 4; + int16_t *l = BUF_S16(left); + int16_t *r = BUF_S16(right); + int16_t *d = BUF_S16(dest); + while (count > 0) { + int16_t l0 = *l++; + int16_t l1 = *l++; + int16_t l2 = *l++; + int16_t l3 = *l++; + int16_t r0 = *r++; + int16_t r1 = *r++; + int16_t r2 = *r++; + int16_t r3 = *r++; + *d++ = l0; + *d++ = r0; + *d++ = l1; + *d++ = r1; + *d++ = l2; + *d++ = r2; + *d++ = l3; + *d++ = r3; + --count; + } +} + +void aDMEMMoveImpl(uint16_t in_addr, uint16_t out_addr, int nbytes) { + nbytes = ROUND_UP_16(nbytes); + memmove(BUF_U8(out_addr), BUF_U8(in_addr), nbytes); +} + +void aSetLoopImpl(ADPCM_STATE *adpcm_loop_state) { + rspa.adpcm_loop_state = adpcm_loop_state; +} + +void aADPCMdecImpl(uint8_t flags, ADPCM_STATE state) { + uint8_t *in = BUF_U8(rspa.in); + int16_t *out = BUF_S16(rspa.out); + int nbytes = ROUND_UP_32(rspa.nbytes); + if (flags & A_INIT) { + memset(out, 0, 16 * sizeof(int16_t)); + } else if (flags & A_LOOP) { + memcpy(out, rspa.adpcm_loop_state, 16 * sizeof(int16_t)); + } else { + memcpy(out, state, 16 * sizeof(int16_t)); + } + out += 16; + + while (nbytes > 0) { + int shift = *in >> 4; // should be in 0..12 or 0..14 + int table_index = *in++ & 0xf; // should be in 0..7 + int16_t (*tbl)[8] = rspa.adpcm_table[table_index]; + int i; + + for (i = 0; i < 2; i++) { + int16_t ins[8]; + int16_t prev1 = out[-1]; + int16_t prev2 = out[-2]; + int j, k; + if (flags & 4) { + for (j = 0; j < 2; j++) { + ins[j * 4] = (((*in >> 6) << 30) >> 30) << shift; + ins[j * 4 + 1] = ((((*in >> 4) & 0x3) << 30) >> 30) << shift; + ins[j * 4 + 2] = ((((*in >> 2) & 0x3) << 30) >> 30) << shift; + ins[j * 4 + 3] = (((*in++ & 0x3) << 30) >> 30) << shift; + } + } else { + for (j = 0; j < 4; j++) { + ins[j * 2] = (((*in >> 4) << 28) >> 28) << shift; + ins[j * 2 + 1] = (((*in++ & 0xf) << 28) >> 28) << shift; + } + } + for (j = 0; j < 8; j++) { + int32_t acc = tbl[0][j] * prev2 + tbl[1][j] * prev1 + (ins[j] << 11); + for (k = 0; k < j; k++) { + acc += tbl[1][((j - k) - 1)] * ins[k]; + } + acc >>= 11; + *out++ = clamp16(acc); + } + } + nbytes -= 16 * sizeof(int16_t); + } + memcpy(state, out - 16, 16 * sizeof(int16_t)); +} + +void aResampleImpl(uint8_t flags, uint16_t pitch, RESAMPLE_STATE state) { + int16_t tmp[16]; + int16_t *in_initial = BUF_S16(rspa.in); + int16_t *in = in_initial; + int16_t *out = BUF_S16(rspa.out); + int nbytes = ROUND_UP_16(rspa.nbytes); + uint32_t pitch_accumulator; + int i; + int16_t *tbl; + int32_t sample; + + if (flags & A_INIT) { + memset(tmp, 0, 5 * sizeof(int16_t)); + } else { + memcpy(tmp, state, 16 * sizeof(int16_t)); + } + if (flags & 2) { + memcpy(in - 8, tmp + 8, 8 * sizeof(int16_t)); + in -= tmp[5] / sizeof(int16_t); + } + in -= 4; + pitch_accumulator = (uint16_t)tmp[4]; + memcpy(in, tmp, 4 * sizeof(int16_t)); + + do { + for (i = 0; i < 8; i++) { + tbl = resample_table[pitch_accumulator * 64 >> 16]; + sample = ((in[0] * tbl[0] + 0x4000) >> 15) + + ((in[1] * tbl[1] + 0x4000) >> 15) + + ((in[2] * tbl[2] + 0x4000) >> 15) + + ((in[3] * tbl[3] + 0x4000) >> 15); + *out++ = clamp16(sample); + + pitch_accumulator += (pitch << 1); + in += pitch_accumulator >> 16; + pitch_accumulator %= 0x10000; + } + nbytes -= 8 * sizeof(int16_t); + } while (nbytes > 0); + + state[4] = (int16_t)pitch_accumulator; + memcpy(state, in, 4 * sizeof(int16_t)); + i = (in - in_initial + 4) & 7; + in -= i; + if (i != 0) { + i = -8 - i; + } + state[5] = i; + memcpy(state + 8, in, 8 * sizeof(int16_t)); +} + +void aEnvSetup1Impl(uint8_t initial_vol_wet, uint16_t rate_wet, uint16_t rate_left, uint16_t rate_right) { + rspa.vol_wet = (uint16_t)(initial_vol_wet << 8); + rspa.rate_wet = rate_wet; + rspa.rate[0] = rate_left; + rspa.rate[1] = rate_right; +} + +void aEnvSetup2Impl(uint16_t initial_vol_left, uint16_t initial_vol_right) { + rspa.vol[0] = initial_vol_left; + rspa.vol[1] = initial_vol_right; +} + +void aEnvMixerImpl(uint16_t in_addr, uint16_t n_samples, bool swap_reverb, + bool neg_3, bool neg_2, + bool neg_left, bool neg_right, + int32_t wet_dry_addr, u32 unk) +{ + int16_t *in = BUF_S16(in_addr); + int16_t *dry[2] = {BUF_S16(((wet_dry_addr >> 24) & 0xFF) << 4), BUF_S16(((wet_dry_addr >> 16) & 0xFF) << 4)}; + int16_t *wet[2] = {BUF_S16(((wet_dry_addr >> 8) & 0xFF) << 4), BUF_S16(((wet_dry_addr) & 0xFF) << 4)}; + int16_t negs[4] = {neg_left ? -1 : 0, neg_right ? -1 : 0, neg_3 ? -4 : 0, neg_2 ? -2 : 0}; + int swapped[2] = {swap_reverb ? 1 : 0, swap_reverb ? 0 : 1}; + int n = ROUND_UP_16(n_samples); + + uint16_t vols[2] = {rspa.vol[0], rspa.vol[1]}; + uint16_t rates[2] = {rspa.rate[0], rspa.rate[1]}; + uint16_t vol_wet = rspa.vol_wet; + uint16_t rate_wet = rspa.rate_wet; + + do { + for (int i = 0; i < 8; i++) { + int16_t samples[2] = {*in, *in}; in++; + for (int j = 0; j < 2; j++) { + samples[j] = (samples[j] * vols[j] >> 16) ^ negs[j]; + } + for (int j = 0; j < 2; j++) { + *dry[j] = clamp16(*dry[j] + samples[j]); dry[j]++; + *wet[j] = clamp16(*wet[j] + ((samples[swapped[j]] * vol_wet >> 16) ^ negs[2 + j])); wet[j]++; + } + } + vols[0] += rates[0]; + vols[1] += rates[1]; + vol_wet += rate_wet; + + n -= 8; + } while (n > 0); +} + +void aMixImpl(uint16_t count, int16_t gain, uint16_t in_addr, uint16_t out_addr) { + int nbytes = ROUND_UP_32(ROUND_DOWN_16(count << 4)); + int16_t *in = BUF_S16(in_addr); + int16_t *out = BUF_S16(out_addr); + int i; + int32_t sample; + + if (gain == -0x8000) { + while (nbytes > 0) { + for (i = 0; i < 16; i++) { + sample = *out - *in++; + *out++ = clamp16(sample); + } + nbytes -= 16 * sizeof(int16_t); + } + } + + while (nbytes > 0) { + for (i = 0; i < 16; i++) { + sample = ((*out * 0x7fff + *in++ * gain) + 0x4000) >> 15; + *out++ = clamp16(sample); + } + + nbytes -= 16 * sizeof(int16_t); + } +} + +void aS8DecImpl(uint8_t flags, ADPCM_STATE state) { + uint8_t *in = BUF_U8(rspa.in); + int16_t *out = BUF_S16(rspa.out); + int nbytes = ROUND_UP_32(rspa.nbytes); + if (flags & A_INIT) { + memset(out, 0, 16 * sizeof(int16_t)); + } else if (flags & A_LOOP) { + memcpy(out, rspa.adpcm_loop_state, 16 * sizeof(int16_t)); + } else { + memcpy(out, state, 16 * sizeof(int16_t)); + } + out += 16; + + while (nbytes > 0) { + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + *out++ = (int16_t)(*in++ << 8); + + nbytes -= 16 * sizeof(int16_t); + } + + memcpy(state, out - 16, 16 * sizeof(int16_t)); +} + +void aAddMixerImpl(uint16_t count, uint16_t in_addr, uint16_t out_addr) { + int16_t *in = BUF_S16(in_addr); + int16_t *out = BUF_S16(out_addr); + int nbytes = ROUND_UP_64(ROUND_DOWN_16(count)); + + do { + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + *out = clamp16(*out + *in++); out++; + + nbytes -= 16 * sizeof(int16_t); + } while (nbytes > 0); +} + +void aDuplicateImpl(uint16_t count, uint16_t in_addr, uint16_t out_addr) { + uint8_t* in = BUF_U8(in_addr); + uint8_t *out = BUF_U8(out_addr); + + uint8_t tmp[128]; + memcpy(tmp, in, 128); + do { + memcpy(out, tmp, 128); + out += 128; + } while (count-- > 0); +} + +void aResampleZohImpl(uint16_t pitch, uint16_t start_fract) { + int16_t *in = BUF_S16(rspa.in); + int16_t *out = BUF_S16(rspa.out); + int nbytes = ROUND_UP_8(rspa.nbytes); + uint32_t pos = start_fract; + uint32_t pitch_add = pitch << 2; + + do { + *out++ = in[pos >> 17]; pos += pitch_add; + *out++ = in[pos >> 17]; pos += pitch_add; + *out++ = in[pos >> 17]; pos += pitch_add; + *out++ = in[pos >> 17]; pos += pitch_add; + + nbytes -= 4 * sizeof(int16_t); + } while (nbytes > 0); +} + +void aInterlImpl(uint16_t in_addr, uint16_t out_addr, uint16_t n_samples) { + int16_t *in = BUF_S16(in_addr); + int16_t *out = BUF_S16(out_addr); + int n = ROUND_UP_8(n_samples); + + do { + *out++ = *in++; in++; + *out++ = *in++; in++; + *out++ = *in++; in++; + *out++ = *in++; in++; + *out++ = *in++; in++; + *out++ = *in++; in++; + *out++ = *in++; in++; + *out++ = *in++; in++; + + n -= 8; + } while (n > 0); +} + +void aFilterImpl(uint8_t flags, uint16_t count_or_buf, int16_t *state_or_filter) { + if (flags > A_INIT) { + rspa.filter_count = ROUND_UP_16(count_or_buf); + memcpy(rspa.filter, state_or_filter, sizeof(rspa.filter)); + } else { + int16_t tmp[16], tmp2[8]; + int count = rspa.filter_count; + int16_t *buf = BUF_S16(count_or_buf); + + if (flags == A_INIT) { +#ifndef __clang__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmemset-elt-size" +#endif + memset(tmp, 0, 8 * sizeof(int16_t)); +#ifndef __clang__ +#pragma GCC diagnostic pop +#endif + memset(tmp2, 0, 8 * sizeof(int16_t)); + } else { + memcpy(tmp, state_or_filter, 8 * sizeof(int16_t)); + memcpy(tmp2, state_or_filter + 8, 8 * sizeof(int16_t)); + } + + for (int i = 0; i < 8; i++) { + rspa.filter[i] = (tmp2[i] + rspa.filter[i]) / 2; + } + + do { + memcpy(tmp + 8, buf, 8 * sizeof(int16_t)); + for (int i = 0; i < 8; i++) { + int64_t sample = 0x4000; // round term + for (int j = 0; j < 8; j++) { + sample += tmp[i + j] * rspa.filter[7 - j]; + } + buf[i] = clamp16((int32_t)(sample >> 15)); + } + memcpy(tmp, tmp + 8, 8 * sizeof(int16_t)); + + buf += 8; + count -= 8 * sizeof(int16_t); + } while (count > 0); + + memcpy(state_or_filter, tmp, 8 * sizeof(int16_t)); + memcpy(state_or_filter + 8, rspa.filter, 8 * sizeof(int16_t)); + } +} + +void aHiLoGainImpl(uint8_t g, uint16_t count, uint16_t addr) { + int16_t *samples = BUF_S16(addr); + int nbytes = ROUND_UP_32(count); + + do { + *samples = clamp16((*samples * g) >> 4); samples++; + *samples = clamp16((*samples * g) >> 4); samples++; + *samples = clamp16((*samples * g) >> 4); samples++; + *samples = clamp16((*samples * g) >> 4); samples++; + *samples = clamp16((*samples * g) >> 4); samples++; + *samples = clamp16((*samples * g) >> 4); samples++; + *samples = clamp16((*samples * g) >> 4); samples++; + *samples = clamp16((*samples * g) >> 4); samples++; + + nbytes -= 8; + } while (nbytes > 0); +} + +void aUnkCmd3Impl(uint16_t a, uint16_t b, uint16_t c) { +} + +void aUnkCmd19Impl(uint8_t f, uint16_t count, uint16_t out_addr, uint16_t in_addr) { + int nbytes = ROUND_UP_64(count); + int16_t *in = BUF_S16(in_addr + f); + int16_t *out = BUF_S16(out_addr); + int16_t tbl[32]; + + memcpy(tbl, in, 32 * sizeof(int16_t)); + do { + for (int i = 0; i < 32; i++) { + out[i] = clamp16(out[i] * tbl[i]); + } + out += 32; + nbytes -= 32 * sizeof(int16_t); + } while (nbytes > 0); +} \ No newline at end of file diff --git a/src/audio/mixer.h b/src/audio/mixer.h new file mode 100644 index 00000000..674c9589 --- /dev/null +++ b/src/audio/mixer.h @@ -0,0 +1,88 @@ +#pragma once + +#include +#include +#include "libultraship/libultra/abi.h" + +#undef aSegment +#undef aClearBuffer +#undef aSetBuffer +#undef aLoadBuffer +#undef aSaveBuffer +#undef aDMEMMove +#undef aMix +#undef aEnvMixer +#undef aResample +#undef aInterleave +#undef aSetVolume +#undef aSetVolume32 +#undef aSetLoop +#undef aLoadADPCM +#undef aADPCMdec +#undef aS8Dec +#undef aAddMixer +#undef aDuplicate +#undef aDMEMMove2 +#undef aResampleZoh +#undef aEnvSetup1 +#undef aEnvSetup2 +#undef aFilter +#undef aHiLoGain +#undef aInterl +#undef aUnkCmd3 +#undef aUnkCmd19 + +void aClearBufferImpl(uint16_t addr, int nbytes); +void aLoadBufferImpl(const void* source_addr, uint16_t dest_addr, uint16_t nbytes); +void aSaveBufferImpl(uint16_t source_addr, int16_t* dest_addr, uint16_t nbytes); +void aLoadADPCMImpl(int num_entries_times_16, const int16_t* book_source_addr); +void aSetBufferImpl(uint8_t flags, uint16_t in, uint16_t out, uint16_t nbytes); +void aInterleaveImpl(uint16_t dest, uint16_t left, uint16_t right, uint16_t c); +void aDMEMMoveImpl(uint16_t in_addr, uint16_t out_addr, int nbytes); +void aSetLoopImpl(ADPCM_STATE* adpcm_loop_state); +void aADPCMdecImpl(uint8_t flags, ADPCM_STATE state); +void aResampleImpl(uint8_t flags, uint16_t pitch, RESAMPLE_STATE state); +void aEnvSetup1Impl(uint8_t initial_vol_wet, uint16_t rate_wet, uint16_t rate_left, uint16_t rate_right); +void aEnvSetup2Impl(uint16_t initial_vol_left, uint16_t initial_vol_right); +void aEnvMixerImpl(uint16_t in_addr, uint16_t n_samples, bool swap_reverb, bool neg_3, bool neg_2, bool neg_left, + bool neg_right, int32_t wet_dry_addr, u32 unk); +void aMixImpl(uint16_t count, int16_t gain, uint16_t in_addr, uint16_t out_addr); +void aS8DecImpl(uint8_t flags, ADPCM_STATE state); +void aAddMixerImpl(uint16_t count, uint16_t in_addr, uint16_t out_addr); +void aDuplicateImpl(uint16_t count, uint16_t in_addr, uint16_t out_addr); +void aResampleZohImpl(uint16_t pitch, uint16_t start_fract); +void aInterlImpl(uint16_t in_addr, uint16_t out_addr, uint16_t n_samples); +void aFilterImpl(uint8_t flags, uint16_t count_or_buf, int16_t* state_or_filter); +void aHiLoGainImpl(uint8_t g, uint16_t count, uint16_t addr); +void aUnkCmd3Impl(uint16_t a, uint16_t b, uint16_t c); +void aUnkCmd19Impl(uint8_t f, uint16_t count, uint16_t out_addr, uint16_t in_addr); + +#define aSegment(pkt, s, b) \ + do { \ + } while (0) +#define aClearBuffer(pkt, d, c) aClearBufferImpl(d, c) +#define aLoadBuffer(pkt, s, d, c) aLoadBufferImpl(s, d, c) +#define aSaveBuffer(pkt, s, d, c) aSaveBufferImpl(s, d, c) +#define aLoadADPCM(pkt, c, d) aLoadADPCMImpl(c, d) +#define aSetBuffer(pkt, f, i, o, c) aSetBufferImpl(f, i, o, c) +#define aInterleave(pkt, o, l, r, c) aInterleaveImpl(o, l, r, c) +#define aDMEMMove(pkt, i, o, c) aDMEMMoveImpl(i, o, c) +#define aSetLoop(pkt, a) aSetLoopImpl(a) +#define aADPCMdec(pkt, f, s) aADPCMdecImpl(f, s) +#define aResample(pkt, f, p, s) aResampleImpl(f, p, s) +#define aEnvSetup1(pkt, initialVolReverb, rampReverb, rampLeft, rampRight) \ + aEnvSetup1Impl(initialVolReverb, rampReverb, rampLeft, rampRight) +#define aEnvSetup2(pkt, initialVolLeft, initialVolRight) aEnvSetup2Impl(initialVolLeft, initialVolRight) +#define aEnvMixer(pkt, inBuf, nSamples, swapReverb, negLeft, negRight, dryLeft, dryRight, wetLeft, wetRight) \ + aEnvMixerImpl(inBuf, nSamples, swapReverb, negLeft, negRight, dryLeft, dryRight, wetLeft, wetRight) +#define aMix(pkt, c, g, i, o) aMixImpl(c, g, i, o) +#define aS8Dec(pkt, f, s) aS8DecImpl(f, s) +#define aAddMixer(pkt, s, d, c, x) aAddMixerImpl(s, d, c) +#define aDuplicate(pkt, s, d, c) aDuplicateImpl(s, d, c) +#define aDMEMMove2(pkt, t, i, o, c) aDMEMMove2Impl(t, i, o, c) +#define aResampleZoh(pkt, pitch, startFract) aResampleZohImpl(pitch, startFract) +#define aInterl(pkt, dmemi, dmemo, count) aInterlImpl(dmemi, dmemo, count) +#define aFilter(pkt, f, countOrBuf, addr) aFilterImpl(f, countOrBuf, addr) +#define aHiLoGain(pkt, g, buflen, i, a4) aHiLoGainImpl(g, buflen, i) +#define aUnkCmd3(pkt, a1, a2, a3) aUnkCmd3Impl(a1, a2, a3) +#define aUnkCmd19(pkt, a1, a2, a3, a4) aUnkCmd19Impl(a1, a2, a3, a4) \ No newline at end of file diff --git a/src/buffers.c b/src/buffers.c index 2c55c737..c989a3f2 100644 --- a/src/buffers.c +++ b/src/buffers.c @@ -3,7 +3,7 @@ u8 gOSYieldData[OS_YIELD_DATA_SIZE]; FrameBuffer gZBuffer; // z buffer u8 gTaskOutputBuffer[0x30000]; -u8 gAudioHeap[0xB0000]; +u8 gAudioHeap[0xB0000 * 2]; u16 gTextureRenderBuffer[0x3C40]; u16 gFillBuffer[3 * SCREEN_WIDTH]; FrameBuffer gFrameBuffers[3]; diff --git a/src/libultra/io/aisetfreq.c b/src/libultra/io/aisetfreq.c index 7d41f96d..8876c449 100644 --- a/src/libultra/io/aisetfreq.c +++ b/src/libultra/io/aisetfreq.c @@ -3,6 +3,7 @@ s32 osViClock2 = 0x02E6D354; s32 osAiSetFrequency(u32 freq) { + return 32000; register u32 a1; register s32 a2; register float ftmp; diff --git a/src/overlays/ovl_menu/fox_option.c b/src/overlays/ovl_menu/fox_option.c index 446e2b21..122f26e3 100644 --- a/src/overlays/ovl_menu/fox_option.c +++ b/src/overlays/ovl_menu/fox_option.c @@ -13,6 +13,7 @@ #include "assets/ast_option.h" #include "assets/ast_vs_menu.h" #include "assets/ast_map.h" +#include "port/interpolation/FrameInterpolation.h" extern s32 gRankingTeamAlive[][3]; extern Gfx gMapVenomCloudDL[]; diff --git a/src/overlays/ovl_menu/fox_title.c b/src/overlays/ovl_menu/fox_title.c index a75456e6..5b62ecfb 100644 --- a/src/overlays/ovl_menu/fox_title.c +++ b/src/overlays/ovl_menu/fox_title.c @@ -10,6 +10,7 @@ #include "fox_title.h" #include "assets/ast_title.h" #include "assets/ast_text.h" +#include "port/interpolation/FrameInterpolation.h" f32 D_menu_801B7BB0; f32 D_menu_801B7BB4; diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp index 28a24845..f140d224 100644 --- a/src/port/Engine.cpp +++ b/src/port/Engine.cpp @@ -24,6 +24,7 @@ #include #include #include +#include "audio/GameAudio.h" #include #include @@ -36,6 +37,7 @@ extern uint16_t gFPS; float gInterpolationStep = 0.0f; #include #include +void AudioThread_CreateNextAudioBuffer(int16_t *samples, uint32_t num_samples); } GameEngine* GameEngine::Instance; @@ -85,6 +87,7 @@ GameEngine::GameEngine() { void GameEngine::Create(){ const auto instance = Instance = new GameEngine(); + instance->AudioInit(); GameUI::SetupGuiElements(); #if defined(__SWITCH__) || defined(__WIIU__) CVarRegisterInteger("gControlNav", 1); // always enable controller nav on switch/wii u @@ -114,6 +117,64 @@ void GameEngine::StartFrame() const{ this->context->GetWindow()->StartFrame(); } +void GameEngine::HandleAudioThread(){ + while (audio.running) { + { + std::unique_lock Lock(audio.mutex); + while (!audio.processing && audio.running) { + audio.cv_to_thread.wait(Lock); + } + if (!audio.running) { + break; + } + } + std::unique_lock Lock(audio.mutex); + int samples_left = AudioPlayerBuffered(); + u32 num_audio_samples = samples_left < AudioPlayerGetDesiredBuffered() ? SAMPLES_HIGH : SAMPLES_LOW; + s16 audio_buffer[SAMPLES_PER_FRAME]; + for (int i = 0; i < NUM_AUDIO_CHANNELS; i++) { + AudioThread_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * 2), num_audio_samples); + } + AudioPlayerPlayFrame((u8 *) audio_buffer, 2 * num_audio_samples * 4); + audio.processing = false; + audio.cv_from_thread.notify_one(); + } +} + +void GameEngine::StartAudioFrame(){ + { + std::unique_lock Lock(audio.mutex); + audio.processing = true; + } + audio.cv_to_thread.notify_one(); +} + +void GameEngine::EndAudioFrame(){ + { + std::unique_lock Lock(audio.mutex); + while (audio.processing) { + audio.cv_from_thread.wait(Lock); + } + } +} + +void GameEngine::AudioInit() { + if (!audio.running) { + audio.running = true; + audio.thread = std::thread(HandleAudioThread); + } +} + +void GameEngine::AudioExit() { + { + std::unique_lock lock(audio.mutex); + audio.running = false; + } + audio.cv_to_thread.notify_all(); + // Wait until the audio thread quit + audio.thread.join(); +} + void GameEngine::RunCommands(Gfx* Commands, const std::vector>& mtx_replacements) { for (const auto& m : mtx_replacements) { gfx_run(Commands, m); diff --git a/src/port/Engine.h b/src/port/Engine.h index dd83c801..4f12a326 100644 --- a/src/port/Engine.h +++ b/src/port/Engine.h @@ -23,6 +23,11 @@ class GameEngine { GameEngine(); static void Create(); void StartFrame() const; + static void HandleAudioThread(); + static void StartAudioFrame(); + static void EndAudioFrame(); + static void AudioInit(); + static void AudioExit(); static void RunCommands(Gfx* Commands, const std::vector>& mtx_replacements); void ProcessFrame(void (*run_one_game_iter)()) const; static void Destroy(); diff --git a/src/port/Game.cpp b/src/port/Game.cpp index f0e6a226..63f15981 100644 --- a/src/port/Game.cpp +++ b/src/port/Game.cpp @@ -10,6 +10,7 @@ extern "C" { void Main_ThreadEntry(void* arg); void Lib_FillScreen(u8 setFill); void Graphics_ThreadUpdate(); + void AudioThread_CreateTask(); } extern "C" @@ -20,12 +21,12 @@ void Graphics_PushFrame(Gfx* data) { extern "C" void Timer_Update(); void push_frame() { - // GameEngine::StartAudioFrame(); + GameEngine::StartAudioFrame(); GameEngine::Instance->StartFrame(); Graphics_ThreadUpdate(); Timer_Update(); // thread5_iteration(); - // GameEngine::EndAudioFrame(); + GameEngine::EndAudioFrame(); } #ifdef _WIN32 diff --git a/src/port/audio/GameAudio.h b/src/port/audio/GameAudio.h new file mode 100644 index 00000000..5626acf8 --- /dev/null +++ b/src/port/audio/GameAudio.h @@ -0,0 +1,10 @@ +#pragma once +#include +#include +static struct { + std::thread thread; + std::condition_variable cv_to_thread, cv_from_thread; + std::mutex mutex; + bool running; + bool processing; +} audio; \ No newline at end of file diff --git a/src/sys/sys_main.c b/src/sys/sys_main.c index 5f382966..867f0d74 100644 --- a/src/sys/sys_main.c +++ b/src/sys/sys_main.c @@ -105,8 +105,9 @@ void Main_Initialize(void) { void Audio_ThreadEntry(void* arg0) { // SPTask* task; - // AudioLoad_Init(); - // Audio_InitSounds(); + AudioLoad_Init(); + Audio_InitSounds(); + // task = AudioThread_CreateTask(); // if (task != NULL) { // task->msgQueue = &gAudioTaskMsgQueue; @@ -383,7 +384,7 @@ void Main_ThreadEntry(void* arg0) { u32 mesg; // LTODO: Implement audio - // Audio_ThreadEntry(NULL); + Audio_ThreadEntry(NULL); Graphics_ThreadEntry(NULL); Controller_Init(); diff --git a/tools/Torch b/tools/Torch index 29857867..ff8c2d13 160000 --- a/tools/Torch +++ b/tools/Torch @@ -1 +1 @@ -Subproject commit 298578677e28ef1fe0d3a425f2856930c6cc4ef1 +Subproject commit ff8c2d13c71069f3117faf42ed25f0816229a872