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 02/03/2006 11:44:07 aka
15 Changed audio frame from 5msec to 3msec.
16
17 Revision 1.1 02/03/2006 10:01:41 aka
18 Imported from Dolphin tree.
19
20
21 5 03/11/25 11:24 Dante
22 Japanese to English translation of comments and text strings
23
24 4 03/09/16 15:48:00 Suzuki
25 changed the process which skips to decode when decoding is delay.
26
27 3 02/05/31 9:06a Suzuki
28 supported multi audio track
29
30 2 02/02/28 6:36p Akagi
31 enabled to use with MusyX/AX by Suzuki-san (IRD).
32
33 1 02/01/16 10:53gi
34 Initial revision made by Suzuki-san (IRD).
35
36 $NoKeywords: $
37
38 *---------------------------------------------------------------------------*/
39
40 #include <string.h>
41 #include <revolution.h>
42 #include "THPPlayer.h"
43 #include "THPVideoDecode.h"
44 #include "THPAudioDecode.h"
45 #include "THPRead.h"
46 #include "THPDraw.h"
47
48 #define SAMPLES_PER_AUDIO_FRAME (96) // 3msec (32khz)
49 #define BYTES_PER_AUDIO_FRAME (SAMPLES_PER_AUDIO_FRAME * 4)
50
51 /*---------------------------------------------------------------------------*
52 Global Function
53 *---------------------------------------------------------------------------*/
54
55 void PrepareReady(BOOL flag);
56
57 /*---------------------------------------------------------------------------*
58 Static Function
59 *---------------------------------------------------------------------------*/
60
61 static void PlayControl(u32 retraceCount);
62 static BOOL ProperTimingForStart(void);
63 static BOOL ProperTimingForGettingNextFrame(void);
64 static void PushUsedTextureSet(void *buffer);
65 static void *PopUsedTextureSet(void);
66 static void THPAudioMixCallback(void);
67 static void MixAudio(s16 *destination, s16 *source, u32 sample);
68
69 /*---------------------------------------------------------------------------*
70 Global Variable
71 *---------------------------------------------------------------------------*/
72
73 THPPlayer ActivePlayer;
74
75 /*---------------------------------------------------------------------------*
76 Static Variable
77 *---------------------------------------------------------------------------*/
78
79 // 32768 * ((vol * vol) / (127 * 127))
80 static u16 VolumeTable[] =
81 {
82 0, 2, 8, 18, 32, 50, 73, 99,
83 130, 164, 203, 245, 292, 343, 398, 457,
84 520, 587, 658, 733, 812, 895, 983, 1074,
85 1170, 1269, 1373, 1481, 1592, 1708, 1828, 1952,
86 2080, 2212, 2348, 2488, 2632, 2781, 2933, 3090,
87 3250, 3415, 3583, 3756, 3933, 4114, 4298, 4487,
88 4680, 4877, 5079, 5284, 5493, 5706, 5924, 6145,
89 6371, 6600, 6834, 7072, 7313, 7559, 7809, 8063,
90 8321, 8583, 8849, 9119, 9394, 9672, 9954, 10241,
91 10531, 10826, 11125, 11427, 11734, 12045, 12360, 12679,
92 13002, 13329, 13660, 13995, 14335, 14678, 15025, 15377,
93 15732, 16092, 16456, 16823, 17195, 17571, 17951, 18335,
94 18723, 19115, 19511, 19911, 20316, 20724, 21136, 21553,
95 21974, 22398, 22827, 23260, 23696, 24137, 24582, 25031,
96 25484, 25941, 26402, 26868, 27337, 27810, 28288, 28769,
97 29255, 29744, 30238, 30736, 31238, 31744, 32254, 32768
98 };
99
100 static BOOL Initialized = 0;
101 static s32 WorkBuffer[16] ATTRIBUTE_ALIGN(32);
102 static OSMessageQueue PrepareReadyQueue;
103 static OSMessageQueue UsedTextureSetQueue;
104 static OSMessage PrepareReadyMessage;
105 static OSMessage UsedTextureSetMessage[DECODE_VIDEO_BUFFER_NUM];
106 static VIRetraceCallback OldVIPostCallback = NULL;
107 static s16 SoundBuffer[2][SAMPLES_PER_AUDIO_FRAME * 2] ATTRIBUTE_ALIGN(32);
108 static s32 SoundBufferIndex;
109 static AIDCallback OldAIDCallback = NULL;
110 static s16 *LastAudioBuffer;
111 static s16 *CurAudioBuffer;
112 static s32 AudioSystem = 0;
113
114 /*---------------------------------------------------------------------------*
115 Name: THPPlayerInit
116
117 Description: Enables locked-cache, and initializes player and THP library.
118 Register AI FIFO DMA call back for player.
119
120 Arguments: audioSystem Specifies the audio library to be used simultaneously.
121 If no audio library is to be used simultaneously,
122 specifies THP_MODE_ALONE.
123
124 Returns: Returns TRUE if succeeds, and FALSE if fails.
125 *---------------------------------------------------------------------------*/
126
THPPlayerInit(s32 audioSystem)127 BOOL THPPlayerInit(s32 audioSystem)
128 {
129 BOOL old;
130
131 ASSERTMSG(audioSystem >= 0 && audioSystem <= THP_MODE_WITH_MUSYX, "audioSystem flag is invalid\n");
132
133 memset(&ActivePlayer, 0, sizeof(THPPlayer));
134
135 LCEnable();
136
137 OSInitMessageQueue(&UsedTextureSetQueue,
138 UsedTextureSetMessage,
139 DECODE_VIDEO_BUFFER_NUM);
140
141 if (THPInit() == FALSE)
142 {
143 return FALSE;
144 }
145
146 old = OSDisableInterrupts();
147
148 AudioSystem = audioSystem;
149 SoundBufferIndex = 0;
150 LastAudioBuffer = NULL;
151 CurAudioBuffer = NULL;
152
153 OldAIDCallback = AIRegisterDMACallback(THPAudioMixCallback);
154
155 if (OldAIDCallback == NULL && AudioSystem != THP_MODE_ALONE)
156 {
157 AIRegisterDMACallback(NULL);
158
159 OSRestoreInterrupts(old);
160
161 #ifdef _DEBUG
162 OSReport("Pleae call AXInit or sndInit before you call THPPlayerInit\n");
163 #endif
164 return FALSE;
165 }
166
167 OSRestoreInterrupts(old);
168
169 if (AudioSystem == THP_MODE_ALONE)
170 {
171 memset(SoundBuffer, 0, BYTES_PER_AUDIO_FRAME << 1);
172
173 DCFlushRange(SoundBuffer, BYTES_PER_AUDIO_FRAME << 1);
174
175 AIInitDMA((u32)SoundBuffer[SoundBufferIndex], BYTES_PER_AUDIO_FRAME);
176
177 AIStartDMA();
178 }
179
180 Initialized = TRUE;
181
182 return TRUE;
183 }
184
185 /*---------------------------------------------------------------------------*
186 Name: THPPlayerQuit
187
188 Description: Disables locked cache.
189 Restores AI FIFO DMA callback to the status before THPPlayerInit is called.
190
191 Arguments: None
192
193 Returns: None
194 *---------------------------------------------------------------------------*/
195
THPPlayerQuit(void)196 void THPPlayerQuit(void)
197 {
198 BOOL old;
199
200 LCDisable();
201
202 old = OSDisableInterrupts();
203
204 if (OldAIDCallback)
205 {
206 AIRegisterDMACallback(OldAIDCallback);
207 }
208
209 OSRestoreInterrupts(old);
210
211 Initialized = FALSE;
212
213 return;
214 }
215
216 /*---------------------------------------------------------------------------*
217 Name: THPPlayerOpen
218
219 Description: Opens THP movie file. Loads required data.
220
221 Arguments: fileName Filename of THP movie.
222 onMemory Flag to do OnMemory playback or not.
223
224 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
225 *---------------------------------------------------------------------------*/
226
THPPlayerOpen(char * fileName,BOOL onMemory)227 BOOL THPPlayerOpen(char *fileName, BOOL onMemory)
228 {
229 s32 offset, i;
230
231 if (!Initialized)
232 {
233 #ifdef _DEBUG
234 OSReport("You must call THPPlayerInit before you call this function\n");
235 #endif
236 return FALSE;
237 }
238
239 if (ActivePlayer.open)
240 {
241 #ifdef _DEBUG
242 OSReport("Can't open %s. Because thp file have already opened.\n");
243 #endif
244 return FALSE;
245 }
246
247 // Clears current video data and audio data
248 memset(&ActivePlayer.videoInfo, 0, sizeof(THPVideoInfo));
249 memset(&ActivePlayer.audioInfo, 0, sizeof(THPAudioInfo));
250
251 if (DVDOpen(fileName, &ActivePlayer.fileInfo) == FALSE)
252 {
253 #ifdef _DEBUG
254 OSReport("Can't open %s.\n", fileName);
255 #endif
256 return FALSE;
257 }
258
259 // Get THP header
260 if (DVDRead(&ActivePlayer.fileInfo, WorkBuffer, 64, 0) < 0)
261 {
262 #ifdef _DEBUG
263 OSReport("Fail to read the header from THP file.\n");
264 #endif
265 DVDClose(&ActivePlayer.fileInfo);
266 return FALSE;
267 }
268
269 memcpy(&ActivePlayer.header, WorkBuffer, sizeof(THPHeader));
270
271 // Check if THP movie file
272 if (strcmp(ActivePlayer.header.magic, "THP") != 0)
273 {
274 #ifdef _DEBUG
275 OSReport("This file is not THP file.\n");
276 #endif
277 DVDClose(&ActivePlayer.fileInfo);
278 return FALSE;
279 }
280
281 // Check version
282 if (ActivePlayer.header.version != THP_VERSION)
283 {
284 #ifdef _DEBUG
285 OSReport("invalid version.\n");
286 #endif
287 DVDClose(&ActivePlayer.fileInfo);
288 return FALSE;
289 }
290
291 offset = (s32)ActivePlayer.header.compInfoDataOffsets;
292
293 // Get component data in frame
294 if (DVDRead(&ActivePlayer.fileInfo, WorkBuffer, 32, offset) < 0)
295 {
296 #ifdef _DEBUG
297 OSReport("Fail to read the frame component infomation from THP file.\n");
298 #endif
299 DVDClose(&ActivePlayer.fileInfo);
300 return FALSE;
301 }
302
303 memcpy(&ActivePlayer.compInfo, WorkBuffer, sizeof(THPFrameCompInfo));
304
305 offset += sizeof(THPFrameCompInfo);
306
307 ActivePlayer.audioExist = 0;
308
309 // Check components in frame
310 for(i = 0 ; i < ActivePlayer.compInfo.numComponents ; i++)
311 {
312 switch(ActivePlayer.compInfo.frameComp[i])
313 {
314 case THP_VIDEO_COMP: // Get video data of components
315 if (DVDRead(&ActivePlayer.fileInfo, WorkBuffer, 32, offset) < 0)
316 {
317 #ifdef _DEBUG
318 OSReport("Fail to read the video infomation from THP file.\n");
319 #endif
320 DVDClose(&ActivePlayer.fileInfo);
321 return FALSE;
322 }
323 memcpy(&ActivePlayer.videoInfo, WorkBuffer, sizeof(THPVideoInfo));
324 offset += sizeof(THPVideoInfo);
325 break;
326 case THP_AUDIO_COMP: // Get audio data of components
327 if (DVDRead(&ActivePlayer.fileInfo, WorkBuffer, 32, offset) < 0)
328 {
329 #ifdef _DEBUG
330 OSReport("Fail to read the video infomation from THP file.\n");
331 #endif
332 DVDClose(&ActivePlayer.fileInfo);
333 return FALSE;
334 }
335 memcpy(&ActivePlayer.audioInfo, WorkBuffer, sizeof(THPAudioInfo));
336 ActivePlayer.audioExist = 1;
337 offset += sizeof(THPAudioInfo);
338
339 break;
340 default:
341 #ifdef _DEBUG
342 OSReport("Unknow frame components.\n");
343 #endif
344 return FALSE;
345 }
346 }
347
348 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_STOP;
349 ActivePlayer.playFlag = 0;
350 ActivePlayer.onMemory = onMemory;
351 ActivePlayer.open = TRUE;
352 ActivePlayer.curVolume = 127.0f;
353 ActivePlayer.targetVolume = ActivePlayer.curVolume;
354 ActivePlayer.rampCount = 0;
355
356 return TRUE;
357 }
358
359 /*---------------------------------------------------------------------------*
360 Name: THPPlayerClose
361
362 Description: Close THP movie file.
363
364 Arguments: None
365
366 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
367 *---------------------------------------------------------------------------*/
368
THPPlayerClose(void)369 BOOL THPPlayerClose(void)
370 {
371 if (ActivePlayer.open)
372 {
373 if (ActivePlayer.state == THP_PLAYER_STOP)
374 {
375 ActivePlayer.open = FALSE;
376 DVDClose(&ActivePlayer.fileInfo);
377
378 return TRUE;
379 }
380 }
381
382 return FALSE;
383 }
384
385 /*---------------------------------------------------------------------------*
386 Name: THPPlayerCalcNeedMemory
387
388 Description: Calculates needed memory for THP movie playback
389
390 Arguments: None
391
392 Returns: Returns needed memory size if successful, and 0 if unsuccessful.
393 *---------------------------------------------------------------------------*/
394
THPPlayerCalcNeedMemory(void)395 u32 THPPlayerCalcNeedMemory(void)
396 {
397 u32 size;
398
399 if (ActivePlayer.open)
400 {
401 // Buffer size for read
402 if (ActivePlayer.onMemory)
403 {
404 size = OSRoundUp32B(ActivePlayer.header.movieDataSize);
405 }
406 else
407 {
408 size = OSRoundUp32B(ActivePlayer.header.bufSize) * READ_BUFFER_NUM;
409 }
410
411 // Size of texture buffer
412 size += OSRoundUp32B(ActivePlayer.videoInfo.xSize * ActivePlayer.videoInfo.ySize)
413 * DECODE_VIDEO_BUFFER_NUM; //Y
414 size += OSRoundUp32B(ActivePlayer.videoInfo.xSize * ActivePlayer.videoInfo.ySize / 4)
415 * DECODE_VIDEO_BUFFER_NUM; //U
416 size += OSRoundUp32B(ActivePlayer.videoInfo.xSize * ActivePlayer.videoInfo.ySize / 4)
417 * DECODE_VIDEO_BUFFER_NUM; //V
418
419 // Size of audio buffer
420 if (ActivePlayer.audioExist)
421 {
422 size += (OSRoundUp32B(ActivePlayer.header.audioMaxSamples * 4) * DECODE_AUDIO_BUFFER_NUM);
423 }
424
425 size += THP_WORK_SIZE;
426
427 return size;
428 }
429
430 return 0;
431 }
432
433 /*---------------------------------------------------------------------------*
434 Name: THPPlayerSetBuffer
435
436 Description: Allocate required memory for movie playback to THPPlayer structure.
437
438 Arguments: buffer Pointer to memory area for THP movie set aside externally.
439
440 Returns: Returns TRUE if successful, and FALSE if successful.
441 *---------------------------------------------------------------------------*/
442
THPPlayerSetBuffer(u8 * buffer)443 BOOL THPPlayerSetBuffer(u8 *buffer)
444 {
445 u32 i;
446 u8 *ptr;
447 u32 ysize, uvsize;
448
449 ASSERTMSG(buffer != NULL, "buffer is NULL");
450
451 if (ActivePlayer.open && (ActivePlayer.state == THP_PLAYER_STOP))
452 {
453 ptr = buffer;
454
455 // Set buffer for read
456 if (ActivePlayer.onMemory)
457 {
458 ActivePlayer.movieData = ptr;
459 ptr += ActivePlayer.header.movieDataSize;
460 }
461 else
462 {
463 for (i = 0 ; i < READ_BUFFER_NUM ; i++)
464 {
465 ActivePlayer.readBuffer[i].ptr = ptr;
466 ptr += OSRoundUp32B(ActivePlayer.header.bufSize);
467 }
468 }
469
470 ysize = OSRoundUp32B(ActivePlayer.videoInfo.xSize * ActivePlayer.videoInfo.ySize);
471 uvsize = OSRoundUp32B(ActivePlayer.videoInfo.xSize * ActivePlayer.videoInfo.ySize / 4);
472
473 // Set texture buffer
474 for (i = 0 ; i < DECODE_VIDEO_BUFFER_NUM ; i++)
475 {
476 ActivePlayer.textureSet[i].ytexture = ptr;
477 DCInvalidateRange(ptr, ysize);
478 ptr += ysize;
479 ActivePlayer.textureSet[i].utexture = ptr;
480 DCInvalidateRange(ptr, uvsize);
481 ptr += uvsize;
482 ActivePlayer.textureSet[i].vtexture = ptr;
483 DCInvalidateRange(ptr, uvsize);
484 ptr += uvsize;
485 }
486
487 // Set audio buffer
488 if (ActivePlayer.audioExist)
489 {
490 for (i = 0 ; i < DECODE_AUDIO_BUFFER_NUM ; i++)
491 {
492 ActivePlayer.audioBuffer[i].buffer = (s16 *)ptr;
493 ActivePlayer.audioBuffer[i].curPtr = (s16 *)ptr;
494 ActivePlayer.audioBuffer[i].validSample = 0;
495 ptr += OSRoundUp32B(ActivePlayer.header.audioMaxSamples * 4);
496 }
497 }
498
499 ActivePlayer.thpWork = (void *)ptr;
500
501 return TRUE;
502 }
503
504 return FALSE;
505 }
506
507 /*---------------------------------------------------------------------------*
508 Name: InitAllMessageQueue
509
510 Description: Initializes queue used with video decode thread, audio
511 thread, and read thread.
512
513 Arguments: None
514
515 Returns: None
516 *---------------------------------------------------------------------------*/
517
InitAllMessageQueue(void)518 static void InitAllMessageQueue(void)
519 {
520 s32 i;
521 THPReadBuffer *readBuffer;
522 THPTextureSet *textureSet;
523 THPAudioBuffer *audioBuffer;
524
525 // Push all read buffers to free read buffer queue
526 if (!ActivePlayer.onMemory)
527 {
528 for (i = 0 ; i < READ_BUFFER_NUM ; i++)
529 {
530 readBuffer = &ActivePlayer.readBuffer[i];
531 PushFreeReadBuffer(readBuffer);
532 }
533 }
534
535 // Push all texture buffers for storing decoded THP video data to
536 // free texture buffer queue.
537 for (i = 0 ; i < DECODE_VIDEO_BUFFER_NUM ; i++)
538 {
539 textureSet = &ActivePlayer.textureSet[i];
540 PushFreeTextureSet(textureSet);
541 }
542
543 // Push all audio buffers for storing decoded THP audio data to
544 // free audio buffer queue.
545 if (ActivePlayer.audioExist)
546 {
547 for (i = 0 ; i < DECODE_AUDIO_BUFFER_NUM ; i++)
548 {
549 audioBuffer = &ActivePlayer.audioBuffer[i];
550 PushFreeAudioBuffer(audioBuffer);
551 }
552 }
553
554 OSInitMessageQueue(&PrepareReadyQueue,
555 &PrepareReadyMessage,
556 1);
557 }
558
559 /*---------------------------------------------------------------------------*
560 Name: WaitUntilPrepare
561
562 Description: Waits until playback preparations completed.
563
564 Arguments: None
565
566 Returns: If playback preparations complete, returns TRUE. If preparations fail, returns FALSE.
567 *---------------------------------------------------------------------------*/
568
WaitUntilPrepare(void)569 static BOOL WaitUntilPrepare(void)
570 {
571 OSMessage msg;
572
573 OSReceiveMessage(&PrepareReadyQueue, &msg, OS_MESSAGE_BLOCK);
574
575 if ((BOOL)msg)
576 {
577 return TRUE;
578 }
579 else
580 {
581 return FALSE;
582 }
583 }
584
585 /*---------------------------------------------------------------------------*
586 Name: PrepareReady
587
588 Description: Sends message about whether or not playback preparations are completed.
589
590 Arguments: flag If playback preparations complete, returns TRUE. If preparations fail, returns FALSE.
591
592 Returns: None
593 *---------------------------------------------------------------------------*/
594
PrepareReady(BOOL flag)595 void PrepareReady(BOOL flag)
596 {
597 OSSendMessage(&PrepareReadyQueue, (OSMessage)flag, OS_MESSAGE_BLOCK);
598
599 return;
600 }
601
602 /*---------------------------------------------------------------------------*
603 Name: THPPlayerPrepare
604
605 Description: Carries out preparations for movie playback. Does not
606 return until THP movie playback preparations completed.
607 If the THP movie has no audio, the audioTrack argument is ignored.
608
609 Arguments: frameNum Specifies the frame number of the movie playback.
610 If the THP movie file does not contain THPFrameOffsetData,
611 anything other than 0 is an error.
612 playFlag Specifies one-shot or loop playback flag.
613 audioTrack Specifies audio track number to be played back.
614
615 Returns: If playback is ready, returns TRUE. If playback preparation fails, returns FALSE.
616 *---------------------------------------------------------------------------*/
617
THPPlayerPrepare(s32 frameNum,s32 playFlag,s32 audioTrack)618 BOOL THPPlayerPrepare(s32 frameNum, s32 playFlag, s32 audioTrack)
619 {
620 s32 offset;
621 u8 *ptr;
622
623 if (ActivePlayer.open && (ActivePlayer.state == THP_PLAYER_STOP))
624 {
625 // Check if specified starting frame number is appropriate
626 if (frameNum > 0)
627 {
628 // Does THP movie file have offset data?
629 if (!ActivePlayer.header.offsetDataOffsets)
630 {
631 #ifdef _DEBUG
632 OSReport("This THP file doesn't have the offset data\n");
633 #endif
634 return FALSE;
635 }
636
637 // Does starting frame number exceed total frames?
638 if (ActivePlayer.header.numFrames > frameNum)
639 {
640 offset = (s32)(ActivePlayer.header.offsetDataOffsets + (frameNum - 1) * 4);
641
642 if (DVDRead(&ActivePlayer.fileInfo,
643 WorkBuffer,
644 32,
645 offset) < 0)
646 {
647 #ifdef _DEBUG
648 OSReport("Fail to read the offset data from THP file.\n");
649 #endif
650 return FALSE;
651 }
652
653 // Set starting file offset, frame size, and frame number
654 ActivePlayer.initOffset = (s32)(ActivePlayer.header.movieDataOffsets
655 + WorkBuffer[0]);
656 ActivePlayer.initReadFrame = frameNum;
657 ActivePlayer.initReadSize = (s32)(WorkBuffer[1] - WorkBuffer[0]);
658 }
659 else
660 {
661 #ifdef _DEBUG
662 OSReport("Specified frame number is over total frame number\n");
663 #endif
664 return FALSE;
665 }
666 }
667 // If 0, from beginning
668 else
669 {
670 ActivePlayer.initOffset = (s32)ActivePlayer.header.movieDataOffsets;
671 ActivePlayer.initReadSize = (s32)ActivePlayer.header.firstFrameSize;
672 ActivePlayer.initReadFrame = frameNum;
673 }
674
675 if (ActivePlayer.audioExist)
676 {
677 if (audioTrack < 0 || audioTrack >= ActivePlayer.audioInfo.sndNumTracks)
678 {
679 #ifdef _DEBUG
680 OSReport("Specified audio track number is invalid\n");
681 #endif
682 return FALSE;
683 }
684 else
685 {
686 ActivePlayer.curAudioTrack = audioTrack;
687 }
688 }
689
690 playFlag &= THP_PLAY_LOOP;
691 ActivePlayer.playFlag = (u8)playFlag;
692 ActivePlayer.videoDecodeCount = 0;
693
694 // If On Memory playback, load all THP movie data to memory
695 if (ActivePlayer.onMemory)
696 {
697 if (DVDRead(&ActivePlayer.fileInfo,
698 ActivePlayer.movieData,
699 (s32)ActivePlayer.header.movieDataSize,
700 (s32)ActivePlayer.header.movieDataOffsets) < 0)
701 {
702 #ifdef _DEBUG
703 OSReport("Fail to read all movie data from THP file\n");
704 #endif
705 return FALSE;
706 }
707
708 ptr = ActivePlayer.movieData + ActivePlayer.initOffset - ActivePlayer.header.movieDataOffsets;
709
710 // Create video decode thread
711 CreateVideoDecodeThread(VIDEO_THREAD_PRIORITY, ptr);
712
713 // Create audio decode thread if required
714 if (ActivePlayer.audioExist)
715 {
716 CreateAudioDecodeThread(AUDIO_THREAD_PRIORITY, ptr);
717 }
718 }
719 // Not On Memory playback
720 else
721 {
722 // Create video decode thread
723 CreateVideoDecodeThread(VIDEO_THREAD_PRIORITY, NULL);
724
725 // Create audio decode thread if required
726 if (ActivePlayer.audioExist)
727 {
728 CreateAudioDecodeThread(AUDIO_THREAD_PRIORITY, NULL);
729 }
730
731 // Create read thread
732 CreateReadThread(READ_THREAD_PRIORITY);
733 }
734
735 ActivePlayer.curVideoNumber = -1;
736 ActivePlayer.curAudioNumber = 0;
737
738 // Initialize queues used with various threads
739 InitAllMessageQueue();
740
741 VideoDecodeThreadStart();
742
743 if (ActivePlayer.audioExist)
744 {
745 AudioDecodeThreadStart();
746 }
747
748 if (!ActivePlayer.onMemory)
749 {
750 ReadThreadStart();
751 }
752
753 // Wait until thread preparation completed.
754 if (WaitUntilPrepare() == FALSE)
755 {
756 return FALSE;
757 }
758
759 // If preparations complete, state goes to preparations complete
760 ActivePlayer.state = THP_PLAYER_PREPARE;
761 ActivePlayer.internalState = THP_PLAYER_STOP;
762
763 // Initialize variables
764 ActivePlayer.dispTextureSet = NULL;
765 ActivePlayer.playAudioBuffer = NULL;
766
767 // Register VI post callback that controls playback
768 OldVIPostCallback = VISetPostRetraceCallback(PlayControl);
769
770 return TRUE;
771 }
772
773 return FALSE;
774 }
775
776 /*---------------------------------------------------------------------------*
777 Name: THPPlayerPlay
778
779 Description: Start of movie playback.
780
781 Arguments: None
782
783 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
784 *---------------------------------------------------------------------------*/
785
THPPlayerPlay(void)786 BOOL THPPlayerPlay(void)
787 {
788 if (ActivePlayer.open && ((ActivePlayer.state == THP_PLAYER_PREPARE)
789 || (ActivePlayer.state == THP_PLAYER_PAUSE)))
790 {
791 ActivePlayer.state = THP_PLAYER_PLAY;
792 ActivePlayer.prevCount = 0;
793 ActivePlayer.curCount = 0;
794 ActivePlayer.retraceCount = -1;
795
796 return TRUE;
797 }
798
799 return FALSE;
800 }
801
802 /*---------------------------------------------------------------------------*
803 Name: THPPlayerStop
804
805 Description: Stop for movie playback.
806 Returns VI post callback to original state and stops threads.
807
808 Arguments: None
809
810 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
811 *---------------------------------------------------------------------------*/
812
THPPlayerStop(void)813 void THPPlayerStop(void)
814 {
815 void *texture;
816
817 if (ActivePlayer.open && !(ActivePlayer.state == THP_PLAYER_STOP))
818 {
819 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_STOP;
820
821 // Return VI post callback
822 VISetPostRetraceCallback(OldVIPostCallback);
823
824 // Cancel if stopping threads and loading data
825 if (!ActivePlayer.onMemory)
826 {
827 DVDCancel(&ActivePlayer.fileInfo.cb);
828 ReadThreadCancel();
829 }
830
831 VideoDecodeThreadCancel();
832
833 if (ActivePlayer.audioExist)
834 {
835 AudioDecodeThreadCancel();
836 }
837
838 // Empty played back texture queues.
839 while (1)
840 {
841 texture = PopUsedTextureSet();
842 if (texture == NULL)
843 {
844 break;
845 }
846 }
847
848 ActivePlayer.curVolume = ActivePlayer.targetVolume;
849 ActivePlayer.rampCount = 0;
850
851 // Clear errors
852 ActivePlayer.dvdError = FALSE;
853 ActivePlayer.videoError = FALSE;
854 }
855
856 return;
857 }
858
859 /*---------------------------------------------------------------------------*
860 Name: THPPlayerPause
861
862 Description: Pause movie playback
863
864 Arguments: None
865
866 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
867 *---------------------------------------------------------------------------*/
868
THPPlayerPause(void)869 BOOL THPPlayerPause(void)
870 {
871 if (ActivePlayer.open && (ActivePlayer.state == THP_PLAYER_PLAY))
872 {
873 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_PAUSE;
874
875 return TRUE;
876 }
877
878 return FALSE;
879 }
880
881 /*---------------------------------------------------------------------------*
882 Name: THPPlayerSkip
883
884 Description: Skip movie ahead one frame.
885
886 Arguments: None
887
888 Returns: Returns TRUE if successful, and FALSE if unsuccessful.
889 *---------------------------------------------------------------------------*/
890
THPPlayerSkip(void)891 BOOL THPPlayerSkip(void)
892 {
893 s32 frameNumber, audioGet, videoGet;
894
895 if (ActivePlayer.open && (ActivePlayer.state == THP_PLAYER_PAUSE))
896 {
897 // Block in function until get decoded THP video data so
898 // release played back THP video data in advance.
899 THPPlayerDrawDone();
900
901 // If have audio
902 if (ActivePlayer.audioExist)
903 {
904 frameNumber = ActivePlayer.curAudioNumber + ActivePlayer.initReadFrame;
905
906 // Check if one shot and also if audio has reached end.
907 if (!(ActivePlayer.playFlag & THP_PLAY_LOOP) && (frameNumber == ActivePlayer.header.numFrames))
908 {
909 if (ActivePlayer.playAudioBuffer)
910 {
911 PushFreeAudioBuffer(ActivePlayer.playAudioBuffer);
912 ActivePlayer.playAudioBuffer = NULL;
913 }
914 audioGet = 0;
915 }
916 else
917 {
918 // Release current audio buffer
919 if (ActivePlayer.playAudioBuffer)
920 {
921 PushFreeAudioBuffer(ActivePlayer.playAudioBuffer);
922 }
923
924 // Wait until get next audio buffer
925 ActivePlayer.playAudioBuffer = (THPAudioBuffer *)PopDecodedAudioBuffer(OS_MESSAGE_BLOCK);
926 ActivePlayer.curAudioNumber++;
927
928 audioGet = 1;
929 }
930 }
931
932 if (ActivePlayer.dispTextureSet)
933 {
934 frameNumber = ActivePlayer.dispTextureSet->frameNumber + ActivePlayer.initReadFrame;
935 }
936 else
937 {
938 frameNumber = ActivePlayer.initReadFrame - 1;
939 }
940
941 // Check if one shot and also if video has reached end.
942 if (!(ActivePlayer.playFlag & THP_PLAY_LOOP) && (frameNumber == ActivePlayer.header.numFrames - 1))
943 {
944 videoGet = 0;
945 }
946 else
947 {
948 // Release current texture buffer
949 if (ActivePlayer.dispTextureSet)
950 {
951 PushFreeTextureSet(ActivePlayer.dispTextureSet);
952 }
953
954 // Wait until get next texture buffer
955 ActivePlayer.dispTextureSet = (THPTextureSet *)PopDecodedTextureSet(OS_MESSAGE_BLOCK);
956
957 if (ActivePlayer.audioExist)
958 {
959 ActivePlayer.curVideoNumber++;
960 }
961
962 videoGet = 1;
963 }
964
965 if (audioGet || videoGet)
966 {
967 return TRUE;
968 }
969 else
970 {
971 return FALSE;
972 }
973 }
974
975 return FALSE;
976 }
977
978 /*---------------------------------------------------------------------------*
979 Name: PlayControl
980
981 Description: Controls movie playback. Gets decoded THP video data at
982 appropriate timing.
983
984 Arguments: retraceCount Current retrace count
985
986 Returns: None
987 *---------------------------------------------------------------------------*/
988
PlayControl(u32 retraceCount)989 static void PlayControl(u32 retraceCount)
990 {
991 s32 diff, frameNumber;
992 THPTextureSet *textureSet;
993
994 if (OldVIPostCallback)
995 {
996 OldVIPostCallback(retraceCount);
997 }
998
999 textureSet = (THPTextureSet *)0xFFFFFFFF;
1000
1001 if (ActivePlayer.open && (ActivePlayer.state == THP_PLAYER_PLAY))
1002 {
1003 // If an error has occurred, change state to error.
1004 if (ActivePlayer.dvdError || ActivePlayer.videoError)
1005 {
1006 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_ERROR;
1007
1008 return;
1009 }
1010
1011 ActivePlayer.retraceCount++;
1012
1013 // When start THP movie playback and when end pause
1014 if (ActivePlayer.retraceCount == 0)
1015 {
1016 // Appropriate timing for start of playback?
1017 if (ProperTimingForStart())
1018 {
1019 // If THP movie has audio
1020 if (ActivePlayer.audioExist)
1021 {
1022 // Calculate difference between current audio playback frames and current video playback frames
1023 diff = ActivePlayer.curVideoNumber - ActivePlayer.curAudioNumber;
1024
1025 // If audio is not slower than video, move video ahead
1026 if (diff <= 1)
1027 {
1028 // Acquire decoded THP video data
1029 textureSet = (THPTextureSet *)PopDecodedTextureSet(OS_MESSAGE_NOBLOCK);
1030
1031 // Increment THP video data number (ideal value)
1032 ActivePlayer.curVideoNumber++;
1033 }
1034 // Allow audio output if slow
1035 else
1036 {
1037 ActivePlayer.internalState = THP_PLAYER_PLAY;
1038 }
1039 }
1040 // If THP movie has no audio
1041 else
1042 {
1043 textureSet = (THPTextureSet *)PopDecodedTextureSet(OS_MESSAGE_NOBLOCK);
1044 }
1045 }
1046 // If not appropriate timing, wait for next VSYNC.
1047 else
1048 {
1049 ActivePlayer.retraceCount = -1;
1050 }
1051 }
1052 // During playback
1053 else
1054 {
1055 // Enables audio output after 1 VSYNC to obtain starting THP video
1056 // data. It is assumed that the movie rendering loop is looping with 1 VSYNC.
1057 // The reason for this is:
1058 //
1059 // [Flow from THPPlayerPlay to display of starting frame]
1060 //
1061 // <Call THPPlayerPlay>
1062 // -----------------VSYNC----------------------------------------
1063 // <Obtain starting THP video data in VI post-callback>
1064 // <Render starting THP video data with your rendering loop, and
1065 // call VISetNextFrameBuffer and VIFlush.>
1066 // ------------------VSYNC----------------------------
1067 // From this point, movie is shown on TV. Audio output is enabled
1068 // with this timing.
1069 if (ActivePlayer.retraceCount == 1)
1070 {
1071 ActivePlayer.internalState = THP_PLAYER_PLAY;
1072 }
1073
1074 if (ProperTimingForGettingNextFrame())
1075 {
1076 // If THP movie has audio
1077 if (ActivePlayer.audioExist)
1078 {
1079 //Calculate difference between current audio playback frames
1080 diff = ActivePlayer.curVideoNumber - ActivePlayer.curAudioNumber;
1081
1082 // If audio is not slower than video, move video ahead
1083 if (diff <= 1)
1084 {
1085 // Acquire decoded THP video data
1086 textureSet = (THPTextureSet *)PopDecodedTextureSet(OS_MESSAGE_NOBLOCK);
1087
1088 // Increment THP video data number (ideal value)
1089 ActivePlayer.curVideoNumber++;
1090 }
1091 }
1092 // If THP movie has no audio
1093 else
1094 {
1095 // Acquire decoded video data
1096 textureSet = (THPTextureSet *)PopDecodedTextureSet(OS_MESSAGE_NOBLOCK);
1097 }
1098 }
1099 }
1100
1101 // If can get decoded THP video data, push THP video data held until
1102 // that point to cache for data played back.
1103 if (textureSet && (textureSet != (THPTextureSet *)0xFFFFFFFF))
1104 {
1105 if (ActivePlayer.dispTextureSet)
1106 {
1107 // If you call PushFreeTextureSet here, newly decoded THP video
1108 // data may be written to the texture buffer the graphics
1109 // processor is accessing, so the data is pushed to a temporary
1110 // cache. After verifying with THPPlayerDrawDone() that access from
1111 // the graphics processor is done, the actual release is done.
1112 PushUsedTextureSet(ActivePlayer.dispTextureSet);
1113 }
1114 ActivePlayer.dispTextureSet = textureSet;
1115 }
1116
1117 // Check if playback has reached end during one shot playback
1118 if (!(ActivePlayer.playFlag & THP_PLAY_LOOP))
1119 {
1120 // If THP movie has audio, check if video and audio has reached end
1121 if (ActivePlayer.audioExist)
1122 {
1123 frameNumber = ActivePlayer.curAudioNumber + ActivePlayer.initReadFrame;
1124
1125 // If reached to the end, set state to THP_PLAYER_PLAYED
1126 if (frameNumber == ActivePlayer.header.numFrames && ActivePlayer.playAudioBuffer == NULL)
1127 {
1128 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_PLAYED;
1129 }
1130 }
1131 // If THP movie has audio, check if video has reached end
1132 else
1133 {
1134 if (ActivePlayer.dispTextureSet)
1135 {
1136 frameNumber = ActivePlayer.dispTextureSet->frameNumber + ActivePlayer.initReadFrame;
1137 }
1138 else
1139 {
1140 frameNumber = ActivePlayer.initReadFrame - 1;
1141 }
1142
1143 if ((frameNumber == ActivePlayer.header.numFrames - 1)
1144 &&
1145 (textureSet == NULL))
1146 {
1147 ActivePlayer.state = ActivePlayer.internalState = THP_PLAYER_PLAYED;
1148 }
1149 }
1150 }
1151 }
1152
1153 return;
1154 }
1155
1156 /*---------------------------------------------------------------------------*
1157 Name: ProperTimingForStart
1158
1159 Description: Check if appropriate timing for movie playback start.
1160 Movie rendering loop may be looping with 1 VSYNC.
1161
1162 Arguments: None
1163
1164 Returns: If appropriate timing returns TRUE. If inappropriate returns FALSE.
1165 *---------------------------------------------------------------------------*/
1166
ProperTimingForStart(void)1167 static BOOL ProperTimingForStart(void)
1168 {
1169 if (ActivePlayer.videoInfo.videoType & THP_VIDEO_ODD_INTERLACE)
1170 {
1171 if (VIGetNextField() == VI_FIELD_BELOW)
1172 {
1173 return TRUE;
1174 }
1175 }
1176 else if (ActivePlayer.videoInfo.videoType & THP_VIDEO_EVEN_INTERLACE)
1177 {
1178 if (VIGetNextField() == VI_FIELD_ABOVE)
1179 {
1180 return TRUE;
1181 }
1182 }
1183 else
1184 {
1185 return TRUE;
1186 }
1187
1188 return FALSE;
1189 }
1190
1191 /*---------------------------------------------------------------------------*
1192 Name: ProperTimingForGettingNextFrame
1193
1194 Description: Checks if appropriate timing to get decoded THP
1195 video data.
1196
1197 Arguments: None
1198
1199 Returns: If appropriate timing, returns TRUE. If inappropriate, returns FALSE.
1200 *---------------------------------------------------------------------------*/
1201
ProperTimingForGettingNextFrame(void)1202 static BOOL ProperTimingForGettingNextFrame(void)
1203 {
1204 s32 frameRate;
1205
1206 if (ActivePlayer.videoInfo.videoType & THP_VIDEO_ODD_INTERLACE)
1207 {
1208 if (VIGetNextField() == VI_FIELD_BELOW)
1209 {
1210 return TRUE;
1211 }
1212 }
1213 else if (ActivePlayer.videoInfo.videoType & THP_VIDEO_EVEN_INTERLACE)
1214 {
1215 if (VIGetNextField() == VI_FIELD_ABOVE)
1216 {
1217 return TRUE;
1218 }
1219 }
1220 else
1221 {
1222 // Convert framerate to an integer
1223 frameRate = (s32)((ActivePlayer.header.frameRate) * 100.0f);
1224
1225 if (VIGetTvFormat() == VI_PAL)
1226 {
1227 ActivePlayer.curCount = (s32)((ActivePlayer.retraceCount * frameRate) / PAL_RATE);
1228 }
1229 else
1230 {
1231 ActivePlayer.curCount = (s32)((ActivePlayer.retraceCount * frameRate) / NTSC_RATE);
1232 }
1233
1234 if (ActivePlayer.prevCount != ActivePlayer.curCount)
1235 {
1236 ActivePlayer.prevCount = ActivePlayer.curCount;
1237 return TRUE;
1238 }
1239 }
1240
1241 return FALSE;
1242 }
1243
1244 /*---------------------------------------------------------------------------*
1245 Name: THPPlayerDrawCurrentFrame
1246
1247 Description: Draw currently acquired decoded THP video data.
1248 If none acquired, not displayed.
1249
1250 Arguments: rmode Pointer for currently set GXRenderModeObj
1251 x X coordinate of upper left TV screen for THP movie display
1252 y Y coordinate of upper left TV screen for THP movie display
1253 polygonW Width of polygon for THP movie display
1254 polygonH Height of polygon for THP movie display
1255
1256 Returns: If able to draw, return drawn frame number. If unable to draw, return -1.
1257 *---------------------------------------------------------------------------*/
1258
THPPlayerDrawCurrentFrame(GXRenderModeObj * rmode,u32 x,u32 y,u32 polygonW,u32 polygonH)1259 s32 THPPlayerDrawCurrentFrame(GXRenderModeObj *rmode, u32 x, u32 y, u32 polygonW, u32 polygonH)
1260 {
1261 s32 currentFrameNumber;
1262
1263 if (ActivePlayer.open && !(ActivePlayer.state == THP_PLAYER_STOP) && ActivePlayer.dispTextureSet)
1264 {
1265 THPGXYuv2RgbSetup(rmode);
1266 THPGXYuv2RgbDraw(ActivePlayer.dispTextureSet->ytexture,
1267 ActivePlayer.dispTextureSet->utexture,
1268 ActivePlayer.dispTextureSet->vtexture,
1269 (s16)x, (s16)y,
1270 (s16)ActivePlayer.videoInfo.xSize, (s16)ActivePlayer.videoInfo.ySize,
1271 (s16)polygonW, (s16)polygonH);
1272
1273 THPGXRestore();
1274
1275 currentFrameNumber = (s32)((ActivePlayer.dispTextureSet->frameNumber + ActivePlayer.initReadFrame)
1276 % ActivePlayer.header.numFrames);
1277
1278 return currentFrameNumber;
1279 }
1280
1281 return -1;
1282 }
1283
1284 /*---------------------------------------------------------------------------*
1285 Name: THPPlayerGetVideoInfo
1286
1287 Description: Get THP movie video data.
1288
1289 Arguments: videoInfo Pointer for THPVideoInfo structure.
1290
1291 Returns: If successful, returns TRUE. If unsuccessful, returns FALSE.
1292 *---------------------------------------------------------------------------*/
1293
THPPlayerGetVideoInfo(THPVideoInfo * videoInfo)1294 BOOL THPPlayerGetVideoInfo(THPVideoInfo *videoInfo)
1295 {
1296 if (ActivePlayer.open)
1297 {
1298 memcpy(videoInfo, &ActivePlayer.videoInfo, sizeof(THPVideoInfo));
1299
1300 return TRUE;
1301 }
1302
1303 return FALSE;
1304 }
1305
1306 /*---------------------------------------------------------------------------*
1307 Name: THPPlayerGetAudioInfo
1308
1309 Description: Get THP movie audio data.
1310
1311 Arguments: audioInfo Pointer for THPAudioInfo structure.
1312
1313 Returns: If successful, returns TRUE. If unsuccessful, returns FALSE.
1314 *---------------------------------------------------------------------------*/
1315
THPPlayerGetAudioInfo(THPAudioInfo * audioInfo)1316 BOOL THPPlayerGetAudioInfo(THPAudioInfo *audioInfo)
1317 {
1318 if (ActivePlayer.open)
1319 {
1320 memcpy(audioInfo, &ActivePlayer.audioInfo, sizeof(THPAudioInfo));
1321
1322 return TRUE;
1323 }
1324
1325 return FALSE;
1326 }
1327
1328 /*---------------------------------------------------------------------------*
1329 Name: THPPlayerGetFrameRate
1330
1331 Description: Get THP movie frame rate
1332
1333 Arguments: None
1334
1335 Returns: If successful, returns THP movie frame rate. If unsuccessful, returns 0.0f.
1336 *---------------------------------------------------------------------------*/
1337
THPPlayerGetFrameRate(void)1338 f32 THPPlayerGetFrameRate(void)
1339 {
1340 if (ActivePlayer.open)
1341 {
1342
1343 return ActivePlayer.header.frameRate;
1344 }
1345
1346 return 0.0f;
1347 }
1348
1349 /*---------------------------------------------------------------------------*
1350 Name: THPPlayerGetTotalFrame
1351
1352 Description: Acquire total frames for THP movie
1353
1354 Arguments: None
1355
1356 Returns: If successful, returns THP movie total frames. If unsuccessful, returns 0.
1357 *---------------------------------------------------------------------------*/
1358
THPPlayerGetTotalFrame(void)1359 u32 THPPlayerGetTotalFrame(void)
1360 {
1361 if (ActivePlayer.open)
1362 {
1363 return ActivePlayer.header.numFrames;
1364 }
1365
1366 return 0;
1367 }
1368
1369 /*---------------------------------------------------------------------------*
1370 Name: THPPlayerGetState
1371
1372 Description: Acquire current player status.
1373
1374 Arguments: None
1375
1376 Returns: Player status
1377 THP_PLAYER_STOP stopped
1378 THP_PLAYER_PREPARE preparations completed
1379 THP_PLAYER_PLAY playing
1380 THP_PLAYER_PLAYED played THP movie to end
1381 THP_PLAYER_ERROR error occurred
1382 *---------------------------------------------------------------------------*/
1383
THPPlayerGetState(void)1384 s32 THPPlayerGetState(void)
1385 {
1386 return ActivePlayer.state;
1387 }
1388
1389 /*---------------------------------------------------------------------------*
1390 Name: PushUsedTextureSet
1391
1392 Description: Remove played back THP video data from played back THP
1393 video data queue.
1394
1395 Arguments: None
1396
1397 Returns: None
1398 *---------------------------------------------------------------------------*/
1399
PushUsedTextureSet(void * buffer)1400 static void PushUsedTextureSet(void *buffer)
1401 {
1402 OSSendMessage(&UsedTextureSetQueue, buffer, OS_MESSAGE_NOBLOCK);
1403
1404 return;
1405 }
1406
1407 /*---------------------------------------------------------------------------*
1408 Name: PopUsedTextureSet
1409
1410 Description: Remove played back THP video data from played back THP
1411 video data queue.
1412
1413 Arguments: None
1414
1415 Returns: Pointer for played back THP video data.
1416 *---------------------------------------------------------------------------*/
1417
PopUsedTextureSet(void)1418 static void *PopUsedTextureSet(void)
1419 {
1420 OSMessage msg;
1421
1422 if (OSReceiveMessage(&UsedTextureSetQueue, &msg, OS_MESSAGE_NOBLOCK) == TRUE)
1423 {
1424 return msg;
1425 }
1426 else
1427 {
1428 return NULL;
1429 }
1430 }
1431
1432 /*---------------------------------------------------------------------------*
1433 Name: THPPlayerDrawDone
1434
1435 Description: Function used instead of GXDrawDone to synchronize with
1436 the graphics processor during movie playback. It waits for
1437 the graphics processor to complete processing and frees
1438 internally THP video data determined to be played back.
1439
1440 Arguments: None
1441
1442 Returns: None
1443 *---------------------------------------------------------------------------*/
1444
THPPlayerDrawDone(void)1445 void THPPlayerDrawDone(void)
1446 {
1447 void *textureSet;
1448
1449 GXDrawDone();
1450
1451 if (Initialized)
1452 {
1453 while(1)
1454 {
1455 textureSet = PopUsedTextureSet();
1456 if (textureSet == NULL)
1457 {
1458 break;
1459 }
1460 else
1461 {
1462 PushFreeTextureSet(textureSet);
1463 }
1464 }
1465 }
1466 }
1467
1468 /*---------------------------------------------------------------------------*
1469 Name: THPAudioMixCallback
1470
1471 Description: AI callback function for player.
1472 Call callback functions of AX and MusyX internally when
1473 used with AX and MusyX, and mix output data of AX and
1474 MusyX with THP audio data.
1475
1476 Arguments: None
1477
1478 Returns: None
1479 *---------------------------------------------------------------------------*/
1480
THPAudioMixCallback(void)1481 void THPAudioMixCallback(void)
1482 {
1483 BOOL old;
1484
1485 if (AudioSystem == THP_MODE_ALONE)
1486 {
1487 SoundBufferIndex ^= 1;
1488
1489 AIInitDMA((u32)SoundBuffer[SoundBufferIndex], BYTES_PER_AUDIO_FRAME);
1490
1491 old = OSEnableInterrupts();
1492
1493 MixAudio(SoundBuffer[SoundBufferIndex], NULL, SAMPLES_PER_AUDIO_FRAME);
1494
1495 DCFlushRange(SoundBuffer[SoundBufferIndex], BYTES_PER_AUDIO_FRAME);
1496
1497 OSRestoreInterrupts(old);
1498 }
1499 else
1500 {
1501 if (AudioSystem == THP_MODE_WITH_AX)
1502 {
1503 if (LastAudioBuffer)
1504 {
1505 CurAudioBuffer = LastAudioBuffer;
1506 }
1507
1508 OldAIDCallback();
1509
1510 LastAudioBuffer = (s16 *)OSPhysicalToCached(AIGetDMAStartAddr());
1511 }
1512 else
1513 {
1514 OldAIDCallback();
1515
1516 CurAudioBuffer = (s16 *)OSPhysicalToCached(AIGetDMAStartAddr());
1517 }
1518
1519 SoundBufferIndex ^= 1;
1520
1521 AIInitDMA((u32)SoundBuffer[SoundBufferIndex], BYTES_PER_AUDIO_FRAME);
1522
1523 old = OSEnableInterrupts();
1524
1525 if (CurAudioBuffer)
1526 {
1527 DCInvalidateRange(CurAudioBuffer, BYTES_PER_AUDIO_FRAME);
1528 }
1529
1530 MixAudio(SoundBuffer[SoundBufferIndex], CurAudioBuffer, SAMPLES_PER_AUDIO_FRAME);
1531
1532 DCFlushRange(SoundBuffer[SoundBufferIndex], BYTES_PER_AUDIO_FRAME);
1533
1534 OSRestoreInterrupts(old);
1535 }
1536 }
1537
1538 /*---------------------------------------------------------------------------*
1539 Name: MixAudio
1540
1541 Description: Mix THP audio data in the specified buffer.
1542
1543 Arguments: destination Buffer in which mixed data is stored.
1544 source Output buffer from audio library
1545 sample Sample number of audio data to be mixed.
1546 Specified by stereo sample.
1547
1548 Returns: None
1549 *---------------------------------------------------------------------------*/
1550
MixAudio(s16 * destination,s16 * source,u32 sample)1551 static void MixAudio(s16 *destination, s16 *source, u32 sample)
1552 {
1553 u32 sampleNum, requestSample, i;
1554 s32 mix;
1555 s16 *dst, *libsrc, *thpsrc;
1556 u16 attenuation;
1557
1558 // When mixed with audio library output
1559 if (source)
1560 {
1561 if (ActivePlayer.open && (ActivePlayer.internalState == THP_PLAYER_PLAY) && ActivePlayer.audioExist)
1562 {
1563 requestSample = sample;
1564 dst = destination;
1565 libsrc = source;
1566
1567 while (1)
1568 {
1569 if (ActivePlayer.playAudioBuffer == NULL)
1570 {
1571 if ((ActivePlayer.playAudioBuffer = (THPAudioBuffer *)PopDecodedAudioBuffer(OS_MESSAGE_NOBLOCK)) == NULL)
1572 {
1573 memcpy(dst, libsrc, requestSample << 2);
1574 break;
1575 }
1576 else
1577 {
1578 ActivePlayer.curAudioNumber++;
1579 }
1580 }
1581
1582 if (ActivePlayer.playAudioBuffer->validSample)
1583 {
1584 if (ActivePlayer.playAudioBuffer->validSample >= requestSample)
1585 {
1586 sampleNum = requestSample;
1587 }
1588 else
1589 {
1590 sampleNum = ActivePlayer.playAudioBuffer->validSample;
1591 }
1592
1593 thpsrc = ActivePlayer.playAudioBuffer->curPtr;
1594
1595 // Mixing
1596 for (i = 0 ; i < sampleNum ; i++)
1597 {
1598 if (ActivePlayer.rampCount)
1599 {
1600 ActivePlayer.rampCount--;
1601 ActivePlayer.curVolume += ActivePlayer.deltaVolume;
1602 }
1603 else
1604 {
1605 ActivePlayer.curVolume = ActivePlayer.targetVolume;
1606 }
1607
1608 attenuation = VolumeTable[(s32)ActivePlayer.curVolume];
1609
1610 // Right
1611 mix = (*libsrc) + ((attenuation * (*thpsrc)) >> 15);
1612
1613 if (mix < -32768)
1614 {
1615 mix = -32768;
1616 }
1617 if (mix > 32767)
1618 {
1619 mix = 32767;
1620 }
1621
1622 *dst = (s16)mix;
1623 dst++;
1624 libsrc++;
1625 thpsrc++;
1626
1627 // Left
1628 mix = (*libsrc) + ((attenuation * (*thpsrc)) >> 15);
1629
1630 if (mix < -32768)
1631 {
1632 mix = -32768;
1633 }
1634 if (mix > 32767)
1635 {
1636 mix = 32767;
1637 }
1638
1639 *dst = (s16)mix;
1640 dst++;
1641 libsrc++;
1642 thpsrc++;
1643 }
1644
1645 requestSample -= sampleNum;
1646
1647 ActivePlayer.playAudioBuffer->validSample -= sampleNum;
1648 ActivePlayer.playAudioBuffer->curPtr = thpsrc;
1649
1650 if (ActivePlayer.playAudioBuffer->validSample == 0)
1651 {
1652 // Release used THP audio data
1653 PushFreeAudioBuffer(ActivePlayer.playAudioBuffer);
1654 ActivePlayer.playAudioBuffer = NULL;
1655 }
1656
1657 if (!requestSample)
1658 {
1659 break;
1660 }
1661 }
1662 }
1663 }
1664 else
1665 {
1666 memcpy(destination, source, sample << 2);
1667 }
1668 }
1669 // When not mixing with audio library output
1670 else
1671 {
1672 if (ActivePlayer.open && (ActivePlayer.internalState == THP_PLAYER_PLAY) && ActivePlayer.audioExist)
1673 {
1674 requestSample = sample;
1675 dst = destination;
1676
1677 while (1)
1678 {
1679 if (ActivePlayer.playAudioBuffer == NULL)
1680 {
1681 if ((ActivePlayer.playAudioBuffer = (THPAudioBuffer *)PopDecodedAudioBuffer(OS_MESSAGE_NOBLOCK)) == NULL)
1682 {
1683 memset(dst, 0, requestSample << 2);
1684 break;
1685 }
1686 else
1687 {
1688 ActivePlayer.curAudioNumber++;
1689 }
1690 }
1691
1692 if (ActivePlayer.playAudioBuffer->validSample)
1693 {
1694 if (ActivePlayer.playAudioBuffer->validSample >= requestSample)
1695 {
1696 sampleNum = requestSample;
1697 }
1698 else
1699 {
1700 sampleNum = ActivePlayer.playAudioBuffer->validSample;
1701 }
1702
1703 thpsrc = ActivePlayer.playAudioBuffer->curPtr;
1704
1705 // Mixing
1706 for (i = 0 ; i < sampleNum ; i++)
1707 {
1708 if (ActivePlayer.rampCount)
1709 {
1710 ActivePlayer.rampCount--;
1711 ActivePlayer.curVolume += ActivePlayer.deltaVolume;
1712 }
1713 else
1714 {
1715 ActivePlayer.curVolume = ActivePlayer.targetVolume;
1716 }
1717
1718 attenuation = VolumeTable[(s32)ActivePlayer.curVolume];
1719
1720 // Right
1721 mix = (attenuation * (*thpsrc)) >> 15;
1722
1723 if (mix < -32768)
1724 {
1725 mix = -32768;
1726 }
1727 if (mix > 32767)
1728 {
1729 mix = 32767;
1730 }
1731
1732 *dst = (s16)mix;
1733 dst++;
1734 thpsrc++;
1735
1736 // Left
1737 mix = (attenuation * (*thpsrc)) >> 15;
1738
1739 if (mix < -32768)
1740 {
1741 mix = -32768;
1742 }
1743 if (mix > 32767)
1744 {
1745 mix = 32767;
1746 }
1747
1748 *dst = (s16)mix;
1749 dst++;
1750 thpsrc++;
1751 }
1752
1753 requestSample -= sampleNum;
1754
1755 ActivePlayer.playAudioBuffer->validSample -= sampleNum;
1756 ActivePlayer.playAudioBuffer->curPtr = thpsrc;
1757
1758 if (ActivePlayer.playAudioBuffer->validSample == 0)
1759 {
1760 // Release used THP audio data
1761 PushFreeAudioBuffer(ActivePlayer.playAudioBuffer);
1762 ActivePlayer.playAudioBuffer = NULL;
1763 }
1764
1765 if (!requestSample)
1766 {
1767 break;
1768 }
1769 }
1770 }
1771 }
1772 else
1773 {
1774 memset(destination, 0, sample << 2);
1775 }
1776 }
1777
1778 return;
1779 }
1780
1781 /*---------------------------------------------------------------------------*
1782 Name: THPPlayerSetVolume
1783
1784 Description: Set volume of player. Volume will be changed in the
1785 specified time.
1786
1787 Arguments: vol volume to be set (0 - 127)
1788 time Specify the time required to go from current volume to
1789 specified volume in units of milliseconds (0 - 60000)
1790
1791 Returns: Returns TRUE if succeeds, and FALSE if fails.
1792 *---------------------------------------------------------------------------*/
1793
THPPlayerSetVolume(s32 vol,s32 time)1794 BOOL THPPlayerSetVolume(s32 vol, s32 time)
1795 {
1796 BOOL old;
1797 s32 samplePerMs;
1798
1799 if (ActivePlayer.open && ActivePlayer.audioExist)
1800 {
1801 if (AIGetDSPSampleRate() == AI_SAMPLERATE_32KHZ)
1802 {
1803 samplePerMs = 32;
1804 }
1805 else
1806 {
1807 samplePerMs = 48;
1808 }
1809
1810 if (vol > 127)
1811 {
1812 vol = 127;
1813 }
1814
1815 if (vol < 0)
1816 {
1817 vol = 0;
1818 }
1819
1820 if (time > 60000)
1821 {
1822 time = 60000;
1823 }
1824
1825 if (time < 0)
1826 {
1827 time = 0;
1828 }
1829
1830 old = OSDisableInterrupts();
1831
1832 ActivePlayer.targetVolume = (f32)vol;
1833
1834 if (time)
1835 {
1836 ActivePlayer.rampCount = samplePerMs * time;
1837 ActivePlayer.deltaVolume = (ActivePlayer.targetVolume - ActivePlayer.curVolume)
1838 / (f32)ActivePlayer.rampCount;
1839 }
1840 else
1841 {
1842 ActivePlayer.rampCount = 0;
1843 ActivePlayer.curVolume = ActivePlayer.targetVolume;
1844 }
1845
1846 OSRestoreInterrupts(old);
1847
1848 return TRUE;
1849 }
1850
1851 return FALSE;
1852 }
1853
1854 /*---------------------------------------------------------------------------*
1855 Name: THPPlayerGetVolume
1856
1857 Description: Acquire current volume of player
1858
1859 Arguments: None
1860
1861 Returns: Returns current playback volume if successful, and -1 if unsuccessful.
1862 *---------------------------------------------------------------------------*/
1863
THPPlayerGetVolume(void)1864 s32 THPPlayerGetVolume(void)
1865 {
1866 if (ActivePlayer.open)
1867 {
1868 return (s32)ActivePlayer.curVolume;
1869 }
1870
1871 return -1;
1872 }
1873