f3650bf2eab6c72ddb2fa7fb8e867725e5947c14
[picodrive.git] / platform / common / mp3_helix.c
1 /*
2  * Some mp3 related code for Sega/Mega CD.
3  * Uses the Helix Fixed-point MP3 decoder
4  * (C) notaz, 2007-2009
5  *
6  * This work is licensed under the terms of MAME license.
7  * See COPYING file in the top-level directory.
8  */
9
10 #include <stdio.h>
11 #include <string.h>
12 #include <dlfcn.h>
13
14 #include <pico/pico_int.h>
15 /*#include "helix/pub/mp3dec.h"*/
16 #include "mp3.h"
17
18 #ifndef _MP3DEC_H
19 typedef void *HMP3Decoder;
20 #define ERR_MP3_INDATA_UNDERFLOW -1
21 #define ERR_MP3_MAINDATA_UNDERFLOW -2
22 #endif
23
24 static void *mp3dec;
25 static unsigned char mp3_input_buffer[2 * 1024];
26
27 #ifdef __GP2X__
28 #define mp3dec_decode _mp3dec_decode
29 #define mp3dec_start _mp3dec_start
30 #endif
31
32 static void *libhelix;
33 HMP3Decoder (*p_MP3InitDecoder)(void);
34 void (*p_MP3FreeDecoder)(HMP3Decoder);
35 int (*p_MP3Decode)(HMP3Decoder, unsigned char **, int *, short *, int);
36
37 int mp3dec_decode(FILE *f, int *file_pos, int file_len)
38 {
39         unsigned char *readPtr;
40         int bytesLeft;
41         int offset; // mp3 frame offset from readPtr
42         int had_err;
43         int err = 0;
44         int retry = 3;
45
46         do
47         {
48                 if (*file_pos >= file_len)
49                         return 1; /* EOF, nothing to do */
50
51                 fseek(f, *file_pos, SEEK_SET);
52                 bytesLeft = fread(mp3_input_buffer, 1, sizeof(mp3_input_buffer), f);
53
54                 offset = mp3_find_sync_word(mp3_input_buffer, bytesLeft);
55                 if (offset < 0) {
56                         lprintf("find_sync_word (%i/%i) err %i\n",
57                                 *file_pos, file_len, offset);
58                         *file_pos = file_len;
59                         return 1; // EOF
60                 }
61                 readPtr = mp3_input_buffer + offset;
62                 bytesLeft -= offset;
63
64                 had_err = err;
65                 err = p_MP3Decode(mp3dec, &readPtr, &bytesLeft, cdda_out_buffer, 0);
66                 if (err) {
67                         if (err == ERR_MP3_MAINDATA_UNDERFLOW && !had_err) {
68                                 // just need another frame
69                                 *file_pos += readPtr - mp3_input_buffer;
70                                 continue;
71                         }
72                         if (err == ERR_MP3_INDATA_UNDERFLOW && !had_err) {
73                                 if (offset == 0)
74                                         // something's really wrong here, frame had to fit
75                                         *file_pos = file_len;
76                                 else
77                                         *file_pos += offset;
78                                 continue;
79                         }
80                         if (-12 <= err && err <= -6) {
81                                 // ERR_MP3_INVALID_FRAMEHEADER, ERR_MP3_INVALID_*
82                                 // just try to skip the offending frame..
83                                 *file_pos += offset + 1;
84                                 continue;
85                         }
86                         lprintf("MP3Decode err (%i/%i) %i\n",
87                                 *file_pos, file_len, err);
88                         *file_pos = file_len;
89                         return 1;
90                 }
91                 *file_pos += readPtr - mp3_input_buffer;
92         }
93         while (err && --retry > 0);
94
95         return !!err;
96 }
97
98 int mp3dec_start(FILE *f, int fpos_start)
99 {
100         if (libhelix == NULL) {
101                 libhelix = dlopen("./libhelix.so", RTLD_NOW);
102                 if (libhelix == NULL) {
103                         lprintf("mp3dec: load libhelix.so: %s\n", dlerror());
104                         return -1;
105                 }
106
107                 p_MP3InitDecoder = dlsym(libhelix, "MP3InitDecoder");
108                 p_MP3FreeDecoder = dlsym(libhelix, "MP3FreeDecoder");
109                 p_MP3Decode = dlsym(libhelix, "MP3Decode");
110
111                 if (p_MP3InitDecoder == NULL || p_MP3FreeDecoder == NULL
112                     || p_MP3Decode == NULL)
113                 {
114                         lprintf("mp3dec: missing symbol(s) in libhelix.so\n");
115                         dlclose(libhelix);
116                         libhelix = NULL;
117                         return -1;
118                 }
119         }
120
121         // must re-init decoder for new track
122         if (mp3dec)
123                 p_MP3FreeDecoder(mp3dec);
124         mp3dec = p_MP3InitDecoder();
125
126         return (mp3dec == 0) ? -1 : 0;
127 }