audrenderer.c
12.9 KB
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
/*
* Audio renderer
*/
#include <ultra64.h>
#include <PR/libaudio.h>
#include <PR/sched.h>
#include "viewer.h"
#include "audrenderer.h"
#define MAX_UPDATES 32
#define MAX_EVENTS 32
#define AUDIO_HEAP_SIZE 200000
#define MAX_VOICES 16
#define EXTRA_SAMPLES 80
#define NUM_OUTPUT_BUFFERS 3 /* Need three of these */
#define OUTPUT_RATE 44100
#define AUD_MAX_MESGS 8
#define QUIT_MSG 10
#define NUM_ACMD_LISTS 2 /* two lists used by this example */
#define MAX_RSP_CMDS 4096 /* max number of commands in any command list. */
/* Mainly dependent on sequences used */
#define FRAME_LAG 1 /* The number of frames to keep a dma buffer. */
/* Increasing this number causes buffers to not */
/* be deleted as quickly. This results in fewer */
/* DMA's but you need more buffers. */
#define AUDIO_STACKSIZE 0x2000
typedef struct {
u32 outputRate;
u32 framesPerField;
u32 maxACMDSize;
} AmConfig;
/**** type define's for structures unique to audiomgr ****/
typedef union {
struct {
short type;
} gen;
struct {
short type;
struct AudioInfo_s *info;
} done;
OSScMsg app;
} AudioMsg;
typedef struct AudioInfo_s {
short *data; /* Output data pointer */
short frameSamples; /* # of samples synthesized in this frame */
OSScTask task; /* scheduler structure */
AudioMsg msg; /* completion message */
} AudioInfo;
typedef struct {
Acmd *ACMDList[NUM_ACMD_LISTS];
AudioInfo *audioInfo[NUM_OUTPUT_BUFFERS];
OSThread thread;
OSMesgQueue audioFrameMsgQ;
OSMesg audioFrameMsgBuf[AUD_MAX_MESGS];
OSMesgQueue audioReplyMsgQ;
OSMesg audioReplyMsgBuf[AUD_MAX_MESGS];
ALGlobals g;
} AMAudioMgr;
#include "sound_table.h"
#include "sound_bank.h"
/**** audio globals ****/
static u8 audioHeap[AUDIO_HEAP_SIZE];
static ALHeap hp;
static ALSndpConfig SPConfig;
static OSScClient client;
static OSMesgQueue *schedCmdQ;
static AMAudioMgr am;
static u64 audioStack[AUDIO_STACKSIZE/sizeof(u64)];
static u32 audFrameCt = 0;
static u32 curAcmdList = 0;
static u32 minFrameSize;
static u32 frameSize;
static u32 maxFrameSize;
static u32 maxRSPCmds;
/**** private routines ****/
static void amMain(void *arg);
static s32 amDMA(s32 addr, s32 len, void *state);
static ALDMAproc amDmaNew(int **state);
static u32 amHandleFrameMsg(AudioInfo *, AudioInfo *);
static void amHandleDoneMsg(AudioInfo *);
static void amCreateAudioMgr(ALSynConfig *c, OSPri priority, AmConfig *amc, OSSched *sc);
static ALSndPlayer *soundPlayer;
static ALSndId *sndId;
static int numSounds;
void clearAudio()
{
int i;
int j;
short *data;
for (i=0; i<NUM_OUTPUT_BUFFERS; i++) {
data = (am.audioInfo[i])->data;
for (j=0; j<(maxFrameSize<<1); j++) {
data[j] = 0;
}
}
}
void playSound(int id, int waitFinish)
{
/*
* Always have to wait till its finished before starting again
*/
while (isPlaying(sndId[id])) ;
alSndpSetSound(soundPlayer, sndId[id]);
alSndpSetPitch(soundPlayer, 1);
alSndpSetVol(soundPlayer, 20000);
alSndpPlay(soundPlayer);
if (waitFinish == AUDIO_WAIT) {
while (!isPlaying(id));
while (isPlaying(id)) ;
}
}
int isPlaying(int id)
{
alSndpSetSound(soundPlayer, sndId[id]);
if (alSndpGetState(soundPlayer) == AL_STOPPED) {
return 0;
}
return 1;
}
void initAudio(OSSched *sc)
{
int i;
ALSndPlayer *sndp;
ALSound *snd;
int numAllocated;
ALBank *sfxBank;
u8 *sfxBankPtr;
ALInstrument *inst;
u32 bankLen;
ALSynConfig c;
AmConfig amc;
ALSndId *idPtr;
alHeapInit(&hp, audioHeap, sizeof(audioHeap));
soundPlayer = alHeapAlloc(&hp, 1, sizeof(ALSndPlayer));
sndp = soundPlayer;
bankLen = sizeof(soundBank);
sfxBankPtr = soundBank;
/*
* Create the Audio Manager
*/
c.maxVVoices = MAX_VOICES;
c.maxPVoices = MAX_VOICES;
c.maxUpdates = MAX_UPDATES;
c.dmaproc = 0; /* audio mgr will fill this in */
c.fxType = AL_FX_SMALLROOM;
c.outputRate = 0; /* audio mgr will fill this in */
c.heap = &hp;
amc.outputRate = OUTPUT_RATE;
amc.framesPerField = NUM_FIELDS;
amc.maxACMDSize = MAX_RSP_CMDS;
amCreateAudioMgr(&c, AUDIO_PRIORITY, &amc, sc);
/*
* Initialize the soundplayer
*/
SPConfig.maxSounds = MAX_VOICES;
SPConfig.maxEvents = MAX_EVENTS;
SPConfig.heap = &hp;
alSndpNew(sndp, &SPConfig);
/*
* Allocate all the sounds
*/
alBnkfNew((ALBankFile *)sfxBankPtr, soundTable);
sfxBank = ((ALBankFile *)sfxBankPtr)->bankArray[0];
/*
* Better make sure number of sounds is at least equal
* to the number of effects
*/
inst = sfxBank->instArray[0];
idPtr = alHeapAlloc(&hp, 1, inst->soundCount*sizeof(ALSndId));
for (i=0, numAllocated = 0; i<inst->soundCount; i++) {
snd = inst->soundArray[i];
if ((idPtr[i] = alSndpAllocate(sndp, snd)) != -1)
numAllocated++;
}
sndId = idPtr;
numSounds = numAllocated;
}
/******************************************************************************
* Audio Manager API
*****************************************************************************/
static void amCreateAudioMgr(ALSynConfig *c, OSPri pri, AmConfig *amc, OSSched *sc)
{
u32 i;
f32 fsize;
c->dmaproc = amDmaNew;
c->outputRate = osAiSetFrequency(amc->outputRate);
/*
* Calculate the frame sample parameters from the
* video field rate and the output rate
*/
fsize = (f32) amc->framesPerField * c->outputRate / (f32) 60;
frameSize = (s32) fsize;
if (frameSize < fsize)
frameSize++;
if (frameSize & 0xf)
frameSize = (frameSize & ~0xf) + 0x10;
minFrameSize = frameSize - 16;
maxFrameSize = frameSize + EXTRA_SAMPLES + 16;
alInit(&am.g, c);
for(i=0;i<NUM_ACMD_LISTS;i++)
am.ACMDList[i] = (Acmd*)alHeapAlloc(c->heap, 1,
amc->maxACMDSize * sizeof(Acmd));
maxRSPCmds = amc->maxACMDSize;
/**** initialize the done messages ****/
for (i = 0; i < NUM_OUTPUT_BUFFERS; i++)
{
am.audioInfo[i] = (AudioInfo *)alHeapAlloc(c->heap, 1,
sizeof(AudioInfo));
am.audioInfo[i]->msg.done.type = OS_SC_DONE_MSG;
am.audioInfo[i]->msg.done.info = am.audioInfo[i];
am.audioInfo[i]->data = alHeapAlloc(c->heap, 1, 4*maxFrameSize);
}
osCreateMesgQueue(&am.audioReplyMsgQ, am.audioReplyMsgBuf, AUD_MAX_MESGS);
osCreateMesgQueue(&am.audioFrameMsgQ, am.audioFrameMsgBuf, AUD_MAX_MESGS);
osScAddClient(sc, &client, &am.audioFrameMsgQ);
schedCmdQ = osScGetCmdQ(sc);
osCreateThread(&am.thread, 3, amMain, 0,
(void *)(audioStack+AUDIO_STACKSIZE/sizeof(u64)), pri);
osStartThread(&am.thread);
}
/******************************************************************************
*
* Audio Manager implementation. This thread wakes up at every retrace,
* and builds an audio task, which it returns to the scheduler, who then
* is responsible for its finally execution on the RSP. Once the task has
* finished execution, the scheduler sends back a message saying the task
* is complete. The audio is triple buffered because the switching to a new
* audio buffer does not occur exactly at the gfx swapbuffer time. With
* 3 buffers you ensure that the program does not destroy data before it is
* played.
*
*****************************************************************************/
static void amMain(void *arg)
{
u32 validTask;
u32 done = 0;
AudioMsg *msg;
AudioInfo *lastInfo = 0;
while (!done)
{
(void) osRecvMesg(&am.audioFrameMsgQ, (OSMesg *)&msg, OS_MESG_BLOCK);
switch (msg->gen.type)
{
case (OS_SC_RETRACE_MSG):
validTask = amHandleFrameMsg(am.audioInfo[audFrameCt % 3],
lastInfo);
if(validTask)
{
/* wait for done message */
osRecvMesg(&am.audioReplyMsgQ, (OSMesg *)&msg,
OS_MESG_BLOCK);
amHandleDoneMsg(msg->done.info);
lastInfo = msg->done.info;
}
break;
case (OS_SC_PRE_NMI_MSG):
/* what should we really do here? quit? ramp down volume? */
break;
case (QUIT_MSG):
done = 1;
break;
default:
break;
}
}
alClose(&am.g);
}
/******************************************************************************
*
* amHandleFrameMsg. Handles the video frame messages and schedules calculation
* of a new set of samples.
*
*****************************************************************************/
static u32 amHandleFrameMsg(AudioInfo *info, AudioInfo *lastInfo)
{
s16 *audioPtr;
Acmd *cmdp;
s32 cmdLen;
int samplesLeft = 0;
OSScTask *t;
audFrameCt++;
audioPtr = (s16 *) osVirtualToPhysical(info->data);
if (lastInfo)
osAiSetNextBuffer(lastInfo->data, lastInfo->frameSamples<<2);
/* calculate how many samples needed for this frame to keep the DAC full */
/* this will vary slightly frame to frame, must recalculate every frame */
samplesLeft = osAiGetLength() >> 2; /* divide by four, to convert bytes */
/* to stereo 16 bit samples */
info->frameSamples = 16 + ((frameSize - samplesLeft + EXTRA_SAMPLES) & ~0xf);
if(info->frameSamples < minFrameSize)
info->frameSamples = minFrameSize;
cmdp = alAudioFrame(am.ACMDList[curAcmdList], &cmdLen, audioPtr,
info->frameSamples);
#ifndef _FINALROM
if (cmdLen > maxRSPCmds) {
PRINTF("Command list to long!\n");
}
#endif
if(cmdLen == 0) /* no task produced, return zero to show no valid task */
return 0;
t = &info->task;
t->next = 0; /* paranoia */
t->msgQ = &am.audioReplyMsgQ; /* reply to when finished */
t->msg = (OSMesg)&info->msg; /* reply with this message */
t->flags = OS_SC_NEEDS_RSP;
t->list.t.data_ptr = (u64 *) am.ACMDList[curAcmdList];
t->list.t.data_size = (cmdp - am.ACMDList[curAcmdList]) * sizeof(Acmd);
t->list.t.type = M_AUDTASK;
t->list.t.ucode_boot = (u64 *)rspbootTextStart;
t->list.t.ucode_boot_size =
((int) rspbootTextEnd - (int) rspbootTextStart);
t->list.t.flags = OS_TASK_DP_WAIT;
t->list.t.ucode = (u64 *) aspMainTextStart;
t->list.t.ucode_data = (u64 *) aspMainDataStart;
t->list.t.ucode_data_size = SP_UCODE_DATA_SIZE;
t->list.t.dram_stack = (u64 *) NULL;
t->list.t.dram_stack_size = 0;
t->list.t.output_buff = (u64 *) NULL;
t->list.t.output_buff_size = 0;
t->list.t.yield_data_ptr = NULL;
t->list.t.yield_data_size = 0;
osSendMesg(schedCmdQ, (OSMesg) t, OS_MESG_BLOCK);
curAcmdList ^= 1; /* swap which acmd list you use each frame */
return 1;
}
/******************************************************************************
*
* amHandleDoneMsg. Really just debugging info in this frame. Checks
* to make sure we completed before we were out of samples.
*
*****************************************************************************/
static void amHandleDoneMsg(AudioInfo *info)
{
s32 samplesLeft;
static int firstTime = 1;
samplesLeft = osAiGetLength()>>2;
if (samplesLeft == 0 && !firstTime)
{
#ifndef _FINALROM
PRINTF("audio: ai out of samples\n");
#endif
firstTime = 0;
}
}
/******************************************************************************
*
* audioDMA. This routine usually handles DMA'ing of samples. In our case
* they are already in memory.
*
*****************************************************************************/
s32 amDMA(s32 addr, s32 len, void *state)
{
return (int) osVirtualToPhysical((u32 *) addr);
}
/******************************************************************************
*
* amDmaNew. Return the address of the DMA routine. For this case we don't do
* DMA, but we need to follow the model.
*
*****************************************************************************/
ALDMAproc amDmaNew(int **state)
{
return amDMA;
}