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