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