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