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