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.4 2008/05/12 09:10:42 aka
15 Changed channel conversion from stereo to monaural..
16
17 Revision 1.3 2008/02/28 07:01:00 aka
18 Added THPPlayerSetSoundMode().
19 Added THPPlayerGetSoundMode().
20 Changed MixAudio() enabling to output monaural data.
21
22 Revision 1.2 2006/02/03 11:44:07 aka
23 Changed audio frame from 5msec to 3msec.
24
25 Revision 1.1 2006/02/03 10:01:41 aka
26 Imported from Dolphin tree.
27
28 5 03/11/25 11:24 Dante
29 Japanese to English translation of comments and text strings
30
31 4 03/09/16 15:48 Suzuki
32 changed the process which skips to decode when decoding is delay.
33
34 3 02/05/31 9:06a Suzuki
35 supported multi audio track
36
37 2 02/02/28 6:36p Akagi
38 enabled to use with MusyX/AX by Suzuki-san (IRD).
39
40 1 02/01/16 10:53a Akagi
41 Initial revision made by Suzuki-san (IRD).
42
43 $NoKeywords: $
44
45 *---------------------------------------------------------------------------*/
46
47 #include <string.h>
48 #include <revolution.h>
49 #include "THPPlayer.h"
50 #include "THPVideoDecode.h"
51 #include "THPAudioDecode.h"
52 #include "THPRead.h"
53 #include "THPDraw.h"
54
55 #define SAMPLES_PER_AUDIO_FRAME (96) // 3msec (32khz)
56 #define BYTES_PER_AUDIO_FRAME (SAMPLES_PER_AUDIO_FRAME * 4)
57
58 /*---------------------------------------------------------------------------*
59 Global Function
60 *---------------------------------------------------------------------------*/
61
62 void PrepareReady(BOOL flag);
63
64 /*---------------------------------------------------------------------------*
65 Static Function
66 *---------------------------------------------------------------------------*/
67
68 static void PlayControl(u32 retraceCount);
69 static BOOL ProperTimingForStart(void);
70 static BOOL ProperTimingForGettingNextFrame(void);
71 static void PushUsedTextureSet(void *buffer);
72 static void *PopUsedTextureSet(void);
73 static void THPAudioMixCallback(void);
74 static void MixAudio(s16 *destination, s16 *source, u32 sample);
75
76 /*---------------------------------------------------------------------------*
77 Global Variable
78 *---------------------------------------------------------------------------*/
79
80 THPPlayer ActivePlayer;
81
82 /*---------------------------------------------------------------------------*
83 Static Variable
84 *---------------------------------------------------------------------------*/
85
86 // 32768 * ((vol * vol) / (127 * 127))
87 static u16 VolumeTable[] =
88 {
89 0, 2, 8, 18, 32, 50, 73, 99,
90 130, 164, 203, 245, 292, 343, 398, 457,
91 520, 587, 658, 733, 812, 895, 983, 1074,
92 1170, 1269, 1373, 1481, 1592, 1708, 1828, 1952,
93 2080, 2212, 2348, 2488, 2632, 2781, 2933, 3090,
94 3250, 3415, 3583, 3756, 3933, 4114, 4298, 4487,
95 4680, 4877, 5079, 5284, 5493, 5706, 5924, 6145,
96 6371, 6600, 6834, 7072, 7313, 7559, 7809, 8063,
97 8321, 8583, 8849, 9119, 9394, 9672, 9954, 10241,
98 10531, 10826, 11125, 11427, 11734, 12045, 12360, 12679,
99 13002, 13329, 13660, 13995, 14335, 14678, 15025, 15377,
100 15732, 16092, 16456, 16823, 17195, 17571, 17951, 18335,
101 18723, 19115, 19511, 19911, 20316, 20724, 21136, 21553,
102 21974, 22398, 22827, 23260, 23696, 24137, 24582, 25031,
103 25484, 25941, 26402, 26868, 27337, 27810, 28288, 28769,
104 29255, 29744, 30238, 30736, 31238, 31744, 32254, 32768
105 };
106
107 static BOOL Initialized = 0;
108 static s32 WorkBuffer[16] ATTRIBUTE_ALIGN(32);
109 static OSMessageQueue PrepareReadyQueue;
110 static OSMessageQueue UsedTextureSetQueue;
111 static OSMessage PrepareReadyMessage;
112 static OSMessage UsedTextureSetMessage[DECODE_VIDEO_BUFFER_NUM];
113 static VIRetraceCallback OldVIPostCallback = NULL;
114 static s16 SoundBuffer[2][SAMPLES_PER_AUDIO_FRAME * 2] ATTRIBUTE_ALIGN(32);
115 static s32 SoundBufferIndex;
116 static AIDCallback OldAIDCallback = NULL;
117 static s16 *LastAudioBuffer;
118 static s16 *CurAudioBuffer;
119 static s32 AudioSystem = 0;
120
121 /*---------------------------------------------------------------------------*
122 Name: THPPlayerInit
123
124 Description: Enables locked-cache, and initializes player and THP library.
125 Register AI FIFO DMA call back for player.
126
127 Arguments: audioSystem Specifies the audio library to be used simultaneously.
128 If no audio library is to be used simultaneouly,
129 specifies THP_MODE_ALONE.
130
131 Returns: Returns TRUE if succeeds, and FALSE if fails.
132 *---------------------------------------------------------------------------*/
133
THPPlayerInit(s32 audioSystem)134 BOOL THPPlayerInit(s32 audioSystem)
135 {
136 BOOL old;
137
138 ASSERTMSG(audioSystem >= 0 && audioSystem <= THP_MODE_WITH_MUSYX, "audioSystem flag is invalid\n");
139
140 memset(&ActivePlayer, 0, sizeof(THPPlayer));
141
142 // default sound mode...
143 ActivePlayer.soundMode = THP_SOUND_MODE_STEREO;
144
145 LCEnable();
146
147 OSInitMessageQueue(&UsedTextureSetQueue,
148 UsedTextureSetMessage,
149 DECODE_VIDEO_BUFFER_NUM);
150
151 if (THPInit() == FALSE)
152 {
153 return FALSE;
154 }
155
156 old = OSDisableInterrupts();
157
158 AudioSystem = audioSystem;
159 SoundBufferIndex = 0;
160 LastAudioBuffer = NULL;
161 CurAudioBuffer = NULL;
162
163 OldAIDCallback = AIRegisterDMACallback(THPAudioMixCallback);
164
165 if (OldAIDCallback == NULL && AudioSystem != THP_MODE_ALONE)
166 {
167 AIRegisterDMACallback(NULL);
168
169 OSRestoreInterrupts(old);
170
171 #ifdef _DEBUG
172 OSReport("Pleae call AXInit or sndInit before you call THPPlayerInit\n");
173 #endif
174 return FALSE;
175 }
176
177 OSRestoreInterrupts(old);
178
179 if (AudioSystem == THP_MODE_ALONE)
180 {
181 memset(SoundBuffer, 0, BYTES_PER_AUDIO_FRAME << 1);
182
183 DCFlushRange(SoundBuffer, BYTES_PER_AUDIO_FRAME << 1);
184
185 AIInitDMA((u32)SoundBuffer[SoundBufferIndex], BYTES_PER_AUDIO_FRAME);
186
187 AIStartDMA();
188 }
189
190 Initialized = TRUE;
191
192 return TRUE;
193 }
194
195 /*---------------------------------------------------------------------------*
196 Name: THPPlayerQuit
197
198 Description: Disables locked cache.
199 Restores AI FIFO DMA callback to the status before THPPlayerInit is called.
200
201 Arguments: None
202
203 Returns: None
204 *---------------------------------------------------------------------------*/
205
THPPlayerQuit(void)206 void THPPlayerQuit(void)
207 {
208 BOOL old;
209
210 LCDisable();
211
212 old = OSDisableInterrupts();
213
214 if (OldAIDCallback)
215 {
216 AIRegisterDMACallback(OldAIDCallback);
217 }
218
219 OSRestoreInterrupts(old);
220
221 Initialized = FALSE;
222
223 return;
224 }
225
226 /*---------------------------------------------------------------------------*
227 Name: THPPlayerOpen
228
229 Description: Opens THP movie file. Loads required data.
230
231 Arguments: fileName Filename of THP movie.
232 onMemory Flag to do OnMemory playback or not.
233
234 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
235 *---------------------------------------------------------------------------*/
236
THPPlayerOpen(char * fileName,BOOL onMemory)237 BOOL THPPlayerOpen(char *fileName, BOOL onMemory)
238 {
239 s32 offset, i;
240
241 if (!Initialized)
242 {
243 #ifdef _DEBUG
244 OSReport("You must call THPPlayerInit before you call this function\n");
245 #endif
246 return FALSE;
247 }
248
249 if (ActivePlayer.open)
250 {
251 #ifdef _DEBUG
252 OSReport("Can't open %s. Because thp file have already opened.\n");
253 #endif
254 return FALSE;
255 }
256
257 // Clears current video data and audio data
258 memset(&ActivePlayer.videoInfo, 0, sizeof(THPVideoInfo));
259 memset(&ActivePlayer.audioInfo, 0, sizeof(THPAudioInfo));
260
261 if (DVDOpen(fileName, &ActivePlayer.fileInfo) == FALSE)
262 {
263 #ifdef _DEBUG
264 OSReport("Can't open %s.\n", fileName);
265 #endif
266 return FALSE;
267 }
268
269 // Get THP header
270 if (DVDRead(&ActivePlayer.fileInfo, WorkBuffer, 64, 0) < 0)
271 {
272 #ifdef _DEBUG
273 OSReport("Fail to read the header from THP file.\n");
274 #endif
275 DVDClose(&ActivePlayer.fileInfo);
276 return FALSE;
277 }
278
279 memcpy(&ActivePlayer.header, WorkBuffer, sizeof(THPHeader));
280
281 // Check if THP movie file
282 if (strcmp(ActivePlayer.header.magic, "THP") != 0)
283 {
284 #ifdef _DEBUG
285 OSReport("This file is not THP file.\n");
286 #endif
287 DVDClose(&ActivePlayer.fileInfo);
288 return FALSE;
289 }
290
291 // Check version
292 if (ActivePlayer.header.version != THP_VERSION)
293 {
294 #ifdef _DEBUG
295 OSReport("invalid version.\n");
296 #endif
297 DVDClose(&ActivePlayer.fileInfo);
298 return FALSE;
299 }
300
301 offset = (s32)ActivePlayer.header.compInfoDataOffsets;
302
303 // Get component data in frame
304 if (DVDRead(&ActivePlayer.fileInfo, WorkBuffer, 32, offset) < 0)
305 {
306 #ifdef _DEBUG
307 OSReport("Fail to read the frame component infomation from THP file.\n");
308 #endif
309 DVDClose(&ActivePlayer.fileInfo);
310 return FALSE;
311 }
312
313 memcpy(&ActivePlayer.compInfo, WorkBuffer, sizeof(THPFrameCompInfo));
314
315 offset += sizeof(THPFrameCompInfo);
316
317 ActivePlayer.audioExist = 0;
318
319 // Check components in frame
320 for(i = 0 ; i < ActivePlayer.compInfo.numComponents ; i++)
321 {
322 switch(ActivePlayer.compInfo.frameComp[i])
323 {
324 case THP_VIDEO_COMP: // Get video data of components
325 if (DVDRead(&ActivePlayer.fileInfo, WorkBuffer, 32, offset) < 0)
326 {
327 #ifdef _DEBUG
328 OSReport("Fail to read the video infomation from THP file.\n");
329 #endif
330 DVDClose(&ActivePlayer.fileInfo);
331 return FALSE;
332 }
333 memcpy(&ActivePlayer.videoInfo, WorkBuffer, sizeof(THPVideoInfo));
334 offset += sizeof(THPVideoInfo);
335 break;
336 case THP_AUDIO_COMP: // Get audio data of components
337 if (DVDRead(&ActivePlayer.fileInfo, WorkBuffer, 32, offset) < 0)
338 {
339 #ifdef _DEBUG
340 OSReport("Fail to read the video infomation from THP file.\n");
341 #endif
342 DVDClose(&ActivePlayer.fileInfo);
343 return FALSE;
344 }
345 memcpy(&ActivePlayer.audioInfo, WorkBuffer, sizeof(THPAudioInfo));
346 ActivePlayer.audioExist = 1;
347 offset += sizeof(THPAudioInfo);
348
349 break;
350 default:
351 #ifdef _DEBUG
352 OSReport("Unknow frame components.\n");
353 #endif
354 return FALSE;
355 }
356 }
357
358 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_STOP;
359 ActivePlayer.playFlag = 0;
360 ActivePlayer.onMemory = onMemory;
361 ActivePlayer.open = TRUE;
362 ActivePlayer.curVolume = 127.0f;
363 ActivePlayer.targetVolume = ActivePlayer.curVolume;
364 ActivePlayer.rampCount = 0;
365
366 return TRUE;
367 }
368
369 /*---------------------------------------------------------------------------*
370 Name: THPPlayerClose
371
372 Description: Close THP movie file.
373
374 Arguments: None
375
376 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
377 *---------------------------------------------------------------------------*/
378
THPPlayerClose(void)379 BOOL THPPlayerClose(void)
380 {
381 if (ActivePlayer.open)
382 {
383 if (ActivePlayer.state == THP_PLAYER_STOP)
384 {
385 ActivePlayer.open = FALSE;
386 DVDClose(&ActivePlayer.fileInfo);
387
388 return TRUE;
389 }
390 }
391
392 return FALSE;
393 }
394
395 /*---------------------------------------------------------------------------*
396 Name: THPPlayerCalcNeedMemory
397
398 Description: Calculates needed memory for THP movie playback
399
400 Arguments: None
401
402 Returns: Returns needed memory size if successful, and 0 if unsuccessful.
403 *---------------------------------------------------------------------------*/
404
THPPlayerCalcNeedMemory(void)405 u32 THPPlayerCalcNeedMemory(void)
406 {
407 u32 size;
408
409 if (ActivePlayer.open)
410 {
411 // Buffer size for read
412 if (ActivePlayer.onMemory)
413 {
414 size = OSRoundUp32B(ActivePlayer.header.movieDataSize);
415 }
416 else
417 {
418 size = OSRoundUp32B(ActivePlayer.header.bufSize) * READ_BUFFER_NUM;
419 }
420
421 // Size of texture buffer
422 size += OSRoundUp32B(ActivePlayer.videoInfo.xSize * ActivePlayer.videoInfo.ySize)
423 * DECODE_VIDEO_BUFFER_NUM; //Y
424 size += OSRoundUp32B(ActivePlayer.videoInfo.xSize * ActivePlayer.videoInfo.ySize / 4)
425 * DECODE_VIDEO_BUFFER_NUM; //U
426 size += OSRoundUp32B(ActivePlayer.videoInfo.xSize * ActivePlayer.videoInfo.ySize / 4)
427 * DECODE_VIDEO_BUFFER_NUM; //V
428
429 // Size of audio buffer
430 if (ActivePlayer.audioExist)
431 {
432 size += (OSRoundUp32B(ActivePlayer.header.audioMaxSamples * 4) * DECODE_AUDIO_BUFFER_NUM);
433 }
434
435 size += THP_WORK_SIZE;
436
437 return size;
438 }
439
440 return 0;
441 }
442
443 /*---------------------------------------------------------------------------*
444 Name: THPPlayerSetBuffer
445
446 Description: Allocate required memory for movie playback to THPPlayer structure.
447
448 Arguments: buffer Pointer to memory area for THP movie set aside externally.
449
450 Returns: Returns TRUE if successful, and FALSE if successful.
451 *---------------------------------------------------------------------------*/
452
THPPlayerSetBuffer(u8 * buffer)453 BOOL THPPlayerSetBuffer(u8 *buffer)
454 {
455 u32 i;
456 u8 *ptr;
457 u32 ysize, uvsize;
458
459 ASSERTMSG(buffer != NULL, "buffer is NULL");
460
461 if (ActivePlayer.open && (ActivePlayer.state == THP_PLAYER_STOP))
462 {
463 ptr = buffer;
464
465 // Set buffer for read
466 if (ActivePlayer.onMemory)
467 {
468 ActivePlayer.movieData = ptr;
469 ptr += ActivePlayer.header.movieDataSize;
470 }
471 else
472 {
473 for (i = 0 ; i < READ_BUFFER_NUM ; i++)
474 {
475 ActivePlayer.readBuffer[i].ptr = ptr;
476 ptr += OSRoundUp32B(ActivePlayer.header.bufSize);
477 }
478 }
479
480 ysize = OSRoundUp32B(ActivePlayer.videoInfo.xSize * ActivePlayer.videoInfo.ySize);
481 uvsize = OSRoundUp32B(ActivePlayer.videoInfo.xSize * ActivePlayer.videoInfo.ySize / 4);
482
483 // Set texture buffer
484 for (i = 0 ; i < DECODE_VIDEO_BUFFER_NUM ; i++)
485 {
486 ActivePlayer.textureSet[i].ytexture = ptr;
487 DCInvalidateRange(ptr, ysize);
488 ptr += ysize;
489 ActivePlayer.textureSet[i].utexture = ptr;
490 DCInvalidateRange(ptr, uvsize);
491 ptr += uvsize;
492 ActivePlayer.textureSet[i].vtexture = ptr;
493 DCInvalidateRange(ptr, uvsize);
494 ptr += uvsize;
495 }
496
497 // Set audio buffer
498 if (ActivePlayer.audioExist)
499 {
500 for (i = 0 ; i < DECODE_AUDIO_BUFFER_NUM ; i++)
501 {
502 ActivePlayer.audioBuffer[i].buffer = (s16 *)ptr;
503 ActivePlayer.audioBuffer[i].curPtr = (s16 *)ptr;
504 ActivePlayer.audioBuffer[i].validSample = 0;
505 ptr += OSRoundUp32B(ActivePlayer.header.audioMaxSamples * 4);
506 }
507 }
508
509 ActivePlayer.thpWork = (void *)ptr;
510
511 return TRUE;
512 }
513
514 return FALSE;
515 }
516
517 /*---------------------------------------------------------------------------*
518 Name: InitAllMessageQueue
519
520 Description: Initializes queue used with video decode thread, audio
521 thread, and read thread.
522
523 Arguments: None
524
525 Returns: None
526 *---------------------------------------------------------------------------*/
527
InitAllMessageQueue(void)528 static void InitAllMessageQueue(void)
529 {
530 s32 i;
531 THPReadBuffer *readBuffer;
532 THPTextureSet *textureSet;
533 THPAudioBuffer *audioBuffer;
534
535 // Push all read buffers to free read buffer queue
536 if (!ActivePlayer.onMemory)
537 {
538 for (i = 0 ; i < READ_BUFFER_NUM ; i++)
539 {
540 readBuffer = &ActivePlayer.readBuffer[i];
541 PushFreeReadBuffer(readBuffer);
542 }
543 }
544
545 // Push all texture buffers for storing decoded THP video data to
546 // free texture buffer queue.
547 for (i = 0 ; i < DECODE_VIDEO_BUFFER_NUM ; i++)
548 {
549 textureSet = &ActivePlayer.textureSet[i];
550 PushFreeTextureSet(textureSet);
551 }
552
553 // Push all audio buffers for storing decoded THP audio data to
554 // free audio buffer queue.
555 if (ActivePlayer.audioExist)
556 {
557 for (i = 0 ; i < DECODE_AUDIO_BUFFER_NUM ; i++)
558 {
559 audioBuffer = &ActivePlayer.audioBuffer[i];
560 PushFreeAudioBuffer(audioBuffer);
561 }
562 }
563
564 OSInitMessageQueue(&PrepareReadyQueue,
565 &PrepareReadyMessage,
566 1);
567 }
568
569 /*---------------------------------------------------------------------------*
570 Name: WaitUntilPrepare
571
572 Description: Waits until playback preparations completed.
573
574 Arguments: None
575
576 Returns: If playback preparations complete, returns TRUE. If preparations fail, returns FALSE.
577 *---------------------------------------------------------------------------*/
578
WaitUntilPrepare(void)579 static BOOL WaitUntilPrepare(void)
580 {
581 OSMessage msg;
582
583 OSReceiveMessage(&PrepareReadyQueue, &msg, OS_MESSAGE_BLOCK);
584
585 if ((BOOL)msg)
586 {
587 return TRUE;
588 }
589 else
590 {
591 return FALSE;
592 }
593 }
594
595 /*---------------------------------------------------------------------------*
596 Name: PrepareReady
597
598 Description: Sends message about whether or not playback preparations are completed.
599
600 Arguments: flag If playback preparations complete, returns TRUE. If preparations fail, returns FALSE.
601
602 Returns: None
603 *---------------------------------------------------------------------------*/
604
PrepareReady(BOOL flag)605 void PrepareReady(BOOL flag)
606 {
607 OSSendMessage(&PrepareReadyQueue, (OSMessage)flag, OS_MESSAGE_BLOCK);
608
609 return;
610 }
611
612 /*---------------------------------------------------------------------------*
613 Name: THPPlayerPrepare
614
615 Description: Carries out preparations for movie playback. Does not
616 return until THP movie playback preparations completed.
617 If the THP movie has no audio, the audioTrack argument is ignored.
618
619 Arguments: frameNum Specifies the frame number of the movie playback.
620 If the THP movie file does not contain THPFrameOffsetData,
621 anything other than 0 is an error.
622 playFlag Specifies one-shot or loop playback flag.
623 audioTrack Specifies audio track number to be played back.
624
625 Returns: If playback is ready, returns TRUE. If playback preparation fails, returns FALSE.
626 *---------------------------------------------------------------------------*/
627
THPPlayerPrepare(s32 frameNum,s32 playFlag,s32 audioTrack)628 BOOL THPPlayerPrepare(s32 frameNum, s32 playFlag, s32 audioTrack)
629 {
630 s32 offset;
631 u8 *ptr;
632
633 if (ActivePlayer.open && (ActivePlayer.state == THP_PLAYER_STOP))
634 {
635 // Check if specified starting frame number is appropriate
636 if (frameNum > 0)
637 {
638 // Does THP movie file have offset data?
639 if (!ActivePlayer.header.offsetDataOffsets)
640 {
641 #ifdef _DEBUG
642 OSReport("This THP file doesn't have the offset data\n");
643 #endif
644 return FALSE;
645 }
646
647 // Does starting frame number exceed total frames?
648 if (ActivePlayer.header.numFrames > frameNum)
649 {
650 offset = (s32)(ActivePlayer.header.offsetDataOffsets + (frameNum - 1) * 4);
651
652 if (DVDRead(&ActivePlayer.fileInfo,
653 WorkBuffer,
654 32,
655 offset) < 0)
656 {
657 #ifdef _DEBUG
658 OSReport("Fail to read the offset data from THP file.\n");
659 #endif
660 return FALSE;
661 }
662
663 // Set starting file offset, frame size, and frame number
664 ActivePlayer.initOffset = (s32)(ActivePlayer.header.movieDataOffsets
665 + WorkBuffer[0]);
666 ActivePlayer.initReadFrame = frameNum;
667 ActivePlayer.initReadSize = (s32)(WorkBuffer[1] - WorkBuffer[0]);
668 }
669 else
670 {
671 #ifdef _DEBUG
672 OSReport("Specified frame number is over total frame number\n");
673 #endif
674 return FALSE;
675 }
676 }
677 // If 0, from beginning
678 else
679 {
680 ActivePlayer.initOffset = (s32)ActivePlayer.header.movieDataOffsets;
681 ActivePlayer.initReadSize = (s32)ActivePlayer.header.firstFrameSize;
682 ActivePlayer.initReadFrame = frameNum;
683 }
684
685 if (ActivePlayer.audioExist)
686 {
687 if (audioTrack < 0 || audioTrack >= ActivePlayer.audioInfo.sndNumTracks)
688 {
689 #ifdef _DEBUG
690 OSReport("Specified audio track number is invalid\n");
691 #endif
692 return FALSE;
693 }
694 else
695 {
696 ActivePlayer.curAudioTrack = audioTrack;
697 }
698 }
699
700 playFlag &= THP_PLAY_LOOP;
701 ActivePlayer.playFlag = (u8)playFlag;
702 ActivePlayer.videoDecodeCount = 0;
703
704 // If On Memory playback, load all THP movie data to memory
705 if (ActivePlayer.onMemory)
706 {
707 if (DVDRead(&ActivePlayer.fileInfo,
708 ActivePlayer.movieData,
709 (s32)ActivePlayer.header.movieDataSize,
710 (s32)ActivePlayer.header.movieDataOffsets) < 0)
711 {
712 #ifdef _DEBUG
713 OSReport("Fail to read all movie data from THP file\n");
714 #endif
715 return FALSE;
716 }
717
718 ptr = ActivePlayer.movieData + ActivePlayer.initOffset - ActivePlayer.header.movieDataOffsets;
719
720 // Create video decode thread
721 CreateVideoDecodeThread(VIDEO_THREAD_PRIORITY, ptr);
722
723 // Create audio decode thread if required
724 if (ActivePlayer.audioExist)
725 {
726 CreateAudioDecodeThread(AUDIO_THREAD_PRIORITY, ptr);
727 }
728 }
729 // Not On Memory playback
730 else
731 {
732 // Create video decode thread
733 CreateVideoDecodeThread(VIDEO_THREAD_PRIORITY, NULL);
734
735 // Create audio decode thread if required
736 if (ActivePlayer.audioExist)
737 {
738 CreateAudioDecodeThread(AUDIO_THREAD_PRIORITY, NULL);
739 }
740
741 // Create read thread
742 CreateReadThread(READ_THREAD_PRIORITY);
743 }
744
745 ActivePlayer.curVideoNumber = -1;
746 ActivePlayer.curAudioNumber = 0;
747
748 // Initialize queues used with various threads
749 InitAllMessageQueue();
750
751 VideoDecodeThreadStart();
752
753 if (ActivePlayer.audioExist)
754 {
755 AudioDecodeThreadStart();
756 }
757
758 if (!ActivePlayer.onMemory)
759 {
760 ReadThreadStart();
761 }
762
763 // Wait until thread preparation completed.
764 if (WaitUntilPrepare() == FALSE)
765 {
766 return FALSE;
767 }
768
769 // If preparations complete, state goes to preparations complete
770 ActivePlayer.state = THP_PLAYER_PREPARE;
771 ActivePlayer.internalState = THP_PLAYER_STOP;
772
773 // Initialize variables
774 ActivePlayer.dispTextureSet = NULL;
775 ActivePlayer.playAudioBuffer = NULL;
776
777 // Register VI post callback that controls playback
778 OldVIPostCallback = VISetPostRetraceCallback(PlayControl);
779
780 return TRUE;
781 }
782
783 return FALSE;
784 }
785
786 /*---------------------------------------------------------------------------*
787 Name: THPPlayerPlay
788
789 Description: Start of movie playback.
790
791 Arguments: None
792
793 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
794 *---------------------------------------------------------------------------*/
795
THPPlayerPlay(void)796 BOOL THPPlayerPlay(void)
797 {
798 if (ActivePlayer.open && ((ActivePlayer.state == THP_PLAYER_PREPARE)
799 || (ActivePlayer.state == THP_PLAYER_PAUSE)))
800 {
801 ActivePlayer.state = THP_PLAYER_PLAY;
802 ActivePlayer.prevCount = 0;
803 ActivePlayer.curCount = 0;
804 ActivePlayer.retraceCount = -1;
805
806 return TRUE;
807 }
808
809 return FALSE;
810 }
811
812 /*---------------------------------------------------------------------------*
813 Name: THPPlayerStop
814
815 Description: Stop for movie playback.
816 Returns VI post callback to original state and stops threads.
817
818 Arguments: None
819
820 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
821 *---------------------------------------------------------------------------*/
822
THPPlayerStop(void)823 void THPPlayerStop(void)
824 {
825 void *texture;
826
827 if (ActivePlayer.open && !(ActivePlayer.state == THP_PLAYER_STOP))
828 {
829 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_STOP;
830
831 // Return VI post callback
832 VISetPostRetraceCallback(OldVIPostCallback);
833
834 // Cancel if stopping threads and loading data
835 if (!ActivePlayer.onMemory)
836 {
837 DVDCancel(&ActivePlayer.fileInfo.cb);
838 ReadThreadCancel();
839 }
840
841 VideoDecodeThreadCancel();
842
843 if (ActivePlayer.audioExist)
844 {
845 AudioDecodeThreadCancel();
846 }
847
848 // Empty played back texture queues.
849 while (1)
850 {
851 texture = PopUsedTextureSet();
852 if (texture == NULL)
853 {
854 break;
855 }
856 }
857
858 ActivePlayer.curVolume = ActivePlayer.targetVolume;
859 ActivePlayer.rampCount = 0;
860
861 // Clear errors
862 ActivePlayer.dvdError = FALSE;
863 ActivePlayer.videoError = FALSE;
864 }
865
866 return;
867 }
868
869 /*---------------------------------------------------------------------------*
870 Name: THPPlayerPause
871
872 Description: Pause movie playback
873
874 Arguments: None
875
876 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
877 *---------------------------------------------------------------------------*/
878
THPPlayerPause(void)879 BOOL THPPlayerPause(void)
880 {
881 if (ActivePlayer.open && (ActivePlayer.state == THP_PLAYER_PLAY))
882 {
883 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_PAUSE;
884
885 return TRUE;
886 }
887
888 return FALSE;
889 }
890
891 /*---------------------------------------------------------------------------*
892 Name: THPPlayerSkip
893
894 Description: Skip movie ahead one frame.
895
896 Arguments: None
897
898 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
899 *---------------------------------------------------------------------------*/
900
THPPlayerSkip(void)901 BOOL THPPlayerSkip(void)
902 {
903 s32 frameNumber, audioGet, videoGet;
904
905 if (ActivePlayer.open && (ActivePlayer.state == THP_PLAYER_PAUSE))
906 {
907 // Block in function until get decoded THP video data so
908 // release played back THP video data in advance.
909 THPPlayerDrawDone();
910
911 // If have audio
912 if (ActivePlayer.audioExist)
913 {
914 frameNumber = ActivePlayer.curAudioNumber + ActivePlayer.initReadFrame;
915
916 // Check if one shot and also if audio has reached end.
917 if (!(ActivePlayer.playFlag & THP_PLAY_LOOP) && (frameNumber == ActivePlayer.header.numFrames))
918 {
919 if (ActivePlayer.playAudioBuffer)
920 {
921 PushFreeAudioBuffer(ActivePlayer.playAudioBuffer);
922 ActivePlayer.playAudioBuffer = NULL;
923 }
924 audioGet = 0;
925 }
926 else
927 {
928 // Release current audio buffer
929 if (ActivePlayer.playAudioBuffer)
930 {
931 PushFreeAudioBuffer(ActivePlayer.playAudioBuffer);
932 }
933
934 // Wait until get next audio buffer
935 ActivePlayer.playAudioBuffer = (THPAudioBuffer *)PopDecodedAudioBuffer(OS_MESSAGE_BLOCK);
936 ActivePlayer.curAudioNumber++;
937
938 audioGet = 1;
939 }
940 }
941
942 if (ActivePlayer.dispTextureSet)
943 {
944 frameNumber = ActivePlayer.dispTextureSet->frameNumber + ActivePlayer.initReadFrame;
945 }
946 else
947 {
948 frameNumber = ActivePlayer.initReadFrame - 1;
949 }
950
951 // Check if one shot and also if video has reached end.
952 if (!(ActivePlayer.playFlag & THP_PLAY_LOOP) && (frameNumber == ActivePlayer.header.numFrames - 1))
953 {
954 videoGet = 0;
955 }
956 else
957 {
958 // Release current texture buffer
959 if (ActivePlayer.dispTextureSet)
960 {
961 PushFreeTextureSet(ActivePlayer.dispTextureSet);
962 }
963
964 // Wait until get next texture buffer
965 ActivePlayer.dispTextureSet = (THPTextureSet *)PopDecodedTextureSet(OS_MESSAGE_BLOCK);
966
967 if (ActivePlayer.audioExist)
968 {
969 ActivePlayer.curVideoNumber++;
970 }
971
972 videoGet = 1;
973 }
974
975 if (audioGet || videoGet)
976 {
977 return TRUE;
978 }
979 else
980 {
981 return FALSE;
982 }
983 }
984
985 return FALSE;
986 }
987
988 /*---------------------------------------------------------------------------*
989 Name: PlayControl
990
991 Description: Controls movie playback. Gets decoded THP video data at
992 appropriate timing.
993
994 Arguments: retraceCount Current retrace count
995
996 Returns: None
997 *---------------------------------------------------------------------------*/
998
PlayControl(u32 retraceCount)999 static void PlayControl(u32 retraceCount)
1000 {
1001 s32 diff, frameNumber;
1002 THPTextureSet *textureSet;
1003
1004 if (OldVIPostCallback)
1005 {
1006 OldVIPostCallback(retraceCount);
1007 }
1008
1009 textureSet = (THPTextureSet *)0xFFFFFFFF;
1010
1011 if (ActivePlayer.open && (ActivePlayer.state == THP_PLAYER_PLAY))
1012 {
1013 // If an error has occurred, change state to error.
1014 if (ActivePlayer.dvdError || ActivePlayer.videoError)
1015 {
1016 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_ERROR;
1017
1018 return;
1019 }
1020
1021 ActivePlayer.retraceCount++;
1022
1023 // When start THP movie playback and when end pause
1024 if (ActivePlayer.retraceCount == 0)
1025 {
1026 // Appropriate timing for start of playback?
1027 if (ProperTimingForStart())
1028 {
1029 // If THP movie has audio
1030 if (ActivePlayer.audioExist)
1031 {
1032 // Calculate difference between current audio playback frames and current video playback frames
1033 diff = ActivePlayer.curVideoNumber - ActivePlayer.curAudioNumber;
1034
1035 // If audio is not slower than video, move video ahead
1036 if (diff <= 1)
1037 {
1038 // Acquire decoded THP video data
1039 textureSet = (THPTextureSet *)PopDecodedTextureSet(OS_MESSAGE_NOBLOCK);
1040
1041 // Increment THP video data number (ideal value)
1042 ActivePlayer.curVideoNumber++;
1043 }
1044 // Allow audio output if slow
1045 else
1046 {
1047 ActivePlayer.internalState = THP_PLAYER_PLAY;
1048 }
1049 }
1050 // If THP movie has no audio
1051 else
1052 {
1053 textureSet = (THPTextureSet *)PopDecodedTextureSet(OS_MESSAGE_NOBLOCK);
1054 }
1055 }
1056 // If not appropriate timing, wait for next VSYNC.
1057 else
1058 {
1059 ActivePlayer.retraceCount = -1;
1060 }
1061 }
1062 // During playback
1063 else
1064 {
1065 // Enables audio output after 1 VSYNC to obtain starting THP video
1066 // data. It is assumed that the movie rendering loop is looping with 1 VSYNC.
1067 // The reason for this is:
1068 //
1069 // [Flow from THPPlayerPlay to display of starting frame]
1070 //
1071 // <Call THPPlayerPlay>
1072 // -----------------VSYNC----------------------------------------
1073 // <Obtain starting THP video data in VI post-callback>
1074 // <Render starting THP video data with your rendering loop, and
1075 // call VISetNextFrameBuffer and VIFlush.>
1076 // ------------------VSYNC----------------------------
1077 // From this point, movie is shown on TV. Audio output is enabled
1078 // with this timing.
1079 if (ActivePlayer.retraceCount == 1)
1080 {
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
1090 diff = ActivePlayer.curVideoNumber - ActivePlayer.curAudioNumber;
1091
1092 // If audio is not slower than video, move video ahead
1093 if (diff <= 1)
1094 {
1095 // Acquire 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 can get decoded THP video data, push THP video data held until
1112 // that point to 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 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 reached to the end, 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 if video has reached 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 if appropriate timing for movie playback start.
1170 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 if appropriate timing to get decoded THP
1205 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, return drawn frame number. If unable to draw, return -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: Get THP movie video data.
1298
1299 Arguments: videoInfo Pointer for THPVideoInfo structure.
1300
1301 Returns: If successful, returns TRUE. If unsuccessful, returns FALSE.
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: Get THP movie audio data.
1320
1321 Arguments: audioInfo Pointer for THPAudioInfo structure.
1322
1323 Returns: If successful, returns TRUE. If unsuccessful, returns FALSE.
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: Get 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(void * buffer)1410 static void PushUsedTextureSet(void *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: THPAudioMixCallback
1480
1481 Description: AI callback function for player.
1482 Call callback functions of AX and MusyX internally when
1483 used with AX and MusyX, and mix output data of AX and
1484 MusyX with THP audio data.
1485
1486 Arguments: None
1487
1488 Returns: None
1489 *---------------------------------------------------------------------------*/
1490
THPAudioMixCallback(void)1491 void THPAudioMixCallback(void)
1492 {
1493 BOOL old;
1494
1495 if (AudioSystem == THP_MODE_ALONE)
1496 {
1497 SoundBufferIndex ^= 1;
1498
1499 AIInitDMA((u32)SoundBuffer[SoundBufferIndex], BYTES_PER_AUDIO_FRAME);
1500
1501 old = OSEnableInterrupts();
1502
1503 MixAudio(SoundBuffer[SoundBufferIndex], NULL, SAMPLES_PER_AUDIO_FRAME);
1504
1505 DCFlushRange(SoundBuffer[SoundBufferIndex], BYTES_PER_AUDIO_FRAME);
1506
1507 OSRestoreInterrupts(old);
1508 }
1509 else
1510 {
1511 if (AudioSystem == THP_MODE_WITH_AX)
1512 {
1513 if (LastAudioBuffer)
1514 {
1515 CurAudioBuffer = LastAudioBuffer;
1516 }
1517
1518 OldAIDCallback();
1519
1520 LastAudioBuffer = (s16 *)OSPhysicalToCached(AIGetDMAStartAddr());
1521 }
1522 else
1523 {
1524 OldAIDCallback();
1525
1526 CurAudioBuffer = (s16 *)OSPhysicalToCached(AIGetDMAStartAddr());
1527 }
1528
1529 SoundBufferIndex ^= 1;
1530
1531 AIInitDMA((u32)SoundBuffer[SoundBufferIndex], BYTES_PER_AUDIO_FRAME);
1532
1533 old = OSEnableInterrupts();
1534
1535 if (CurAudioBuffer)
1536 {
1537 DCInvalidateRange(CurAudioBuffer, BYTES_PER_AUDIO_FRAME);
1538 }
1539
1540 MixAudio(SoundBuffer[SoundBufferIndex], CurAudioBuffer, SAMPLES_PER_AUDIO_FRAME);
1541
1542 DCFlushRange(SoundBuffer[SoundBufferIndex], BYTES_PER_AUDIO_FRAME);
1543
1544 OSRestoreInterrupts(old);
1545 }
1546 }
1547
1548 /*---------------------------------------------------------------------------*
1549 Name: MixAudio
1550
1551 Description: Mix THP audio data in the specified buffer.
1552
1553 Arguments: destination Buffer in which mixed data is stored.
1554 source Output buffer from audio library
1555 sample Sample number of audio data to be mixed.
1556 Specified by stereo sample.
1557
1558 Returns: None
1559 *---------------------------------------------------------------------------*/
1560
MixAudio(s16 * destination,s16 * source,u32 sample)1561 static void MixAudio(s16 *destination, s16 *source, u32 sample)
1562 {
1563 u32 sampleNum, requestSample, i;
1564 s32 rmix, lmix;
1565 s16 *dst, *libsrc, *thpsrc;
1566 u16 attenuation;
1567
1568 // When mixed with audio library output
1569 if (source)
1570 {
1571 if (ActivePlayer.open && (ActivePlayer.internalState == THP_PLAYER_PLAY) && ActivePlayer.audioExist)
1572 {
1573 requestSample = sample;
1574 dst = destination;
1575 libsrc = source;
1576
1577 while (1)
1578 {
1579 if (ActivePlayer.playAudioBuffer == NULL)
1580 {
1581 if ((ActivePlayer.playAudioBuffer = (THPAudioBuffer *)PopDecodedAudioBuffer(OS_MESSAGE_NOBLOCK)) == NULL)
1582 {
1583 memcpy(dst, libsrc, requestSample << 2);
1584 break;
1585 }
1586 else
1587 {
1588 ActivePlayer.curAudioNumber++;
1589 }
1590 }
1591
1592 if (ActivePlayer.playAudioBuffer->validSample)
1593 {
1594 if (ActivePlayer.playAudioBuffer->validSample >= requestSample)
1595 {
1596 sampleNum = requestSample;
1597 }
1598 else
1599 {
1600 sampleNum = ActivePlayer.playAudioBuffer->validSample;
1601 }
1602
1603 thpsrc = ActivePlayer.playAudioBuffer->curPtr;
1604
1605 // Mixing
1606 for (i = 0 ; i < sampleNum ; i++)
1607 {
1608 if (ActivePlayer.rampCount)
1609 {
1610 ActivePlayer.rampCount--;
1611 ActivePlayer.curVolume += ActivePlayer.deltaVolume;
1612 }
1613 else
1614 {
1615 ActivePlayer.curVolume = ActivePlayer.targetVolume;
1616 }
1617
1618 attenuation = VolumeTable[(s32)ActivePlayer.curVolume];
1619
1620 // Right
1621 rmix = (attenuation * (*thpsrc)) >> 15;
1622 thpsrc++;
1623
1624 // Left
1625 lmix = (attenuation * (*thpsrc)) >> 15;
1626 thpsrc++;
1627
1628 // monaural convert
1629 if (ActivePlayer.soundMode == THP_SOUND_MODE_MONO)
1630 {
1631 // (L + R) / 2
1632 rmix += lmix;
1633 rmix += 1;
1634 rmix >>= 1;
1635 lmix = rmix;
1636 }
1637
1638 // Right
1639 rmix += *libsrc;
1640 libsrc++;
1641
1642 if (rmix < -32768)
1643 {
1644 rmix = -32768;
1645 }
1646
1647 else if (rmix > 32767)
1648 {
1649 rmix = 32767;
1650 }
1651
1652 *dst = (s16)rmix;
1653 dst++;
1654
1655 // Left
1656 lmix += *libsrc;
1657 libsrc++;
1658
1659 if (lmix < -32768)
1660 {
1661 lmix = -32768;
1662 }
1663
1664 else if (lmix > 32767)
1665 {
1666 lmix = 32767;
1667 }
1668
1669 *dst = (s16)lmix;
1670 dst++;
1671 }
1672
1673 requestSample -= sampleNum;
1674
1675 ActivePlayer.playAudioBuffer->validSample -= sampleNum;
1676 ActivePlayer.playAudioBuffer->curPtr = thpsrc;
1677
1678 if (ActivePlayer.playAudioBuffer->validSample == 0)
1679 {
1680 // Release used THP audio data
1681 PushFreeAudioBuffer(ActivePlayer.playAudioBuffer);
1682 ActivePlayer.playAudioBuffer = NULL;
1683 }
1684
1685 if (!requestSample)
1686 {
1687 break;
1688 }
1689 }
1690 }
1691 }
1692 else
1693 {
1694 memcpy(destination, source, sample << 2);
1695 }
1696 }
1697 // When not mixing with audio library output
1698 else
1699 {
1700 if (ActivePlayer.open && (ActivePlayer.internalState == THP_PLAYER_PLAY) && ActivePlayer.audioExist)
1701 {
1702 requestSample = sample;
1703 dst = destination;
1704
1705 while (1)
1706 {
1707 if (ActivePlayer.playAudioBuffer == NULL)
1708 {
1709 if ((ActivePlayer.playAudioBuffer = (THPAudioBuffer *)PopDecodedAudioBuffer(OS_MESSAGE_NOBLOCK)) == NULL)
1710 {
1711 memset(dst, 0, requestSample << 2);
1712 break;
1713 }
1714 else
1715 {
1716 ActivePlayer.curAudioNumber++;
1717 }
1718 }
1719
1720 if (ActivePlayer.playAudioBuffer->validSample)
1721 {
1722 if (ActivePlayer.playAudioBuffer->validSample >= requestSample)
1723 {
1724 sampleNum = requestSample;
1725 }
1726 else
1727 {
1728 sampleNum = ActivePlayer.playAudioBuffer->validSample;
1729 }
1730
1731 thpsrc = ActivePlayer.playAudioBuffer->curPtr;
1732
1733 // Mixing
1734 for (i = 0 ; i < sampleNum ; i++)
1735 {
1736 if (ActivePlayer.rampCount)
1737 {
1738 ActivePlayer.rampCount--;
1739 ActivePlayer.curVolume += ActivePlayer.deltaVolume;
1740 }
1741 else
1742 {
1743 ActivePlayer.curVolume = ActivePlayer.targetVolume;
1744 }
1745
1746 attenuation = VolumeTable[(s32)ActivePlayer.curVolume];
1747
1748 // Right
1749 rmix = (attenuation * (*thpsrc)) >> 15;
1750 thpsrc++;
1751
1752 // Left
1753 lmix = (attenuation * (*thpsrc)) >> 15;
1754 thpsrc++;
1755
1756 // monaural convert
1757 if (ActivePlayer.soundMode == THP_SOUND_MODE_MONO)
1758 {
1759 // (L + R) / 2
1760 rmix += lmix;
1761 rmix += 1;
1762 rmix >>= 1;
1763 lmix = rmix;
1764 }
1765
1766 // Right
1767 if (rmix < -32768)
1768 {
1769 rmix = -32768;
1770 }
1771
1772 else if (rmix > 32767)
1773 {
1774 rmix = 32767;
1775 }
1776
1777 *dst = (s16)rmix;
1778 dst++;
1779
1780 // Left
1781 if (lmix < -32768)
1782 {
1783 lmix = -32768;
1784 }
1785
1786 else if (lmix > 32767)
1787 {
1788 lmix = 32767;
1789 }
1790
1791 *dst = (s16)lmix;
1792 dst++;
1793 }
1794
1795 requestSample -= sampleNum;
1796
1797 ActivePlayer.playAudioBuffer->validSample -= sampleNum;
1798 ActivePlayer.playAudioBuffer->curPtr = thpsrc;
1799
1800 if (ActivePlayer.playAudioBuffer->validSample == 0)
1801 {
1802 // Release used THP audio data
1803 PushFreeAudioBuffer(ActivePlayer.playAudioBuffer);
1804 ActivePlayer.playAudioBuffer = NULL;
1805 }
1806
1807 if (!requestSample)
1808 {
1809 break;
1810 }
1811 }
1812 }
1813 }
1814 else
1815 {
1816 memset(destination, 0, sample << 2);
1817 }
1818 }
1819
1820 return;
1821 }
1822
1823 /*---------------------------------------------------------------------------*
1824 Name: THPPlayerSetVolume
1825
1826 Description: Set volume of player. Volume will be changed in the
1827 specified time.
1828
1829 Arguments: vol volume to be set (0 - 127)
1830 time Specify the time required to go from current volume to
1831 specified volume in units of milliseconds (0 - 60000)
1832
1833 Returns: Returns TRUE if succeeds, and FALSE if fails.
1834 *---------------------------------------------------------------------------*/
1835
THPPlayerSetVolume(s32 vol,s32 time)1836 BOOL THPPlayerSetVolume(s32 vol, s32 time)
1837 {
1838 BOOL old;
1839 s32 samplePerMs;
1840
1841 if (ActivePlayer.open && ActivePlayer.audioExist)
1842 {
1843 if (AIGetDSPSampleRate() == AI_SAMPLERATE_32KHZ)
1844 {
1845 samplePerMs = 32;
1846 }
1847 else
1848 {
1849 samplePerMs = 48;
1850 }
1851
1852 if (vol > 127)
1853 {
1854 vol = 127;
1855 }
1856
1857 if (vol < 0)
1858 {
1859 vol = 0;
1860 }
1861
1862 if (time > 60000)
1863 {
1864 time = 60000;
1865 }
1866
1867 if (time < 0)
1868 {
1869 time = 0;
1870 }
1871
1872 old = OSDisableInterrupts();
1873
1874 ActivePlayer.targetVolume = (f32)vol;
1875
1876 if (time)
1877 {
1878 ActivePlayer.rampCount = samplePerMs * time;
1879 ActivePlayer.deltaVolume = (ActivePlayer.targetVolume - ActivePlayer.curVolume)
1880 / (f32)ActivePlayer.rampCount;
1881 }
1882 else
1883 {
1884 ActivePlayer.rampCount = 0;
1885 ActivePlayer.curVolume = ActivePlayer.targetVolume;
1886 }
1887
1888 OSRestoreInterrupts(old);
1889
1890 return TRUE;
1891 }
1892
1893 return FALSE;
1894 }
1895
1896 /*---------------------------------------------------------------------------*
1897 Name: THPPlayerGetVolume
1898
1899 Description: Acquire current volume of player
1900
1901 Arguments: None
1902
1903 Returns: Returns current playback volume if successful, and -1 if unsuccessful.
1904 *---------------------------------------------------------------------------*/
1905
THPPlayerGetVolume(void)1906 s32 THPPlayerGetVolume(void)
1907 {
1908 if (ActivePlayer.open)
1909 {
1910 return (s32)ActivePlayer.curVolume;
1911 }
1912
1913 return -1;
1914 }
1915
1916 /*---------------------------------------------------------------------------*
1917 Name: THPPlayerSetSoundMode
1918
1919 Description: Set sound mode
1920
1921 Arguments: mode sound mode (MONO/STEREO/SURROUND/DPL2)
1922
1923 Returns: none.
1924 *---------------------------------------------------------------------------*/
1925
THPPlayerSetSoundMode(s32 mode)1926 void THPPlayerSetSoundMode(s32 mode)
1927 {
1928 ASSERT(mode >= THP_SOUND_MODE_MONO && mode <= THP_SOUND_MODE_DPL2);
1929
1930 if (mode >= THP_SOUND_MODE_MONO && mode <= THP_SOUND_MODE_DPL2)
1931 {
1932 ActivePlayer.soundMode = mode;
1933 }
1934 }
1935
1936 /*---------------------------------------------------------------------------*
1937 Name: THPPlayerGetSoundMode
1938
1939 Description: Get current sound mode
1940
1941 Arguments: none.
1942
1943 Returns: sound mode (MONO/STEREO/SURROUND/DPL2)
1944 *---------------------------------------------------------------------------*/
1945
THPPlayerGetSoundMode(void)1946 s32 THPPlayerGetSoundMode(void)
1947 {
1948 return ActivePlayer.soundMode;
1949 }
1950