1 /*---------------------------------------------------------------------------*
2 Project: Revolution AX Stream Demo
3 File: axstream2.c
4
5 Copyright (C) 2009 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: axstream2.c,v $
14 Revision 1.4 2009/05/13 01:38:16 aka
15 Revised comments.
16
17 Revision 1.3 2009/05/12 08:25:44 aka
18 Added comments.
19
20 Revision 1.2 2009/05/11 08:29:45 aka
21 Added description and restrictions.
22
23 Revision 1.1 2009/05/07 02:26:29 aka
24 Initial version.
25
26 $NoKeywords: $
27 *---------------------------------------------------------------------------*/
28
29 #include <string.h>
30 #include <demo.h>
31 #include <revolution.h>
32 #include <revolution/mix.h>
33 #include <revolution/mem.h>
34 #include <revolution/wpad.h>
35
36 // ###########################################################################
37 // This program is a sample of streaming playback of looped ADPCM data.
38 // ** See axstream for streaming playback of unlooped data.
39 // ###########################################################################
40
41 // ###########################################################################
42 // Description
43 // ###########################################################################
44 //
45 // For streaming playback of looped ADPCM data, you will need:
46 //
47 // (1) A streaming buffer loop
48 //
49 // (2) An ADPCM data loop
50 //
51 // You must switch between these loops depending on data playback conditions.
52 //
53 // Specifically, you must change these settings.
54 //
55 // + Voice type (AX_PB_TYPE_NORMAL <-> AX_PB_TYPE_STREAM)
56 //
57 // + Loop start address (LoopAddr)
58 //
59 // + Loop end address (EndAddr)
60 //
61 // + Loop context (AXPBADPCMLOOP)
62 //
63 //
64 //
65 // The above settings must be changed only when it is safe to do so, at times when modifying the voice parameter block does not cause any AX malfunction.
66 //
67 //
68 // ----
69 //
70 // In contrast to axstream (which was a streaming buffer only), this program adds a loop start buffer in order to support looped ADPCM data playback.
71 //
72 //
73 //
74 // In a nutshell, it operates as follows.
75 //
76 // (a) Pre-loads a certain amount of ADPCM data from the start of the loop into the loop start buffer.
77 //
78 //
79 // (b) The program normally plays back the ADPCM data in the streaming buffer as a loop (AX_PB_TYPE_STREAM).
80 // # Same procedure as for axstream.
81 //
82 //
83 // (c) Once the pointer reaches the end of the ADPCM data loop, this sample uses the loop feature to jump from the streaming buffer to the loop start buffer.
84 // This jump is performed using AX_PB_TYPE_NORMAL.
85 //
86 //
87 // (d) After playing back a certain amount of data from the loop start buffer, this sample uses the loop feature again to jump back to the streaming buffer.
88 // This jump is performed using AX_PB_TYPE_STREAM.
89 //
90 //
91 // (e) Returns to step (b).
92 //
93 // To carry out the steps just described, the program changes the aforementioned settings at the following points in time, when it is safe to do so.
94 //
95 //
96 // + Right after the pointer returns to the first half of the streaming buffer.
97 //
98 // + Right after the pointer jumps to the loop start buffer.
99 //
100 // Moreover, to ensure that settings are changed safely, the program adds an extra margin to the above points in time by waiting until:
101 //
102 //
103 // + The ADPCM data loop end (specifically, the frame including the loop end) has definitely entered the second half of the streaming buffer or reached its boundary.
104 //
105 //
106 // + The loop start buffer includes at least STRM_BUFFER_SIZE bytes of data.
107 //
108 // The placement of data in the buffers is adjusted to meet these restrictions.
109 //
110 //
111 // ###########################################################################
112
113 // ###########################################################################
114 // Restrictions
115 // ###########################################################################
116 //
117 // This program has the following restrictions.
118 //
119 // + The data size from the loop start to loop end must be STRM_BUFFER_SIZE x 2.
120 // If STRM_BUFFER_SIZE is left at the default value, the size is 8KB x 2, or roughly one second at 32000 Hz.
121 //
122 //
123 // + Make sure the frame that includes the loop end never reaches the tail end of the data.
124 // It is no problem if data after the frame that includes the loop end consists of padding, so pad as necessary to make sure there is at least 32 bytes of data.
125 //
126 //
127 // ###########################################################################
128
129 // Output debugging information
130 #define SHOW_INFO
131 //#undef SHOW_INFO
132
133 // Playback data
134 #define SAMPLE_ADPCM_L "/axdemo/stream/left_wl.dsp"
135 #define SAMPLE_ADPCM_R "/axdemo/stream/right_wl.dsp"
136
137 /*---------------------------------------------------------------------------*
138 AX-related
139 *---------------------------------------------------------------------------*/
140
141 static void callbackAudioFrame ( void );
142
143 /*---------------------------------------------------------------------------*
144 Wii Remote-related
145 *---------------------------------------------------------------------------*/
146
147 static void padInit ( MEMHeapHandle* handle );
148 static u32 padUpdate ( void );
149
150 /*---------------------------------------------------------------------------*
151 Streaming playback-related
152 *---------------------------------------------------------------------------*/
153
154 // Streaming buffer size (must be a multiple of 32)
155 // ** Change this size as needed depending on system conditions (such as frequency of DVD access outside of streaming playback).
156 //
157 #define STRM_BUFFER_SIZE (8 * 1024) // 8KB -> 448ms@32KHz
158
159 // Control Structures
160 typedef struct
161 {
162 // Operating status
163 s32 state;
164
165 // DVD access flag
166 BOOL accessDVD;
167
168 // Data read information
169 s32 readSize;
170 s32 readRests;
171 s32 readOffset;
172 s32 writeOffset;
173
174 // Previous current pointer
175 u32 prevPtr;
176
177 // Loop end addresses to set next
178 u32 nextLoopEndL;
179 u32 nextLoopEndR;
180
181 // Loop start addresses to set next
182 u32 nextLoopStartL;
183 u32 nextLoopStartR;
184
185 // Data filename
186 char* dataL;
187 char* dataR;
188
189 //
190 DVDFileInfo finfoL;
191 DVDFileInfo finfoR;
192
193 // DSPADPCM header buffer
194 u8* headerBufferL;
195 u8* headerBufferR;
196
197 // Streaming buffer
198 u8* strmBufferL;
199 u8* strmBufferR;
200
201 u32 strmBufferLHalfPtr;
202
203 // Loop start buffer
204 u8* lsBufferL;
205 u8* lsBufferR;
206
207 u32 lsBufferLTopPtr;
208
209 // Loop context
210 AXPBADPCMLOOP lsContextL;
211 AXPBADPCMLOOP lsContextR;
212
213 // Streaming buffer parameters (initial cycle)
214 s32 strm1stAddr;
215 u32 strm1stPrevPtr;
216 s32 strm1stOffset;
217 s32 strm1stCA;
218 s32 strm1stTotalRead;
219 s32 strm1stLoopEnd;
220
221 // Loop start buffer parameters
222 s32 lsLoopStart;
223 s32 lsLoopEnd;
224
225 // Streaming buffer parameters (after returning from loop start buffer)
226 s32 strmOffset;
227 s32 strmTotalRead;
228 s32 strmLoopEnd;
229
230 // AX voice(s)
231 AXVPB* voiceL;
232 AXVPB* voiceR;
233
234 } STRMInfo;
235
236 // Return value
237 typedef enum
238 {
239 STRM_ERR_NONE = 0,
240
241 STRM_ERR_CANNOT_OPEN,
242 STRM_ERR_CANNOT_READ,
243
244 STRM_ERR_INVALID_STATE,
245
246 STRM_ERR_CANNOT_ACQUIRE_VOICE,
247
248 STRM_ERR_BUSY,
249
250 STRM_ERR_INVALID_DATA,
251
252 STRM_ERR_NUM_ERRORS
253
254 } STRM_ERR;
255
256 // Functions
257 static void STRMInit ( void );
258 static s32 STRMGetWorkSize ( void );
259 static STRM_ERR STRMPrepare ( STRMInfo* strm, char* left, char* right, void* work );
260 static STRM_ERR STRMStart ( STRMInfo* strm );
261 static STRM_ERR STRMStop ( STRMInfo* strm );
262 static void STRMUpdate ( void );
263
264 /*---------------------------------------------------------------------------*
265 Name: Main
266
267 Description: Main
268
269 Arguments: None.
270
271 Returns: None.
272 *---------------------------------------------------------------------------*/
273
main(void)274 void main(void)
275 {
276 void* arenaMem2Lo;
277 void* arenaMem2Hi;
278 MEMHeapHandle hExpHeap;
279
280 void* axBuffer;
281 void* mixBuffer;
282
283 void* strmWork;
284 STRMInfo strmInfo;
285 STRM_ERR retval;
286
287 u32 push;
288
289 // Initialize DEMO
290 DEMOInit(NULL);
291
292 // Initialize Exp Heap on MEM2
293 arenaMem2Lo = OSGetMEM2ArenaLo();
294 arenaMem2Hi = OSGetMEM2ArenaHi();
295 hExpHeap = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32) arenaMem2Lo);
296
297 // Initialize AX and MIX
298 axBuffer = MEMAllocFromExpHeapEx(hExpHeap, AXGetMemorySize(AX_MAX_VOICES), 32);
299 mixBuffer = MEMAllocFromExpHeapEx(hExpHeap, MIXGetMemorySize(AX_MAX_VOICES), 32);
300
301 AIInit(NULL);
302 AXInitSpecifyMem(AX_MAX_VOICES, axBuffer);
303 MIXInitSpecifyMem(mixBuffer);
304
305 // Initialize WPAD
306 padInit(&hExpHeap);
307
308 // Initialize streaming playback
309 STRMInit();
310
311 // Register user callback for audio frames notification
312 // ** STRMUpdate() is called in the callback, so register the callback after STRMInit().
313 //
314 AXRegisterCallback(&callbackAudioFrame);
315
316 //
317 // Prepare for streaming playback
318 //
319
320 strmWork = MEMAllocFromExpHeapEx(hExpHeap, (u32)STRMGetWorkSize(), 32);
321
322 memset(&strmInfo, 0, sizeof(STRMInfo)); // Always zero-clear it the first time.
323
324 retval = STRMPrepare(&strmInfo, SAMPLE_ADPCM_L, SAMPLE_ADPCM_R, strmWork);
325
326 if (retval == STRM_ERR_CANNOT_OPEN)
327 {
328 OSHalt("Cannot open stream files.\n");
329 }
330
331 else if (retval == STRM_ERR_CANNOT_READ)
332 {
333 OSHalt("Cannot read stream files.\n");
334 }
335
336 else if (retval == STRM_ERR_INVALID_STATE)
337 {
338 OSHalt("Error : ??? (invalid STRMInfo state).\n");
339 }
340
341 else if (retval == STRM_ERR_INVALID_DATA)
342 {
343 OSHalt("Invalid ADPCM files (too short loop length).\n");
344 }
345
346 //
347 // Streaming playback controls
348 //
349
350 OSReport("Press the A button to play ADPCM stream.\n");
351 OSReport("Press the B button to stop ADPCM stream.\n");
352
353 while (1)
354 {
355 // wait for retrace
356 VIWaitForRetrace();
357
358 // check wpad
359 push = padUpdate();
360
361 // Start streaming playback
362 if (push & WPAD_BUTTON_A)
363 {
364 retval = STRMStart(&strmInfo);
365
366 if (retval == STRM_ERR_NONE)
367 {
368 OSReport("Start playing ADPCM stream.\n");
369 }
370
371 else if (retval == STRM_ERR_CANNOT_OPEN)
372 {
373 OSHalt("Cannot open stream files.\n");
374 }
375
376 else if (retval == STRM_ERR_CANNOT_READ)
377 {
378 OSHalt("Cannot read stream files.\n");
379 }
380
381 else if (retval == STRM_ERR_CANNOT_ACQUIRE_VOICE)
382 {
383 OSHalt("Cannot acquire AX voices.\n");
384 }
385
386 else if (retval == STRM_ERR_BUSY)
387 {
388 OSReport("Now playing other ADPCM stream.\n");
389 }
390
391 else // if (retval == STRM_ERR_INVALID_STATE)
392 {
393 OSReport("Now playing specified ADPCM stream.\n");
394 }
395 }
396
397 // Stop streaming playback
398 else if (push & WPAD_BUTTON_B)
399 {
400 retval = STRMStop(&strmInfo);
401
402 if (retval == STRM_ERR_NONE)
403 {
404 OSReport("Stop playing ADPCM stream.\n");
405 }
406
407 else // if (retval == STRM_ERR_INVALID_STATE)
408 {
409 OSReport("Not playing specified ADPCM stream.\n");
410 }
411 }
412 }
413 }
414
415 /*---------------------------------------------------------------------------*
416 *---------------------------------------------------------------------------*
417 * Local functions for AX
418 *---------------------------------------------------------------------------*
419 *---------------------------------------------------------------------------*/
420
421 /*---------------------------------------------------------------------------*
422 Name: callbackAudioFrame
423
424 Description: Function called every audio frame.
425
426 Arguments: None.
427
428 Returns: None.
429 *---------------------------------------------------------------------------*/
430
callbackAudioFrame(void)431 static void callbackAudioFrame(void)
432 {
433 // tell the mixer to update settings
434 MIXUpdateSettings();
435
436 // Update streaming playback
437 STRMUpdate();
438 }
439
440 /*---------------------------------------------------------------------------*
441 *---------------------------------------------------------------------------*
442 * Local functions for Wii Remote
443 *---------------------------------------------------------------------------*
444 *---------------------------------------------------------------------------*/
445
446 static MEMHeapHandle* padHeap;
447
448 static void* padAlloc ( u32 size );
449 static u8 padFree ( void* ptr );
450
451 /*---------------------------------------------------------------------------*
452 Name: padInit
453
454 Description: Initializes pad.
455
456 Arguments: handle: pointer of MEMHeapHandle.
457
458 Returns: None.
459 *---------------------------------------------------------------------------*/
460
padInit(MEMHeapHandle * handle)461 static void padInit(MEMHeapHandle* handle)
462 {
463 s32 state;
464
465 padHeap = handle;
466
467 WPADRegisterAllocator(padAlloc, padFree);
468
469 WPADInit();
470
471 do
472 {
473 state = WPADGetStatus();
474
475 } while (WPAD_STATE_SETUP != state);
476 }
477
478 /*---------------------------------------------------------------------------*
479 Name: padUpdate
480
481 Description: Updates pad status.
482
483 Arguments: None.
484
485 Returns: current pad status.
486 *---------------------------------------------------------------------------*/
487
padUpdate(void)488 static u32 padUpdate(void)
489 {
490 static u32 prevButton = 0;
491
492 s32 state;
493 u32 type;
494 WPADStatus padStatus;
495 u32 currPush;
496
497 state = WPADProbe(0, &type);
498
499 if (WPAD_ERR_NONE == state)
500 {
501 WPADRead(0, &padStatus);
502
503 currPush = (prevButton ^ padStatus.button) & padStatus.button;
504
505 prevButton = padStatus.button;
506
507 return currPush;
508 }
509
510 return 0;
511 }
512
513 /*---------------------------------------------------------------------------*
514 Name: padAlloc
515
516 Description: Memory alloc routine for wpad.
517
518 Arguments: size: size of allocating area.
519
520 Returns: pointer of allocated area.
521 *---------------------------------------------------------------------------*/
522
padAlloc(u32 size)523 static void* padAlloc(u32 size)
524 {
525 void* ptr;
526
527 ptr = MEMAllocFromExpHeapEx(*padHeap, size, 32);
528
529 return ptr;
530 }
531
532 /*---------------------------------------------------------------------------*
533 Name: padFree
534
535 Description: Memory free routine for wpad.
536
537 Arguments: ptr: pointer of freeing area.
538
539 Returns: always 1.
540 *---------------------------------------------------------------------------*/
541
padFree(void * ptr)542 static u8 padFree(void* ptr)
543 {
544
545 MEMFreeToExpHeap(*padHeap, ptr);
546
547 return 1;
548 }
549
550 /*---------------------------------------------------------------------------*
551 *---------------------------------------------------------------------------*
552 * Local functions for streaming playback
553 *---------------------------------------------------------------------------*
554 *---------------------------------------------------------------------------*/
555
556 // Conversion constants
557 #define BYTES_PER_FRAME 8
558 #define NIBBLES_PER_FRAME 16
559
560 // State
561 enum
562 {
563 STRM_NONE = 0,
564 STRM_INITIALIZED,
565 STRM_STARTED,
566 STRM_STOPPING,
567 STRM_STOPPED,
568
569 STRM_NUM_STATES
570 };
571
572 // Streaming during playback
573 static STRMInfo* activeStrm = NULL;
574
575 // Functions
576 static void getDataL ( void );
577 static void getDataR ( s32 result, DVDFileInfo* fileInfo );
578 static void gottenData ( s32 result, DVDFileInfo* fileInfo );
579
580 /*---------------------------------------------------------------------------*
581 Name: STRMInit
582
583 Description: Initialize streaming playback
584
585 Arguments: None.
586
587 Returns: None.
588 *---------------------------------------------------------------------------*/
589
STRMInit(void)590 static void STRMInit(void)
591 {
592 activeStrm = NULL;
593 }
594
595 /*---------------------------------------------------------------------------*
596 Name: STRMGetWorkSize
597
598 Description: Gets work size
599
600 Arguments: None.
601
602 Returns: Work size (bytes)
603 *---------------------------------------------------------------------------*/
604
STRMGetWorkSize(void)605 static s32 STRMGetWorkSize(void)
606 {
607 s32 size;
608
609 size = sizeof(DSPADPCM); // DSPADPCM header (96 bytes)
610 size += (STRM_BUFFER_SIZE * 2); // Streaming buffer
611 size += (STRM_BUFFER_SIZE * 2); // Loop start buffer
612 size *= 2; // Stereo
613 size += 32; // Alignment (just in case)
614
615 return size;
616 }
617
618 /*---------------------------------------------------------------------------*
619 Name: STRMPrepare
620
621 Description: Prepares for streaming playback.
622
623 Arguments: strm: Pointer to STRMInfo.
624 left: Data for left channel.
625 right: Data for right channel.
626 work: Pointer to allocated work region.
627
628 Returns: STRM_ERR_NONE: OK.
629 STRM_ERR_CANNOT_OPEN: Failed to open file
630 STRM_ERR_CANNOT_READ: Failed to load file
631 STRM_ERR_INVALID_STATE: STRMInfo is in use
632 STRM_ERR_INVALID_DATA: Invalid data (loop is too short)
633 *---------------------------------------------------------------------------*/
634
STRMPrepare(STRMInfo * strm,char * left,char * right,void * work)635 static STRM_ERR STRMPrepare(STRMInfo* strm, char* left, char* right, void* work)
636 {
637 u32 ptr;
638
639 s32 length;
640
641 DSPADPCM* dspHeaderL;
642 DSPADPCM* dspHeaderR;
643
644 s32 frameCA, frameLoopStart, frameLoopEnd;
645 s32 deltaToLoopEnd1, deltaToLoopEnd2;
646 s32 buffers1, buffers2;
647
648 s32 strm1stAddr;
649 u32 strm1stPrevPtr;
650 s32 strm1stOffset;
651 s32 strm1stCA;
652 s32 strm1stTotalRead;
653 s32 strm1stLoopEnd;
654
655 s32 lsLength;
656 s32 lsOffset;
657 s32 lsLoopStart;
658 s32 lsLoopEnd;
659
660 s32 strmOffset;
661 s32 strmTotalRead;
662 s32 strmLoopEnd;
663
664 //
665 // Check the state
666 //
667
668 if (strm->state > STRM_INITIALIZED)
669 {
670 return STRM_ERR_INVALID_STATE;
671 }
672
673 //
674 // Memory allocation
675 //
676 // ** Due to the relationship of pointer checks in STRMUpdate(), the streaming buffer must be allocated before allocating the loop start buffer.
677 //
678 //
679
680 ptr = ((u32)work + 0x1f) & ~0x1f;
681
682 strm->headerBufferL = (u8*)ptr;
683 ptr += sizeof(DSPADPCM);
684
685 strm->strmBufferL = (u8*)ptr;
686 ptr += STRM_BUFFER_SIZE * 2;
687
688 strm->lsBufferL = (u8*)ptr;
689 ptr += STRM_BUFFER_SIZE * 2;
690
691 strm->headerBufferR = (u8*)ptr;
692 ptr += sizeof(DSPADPCM);
693
694 strm->strmBufferR = (u8*)ptr;
695 ptr += STRM_BUFFER_SIZE * 2;
696
697 strm->lsBufferR = (u8*)ptr;
698 ptr += STRM_BUFFER_SIZE * 2;
699
700 //
701 // Initialize members
702 //
703
704 // Filename
705 strm->dataL = left;
706 strm->dataR = right;
707
708 // DVD access flag
709 strm->accessDVD = FALSE;
710
711 // Buffer management threshold (nibbles, absolute address)
712 strm->strmBufferLHalfPtr = (u32)OSCachedToPhysical(strm->strmBufferL) + STRM_BUFFER_SIZE;
713 strm->strmBufferLHalfPtr *= 2; // Bytes -> nibbles
714 strm->lsBufferLTopPtr = (u32)OSCachedToPhysical(strm->lsBufferL);
715 strm->lsBufferLTopPtr *= 2; // Bytes -> nibbles
716
717 //
718 // Open the file
719 //
720
721 if (!DVDOpen(left, &strm->finfoL))
722 {
723 strm->state = STRM_NONE;
724
725 return STRM_ERR_CANNOT_OPEN;
726 }
727
728 if (!DVDOpen(right, &strm->finfoR))
729 {
730 DVDClose(&strm->finfoL);
731
732 strm->state = STRM_NONE;
733
734 return STRM_ERR_CANNOT_OPEN;
735 }
736
737 //
738 // Load header
739 //
740
741 length = sizeof(DSPADPCM);
742
743 if (length != DVDRead(&strm->finfoL, strm->headerBufferL, length, 0) ||
744 length != DVDRead(&strm->finfoR, strm->headerBufferR, length, 0))
745 {
746 DVDClose(&strm->finfoL);
747 DVDClose(&strm->finfoR);
748
749 strm->state = STRM_NONE;
750
751 return STRM_ERR_CANNOT_READ;
752 }
753
754 dspHeaderL = (DSPADPCM*)strm->headerBufferL;
755 dspHeaderR = (DSPADPCM*)strm->headerBufferR;
756
757 //
758 // Set fixed values for parameters needed to load data (such as address and amount to load)
759 //
760 // ** The addresses are represented by the 'Lch' data.
761 //
762
763 // Convert each address to a frame offset
764 frameCA = (s32)(dspHeaderL->ca / NIBBLES_PER_FRAME);
765 frameLoopStart = (s32)(dspHeaderL->sa / NIBBLES_PER_FRAME);
766 frameLoopEnd = (s32)(dspHeaderL->ea / NIBBLES_PER_FRAME);
767
768 // Calculate initial position and amount of data that must be loaded from loop start to loop end
769 deltaToLoopEnd1 = (frameLoopEnd - frameCA + 1) * BYTES_PER_FRAME;
770 deltaToLoopEnd2 = (frameLoopEnd - frameLoopStart + 1) * BYTES_PER_FRAME;
771
772 // Convert amount of data to load -> number of streaming buffers (single sided)
773 buffers1 = deltaToLoopEnd1 / STRM_BUFFER_SIZE;
774 buffers2 = deltaToLoopEnd2 / STRM_BUFFER_SIZE;
775
776 if (buffers1 < 2 || buffers2 < 2)
777 {
778 DVDClose(&strm->finfoL);
779 DVDClose(&strm->finfoR);
780
781 strm->state = STRM_NONE;
782
783 return STRM_ERR_INVALID_DATA;
784 }
785
786 #ifdef SHOW_INFO
787 OSReport("ca %ld\n", dspHeaderL->ca);
788 OSReport("sa %ld\n", dspHeaderL->sa);
789 OSReport("es %ld\n", dspHeaderL->ea);
790
791 OSReport("frameCA %ld\n", frameCA);
792 OSReport("frameLoopStart %ld\n", frameLoopStart);
793 OSReport("frameLoopEnd %ld\n", frameLoopEnd);
794
795 OSReport("deltaToLoopEnd1 %ld\n", deltaToLoopEnd1);
796 OSReport("deltaToLoopEnd2 %ld\n", deltaToLoopEnd2);
797
798 OSReport("buffers1 %ld\n", buffers1);
799 OSReport("buffers2 %ld\n", buffers2);
800 #endif
801
802 // Calculate parameters related to the streaming buffer (initial cycle)
803 // - Load destination for loaded data: strm1stAddr
804 // - Initial value of previous current pointer: strm1stPrevPtr
805 // - Initial value of current address: strm1stCA
806 // - Offset of load data: strm1stOffset
807 // - Amount of data to load until loop end: strm1stTotalRead
808 // - Loop end address: strm1stLoopEnd
809 //
810 // ** The load destination for the initial data (strm1stAddr) is adjusted so that the loop end (specifically, the frame including the loop end) will enter the second half of the streaming buffer or reach its boundary.
811 //
812
813 if (buffers1 & 0x1)
814 {
815 strm1stAddr = 0;
816 strm1stPrevPtr = (u32)OSCachedToPhysical(strm->strmBufferL) + STRM_BUFFER_SIZE * 2;
817 strm1stPrevPtr *= 2; // Bytes -> nibbles
818 strm1stPrevPtr += -1; // Specify the last nibble
819 }
820 else
821 {
822 strm1stAddr = STRM_BUFFER_SIZE;
823 strm1stPrevPtr = (u32)OSCachedToPhysical(strm->strmBufferL);
824 strm1stPrevPtr *= 2; // Bytes -> nibbles
825 strm1stPrevPtr += 2; // Skip frame header
826 }
827
828 strm1stOffset = frameCA * BYTES_PER_FRAME + (s32)sizeof(DSPADPCM);
829 strm1stCA = strm1stAddr;
830 strm1stCA *= 2; // Bytes -> nibbles
831 strm1stCA += (s32)(dspHeaderL->ca % NIBBLES_PER_FRAME);
832 strm1stTotalRead = deltaToLoopEnd1;
833 strm1stLoopEnd = STRM_BUFFER_SIZE + strm1stTotalRead % STRM_BUFFER_SIZE - BYTES_PER_FRAME;
834 strm1stLoopEnd *= 2; // Bytes -> nibbles
835 strm1stLoopEnd += (s32)(dspHeaderL->ea % NIBBLES_PER_FRAME);
836
837 // Calculate parameters related to loop start buffer
838 // - Amount of data to load: lsLength
839 // - Offset of load data: lsOffset
840 // - Loop start address: lsLoopStart
841 // - Loop end address: lsLoopEnd
842 //
843 // ** The amount of data to load into the loop start buffer (lsLength) is adjusted so that the loop end (specifically, the frame including the loop end) enters the second half of the streaming buffer or reaches its boundary.
844 //
845
846 if (buffers2 & 0x1)
847 {
848 lsLength = STRM_BUFFER_SIZE * 2;
849 }
850 else
851 {
852 lsLength = STRM_BUFFER_SIZE;
853 }
854
855 lsOffset = frameLoopStart * BYTES_PER_FRAME + (s32)sizeof(DSPADPCM);
856 lsLoopStart = (s32)(dspHeaderL->sa % NIBBLES_PER_FRAME);
857 lsLoopEnd = lsLength;
858 lsLoopEnd *= 2; // Bytes -> nibbles
859 lsLoopEnd += -1; // Specify the last nibble
860
861 // Calculate parameters related to streaming buffer (these parameters applied after returning from loop start buffer)
862 // - Offset of load data: strmOffset
863 // - Amount of data to load until loop end: strmTotalRead
864 // - Loop end address: strmLoopEnd
865
866 strmOffset = lsOffset + lsLength;
867 strmTotalRead = deltaToLoopEnd2 - lsLength;
868 strmLoopEnd = STRM_BUFFER_SIZE + strmTotalRead % STRM_BUFFER_SIZE - BYTES_PER_FRAME;
869 strmLoopEnd *= 2; // Bytes -> nibbles
870 strmLoopEnd += (s32)(dspHeaderL->ea % NIBBLES_PER_FRAME);
871
872 // Save
873 strm->strm1stAddr = strm1stAddr;
874 strm->strm1stPrevPtr = strm1stPrevPtr;
875 strm->strm1stOffset = strm1stOffset;
876 strm->strm1stCA = strm1stCA;
877 strm->strm1stTotalRead = strm1stTotalRead;
878 strm->strm1stLoopEnd = strm1stLoopEnd;
879
880 strm->lsLoopStart = lsLoopStart;
881 strm->lsLoopEnd = lsLoopEnd;
882
883 strm->strmOffset = strmOffset;
884 strm->strmTotalRead = strmTotalRead;
885 strm->strmLoopEnd = strmLoopEnd;
886
887 //
888 // Pre-load data into loop start buffer
889 //
890
891 if (lsLength != DVDRead(&strm->finfoL, strm->lsBufferL, lsLength, lsOffset) ||
892 lsLength != DVDRead(&strm->finfoR, strm->lsBufferR, lsLength, lsOffset))
893 {
894 DVDClose(&strm->finfoL);
895 DVDClose(&strm->finfoR);
896
897 strm->state = STRM_NONE;
898
899 return STRM_ERR_CANNOT_READ;
900 }
901
902 //
903 // Set loop context
904 //
905
906 strm->lsContextL.loop_pred_scale = dspHeaderL->lps;
907 strm->lsContextL.loop_yn1 = dspHeaderL->lyn1;
908 strm->lsContextL.loop_yn2 = dspHeaderL->lyn2;
909
910 strm->lsContextR.loop_pred_scale = dspHeaderR->lps;
911 strm->lsContextR.loop_yn1 = dspHeaderR->lyn1;
912 strm->lsContextR.loop_yn2 = dspHeaderR->lyn2;
913
914 //
915 // Normal end
916 //
917
918 DVDClose(&strm->finfoL);
919 DVDClose(&strm->finfoR);
920
921 strm->state = STRM_INITIALIZED;
922
923 return STRM_ERR_NONE;
924 }
925
926 /*---------------------------------------------------------------------------*
927 Name: STRMStart
928
929 Description: Start streaming playback
930
931 Arguments: strm: Pointer to STRMInfo.
932
933 Returns: STRM_ERR_NONE: OK.
934 STRM_ERR_CANNOT_OPEN: Failed to open file
935 STRM_ERR_CANNOT_READ: Failed to load file
936 STRM_ERR_INVALID_STATE: STRMInfo not initialized, or is in use
937 STRM_ERR_CANNOT_ACQUIRE_VOICE
938 Failed to get AX voice
939 STRM_ERR_BUSY: Other stream currently being played back
940 *---------------------------------------------------------------------------*/
941
STRMStart(STRMInfo * strm)942 static STRM_ERR STRMStart(STRMInfo* strm)
943 {
944 s32 length;
945
946 DSPADPCM* dspHeader;
947
948 u32 loopStart;
949 u32 loopEnd;
950 u32 currAddr;
951
952 AXPBADDR addr;
953 AXPBADPCM adpcm;
954
955 //
956 // Check the state
957 //
958
959 if (strm->state != STRM_INITIALIZED)
960 {
961 return STRM_ERR_INVALID_STATE;
962 }
963
964 else if (activeStrm)
965 {
966 return STRM_ERR_BUSY;
967 }
968
969 //
970 // Open the file
971 //
972
973 if (!DVDOpen(strm->dataL, &strm->finfoL))
974 {
975 return STRM_ERR_CANNOT_OPEN;
976 }
977
978 if (!DVDOpen(strm->dataR, &strm->finfoR))
979 {
980 DVDClose(&strm->finfoL);
981
982 return STRM_ERR_CANNOT_OPEN;
983 }
984
985 //
986 // Load initial data into streaming buffer
987 //
988
989 length = STRM_BUFFER_SIZE;
990
991 if (length != DVDRead(&strm->finfoL,
992 strm->strmBufferL + strm->strm1stAddr,
993 length,
994 strm->strm1stOffset) ||
995 length != DVDRead(&strm->finfoR,
996 strm->strmBufferR + strm->strm1stAddr,
997 length,
998 strm->strm1stOffset))
999 {
1000 DVDClose(&strm->finfoL);
1001 DVDClose(&strm->finfoR);
1002
1003 return STRM_ERR_CANNOT_READ;
1004 }
1005
1006 strm->prevPtr = strm->strm1stPrevPtr;
1007 strm->readRests = strm->strm1stTotalRead - length;
1008 strm->readOffset = strm->strm1stOffset + length;
1009
1010 //
1011 // Prepare for next jump (streaming -> loop start)
1012 //
1013
1014 // Loop end (= the end of the streaming buffer)
1015 strm->nextLoopEndL = (u32)OSCachedToPhysical(strm->strmBufferL);
1016 strm->nextLoopEndL *= 2; // Bytes -> nibbles
1017 strm->nextLoopEndL += (u32)strm->strm1stLoopEnd;
1018 strm->nextLoopEndR = (u32)OSCachedToPhysical(strm->strmBufferR);
1019 strm->nextLoopEndR *= 2; // Bytes -> nibbles
1020 strm->nextLoopEndR += (u32)strm->strm1stLoopEnd;
1021
1022 // Loop start (= beginning of the loop start buffer)
1023 strm->nextLoopStartL = (u32)OSCachedToPhysical(strm->lsBufferL);
1024 strm->nextLoopStartL *= 2; // Bytes -> nibbles
1025 strm->nextLoopStartL += (u32)strm->lsLoopStart;
1026 strm->nextLoopStartR = (u32)OSCachedToPhysical(strm->lsBufferR);
1027 strm->nextLoopStartR *= 2; // Bytes -> nibbles
1028 strm->nextLoopStartR += (u32)strm->lsLoopStart;
1029
1030 //
1031 // Set AX
1032 //
1033
1034 // Get AX voice
1035 if (!(strm->voiceL = AXAcquireVoice(15, NULL, 0)) ||
1036 !(strm->voiceR = AXAcquireVoice(15, NULL, 0)))
1037 {
1038 DVDClose(&strm->finfoL);
1039 DVDClose(&strm->finfoR);
1040
1041 return STRM_ERR_CANNOT_ACQUIRE_VOICE;
1042 }
1043
1044 // Set left channel
1045
1046 dspHeader = (DSPADPCM*)strm->headerBufferL;
1047
1048 loopStart = (u32)OSCachedToPhysical(strm->strmBufferL) * 2;
1049 loopEnd = loopStart + (STRM_BUFFER_SIZE * 4) - 1;
1050 currAddr = loopStart + (u32)strm->strm1stCA;
1051
1052 loopStart += 2; // Skip frame header
1053
1054 MIXInitChannel(strm->voiceL, 0, 0, -904, -904, -904, 0, 127, 0);
1055
1056 addr.loopFlag = AXPBADDR_LOOP_ON;
1057 addr.format = AX_PB_FORMAT_ADPCM;
1058 addr.loopAddressHi = (u16)(loopStart >> 16);
1059 addr.loopAddressLo = (u16)(loopStart & 0xFFFF);
1060 addr.endAddressHi = (u16)(loopEnd >> 16);
1061 addr.endAddressLo = (u16)(loopEnd & 0xFFFF);
1062 addr.currentAddressHi = (u16)(currAddr >> 16);
1063 addr.currentAddressLo = (u16)(currAddr & 0xFFFF);
1064
1065 adpcm.a[0][0] = dspHeader->coef[0];
1066 adpcm.a[0][1] = dspHeader->coef[1];
1067 adpcm.a[1][0] = dspHeader->coef[2];
1068 adpcm.a[1][1] = dspHeader->coef[3];
1069 adpcm.a[2][0] = dspHeader->coef[4];
1070 adpcm.a[2][1] = dspHeader->coef[5];
1071 adpcm.a[3][0] = dspHeader->coef[6];
1072 adpcm.a[3][1] = dspHeader->coef[7];
1073 adpcm.a[4][0] = dspHeader->coef[8];
1074 adpcm.a[4][1] = dspHeader->coef[9];
1075 adpcm.a[5][0] = dspHeader->coef[10];
1076 adpcm.a[5][1] = dspHeader->coef[11];
1077 adpcm.a[6][0] = dspHeader->coef[12];
1078 adpcm.a[6][1] = dspHeader->coef[13];
1079 adpcm.a[7][0] = dspHeader->coef[14];
1080 adpcm.a[7][1] = dspHeader->coef[15];
1081 adpcm.gain = dspHeader->gain;
1082 adpcm.pred_scale = dspHeader->ps;
1083 adpcm.yn1 = dspHeader->yn1;
1084 adpcm.yn2 = dspHeader->yn2;
1085
1086 AXSetVoiceType (strm->voiceL, AX_PB_TYPE_STREAM);
1087 AXSetVoiceAdpcm (strm->voiceL, &adpcm);
1088 AXSetVoiceAddr (strm->voiceL, &addr);
1089 AXSetVoiceSrcType(strm->voiceL, AX_SRC_TYPE_NONE);
1090
1091 // Set right channel
1092
1093 dspHeader = (DSPADPCM*)strm->headerBufferR;
1094
1095 loopStart = (u32)OSCachedToPhysical(strm->strmBufferR) * 2;
1096 loopEnd = loopStart + (STRM_BUFFER_SIZE * 4) - 1;
1097 currAddr = loopStart + (u32)strm->strm1stCA;
1098
1099 loopStart += 2; // Skip frame header
1100
1101 MIXInitChannel(strm->voiceR, 0, 0, -904, -904, -904, 127, 127, 0);
1102
1103 addr.loopFlag = AXPBADDR_LOOP_ON;
1104 addr.format = AX_PB_FORMAT_ADPCM;
1105 addr.loopAddressHi = (u16)(loopStart >> 16);
1106 addr.loopAddressLo = (u16)(loopStart & 0xFFFF);
1107 addr.endAddressHi = (u16)(loopEnd >> 16);
1108 addr.endAddressLo = (u16)(loopEnd & 0xFFFF);
1109 addr.currentAddressHi = (u16)(currAddr >> 16);
1110 addr.currentAddressLo = (u16)(currAddr & 0xFFFF);
1111
1112 adpcm.a[0][0] = dspHeader->coef[0];
1113 adpcm.a[0][1] = dspHeader->coef[1];
1114 adpcm.a[1][0] = dspHeader->coef[2];
1115 adpcm.a[1][1] = dspHeader->coef[3];
1116 adpcm.a[2][0] = dspHeader->coef[4];
1117 adpcm.a[2][1] = dspHeader->coef[5];
1118 adpcm.a[3][0] = dspHeader->coef[6];
1119 adpcm.a[3][1] = dspHeader->coef[7];
1120 adpcm.a[4][0] = dspHeader->coef[8];
1121 adpcm.a[4][1] = dspHeader->coef[9];
1122 adpcm.a[5][0] = dspHeader->coef[10];
1123 adpcm.a[5][1] = dspHeader->coef[11];
1124 adpcm.a[6][0] = dspHeader->coef[12];
1125 adpcm.a[6][1] = dspHeader->coef[13];
1126 adpcm.a[7][0] = dspHeader->coef[14];
1127 adpcm.a[7][1] = dspHeader->coef[15];
1128 adpcm.gain = dspHeader->gain;
1129 adpcm.pred_scale = dspHeader->ps;
1130 adpcm.yn1 = dspHeader->yn1;
1131 adpcm.yn2 = dspHeader->yn2;
1132
1133 AXSetVoiceType (strm->voiceR, AX_PB_TYPE_STREAM);
1134 AXSetVoiceAdpcm (strm->voiceR, &adpcm);
1135 AXSetVoiceAddr (strm->voiceR, &addr);
1136 AXSetVoiceSrcType(strm->voiceR, AX_SRC_TYPE_NONE);
1137
1138 //
1139 // Start playback
1140 //
1141
1142 AXSetVoiceState(strm->voiceL, AX_PB_STATE_RUN);
1143 AXSetVoiceState(strm->voiceR, AX_PB_STATE_RUN);
1144
1145 //
1146 // Normal end
1147 //
1148
1149 strm->state = STRM_STARTED;
1150
1151 activeStrm = strm;
1152
1153 return STRM_ERR_NONE;
1154 }
1155
1156 /*---------------------------------------------------------------------------*
1157 Name: STRMStop
1158
1159 Description: Stop streaming playback
1160
1161 Arguments: strm: Pointer to STRMInfo.
1162
1163 Returns: STRM_ERR_NONE: OK.
1164 STRM_ERR_INVALID_STATE: Specified streaming buffer is not currently playing
1165 *---------------------------------------------------------------------------*/
1166
STRMStop(STRMInfo * strm)1167 static STRM_ERR STRMStop(STRMInfo* strm)
1168 {
1169 if (strm->state != STRM_STARTED)
1170 {
1171 return STRM_ERR_INVALID_STATE;
1172 }
1173
1174 MIXSetFader(strm->voiceL, -960);
1175 MIXSetFader(strm->voiceR, -960);
1176
1177 strm->state = STRM_STOPPING;
1178
1179 return STRM_ERR_NONE;
1180 }
1181
1182 /*---------------------------------------------------------------------------*
1183 Name: STRMUpdate
1184
1185 Description: Update streaming playback
1186
1187 Arguments: None.
1188
1189 Returns: None.
1190 *---------------------------------------------------------------------------*/
1191
STRMUpdate(void)1192 static void STRMUpdate(void)
1193 {
1194 u32 currPtr;
1195 u32 halfPtr;
1196 u32 lsPtr;
1197 u32 prevPtr;
1198
1199 u32 loopStartL, loopStartR;
1200 u32 loopEndL, loopEndR;
1201
1202 if (!activeStrm)
1203 {
1204 return;
1205 }
1206
1207 switch (activeStrm->state)
1208 {
1209 case STRM_NONE:
1210 case STRM_INITIALIZED:
1211
1212 break;
1213
1214 case STRM_STARTED:
1215
1216 //
1217 // Get address
1218 //
1219 // ** The addresses are represented by the 'Lch' data.
1220 //
1221
1222 currPtr = (u32)((activeStrm->voiceL->pb.addr.currentAddressHi << 16) |
1223 (activeStrm->voiceL->pb.addr.currentAddressLo));
1224 halfPtr = activeStrm->strmBufferLHalfPtr;
1225 lsPtr = activeStrm->lsBufferLTopPtr;
1226 prevPtr = activeStrm->prevPtr;
1227
1228 //
1229 // Switch between behavior depending on positions of currPtr and prevPtr
1230 //
1231
1232 if (currPtr < prevPtr)
1233 {
1234 // When the current pointer has returned to the first half of the streaming buffer either from the second half of the streaming buffer or from the the loop start buffer...
1235 //
1236 //
1237 //
1238 //
1239 //
1240 //
1241 //
1242 // -> Check amount of data remaining
1243
1244 if (!activeStrm->accessDVD)
1245 {
1246 if (activeStrm->readRests < STRM_BUFFER_SIZE)
1247 {
1248 // Amount of data remaining is less than STRM_BUFFER_SIZE
1249 //
1250 // -> Jump to loop start buffer while processing second half
1251 //
1252 // -> (1) Set next jump (streaming -> loop start)
1253 // (2) Prepare for jump after next (loop start -> streaming)
1254 // (3) Fill second half of streaming buffer with data
1255
1256 //
1257 // (1) Set next jump (streaming -> loop start)
1258 //
1259
1260 // Set loop end (= end of streaming buffer)
1261 AXSetVoiceEndAddr(activeStrm->voiceL, activeStrm->nextLoopEndL);
1262 AXSetVoiceEndAddr(activeStrm->voiceR, activeStrm->nextLoopEndR);
1263
1264 // Set loop start (= beginning of loop start buffer)
1265 AXSetVoiceLoopAddr(activeStrm->voiceL, activeStrm->nextLoopStartL);
1266 AXSetVoiceLoopAddr(activeStrm->voiceR, activeStrm->nextLoopStartR);
1267
1268 // Set loop context
1269 AXSetVoiceAdpcmLoop(activeStrm->voiceL, &activeStrm->lsContextL);
1270 AXSetVoiceAdpcmLoop(activeStrm->voiceR, &activeStrm->lsContextR);
1271
1272 // Set loop type
1273 AXSetVoiceType(activeStrm->voiceL, AX_PB_TYPE_NORMAL);
1274 AXSetVoiceType(activeStrm->voiceR, AX_PB_TYPE_NORMAL);
1275
1276 //
1277 // (2) Prepare for jump after next (loop start -> streaming)
1278 //
1279
1280 // Loop end (= end of loop start buffer)
1281 activeStrm->nextLoopEndL = (u32)OSCachedToPhysical(activeStrm->lsBufferL);
1282 activeStrm->nextLoopEndL *= 2; // Bytes -> nibbles
1283 activeStrm->nextLoopEndL += (u32)activeStrm->lsLoopEnd;
1284 activeStrm->nextLoopEndR = (u32)OSCachedToPhysical(activeStrm->lsBufferR);
1285 activeStrm->nextLoopEndR *= 2; // Bytes -> nibbles
1286 activeStrm->nextLoopEndR += (u32)activeStrm->lsLoopEnd;
1287
1288 // Loop start (= beginning of streaming buffer)
1289 activeStrm->nextLoopStartL = (u32)OSCachedToPhysical(activeStrm->strmBufferL);
1290 activeStrm->nextLoopStartL *= 2; // Bytes -> nibbles
1291 activeStrm->nextLoopStartL += 2; // Skip frame header
1292 activeStrm->nextLoopStartR = (u32)OSCachedToPhysical(activeStrm->strmBufferR);
1293 activeStrm->nextLoopStartR *= 2; // Bytes -> nibbles
1294 activeStrm->nextLoopStartR += 2; // Skip frame header
1295
1296 //
1297 // (3) Fill second half of streaming buffer with remaining data
1298 //
1299
1300 activeStrm->readSize = activeStrm->readRests;
1301 activeStrm->readRests = 0;
1302 }
1303
1304 else
1305 {
1306 // Amount of data remaining is more than STRM_BUFFER_SIZE
1307 //
1308 // -> Do not jump to the loop start buffer while processing second half
1309 //
1310 // -> (1) Set the loop to be within the streaming buffer
1311 // (2) Fill second half of streaming buffer with data
1312
1313 //
1314 // (1) Set the loop to be within the streaming buffer
1315 //
1316
1317 loopStartL = (u32)OSCachedToPhysical(activeStrm->strmBufferL);
1318 loopEndL = loopStartL + STRM_BUFFER_SIZE * 2;
1319 loopStartL *= 2; // Bytes -> nibbles
1320 loopStartL += 2; // Skip frame header
1321 loopEndL *= 2; // Bytes -> nibbles
1322 loopEndL += -1; // Specify the last nibble
1323
1324 loopStartR = (u32)OSCachedToPhysical(activeStrm->strmBufferR);
1325 loopEndR = loopStartR + STRM_BUFFER_SIZE * 2;
1326 loopStartR *= 2; // Bytes -> nibbles
1327 loopStartR += 2; // Skip frame header
1328 loopEndR *= 2; // Bytes -> nibbles
1329 loopEndR += -1; // Specify the last nibble
1330
1331 // Set loop end (= position of end of the streaming buffer)
1332 AXSetVoiceEndAddr(activeStrm->voiceL, loopEndL);
1333 AXSetVoiceEndAddr(activeStrm->voiceR, loopEndR);
1334
1335 // Set loop start (= position of beginning of streaming buffer)
1336 AXSetVoiceLoopAddr(activeStrm->voiceL, loopStartL);
1337 AXSetVoiceLoopAddr(activeStrm->voiceR, loopStartR);
1338
1339 // No need to set loop context
1340
1341 // No need to set loop type (already set)
1342
1343 //
1344 // (2) Fill second half of streaming buffer with data
1345 //
1346
1347 activeStrm->readSize = STRM_BUFFER_SIZE;
1348 activeStrm->readRests -= STRM_BUFFER_SIZE;
1349 }
1350
1351 // Fill second half of streaming buffer with data
1352 if (activeStrm->readSize)
1353 {
1354 activeStrm->writeOffset = STRM_BUFFER_SIZE;
1355
1356 getDataL();
1357
1358 activeStrm->accessDVD = TRUE;
1359 }
1360
1361 activeStrm->prevPtr = currPtr;
1362 }
1363 }
1364
1365 else if ((currPtr >= lsPtr) && (prevPtr < lsPtr))
1366 {
1367 // When the current pointer has jumped to the loop start buffer from the streaming buffer...
1368 //
1369 //
1370 //
1371 //
1372 //
1373 // -> (1) Set next jump (loop start -> streaming)
1374 // (2) Prepare for jump after next (streaming -> loop start)
1375 // (3) Fill first half of streaming buffer with data
1376
1377 if (!activeStrm->accessDVD)
1378 {
1379 //
1380 // (1) Set next jump (loop start -> streaming)
1381 //
1382
1383 // Set loop end (= end of loop start buffer)
1384 AXSetVoiceEndAddr(activeStrm->voiceL, activeStrm->nextLoopEndL);
1385 AXSetVoiceEndAddr(activeStrm->voiceR, activeStrm->nextLoopEndR);
1386
1387 // Set loop start (= beginning of streaming buffer)
1388 AXSetVoiceLoopAddr(activeStrm->voiceL, activeStrm->nextLoopStartL);
1389 AXSetVoiceLoopAddr(activeStrm->voiceR, activeStrm->nextLoopStartR);
1390
1391 // Set loop context after getting data
1392
1393 // Set loop type
1394 AXSetVoiceType(activeStrm->voiceL, AX_PB_TYPE_STREAM);
1395 AXSetVoiceType(activeStrm->voiceR, AX_PB_TYPE_STREAM);
1396
1397 //
1398 // (2) Prepare for jump after next (streaming -> loop start)
1399 //
1400
1401 // Loop end (= the end of the streaming buffer)
1402 activeStrm->nextLoopEndL = (u32)OSCachedToPhysical(activeStrm->strmBufferL);
1403 activeStrm->nextLoopEndL *= 2; // Bytes -> nibbles
1404 activeStrm->nextLoopEndL += (u32)activeStrm->strmLoopEnd;
1405 activeStrm->nextLoopEndR = (u32)OSCachedToPhysical(activeStrm->strmBufferR);
1406 activeStrm->nextLoopEndR *= 2; // Bytes -> nibbles
1407 activeStrm->nextLoopEndR += (u32)activeStrm->strmLoopEnd;
1408
1409 // Loop start (= beginning of the loop start buffer)
1410 activeStrm->nextLoopStartL = (u32)OSCachedToPhysical(activeStrm->lsBufferL);
1411 activeStrm->nextLoopStartL *= 2; // Bytes -> nibbles
1412 activeStrm->nextLoopStartL += (u32)activeStrm->lsLoopStart;
1413 activeStrm->nextLoopStartR = (u32)OSCachedToPhysical(activeStrm->lsBufferR);
1414 activeStrm->nextLoopStartR *= 2; // Bytes -> nibbles
1415 activeStrm->nextLoopStartR += (u32)activeStrm->lsLoopStart;
1416
1417 //
1418 // (3) Fill first half of streaming buffer with data
1419 //
1420
1421 activeStrm->readSize = STRM_BUFFER_SIZE;
1422 activeStrm->readRests = activeStrm->strmTotalRead - STRM_BUFFER_SIZE;
1423 activeStrm->readOffset = activeStrm->strmOffset;
1424 activeStrm->writeOffset = 0;
1425
1426 getDataL();
1427
1428 activeStrm->accessDVD = TRUE;
1429
1430 activeStrm->prevPtr = currPtr;
1431 }
1432 }
1433
1434 else if ((currPtr >= halfPtr) && (prevPtr < halfPtr))
1435 {
1436 // When the current pointer has jumped to the second half of the streaming buffer from the first half of the streaming buffer...
1437 //
1438 //
1439 //
1440 //
1441 //
1442 // -> Check amount of data remaining
1443
1444 if (!activeStrm->accessDVD)
1445 {
1446 if (activeStrm->readRests > 0)
1447 {
1448 // There is data remaining
1449 //
1450 // -> Do not jump to the loop start buffer while processing second half
1451
1452 // No need to set loop end
1453
1454 // No need to set loop start
1455
1456 // Set loop context after getting data
1457
1458 // No need to set loop type
1459
1460 //
1461 // Fill first half of streaming buffer with data
1462 //
1463
1464 activeStrm->readSize = STRM_BUFFER_SIZE;
1465 activeStrm->readRests -= STRM_BUFFER_SIZE;
1466 activeStrm->writeOffset = 0;
1467
1468 getDataL();
1469
1470 activeStrm->accessDVD = TRUE;
1471 }
1472
1473 else
1474 {
1475 // There is no data remaining
1476 //
1477 // -> Jump to loop start buffer while processing second half
1478
1479 // Does not do anything
1480 }
1481
1482 activeStrm->prevPtr = currPtr;
1483 }
1484 }
1485
1486 else
1487 {
1488 activeStrm->prevPtr = currPtr;
1489 }
1490
1491 break;
1492
1493 case STRM_STOPPING:
1494
1495 // Lch
1496 MIXReleaseChannel(activeStrm->voiceL);
1497 AXFreeVoice(activeStrm->voiceL);
1498 activeStrm->voiceL = NULL;
1499
1500 // Rch
1501 MIXReleaseChannel(activeStrm->voiceR);
1502 AXFreeVoice(activeStrm->voiceR);
1503 activeStrm->voiceR = NULL;
1504
1505 activeStrm->state = STRM_STOPPED;
1506
1507 break;
1508
1509 case STRM_STOPPED:
1510
1511 if (!activeStrm->accessDVD)
1512 {
1513 DVDClose(&activeStrm->finfoL);
1514 DVDClose(&activeStrm->finfoR);
1515
1516 activeStrm->state = STRM_INITIALIZED;
1517
1518 activeStrm = NULL;
1519 }
1520
1521 break;
1522 }
1523 }
1524
1525 /*---------------------------------------------------------------------------*
1526 Name: getDataL
1527
1528 Description: Loads stream data for Lch.
1529
1530 Arguments: None.
1531
1532 Returns: None.
1533 *---------------------------------------------------------------------------*/
1534
getDataL(void)1535 static void getDataL(void)
1536 {
1537 s32 length;
1538
1539 // Regarding loads:
1540 // (1) Their size is in units of a single side of the streaming buffer (STRM_BUFFER_SIZE).
1541 // (2) However, right before jumping to the loop start buffer, it's possible that an entire single side will not be filled.
1542 // (3) Even in case (2), size is a multiple of the frame length (= 8 bytes).
1543 // (4) File offset is the frame alignment (= 8 bytes).
1544
1545 length = (activeStrm->readSize + 0x1f) & ~0x1f;
1546
1547 DVDReadAsync (&activeStrm->finfoL,
1548 activeStrm->strmBufferL + activeStrm->writeOffset,
1549 length,
1550 activeStrm->readOffset,
1551 getDataR);
1552 }
1553
1554 /*---------------------------------------------------------------------------*
1555 Name: getDataR
1556
1557 Description: Loads stream data for Rch.
1558
1559 Arguments: Not used.
1560
1561 Returns: None.
1562 *---------------------------------------------------------------------------*/
1563
getDataR(s32 result,DVDFileInfo * fileInfo)1564 static void getDataR(s32 result, DVDFileInfo* fileInfo)
1565 {
1566 #pragma unused(result)
1567 #pragma unused(fileInfo)
1568
1569 s32 length;
1570 AXPBADPCMLOOP loop;
1571
1572 // Regarding loads:
1573 // (1) Their size is in units of a single side of the streaming buffer (STRM_BUFFER_SIZE).
1574 // (2) However, right before jumping to the loop start buffer, it's possible that an entire single side will not be filled.
1575 // (3) Even in case (2), size is a multiple of the frame length (= 8 bytes).
1576 // (4) File offset is the frame alignment (= 8 bytes).
1577
1578 length = (activeStrm->readSize + 0x1f) & ~0x1f;
1579
1580 DVDReadAsync (&activeStrm->finfoR,
1581 activeStrm->strmBufferR + activeStrm->writeOffset,
1582 length,
1583 activeStrm->readOffset,
1584 gottenData);
1585
1586 activeStrm->readOffset += activeStrm->readSize;
1587
1588 // You must update the loop context if you fill the first half of the streaming buffer with data.
1589 // However, since this is an AX_PB_TYPE_STREAM loop, the only setting is 'ps'.
1590
1591 if (activeStrm->writeOffset == 0 && activeStrm->state == STRM_STARTED)
1592 {
1593 loop.loop_pred_scale = (u16)(*((u8*)(activeStrm->strmBufferL)));
1594
1595 AXSetVoiceAdpcmLoop(activeStrm->voiceL, &loop);
1596 }
1597 }
1598
1599 /*---------------------------------------------------------------------------*
1600 Name: gottenData
1601
1602 Description: Function called when DVD read (of L&R ch) is done.
1603
1604 Arguments: Not used.
1605
1606 Returns: None.
1607 *---------------------------------------------------------------------------*/
1608
gottenData(s32 result,DVDFileInfo * fileInfo)1609 static void gottenData(s32 result, DVDFileInfo* fileInfo)
1610 {
1611 #pragma unused(result)
1612 #pragma unused(fileInfo)
1613
1614 AXPBADPCMLOOP loop;
1615
1616 // You must update the loop context if you fill the first half of the streaming buffer with data.
1617 // However, since this is an AX_PB_TYPE_STREAM loop, the only setting is 'ps'.
1618
1619 if (activeStrm->writeOffset == 0 && activeStrm->state == STRM_STARTED)
1620 {
1621 loop.loop_pred_scale = (u16)(*((u8*)(activeStrm->strmBufferR)));
1622
1623 AXSetVoiceAdpcmLoop(activeStrm->voiceR, &loop);
1624 }
1625
1626 activeStrm->accessDVD = FALSE;
1627 }
1628