1 /*---------------------------------------------------------------------------*
2 Project: THP Player
3 File: THPPlayer.c
4
5 Copyright (C)2002-2006 Nintendo All Rights Reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Log: THPPlayer.c,v $
14 Revision 1.2 2006/10/11 02:51:46 aka
15 Revised HOLLYWOOD_REV.
16
17 Revision 1.1 2006/02/03 10:01:27 aka
18 Imported from Dolphin tree.
19
20
21 4 03/11/25 11:24 Dante
22 Japanese to English translation of comments and text strings
23
24 3 03/09/16 15:50 Suzuki
25 changed the process which skips to decode when decoding is delay.
26
27 2 03/06/12 15:33 Suzuki
28 corresponded to multi audio frequency.
29
30 1 02/05/31 8:57a Suzuki
31 Initial checkin
32
33 $NoKeywords: $
34
35 *---------------------------------------------------------------------------*/
36
37 #include <string.h>
38 #include <revolution.h>
39 #include <revolution/mix.h>
40
41 #include "THPPlayerStrmAX.h"
42 #include "THPVideoDecode.h"
43 #include "THPAudioDecode.h"
44 #include "THPRead.h"
45 #include "THPDraw.h"
46
47 /*---------------------------------------------------------------------------*
48 Definition
49 *---------------------------------------------------------------------------*/
50
51 #define STREAM_BUFFER_MS 40
52 #define BOUNDARY_NUM 5
53
54 enum
55 {
56 SUCCESS,
57 EMPTY_AUDIO_BUFFER,
58 EMPTY_AUDIO_DATA
59 };
60
61 typedef struct
62 {
63 u64 boundary[BOUNDARY_NUM];
64 s32 top;
65 s32 bottom;
66 u32 lastPos;
67 u64 curSampleNum;
68 u64 endSampleNum;
69 } AudioStreamInfo;
70
71 /*---------------------------------------------------------------------------*
72 Global Function
73 *---------------------------------------------------------------------------*/
74
75 void PrepareReady(BOOL flag);
76
77 /*---------------------------------------------------------------------------*
78 Static Function
79 *---------------------------------------------------------------------------*/
80
81 static void PlayControl(u32 retraceCount);
82 static BOOL ProperTimingForStart(void);
83 static BOOL ProperTimingForGettingNextFrame(void);
84 static void PushUsedTextureSet(THPTextureSet *buffer);
85 static void *PopUsedTextureSet(void);
86 static void EntryBoundary(u64 boundarySampleNum);
87 static void CheckBoundary(u64 curSampleNum);
88 static void TransferStreamData(s32 flag);
89 static u32 GetAudioSample(s16 *left, s16 *right, u32 sample, s32 *reason);
90 static void FillStreamBuffer(s16 *left, s16 *right, u32 sample);
91 static u32 StreamUpdateCallback(void *buffer1, u32 len1, void *buffer2, u32 len2, u32 user);
92 static BOOL StreamInit(void);
93 static void StreamReInit(void);
94 static void StreamPlay(void);
95 static void StreamPause(void);
96 static void StreamQuit(void);
97
98 /*---------------------------------------------------------------------------*
99 Global Function
100 *---------------------------------------------------------------------------*/
101
102 THPPlayer ActivePlayer;
103
104 /*---------------------------------------------------------------------------*
105 Static Function
106 *---------------------------------------------------------------------------*/
107
108 static BOOL Initialized = FALSE;
109 static s16 WorkBuffer[24 * STREAM_BUFFER_MS] ATTRIBUTE_ALIGN(32);
110 static OSMessageQueue PrepareReadyQueue;
111 static OSMessageQueue UsedTextureSetQueue;
112 static OSMessage PrepareReadyMessage[2];
113 static OSMessage UsedTextureSetMessage[DECODE_VIDEO_BUFFER_NUM];
114 static VIRetraceCallback OldVIPostCallback = NULL;
115
116 static AXVPB *StreamL = NULL;
117 static AXVPB *StreamR = NULL;
118 static s16 *StreamBufferL = NULL;
119 static s16 *StreamBufferR = NULL;
120 static AudioStreamInfo StreamInfo;
121
122 /*---------------------------------------------------------------------------*
123 Name: THPPlayerInit
124
125 Description: Enables locked-cache, and initializes player and THP library.
126
127 Arguments: None
128
129 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
130 *---------------------------------------------------------------------------*/
131
THPPlayerInit(void)132 BOOL THPPlayerInit(void)
133 {
134 memset(&ActivePlayer, 0, sizeof(THPPlayer));
135
136 LCEnable();
137
138 OSInitMessageQueue(&UsedTextureSetQueue,
139 UsedTextureSetMessage,
140 DECODE_VIDEO_BUFFER_NUM);
141
142 if (THPInit() == FALSE)
143 {
144 return FALSE;
145 }
146
147 Initialized = TRUE;
148
149 return TRUE;
150 }
151
152 /*---------------------------------------------------------------------------*
153 Name: THPPlayerQuit
154
155 Description: Disables locked cache.
156
157 Arguments: None
158
159 Returns: None
160 *---------------------------------------------------------------------------*/
161
THPPlayerQuit(void)162 void THPPlayerQuit(void)
163 {
164 LCDisable();
165
166 Initialized = FALSE;
167
168 return;
169 }
170
171 /*---------------------------------------------------------------------------*
172 Name: THPPlayerOpen
173
174 Description: Opens THP movie file. Loads required data.
175
176 Arguments: fileName Filename of THP movie.
177 onMemory Flag to do OnMemory playback or not.
178
179 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
180 *---------------------------------------------------------------------------*/
181
THPPlayerOpen(char * fileName,BOOL onMemory)182 BOOL THPPlayerOpen(char *fileName, BOOL onMemory)
183 {
184 s32 offset, i;
185
186 if (!Initialized)
187 {
188 #ifdef _DEBUG
189 OSReport("You must call THPPlayerInit before you call this function\n");
190 #endif
191 return FALSE;
192 }
193
194 if (ActivePlayer.open)
195 {
196 #ifdef _DEBUG
197 OSReport("Can't open %s. Because thp file have already opened.\n");
198 #endif
199 return FALSE;
200 }
201
202 // Clears current video data and audio data
203 memset(&ActivePlayer.videoInfo, 0, sizeof(THPVideoInfo));
204 memset(&ActivePlayer.audioInfo, 0, sizeof(THPAudioInfo));
205
206 if (DVDOpen(fileName, &ActivePlayer.fileInfo) == FALSE)
207 {
208 #ifdef _DEBUG
209 OSReport("Can't open %s.\n", fileName);
210 #endif
211 return FALSE;
212 }
213
214 // Get THP header
215 if (DVDRead(&ActivePlayer.fileInfo, WorkBuffer, 64, 0) < 0)
216 {
217 #ifdef _DEBUG
218 OSReport("Fail to read the header from THP file.\n");
219 #endif
220 DVDClose(&ActivePlayer.fileInfo);
221 return FALSE;
222 }
223
224 memcpy(&ActivePlayer.header, WorkBuffer, sizeof(THPHeader));
225
226 // Check if THP movie file
227 if (strcmp(ActivePlayer.header.magic, "THP") != 0)
228 {
229 #ifdef _DEBUG
230 OSReport("This file is not THP file.\n");
231 #endif
232 DVDClose(&ActivePlayer.fileInfo);
233 return FALSE;
234 }
235
236 // Check version
237 if (ActivePlayer.header.version != THP_VERSION)
238 {
239 #ifdef _DEBUG
240 OSReport("invalid version.\n");
241 #endif
242 DVDClose(&ActivePlayer.fileInfo);
243 return FALSE;
244 }
245
246 offset = (s32)ActivePlayer.header.compInfoDataOffsets;
247
248 // Acquire component data in frame
249 if (DVDRead(&ActivePlayer.fileInfo, WorkBuffer, 32, offset) < 0)
250 {
251 #ifdef _DEBUG
252 OSReport("Fail to read the frame component infomation from THP file.\n");
253 #endif
254 DVDClose(&ActivePlayer.fileInfo);
255 return FALSE;
256 }
257
258 memcpy(&ActivePlayer.compInfo, WorkBuffer, sizeof(THPFrameCompInfo));
259
260 offset += sizeof(THPFrameCompInfo);
261
262 ActivePlayer.audioExist = 0;
263
264 // Check components in frame
265 for(i = 0 ; i < ActivePlayer.compInfo.numComponents ; i++)
266 {
267 switch(ActivePlayer.compInfo.frameComp[i])
268 {
269 case THP_VIDEO_COMP: // Get video data of components
270 if (DVDRead(&ActivePlayer.fileInfo, WorkBuffer, 32, offset) < 0)
271 {
272 #ifdef _DEBUG
273 OSReport("Fail to read the video infomation from THP file.\n");
274 #endif
275 DVDClose(&ActivePlayer.fileInfo);
276 return FALSE;
277 }
278 memcpy(&ActivePlayer.videoInfo, WorkBuffer, sizeof(THPVideoInfo));
279 offset += sizeof(THPVideoInfo);
280 break;
281 case THP_AUDIO_COMP: // Get audio data of components
282 if (DVDRead(&ActivePlayer.fileInfo, WorkBuffer, 32, offset) < 0)
283 {
284 #ifdef _DEBUG
285 OSReport("Fail to read the video infomation from THP file.\n");
286 #endif
287 DVDClose(&ActivePlayer.fileInfo);
288 return FALSE;
289 }
290 memcpy(&ActivePlayer.audioInfo, WorkBuffer, sizeof(THPAudioInfo));
291 ActivePlayer.audioExist = 1;
292 offset += sizeof(THPAudioInfo);
293
294 break;
295 default:
296 #ifdef _DEBUG
297 OSReport("Unknow frame components.\n");
298 #endif
299 return FALSE;
300 }
301 }
302
303 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_STOP;
304 ActivePlayer.playFlag = 0;
305 ActivePlayer.onMemory = onMemory;
306 ActivePlayer.open = TRUE;
307
308 return TRUE;
309 }
310
311 /*---------------------------------------------------------------------------*
312 Name: THPPlayerClose
313
314 Description: Close THP movie file.
315
316 Arguments: None
317
318 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
319 *---------------------------------------------------------------------------*/
320
THPPlayerClose(void)321 BOOL THPPlayerClose(void)
322 {
323 if (ActivePlayer.open)
324 {
325 if (ActivePlayer.state == THP_PLAYER_STOP)
326 {
327 ActivePlayer.open = FALSE;
328 DVDClose(&ActivePlayer.fileInfo);
329
330 return TRUE;
331 }
332 }
333
334 return FALSE;
335 }
336
337 /*---------------------------------------------------------------------------*
338 Name: THPPlayerCalcNeedMemory
339
340 Description: Calculates needed memory for THP movie playback
341
342 Arguments: None
343
344 Returns: Returns needed memory size if successful, and 0 if unsuccessful.
345 *---------------------------------------------------------------------------*/
346
THPPlayerCalcNeedMemory(void)347 u32 THPPlayerCalcNeedMemory(void)
348 {
349 u32 size;
350
351 if (ActivePlayer.open)
352 {
353 // Buffer size for read
354 if (ActivePlayer.onMemory)
355 {
356 size = OSRoundUp32B(ActivePlayer.header.movieDataSize);
357 }
358 else
359 {
360 size = OSRoundUp32B(ActivePlayer.header.bufSize) * READ_BUFFER_NUM;
361 }
362
363 // Size of texture buffer
364 size += OSRoundUp32B(ActivePlayer.videoInfo.xSize * ActivePlayer.videoInfo.ySize)
365 * DECODE_VIDEO_BUFFER_NUM; //Y
366 size += OSRoundUp32B(ActivePlayer.videoInfo.xSize * ActivePlayer.videoInfo.ySize / 4)
367 * DECODE_VIDEO_BUFFER_NUM; //U
368 size += OSRoundUp32B(ActivePlayer.videoInfo.xSize * ActivePlayer.videoInfo.ySize / 4)
369 * DECODE_VIDEO_BUFFER_NUM; //V
370
371 // Size of audio buffer
372 if (ActivePlayer.audioExist)
373 {
374 size += (OSRoundUp32B(ActivePlayer.header.audioMaxSamples * 4) * DECODE_AUDIO_BUFFER_NUM);
375 size += (OSRoundUp32B(STREAM_BUFFER_MS * ActivePlayer.audioInfo.sndFrequency / 1000.0f + 0.5f) * 2
376 * ActivePlayer.audioInfo.sndChannels);
377 }
378
379 size += THP_WORK_SIZE;
380
381 return size;
382 }
383
384 return 0;
385 }
386
387 /*---------------------------------------------------------------------------*
388 Name: THPPlayerSetBuffer
389
390 Description: Allocate required memory for movie playback to THPPlayer structure.
391
392 Arguments: Pointer to memory area for THP movie set aside externally.
393
394 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
395 *---------------------------------------------------------------------------*/
396
THPPlayerSetBuffer(u8 * buffer)397 BOOL THPPlayerSetBuffer(u8 *buffer)
398 {
399 u32 i;
400 u8 *ptr;
401 u32 ysize, uvsize;
402
403 ASSERTMSG(buffer != NULL, "buffer is NULL");
404
405 if (ActivePlayer.open && (ActivePlayer.state == THP_PLAYER_STOP))
406 {
407 ptr = buffer;
408
409 // Set buffer for read
410 if (ActivePlayer.onMemory)
411 {
412 ActivePlayer.movieData = ptr;
413 ptr += ActivePlayer.header.movieDataSize;
414 }
415 else
416 {
417 for (i = 0 ; i < READ_BUFFER_NUM ; i++)
418 {
419 ActivePlayer.readBuffer[i].ptr = ptr;
420 ptr += OSRoundUp32B(ActivePlayer.header.bufSize);
421 }
422 }
423
424 ysize = OSRoundUp32B(ActivePlayer.videoInfo.xSize * ActivePlayer.videoInfo.ySize);
425 uvsize = OSRoundUp32B(ActivePlayer.videoInfo.xSize * ActivePlayer.videoInfo.ySize / 4);
426
427 // Set texture buffer
428 for (i = 0 ; i < DECODE_VIDEO_BUFFER_NUM ; i++)
429 {
430 ActivePlayer.textureSet[i].ytexture = ptr;
431 DCInvalidateRange(ptr, ysize);
432 ptr += ysize;
433 ActivePlayer.textureSet[i].utexture = ptr;
434 DCInvalidateRange(ptr, uvsize);
435 ptr += uvsize;
436 ActivePlayer.textureSet[i].vtexture = ptr;
437 DCInvalidateRange(ptr, uvsize);
438 ptr += uvsize;
439 }
440
441 // Set audio buffer
442 if (ActivePlayer.audioExist)
443 {
444 for (i = 0 ; i < DECODE_AUDIO_BUFFER_NUM ; i++)
445 {
446 ActivePlayer.audioBuffer[i].buffer = (s16 *)ptr;
447 ActivePlayer.audioBuffer[i].curPtr = (s16 *)ptr;
448 ActivePlayer.audioBuffer[i].validSample = 0;
449 ptr += OSRoundUp32B(ActivePlayer.header.audioMaxSamples * 4);
450 }
451
452 StreamBufferL = (s16 *)ptr;
453 ptr += (OSRoundUp32B(STREAM_BUFFER_MS * ActivePlayer.audioInfo.sndFrequency / 1000.0f + 0.5f) * 2);
454 if (ActivePlayer.audioInfo.sndChannels == 2)
455 {
456 StreamBufferR = (s16 *)ptr;
457 ptr += (OSRoundUp32B(STREAM_BUFFER_MS * ActivePlayer.audioInfo.sndFrequency / 1000.0f + 0.5f) * 2);
458 }
459 }
460
461 ActivePlayer.thpWork = (void *)ptr;
462
463 return TRUE;
464 }
465
466 return FALSE;
467 }
468
469 /*---------------------------------------------------------------------------*
470 Name: InitAllMessageQueue
471
472 Description: Initializes queue used with video decode thread, audio
473 thread, and read thread.
474
475 Arguments: None
476
477 Returns: None
478 *---------------------------------------------------------------------------*/
479
InitAllMessageQueue(void)480 static void InitAllMessageQueue(void)
481 {
482 s32 i;
483 THPReadBuffer *readBuffer;
484 THPTextureSet *textureSet;
485 THPAudioBuffer *audioBuffer;
486
487 // Push all read buffers to free read buffer queue
488 if (!ActivePlayer.onMemory)
489 {
490 for (i = 0 ; i < READ_BUFFER_NUM ; i++)
491 {
492 readBuffer = &ActivePlayer.readBuffer[i];
493 PushFreeReadBuffer(readBuffer);
494 }
495 }
496
497 // Push all texture buffers for storing decoded THP video data to
498 // free texture buffer queue.
499 for (i = 0 ; i < DECODE_VIDEO_BUFFER_NUM ; i++)
500 {
501 textureSet = &ActivePlayer.textureSet[i];
502 PushFreeTextureSet(textureSet);
503 }
504
505 // Push all audio buffers for storing decoded THP audio data to
506 // free audio buffer queue.
507 if (ActivePlayer.audioExist)
508 {
509 for (i = 0 ; i < DECODE_AUDIO_BUFFER_NUM ; i++)
510 {
511 audioBuffer = &ActivePlayer.audioBuffer[i];
512 PushFreeAudioBuffer(audioBuffer);
513 }
514 }
515
516 OSInitMessageQueue(&PrepareReadyQueue,
517 PrepareReadyMessage,
518 2);
519 }
520
521 /*---------------------------------------------------------------------------*
522 Name: WaitUntilPrepare
523
524 Description: Waits until playback preparations completed.
525
526 Arguments: None
527
528 Returns: If playback preparations complete, returns TRUE. If preparations fail, returns FALSE.
529 *---------------------------------------------------------------------------*/
530
WaitUntilPrepare(void)531 static BOOL WaitUntilPrepare(void)
532 {
533 OSMessage msg1, msg2;
534
535 if (ActivePlayer.audioExist)
536 {
537 OSReceiveMessage(&PrepareReadyQueue, &msg1, OS_MESSAGE_BLOCK);
538 OSReceiveMessage(&PrepareReadyQueue, &msg2, OS_MESSAGE_BLOCK);
539
540 if ((BOOL)msg1 && (BOOL)msg2)
541 {
542 return TRUE;
543 }
544 else
545 {
546 return FALSE;
547 }
548 }
549 else
550 {
551 OSReceiveMessage(&PrepareReadyQueue, &msg1, OS_MESSAGE_BLOCK);
552
553 if ((BOOL)msg1)
554 {
555 return TRUE;
556 }
557 else
558 {
559 return FALSE;
560 }
561 }
562 }
563
564 /*---------------------------------------------------------------------------*
565 Name: PrepareReady
566
567 Description: Sends message about whether or not playback preparations are completed.
568
569 Arguments: flag If playback preparations completed, returns TRUE. If preparations fail, returns FALSE.
570
571 Returns: None
572 *---------------------------------------------------------------------------*/
573
PrepareReady(BOOL flag)574 void PrepareReady(BOOL flag)
575 {
576 OSSendMessage(&PrepareReadyQueue, (OSMessage)flag, OS_MESSAGE_BLOCK);
577
578 return;
579 }
580
581 /*---------------------------------------------------------------------------*
582 Name: THPPlayerPrepare
583
584 Description: Carries out preparations for movie playback. Does not
585 return until THP movie playback preparations are completed.
586
587 Arguments: frameNum Specifies the frame number of the movie playback.
588 If the THP movie file does not contain THPFrameOffsetData,
589 anything other than 0 is an error.
590 playFlag Specifies one-shot or loop playback flag.
591 audioTrack Specifies audio track number to be played back.
592
593 Returns: If playback is ready, returns TRUE. If playback preparation fails, returns FALSE.
594 *---------------------------------------------------------------------------*/
595
THPPlayerPrepare(s32 frameNum,s32 playFlag,s32 audioTrack)596 BOOL THPPlayerPrepare(s32 frameNum, s32 playFlag, s32 audioTrack)
597 {
598 s32 offset;
599 u8 *ptr;
600
601 if (ActivePlayer.open && (ActivePlayer.state == THP_PLAYER_STOP))
602 {
603 // Check if specified starting frame number is appropriate
604 if (frameNum > 0)
605 {
606 // Does THP movie file have offset data?
607 if (!ActivePlayer.header.offsetDataOffsets)
608 {
609 #ifdef _DEBUG
610 OSReport("This THP file doesn't have the offset data\n");
611 #endif
612 return FALSE;
613 }
614
615 // Does starting frame number exceed total frames?
616 if (ActivePlayer.header.numFrames > frameNum)
617 {
618 offset = (s32)(ActivePlayer.header.offsetDataOffsets + (frameNum - 1) * 4);
619
620 if (DVDRead(&ActivePlayer.fileInfo,
621 WorkBuffer,
622 32,
623 offset) < 0)
624 {
625 #ifdef _DEBUG
626 OSReport("Fail to read the offset data from THP file.\n");
627 #endif
628 return FALSE;
629 }
630
631 // Set starting file offset, frame size, and frame number
632 ActivePlayer.initOffset = (s32)(ActivePlayer.header.movieDataOffsets
633 + WorkBuffer[0]);
634 ActivePlayer.initReadFrame = frameNum;
635 ActivePlayer.initReadSize = (s32)(WorkBuffer[1] - WorkBuffer[0]);
636 }
637 else
638 {
639 #ifdef _DEBUG
640 OSReport("Specified frame number is over total frame number\n");
641 #endif
642 return FALSE;
643 }
644 }
645 // If 0, from beginning
646 else
647 {
648 ActivePlayer.initOffset = (s32)ActivePlayer.header.movieDataOffsets;
649 ActivePlayer.initReadSize = (s32)ActivePlayer.header.firstFrameSize;
650 ActivePlayer.initReadFrame = frameNum;
651 }
652
653 if (ActivePlayer.audioExist)
654 {
655 if (audioTrack < 0 || audioTrack >= ActivePlayer.audioInfo.sndNumTracks)
656 {
657 #ifdef _DEBUG
658 OSReport("Specified audio track number is invalid\n");
659 #endif
660 return FALSE;
661 }
662 else
663 {
664 ActivePlayer.curAudioTrack = audioTrack;
665 }
666 }
667
668 playFlag &= THP_PLAY_LOOP;
669 ActivePlayer.playFlag = (u8)playFlag;
670 ActivePlayer.videoDecodeCount = 0;
671
672 // If On Memory playback, load all THP movie data to memory
673 if (ActivePlayer.onMemory)
674 {
675 if (DVDRead(&ActivePlayer.fileInfo,
676 ActivePlayer.movieData,
677 (s32)ActivePlayer.header.movieDataSize,
678 (s32)ActivePlayer.header.movieDataOffsets) < 0)
679 {
680 #ifdef _DEBUG
681 OSReport("Fail to read all movie data from THP file\n");
682 #endif
683 return FALSE;
684 }
685
686 ptr = ActivePlayer.movieData + ActivePlayer.initOffset - ActivePlayer.header.movieDataOffsets;
687
688 // Create video decode thread
689 CreateVideoDecodeThread(VIDEO_THREAD_PRIORITY, ptr);
690
691 // Create audio decode thread if required
692 if (ActivePlayer.audioExist)
693 {
694 CreateAudioDecodeThread(AUDIO_THREAD_PRIORITY, ptr);
695 }
696 }
697 // Not On Memory playback
698 else
699 {
700 // Create video decode thread
701 CreateVideoDecodeThread(VIDEO_THREAD_PRIORITY, NULL);
702
703 // Create audio decode thread if required
704 if (ActivePlayer.audioExist)
705 {
706 CreateAudioDecodeThread(AUDIO_THREAD_PRIORITY, NULL);
707 }
708
709 // Create read thread
710 CreateReadThread(READ_THREAD_PRIORITY);
711 }
712
713 ActivePlayer.curVideoNumber = -1;
714 ActivePlayer.curAudioNumber = 0;
715
716 // Initialize queues used with various threads
717 InitAllMessageQueue();
718
719 VideoDecodeThreadStart();
720
721 if (ActivePlayer.audioExist)
722 {
723 AudioDecodeThreadStart();
724 }
725
726 if (!ActivePlayer.onMemory)
727 {
728 ReadThreadStart();
729 }
730
731 // Wait until thread preparation completed.
732 if (WaitUntilPrepare() == FALSE)
733 {
734 return FALSE;
735 }
736
737 // If preparations complete, state goes to preparations complete
738 ActivePlayer.state = THP_PLAYER_PREPARE;
739 ActivePlayer.internalState = THP_PLAYER_STOP;
740
741 // Initialize variables
742 ActivePlayer.dispTextureSet = NULL;
743 ActivePlayer.playAudioBuffer = NULL;
744
745 if (ActivePlayer.audioExist)
746 {
747 StreamInit();
748 }
749
750 // Register VI post callback that controls playback
751 OldVIPostCallback = VISetPostRetraceCallback(PlayControl);
752
753 return TRUE;
754 }
755
756 return FALSE;
757 }
758
759 /*---------------------------------------------------------------------------*
760 Name: THPPlayerPlay
761
762 Description: Start of movie playback.
763
764 Arguments: None
765
766 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
767 *---------------------------------------------------------------------------*/
768
THPPlayerPlay(void)769 BOOL THPPlayerPlay(void)
770 {
771 if (ActivePlayer.open && ((ActivePlayer.state == THP_PLAYER_PREPARE)
772 || (ActivePlayer.state == THP_PLAYER_PAUSE)))
773 {
774 if (ActivePlayer.state == THP_PLAYER_PAUSE && ActivePlayer.audioExist)
775 {
776 StreamReInit();
777 }
778 ActivePlayer.state = THP_PLAYER_PLAY;
779 ActivePlayer.prevCount = 0;
780 ActivePlayer.curCount = 0;
781 ActivePlayer.retraceCount = -1;
782
783 return TRUE;
784 }
785
786 return FALSE;
787 }
788
789 /*---------------------------------------------------------------------------*
790 Name: THPPlayerStop
791
792 Description: Stop for movie playback.
793 Returns VI post callback to original state and stops threads.
794
795 Arguments: None
796
797 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
798 *---------------------------------------------------------------------------*/
799
THPPlayerStop(void)800 void THPPlayerStop(void)
801 {
802 void *texture;
803
804 if (ActivePlayer.open && !(ActivePlayer.state == THP_PLAYER_STOP))
805 {
806 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_STOP;
807
808 // Return VI post callback
809 VISetPostRetraceCallback(OldVIPostCallback);
810
811 // Cancel if stopping threads and loading data
812 if (!ActivePlayer.onMemory)
813 {
814 DVDCancel(&ActivePlayer.fileInfo.cb);
815 ReadThreadCancel();
816 }
817
818 VideoDecodeThreadCancel();
819
820 if (ActivePlayer.audioExist)
821 {
822 StreamQuit();
823 AudioDecodeThreadCancel();
824 }
825
826 // Empty played back texture queues.
827 while (1)
828 {
829 texture = PopUsedTextureSet();
830 if (texture == NULL)
831 {
832 break;
833 }
834 }
835
836 // Clear errors
837 ActivePlayer.dvdError = FALSE;
838 ActivePlayer.videoError = FALSE;
839 }
840
841 return;
842 }
843
844 /*---------------------------------------------------------------------------*
845 Name: THPPlayerPause
846
847 Description: Pause movie playback
848
849 Arguments: None
850
851 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
852 *---------------------------------------------------------------------------*/
853
THPPlayerPause(void)854 BOOL THPPlayerPause(void)
855 {
856 if (ActivePlayer.open && (ActivePlayer.state == THP_PLAYER_PLAY))
857 {
858 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_PAUSE;
859
860 if (ActivePlayer.audioExist)
861 {
862 StreamPause();
863 }
864
865 return TRUE;
866 }
867
868 return FALSE;
869 }
870
871 /*---------------------------------------------------------------------------*
872 Name: THPPlayerSkip
873
874 Description: Skip movie ahead one frame.
875
876 Arguments: None
877
878 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
879 *---------------------------------------------------------------------------*/
880
THPPlayerSkip(void)881 BOOL THPPlayerSkip(void)
882 {
883 s32 frameNumber, audioGet, videoGet;
884
885 if (ActivePlayer.open && (ActivePlayer.state == THP_PLAYER_PAUSE))
886 {
887 // Function is blocked until decoded THP video data is acquired,
888 // so release the played THP video data in advance.
889 THPPlayerDrawDone();
890
891 // If have audio
892 if (ActivePlayer.audioExist)
893 {
894 if (StreamInfo.top != StreamInfo.bottom)
895 {
896 StreamInfo.curSampleNum = StreamInfo.boundary[StreamInfo.top];
897 StreamInfo.top++;
898 if (StreamInfo.top >= BOUNDARY_NUM)
899 {
900 StreamInfo.top = 0;
901 }
902
903 ActivePlayer.curAudioNumber++;
904 audioGet = 1;
905 }
906 else
907 {
908 StreamInfo.curSampleNum = StreamInfo.endSampleNum;
909
910 frameNumber = ActivePlayer.curAudioNumber + ActivePlayer.initReadFrame;
911
912 // Check if one shot and also if audio has reached end.
913 if (!(ActivePlayer.playFlag & THP_PLAY_LOOP) && (frameNumber >= ActivePlayer.header.numFrames - 1))
914 {
915 if (ActivePlayer.playAudioBuffer)
916 {
917 PushFreeAudioBuffer(ActivePlayer.playAudioBuffer);
918 ActivePlayer.playAudioBuffer = NULL;
919 ActivePlayer.curAudioNumber++;
920 }
921 audioGet = 0;
922 }
923 else
924 {
925 // Release current audio buffer
926 if (ActivePlayer.playAudioBuffer)
927 {
928 PushFreeAudioBuffer(ActivePlayer.playAudioBuffer);
929 }
930
931 // Wait until get next audio buffer
932 ActivePlayer.playAudioBuffer = (THPAudioBuffer *)PopDecodedAudioBuffer(OS_MESSAGE_BLOCK);
933 ActivePlayer.curAudioNumber++;
934
935 audioGet = 1;
936 }
937 }
938 }
939
940 if (ActivePlayer.dispTextureSet)
941 {
942 frameNumber = ActivePlayer.dispTextureSet->frameNumber + ActivePlayer.initReadFrame;
943 }
944 else
945 {
946 frameNumber = ActivePlayer.initReadFrame - 1;
947 }
948
949 // Check if one shot and also if video has reached end.
950 if (!(ActivePlayer.playFlag & THP_PLAY_LOOP) && (frameNumber == ActivePlayer.header.numFrames - 1))
951 {
952 videoGet = 0;
953 }
954 else
955 {
956 // Release current texture buffer
957 if (ActivePlayer.dispTextureSet)
958 {
959 PushFreeTextureSet(ActivePlayer.dispTextureSet);
960 }
961
962 // Wait until the next texture buffer is acquired
963 ActivePlayer.dispTextureSet = (THPTextureSet *)PopDecodedTextureSet(OS_MESSAGE_BLOCK);
964
965 if (ActivePlayer.audioExist)
966 {
967 ActivePlayer.curVideoNumber++;
968 }
969
970 videoGet = 1;
971 }
972
973 if (audioGet || videoGet)
974 {
975 return TRUE;
976 }
977 else
978 {
979 return FALSE;
980 }
981 }
982
983 return FALSE;
984 }
985
986 /*---------------------------------------------------------------------------*
987 Name: PlayControl
988
989 Description: Controls movie playback. Gets decoded THP video data at
990 appropriate timing.
991
992 Arguments: retraceCount Current retrace count
993
994 Returns: None
995 *---------------------------------------------------------------------------*/
996
PlayControl(u32 retraceCount)997 static void PlayControl(u32 retraceCount)
998 {
999 s32 diff, frameNumber;
1000 THPTextureSet *textureSet;
1001
1002 if (OldVIPostCallback)
1003 {
1004 OldVIPostCallback(retraceCount);
1005 }
1006
1007 textureSet = (THPTextureSet *)0xFFFFFFFF;
1008
1009 if (ActivePlayer.open && (ActivePlayer.state == THP_PLAYER_PLAY))
1010 {
1011 // If an error has occurred, change state to error.
1012 if (ActivePlayer.dvdError || ActivePlayer.videoError)
1013 {
1014 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_ERROR;
1015
1016 return;
1017 }
1018
1019 ActivePlayer.retraceCount++;
1020
1021 // When start THP movie playback and when end pause
1022 if (ActivePlayer.retraceCount == 0)
1023 {
1024 // Appropriate timing for start of playback?
1025 if (ProperTimingForStart())
1026 {
1027 // If THP movie has audio
1028 if (ActivePlayer.audioExist)
1029 {
1030 // Calculate difference between current audio playback frames and current video playback frames
1031 diff = ActivePlayer.curVideoNumber - ActivePlayer.curAudioNumber;
1032
1033 // If audio is not behind video, move video forward
1034 if (diff <= 1)
1035 {
1036 // Get decoded THP video data
1037 textureSet = (THPTextureSet *)PopDecodedTextureSet(OS_MESSAGE_NOBLOCK);
1038
1039 // Increment THP video data number (ideal value)
1040 ActivePlayer.curVideoNumber++;
1041 }
1042 // Allow audio output if slow
1043 else
1044 {
1045 StreamPlay();
1046 ActivePlayer.internalState = THP_PLAYER_PLAY;
1047 }
1048 }
1049 // If THP movie has no audio
1050 else
1051 {
1052 textureSet = (THPTextureSet *)PopDecodedTextureSet(OS_MESSAGE_NOBLOCK);
1053 }
1054 }
1055 // If not appropriate timing, wait for next VSYNC.
1056 else
1057 {
1058 ActivePlayer.retraceCount = -1;
1059 }
1060 }
1061 // During playback
1062 else
1063 {
1064 // Enables audio output after 1 VSYNC to obtain starting THP video
1065 // data. It is assumed that the movie rendering loop is looping with 1 VSYNC.
1066 // The reason for this is:
1067 //
1068 // [Flow from THPPlayerPlay to display of starting frame]
1069 //
1070 // <Call THPPlayerPlay>
1071 // -----------------VSYNC---------------------------
1072 // <Obtain starting THP video data in VI post-callback>
1073 // <Render starting THP video data with your rendering loop, and
1074 // call VISetNextFrameBuffer and VIFlush.>
1075 // ------------------VSYNC----------------------------
1076 // From this point, the movie is shown on TV. Audio output is enabled
1077 // with this timing.
1078 if (ActivePlayer.audioExist && ActivePlayer.retraceCount == 1 && ActivePlayer.internalState != THP_PLAYER_PLAY)
1079 {
1080 StreamPlay();
1081 ActivePlayer.internalState = THP_PLAYER_PLAY;
1082 }
1083
1084 if (ProperTimingForGettingNextFrame())
1085 {
1086 // If THP movie has audio
1087 if (ActivePlayer.audioExist)
1088 {
1089 // Calculate difference between current audio playback frames and current video playback frames
1090 diff = ActivePlayer.curVideoNumber - ActivePlayer.curAudioNumber;
1091
1092 // If audio is not slower than video, move video ahead
1093 if (diff <= 1)
1094 {
1095 // Get decoded THP video data
1096 textureSet = (THPTextureSet *)PopDecodedTextureSet(OS_MESSAGE_NOBLOCK);
1097
1098 // Increment THP video data number (ideal value)
1099 ActivePlayer.curVideoNumber++;
1100 }
1101 }
1102 // If THP movie has no audio
1103 else
1104 {
1105 // Acquire decoded video data
1106 textureSet = (THPTextureSet *)PopDecodedTextureSet(OS_MESSAGE_NOBLOCK);
1107 }
1108 }
1109 }
1110
1111 // If decoded THP video data can be acquired, push the THP video data
1112 // that has been held until that point to the cache for data played back.
1113 if (textureSet && (textureSet != (THPTextureSet *)0xFFFFFFFF))
1114 {
1115 if (ActivePlayer.dispTextureSet)
1116 {
1117 // If you call PushFreeTextureSet here, newly decoded THP video
1118 // data may be written to the texture buffer that the graphics
1119 // processor is accessing, so the data is pushed to a temporary
1120 // cache. After verifying with THPPlayerDrawDone() that access from
1121 // the graphics processor is done, the actual release is done.
1122 PushUsedTextureSet(ActivePlayer.dispTextureSet);
1123 }
1124 ActivePlayer.dispTextureSet = textureSet;
1125 }
1126
1127 // Check if playback has reached end during one shot playback
1128 if (!(ActivePlayer.playFlag & THP_PLAY_LOOP))
1129 {
1130 // If THP movie has audio, check if video and audio has reached end.
1131 if (ActivePlayer.audioExist)
1132 {
1133 frameNumber = ActivePlayer.curAudioNumber + ActivePlayer.initReadFrame;
1134
1135 // If the end has been reached, set state to THP_PLAYER_PLAYED.
1136 if (frameNumber == ActivePlayer.header.numFrames && ActivePlayer.playAudioBuffer == NULL)
1137 {
1138 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_PLAYED;
1139 }
1140 }
1141 // If THP movie has audio, check whether the video has reached the end.
1142 else
1143 {
1144 if (ActivePlayer.dispTextureSet)
1145 {
1146 frameNumber = ActivePlayer.dispTextureSet->frameNumber + ActivePlayer.initReadFrame;
1147 }
1148 else
1149 {
1150 frameNumber = ActivePlayer.initReadFrame - 1;
1151 }
1152
1153 if ((frameNumber == ActivePlayer.header.numFrames - 1)
1154 &&
1155 (textureSet == NULL))
1156 {
1157 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_PLAYED;
1158 }
1159 }
1160 }
1161 }
1162
1163 return;
1164 }
1165
1166 /*---------------------------------------------------------------------------*
1167 Name: ProperTimingForStart
1168
1169 Description: Check whether the timing is appropriate for movie playback
1170 start. Movie rendering loop may be looping with 1 VSYNC.
1171
1172 Arguments: None
1173
1174 Returns: If appropriate timing, returns TRUE. If inappropriate, returns FALSE.
1175 *---------------------------------------------------------------------------*/
1176
ProperTimingForStart(void)1177 static BOOL ProperTimingForStart(void)
1178 {
1179 if (ActivePlayer.videoInfo.videoType & THP_VIDEO_ODD_INTERLACE)
1180 {
1181 if (VIGetNextField() == VI_FIELD_BELOW)
1182 {
1183 return TRUE;
1184 }
1185 }
1186 else if (ActivePlayer.videoInfo.videoType & THP_VIDEO_EVEN_INTERLACE)
1187 {
1188 if (VIGetNextField() == VI_FIELD_ABOVE)
1189 {
1190 return TRUE;
1191 }
1192 }
1193 else
1194 {
1195 return TRUE;
1196 }
1197
1198 return FALSE;
1199 }
1200
1201 /*---------------------------------------------------------------------------*
1202 Name: ProperTimingForGettingNextFrame
1203
1204 Description: Checks whether the timing is appropriate for getting decoded
1205 THP video data.
1206
1207 Arguments: None
1208
1209 Returns: If appropriate timing, returns TRUE. If inappropriate, returns FALSE.
1210 *---------------------------------------------------------------------------*/
1211
ProperTimingForGettingNextFrame(void)1212 static BOOL ProperTimingForGettingNextFrame(void)
1213 {
1214 s32 frameRate;
1215
1216 if (ActivePlayer.videoInfo.videoType & THP_VIDEO_ODD_INTERLACE)
1217 {
1218 if (VIGetNextField() == VI_FIELD_BELOW)
1219 {
1220 return TRUE;
1221 }
1222 }
1223 else if (ActivePlayer.videoInfo.videoType & THP_VIDEO_EVEN_INTERLACE)
1224 {
1225 if (VIGetNextField() == VI_FIELD_ABOVE)
1226 {
1227 return TRUE;
1228 }
1229 }
1230 else
1231 {
1232 // Convert framerate to an integer
1233 frameRate = (s32)((ActivePlayer.header.frameRate) * 100.0f);
1234
1235 if (VIGetTvFormat() == VI_PAL)
1236 {
1237 ActivePlayer.curCount = (s32)((ActivePlayer.retraceCount * frameRate) / PAL_RATE);
1238 }
1239 else
1240 {
1241 ActivePlayer.curCount = (s32)((ActivePlayer.retraceCount * frameRate) / NTSC_RATE);
1242 }
1243
1244 if (ActivePlayer.prevCount != ActivePlayer.curCount)
1245 {
1246 ActivePlayer.prevCount = ActivePlayer.curCount;
1247 return TRUE;
1248 }
1249 }
1250
1251 return FALSE;
1252 }
1253
1254 /*---------------------------------------------------------------------------*
1255 Name: THPPlayerDrawCurrentFrame
1256
1257 Description: Draw currently acquired decoded THP video data.
1258 If none acquired, not displayed.
1259
1260 Arguments: rmode Pointer for currently set GXRenderModeObj
1261 x X coordinate of upper left TV screen for THP movie display
1262 y Y coordinate of upper left TV screen for THP movie display
1263 polygonW Width of polygon for THP movie display
1264 polygonH Height of polygon for THP movie display
1265
1266 Returns: If able to draw, returns drawn frame number. If unable to draw, returns -1.
1267 *---------------------------------------------------------------------------*/
1268
THPPlayerDrawCurrentFrame(GXRenderModeObj * rmode,u32 x,u32 y,u32 polygonW,u32 polygonH)1269 s32 THPPlayerDrawCurrentFrame(GXRenderModeObj *rmode, u32 x, u32 y, u32 polygonW, u32 polygonH)
1270 {
1271 s32 currentFrameNumber;
1272
1273 if (ActivePlayer.open && !(ActivePlayer.state == THP_PLAYER_STOP) && ActivePlayer.dispTextureSet)
1274 {
1275 THPGXYuv2RgbSetup(rmode);
1276 THPGXYuv2RgbDraw(ActivePlayer.dispTextureSet->ytexture,
1277 ActivePlayer.dispTextureSet->utexture,
1278 ActivePlayer.dispTextureSet->vtexture,
1279 (s16)x, (s16)y,
1280 (s16)ActivePlayer.videoInfo.xSize, (s16)ActivePlayer.videoInfo.ySize,
1281 (s16)polygonW, (s16)polygonH);
1282
1283 THPGXRestore();
1284
1285 currentFrameNumber = (s32)((ActivePlayer.dispTextureSet->frameNumber + ActivePlayer.initReadFrame)
1286 % ActivePlayer.header.numFrames);
1287
1288 return currentFrameNumber;
1289 }
1290
1291 return -1;
1292 }
1293
1294 /*---------------------------------------------------------------------------*
1295 Name: THPPlayerGetVideoInfo
1296
1297 Description: Acquire THP movie video data.
1298
1299 Arguments: videoInfo Pointer for THPVideoInfo structure.
1300
1301 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
1302 *---------------------------------------------------------------------------*/
1303
THPPlayerGetVideoInfo(THPVideoInfo * videoInfo)1304 BOOL THPPlayerGetVideoInfo(THPVideoInfo *videoInfo)
1305 {
1306 if (ActivePlayer.open)
1307 {
1308 memcpy(videoInfo, &ActivePlayer.videoInfo, sizeof(THPVideoInfo));
1309
1310 return TRUE;
1311 }
1312
1313 return FALSE;
1314 }
1315
1316 /*---------------------------------------------------------------------------*
1317 Name: THPPlayerGetAudioInfo
1318
1319 Description: Acquire THP movie audio data.
1320
1321 Arguments: audioInfo Pointer for THPAudioInfo structure.
1322
1323 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
1324 *---------------------------------------------------------------------------*/
1325
THPPlayerGetAudioInfo(THPAudioInfo * audioInfo)1326 BOOL THPPlayerGetAudioInfo(THPAudioInfo *audioInfo)
1327 {
1328 if (ActivePlayer.open)
1329 {
1330 memcpy(audioInfo, &ActivePlayer.audioInfo, sizeof(THPAudioInfo));
1331
1332 return TRUE;
1333 }
1334
1335 return FALSE;
1336 }
1337
1338 /*---------------------------------------------------------------------------*
1339 Name: THPPlayerGetFrameRate
1340
1341 Description: Acquire THP movie frame rate
1342
1343 Arguments: None
1344
1345 Returns: If successful, returns THP movie frame rate. If unsuccessful, returns 0.0f.
1346 *---------------------------------------------------------------------------*/
1347
THPPlayerGetFrameRate(void)1348 f32 THPPlayerGetFrameRate(void)
1349 {
1350 if (ActivePlayer.open)
1351 {
1352
1353 return ActivePlayer.header.frameRate;
1354 }
1355
1356 return 0.0f;
1357 }
1358
1359 /*---------------------------------------------------------------------------*
1360 Name: THPPlayerGetTotalFrame
1361
1362 Description: Acquire total frames for THP movie
1363
1364 Arguments: None
1365
1366 Returns: If successful, returns THP movie total frames. If unsuccessful, returns 0.
1367 *---------------------------------------------------------------------------*/
1368
THPPlayerGetTotalFrame(void)1369 u32 THPPlayerGetTotalFrame(void)
1370 {
1371 if (ActivePlayer.open)
1372 {
1373 return ActivePlayer.header.numFrames;
1374 }
1375
1376 return 0;
1377 }
1378
1379 /*---------------------------------------------------------------------------*
1380 Name: THPPlayerGetState
1381
1382 Description: Acquire current player status.
1383
1384 Arguments: None
1385
1386 Returns: Player status
1387 THP_PLAYER_STOP stopped
1388 THP_PLAYER_PREPARE preparations completed
1389 THP_PLAYER_PLAY playing
1390 THP_PLAYER_PLAYED played THP movie to end
1391 THP_PLAYER_ERROR error occured
1392 *---------------------------------------------------------------------------*/
1393
THPPlayerGetState(void)1394 s32 THPPlayerGetState(void)
1395 {
1396 return ActivePlayer.state;
1397 }
1398
1399 /*---------------------------------------------------------------------------*
1400 Name: PushUsedTextureSet
1401
1402 Description: Remove played back THP video data from played back THP
1403 video data queue.
1404
1405 Arguments: None
1406
1407 Returns: None
1408 *---------------------------------------------------------------------------*/
1409
PushUsedTextureSet(THPTextureSet * buffer)1410 static void PushUsedTextureSet(THPTextureSet *buffer)
1411 {
1412 OSSendMessage(&UsedTextureSetQueue, buffer, OS_MESSAGE_NOBLOCK);
1413
1414 return;
1415 }
1416
1417 /*---------------------------------------------------------------------------*
1418 Name: PopUsedTextureSet
1419
1420 Description: Remove played back THP video data from played back THP
1421 video data queue.
1422
1423 Arguments: None
1424
1425 Returns: Pointer for played back THP video data.
1426 *---------------------------------------------------------------------------*/
1427
PopUsedTextureSet(void)1428 static void *PopUsedTextureSet(void)
1429 {
1430 OSMessage msg;
1431
1432 if (OSReceiveMessage(&UsedTextureSetQueue, &msg, OS_MESSAGE_NOBLOCK) == TRUE)
1433 {
1434 return msg;
1435 }
1436 else
1437 {
1438 return NULL;
1439 }
1440 }
1441
1442 /*---------------------------------------------------------------------------*
1443 Name: THPPlayerDrawDone
1444
1445 Description: Function used instead of GXDrawDone to synchronize with
1446 the graphics processor during movie playback. It waits for
1447 the graphics processor to complete processing and frees
1448 internally THP video data determined to be played back.
1449
1450 Arguments: None
1451
1452 Returns: None
1453 *---------------------------------------------------------------------------*/
1454
THPPlayerDrawDone(void)1455 void THPPlayerDrawDone(void)
1456 {
1457 void *textureSet;
1458
1459 GXDrawDone();
1460
1461 if (Initialized)
1462 {
1463 while(1)
1464 {
1465 textureSet = PopUsedTextureSet();
1466 if (textureSet == NULL)
1467 {
1468 break;
1469 }
1470 else
1471 {
1472 PushFreeTextureSet(textureSet);
1473 }
1474 }
1475 }
1476 }
1477
1478 /*---------------------------------------------------------------------------*
1479 Name: StreamInit
1480
1481 Description: Carries out initialization for using streaming with
1482 THP audio data playblack.
1483
1484 Arguments: None
1485
1486 Returns: None
1487 *---------------------------------------------------------------------------*/
1488
StreamInit(void)1489 static BOOL StreamInit(void)
1490 {
1491 AXPBADDR addr;
1492 u32 bufferSize;
1493 u32 startAddr;
1494 u32 endAddr;
1495
1496 bufferSize = OSRoundUp32B(STREAM_BUFFER_MS * ActivePlayer.audioInfo.sndFrequency / 1000.0f + 0.5f) * 2;
1497
1498 // Allocate voice for left channel
1499 StreamL = AXAcquireVoice(AX_PRIORITY_NODROP, NULL, 0);
1500
1501 if (StreamL == NULL)
1502 {
1503 #ifdef _DEBUG
1504 OSReport("Couldn't complete the initialization for streaming\n");
1505 #endif
1506 return FALSE;
1507 }
1508
1509 // Initialize mixer channel
1510 if (ActivePlayer.audioInfo.sndChannels == 2)
1511 {
1512 MIXInitChannel(StreamL, 0, 0, -904, -904, -904, 0, 127, 0);
1513 }
1514 else
1515 {
1516 MIXInitChannel(StreamL, 0, 0, -904, -904, -904, 64, 127, 0);
1517 }
1518
1519 // Carry out settings for voice for left channel
1520 startAddr = OSCachedToPhysical(StreamBufferL) / 2;
1521 endAddr = ((OSCachedToPhysical(StreamBufferL) + bufferSize) / 2) - 1;
1522
1523 addr.loopFlag = AXPBADDR_LOOP_ON;
1524 addr.format = AX_PB_FORMAT_PCM16;
1525 addr.loopAddressHi = (u16)(startAddr >> 16);
1526 addr.loopAddressLo = (u16)(startAddr & 0xFFFF);
1527 addr.endAddressHi = (u16)(endAddr >> 16);
1528 addr.endAddressLo = (u16)(endAddr & 0xFFFF);
1529 addr.currentAddressHi = (u16)(startAddr >> 16);
1530 addr.currentAddressLo = (u16)(startAddr & 0xFFFF);
1531
1532 AXSetVoiceAddr(StreamL, &addr);
1533
1534 if (ActivePlayer.audioInfo.sndFrequency == 32000)
1535 {
1536 AXSetVoiceSrcType(StreamL, AX_SRC_TYPE_NONE);
1537 }
1538 else
1539 {
1540 AXSetVoiceSrcType(StreamL, AX_SRC_TYPE_4TAP_12K);
1541 AXSetVoiceSrcRatio(StreamL, (f32)(ActivePlayer.audioInfo.sndFrequency / 32000.0));
1542 }
1543
1544 if (ActivePlayer.audioInfo.sndChannels == 2)
1545 {
1546 // Allocate voice for right channel
1547 StreamR = AXAcquireVoice(AX_PRIORITY_NODROP, NULL, 0);
1548
1549 if (StreamR == NULL)
1550 {
1551 MIXReleaseChannel(StreamL);
1552 AXFreeVoice(StreamL);
1553 StreamL = NULL;
1554 #ifdef _DEBUG
1555 OSReport("Couldn't complete the initialization for streaming\n");
1556 #endif
1557 return FALSE;
1558 }
1559
1560 // Initialize mixer channel
1561 MIXInitChannel(StreamR, 0, 0, -904, -904, -904, 127, 127, 0);
1562
1563 // Carry out settings for voice for right channel
1564 startAddr = OSCachedToPhysical(StreamBufferR) / 2;
1565 endAddr = ((OSCachedToPhysical(StreamBufferR) + bufferSize) / 2) - 1;
1566
1567 addr.loopFlag = AXPBADDR_LOOP_ON;
1568 addr.format = AX_PB_FORMAT_PCM16;
1569 addr.loopAddressHi = (u16)(startAddr >> 16);
1570 addr.loopAddressLo = (u16)(startAddr & 0xFFFF);
1571 addr.endAddressHi = (u16)(endAddr >> 16);
1572 addr.endAddressLo = (u16)(endAddr & 0xFFFF);
1573 addr.currentAddressHi = (u16)(startAddr >> 16);
1574 addr.currentAddressLo = (u16)(startAddr & 0xFFFF);
1575
1576 AXSetVoiceAddr(StreamR, &addr);
1577 if (ActivePlayer.audioInfo.sndFrequency == 32000)
1578 {
1579 AXSetVoiceSrcType(StreamR, AX_SRC_TYPE_NONE);
1580 }
1581 else
1582 {
1583 AXSetVoiceSrcType(StreamR, AX_SRC_TYPE_4TAP_12K);
1584 AXSetVoiceSrcRatio(StreamR, (f32)(ActivePlayer.audioInfo.sndFrequency / 32000.0));
1585 }
1586 }
1587
1588 // Initialize structure for control of streaming
1589 StreamInfo.top = 0;
1590 StreamInfo.bottom = 0;
1591 StreamInfo.curSampleNum = 0;
1592 StreamInfo.endSampleNum = 0;
1593
1594 // Store decoded THP audio data in streaming buffer
1595 if (ActivePlayer.audioInfo.sndChannels == 2)
1596 {
1597 FillStreamBuffer(StreamBufferL, StreamBufferR, bufferSize >> 1);
1598 DCFlushRange(StreamBufferL, bufferSize);
1599 DCFlushRange(StreamBufferR, bufferSize);
1600 }
1601 else
1602 {
1603 FillStreamBuffer(StreamBufferL, NULL, bufferSize >> 1);
1604 DCFlushRange(StreamBufferL, bufferSize);
1605 }
1606
1607 StreamInfo.lastPos = OSCachedToPhysical(StreamBufferL) / 2;
1608
1609 return TRUE;
1610 }
1611
1612 /*---------------------------------------------------------------------------*
1613 Name: StreamReInit
1614
1615 Description: Rearranges the data in the streaming buffer in order to
1616 restart streaming from the stopped location during a pause.
1617 Also updates the list that saves the sample numbers that
1618 show the boundaries between movie frames.
1619
1620 Arguments: None
1621
1622 Returns: None
1623 *---------------------------------------------------------------------------*/
1624
StreamReInit(void)1625 static void StreamReInit(void)
1626 {
1627 u64 tmp;
1628 u32 size, sample, offset1, offset2, bufferSampleNum;
1629 s32 index;
1630 s16 *lsrc, *ldst, *rsrc, *rdst;
1631
1632 bufferSampleNum = OSRoundUp32B(STREAM_BUFFER_MS * ActivePlayer.audioInfo.sndFrequency / 1000.0f + 0.5f);
1633
1634 if (StreamInfo.curSampleNum == StreamInfo.endSampleNum)
1635 {
1636 // There is no valid data in the streaming buffer so replace all
1637 // with new data.
1638 StreamInfo.endSampleNum = 0;
1639
1640 if (ActivePlayer.audioInfo.sndChannels == 2)
1641 {
1642 FillStreamBuffer(StreamBufferL, StreamBufferR, bufferSampleNum);
1643 }
1644 else
1645 {
1646 FillStreamBuffer(StreamBufferL, NULL, bufferSampleNum);
1647 }
1648 }
1649 else
1650 {
1651 // Calculates beginning and end of valid data in streaming buffer
1652 offset1 = (u32)(StreamInfo.curSampleNum % bufferSampleNum);
1653 offset2 = (u32)(StreamInfo.endSampleNum % bufferSampleNum);
1654
1655 if (!offset2)
1656 {
1657 offset2 = bufferSampleNum;
1658 }
1659
1660 if (offset1 < offset2)
1661 {
1662 // Buffer status is as follows:
1663 // |----|==========| Or |---|==|-----|
1664 // - Played back data
1665 // = Not played back data
1666 // Line up data in streaming buffer not played back yet from
1667 // beginning of buffer.
1668
1669 ldst = StreamBufferL;
1670 lsrc = StreamBufferL + offset1;
1671 size = (offset2 - offset1) << 1;
1672
1673 memcpy(ldst, lsrc, size);
1674
1675 if (ActivePlayer.audioInfo.sndChannels == 2)
1676 {
1677 rdst = StreamBufferR;
1678 rsrc = StreamBufferR + offset1;
1679 memcpy(rdst, rsrc, size);
1680 }
1681
1682 // Updates the list that saves the sample numbers which show the boundaries between movie frames.
1683 index = StreamInfo.top;
1684
1685 while (index != StreamInfo.bottom)
1686 {
1687 tmp = StreamInfo.boundary[index] % bufferSampleNum;
1688
1689 StreamInfo.boundary[index] = tmp - offset1;
1690
1691 index++;
1692 if (index >= BOUNDARY_NUM)
1693 {
1694 index = 0;
1695 }
1696 }
1697
1698 ldst = StreamBufferL + (size >> 1);
1699 sample = bufferSampleNum - (size >> 1);
1700 StreamInfo.endSampleNum = bufferSampleNum - sample;
1701
1702 // Fill in open areas of streaming buffer with new data
1703 if (ActivePlayer.audioInfo.sndChannels == 2)
1704 {
1705 rdst = StreamBufferR + (size >> 1);
1706 FillStreamBuffer(ldst, rdst, sample);
1707 }
1708 else
1709 {
1710 FillStreamBuffer(ldst, NULL, sample);
1711 }
1712 }
1713 else
1714 {
1715 // Buffer status is as follows:
1716 // |=====|--|==|
1717 // - Played back data
1718 // = Not played back data
1719 // Line up data in streaming buffer not played back yet from
1720 // beginning of buffer.
1721 memcpy(WorkBuffer, StreamBufferL, bufferSampleNum >> 2);
1722
1723 ldst = StreamBufferL;
1724 lsrc = StreamBufferL + offset1;
1725 size = (bufferSampleNum - offset1) << 1;
1726
1727 memcpy(ldst, lsrc, size);
1728
1729 ldst = StreamBufferL + (size >> 1);
1730
1731 memcpy(ldst, WorkBuffer, bufferSampleNum >> 2);
1732
1733 if (ActivePlayer.audioInfo.sndChannels == 2)
1734 {
1735 memcpy(WorkBuffer, StreamBufferR, bufferSampleNum >> 2);
1736
1737 rdst = StreamBufferR;
1738 rsrc = StreamBufferR + offset1;
1739
1740 memcpy(rdst, rsrc, size);
1741
1742 rdst = StreamBufferR + (size >> 1);
1743
1744 memcpy(rdst, WorkBuffer, bufferSampleNum >> 2);
1745 }
1746
1747 // Updates the list that saves the sample numbers which show the boundaries between movie frames.
1748 index = StreamInfo.top;
1749
1750 while (index != StreamInfo.bottom)
1751 {
1752 tmp = StreamInfo.boundary[index] % bufferSampleNum;
1753
1754 if (tmp > (bufferSampleNum >> 1))
1755 {
1756 StreamInfo.boundary[index] = tmp - offset1;
1757 }
1758 else
1759 {
1760 StreamInfo.boundary[index] = tmp + (bufferSampleNum - offset1);
1761 }
1762
1763 index++;
1764 if (index >= BOUNDARY_NUM)
1765 {
1766 index = 0;
1767 }
1768 }
1769
1770 ldst = StreamBufferL + bufferSampleNum - offset1 + offset2;
1771 sample = offset1 - offset2;
1772
1773 StreamInfo.endSampleNum = bufferSampleNum - sample;
1774
1775 // Fill in open areas of streaming buffer with new data
1776 if (ActivePlayer.audioInfo.sndChannels == 2)
1777 {
1778 rdst = StreamBufferR + bufferSampleNum - offset1 + offset2;
1779 FillStreamBuffer(ldst, rdst, sample);
1780 }
1781 else
1782 {
1783 FillStreamBuffer(ldst, NULL, sample);
1784 }
1785 }
1786 }
1787
1788 // Reinitializes the structure controlling the streaming
1789 StreamInfo.curSampleNum = 0;
1790
1791 if (ActivePlayer.audioInfo.sndChannels == 2)
1792 {
1793 DCFlushRange(StreamBufferR, bufferSampleNum << 1);
1794 AXSetVoiceCurrentAddr(StreamR, OSCachedToPhysical(StreamBufferR) / 2);
1795
1796 }
1797
1798 DCFlushRange(StreamBufferL, bufferSampleNum << 1);
1799 AXSetVoiceCurrentAddr(StreamL, OSCachedToPhysical(StreamBufferL) / 2);
1800
1801 StreamInfo.lastPos = OSCachedToPhysical(StreamBufferL) / 2;
1802
1803 return;
1804 }
1805
1806 /*---------------------------------------------------------------------------*
1807 Name: EntryBoundary
1808
1809 Description: Saves number of samples that shows the boundaries between movie frames to a list.
1810
1811 Arguments: boundarySampleNum Number of samples that shows the boundaries between movie frames
1812
1813 Returns: None
1814 *---------------------------------------------------------------------------*/
1815
EntryBoundary(u64 boundarySampleNum)1816 static void EntryBoundary(u64 boundarySampleNum)
1817 {
1818 StreamInfo.boundary[StreamInfo.bottom] = boundarySampleNum;
1819 StreamInfo.bottom++;
1820 if (StreamInfo.bottom >= BOUNDARY_NUM)
1821 {
1822 StreamInfo.bottom = 0;
1823 }
1824
1825 return;
1826 }
1827
1828 /*---------------------------------------------------------------------------*
1829 Name: CheckBoundary
1830
1831 Description: Checks whether streaming playback location exceeds the boundaries
1832 between movie frames. If it does, the current audio frame
1833 number is incremented.
1834
1835 Arguments: curSampleNum Current streaming playback location.
1836
1837 Returns: None
1838 *---------------------------------------------------------------------------*/
1839
CheckBoundary(u64 curSampleNum)1840 static void CheckBoundary(u64 curSampleNum)
1841 {
1842 while (StreamInfo.top != StreamInfo.bottom)
1843 {
1844 if (StreamInfo.boundary[StreamInfo.top] <= curSampleNum)
1845 {
1846 StreamInfo.top++;
1847 if (StreamInfo.top >= BOUNDARY_NUM)
1848 {
1849 StreamInfo.top = 0;
1850 }
1851 ActivePlayer.curAudioNumber++;
1852 }
1853 else
1854 {
1855 break;
1856 }
1857 }
1858
1859 return;
1860 }
1861
1862 /*---------------------------------------------------------------------------*
1863 Name: GetAudioSample
1864
1865 Description: Copies decoded data to THP audio data buffer specified.
1866
1867 Arguments: left Pointer to memory that copies left channel data.
1868 right Pointer to memory that copies right channel data.
1869 sample Number of samples copied. (Stereo Samples)
1870 reason Pointer to variable to store error code if number
1871 of samples copied does not equal the specified number.
1872
1873 Returns: Actual number of samples copied.
1874 *---------------------------------------------------------------------------*/
1875
GetAudioSample(s16 * left,s16 * right,u32 sample,s32 * reason)1876 static u32 GetAudioSample(s16 *left, s16 *right, u32 sample, s32 *reason)
1877 {
1878 u32 sampleNum, i;
1879 s16 *src;
1880
1881 if (ActivePlayer.playAudioBuffer == NULL)
1882 {
1883 if ((ActivePlayer.playAudioBuffer = (THPAudioBuffer *)PopDecodedAudioBuffer(OS_MESSAGE_NOBLOCK)) == NULL)
1884 {
1885 *reason = EMPTY_AUDIO_DATA;
1886 return 0;
1887 }
1888 }
1889
1890 if (ActivePlayer.playAudioBuffer->validSample)
1891 {
1892 if (ActivePlayer.playAudioBuffer->validSample >= sample)
1893 {
1894 sampleNum = sample;
1895 }
1896 else
1897 {
1898 sampleNum = ActivePlayer.playAudioBuffer->validSample;
1899 }
1900
1901 src = ActivePlayer.playAudioBuffer->curPtr;
1902
1903 // Monaural
1904 if (right == NULL)
1905 {
1906 for (i = 0 ; i < sampleNum ; i++)
1907 {
1908 src++;
1909 *left = *src;
1910 src++;
1911 left++;
1912 }
1913 }
1914 // Stereo
1915 else
1916 {
1917 for (i = 0 ; i < sampleNum ; i++)
1918 {
1919 *right = *src;
1920 src++;
1921 *left = *src;
1922 src++;
1923 right++;
1924 left++;
1925 }
1926 }
1927
1928 ActivePlayer.playAudioBuffer->validSample -= sampleNum;
1929 ActivePlayer.playAudioBuffer->curPtr = src;
1930
1931 if (ActivePlayer.playAudioBuffer->validSample == 0)
1932 {
1933 // Free used THP audio data
1934 PushFreeAudioBuffer(ActivePlayer.playAudioBuffer);
1935 ActivePlayer.playAudioBuffer = NULL;
1936 *reason = EMPTY_AUDIO_BUFFER;
1937 }
1938 else
1939 {
1940 *reason = SUCCESS;
1941 }
1942 }
1943
1944 return sampleNum;
1945 }
1946
1947 /*---------------------------------------------------------------------------*
1948 Name: FillStreamBuffer
1949
1950 Description: Fill in specified buffer with decoded THP audio data.
1951 If boundaries between movie frames appear when filling in,
1952 save the sample number that displays this boundaries to a list.
1953 If the decoded THP audio data does not equal the specified
1954 sample number, fill in boundaries with buffer.
1955
1956 Arguments: left Pointer to memory that copies left channel data.
1957 right Pointer to memory that copies right channel data.
1958 sample Number of samples copied. (Stereo Samples)
1959
1960 Returns: None
1961 *---------------------------------------------------------------------------*/
1962
FillStreamBuffer(s16 * left,s16 * right,u32 sample)1963 static void FillStreamBuffer(s16 *left, s16 *right, u32 sample)
1964 {
1965 u64 tmp;
1966 u32 actualSample, requestSample;
1967 s32 reason;
1968 s16 *l, *r;
1969
1970 requestSample = sample;
1971 l = left;
1972 r = right;
1973
1974 tmp = StreamInfo.endSampleNum;
1975
1976 while(1)
1977 {
1978 actualSample = GetAudioSample(l, r, requestSample, &reason);
1979 tmp += actualSample;
1980
1981 // Copied all of specified sample number to designated buffer
1982 if (reason == SUCCESS)
1983 {
1984 break;
1985 }
1986 // If boundaries between movie frames appears when copying to specified buffer
1987 else if (reason == EMPTY_AUDIO_BUFFER)
1988 {
1989 requestSample -= actualSample;
1990 l += actualSample;
1991 if (r)
1992 {
1993 r += actualSample;
1994 }
1995 // Save number of samples that shows the boundaries between movie frames to a list
1996 EntryBoundary(tmp);
1997 }
1998 // No more decoded THP audio data (decode did not finish in time)
1999 else
2000 {
2001 memset(l, 0, requestSample << 1);
2002 if (r)
2003 {
2004 memset(r, 0, requestSample << 1);
2005 }
2006
2007 break;
2008 }
2009 }
2010
2011 StreamInfo.endSampleNum += sample;
2012
2013 return;
2014 }
2015
2016 /*---------------------------------------------------------------------------*
2017 Name: THPPlayerStreamUpdate
2018
2019 Description: Calls from AX user callback, and checks status of
2020 streaming buffer. If need to update, updates buffer.
2021
2022 Arguments: None
2023
2024 Returns: None
2025 *---------------------------------------------------------------------------*/
2026
THPPlayerStreamUpdate(void)2027 void THPPlayerStreamUpdate(void)
2028 {
2029 u32 bufferSampleNum;
2030 u32 currentPosition;
2031 u32 diff;
2032 u32 halfPosition;
2033
2034 if (Initialized && (StreamL || StreamR) & (ActivePlayer.internalState == THP_PLAYER_PLAY))
2035 {
2036 bufferSampleNum = OSRoundUp32B(STREAM_BUFFER_MS * ActivePlayer.audioInfo.sndFrequency / 1000.0f + 0.5f);
2037
2038 currentPosition = (u32)(StreamL->pb.addr.currentAddressHi << 16) | (StreamL->pb.addr.currentAddressLo);
2039
2040 if (currentPosition >= StreamInfo.lastPos)
2041 {
2042 diff = currentPosition - StreamInfo.lastPos;
2043 }
2044 else
2045 {
2046 u32 startAddr;
2047 u32 endAddr;
2048
2049 startAddr = OSCachedToPhysical(StreamBufferL) / 2;
2050 endAddr = startAddr + bufferSampleNum;
2051
2052 diff = endAddr - StreamInfo.lastPos;
2053 diff += currentPosition - startAddr;
2054 }
2055
2056 StreamInfo.curSampleNum += diff;
2057
2058 // Check if streaming data exceeds boundaries between movie frames
2059 CheckBoundary(StreamInfo.curSampleNum);
2060
2061 halfPosition = OSCachedToPhysical(StreamBufferL) / 2 + bufferSampleNum / 2;
2062
2063 // Check if streaming buffer needs update. If so, updates.
2064 if (currentPosition < StreamInfo.lastPos)
2065 {
2066 TransferStreamData(1);
2067 }
2068
2069 if ((currentPosition >= halfPosition) && (StreamInfo.lastPos < halfPosition))
2070 {
2071 TransferStreamData(0);
2072 }
2073
2074 StreamInfo.lastPos = currentPosition;
2075 }
2076 }
2077
2078 /*---------------------------------------------------------------------------*
2079 Name: TransferStreamData
2080
2081 Description: Updates streaming buffer. Using argument, flag, specifies
2082 location to update streaming buffer (first half or second half).
2083
2084 Arguments: flag 1 is update of second half
2085 0 is update of first half
2086
2087 Returns: None
2088 *---------------------------------------------------------------------------*/
2089
TransferStreamData(s32 flag)2090 static void TransferStreamData(s32 flag)
2091 {
2092 s16 *destBufferL;
2093 s16 *destBufferR;
2094 u32 bufferSampleHalfNum;
2095
2096 bufferSampleHalfNum = OSRoundUp32B(STREAM_BUFFER_MS * ActivePlayer.audioInfo.sndFrequency / 1000.0f + 0.5f) / 2;
2097
2098 if (flag)
2099 {
2100 destBufferL = StreamBufferL + bufferSampleHalfNum;
2101 destBufferR = StreamBufferR + bufferSampleHalfNum;
2102 }
2103 else
2104 {
2105 destBufferL = StreamBufferL;
2106 destBufferR = StreamBufferR;
2107 }
2108
2109 if (ActivePlayer.audioInfo.sndChannels == 2)
2110 {
2111 FillStreamBuffer(destBufferL,
2112 destBufferR,
2113 bufferSampleHalfNum);
2114 DCFlushRange(destBufferL, bufferSampleHalfNum << 1);
2115 DCFlushRange(destBufferR, bufferSampleHalfNum << 1);
2116 }
2117 else
2118 {
2119 FillStreamBuffer(destBufferL,
2120 NULL,
2121 bufferSampleHalfNum);
2122 DCFlushRange(destBufferL, bufferSampleHalfNum << 1);
2123 }
2124
2125 return;
2126 }
2127
2128 /*---------------------------------------------------------------------------*
2129 Name: StreamPlay
2130
2131 Description: Start streaming
2132
2133 Arguments: None
2134
2135 Returns: None
2136 *---------------------------------------------------------------------------*/
2137
StreamPlay(void)2138 static void StreamPlay(void)
2139 {
2140 if (StreamL)
2141 {
2142 AXSetVoiceState(StreamL, AX_PB_STATE_RUN);
2143 }
2144
2145 if (StreamR)
2146 {
2147 AXSetVoiceState(StreamR, AX_PB_STATE_RUN);
2148 }
2149
2150 return;
2151 }
2152
2153 /*---------------------------------------------------------------------------*
2154 Name: StreamPause
2155
2156 Description: Pause streaming
2157
2158 Arguments: None
2159
2160 Returns: None
2161 *---------------------------------------------------------------------------*/
2162
StreamPause(void)2163 static void StreamPause(void)
2164 {
2165 if (StreamL)
2166 {
2167 AXSetVoiceState(StreamL, AX_PB_STATE_STOP);
2168 }
2169
2170 if (StreamR)
2171 {
2172 AXSetVoiceState(StreamR, AX_PB_STATE_STOP);
2173 }
2174
2175 return;
2176 }
2177
2178 /*---------------------------------------------------------------------------*
2179 Name: StreamQuit
2180
2181 Description: Stop streaming
2182
2183 Arguments: None
2184
2185 Returns: None
2186 *---------------------------------------------------------------------------*/
2187
StreamQuit(void)2188 static void StreamQuit(void)
2189 {
2190 if (StreamL)
2191 {
2192 MIXReleaseChannel(StreamL);
2193 AXFreeVoice(StreamL);
2194 StreamL = NULL;
2195 }
2196
2197 if (StreamR)
2198 {
2199 MIXReleaseChannel(StreamR);
2200 AXFreeVoice(StreamR);
2201 StreamR = NULL;
2202 }
2203
2204 return;
2205 }
2206
2207 /*---------------------------------------------------------------------------*
2208 Name: THPPlayerGetStreamAXPB
2209
2210 Description: Acquires AXVPB pointer set aside internally
2211
2212 Arguments: left Pointer to pointer variable to save left channel
2213 AXVPB pointer.
2214 right Pointer to pointer variable to save right channel
2215 AXVPB pointer.
2216
2217 Returns: None
2218 *---------------------------------------------------------------------------*/
2219
THPPlayerGetStreamAXPB(AXVPB ** left,AXVPB ** right)2220 void THPPlayerGetStreamAXPB(AXVPB **left, AXVPB **right)
2221 {
2222 *left = StreamL;
2223 *right = StreamR;
2224
2225 return;
2226 }
2227