From 5ce4670fd19db356a9a31756a57428aab2978e0e Mon Sep 17 00:00:00 2001 From: Tharo <17233964+Thar0@users.noreply.github.com> Date: Thu, 30 Nov 2023 21:22:30 +0000 Subject: [PATCH] Documentation for audio_thread_manager.c (#1562) * Documentation for audio_thread_manager.c * Fixes * Move AudioTask back to z64audio.h and include in audiomgr.h, adjust bug comment * Adjust AudioMgrDebugLevel enum --- include/audiomgr.h | 33 ++++++++++++ include/functions.h | 7 --- include/regs.h | 1 + include/z64.h | 15 +----- include/z64audio.h | 14 ++--- src/code/audio_thread_manager.c | 90 +++++++++++++++++++++++++++------ src/code/main.c | 2 +- 7 files changed, 118 insertions(+), 44 deletions(-) create mode 100644 include/audiomgr.h diff --git a/include/audiomgr.h b/include/audiomgr.h new file mode 100644 index 0000000000..036192ce7c --- /dev/null +++ b/include/audiomgr.h @@ -0,0 +1,33 @@ +#ifndef AUDIOMGR_H +#define AUDIOMGR_H + +#include "sched.h" +#include "z64audio.h" + +typedef enum { + /* 0 */ AUDIOMGR_DEBUG_LEVEL_NONE, + /* 1 */ AUDIOMGR_DEBUG_LEVEL_NO_RSP, + /* 2 */ AUDIOMGR_DEBUG_LEVEL_NO_UPDATE +} AudioMgrDebugLevel; + +typedef struct { + /* 0x0000 */ IrqMgr* irqMgr; + /* 0x0004 */ Scheduler* sched; + /* 0x0008 */ OSScTask audioTask; + /* 0x0070 */ AudioTask* rspTask; + /* 0x0074 */ OSMesgQueue interruptQueue; + /* 0x008C */ OSMesg interruptMsgBuf[8]; + /* 0x00AC */ OSMesgQueue taskDoneQueue; + /* 0x00C4 */ OSMesg taskDoneMsg; + /* 0x00C8 */ OSMesgQueue initQueue; + /* 0x00E0 */ OSMesg initMsg; + /* 0x00E8 */ OSThread thread; +} AudioMgr; // size = 0x298 + +void AudioMgr_Init(AudioMgr* audioMgr, void* stack, OSPri pri, OSId id, Scheduler* sched, IrqMgr* irqMgr); + +void AudioMgr_WaitForInit(AudioMgr* audioMgr); + +void AudioMgr_StopAllSfx(void); + +#endif diff --git a/include/functions.h b/include/functions.h index 1fa82e044f..62ca9d5003 100644 --- a/include/functions.h +++ b/include/functions.h @@ -1295,13 +1295,6 @@ void func_800C213C(PreRender* this, Gfx** gfxP); void PreRender_RestoreFramebuffer(PreRender* this, Gfx** gfxP); void PreRender_CopyImageRegion(PreRender* this, Gfx** gfxP); void PreRender_ApplyFilters(PreRender* this); -void AudioMgr_StopAllSfx(void); -void func_800C3C80(AudioMgr* audioMgr); -void AudioMgr_HandleRetrace(AudioMgr* audioMgr); -void AudioMgr_HandlePreNMI(AudioMgr* audioMgr); -void AudioMgr_ThreadEntry(void* arg0); -void AudioMgr_Unlock(AudioMgr* audioMgr); -void AudioMgr_Init(AudioMgr* audioMgr, void* stack, OSPri pri, OSId id, Scheduler* sched, IrqMgr* irqMgr); void GameState_FaultPrint(void); void GameState_SetFBFilter(Gfx** gfxP); void GameState_DrawInputDisplay(u16 input, Gfx** gfxP); diff --git a/include/regs.h b/include/regs.h index 7806afc966..f9f1df63c8 100644 --- a/include/regs.h +++ b/include/regs.h @@ -48,6 +48,7 @@ #define R_ENV_TIME_SPEED_OLD REG(15) // Most likely used during development. Unused in the final game. #define R_RUN_SPEED_LIMIT REG(45) #define R_ENABLE_ARENA_DBG SREG(0) +#define R_AUDIOMGR_DEBUG_LEVEL SREG(20) #define R_ROOM_IMAGE_NODRAW_FLAGS SREG(25) #define R_ROOM_BG2D_FORCE_SCALEBG SREG(26) #define R_UPDATE_RATE SREG(30) diff --git a/include/z64.h b/include/z64.h index 572afb4d1d..f99078d985 100644 --- a/include/z64.h +++ b/include/z64.h @@ -4,6 +4,7 @@ #include "ultra64.h" #include "ultra64/gs2dex.h" #include "attributes.h" +#include "audiomgr.h" #include "z64save.h" #include "z64light.h" #include "z64bgcheck.h" @@ -695,20 +696,6 @@ typedef struct { /* 0x10 */ u8 data[1]; } Yaz0Header; // size = 0x10 ("data" is not part of the header) -typedef struct { - /* 0x0000 */ IrqMgr* irqMgr; - /* 0x0004 */ Scheduler* sched; - /* 0x0008 */ OSScTask audioTask; - /* 0x0070 */ AudioTask* rspTask; - /* 0x0074 */ OSMesgQueue interruptQueue; - /* 0x008C */ OSMesg interruptMsgBuf[8]; - /* 0x00AC */ OSMesgQueue taskQueue; - /* 0x00C4 */ OSMesg taskMsgBuf[1]; - /* 0x00C8 */ OSMesgQueue lockQueue; - /* 0x00E0 */ OSMesg lockMsgBuf[1]; - /* 0x00E8 */ OSThread thread; -} AudioMgr; // size = 0x298 - struct ArenaNode; typedef struct Arena { diff --git a/include/z64audio.h b/include/z64audio.h index d2f3d6360f..7e6f4e40bf 100644 --- a/include/z64audio.h +++ b/include/z64audio.h @@ -814,13 +814,6 @@ typedef struct { /* 0x10 */ AudioTableEntry entries[1]; // (dynamic size) } AudioTable; // size >= 0x20 -typedef struct { - /* 0x00 */ OSTask task; - /* 0x40 */ OSMesgQueue* msgQueue; - /* 0x44 */ void* unk_44; // probably a message that gets unused. - /* 0x48 */ char unk_48[0x8]; -} AudioTask; // size = 0x50 - typedef struct { /* 0x00 */ u8* ramAddr; /* 0x04 */ u32 devAddr; @@ -831,6 +824,13 @@ typedef struct { /* 0x0E */ u8 ttl; // duration after which the DMA can be discarded } SampleDma; // size = 0x10 +typedef struct { + /* 0x00 */ OSTask task; + /* 0x40 */ OSMesgQueue* msgQueue; + /* 0x44 */ void* unk_44; // probably a message that gets unused. + /* 0x48 */ char unk_48[0x8]; +} AudioTask; // size = 0x50 + typedef struct { /* 0x0000 */ char unk_0000; /* 0x0001 */ s8 numSynthesisReverbs; diff --git a/src/code/audio_thread_manager.c b/src/code/audio_thread_manager.c index 01bf54397d..1593411537 100644 --- a/src/code/audio_thread_manager.c +++ b/src/code/audio_thread_manager.c @@ -1,82 +1,134 @@ +/** + * @file audio_thread_manager.c + * + * This file implements basic thread features for the audio driver. It manages updating the driver on vertical retrace + * and sending the audio rsp tasks generated by the driver to the task scheduler. + */ + #include "global.h" -void func_800C3C80(AudioMgr* audioMgr) { +void AudioMgr_NotifyTaskDone(AudioMgr* audioMgr) { AudioTask* task = audioMgr->rspTask; + // If the audio rsp task has a message queue to receive task done notifications, post a message to it. if (audioMgr->rspTask->msgQueue != NULL) { osSendMesg(task->msgQueue, NULL, OS_MESG_BLOCK); } } +/** + * Handle retrace event. + * Update the audio driver and schedule audio rsp tasks. + */ void AudioMgr_HandleRetrace(AudioMgr* audioMgr) { AudioTask* rspTask; - if (SREG(20) > 0) { + if (R_AUDIOMGR_DEBUG_LEVEL > AUDIOMGR_DEBUG_LEVEL_NONE) { + // Inhibit audio rsp task processing audioMgr->rspTask = NULL; } + if (audioMgr->rspTask != NULL) { + // Got an rsp task to process, build the OSScTask and forward it to the scheduler to run + audioMgr->audioTask.next = NULL; audioMgr->audioTask.flags = OS_SC_NEEDS_RSP; audioMgr->audioTask.framebuffer = NULL; audioMgr->audioTask.list = audioMgr->rspTask->task; - audioMgr->audioTask.msgQueue = &audioMgr->taskQueue; + audioMgr->audioTask.msgQueue = &audioMgr->taskDoneQueue; audioMgr->audioTask.msg = NULL; osSendMesg(&audioMgr->sched->cmdQueue, (OSMesg)&audioMgr->audioTask, OS_MESG_BLOCK); Sched_Notify(audioMgr->sched); } + // Update the audio driver + gAudioThreadUpdateTimeStart = osGetTime(); - if (SREG(20) >= 2) { + + if (R_AUDIOMGR_DEBUG_LEVEL >= AUDIOMGR_DEBUG_LEVEL_NO_UPDATE) { + // Skip update, no rsp task produced rspTask = NULL; } else { rspTask = func_800E4FE0(); } + gAudioThreadUpdateTimeAcc += osGetTime() - gAudioThreadUpdateTimeStart; gAudioThreadUpdateTimeStart = 0; if (audioMgr->rspTask != NULL) { - osRecvMesg(&audioMgr->taskQueue, NULL, OS_MESG_BLOCK); - func_800C3C80(audioMgr); + // Wait for the audio rsp task scheduled on the previous retrace to complete. This looks like it should wait + // for the task scheduled on the current retrace, earlier in this function, but since the queue is initially + // filled in AudioMgr_Init this osRecvMesg call doesn't wait for the task scheduler to post a message for the + // most recent task as there is already a message waiting. + osRecvMesg(&audioMgr->taskDoneQueue, NULL, OS_MESG_BLOCK); + // Report task done + //! @bug As the above osRecvMesg is waiting for the previous task to complete rather than the current task, + //! the task done notification is sent to the task done queue for the current task as soon as the previous task + //! is completed, without waiting for the current task. + //! In practice, task done notifications are not used by the audio driver so this is inconsequential. + AudioMgr_NotifyTaskDone(audioMgr); } + // Update rsp task to be scheduled on next retrace audioMgr->rspTask = rspTask; } +/** + * Handle Pre-NMI event. + * Implemented by the audio driver. + * + * @see Audio_PreNMI + */ void AudioMgr_HandlePreNMI(AudioMgr* audioMgr) { // "Audio manager received OS_SC_PRE_NMI_MSG" osSyncPrintf("オーディオマネージャが OS_SC_PRE_NMI_MSG を受け取りました\n"); Audio_PreNMI(); } -void AudioMgr_ThreadEntry(void* arg0) { - AudioMgr* audioMgr = (AudioMgr*)arg0; +void AudioMgr_ThreadEntry(void* arg) { + AudioMgr* audioMgr = (AudioMgr*)arg; IrqMgrClient irqClient; s16* msg = NULL; - osSyncPrintf("オーディオマネージャスレッド実行開始\n"); // "Start running audio manager thread" + // "Start running audio manager thread" + osSyncPrintf("オーディオマネージャスレッド実行開始\n"); + + // Initialize audio driver Audio_Init(); AudioLoad_SetDmaHandler(DmaMgr_AudioDmaHandler); Audio_InitSound(); - osSendMesg(&audioMgr->lockQueue, NULL, OS_MESG_BLOCK); + + // Fill init queue to signal that the audio driver is initialized + osSendMesg(&audioMgr->initQueue, NULL, OS_MESG_BLOCK); + IrqMgr_AddClient(audioMgr->irqMgr, &irqClient, &audioMgr->interruptQueue); + // Spin waiting for events while (true) { osRecvMesg(&audioMgr->interruptQueue, (OSMesg*)&msg, OS_MESG_BLOCK); + switch (*msg) { case OS_SC_RETRACE_MSG: AudioMgr_HandleRetrace(audioMgr); + + // Empty the interrupt queue while (!MQ_IS_EMPTY(&audioMgr->interruptQueue)) { osRecvMesg(&audioMgr->interruptQueue, (OSMesg*)&msg, OS_MESG_BLOCK); + switch (*msg) { case OS_SC_RETRACE_MSG: + // Don't process a retrace more than once in quick succession break; + case OS_SC_PRE_NMI_MSG: + // Always handle Pre-NMI AudioMgr_HandlePreNMI(audioMgr); break; } } break; + case OS_SC_PRE_NMI_MSG: AudioMgr_HandlePreNMI(audioMgr); break; @@ -84,8 +136,15 @@ void AudioMgr_ThreadEntry(void* arg0) { } } -void AudioMgr_Unlock(AudioMgr* audioMgr) { - osRecvMesg(&audioMgr->lockQueue, NULL, OS_MESG_BLOCK); +/** + * Stalls the current thread until the audio thread is sufficiently initialized. + * + * Note this function only works once. After the first call the message that the audio thread posted to the init queue + * will have been removed, subsequent calls to this function will block indefinitely as the audio thread does not refill + * the queue. + */ +void AudioMgr_WaitForInit(AudioMgr* audioMgr) { + osRecvMesg(&audioMgr->initQueue, NULL, OS_MESG_BLOCK); } void AudioMgr_Init(AudioMgr* audioMgr, void* stack, OSPri pri, OSId id, Scheduler* sched, IrqMgr* irqMgr) { @@ -95,11 +154,12 @@ void AudioMgr_Init(AudioMgr* audioMgr, void* stack, OSPri pri, OSId id, Schedule audioMgr->irqMgr = irqMgr; audioMgr->rspTask = NULL; - osCreateMesgQueue(&audioMgr->taskQueue, audioMgr->taskMsgBuf, ARRAY_COUNT(audioMgr->taskMsgBuf)); + osCreateMesgQueue(&audioMgr->taskDoneQueue, &audioMgr->taskDoneMsg, 1); osCreateMesgQueue(&audioMgr->interruptQueue, audioMgr->interruptMsgBuf, ARRAY_COUNT(audioMgr->interruptMsgBuf)); - osCreateMesgQueue(&audioMgr->lockQueue, audioMgr->lockMsgBuf, ARRAY_COUNT(audioMgr->lockMsgBuf)); + osCreateMesgQueue(&audioMgr->initQueue, &audioMgr->initMsg, 1); - osSendMesg(&audioMgr->taskQueue, NULL, OS_MESG_BLOCK); + // Send a message to the task done queue so it is initially full + osSendMesg(&audioMgr->taskDoneQueue, NULL, OS_MESG_BLOCK); osCreateThread(&audioMgr->thread, id, AudioMgr_ThreadEntry, audioMgr, stack, pri); osStartThread(&audioMgr->thread); diff --git a/src/code/main.c b/src/code/main.c index a1fe35ef38..4e633fd343 100644 --- a/src/code/main.c +++ b/src/code/main.c @@ -90,7 +90,7 @@ void Main(void* arg) { StackCheck_Init(&sPadMgrStackInfo, sPadMgrStack, STACK_TOP(sPadMgrStack), 0, 0x100, "padmgr"); PadMgr_Init(&gPadMgr, &sSerialEventQueue, &gIrqMgr, THREAD_ID_PADMGR, THREAD_PRI_PADMGR, STACK_TOP(sPadMgrStack)); - AudioMgr_Unlock(&gAudioMgr); + AudioMgr_WaitForInit(&gAudioMgr); StackCheck_Init(&sGraphStackInfo, sGraphStack, STACK_TOP(sGraphStack), 0, 0x100, "graph"); osCreateThread(&sGraphThread, THREAD_ID_GRAPH, Graph_ThreadEntry, arg, STACK_TOP(sGraphStack), THREAD_PRI_GRAPH);