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