1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
|
/**
* @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 "array_count.h"
#include "audiomgr.h"
#include "printf.h"
#include "regs.h"
#include "speed_meter.h"
#include "translation.h"
#include "dma.h"
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 (R_AUDIOMGR_ACTIVITY_LEVEL > AUDIOMGR_ACTIVITY_LEVEL_ALL) {
// 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->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 (R_AUDIOMGR_ACTIVITY_LEVEL >= AUDIOMGR_ACTIVITY_LEVEL_NO_UPDATE) {
// Skip update, no rsp task produced
rspTask = NULL;
} else {
rspTask = AudioThread_Update();
}
gAudioThreadUpdateTimeAcc += osGetTime() - gAudioThreadUpdateTimeStart;
gAudioThreadUpdateTimeStart = 0;
if (audioMgr->rspTask != NULL) {
// 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) {
PRINTF(
T("オーディオマネージャが OS_SC_PRE_NMI_MSG を受け取りました\n", "Audio manager received OS_SC_PRE_NMI_MSG\n"));
Audio_PreNMI();
}
void AudioMgr_ThreadEntry(void* arg) {
AudioMgr* audioMgr = (AudioMgr*)arg;
IrqMgrClient irqClient;
s16* msg = NULL;
PRINTF(T("オーディオマネージャスレッド実行開始\n", "Start running audio manager thread\n"));
// Initialize audio driver
Audio_Init();
AudioLoad_SetDmaHandler(DmaMgr_AudioDmaHandler);
Audio_InitSound();
// 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
for (;;) {
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;
}
}
}
/**
* 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) {
bzero(audioMgr, sizeof(AudioMgr));
audioMgr->sched = sched;
audioMgr->irqMgr = irqMgr;
audioMgr->rspTask = NULL;
#if PLATFORM_N64
R_AUDIOMGR_ACTIVITY_LEVEL = AUDIOMGR_ACTIVITY_LEVEL_NO_RSP;
#endif
osCreateMesgQueue(&audioMgr->taskDoneQueue, &audioMgr->taskDoneMsg, 1);
osCreateMesgQueue(&audioMgr->interruptQueue, audioMgr->interruptMsgBuf, ARRAY_COUNT(audioMgr->interruptMsgBuf));
osCreateMesgQueue(&audioMgr->initQueue, &audioMgr->initMsg, 1);
// 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);
}
|