1 /*---------------------------------------------------------------------------*
2 Project: Revolution PMIC simple demo
3 File: audio.c
4
5 Copyright (C)2008 Nintendo All Rights Reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Log: audio.c,v $
14 Revision 1.4 2008/08/06 01:39:14 carlmu
15 Added graphic demo.
16
17 Revision 1.3 2008/08/04 23:51:51 carlmu
18 Added quit function for audio.
19
20 Revision 1.2 2008/04/23 00:09:19 aka
21 Modified an argument of PMICChangeRate().
22
23 Revision 1.1 2008/01/22 02:50:04 aka
24 initial check-in.
25
26 $NoKeywords: $
27 *---------------------------------------------------------------------------*/
28
29 #include <string.h>
30 #include <revolution.h>
31 #include <revolution/mem.h>
32 #include <revolution/mix.h>
33 #include <revolution/seq.h>
34 #include <revolution/syn.h>
35 #include <revolution/pmic.h>
36
37 #include "audio.h"
38
39 /*---------------------------------------------------------------------------*
40 definitions
41 *---------------------------------------------------------------------------*/
42
43 #define USE_HEADPHONE
44 #undef USE_HEADPHONE
45
46 #define USE_64TAP_SRC
47 //#undef USE_64TAP_SRC
48
49 // audio data
50 #define AUDIO_GM_WT "/axdemo/synth/gm16adpcm.wt"
51 #define AUDIO_GM_PCM "/axdemo/synth/gm16adpcm.pcm"
52 #define AUDIO_MIDI_FILE "/axdemo/midi/candy.mid"
53
54 // audio processing unit
55 #define AUDIO_SAMPLES_PER_FRAME (32 * 3) // 32KHz x 3msec
56 #define AUDIO_BYTES_PER_FRAME (AUDIO_SAMPLES_PER_FRAME * 2 * 2)
57
58 // for voice processing
59 #define STACK_SIZE (256 * 1024) // 256KB
60 #define THREAD_PRIORITY 20
61
62 #define SRC_TAPS 64
63 #define SRC_BUFF_SAMPLES (SRC_TAPS + 16 * 3) // taps + 16KHz x 3msec
64
65 #define PROC_BUFF_SAMPLES (32000 * 10) // 32KHz * 10sec
66
67 typedef struct
68 {
69 s32 samples; // buffer size
70 s32 top; // write ptr
71 s32 buttom; // read ptr
72 s16* buffer; // <- mono
73
74 } PROCInfo;
75
76 #define PROC_VOICE_GAIN 2 // 2bit SLA = + 12dB
77
78 /*---------------------------------------------------------------------------*
79 variables
80 *---------------------------------------------------------------------------*/
81
82 // for AXs
83 static u8* axBuff;
84 static u8* mixBuff;
85 static u8* synBuff;
86 static u8* waveTbl;
87 static u8* pcmData;
88 static u8* midiData;
89 static SEQSEQUENCE sequence;
90
91 // for AI
92 static s16* aiBuff[2];
93 static AIDCallback old_aiCb;
94
95 // for ISO-OUT
96 static s16* isoBuff;
97
98 // for voice processing
99 static OSThread procThread;
100 static u8 procStack[STACK_SIZE];
101 static OSThreadQueue procWait;
102
103 static s16* srcBuff;
104 static s16 srcCoef[2][SRC_TAPS] =
105 {
106 {
107 -23, 10, -11, 12, -10, 7, -1, -6,
108 18, -31, 49, -69, 94, -120, 149, -177,
109 208, -235, 262, -281, 296, -299, 292, -267,
110 223, -149, 37, 140, -426, 962, -2313, 14176,
111 5461, -2586, 1754, -1326, 1049, -842, 678, -540,
112 424, -322, 236, -162, 101, -49, 9, 23,
113 -45, 62, -70, 75, -74, 72, -65, 58,
114 -49, 41, -32, 25, -18, 13, -12, 8,
115 },
116
117 {
118 8, -12, 13, -18, 25, -32, 41, -49,
119 58, -65, 72, -74, 75, -70, 62, -45,
120 23, 9, -49, 101, -162, 236, -322, 424,
121 -540, 678, -842, 1049, -1326, 1754, -2586, 5461,
122 14176, -2313, 962, -426, 140, 37, -149, 223,
123 -267, 292, -299, 296, -281, 262, -235, 208,
124 -177, 149, -120, 94, -69, 49, -31, 18,
125 -6, -1, 7, -10, 12, -11, 10, -23
126 }
127 };
128
129 static PROCInfo procInfo;
130
131 // for voice playback
132 static BOOL playVoice = FALSE;
133
134 /*---------------------------------------------------------------------------*
135 funcs
136 *---------------------------------------------------------------------------*/
137
138 static void audioCb ( void );
139 static u8* loadFile ( char *path, MEMHeapHandle* heap );
140 static void aiCb ( void );
141 static void* voiceProc ( void *param );
142 static s32 fillVoice ( s16* buffer, s32 samples);
143
144 /*---------------------------------------------------------------------------*
145 Name: AUDIOInit
146
147 Description: initialize audio.
148
149 Arguments: heap for mem allocation.
150
151 Returns: TRUE/FALSE
152 *---------------------------------------------------------------------------*/
153
AUDIOInit(MEMHeapHandle * heap,MicFunc procFunc)154 BOOL AUDIOInit(MEMHeapHandle* heap, MicFunc procFunc)
155 {
156 BOOL old;
157
158 //
159 // init AXs.
160 //
161 axBuff = MEMAllocFromExpHeapEx(*heap, AXGetMemorySize (AX_MAX_VOICES), 32);
162 mixBuff = MEMAllocFromExpHeapEx(*heap, MIXGetMemorySize(AX_MAX_VOICES), 32);
163 synBuff = MEMAllocFromExpHeapEx(*heap, SYNGetMemorySize(AX_MAX_VOICES), 32);
164
165 if (!axBuff || !mixBuff || !synBuff)
166 {
167 return FALSE;
168 }
169
170 AIInit(NULL); // initialize AI
171 AXInitSpecifyMem(AX_MAX_VOICES, axBuff); // initialize AX
172 MIXInitSpecifyMem(mixBuff); // initialize mixer
173 SYNInitSpecifyMem(synBuff); // initialize synthesizer
174 SEQInit(); // initialize sequencer
175
176 AXRegisterCallback(audioCb);
177
178 //
179 // play MIDI.
180 //
181
182 waveTbl = loadFile(AUDIO_GM_WT, heap);
183 pcmData = loadFile(AUDIO_GM_PCM, heap);
184 midiData = loadFile(AUDIO_MIDI_FILE, heap);
185
186 if (!waveTbl || !pcmData || !midiData)
187 {
188 return FALSE;
189 }
190
191 SEQAddSequence(&sequence,
192 midiData,
193 waveTbl,
194 pcmData,
195 NULL,
196 16,
197 15,
198 1);
199
200 SEQSetState (&sequence, SEQ_STATE_RUNLOOPED);
201 SEQSetVolume(&sequence, -120);
202
203 //
204 // create voice processing thread.
205 //
206
207 srcBuff = MEMAllocFromExpHeapEx(*heap, SRC_BUFF_SAMPLES * sizeof(s16), 32);
208 memset(srcBuff, 0, SRC_BUFF_SAMPLES * sizeof(s16));
209
210 procInfo.samples = PROC_BUFF_SAMPLES;
211 procInfo.top = 0;
212 procInfo.buttom = 0;
213 procInfo.buffer = MEMAllocFromExpHeapEx(*heap, PROC_BUFF_SAMPLES * sizeof(s16), 32);
214
215 OSInitThreadQueue(&procWait);
216
217 if (procFunc == NULL) {
218 procFunc = voiceProc;
219 }
220
221 OSCreateThread(&procThread,
222 procFunc,
223 NULL,
224 procStack + STACK_SIZE,
225 STACK_SIZE,
226 THREAD_PRIORITY,
227 OS_THREAD_ATTR_DETACH);
228
229 OSResumeThread(&procThread);
230
231 //
232 // snatch AI callback.
233 //
234
235 aiBuff[0]= MEMAllocFromExpHeapEx(*heap, AUDIO_BYTES_PER_FRAME, 32);
236 aiBuff[1]= MEMAllocFromExpHeapEx(*heap, AUDIO_BYTES_PER_FRAME, 32);
237
238 isoBuff = MEMAllocFromExpHeapEx(*heap, (AUDIO_SAMPLES_PER_FRAME / 2) * sizeof(s16) * 2, 32);
239 // 16KHz * s16 * stereo
240
241 old = OSDisableInterrupts();
242 old_aiCb = AIRegisterDMACallback(aiCb);
243 OSRestoreInterrupts(old);
244
245 return TRUE;
246 }
247
248 /*---------------------------------------------------------------------------*
249 Name: AUDIOQuit
250
251 Description: shut down audio system.
252
253 Arguments: none.
254
255 Returns: none.
256 *---------------------------------------------------------------------------*/
257
AUDIOQuit(void)258 void AUDIOQuit(void)
259 {
260 SEQQuit();
261 SYNQuit();
262 MIXQuit();
263 AXQuit();
264 }
265
266 /*---------------------------------------------------------------------------*
267 Name: AUDIOStartPlay
268
269 Description: start playing P-Mic's data.
270
271 Arguments: none.
272
273 Returns: none.
274 *---------------------------------------------------------------------------*/
275
AUDIOStartPlay(void)276 void AUDIOStartPlay(void)
277 {
278 playVoice = TRUE;
279 }
280
281 /*---------------------------------------------------------------------------*
282 Name: AUDIOStopPlay
283
284 Description: stop playing P-Mic's data.
285
286 Arguments: none.
287
288 Returns: none.
289 *---------------------------------------------------------------------------*/
290
AUDIOStopPlay(void)291 void AUDIOStopPlay(void)
292 {
293 playVoice = FALSE;
294
295 procInfo.top = 0;
296 procInfo.buttom = 0;
297 }
298
299 /*---------------------------------------------------------------------------*
300 Name: AUDIOSleepThread
301
302 Description: Used by (external) pmic processing function, since the
303 threadqueue is not public.
304
305 Arguments: none.
306
307 Returns: none.
308 *---------------------------------------------------------------------------*/
309
AUDIOSleepThread(void)310 void AUDIOSleepThread(void)
311 {
312 OSSleepThread(&procWait);
313 }
314
315 /*---------------------------------------------------------------------------*
316 *---------------------------------------------------------------------------*
317 *---------------------------------------------------------------------------*
318 *---------------------------------------------------------------------------*/
319
320 /*---------------------------------------------------------------------------*
321 Name: audioCb
322
323 Description: callback for AX apps.
324
325 Arguments: none.
326
327 Returns: none.
328 *---------------------------------------------------------------------------*/
329
audioCb(void)330 static void audioCb(void)
331 {
332 SEQRunAudioFrame();
333 SYNRunAudioFrame();
334 MIXUpdateSettings();
335 }
336
337 /*---------------------------------------------------------------------------*
338 Name: loadFile
339
340 Description: load file from DVD.
341
342 Arguments: path file name with path info.
343 heap for mem allocation.
344
345 Returns: data address.
346 *---------------------------------------------------------------------------*/
347
loadFile(char * path,MEMHeapHandle * heap)348 static u8* loadFile(char *path, MEMHeapHandle* heap)
349 {
350 DVDFileInfo handle;
351 u32 round_length;
352 s32 read_length;
353 void* buffer;
354
355 if (!DVDOpen(path, &handle))
356 {
357 return NULL;
358 }
359
360 if (DVDGetLength(&handle) == 0)
361 {
362 return NULL;
363 }
364
365 round_length = OSRoundUp32B(DVDGetLength(&handle));
366 buffer = MEMAllocFromExpHeapEx(*heap, round_length, 32);
367
368 if (buffer == NULL)
369 {
370 return NULL;
371 }
372
373 read_length = DVDRead(&handle, buffer, (s32)(round_length), 0);
374
375 if (read_length <= 0)
376 {
377 return NULL;
378 }
379
380 return buffer;
381 }
382
383 /*---------------------------------------------------------------------------*
384 Name: aiCb
385
386 Description: new AI callback func.
387
388 Arguments: none.
389
390 Returns: none.
391 *---------------------------------------------------------------------------*/
392
aiCb(void)393 static void aiCb(void)
394 {
395 static s16* last_buff = NULL;
396 static s32 buff_ptr = 0;
397 static BOOL init = TRUE;
398
399 s16* curr_buff;
400 s32 retval;
401
402 // hand over AI buffer address.
403 curr_buff = last_buff;
404
405 // call old AI callback func.
406 old_aiCb();
407
408 // get AI buffer address set in the old AI callback func.
409 last_buff = (s16 *)OSPhysicalToCached(AIGetDMAStartAddr());
410
411 // set new AI buffer.
412 AIInitDMA((u32)aiBuff[buff_ptr], AUDIO_BYTES_PER_FRAME);
413
414 // fill the new AI buffer.
415 if (playVoice)
416 {
417 // fill P-Mic's data to the new AI buffer.
418 if (!fillVoice(aiBuff[buff_ptr], AUDIO_BYTES_PER_FRAME / 2))
419 {
420 playVoice = FALSE;
421 }
422 }
423 else
424 {
425 if (curr_buff)
426 {
427 DCInvalidateRange(curr_buff, AUDIO_BYTES_PER_FRAME); // to make sure
428 memcpy(aiBuff[buff_ptr], curr_buff, AUDIO_BYTES_PER_FRAME);
429 }
430 else
431 {
432 memset(aiBuff[buff_ptr], 0, AUDIO_BYTES_PER_FRAME);
433 }
434 }
435
436 // send final AI data in the new AI buffer to P-Mic (ISO-OUT).
437
438 #ifdef USE_64TAP_SRC
439
440 // retval is -1 if PMIC lib is not initialized
441 retval = PMICChangeRate(aiBuff[buff_ptr],
442 AUDIO_BYTES_PER_FRAME / 2,
443 AI_SAMPLERATE_32KHZ,
444 isoBuff,
445 init);
446 #else
447 {
448 extern BOOL PMICIsUp();
449
450 s16 *pIn = aiBuff[buff_ptr];
451 s16 *pOut = isoBuff;
452 u32 i;
453
454 for(i=0; i<AUDIO_SAMPLES_PER_FRAME/2; i++) {
455 *pOut++ = *pIn++; // right sample
456 *pOut++ = *pIn++; // left sample
457 pIn+=2; // skip next pair for 32Khz->16Khz
458 }
459
460 if (PMICIsUp())
461 retval = AUDIO_SAMPLES_PER_FRAME;
462 else
463 retval = -1;
464 }
465 #endif
466
467 if (retval > 0)
468 {
469 #ifdef USE_HEADPHONE
470 memset(isoBuff, 0, (u32)(retval * sizeof(s16)));
471 #endif
472 PMICWrite(isoBuff, retval);
473 init = FALSE;
474 }
475
476 // finish touching the new AI buffer.
477 DCFlushRange(aiBuff[buff_ptr], AUDIO_BYTES_PER_FRAME);
478 buff_ptr ^= 1;
479
480 // report to voice processing thread.
481 OSWakeupThread(&procWait);
482 }
483
484 /*---------------------------------------------------------------------------*
485 Name: voiceProc
486
487 Description: convert P-Mic's data from 16KHz to 32KHz.
488
489 Arguments: param no use.
490
491 Returns: none.
492 *---------------------------------------------------------------------------*/
493
voiceProc(void * param)494 static void* voiceProc(void *param)
495 {
496 #pragma unused(param)
497
498 s32 wptr;
499 s32 limits;
500 s16* buffer;
501 s32 writables;
502 s32 needs;
503 s32 reads;
504 s32 ii, jj;
505 s32 acc_1st, acc_2nd;
506 s16* inptr;
507
508 while (1)
509 {
510 AUDIOSleepThread();
511
512 wptr = procInfo.top;
513 limits = procInfo.samples;
514 buffer = procInfo.buffer;
515
516 // check space of output buffer.
517 // # don't overwrite here <- P-Mic lib may overwrite...
518 writables = (limits - wptr) & ~0x1;
519 if (writables <= 0)
520 {
521 continue;
522 }
523
524 // get P-Mic's data (16KHz).
525 needs = writables >> 1;
526 if (needs >= SRC_BUFF_SAMPLES - (SRC_TAPS - 1))
527 {
528 needs = SRC_BUFF_SAMPLES - (SRC_TAPS - 1);
529 }
530
531 reads = PMICRead(&srcBuff[SRC_TAPS - 1], needs);
532 if (reads <= 0)
533 {
534 continue;
535 }
536
537 // do SRC from 16KHz to 32KHz.
538 for (ii = 0; ii < reads; ii++)
539 {
540 inptr = &srcBuff[ii];
541
542 #ifdef USE_64TAP_SRC
543
544 acc_1st = 0;
545 acc_2nd = 0;
546
547 for (jj = 0; jj < SRC_TAPS; jj++)
548 {
549 acc_1st += 2 * srcCoef[0][jj] * *inptr;
550 acc_2nd += 2 * srcCoef[1][jj] * *inptr++;
551 }
552
553 acc_1st = (acc_1st + 0x4000) >> 15;
554 acc_2nd = (acc_2nd + 0x4000) >> 15;
555
556 if (acc_1st > 32767)
557 {
558 acc_1st = 32767;
559 }
560 else if (acc_1st < -32768)
561 {
562 acc_1st = -32768;
563 }
564
565 if (acc_2nd > 32767)
566 {
567 acc_2nd = 32767;
568 }
569 else if (acc_2nd < -32768)
570 {
571 acc_2nd = -32768;
572 }
573 #else
574 acc_1st = *inptr++;
575 acc_2nd = (acc_1st + (*inptr)) >> 1;
576 #endif
577
578 buffer[wptr++] = (s16)acc_1st;
579 buffer[wptr++] = (s16)acc_2nd;
580 }
581
582 // move histories.
583 for (jj = 0; jj < SRC_TAPS - 1; jj++)
584 {
585 srcBuff[jj] = srcBuff[ii + jj];
586 }
587
588 procInfo.top = wptr;
589 }
590 }
591
592 /*---------------------------------------------------------------------------*
593 Name: fillVoice
594
595 Description: fill P-Mic's data (already converted to 32KHz).
596
597 Arguments: buffer output buffer.
598 samples samples to output.
599
600 Returns: num of outputs.
601 *---------------------------------------------------------------------------*/
602
fillVoice(s16 * buffer,s32 samples)603 static s32 fillVoice(s16* buffer, s32 samples)
604 {
605 s32 needs;
606 s32 rptr;
607 s32 lefts;
608 s16* data;
609 s32 outputs;
610 s32 ii;
611 s32 stmp32;
612
613 needs = samples >> 1; // stereo -> mono
614
615 rptr = procInfo.buttom;
616 lefts = procInfo.top - rptr;
617 data = procInfo.buffer;
618
619 outputs = needs > lefts? lefts: needs;
620
621 for (ii = 0; ii < outputs; ii++)
622 {
623 stmp32 = (s32)data[rptr++] << PROC_VOICE_GAIN;
624 if (stmp32 > 32767)
625 {
626 stmp32 = 32767;
627 }
628 else if (stmp32 < -32768)
629 {
630 stmp32 = -32768;
631 }
632
633 *buffer++ = (s16)stmp32; // Rch
634 *buffer++ = (s16)stmp32; // Lch
635 }
636
637 for (; ii < needs; ii++)
638 {
639 *buffer++ = 0; // Rch
640 *buffer++ = 0; // Lch
641 }
642
643 procInfo.buttom = rptr;
644
645 return outputs << 1; // mono -> stereo
646 }
647