1 /*---------------------------------------------------------------------------*
2   Project:  Revolution AX stream demo
3   File:     axstream.c
4 
5   Copyright (C)1998-2006 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: axstream.c,v $
14   Revision 1.11.4.2  2007/09/25 02:28:08  aka
15   For SDK3.1 patch1.
16 
17   Revision 1.13  2007/09/25 02:25:35  aka
18   Added checking flag in DVDRead callbacks.
19 
20   Revision 1.12  2007/09/03 09:01:01  aka
21   Changed from *.adpcm to *.dsp.
22 
23   Revision 1.11  2007/05/18 03:01:36  ekwon
24   Bug fix: Predictor/scale values not updated at loop point;
25   game developers reported some pops/clicks in some streams.
26   Not audible in our demo because our sample happens to be ok
27   without predictor/scale update. Oops.
28 
29   Revision 1.7.2.1  2006/12/27 07:31:56  aka
30   For SDK2.4 patch2.
31 
32   Revision 1.8  2006/12/20 05:56:05  aka
33   Added memory clear when starting stream.
34   Changed way of detecting stream end.
35 
36   Revision 1.7  2006/11/20 04:22:12  aka
37   Revised codes of getting stream data from DVD.
38 
39   Revision 1.6  2006/10/23 02:05:52  aka
40   Changed from AXInit() to AXInitSpecifyMem().
41   Changed from MIXInit() to MIXInitSpecifyMem().
42   Changed from SYNInit() to SYNInitSpecifyMem().
43 
44   Revision 1.5  2006/10/10 08:30:06  aka
45   Revised AXInit(), MIXInit() and SYNInit().
46 
47   Revision 1.4  2006/02/02 08:12:18  aka
48   Modified using MEM functions instead of OSAlloc()/OSFree().
49 
50   Revision 1.3  2006/02/01 05:16:03  aka
51   Added #ifndef(#ifdef) HOLLYWOOD_REV - #else - #endif.
52 
53   Revision 1.2  2005/11/08 02:55:02  aka
54   Changed suiting to Revolution's audio spec.
55 
56   Revision 1.1  2005/11/04 05:01:39  aka
57   Imported from dolphin tree.
58 
59     10    04/04/21 11:18a Akagi
60     Added considering alignment of stream buffers.
61 
62     9     2003/04/24 8:45 Dante
63     Removed ramBufferLenAdpcm.
64 
65     8     2003/04/15 8:08 Dante
66     Modified to read APDCP header instead of hard coded information &
67     coefficients.
68 
69     7     2002/07/01 4:18p Billyjack
70     Changed ramBufferLenPcm16 = 827260; to ramBufferLenPcm16 = 827270; used
71     to be off by 10 bytes at the end where it was silent.
72 
73     6     2001/12/11 7:02p Billyjack
74     - keep interrupts disabled during audio frame callback
75 
76     5     2001/08/03 4:32p Billyjack
77     Added OSEnableInterrupts() and OSRestoreInterrupts() to AX audio frame
78     callback per change in AX lib.
79 
80     4     2001/07/06 11:50a Billyjack
81     Commented DCInvalidateRange for DVD to RAM transfers.
82 
83     3     2001/05/14 1:39p Billyjack
84     - reworked for DVDDATA file location and names
85     - uses ARGetBaseAddress where applicable
86 
87     2     2001/05/09 6:09p Billyjack
88     Now uses the mix lib.
89 
90     1     2001/03/26 2:32p Billyjack
91     Created
92 
93   $NoKeywords: $
94  *---------------------------------------------------------------------------*/
95 #include <string.h>
96 #include <demo.h>
97 #include <revolution.h>
98 #include <revolution/mix.h>
99 #include <revolution/mem.h>
100 
101 /*---------------------------------------------------------------------------*
102    Stream control flag
103  *---------------------------------------------------------------------------*/
104 #define STREAM_NONE         0
105 #define STREAM_INITIALIZING 1
106 #define STREAM_STARTED      2
107 #define STREAM_STOPPING     3
108 #define STREAM_STOPPED      4
109 
110 static u32          flag = STREAM_NONE;
111 
112 /*---------------------------------------------------------------------------*
113    Stream data
114  *---------------------------------------------------------------------------*/
115 #define SAMPLE_PCM16_L  "/axdemo/stream/left.pcm16"
116 #define SAMPLE_PCM16_R  "/axdemo/stream/right.pcm16"
117 
118 #define SAMPLE_PCM8_L   "/axdemo/stream/left.pcm8"
119 #define SAMPLE_PCM8_R   "/axdemo/stream/right.pcm8"
120 
121 #define SAMPLE_ADPCM_L  "/axdemo/stream/left.dsp"
122 #define SAMPLE_ADPCM_R  "/axdemo/stream/right.dsp"
123 
124 // Stream info
125 typedef struct
126 {
127     DVDFileInfo finfoL;
128     DVDFileInfo finfoR;
129 
130     u16         format;
131     s32         done;
132     s32         rest;
133     s32         offset;
134     u32         end_addr;
135 
136 } STRMInfo;
137 
138 static STRMInfo      strm;
139 
140 /*---------------------------------------------------------------------------*
141    AX voice
142  *---------------------------------------------------------------------------*/
143 // voice
144 static AXVPB        *streamL = NULL;
145 static AXVPB        *streamR = NULL;
146 
147 /*---------------------------------------------------------------------------*
148    DVD read
149  *---------------------------------------------------------------------------*/
150 // DVD control flag
151 static BOOL          active = FALSE;
152 
153 // Write buffer offset
154 static s32           offset;
155 
156 // Stream buffer
157 #define STREAMBUFFER_BYTES (32 * 1024 * 2) // 32KB x DoubleBuffer
158 static u8           *streamBufferL = NULL;
159 static u8           *streamBufferR = NULL;
160 
161 // Stream pointer
162 static u32           half_ptr;
163 static u32           prev_ptr;
164 
165 /*---------------------------------------------------------------------------*
166     Name:               gottenData
167 
168     Description:        Function called when DVD read (of L&R ch) is done.
169 
170     Arguments:          No use
171 
172     Returns:            None
173  *---------------------------------------------------------------------------*/
gottenData(s32 result,DVDFileInfo * fileInfo)174 static void gottenData(s32 result, DVDFileInfo* fileInfo)
175 {
176 #pragma unused(result)
177 #pragma unused(fileInfo)
178 
179     // If we just filled the first half of the buffer we need to set the
180     // loop context
181     if (offset == 0 && flag == STREAM_STARTED )
182     {
183         AXPBADPCMLOOP   loop;
184 
185         loop.loop_pred_scale    = (u16)(*((u8*)(streamBufferR)));
186         loop.loop_yn1           = 0;
187         loop.loop_yn2           = 0;
188 
189         AXSetVoiceAdpcmLoop(streamR, &loop);
190     }
191 
192 
193     active = FALSE;
194 }
195 
196 /*---------------------------------------------------------------------------*
197     Name:               getDataR
198 
199     Description:        Read stream data for Rch.
200 
201     Arguments:          No use
202 
203     Returns:            None
204  *---------------------------------------------------------------------------*/
getDataR(s32 result,DVDFileInfo * fileInfo)205 static void getDataR(s32 result, DVDFileInfo* fileInfo)
206 {
207 #pragma unused(result)
208 #pragma unused(fileInfo)
209 
210     s32  size;
211     u8  *buffer;
212     s32  length;
213     s32  rlength;
214 
215     size    = STREAMBUFFER_BYTES / 2;
216     buffer  = streamBufferR + offset;
217 
218     length  = strm.rest > size? size: strm.rest;
219     rlength = length & ~0x1f;
220 
221 //    memset(buffer + rlength, 0, (u32)(size - rlength));
222 //    DCFlushRange(buffer + rlength, (u32)(size - rlength));
223 
224     DVDReadAsync(&strm.finfoR, buffer, rlength, strm.offset, gottenData);
225 
226     strm.rest   -= rlength;
227     strm.offset += rlength;
228 
229     // If we just filled the first half of the buffer we need to set the
230     // loop context
231     if (offset == 0 && flag == STREAM_STARTED )
232     {
233         AXPBADPCMLOOP   loop;
234 
235         loop.loop_pred_scale    = (u16)(*((u8*)(streamBufferL)));
236         loop.loop_yn1           = 0;
237         loop.loop_yn2           = 0;
238 
239         AXSetVoiceAdpcmLoop(streamL, &loop);
240     }
241 }
242 
243 /*---------------------------------------------------------------------------*
244     Name:               getDataL
245 
246     Description:        Read stream data for Lch.
247 
248     Arguments:          None
249 
250     Returns:            None
251  *---------------------------------------------------------------------------*/
getDataL(void)252 static void getDataL(void)
253 {
254     s32  size;
255     u8  *buffer;
256     s32  length;
257     s32  rlength;
258 
259     active = TRUE;
260 
261     size    = STREAMBUFFER_BYTES / 2;
262     buffer  = streamBufferL + offset;
263 
264     length  = strm.rest > size? size: strm.rest;
265     rlength = length & ~0x1f;
266 
267 //    memset(buffer + rlength, 0, (u32)(size - rlength));
268 //    DCFlushRange(buffer + rlength, (u32)(size - rlength));
269 
270     DVDReadAsync(&strm.finfoL, buffer, rlength, strm.offset, getDataR);
271 
272     if (!((strm.rest  - rlength) & ~0x1f))
273     {
274         strm.done++;
275         if (rlength)
276         {
277             strm.end_addr = (u32)OSCachedToPhysical(buffer) + rlength;
278         }
279     }
280 }
281 
282 /*---------------------------------------------------------------------------*
283     Name:               callbackAudioFrame
284 
285     Description:        Function called every audio frame.
286 
287     Arguments:          None
288 
289     Returns:            None
290  *---------------------------------------------------------------------------*/
callbackAudioFrame(void)291 static void callbackAudioFrame(void)
292 {
293     u32  curr_ptr;
294 
295     // Tell the mixer to update settings.
296     MIXUpdateSettings();
297 
298     switch (flag)
299     {
300         case STREAM_NONE:
301         case STREAM_INITIALIZING:
302 
303             break;
304 
305         case STREAM_STARTED:
306 
307             curr_ptr = (u32)(streamL->pb.addr.currentAddressHi << 16) | (streamL->pb.addr.currentAddressLo);
308             if (strm.format == AX_PB_FORMAT_PCM16)
309             {
310                 curr_ptr <<= 1;
311             }
312             else if (strm.format == AX_PB_FORMAT_ADPCM)
313             {
314                 curr_ptr >>= 1;
315             }
316 
317             if (strm.done)
318             {
319                 if (((curr_ptr < prev_ptr) && ((prev_ptr < strm.end_addr) || (strm.end_addr <= curr_ptr))) ||
320                     ((prev_ptr < curr_ptr) && ((prev_ptr < strm.end_addr) && (strm.end_addr <= curr_ptr))))
321                 {
322                     flag = STREAM_STOPPING;
323                 }
324             }
325 
326             if (curr_ptr < prev_ptr)
327             {
328                 // Fill 2nd half.
329                 if (!active)
330                 {
331                     offset = STREAMBUFFER_BYTES / 2;
332                     getDataL();
333                     prev_ptr = curr_ptr;
334                 }
335             }
336 
337             else if ((prev_ptr < half_ptr) && (curr_ptr >= half_ptr))
338             {
339                 // Fill 1st half.
340                 if (!active)
341                 {
342                     offset = 0;
343                     getDataL();
344                     prev_ptr = curr_ptr;
345                 }
346             }
347 
348             else
349             {
350                 prev_ptr = curr_ptr;
351             }
352 
353             break;
354 
355         case STREAM_STOPPING:
356 
357             // Lch
358             MIXReleaseChannel(streamL);
359             AXFreeVoice(streamL);
360             streamL = NULL;
361 
362             // Rch
363             MIXReleaseChannel(streamR);
364             AXFreeVoice(streamR);
365             streamR = NULL;
366 
367             flag = STREAM_STOPPED;
368 
369             break;
370 
371         case STREAM_STOPPED:
372 
373             if (!active)
374             {
375                 DVDClose(&strm.finfoL);
376                 DVDClose(&strm.finfoR);
377                 flag = STREAM_NONE;
378             }
379 
380             break;
381     }
382 }
383 
384 /*---------------------------------------------------------------------------*
385     Name:               startStreamPcm16
386 
387     Description:        Play PCM16 stream.
388 
389     Arguments:          None
390 
391     Returns:            None
392  *---------------------------------------------------------------------------*/
startStreamPcm16(void)393 static void startStreamPcm16(void)
394 {
395     s32       length;
396     s32       rlength;
397     AXPBADDR  addr;
398     u32       ls_addr;
399     u32       le_addr;
400 
401     if (flag != STREAM_NONE)
402     {
403         return;
404     }
405 
406     flag = STREAM_INITIALIZING;
407 
408     // Open stream files.
409     if (!DVDOpen(SAMPLE_PCM16_L, &strm.finfoL) || !DVDOpen(SAMPLE_PCM16_R, &strm.finfoR))
410     {
411         OSHalt("Cannot open stream files\n");
412     }
413 
414     strm.format = AX_PB_FORMAT_PCM16;
415     strm.done   = 0;
416     strm.rest   = (s32)DVDGetLength(&strm.finfoL);
417     strm.offset = 0;
418 
419     // Read stream data.
420     length  = strm.rest > STREAMBUFFER_BYTES? STREAMBUFFER_BYTES: strm.rest;
421     rlength = length & ~0x1f;
422 
423     memset(streamBufferL + rlength, 0, (u32)(STREAMBUFFER_BYTES - rlength));
424     memset(streamBufferR + rlength, 0, (u32)(STREAMBUFFER_BYTES - rlength));
425 
426     DCFlushRange(streamBufferL + rlength, (u32)(STREAMBUFFER_BYTES - rlength));
427     DCFlushRange(streamBufferR + rlength, (u32)(STREAMBUFFER_BYTES - rlength));
428 
429     DVDRead(&strm.finfoL, streamBufferL, rlength, strm.offset);
430     DVDRead(&strm.finfoR, streamBufferR, rlength, strm.offset);
431 
432     strm.rest   -= rlength;
433     strm.offset += rlength;
434 
435     if (!(strm.rest & ~0x1f))
436     {
437         strm.done++;
438         strm.end_addr = (u32)OSCachedToPhysical(streamBufferL) + rlength;
439     }
440 
441     // Setup pointer
442     half_ptr = (u32)OSCachedToPhysical(streamBufferL) + STREAMBUFFER_BYTES / 2;
443     prev_ptr = (u32)OSCachedToPhysical(streamBufferL);
444 
445     // Acquire voices.
446     if (!(streamL = AXAcquireVoice(15, NULL, 0)) || !(streamR = AXAcquireVoice(15, NULL, 0)))
447     {
448         OSHalt("Cannot acquire voice\n");
449     }
450 
451     // Setup Lch.
452     ls_addr = (u32)OSCachedToPhysical(streamBufferL) >> 1;
453     le_addr = ls_addr + (STREAMBUFFER_BYTES >> 1) - 1;
454 
455     MIXInitChannel(streamL, 0, 0, -904, -904, -904, 0, 127, 0);
456 
457     addr.loopFlag         = AXPBADDR_LOOP_ON;
458     addr.format           = AX_PB_FORMAT_PCM16;
459     addr.loopAddressHi    = (u16)(ls_addr >> 16);
460     addr.loopAddressLo    = (u16)(ls_addr &  0xFFFF);
461     addr.endAddressHi     = (u16)(le_addr >> 16);
462     addr.endAddressLo     = (u16)(le_addr &  0xFFFF);
463     addr.currentAddressHi = (u16)(ls_addr >> 16);
464     addr.currentAddressLo = (u16)(ls_addr &  0xFFFF);
465 
466     AXSetVoiceAddr   (streamL, &addr);
467     AXSetVoiceSrcType(streamL, AX_SRC_TYPE_NONE);
468     AXSetVoiceState  (streamL, AX_PB_STATE_RUN);
469 
470     // Setup Rch.
471     ls_addr = (u32)OSCachedToPhysical(streamBufferR) >> 1;
472     le_addr = ls_addr + (STREAMBUFFER_BYTES >> 1) - 1;
473 
474     MIXInitChannel(streamR, 0, 0, -904, -904, -904, 127, 127, 0);
475 
476     addr.loopFlag         = AXPBADDR_LOOP_ON;
477     addr.format           = AX_PB_FORMAT_PCM16;
478     addr.loopAddressHi    = (u16)(ls_addr >> 16);
479     addr.loopAddressLo    = (u16)(ls_addr &  0xFFFF);
480     addr.endAddressHi     = (u16)(le_addr >> 16);
481     addr.endAddressLo     = (u16)(le_addr &  0xFFFF);
482     addr.currentAddressHi = (u16)(ls_addr >> 16);
483     addr.currentAddressLo = (u16)(ls_addr &  0xFFFF);
484 
485     AXSetVoiceAddr   (streamR, &addr);
486     AXSetVoiceSrcType(streamR, AX_SRC_TYPE_NONE);
487     AXSetVoiceState  (streamR, AX_PB_STATE_RUN);
488 
489     flag = STREAM_STARTED;
490 }
491 
492 /*---------------------------------------------------------------------------*
493     Name:               startStreamPcm8
494 
495     Description:        Play PCM8 stream.
496 
497     Arguments:          None
498 
499     Returns:            None
500  *---------------------------------------------------------------------------*/
startStreamPcm8(void)501 static void startStreamPcm8(void)
502 {
503     s32       length;
504     s32       rlength;
505     AXPBADDR  addr;
506     u32       ls_addr;
507     u32       le_addr;
508 
509     if (flag != STREAM_NONE)
510     {
511         return;
512     }
513 
514     flag = STREAM_INITIALIZING;
515 
516     // Open stream files.
517     if (!DVDOpen(SAMPLE_PCM8_L, &strm.finfoL) || !DVDOpen(SAMPLE_PCM8_R, &strm.finfoR))
518     {
519         OSHalt("Cannot open stream files\n");
520     }
521 
522     strm.format = AX_PB_FORMAT_PCM8;
523     strm.done   = 0;
524     strm.rest   = (s32)DVDGetLength(&strm.finfoL);
525     strm.offset = 0;
526 
527     // Read data.
528     length  = strm.rest > STREAMBUFFER_BYTES? STREAMBUFFER_BYTES: strm.rest;
529     rlength = length & ~0x1f;
530 
531     memset(streamBufferL + rlength, 0, (u32)(STREAMBUFFER_BYTES - rlength));
532     memset(streamBufferR + rlength, 0, (u32)(STREAMBUFFER_BYTES - rlength));
533 
534     DCFlushRange(streamBufferL + rlength, (u32)(STREAMBUFFER_BYTES - rlength));
535     DCFlushRange(streamBufferR + rlength, (u32)(STREAMBUFFER_BYTES - rlength));
536 
537     DVDRead(&strm.finfoL, streamBufferL, rlength, strm.offset);
538     DVDRead(&strm.finfoR, streamBufferR, rlength, strm.offset);
539 
540     strm.rest   -= rlength;
541     strm.offset += rlength;
542 
543     if (!(strm.rest & ~0x1f))
544     {
545         strm.done++;
546         strm.end_addr = (u32)OSCachedToPhysical(streamBufferL) + rlength;
547     }
548 
549     // Setup pointer.
550     half_ptr = (u32)OSCachedToPhysical(streamBufferL) + STREAMBUFFER_BYTES / 2;
551     prev_ptr = (u32)OSCachedToPhysical(streamBufferL);
552 
553     // Acquire voices.
554     if (!(streamL = AXAcquireVoice(15, NULL, 0)) || !(streamR = AXAcquireVoice(15, NULL, 0)))
555     {
556         OSHalt("Cannot acquire voice\n");
557     }
558 
559     // Setup Lch.
560     ls_addr = (u32)OSCachedToPhysical(streamBufferL);
561     le_addr = ls_addr + STREAMBUFFER_BYTES - 1;
562 
563     MIXInitChannel(streamL, 0, 0, -904, -904, -904, 0, 127, 0);
564 
565     addr.loopFlag         = AXPBADDR_LOOP_ON;
566     addr.format           = AX_PB_FORMAT_PCM8;
567     addr.loopAddressHi    = (u16)(ls_addr >> 16);
568     addr.loopAddressLo    = (u16)(ls_addr &  0xFFFF);
569     addr.endAddressHi     = (u16)(le_addr >> 16);
570     addr.endAddressLo     = (u16)(le_addr &  0xFFFF);
571     addr.currentAddressHi = (u16)(ls_addr >> 16);
572     addr.currentAddressLo = (u16)(ls_addr &  0xFFFF);
573 
574     AXSetVoiceAddr   (streamL, &addr);
575     AXSetVoiceSrcType(streamL, AX_SRC_TYPE_NONE);
576     AXSetVoiceState  (streamL, AX_PB_STATE_RUN);
577 
578     // Setup Rch.
579     ls_addr = (u32)OSCachedToPhysical(streamBufferR);
580     le_addr = ls_addr + STREAMBUFFER_BYTES - 1;
581 
582     MIXInitChannel(streamR, 0, 0, -904, -904, -904, 127, 127, 0);
583 
584     addr.loopFlag         = AXPBADDR_LOOP_ON;
585     addr.format           = AX_PB_FORMAT_PCM8;
586     addr.loopAddressHi    = (u16)(ls_addr >> 16);
587     addr.loopAddressLo    = (u16)(ls_addr &  0xFFFF);
588     addr.endAddressHi     = (u16)(le_addr >> 16);
589     addr.endAddressLo     = (u16)(le_addr &  0xFFFF);
590     addr.currentAddressHi = (u16)(ls_addr >> 16);
591     addr.currentAddressLo = (u16)(ls_addr &  0xFFFF);
592 
593     AXSetVoiceAddr   (streamR, &addr);
594     AXSetVoiceSrcType(streamR, AX_SRC_TYPE_NONE);
595     AXSetVoiceState  (streamR, AX_PB_STATE_RUN);
596 
597     flag = STREAM_STARTED;
598 }
599 
600 /*---------------------------------------------------------------------------*
601     Name:               startStreamAdpcm
602 
603     Description:        Play ADPCM stream.
604 
605     Arguments:          None
606 
607     Returns:            None
608  *---------------------------------------------------------------------------*/
startStreamAdpcm(void)609 static void startStreamAdpcm(void)
610 {
611     s32        length;
612     s32        rlength;
613     DSPADPCM  *dspHeader;
614     AXPBADDR   addr;
615     AXPBADPCM  adpcm;
616     u32        ls_addr;
617     u32        le_addr;
618     u32        cu_addr;
619 
620     if (flag != STREAM_NONE)
621     {
622         return;
623     }
624 
625     flag = STREAM_INITIALIZING;
626 
627     // Open stream files.
628     if (!DVDOpen(SAMPLE_ADPCM_L, &strm.finfoL) || !DVDOpen(SAMPLE_ADPCM_R, &strm.finfoR))
629     {
630         OSHalt("Cannot open stream files\n");
631     }
632 
633     strm.format = AX_PB_FORMAT_ADPCM;
634     strm.done   = 0;
635     strm.rest   = (s32)DVDGetLength(&strm.finfoL);
636     strm.offset = 0;
637 
638     // Read data.
639     length  = strm.rest > STREAMBUFFER_BYTES? STREAMBUFFER_BYTES: strm.rest;
640     rlength = length & ~0x1f;
641 
642     memset(streamBufferL + rlength, 0, (u32)(STREAMBUFFER_BYTES - rlength));
643     memset(streamBufferR + rlength, 0, (u32)(STREAMBUFFER_BYTES - rlength));
644 
645     DCFlushRange(streamBufferL + rlength, (u32)(STREAMBUFFER_BYTES - rlength));
646     DCFlushRange(streamBufferR + rlength, (u32)(STREAMBUFFER_BYTES - rlength));
647 
648     DVDRead(&strm.finfoL, streamBufferL, rlength, strm.offset);
649     DVDRead(&strm.finfoR, streamBufferR, rlength, strm.offset);
650 
651     strm.rest   -= rlength;
652     strm.offset += rlength;
653 
654     if (!(strm.rest & ~0x1f))
655     {
656         strm.done++;
657         strm.end_addr = (u32)OSCachedToPhysical(streamBufferL) + rlength;
658     }
659 
660     // Setup pointer.
661     half_ptr = (u32)OSCachedToPhysical(streamBufferL) + STREAMBUFFER_BYTES / 2;
662     prev_ptr = (u32)OSCachedToPhysical(streamBufferL);
663 
664     // Acquire voices.
665     if (!(streamL = AXAcquireVoice(15, NULL, 0)) || !(streamR = AXAcquireVoice(15, NULL, 0)))
666     {
667         OSHalt("Cannot acquire voice\n");
668     }
669 
670     //
671     // - Now half of the stream buffer size is 32KB which is enough larger than size of
672     //   ADPCM header (DSPADPCM structure + initial current address (= dspHeader->ca)).
673     //   So this program applies the stream buffer to the ADPCM header temporarily.
674     //
675     // - Must avoid locating the loop start & end addr (ls_addr & le_addr) on the frame
676     //   header.
677     //
678 
679     // Setup Lch.
680     dspHeader = (DSPADPCM *)streamBufferL;
681 
682     ls_addr = (u32)OSCachedToPhysical(streamBufferL) << 1;
683     le_addr = ls_addr + (STREAMBUFFER_BYTES << 1) - 1;
684     cu_addr = ls_addr + (sizeof(DSPADPCM) << 1) + dspHeader->ca;
685 
686     ls_addr += 2; // Skip the frame header.
687 
688     MIXInitChannel(streamL, 0, 0, -904, -904, -904, 0, 127, 0);
689 
690     addr.loopFlag         = AXPBADDR_LOOP_ON;
691     addr.format           = AX_PB_FORMAT_ADPCM;
692     addr.loopAddressHi    = (u16)(ls_addr >> 16);
693     addr.loopAddressLo    = (u16)(ls_addr &  0xFFFF);
694     addr.endAddressHi     = (u16)(le_addr >> 16);
695     addr.endAddressLo     = (u16)(le_addr &  0xFFFF);
696     addr.currentAddressHi = (u16)(cu_addr >> 16);
697     addr.currentAddressLo = (u16)(cu_addr &  0xFFFF);
698 
699     adpcm.a[0][0]         = dspHeader->coef[0];
700     adpcm.a[0][1]         = dspHeader->coef[1];
701     adpcm.a[1][0]         = dspHeader->coef[2];
702     adpcm.a[1][1]         = dspHeader->coef[3];
703     adpcm.a[2][0]         = dspHeader->coef[4];
704     adpcm.a[2][1]         = dspHeader->coef[5];
705     adpcm.a[3][0]         = dspHeader->coef[6];
706     adpcm.a[3][1]         = dspHeader->coef[7];
707     adpcm.a[4][0]         = dspHeader->coef[8];
708     adpcm.a[4][1]         = dspHeader->coef[9];
709     adpcm.a[5][0]         = dspHeader->coef[10];
710     adpcm.a[5][1]         = dspHeader->coef[11];
711     adpcm.a[6][0]         = dspHeader->coef[12];
712     adpcm.a[6][1]         = dspHeader->coef[13];
713     adpcm.a[7][0]         = dspHeader->coef[14];
714     adpcm.a[7][1]         = dspHeader->coef[15];
715     adpcm.gain            = dspHeader->gain;
716     adpcm.pred_scale      = dspHeader->ps;
717     adpcm.yn1             = dspHeader->yn1;
718     adpcm.yn2             = dspHeader->yn2;
719 
720     AXSetVoiceType   (streamL, AX_PB_TYPE_STREAM); // No loop context.
721     AXSetVoiceAdpcm  (streamL, &adpcm);
722     AXSetVoiceAddr   (streamL, &addr);
723     AXSetVoiceSrcType(streamL, AX_SRC_TYPE_NONE);
724     AXSetVoiceState  (streamL, AX_PB_STATE_RUN);
725 
726     // Setup Rch.
727     dspHeader = (DSPADPCM *)streamBufferR;
728 
729     ls_addr = (u32)OSCachedToPhysical(streamBufferR) << 1;
730     le_addr = ls_addr + (STREAMBUFFER_BYTES << 1) - 1;
731     cu_addr = ls_addr + (sizeof(DSPADPCM) << 1) + dspHeader->ca;
732 
733     ls_addr += 2; // Skip the frame header.
734 
735     MIXInitChannel(streamR, 0, 0, -904, -904, -904, 127, 127, 0);
736 
737     addr.loopFlag         = AXPBADDR_LOOP_ON;
738     addr.format           = AX_PB_FORMAT_ADPCM;
739     addr.loopAddressHi    = (u16)(ls_addr >> 16);
740     addr.loopAddressLo    = (u16)(ls_addr &  0xFFFF);
741     addr.endAddressHi     = (u16)(le_addr >> 16);
742     addr.endAddressLo     = (u16)(le_addr &  0xFFFF);
743     addr.currentAddressHi = (u16)(cu_addr >> 16);
744     addr.currentAddressLo = (u16)(cu_addr &  0xFFFF);
745 
746     adpcm.a[0][0]         = dspHeader->coef[0];
747     adpcm.a[0][1]         = dspHeader->coef[1];
748     adpcm.a[1][0]         = dspHeader->coef[2];
749     adpcm.a[1][1]         = dspHeader->coef[3];
750     adpcm.a[2][0]         = dspHeader->coef[4];
751     adpcm.a[2][1]         = dspHeader->coef[5];
752     adpcm.a[3][0]         = dspHeader->coef[6];
753     adpcm.a[3][1]         = dspHeader->coef[7];
754     adpcm.a[4][0]         = dspHeader->coef[8];
755     adpcm.a[4][1]         = dspHeader->coef[9];
756     adpcm.a[5][0]         = dspHeader->coef[10];
757     adpcm.a[5][1]         = dspHeader->coef[11];
758     adpcm.a[6][0]         = dspHeader->coef[12];
759     adpcm.a[6][1]         = dspHeader->coef[13];
760     adpcm.a[7][0]         = dspHeader->coef[14];
761     adpcm.a[7][1]         = dspHeader->coef[15];
762     adpcm.gain            = dspHeader->gain;
763     adpcm.pred_scale      = dspHeader->ps;
764     adpcm.yn1             = dspHeader->yn1;
765     adpcm.yn2             = dspHeader->yn2;
766 
767     AXSetVoiceType   (streamR, AX_PB_TYPE_STREAM); // No loop context.
768     AXSetVoiceAdpcm  (streamR, &adpcm);
769     AXSetVoiceAddr   (streamR, &addr);
770     AXSetVoiceSrcType(streamR, AX_SRC_TYPE_NONE);
771     AXSetVoiceState  (streamR, AX_PB_STATE_RUN);
772 
773     flag = STREAM_STARTED;
774 }
775 
776 /*---------------------------------------------------------------------------*
777     Name:               stopStream
778 
779     Description:        Stop stream.
780 
781     Arguments:          None
782 
783     Returns:            None
784  *---------------------------------------------------------------------------*/
stopStream(void)785 static void stopStream(void)
786 {
787     if (flag != STREAM_STARTED)
788     {
789         return;
790     }
791 
792     MIXSetFader(streamL, -960);
793     MIXSetFader(streamR, -960);
794 
795     flag = STREAM_STOPPING;
796 }
797 
798 /*---------------------------------------------------------------------------*
799  *---------------------------------------------------------------------------*/
main(void)800 void main(void)
801 {
802     void          *arenaMem2Lo;
803     void          *arenaMem2Hi;
804     void          *axBuffer;
805     void          *mixBuffer;
806     MEMHeapHandle  hExpHeap;
807     PADStatus      pads[PAD_MAX_CONTROLLERS];
808 
809     DEMOInit(NULL);
810 
811     // Initialize Exp Heap on MEM2.
812     arenaMem2Lo = OSGetMEM2ArenaLo();
813     arenaMem2Hi = OSGetMEM2ArenaHi();
814     hExpHeap    = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32) arenaMem2Lo);
815 
816     // Initialize AX and MIX.
817     axBuffer  = MEMAllocFromExpHeapEx(hExpHeap, AXGetMemorySize(AX_MAX_VOICES), 32);
818     mixBuffer = MEMAllocFromExpHeap(hExpHeap, MIXGetMemorySize(AX_MAX_VOICES));
819 
820     AIInit(NULL);
821     AXInitSpecifyMem(AX_MAX_VOICES, axBuffer);
822     MIXInitSpecifyMem(mixBuffer);
823 
824     // Prepare stream buffers.
825     streamBufferL = MEMAllocFromExpHeapEx(hExpHeap, STREAMBUFFER_BYTES, 32);
826     streamBufferR = MEMAllocFromExpHeapEx(hExpHeap, STREAMBUFFER_BYTES, 32);
827 
828     // Register user callback for audio frames notification.
829     AXRegisterCallback(&callbackAudioFrame);
830 
831     OSReport("Press the A button to play PCM16 stream.\n");
832     OSReport("Press the B button to play PCM8 stream.\n");
833     OSReport("Press the X button to play DSP ADPCM stream.\n");
834     OSReport("Press the Y button to stop the stream.\n");
835     OSReport("Press START/PAUSE button to exit program\n");
836 
837     while (1)
838     {
839 
840         if (streamR)
841         {
842             OSReport("L:%x R:%x\n",
843                      *(u32*)&streamL->pb.addr.currentAddressHi,
844                      *(u32*)&streamR->pb.addr.currentAddressHi);
845         }
846 
847         // Wait for retrace.
848         VIWaitForRetrace();
849 
850         // Check pad.
851         PADRead(pads);
852 
853         // See if we should quit.
854         if (pads[0].button & PAD_BUTTON_MENU)
855         {
856             break;
857         }
858 
859         // Run PCM16 stream.
860         if (pads[0].button & PAD_BUTTON_A)
861         {
862             startStreamPcm16();
863         }
864 
865         // Run PCM8 stream.
866         if (pads[0].button & PAD_BUTTON_B)
867         {
868             startStreamPcm8();
869         }
870 
871         // Run ADPCM stream.
872         if (pads[0].button & PAD_BUTTON_X)
873         {
874             startStreamAdpcm();
875         }
876 
877         // Stop stream.
878         if (pads[0].button & PAD_BUTTON_Y)
879         {
880             stopStream();
881         }
882     }
883 
884     MIXQuit();
885     AXQuit();
886 
887     OSHalt("End of program\n");
888 }
889