1 /*---------------------------------------------------------------------------*
2 Project: THP Simple Player
3 File: THPSimple.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: THPSimple.c,v $
14 Revision 1.2 02/03/2006 11:43:16 aka
15 Changed audio frame from 5msec to 3msec.
16
17 Revision 1.1 02/03/2006 10:01:13 aka
18 Imported from Dolphin tree.
19
20 4 03/11/25 11:24 Dante
21 Japanese to English translation of comments and text strings
22
23 3 02/05/14 9:18a Suzuki
24 Support multi audio track
25
26 2 02/02/28 6:37p Akagi
27 enabled to use with MusyX/AX by Suzuki-san (IRD).
28
29 1 02/01/16 10:54a Akagi
30 Initial revision made by Suzuki-san (IRD).
31
32 $NoKeywords: $
33
34 *---------------------------------------------------------------------------*/
35
36 #include <string.h>
37 #include <revolution.h>
38
39 #include "THPSimple.h"
40 #include "THPDraw.h"
41
42 #define NEXT_BUFFER(p) ((p) + 1 >= READ_BUFFER_NUM ? 0 : (p) + 1)
43 #define GET_DECODE_BUFFER(p) ((p).readBuffer[(p).nextDecodeIndex].ptr)
44 #define GET_READ_BUFFER(p) ((p).readBuffer[(p).readIndex].ptr)
45 #define BUFFER_VALID(p) (SimpleControl.readBuffer[p].isValid)
46 #define NEXT_READ_SIZE(p) *(u32 *)((p).readBuffer[(p).readIndex].ptr)
47 #define GET_COMP_SIZE(p, i) *(u32 *)((p).readBuffer[(p).nextDecodeIndex].ptr + 4 * i + 8)
48
49 #define SAMPLES_PER_AUDIO_FRAME 96 // 3msec (32khz)
50 #define BYTES_PER_AUDIO_FRAME (SAMPLES_PER_AUDIO_FRAME * 4)
51
52 /*---------------------------------------------------------------------------*
53 Static Function
54 *---------------------------------------------------------------------------*/
55
56 static void ReadFrameAsync(void);
57 static BOOL VideoDecode(u8 *videoFrame);
58 static void __THPSimpleDVDCallback(s32 result, DVDFileInfo *fileInfo);
59 static inline void CheckPrefetch(void);
60 static void THPAudioMixCallback(void);
61 static void MixAudio(s16 *destination, s16 *source, u32 sample);
62
63 /*---------------------------------------------------------------------------*
64 Static Variable
65 *---------------------------------------------------------------------------*/
66
67 // 32768 * ((vol * vol) / (127 * 127))
68 static u16 VolumeTable[] =
69 {
70 0, 2, 8, 18, 32, 50, 73, 99,
71 130, 164, 203, 245, 292, 343, 398, 457,
72 520, 587, 658, 733, 812, 895, 983, 1074,
73 1170, 1269, 1373, 1481, 1592, 1708, 1828, 1952,
74 2080, 2212, 2348, 2488, 2632, 2781, 2933, 3090,
75 3250, 3415, 3583, 3756, 3933, 4114, 4298, 4487,
76 4680, 4877, 5079, 5284, 5493, 5706, 5924, 6145,
77 6371, 6600, 6834, 7072, 7313, 7559, 7809, 8063,
78 8321, 8583, 8849, 9119, 9394, 9672, 9954, 10241,
79 10531, 10826, 11125, 11427, 11734, 12045, 12360, 12679,
80 13002, 13329, 13660, 13995, 14335, 14678, 15025, 15377,
81 15732, 16092, 16456, 16823, 17195, 17571, 17951, 18335,
82 18723, 19115, 19511, 19911, 20316, 20724, 21136, 21553,
83 21974, 22398, 22827, 23260, 23696, 24137, 24582, 25031,
84 25484, 25941, 26402, 26868, 27337, 27810, 28288, 28769,
85 29255, 29744, 30238, 30736, 31238, 31744, 32254, 32768
86 };
87
88 static THPSimple SimpleControl;
89 static s32 WorkBuffer[16] ATTRIBUTE_ALIGN(32);
90 static BOOL Initialized = 0;
91 static s16 SoundBuffer[2][SAMPLES_PER_AUDIO_FRAME * 2] ATTRIBUTE_ALIGN(32);
92 static s32 SoundBufferIndex;
93 static AIDCallback OldAIDCallback = NULL;
94 static s16 *LastAudioBuffer;
95 static s16 *CurAudioBuffer;
96 static s32 AudioSystem = 0;
97
98 /*---------------------------------------------------------------------------*
99 Name: THPSimpleInit
100
101 Description: Initializes player and THP library and turns locked cache
102 to ON.
103 Register AI FIFO DMA callback function for single player
104
105 Arguments: audioSystem Specifies the audio library to be used
106 simultaneously.
107 If no audio library is to be used simultaneously,
108 specifies THP_MODE_ALONE.
109
110 Returns: If successful, returns TRUE. If unsuccessful, returns FALSE.
111 *---------------------------------------------------------------------------*/
112
THPSimpleInit(s32 audioSystem)113 BOOL THPSimpleInit(s32 audioSystem)
114 {
115 BOOL old;
116
117 ASSERTMSG(audioSystem >= 0 && audioSystem <= THP_MODE_WITH_MUSYX, "audioSystem flag is invalid\n");
118
119 memset(&SimpleControl, 0, sizeof(THPSimple));
120
121 LCEnable();
122
123 if (THPInit() == FALSE)
124 {
125 return FALSE;
126 }
127
128 old = OSDisableInterrupts();
129
130 AudioSystem = audioSystem;
131 SoundBufferIndex = 0;
132 LastAudioBuffer = NULL;
133 CurAudioBuffer = NULL;
134
135 OldAIDCallback = AIRegisterDMACallback(THPAudioMixCallback);
136
137 if (OldAIDCallback == NULL && AudioSystem != THP_MODE_ALONE)
138 {
139 AIRegisterDMACallback(NULL);
140
141 OSRestoreInterrupts(old);
142
143 #ifdef _DEBUG
144 OSReport("Please call AXInit or sndInit before you call THPPlayerInit\n");
145 #endif
146 return FALSE;
147 }
148
149 OSRestoreInterrupts(old);
150
151 if (AudioSystem == THP_MODE_ALONE)
152 {
153 memset(SoundBuffer, 0, BYTES_PER_AUDIO_FRAME << 1);
154
155 DCFlushRange(SoundBuffer, BYTES_PER_AUDIO_FRAME << 1);
156
157 AIInitDMA((u32)SoundBuffer[SoundBufferIndex], BYTES_PER_AUDIO_FRAME);
158
159 AIStartDMA();
160 }
161
162 Initialized = TRUE;
163
164 return TRUE;
165 }
166
167 /*---------------------------------------------------------------------------*
168 Name: THPSimpleQuit
169
170 Description: Turns locked cache to OFF.
171 Restore AI FIFO DMA callback to the state before THPPlayerInit is called.
172
173 Arguments: None
174
175 Returns: None
176 *---------------------------------------------------------------------------*/
177
THPSimpleQuit(void)178 void THPSimpleQuit(void)
179 {
180 BOOL old;
181
182 LCDisable();
183
184 old = OSDisableInterrupts();
185
186 if (OldAIDCallback)
187 {
188 AIRegisterDMACallback(OldAIDCallback);
189 }
190
191 OSRestoreInterrupts(old);
192
193 Initialized = FALSE;
194
195 return;
196 }
197
198 /*---------------------------------------------------------------------------*
199 Name: THPSimpleOpen
200
201 Description: Opens THP movie file. Reads required data.
202
203 Arguments: fileName THP movie filename
204
205 Returns: If successful, returns TRUE. If unsuccessful, returns FALSE.
206 *---------------------------------------------------------------------------*/
207
THPSimpleOpen(char * fileName)208 BOOL THPSimpleOpen(char *fileName)
209 {
210 s32 offset, i;
211
212 if (!Initialized)
213 {
214 #ifdef _DEBUG
215 OSReport("You must call THPSimpleInit before you call this function\n");
216 #endif
217 return FALSE;
218 }
219
220 if (SimpleControl.open)
221 {
222 #ifdef _DEBUG
223 OSReport("Cannot open %s. The THP file is already open.\n");
224 #endif
225 return FALSE;
226 }
227
228 // Clear current video data and audio data
229 memset(&SimpleControl.videoInfo, 0, sizeof(THPVideoInfo));
230 memset(&SimpleControl.audioInfo, 0, sizeof(THPAudioInfo));
231
232 if (DVDOpen(fileName, &SimpleControl.fileInfo) == FALSE)
233 {
234 #ifdef _DEBUG
235 OSReport("Cannot open %s\n", fileName);
236 #endif
237 return FALSE;
238 }
239
240 // Get THP header
241 if (DVDRead(&SimpleControl.fileInfo, WorkBuffer, 64, 0) < 0)
242 {
243 #ifdef _DEBUG
244 OSReport("Failed to read the header from THP file\n");
245 #endif
246 DVDClose(&SimpleControl.fileInfo);
247 return FALSE;
248 }
249
250 memcpy(&SimpleControl.header, WorkBuffer, sizeof(THPHeader));
251
252 // Check if THP movie file
253 if (strcmp(SimpleControl.header.magic, "THP") != 0)
254 {
255 #ifdef _DEBUG
256 OSReport("This file is not THP file\n");
257 #endif
258 DVDClose(&SimpleControl.fileInfo);
259 return FALSE;
260 }
261
262 // Check version
263 if (SimpleControl.header.version != THP_VERSION)
264 {
265 #ifdef _DEBUG
266 OSReport("invalid version\n");
267 #endif
268 DVDClose(&SimpleControl.fileInfo);
269 return FALSE;
270 }
271
272 offset = (s32)SimpleControl.header.compInfoDataOffsets;
273
274 // Get component data in frame
275 if (DVDRead(&SimpleControl.fileInfo, WorkBuffer, 32, offset) < 0)
276 {
277 #ifdef _DEBUG
278 OSReport("Failed to read the frame component infomation from THP file\n");
279 #endif
280 DVDClose(&SimpleControl.fileInfo);
281 return FALSE;
282 }
283
284 memcpy(&SimpleControl.compInfo, WorkBuffer, sizeof(THPFrameCompInfo));
285
286 offset += sizeof(THPFrameCompInfo);
287
288 SimpleControl.audioExist = 0;
289
290 // Check components in frame
291 for(i = 0 ; i < SimpleControl.compInfo.numComponents ; i++)
292 {
293 switch(SimpleControl.compInfo.frameComp[i])
294 {
295 case THP_VIDEO_COMP: // Get component video data
296 if (DVDRead(&SimpleControl.fileInfo, WorkBuffer, 32, offset) < 0)
297 {
298 #ifdef _DEBUG
299 OSReport("Failed to read the video infomation from THP file\n");
300 #endif
301 DVDClose(&SimpleControl.fileInfo);
302 return FALSE;
303 }
304 memcpy(&SimpleControl.videoInfo, WorkBuffer, sizeof(THPVideoInfo));
305 offset += sizeof(THPVideoInfo);
306 break;
307 case THP_AUDIO_COMP: // Get component audio data
308 if (DVDRead(&SimpleControl.fileInfo, WorkBuffer, 32, offset) < 0)
309 {
310 #ifdef _DEBUG
311 OSReport("Failed to read the video infomation from THP file\n");
312 #endif
313 DVDClose(&SimpleControl.fileInfo);
314 return FALSE;
315 }
316 memcpy(&SimpleControl.audioInfo, WorkBuffer, sizeof(THPAudioInfo));
317 SimpleControl.audioExist = 1;
318 offset += sizeof(THPAudioInfo);
319
320 break;
321 default:
322 #ifdef _DEBUG
323 OSReport("Unknown frame components\n");
324 #endif
325 return FALSE;
326 }
327 }
328
329 SimpleControl.curOffset = (s32)SimpleControl.header.movieDataOffsets;
330 SimpleControl.readSize = (s32)SimpleControl.header.firstFrameSize;
331 SimpleControl.readIndex = 0;
332 SimpleControl.totalReadFrame = 0;
333 SimpleControl.dvdError = FALSE;
334 SimpleControl.textureSet.frameNumber = -1;
335 SimpleControl.nextDecodeIndex = 0;
336 SimpleControl.audioDecodeIndex = 0;
337 SimpleControl.audioOutputIndex = 0;
338 SimpleControl.preFetchState = FALSE;
339 SimpleControl.audioState = FALSE;
340 SimpleControl.loop = THP_PLAY_ONESHOT;
341 SimpleControl.open = TRUE;
342 SimpleControl.curVolume = 127.0f;
343 SimpleControl.targetVolume = SimpleControl.curVolume;
344 SimpleControl.rampCount = 0;
345
346 return TRUE;
347 }
348
349 /*---------------------------------------------------------------------------*
350 Name: THPSimpleClose
351
352 Description: Closes THP movie file
353
354 Arguments: None
355
356 Returns: If successful, returns TRUE. If unsuccessful, returns FALSE.
357 *---------------------------------------------------------------------------*/
358
THPSimpleClose(void)359 BOOL THPSimpleClose(void)
360 {
361 if (SimpleControl.open)
362 {
363 if (SimpleControl.preFetchState == FALSE)
364 {
365 if (SimpleControl.audioExist)
366 {
367 if (SimpleControl.audioState == TRUE)
368 {
369 return FALSE;
370 }
371 }
372 else
373 {
374 SimpleControl.audioState = FALSE;
375 }
376
377 if (!SimpleControl.readProgress)
378 {
379 SimpleControl.open = FALSE;
380
381 DVDClose(&SimpleControl.fileInfo);
382
383 return TRUE;
384 }
385 }
386 }
387
388 return FALSE;
389 }
390
391 /*---------------------------------------------------------------------------*
392 Name: THPSimpleCalcNeedMemory
393
394 Description: Calculates required memory for THP movie playback
395
396 Arguments: None
397
398 Returns: If successful, returns required memory size. If unsuccessful,
399 returns 0.
400 *---------------------------------------------------------------------------*/
401
THPSimpleCalcNeedMemory(void)402 u32 THPSimpleCalcNeedMemory(void)
403 {
404 u32 size;
405
406 if (SimpleControl.open)
407 {
408 // Buffer size for read
409 size = OSRoundUp32B(SimpleControl.header.bufSize) * READ_BUFFER_NUM;
410
411 // Texture buffer size
412 size += OSRoundUp32B(SimpleControl.videoInfo.xSize * SimpleControl.videoInfo.ySize); //Y
413 size += OSRoundUp32B(SimpleControl.videoInfo.xSize * SimpleControl.videoInfo.ySize / 4); //U
414 size += OSRoundUp32B(SimpleControl.videoInfo.xSize * SimpleControl.videoInfo.ySize / 4); //V
415
416 // Audio buffer size
417 if (SimpleControl.audioExist)
418 {
419 size += (OSRoundUp32B(SimpleControl.header.audioMaxSamples * 4) * AUDIO_BUFFER_NUM);
420 }
421
422 size += THP_WORK_SIZE;
423
424 return size;
425 }
426
427 return 0;
428 }
429
430 /*---------------------------------------------------------------------------*
431 Name: THPSimpleSetBuffer
432
433 Description: Allocates required memory for THP movie playback to the
434 THPSimple structure.
435
436 Arguments: buffer Pointer for THP movie memory area set aside
437 externally.
438
439 Returns: If successful, returns TRUE. If unsuccessful, returns FALSE.
440 *---------------------------------------------------------------------------*/
441
THPSimpleSetBuffer(u8 * buffer)442 BOOL THPSimpleSetBuffer(u8 *buffer)
443 {
444 u32 i;
445 u8 *ptr;
446 u32 ysize, uvsize;
447
448 ASSERTMSG(buffer != NULL, "buffer is NULL\n");
449
450 if (SimpleControl.open && SimpleControl.preFetchState == FALSE)
451 {
452 if (SimpleControl.audioState == TRUE)
453 {
454 return FALSE;
455 }
456
457 ysize = OSRoundUp32B(SimpleControl.videoInfo.xSize * SimpleControl.videoInfo.ySize);
458 uvsize = OSRoundUp32B(SimpleControl.videoInfo.xSize * SimpleControl.videoInfo.ySize / 4);
459
460 ptr = buffer;
461
462 // Set texture buffer
463 SimpleControl.textureSet.ytexture = ptr;
464 DCInvalidateRange(ptr, ysize);
465 ptr += ysize;
466 SimpleControl.textureSet.utexture = ptr;
467 DCInvalidateRange(ptr, uvsize);
468 ptr += uvsize;
469 SimpleControl.textureSet.vtexture = ptr;
470 DCInvalidateRange(ptr, uvsize);
471 ptr += uvsize;
472
473 // Set audio buffer
474 for (i = 0 ; i < READ_BUFFER_NUM ; i++)
475 {
476 SimpleControl.readBuffer[i].ptr = ptr;
477 ptr += OSRoundUp32B(SimpleControl.header.bufSize);
478 SimpleControl.readBuffer[i].isValid = FALSE;
479 }
480
481 // Set Audio Buffer
482 if (SimpleControl.audioExist)
483 {
484 for (i = 0 ; i < AUDIO_BUFFER_NUM ; i++)
485 {
486 SimpleControl.audioBuffer[i].buffer = (s16 *)ptr;
487 SimpleControl.audioBuffer[i].curPtr = (s16 *)ptr;
488 SimpleControl.audioBuffer[i].validSample = 0;
489 ptr += OSRoundUp32B(SimpleControl.header.audioMaxSamples * 4);
490 }
491 }
492
493 SimpleControl.thpWork = (void *)ptr;
494 }
495
496 return TRUE;
497 }
498
499 /*---------------------------------------------------------------------------*
500 Name: ReadFrameAsync
501
502 Description: Asynchronous read of 1 frame of movie data
503
504 Arguments: None
505
506 Returns: None
507 *---------------------------------------------------------------------------*/
508
ReadFrameAsync(void)509 static void ReadFrameAsync(void)
510 {
511 if (!SimpleControl.dvdError && SimpleControl.preFetchState == TRUE)
512 {
513 if (SimpleControl.totalReadFrame > SimpleControl.header.numFrames - 1)
514 {
515 // If loop playback, offset and size to starting frame
516 if (SimpleControl.loop == THP_PLAY_LOOP)
517 {
518 SimpleControl.totalReadFrame = 0;
519 SimpleControl.curOffset = (s32)SimpleControl.header.movieDataOffsets;
520 SimpleControl.readSize = (s32)SimpleControl.header.firstFrameSize;
521 }
522 // Do nothing if one-shot playback
523 else
524 {
525 return;
526 }
527 }
528
529 SimpleControl.readProgress = TRUE;
530
531 if (DVDReadAsync(&SimpleControl.fileInfo,
532 GET_READ_BUFFER(SimpleControl),
533 SimpleControl.readSize,
534 SimpleControl.curOffset,
535 __THPSimpleDVDCallback) != TRUE)
536 {
537 SimpleControl.readProgress = FALSE;
538 SimpleControl.dvdError = TRUE;
539 }
540 }
541
542 return;
543 }
544
545 /*---------------------------------------------------------------------------*
546 Name: __THPSimpleDVDCallback
547
548 Description: Callback function specified in DVDReadAsync. If space in
549 read buffer, call DVDReadAsync again and do preloading of
550 THP movie data.
551
552 Arguments: None
553
554 Returns: None
555 *---------------------------------------------------------------------------*/
556
__THPSimpleDVDCallback(s32 result,DVDFileInfo * fileInfo)557 static void __THPSimpleDVDCallback(s32 result, DVDFileInfo *fileInfo)
558 {
559 #pragma unused(fileInfo)
560 // If error occurs, turn on error flag and end
561 if (result == DVD_RESULT_FATAL_ERROR)
562 {
563 SimpleControl.dvdError = TRUE;
564 return;
565 }
566 // Return if canceled
567 else if (result == DVD_RESULT_CANCELED)
568 {
569 return;
570 }
571
572 SimpleControl.readProgress = FALSE;
573
574 // Record frame # of currently completed read
575 SimpleControl.readBuffer[SimpleControl.readIndex].frameNumber = SimpleControl.totalReadFrame;
576
577 SimpleControl.totalReadFrame++;
578
579 SimpleControl.readBuffer[SimpleControl.readIndex].isValid = TRUE;
580 SimpleControl.curOffset += SimpleControl.readSize;
581 SimpleControl.readSize = (s32)NEXT_READ_SIZE(SimpleControl);
582 SimpleControl.readIndex = NEXT_BUFFER(SimpleControl.readIndex);
583
584 // Continue preload if open buffer present
585 if (!BUFFER_VALID(SimpleControl.readIndex))
586 {
587 ReadFrameAsync();
588 }
589
590 return;
591 }
592
593 /*---------------------------------------------------------------------------*
594 Name: CheckPrefetch
595
596 Description: Checks whether preload has stopped. Does preload if preload
597 has stopped and read space in read buffer.
598
599 Arguments: None
600
601 Returns: None
602 *---------------------------------------------------------------------------*/
603
CheckPrefetch(void)604 static inline void CheckPrefetch(void)
605 {
606 BOOL enabled;
607
608 enabled = OSDisableInterrupts();
609
610 if (!BUFFER_VALID(SimpleControl.readIndex) && !SimpleControl.readProgress)
611 {
612 ReadFrameAsync();
613 }
614
615 OSRestoreInterrupts(enabled);
616 }
617
618 /*---------------------------------------------------------------------------*
619 Name: THPSimplePreLoad
620
621 Description: As preparation for movie playback, preload READ_BUFFER_NUM
622 worth of frame data.
623
624 Arguments: loop Specify loop flag.
625
626 Returns: If playback preparations completed returns TRUE.
627 If fail returns FALSE.
628 *---------------------------------------------------------------------------*/
629
THPSimplePreLoad(s32 loop)630 BOOL THPSimplePreLoad(s32 loop)
631 {
632 u32 i, readNum;
633
634 if (SimpleControl.open && (SimpleControl.preFetchState == FALSE))
635 {
636 readNum = READ_BUFFER_NUM;
637
638 if (!loop)
639 {
640 if (SimpleControl.header.numFrames < READ_BUFFER_NUM)
641 {
642 readNum = SimpleControl.header.numFrames;
643 }
644 }
645
646 // Do preload
647 for (i = 0 ; i < readNum ; i++)
648 {
649 if (DVDRead(&SimpleControl.fileInfo,
650 SimpleControl.readBuffer[SimpleControl.readIndex].ptr,
651 SimpleControl.readSize,
652 SimpleControl.curOffset) < 0)
653 {
654 #ifdef _DEBUG
655 OSReport("Failed to read the frame data from THP file.\n");
656 #endif
657 SimpleControl.dvdError = TRUE;
658 return FALSE;
659 }
660
661 SimpleControl.curOffset += SimpleControl.readSize;
662 SimpleControl.readSize = (s32)NEXT_READ_SIZE(SimpleControl);
663
664 SimpleControl.readBuffer[SimpleControl.readIndex].isValid = TRUE;
665 SimpleControl.readBuffer[SimpleControl.readIndex].frameNumber = SimpleControl.totalReadFrame;
666
667 SimpleControl.readIndex = NEXT_BUFFER(SimpleControl.readIndex);
668
669 SimpleControl.totalReadFrame++;
670
671 if (SimpleControl.totalReadFrame > SimpleControl.header.numFrames - 1)
672 {
673 if (SimpleControl.loop == THP_PLAY_LOOP)
674 {
675 SimpleControl.totalReadFrame = 0;
676 SimpleControl.curOffset = (s32)SimpleControl.header.movieDataOffsets;
677 SimpleControl.readSize = (s32)SimpleControl.header.firstFrameSize;
678 }
679 }
680 }
681
682 // Set loop flag
683 SimpleControl.loop = (u8)loop;
684 SimpleControl.preFetchState = TRUE;
685
686 return TRUE;
687 }
688
689 return FALSE;
690 }
691
692 /*---------------------------------------------------------------------------*
693 Name: THPSimpleAudioStart
694
695 Description: Allow mixing of audio data using THPSimpleAudioOutput.
696
697 Arguments: None
698
699 Returns: None
700 *---------------------------------------------------------------------------*/
701
THPSimpleAudioStart(void)702 void THPSimpleAudioStart(void)
703 {
704 SimpleControl.audioState = TRUE;
705
706 return;
707 }
708
709 /*---------------------------------------------------------------------------*
710 Name: THPSimpleAudioStop
711
712 Description: Prohibit mixing of audio data using THPSimpleAudioOutput.
713
714 Arguments: None
715
716 Returns: None
717 *---------------------------------------------------------------------------*/
718
THPSimpleAudioStop(void)719 void THPSimpleAudioStop(void)
720 {
721 SimpleControl.audioState = FALSE;
722
723 return;
724 }
725
726 /*---------------------------------------------------------------------------*
727 Name: THPSimpleLoadStop
728
729 Description: Stop preloading of movie.
730
731 Arguments: None
732
733 Returns: If successful, returns TRUE. If unsuccessful, returns FALSE.
734 *---------------------------------------------------------------------------*/
735
THPSimpleLoadStop(void)736 BOOL THPSimpleLoadStop(void)
737 {
738 s32 i;
739
740 if (SimpleControl.open && (SimpleControl.audioState == FALSE))
741 {
742 SimpleControl.preFetchState = FALSE;
743
744 // Cancel process if read in progress
745 if (SimpleControl.readProgress)
746 {
747 DVDCancel(&SimpleControl.fileInfo.cb);
748 SimpleControl.readProgress = FALSE;
749 }
750
751 // Initialize SimpleControl structure members
752 for (i = 0 ; i < READ_BUFFER_NUM ; i++)
753 {
754 SimpleControl.readBuffer[i].isValid = 0;
755 }
756
757 for (i = 0 ; i < AUDIO_BUFFER_NUM ; i++)
758 {
759 SimpleControl.audioBuffer[i].validSample = 0;
760 }
761
762 SimpleControl.textureSet.frameNumber = -1;
763 SimpleControl.curOffset = (s32)SimpleControl.header.movieDataOffsets;
764 SimpleControl.readSize = (s32)SimpleControl.header.firstFrameSize;
765 SimpleControl.readIndex = 0;
766 SimpleControl.totalReadFrame = 0;
767 SimpleControl.dvdError = FALSE;
768 SimpleControl.nextDecodeIndex = 0;
769 SimpleControl.audioDecodeIndex = 0;
770 SimpleControl.audioOutputIndex = 0;
771 SimpleControl.curVolume = SimpleControl.targetVolume;
772 SimpleControl.rampCount = 0;
773
774 return TRUE;
775 }
776
777 return FALSE;
778 }
779
780 /*---------------------------------------------------------------------------*
781 Name: THPSimpleDecode
782
783 Description: Decode THP frame data.
784 If the THP movie has no audio, the audioTrack argument
785 is ignored.
786
787 Arguments: audioTrack Specifies audio track number to be decoded.
788
789 Returns: If successful: THP_DECODE_OK.
790 If failed to decode THP video data: THP_VIDEO_DECODE_FAIL.
791 If no data in read buffer: THP_NO_READ_BUFFER.
792 If no open audio buffer: THP_NO_AUDIO_BUFFER.
793 If specified audio track number is invalid:
794 THP_INVALID_AUDIO_TRACK.
795 *---------------------------------------------------------------------------*/
796
THPSimpleDecode(s32 audioTrack)797 s32 THPSimpleDecode(s32 audioTrack)
798 {
799 BOOL old;
800 u32 i;
801 u8 *ptr;
802 u32 *compSizePtr, sample;
803
804 // Is load for buffer to be decoded complete?
805 if (BUFFER_VALID(SimpleControl.nextDecodeIndex))
806 {
807 compSizePtr = (u32 *)(GET_DECODE_BUFFER(SimpleControl) + 8);
808 ptr = GET_DECODE_BUFFER(SimpleControl) + SimpleControl.compInfo.numComponents * 4
809 + 8;
810
811 if (SimpleControl.audioExist)
812 {
813 if (audioTrack < 0 || audioTrack >= SimpleControl.audioInfo.sndNumTracks)
814 {
815 #ifdef _DEBUG
816 OSReport("Specified audio track number is invalid\n");
817 #endif
818 return THP_INVALID_AUDIO_TRACK;
819 }
820
821 // Is audio buffer free?
822 if (SimpleControl.audioBuffer[SimpleControl.audioDecodeIndex].validSample == 0)
823 {
824 for (i = 0 ; i < SimpleControl.compInfo.numComponents ; i++)
825 {
826 switch (SimpleControl.compInfo.frameComp[i])
827 {
828 // Decode THP video data
829 case THP_VIDEO_COMP:
830 if (VideoDecode(ptr) == FALSE)
831 {
832 return THP_VIDEO_DECODE_FAIL;
833 }
834 break;
835 // Decode THP audio data
836 case THP_AUDIO_COMP:
837 sample = THPAudioDecode(SimpleControl.audioBuffer[SimpleControl.audioDecodeIndex].buffer,
838 ptr + (*compSizePtr) * audioTrack,
839 THP_AUDIO_INTERLEAVE);
840 old = OSDisableInterrupts();
841 // Record decode sample number
842 SimpleControl.audioBuffer[SimpleControl.audioDecodeIndex].validSample = sample;
843 // Set pointer used during playback
844 SimpleControl.audioBuffer[SimpleControl.audioDecodeIndex].curPtr
845 = SimpleControl.audioBuffer[SimpleControl.audioDecodeIndex].buffer;
846 OSRestoreInterrupts(old);
847 SimpleControl.audioDecodeIndex++;
848 if (SimpleControl.audioDecodeIndex >= AUDIO_BUFFER_NUM)
849 {
850 SimpleControl.audioDecodeIndex = 0;
851 }
852 break;
853 }
854 ptr += *compSizePtr;
855 compSizePtr++;
856 }
857 }
858 else
859 {
860 #ifdef _DEBUG
861 OSReport("Cannot decode. There is no free audio buffer\n");
862 #endif
863 return THP_NO_AUDIO_BUFFER;
864 }
865 }
866 else
867 {
868 for (i = 0 ; i < SimpleControl.compInfo.numComponents ; i++)
869 {
870 switch (SimpleControl.compInfo.frameComp[i])
871 {
872 // Decode THP video data
873 case THP_VIDEO_COMP:
874 if (VideoDecode(ptr) == FALSE)
875 {
876 return THP_VIDEO_DECODE_FAIL;
877 }
878 break;
879 }
880 ptr += *compSizePtr;
881 compSizePtr++;
882 }
883 }
884
885 SimpleControl.readBuffer[SimpleControl.nextDecodeIndex].isValid = FALSE;
886 SimpleControl.nextDecodeIndex = NEXT_BUFFER(SimpleControl.nextDecodeIndex);
887
888 // Check if preload is stopped
889 CheckPrefetch();
890
891 return THP_DECODE_OK;
892 }
893
894 #ifdef _DEBUG
895 OSReport("Cannot decode. There is no valid buffer\n");
896 #endif
897
898 return THP_NO_READ_BUFFER;
899 }
900
901 /*---------------------------------------------------------------------------*
902 Name: VideoDecode
903
904 Description: Decode THP video data
905
906 Arguments: videoFrame Pointer to THP video frame
907
908 Returns: If successful, returns TRUE. If unsuccessful, returns FALSE.
909 *---------------------------------------------------------------------------*/
910
VideoDecode(u8 * videoFrame)911 static BOOL VideoDecode(u8 *videoFrame)
912 {
913 s32 ret;
914
915 ret = THPVideoDecode(videoFrame,
916 SimpleControl.textureSet.ytexture,
917 SimpleControl.textureSet.utexture,
918 SimpleControl.textureSet.vtexture,
919 SimpleControl.thpWork);
920
921 if (ret == THP_OK)
922 {
923 SimpleControl.textureSet.frameNumber
924 = SimpleControl.readBuffer[SimpleControl.nextDecodeIndex].frameNumber;
925
926 return TRUE;
927 }
928
929 return FALSE;
930 }
931
932 /*---------------------------------------------------------------------------*
933 Name: THPSimpleDrawCurrentFrame
934
935 Description: Render decoded THP video data
936
937 Arguments: rmode Pointer for currently set GXRenderModeObj
938 x Upper left x coordinate of TV screen for movie
939 display.
940 y Upper left y coordinate of TV screen for movie
941 display.
942 polygonW Width of polygon for movie display
943 polygonH Height of polygon for movie display
944
945 Returns: If able to draw, returns drawn frame #. If unable to draw,
946 returns -1.
947 *---------------------------------------------------------------------------*/
948
THPSimpleDrawCurrentFrame(GXRenderModeObj * rmode,u32 x,u32 y,u32 polygonW,u32 polygonH)949 s32 THPSimpleDrawCurrentFrame(GXRenderModeObj *rmode, u32 x, u32 y, u32 polygonW, u32 polygonH)
950 {
951 if (SimpleControl.textureSet.frameNumber >= 0)
952 {
953 THPGXYuv2RgbSetup(rmode);
954 THPGXYuv2RgbDraw( SimpleControl.textureSet.ytexture,
955 SimpleControl.textureSet.utexture,
956 SimpleControl.textureSet.vtexture,
957 (s16)x, (s16)y,
958 (s16)SimpleControl.videoInfo.xSize, (s16)SimpleControl.videoInfo.ySize,
959 (s16)polygonW, (s16)polygonH);
960 THPGXRestore();
961
962 return (s32)SimpleControl.textureSet.frameNumber;
963 }
964
965 return -1;
966 }
967
968 /*---------------------------------------------------------------------------*
969 Name: MixAudio
970
971 Description: Mix THP audio data in the specified buffer
972
973 Arguments: destination Buffer in which mixed data is stored
974 source Output buffer from audio library
975 sample Sample number of audio data to be mixed. Specified by stereo sample.
976
977 Returns: Sample number mixed.
978 *---------------------------------------------------------------------------*/
979
MixAudio(s16 * destination,s16 * source,u32 sample)980 static void MixAudio(s16 *destination, s16 *source, u32 sample)
981 {
982 u32 sampleNum, requestSample, i;
983 s32 mix;
984 s16 *dst, *libsrc, *thpsrc;
985 u16 attenuation;
986
987 // When mix with audio library output
988 if (source)
989 {
990 if (SimpleControl.open && (SimpleControl.audioState == TRUE) && SimpleControl.audioExist)
991 {
992 requestSample = sample;
993 dst = destination;
994 libsrc = source;
995
996 while (1)
997 {
998 if (SimpleControl.audioBuffer[SimpleControl.audioOutputIndex].validSample)
999 {
1000 if (SimpleControl.audioBuffer[SimpleControl.audioOutputIndex].validSample >= requestSample)
1001 {
1002 sampleNum = requestSample;
1003 }
1004 else
1005 {
1006 sampleNum = SimpleControl.audioBuffer[SimpleControl.audioOutputIndex].validSample;
1007 }
1008
1009 thpsrc = SimpleControl.audioBuffer[SimpleControl.audioOutputIndex].curPtr;
1010
1011 // Mixing
1012 for (i = 0 ; i < sampleNum ; i++)
1013 {
1014 if (SimpleControl.rampCount)
1015 {
1016 SimpleControl.rampCount--;
1017 SimpleControl.curVolume += SimpleControl.deltaVolume;
1018 }
1019 else
1020 {
1021 SimpleControl.curVolume = SimpleControl.targetVolume;
1022 }
1023
1024 attenuation = VolumeTable[(s32)SimpleControl.curVolume];
1025
1026 // Right
1027 mix = (*libsrc) + ((attenuation * (*thpsrc)) >> 15);
1028
1029 if (mix < -32768)
1030 {
1031 mix = -32768;
1032 }
1033 if (mix > 32767)
1034 {
1035 mix = 32767;
1036 }
1037
1038 *dst = (s16)mix;
1039 dst++;
1040 libsrc++;
1041 thpsrc++;
1042
1043 // Left
1044 mix = (*libsrc) + ((attenuation * (*thpsrc)) >> 15);
1045
1046 if (mix < -32768)
1047 {
1048 mix = -32768;
1049 }
1050 if (mix > 32767)
1051 {
1052 mix = 32767;
1053 }
1054
1055 *dst = (s16)mix;
1056 dst++;
1057 libsrc++;
1058 thpsrc++;
1059 }
1060
1061 requestSample -= sampleNum;
1062
1063 SimpleControl.audioBuffer[SimpleControl.audioOutputIndex].validSample -= sampleNum;
1064 SimpleControl.audioBuffer[SimpleControl.audioOutputIndex].curPtr = thpsrc;
1065
1066 if (SimpleControl.audioBuffer[SimpleControl.audioOutputIndex].validSample == 0)
1067 {
1068 SimpleControl.audioOutputIndex++;
1069 if (SimpleControl.audioOutputIndex >= AUDIO_BUFFER_NUM)
1070 {
1071 SimpleControl.audioOutputIndex = 0;
1072 }
1073 }
1074
1075 if (!requestSample)
1076 {
1077 break;
1078 }
1079 }
1080 else
1081 {
1082 memcpy(dst, libsrc, requestSample << 2);
1083 break;
1084 }
1085 }
1086 }
1087 else
1088 {
1089 memcpy(destination, source, sample << 2);
1090 }
1091 }
1092 else
1093 {
1094 if (SimpleControl.open && (SimpleControl.audioState == TRUE) && SimpleControl.audioExist)
1095 {
1096 requestSample = sample;
1097 dst = destination;
1098
1099 while (1)
1100 {
1101 if (SimpleControl.audioBuffer[SimpleControl.audioOutputIndex].validSample)
1102 {
1103 if (SimpleControl.audioBuffer[SimpleControl.audioOutputIndex].validSample >= requestSample)
1104 {
1105 sampleNum = requestSample;
1106 }
1107 else
1108 {
1109 sampleNum = SimpleControl.audioBuffer[SimpleControl.audioOutputIndex].validSample;
1110 }
1111
1112 thpsrc = SimpleControl.audioBuffer[SimpleControl.audioOutputIndex].curPtr;
1113
1114 // Mixing
1115 for (i = 0 ; i < sampleNum ; i++)
1116 {
1117 if (SimpleControl.rampCount)
1118 {
1119 SimpleControl.rampCount--;
1120 SimpleControl.curVolume += SimpleControl.deltaVolume;
1121 }
1122 else
1123 {
1124 SimpleControl.curVolume = SimpleControl.targetVolume;
1125 }
1126
1127 attenuation = VolumeTable[(s32)SimpleControl.curVolume];
1128
1129 // Right
1130 mix = (attenuation * (*thpsrc)) >> 15;
1131
1132 if (mix < -32768)
1133 {
1134 mix = -32768;
1135 }
1136 if (mix > 32767)
1137 {
1138 mix = 32767;
1139 }
1140
1141 *dst = (s16)mix;
1142 dst++;
1143 thpsrc++;
1144
1145 // Left
1146 mix = (attenuation * (*thpsrc)) >> 15;
1147
1148 if (mix < -32768)
1149 {
1150 mix = -32768;
1151 }
1152 if (mix > 32767)
1153 {
1154 mix = 32767;
1155 }
1156
1157 *dst = (s16)mix;
1158 dst++;
1159 thpsrc++;
1160 }
1161
1162 requestSample -= sampleNum;
1163
1164 SimpleControl.audioBuffer[SimpleControl.audioOutputIndex].validSample -= sampleNum;
1165 SimpleControl.audioBuffer[SimpleControl.audioOutputIndex].curPtr = thpsrc;
1166
1167 if (SimpleControl.audioBuffer[SimpleControl.audioOutputIndex].validSample == 0)
1168 {
1169 SimpleControl.audioOutputIndex++;
1170 if (SimpleControl.audioOutputIndex >= AUDIO_BUFFER_NUM)
1171 {
1172 SimpleControl.audioOutputIndex = 0;
1173 }
1174 }
1175
1176 if (!requestSample)
1177 {
1178 break;
1179 }
1180 }
1181 else
1182 {
1183 memset(dst, 0, requestSample << 2);
1184 break;
1185 }
1186 }
1187 }
1188 else
1189 {
1190 memset(destination, 0, sample << 2);
1191 }
1192 }
1193
1194 return;
1195 }
1196
1197 /*---------------------------------------------------------------------------*
1198 Name: THPSimpleGetVideoInfo
1199
1200 Description: Acquire THP movie video data
1201
1202 Arguments: videoInfo Pointer for THPVideoInfo structure
1203
1204 Returns: If successful, returns TRUE. If unsuccessful, returns FALSE.
1205 *---------------------------------------------------------------------------*/
1206
THPSimpleGetVideoInfo(THPVideoInfo * videoInfo)1207 BOOL THPSimpleGetVideoInfo(THPVideoInfo *videoInfo)
1208 {
1209 if (SimpleControl.open)
1210 {
1211 memcpy(videoInfo, &SimpleControl.videoInfo, sizeof(THPVideoInfo));
1212
1213 return TRUE;
1214 }
1215
1216 return FALSE;
1217 }
1218
1219 /*---------------------------------------------------------------------------*
1220 Name: THPSimpleGetAudioInfo
1221
1222 Description: Acquire THP movie audio data
1223
1224 Arguments: audioInfo Pointer for THPAudioInfo structure
1225
1226 Returns: If successful, returns TRUE. If unsuccessful, returns FALSE.
1227 *---------------------------------------------------------------------------*/
1228
THPSimpleGetAudioInfo(THPAudioInfo * audioInfo)1229 BOOL THPSimpleGetAudioInfo(THPAudioInfo *audioInfo)
1230 {
1231 if (SimpleControl.open)
1232 {
1233 memcpy(audioInfo, &SimpleControl.audioInfo, sizeof(THPAudioInfo));
1234
1235 return TRUE;
1236 }
1237
1238 return FALSE;
1239 }
1240
1241 /*---------------------------------------------------------------------------*
1242 Name: THPSimpleGetFrameRate
1243
1244 Description: Acquire THP movie frame rate
1245
1246 Arguments: None
1247
1248 Returns: If successful, returns THP file frame rate. If unsuccessful,
1249 returns 0.0f.
1250 *---------------------------------------------------------------------------*/
1251
THPSimpleGetFrameRate(void)1252 f32 THPSimpleGetFrameRate(void)
1253 {
1254 if (SimpleControl.open)
1255 {
1256
1257 return SimpleControl.header.frameRate;
1258 }
1259
1260 return 0.0f;
1261 }
1262
1263 /*---------------------------------------------------------------------------*
1264 Name: THPSimpleGetTotalFrame
1265
1266 Description: Acquire total frame number of THP movie.
1267
1268 Arguments: None
1269
1270 Returns: If successful, returns total frame number of THP file.
1271 If unsuccessful, returns 0.
1272 *---------------------------------------------------------------------------*/
1273
THPSimpleGetTotalFrame(void)1274 u32 THPSimpleGetTotalFrame(void)
1275 {
1276 if (SimpleControl.open)
1277 {
1278 return SimpleControl.header.numFrames;
1279 }
1280
1281 return 0;
1282 }
1283
1284 /*---------------------------------------------------------------------------*
1285 Name: THPAudioMixCallback
1286
1287 Description: AI callback function for player.
1288 Call callback functions of AX and MusyX internally when
1289 used with AX and MusyX, and mix output data of AX and
1290 MusyX with THP audio data.
1291
1292 Arguments: None
1293
1294 Returns: None
1295 *---------------------------------------------------------------------------*/
1296
THPAudioMixCallback(void)1297 static void THPAudioMixCallback(void)
1298 {
1299 BOOL old;
1300
1301 if (AudioSystem == THP_MODE_ALONE)
1302 {
1303 SoundBufferIndex ^= 1;
1304
1305 AIInitDMA((u32)SoundBuffer[SoundBufferIndex], BYTES_PER_AUDIO_FRAME);
1306
1307 old = OSEnableInterrupts();
1308
1309 MixAudio(SoundBuffer[SoundBufferIndex], NULL, SAMPLES_PER_AUDIO_FRAME);
1310
1311 DCFlushRange(SoundBuffer[SoundBufferIndex], BYTES_PER_AUDIO_FRAME);
1312
1313 OSRestoreInterrupts(old);
1314 }
1315 else
1316 {
1317 if (AudioSystem == THP_MODE_WITH_AX)
1318 {
1319 if (LastAudioBuffer)
1320 {
1321 CurAudioBuffer = LastAudioBuffer;
1322 }
1323
1324 OldAIDCallback();
1325
1326 LastAudioBuffer = (s16 *)OSPhysicalToCached(AIGetDMAStartAddr());
1327 }
1328 else
1329 {
1330 OldAIDCallback();
1331
1332 CurAudioBuffer = (s16 *)OSPhysicalToCached(AIGetDMAStartAddr());
1333 }
1334
1335 SoundBufferIndex ^= 1;
1336
1337 AIInitDMA((u32)SoundBuffer[SoundBufferIndex], BYTES_PER_AUDIO_FRAME);
1338
1339 old = OSEnableInterrupts();
1340
1341 if (CurAudioBuffer)
1342 {
1343 DCInvalidateRange(CurAudioBuffer, BYTES_PER_AUDIO_FRAME);
1344 }
1345
1346 MixAudio(SoundBuffer[SoundBufferIndex], CurAudioBuffer, SAMPLES_PER_AUDIO_FRAME);
1347
1348 DCFlushRange(SoundBuffer[SoundBufferIndex], BYTES_PER_AUDIO_FRAME);
1349
1350 OSRestoreInterrupts(old);
1351 }
1352 }
1353
1354 /*---------------------------------------------------------------------------*
1355 Name: THPSimpleSetVolume
1356
1357 Description: Set volume for player. Volume will be changed in the
1358 specified time.
1359
1360 Arguments: vol volume to be set (0 - 127)
1361 time Specify the time required to go from current volume to
1362 specified volume in units of milliseconds (0 - 60000)
1363
1364 Returns: If successful, returns TRUE. If unsuccessful, returns FALSE.
1365 *---------------------------------------------------------------------------*/
1366
THPSimpleSetVolume(s32 vol,s32 time)1367 BOOL THPSimpleSetVolume(s32 vol, s32 time)
1368 {
1369 BOOL old;
1370 s32 samplePerMs;
1371
1372 if (SimpleControl.open && SimpleControl.audioExist)
1373 {
1374 if (AIGetDSPSampleRate() == AI_SAMPLERATE_32KHZ)
1375 {
1376 samplePerMs = 32;
1377 }
1378 else
1379 {
1380 samplePerMs = 48;
1381 }
1382
1383 if (vol > 127)
1384 {
1385 vol = 127;
1386 }
1387
1388 if (vol < 0)
1389 {
1390 vol = 0;
1391 }
1392
1393 if (time > 60000)
1394 {
1395 time = 60000;
1396 }
1397
1398 if (time < 0)
1399 {
1400 time = 0;
1401 }
1402
1403 old = OSDisableInterrupts();
1404
1405 SimpleControl.targetVolume = (f32)vol;
1406
1407 if (time)
1408 {
1409 SimpleControl.rampCount = samplePerMs * time;
1410 SimpleControl.deltaVolume = (SimpleControl.targetVolume - SimpleControl.curVolume)
1411 / (f32)SimpleControl.rampCount;
1412 }
1413 else
1414 {
1415 SimpleControl.rampCount = 0;
1416 SimpleControl.curVolume = SimpleControl.targetVolume;
1417 }
1418
1419 OSRestoreInterrupts(old);
1420
1421 return TRUE;
1422 }
1423
1424 return FALSE;
1425 }
1426
1427 /*---------------------------------------------------------------------------*
1428 Name: THPSimpleGetVolume
1429
1430 Description: Acquire current volume of player
1431
1432 Arguments: None
1433
1434 Returns: Returns current playback volume if successful, and -1 if
1435 unsuccessful.
1436 *---------------------------------------------------------------------------*/
1437
THPSimpleGetVolume(void)1438 s32 THPSimpleGetVolume(void)
1439 {
1440 if (SimpleControl.open)
1441 {
1442 return (s32)SimpleControl.curVolume;
1443 }
1444
1445 return -1;
1446 }
1447