a5da3761eadddd3a90bc15f78b0b7cb8e3fd24e5
[libpicofe.git] / common / mp3_helix.c
1 // Some mp3 related code for Sega/Mega CD.
2 // Uses the Helix Fixed-point MP3 decoder
3
4 // (c) Copyright 2007, Grazvydas "notaz" Ignotas
5
6 #include <stdio.h>
7 #include <string.h>
8
9 #include "../../Pico/PicoInt.h"
10 #include "../../Pico/sound/mix.h"
11 #include "helix/pub/mp3dec.h"
12 #include "lprintf.h"
13
14 static short mp3_out_buffer[2*1152];
15 static HMP3Decoder mp3dec = 0;
16 static int mp3_buffer_offs = 0;
17
18
19 static int try_get_header(unsigned char *buff, MP3FrameInfo *fi)
20 {
21         int ret, offs1, offs = 0;
22
23         while (1)
24         {
25                 offs1 = MP3FindSyncWord(buff + offs, 2048 - offs);
26                 if (offs1 < 0) return -2;
27                 offs += offs1;
28                 if (2048 - offs < 4) return -3;
29
30                 // printf("trying header %08x\n", *(int *)(buff + offs));
31
32                 ret = MP3GetNextFrameInfo(mp3dec, fi, buff + offs);
33                 if (ret == 0 && fi->bitrate != 0) break;
34                 offs++;
35         }
36
37         return ret;
38 }
39
40 int mp3_get_bitrate(FILE *f, int len)
41 {
42         unsigned char buff[2048];
43         MP3FrameInfo fi;
44         int ret;
45
46         memset(buff, 0, 2048);
47
48         if (!mp3dec) mp3dec = MP3InitDecoder();
49
50         fseek(f, 0, SEEK_SET);
51         ret = fread(buff, 1, 2048, f);
52         fseek(f, 0, SEEK_SET);
53         if (ret <= 0) return -1;
54
55         ret = try_get_header(buff, &fi);
56         if (ret != 0 || fi.bitrate == 0) {
57                 // try to read somewhere around the middle
58                 fseek(f, len>>1, SEEK_SET);
59                 fread(buff, 1, 2048, f);
60                 fseek(f, 0, SEEK_SET);
61                 ret = try_get_header(buff, &fi);
62         }
63         if (ret != 0) return ret;
64
65         // printf("bitrate: %i\n", fi.bitrate / 1000);
66
67         return fi.bitrate / 1000;
68 }
69
70
71 #ifdef __GP2X__
72
73 #include "../gp2x/code940/940shared.h"
74
75 extern _940_ctl_t *shared_ctl;
76 extern unsigned char *mp3_mem;
77
78 static int mp3_decode(void)
79 {
80         // tried copying this to cached mem, no improvement noticed
81         int mp3_offs = shared_ctl->mp3_offs;
82         unsigned char *readPtr = mp3_mem + mp3_offs;
83         int bytesLeft = shared_ctl->mp3_len - mp3_offs;
84         int offset; // frame offset from readPtr
85         int err;
86
87         if (bytesLeft <= 0) return 1; // EOF, nothing to do
88
89         offset = MP3FindSyncWord(readPtr, bytesLeft);
90         if (offset < 0) {
91                 shared_ctl->mp3_offs = shared_ctl->mp3_len;
92                 return 1; // EOF
93         }
94         readPtr += offset;
95         bytesLeft -= offset;
96
97         err = MP3Decode(mp3dec, &readPtr, &bytesLeft, mp3_out_buffer, 0);
98         if (err) {
99                 if (err == ERR_MP3_INDATA_UNDERFLOW) {
100                         shared_ctl->mp3_offs = shared_ctl->mp3_len; // EOF
101                         return 1;
102                 } else if (err <= -6 && err >= -12) {
103                         // ERR_MP3_INVALID_FRAMEHEADER, ERR_MP3_INVALID_*
104                         // just try to skip the offending frame..
105                         readPtr++;
106                 }
107                 shared_ctl->mp3_errors++;
108                 shared_ctl->mp3_lasterr = err;
109         }
110         shared_ctl->mp3_offs = readPtr - mp3_mem;
111         return 0;
112 }
113
114 void mp3_start_local(void)
115 {
116         mp3_buffer_offs = 0;
117         mp3_decode();
118 }
119
120 #define mp3_update mp3_update_local
121
122 #else
123
124 static FILE *mp3_current_file = NULL;
125 static int mp3_file_len = 0, mp3_file_pos = 0;
126 static unsigned char mp3_input_buffer[2*1024];
127
128 static int mp3_decode(void)
129 {
130         unsigned char *readPtr;
131         int bytesLeft;
132         int offset; // mp3 frame offset from readPtr
133         int err;
134
135         do
136         {
137                 if (mp3_file_pos >= mp3_file_len) return 1; // EOF, nothing to do
138
139                 fseek(mp3_current_file, mp3_file_pos, SEEK_SET);
140                 bytesLeft = fread(mp3_input_buffer, 1, sizeof(mp3_input_buffer), mp3_current_file);
141
142                 offset = MP3FindSyncWord(mp3_input_buffer, bytesLeft);
143                 if (offset < 0) {
144                         //lprintf("MP3FindSyncWord (%i/%i) err %i\n", mp3_file_pos, mp3_file_len, offset);
145                         mp3_file_pos = mp3_file_len;
146                         return 1; // EOF
147                 }
148                 readPtr = mp3_input_buffer + offset;
149                 bytesLeft -= offset;
150
151                 err = MP3Decode(mp3dec, &readPtr, &bytesLeft, mp3_out_buffer, 0);
152                 if (err) {
153                         //lprintf("MP3Decode err (%i/%i) %i\n", mp3_file_pos, mp3_file_len, err);
154                         if (err == ERR_MP3_INDATA_UNDERFLOW) {
155                                 if (offset == 0)
156                                         // something's really wrong here, frame had to fit
157                                         mp3_file_pos = mp3_file_len;
158                                 else
159                                         mp3_file_pos += offset;
160                                 continue;
161                         } else if (err <= -6 && err >= -12) {
162                                 // ERR_MP3_INVALID_FRAMEHEADER, ERR_MP3_INVALID_*
163                                 // just try to skip the offending frame..
164                                 mp3_file_pos += offset + 1;
165                                 continue;
166                         }
167                         mp3_file_pos = mp3_file_len;
168                         return 1;
169                 }
170                 mp3_file_pos += readPtr - mp3_input_buffer;
171         }
172         while (0);
173
174         return 0;
175 }
176
177 void mp3_start_play(FILE *f, int pos)
178 {
179         mp3_file_len = mp3_file_pos = 0;
180         mp3_current_file = NULL;
181         mp3_buffer_offs = 0;
182
183         if (!(PicoOpt&0x800) || f == NULL) // cdda disabled or no file?
184                 return;
185
186         //lprintf("mp3_start_play %p %i\n", f, pos);
187
188         mp3_current_file = f;
189         fseek(f, 0, SEEK_END);
190         mp3_file_len = ftell(f);
191
192         // seek..
193         if (pos) {
194                 mp3_file_pos = (mp3_file_len << 6) >> 10;
195                 mp3_file_pos *= pos;
196                 mp3_file_pos >>= 6;
197         }
198
199         mp3_decode();
200 }
201
202 int mp3_get_offset(void)
203 {
204         unsigned int offs1024 = 0;
205         int cdda_on;
206
207         cdda_on = (PicoMCD & 1) && (PicoOpt&0x800) && !(Pico_mcd->s68k_regs[0x36] & 1) &&
208                         (Pico_mcd->scd.Status_CDC & 1) && mp3_current_file != NULL;
209
210         if (cdda_on) {
211                 offs1024  = mp3_file_pos << 7;
212                 offs1024 /= mp3_file_len >> 3;
213         }
214         //lprintf("mp3_get_offset offs1024=%u (%i/%i)\n", offs1024, mp3_file_pos, mp3_file_len);
215
216         return offs1024;
217 }
218
219 #endif // ifndef __GP2X__
220
221 void mp3_update(int *buffer, int length, int stereo)
222 {
223         int length_mp3, shr = 0;
224         void (*mix_samples)(int *dest_buf, short *mp3_buf, int count) = mix_16h_to_32;
225
226 #ifndef __GP2X__
227         if (mp3_current_file == NULL || mp3_file_pos >= mp3_file_len) return; // no file / EOF
228 #endif
229
230         length_mp3 = length;
231         if (PsndRate == 22050) { mix_samples = mix_16h_to_32_s1; length_mp3 <<= 1; shr = 1; }
232         else if (PsndRate == 11025) { mix_samples = mix_16h_to_32_s2; length_mp3 <<= 2; shr = 2; }
233
234         if (1152 - mp3_buffer_offs >= length_mp3) {
235                 mix_samples(buffer, mp3_out_buffer + mp3_buffer_offs*2, length<<1);
236
237                 mp3_buffer_offs += length_mp3;
238         } else {
239                 int ret, left = 1152 - mp3_buffer_offs;
240
241                 mix_samples(buffer, mp3_out_buffer + mp3_buffer_offs*2, (left>>shr)<<1);
242                 ret = mp3_decode();
243                 if (ret == 0) {
244                         mp3_buffer_offs = length_mp3 - left;
245                         mix_samples(buffer + ((left>>shr)<<1), mp3_out_buffer, (mp3_buffer_offs>>shr)<<1);
246                 } else
247                         mp3_buffer_offs = 0;
248         }
249 }
250
251