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