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