Mercurial > SDL_sound_CoreAudio
annotate decoders/mpglib.c @ 348:4b46cf359136
Updated.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Thu, 23 May 2002 12:51:01 +0000 |
parents | 31cc49d7d0ce |
children | 069ce624d6cf |
rev | line source |
---|---|
261 | 1 /* |
2 * SDL_sound -- An abstract sound format decoding API. | |
3 * Copyright (C) 2001 Ryan C. Gordon. | |
4 * | |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Lesser General Public | |
7 * License as published by the Free Software Foundation; either | |
8 * version 2.1 of the License, or (at your option) any later version. | |
9 * | |
10 * This library is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * Lesser General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Lesser General Public | |
16 * License along with this library; if not, write to the Free Software | |
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 */ | |
19 | |
20 /* | |
21 * MPGLIB decoder for SDL_sound. This is a very lightweight MP3 decoder, | |
22 * which is included with the SDL_sound source, so that it doesn't rely on | |
23 * unnecessary external libraries. | |
24 * | |
25 * The SMPEG decoder plays back more forms of MPEGs, and may behave better or | |
26 * worse under various conditions. mpglib is (apparently) more efficient than | |
27 * SMPEG, and, again, doesn't need an external library. You should test both | |
28 * decoders and use what you find works best for you. | |
29 * | |
306
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
293
diff
changeset
|
30 * mpglib is an LGPL'd portion of mpg123, which can be found in its original |
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
293
diff
changeset
|
31 * form at: http://www.mpg123.de/ |
261 | 32 * |
33 * Please see the file COPYING in the source's root directory. The included | |
34 * source code for mpglib falls under the LGPL, which is the same license as | |
35 * SDL_sound (so you can consider it a single work). | |
36 * | |
37 * This file written by Ryan C. Gordon. (icculus@clutteredmind.org) | |
38 */ | |
39 | |
40 #if HAVE_CONFIG_H | |
41 # include <config.h> | |
42 #endif | |
43 | |
44 #ifdef SOUND_SUPPORTS_MPGLIB | |
45 | |
46 #include <stdio.h> | |
47 #include <stdlib.h> | |
48 #include <string.h> | |
49 #include <assert.h> | |
50 #include "mpglib/mpg123_sdlsound.h" | |
51 #include "mpglib/mpglib_sdlsound.h" | |
52 | |
53 #include "SDL_sound.h" | |
54 | |
55 #define __SDL_SOUND_INTERNAL__ | |
56 #include "SDL_sound_internal.h" | |
57 | |
58 static int MPGLIB_init(void); | |
59 static void MPGLIB_quit(void); | |
60 static int MPGLIB_open(Sound_Sample *sample, const char *ext); | |
61 static void MPGLIB_close(Sound_Sample *sample); | |
62 static Uint32 MPGLIB_read(Sound_Sample *sample); | |
63 static int MPGLIB_rewind(Sound_Sample *sample); | |
306
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
293
diff
changeset
|
64 static int MPGLIB_seek(Sound_Sample *sample, Uint32 ms); |
261 | 65 |
66 static const char *extensions_mpglib[] = { "MP3", NULL }; | |
67 const Sound_DecoderFunctions __Sound_DecoderFunctions_MPGLIB = | |
68 { | |
69 { | |
70 extensions_mpglib, | |
71 "MP3 decoding via internal mpglib", | |
72 "Ryan C. Gordon <icculus@clutteredmind.org>", | |
73 "http://www.icculus.org/SDL_sound/" | |
74 }, | |
75 | |
76 MPGLIB_init, /* init() method */ | |
77 MPGLIB_quit, /* quit() method */ | |
78 MPGLIB_open, /* open() method */ | |
79 MPGLIB_close, /* close() method */ | |
80 MPGLIB_read, /* read() method */ | |
306
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
293
diff
changeset
|
81 MPGLIB_rewind, /* rewind() method */ |
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
293
diff
changeset
|
82 MPGLIB_seek /* seek() method */ |
261 | 83 }; |
84 | |
85 | |
86 /* this is what we store in our internal->decoder_private field... */ | |
87 typedef struct | |
88 { | |
89 struct mpstr mp; | |
90 Uint8 inbuf[16384]; | |
91 Uint8 outbuf[8192]; | |
92 int outleft; | |
93 int outpos; | |
94 } mpglib_t; | |
95 | |
96 | |
97 | |
98 static int MPGLIB_init(void) | |
99 { | |
100 return(1); /* always succeeds. */ | |
101 } /* MPGLIB_init */ | |
102 | |
103 | |
104 static void MPGLIB_quit(void) | |
105 { | |
106 /* it's a no-op. */ | |
107 } /* MPGLIB_quit */ | |
108 | |
109 | |
110 static int MPGLIB_open(Sound_Sample *sample, const char *ext) | |
111 { | |
112 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
113 mpglib_t *mpg = NULL; | |
293
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
114 int rc; |
261 | 115 |
116 /* | |
117 * If I understand things correctly, MP3 files don't really have any | |
118 * magic header we can check for. The MP3 player is expected to just | |
119 * pick the first thing that looks like a valid frame and start | |
120 * playing from there. | |
121 * | |
122 * So here's what we do: If the caller insists that this is really | |
123 * MP3 we'll take his word for it. Otherwise, use the same test as | |
124 * SDL_mixer does and check if the stream starts with something that | |
125 * looks like a frame. | |
126 * | |
127 * A frame begins with 11 bits of frame sync (all bits must be set), | |
128 * followed by a two-bit MPEG Audio version ID: | |
129 * | |
130 * 00 - MPEG Version 2.5 (later extension of MPEG 2) | |
131 * 01 - reserved | |
132 * 10 - MPEG Version 2 (ISO/IEC 13818-3) | |
133 * 11 - MPEG Version 1 (ISO/IEC 11172-3) | |
134 * | |
135 * Apparently we don't handle MPEG Version 2.5. | |
136 */ | |
137 if (__Sound_strcasecmp(ext, "MP3") != 0) | |
138 { | |
139 Uint8 mp3_magic[2]; | |
140 | |
141 if (SDL_RWread(internal->rw, mp3_magic, sizeof (mp3_magic), 1) != 1) | |
293
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
142 BAIL_MACRO("MP3: Could not read MP3 magic.", 0); |
261 | 143 |
144 if (mp3_magic[0] != 0xFF || (mp3_magic[1] & 0xF0) != 0xF0) | |
293
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
145 BAIL_MACRO("MP3: Not an MP3 stream.", 0); |
261 | 146 |
147 /* !!! FIXME: If the seek fails, we'll probably miss a frame */ | |
148 SDL_RWseek(internal->rw, -sizeof (mp3_magic), SEEK_CUR); | |
149 } /* if */ | |
150 | |
151 mpg = (mpglib_t *) malloc(sizeof (mpglib_t)); | |
152 BAIL_IF_MACRO(mpg == NULL, ERR_OUT_OF_MEMORY, 0); | |
293
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
153 memset(mpg, '\0', sizeof (mpglib_t)); |
261 | 154 InitMP3(&mpg->mp); |
155 | |
293
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
156 rc = SDL_RWread(internal->rw, mpg->inbuf, 1, sizeof (mpg->inbuf)); |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
157 if (rc <= 0) |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
158 { |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
159 free(mpg); |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
160 BAIL_MACRO("MPGLIB: Failed to read any data at all", 0); |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
161 } /* if */ |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
162 |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
163 if (decodeMP3(&mpg->mp, mpg->inbuf, rc, |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
164 mpg->outbuf, sizeof (mpg->outbuf), |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
165 &mpg->outleft) == MP3_ERR) |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
166 { |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
167 free(mpg); |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
168 BAIL_MACRO("MPGLIB: Not an MP3 stream?", 0); |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
169 } /* if */ |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
170 |
261 | 171 SNDDBG(("MPGLIB: Accepting data stream.\n")); |
172 | |
271
493dd0173f3d
Added a FIXME comment.
Ryan C. Gordon <icculus@icculus.org>
parents:
268
diff
changeset
|
173 /* !!! FIXME: Determine what format mpglib is spitting out... */ |
261 | 174 internal->decoder_private = mpg; |
293
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
175 sample->actual.rate = mpglib_freqs[mpg->mp.fr.sampling_frequency]; |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
176 sample->actual.channels = mpg->mp.fr.stereo; |
322 | 177 sample->actual.format = AUDIO_S16SYS; /* !!! FIXME: Is this right? */ |
261 | 178 sample->flags = SOUND_SAMPLEFLAG_NONE; |
179 | |
180 return(1); /* we'll handle this data. */ | |
181 } /* MPGLIB_open */ | |
182 | |
183 | |
184 static void MPGLIB_close(Sound_Sample *sample) | |
185 { | |
186 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
187 mpglib_t *mpg = ((mpglib_t *) internal->decoder_private); | |
188 ExitMP3(&mpg->mp); | |
189 free(mpg); | |
190 } /* MPGLIB_close */ | |
191 | |
192 | |
193 static Uint32 MPGLIB_read(Sound_Sample *sample) | |
194 { | |
195 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
196 mpglib_t *mpg = ((mpglib_t *) internal->decoder_private); | |
197 Uint32 bw = 0; | |
198 int rc; | |
199 | |
200 while (bw < internal->buffer_size) | |
201 { | |
202 if (mpg->outleft > 0) | |
203 { | |
204 Uint16 cpysize = internal->buffer_size - bw; | |
205 if (cpysize > mpg->outleft) | |
206 cpysize = mpg->outleft; | |
207 memcpy(((Uint8 *) internal->buffer) + bw, | |
208 mpg->outbuf + mpg->outpos, cpysize); | |
209 bw += cpysize; | |
210 mpg->outpos += cpysize; | |
211 mpg->outleft -= cpysize; | |
212 continue; | |
213 } /* if */ | |
214 | |
215 /* need to decode more from the MP3 stream... */ | |
216 mpg->outpos = 0; | |
217 rc = decodeMP3(&mpg->mp, NULL, 0, mpg->outbuf, | |
218 sizeof (mpg->outbuf), &mpg->outleft); | |
219 if (rc == MP3_ERR) | |
220 { | |
221 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
222 return(bw); | |
223 } /* if */ | |
224 | |
225 else if (rc == MP3_NEED_MORE) | |
226 { | |
227 rc = SDL_RWread(internal->rw, mpg->inbuf, 1, sizeof (mpg->inbuf)); | |
228 if (rc == -1) | |
229 { | |
230 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
231 return(bw); | |
232 } /* if */ | |
268
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
233 |
261 | 234 else if (rc == 0) |
235 { | |
236 sample->flags |= SOUND_SAMPLEFLAG_EOF; | |
237 return(bw); | |
238 } /* else if */ | |
239 | |
268
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
240 /* make sure there isn't an ID3 tag. */ |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
241 /* |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
242 * !!! FIXME: This can fail under the following circumstances: |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
243 * First, if there's the sequence "TAG" 128 bytes from the end |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
244 * of a read that isn't the EOF. This is unlikely. |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
245 * Second, if the TAG sequence is split between two reads (ie, |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
246 * the last byte of a read is 'T', and the next read is the |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
247 * final 127 bytes of the stream, being the rest of the ID3 tag). |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
248 * While this is more likely, it's still not very likely at all. |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
249 * Still, something SHOULD be done about this. |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
250 * ID3v2 tags are more complex, too, not to mention LYRICS tags, |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
251 * etc, which aren't handled, either. Hey, this IS meant to be |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
252 * a lightweight decoder. Use SMPEG if you need an all-purpose |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
253 * decoder. mpglib really assumes you control all your assets. |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
254 */ |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
255 if (rc >= 128) |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
256 { |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
257 Uint8 *ptr = &mpg->inbuf[rc - 128]; |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
258 if ((ptr[0] == 'T') && (ptr[1] == 'A') && (ptr[2] == 'G')) |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
259 rc -= 128; /* disregard it. */ |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
260 } /* if */ |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
261 |
263 | 262 rc = decodeMP3(&mpg->mp, mpg->inbuf, rc, |
268
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
263 mpg->outbuf, sizeof (mpg->outbuf), |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
264 &mpg->outleft); |
261 | 265 if (rc == MP3_ERR) |
266 { | |
267 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
268 return(bw); | |
269 } /* if */ | |
270 } /* else if */ | |
271 } /* while */ | |
272 | |
273 return(bw); | |
274 } /* MPGLIB_read */ | |
275 | |
276 | |
277 static int MPGLIB_rewind(Sound_Sample *sample) | |
278 { | |
279 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
280 mpglib_t *mpg = ((mpglib_t *) internal->decoder_private); | |
281 BAIL_IF_MACRO(SDL_RWseek(internal->rw, 0, SEEK_SET) != 0, ERR_IO_ERROR, 0); | |
282 | |
283 /* this is just resetting some fields in a structure; it's very fast. */ | |
284 ExitMP3(&mpg->mp); | |
285 InitMP3(&mpg->mp); | |
286 mpg->outpos = mpg->outleft = 0; | |
287 return(1); | |
288 } /* MPGLIB_rewind */ | |
289 | |
290 | |
306
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
293
diff
changeset
|
291 static int MPGLIB_seek(Sound_Sample *sample, Uint32 ms) |
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
293
diff
changeset
|
292 { |
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
293
diff
changeset
|
293 BAIL_MACRO("!!! FIXME: Not implemented", 0); |
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
293
diff
changeset
|
294 } /* MPGLIB_seek */ |
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
293
diff
changeset
|
295 |
261 | 296 #endif /* SOUND_SUPPORTS_MPGLIB */ |
297 | |
298 | |
299 /* end of mpglib.c ... */ | |
300 |