Mercurial > SDL_sound_CoreAudio
annotate decoders/mpglib.c @ 413:4a0829038968
Updated.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Thu, 22 Aug 2002 09:39:28 +0000 |
parents | cbb15ecf423a |
children | c66080364dff ef0d98d55865 |
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 "mpglib/mpg123_sdlsound.h" | |
50 #include "mpglib/mpglib_sdlsound.h" | |
51 | |
52 #include "SDL_sound.h" | |
53 | |
54 #define __SDL_SOUND_INTERNAL__ | |
55 #include "SDL_sound_internal.h" | |
56 | |
57 static int MPGLIB_init(void); | |
58 static void MPGLIB_quit(void); | |
59 static int MPGLIB_open(Sound_Sample *sample, const char *ext); | |
60 static void MPGLIB_close(Sound_Sample *sample); | |
61 static Uint32 MPGLIB_read(Sound_Sample *sample); | |
62 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
|
63 static int MPGLIB_seek(Sound_Sample *sample, Uint32 ms); |
261 | 64 |
65 static const char *extensions_mpglib[] = { "MP3", NULL }; | |
66 const Sound_DecoderFunctions __Sound_DecoderFunctions_MPGLIB = | |
67 { | |
68 { | |
69 extensions_mpglib, | |
70 "MP3 decoding via internal mpglib", | |
71 "Ryan C. Gordon <icculus@clutteredmind.org>", | |
72 "http://www.icculus.org/SDL_sound/" | |
73 }, | |
74 | |
75 MPGLIB_init, /* init() method */ | |
76 MPGLIB_quit, /* quit() method */ | |
77 MPGLIB_open, /* open() method */ | |
78 MPGLIB_close, /* close() method */ | |
79 MPGLIB_read, /* read() method */ | |
306
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
293
diff
changeset
|
80 MPGLIB_rewind, /* rewind() method */ |
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
293
diff
changeset
|
81 MPGLIB_seek /* seek() method */ |
261 | 82 }; |
83 | |
84 | |
85 /* this is what we store in our internal->decoder_private field... */ | |
86 typedef struct | |
87 { | |
88 struct mpstr mp; | |
89 Uint8 inbuf[16384]; | |
90 Uint8 outbuf[8192]; | |
91 int outleft; | |
92 int outpos; | |
93 } mpglib_t; | |
94 | |
95 | |
96 | |
97 static int MPGLIB_init(void) | |
98 { | |
99 return(1); /* always succeeds. */ | |
100 } /* MPGLIB_init */ | |
101 | |
102 | |
103 static void MPGLIB_quit(void) | |
104 { | |
105 /* it's a no-op. */ | |
106 } /* MPGLIB_quit */ | |
107 | |
108 | |
109 static int MPGLIB_open(Sound_Sample *sample, const char *ext) | |
110 { | |
111 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
112 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
|
113 int rc; |
261 | 114 |
115 /* | |
116 * If I understand things correctly, MP3 files don't really have any | |
117 * magic header we can check for. The MP3 player is expected to just | |
118 * pick the first thing that looks like a valid frame and start | |
119 * playing from there. | |
120 * | |
121 * So here's what we do: If the caller insists that this is really | |
122 * MP3 we'll take his word for it. Otherwise, use the same test as | |
123 * SDL_mixer does and check if the stream starts with something that | |
124 * looks like a frame. | |
125 * | |
126 * A frame begins with 11 bits of frame sync (all bits must be set), | |
127 * followed by a two-bit MPEG Audio version ID: | |
128 * | |
129 * 00 - MPEG Version 2.5 (later extension of MPEG 2) | |
130 * 01 - reserved | |
131 * 10 - MPEG Version 2 (ISO/IEC 13818-3) | |
132 * 11 - MPEG Version 1 (ISO/IEC 11172-3) | |
133 * | |
134 * Apparently we don't handle MPEG Version 2.5. | |
135 */ | |
136 if (__Sound_strcasecmp(ext, "MP3") != 0) | |
137 { | |
138 Uint8 mp3_magic[2]; | |
139 | |
140 if (SDL_RWread(internal->rw, mp3_magic, sizeof (mp3_magic), 1) != 1) | |
358
f11c10ffa31a
Fixed some debug messages.
Ryan C. Gordon <icculus@icculus.org>
parents:
351
diff
changeset
|
141 BAIL_MACRO("MPGLIB: Could not read MP3 magic.", 0); |
261 | 142 |
143 if (mp3_magic[0] != 0xFF || (mp3_magic[1] & 0xF0) != 0xF0) | |
358
f11c10ffa31a
Fixed some debug messages.
Ryan C. Gordon <icculus@icculus.org>
parents:
351
diff
changeset
|
144 BAIL_MACRO("MPGLIB: Not an MP3 stream.", 0); |
261 | 145 |
351 | 146 /* If the seek fails, we'll probably miss a frame, but oh well. */ |
261 | 147 SDL_RWseek(internal->rw, -sizeof (mp3_magic), SEEK_CUR); |
148 } /* if */ | |
149 | |
150 mpg = (mpglib_t *) malloc(sizeof (mpglib_t)); | |
151 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
|
152 memset(mpg, '\0', sizeof (mpglib_t)); |
261 | 153 InitMP3(&mpg->mp); |
154 | |
293
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
155 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
|
156 if (rc <= 0) |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
157 { |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
158 free(mpg); |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
159 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
|
160 } /* if */ |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
161 |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
162 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
|
163 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
|
164 &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
|
165 { |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
166 free(mpg); |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
167 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
|
168 } /* if */ |
ee6e1f8bfae9
Can determine audio format correctly (mostly correctly?) now. Other cleanups.
Ryan C. Gordon <icculus@icculus.org>
parents:
271
diff
changeset
|
169 |
261 | 170 SNDDBG(("MPGLIB: Accepting data stream.\n")); |
171 | |
172 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
|
173 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
|
174 sample->actual.channels = mpg->mp.fr.stereo; |
351 | 175 sample->actual.format = AUDIO_S16SYS; |
261 | 176 sample->flags = SOUND_SAMPLEFLAG_NONE; |
177 | |
178 return(1); /* we'll handle this data. */ | |
179 } /* MPGLIB_open */ | |
180 | |
181 | |
182 static void MPGLIB_close(Sound_Sample *sample) | |
183 { | |
184 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
185 mpglib_t *mpg = ((mpglib_t *) internal->decoder_private); | |
186 ExitMP3(&mpg->mp); | |
187 free(mpg); | |
188 } /* MPGLIB_close */ | |
189 | |
190 | |
191 static Uint32 MPGLIB_read(Sound_Sample *sample) | |
192 { | |
193 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
194 mpglib_t *mpg = ((mpglib_t *) internal->decoder_private); | |
195 Uint32 bw = 0; | |
196 int rc; | |
197 | |
198 while (bw < internal->buffer_size) | |
199 { | |
200 if (mpg->outleft > 0) | |
201 { | |
202 Uint16 cpysize = internal->buffer_size - bw; | |
203 if (cpysize > mpg->outleft) | |
204 cpysize = mpg->outleft; | |
205 memcpy(((Uint8 *) internal->buffer) + bw, | |
206 mpg->outbuf + mpg->outpos, cpysize); | |
207 bw += cpysize; | |
208 mpg->outpos += cpysize; | |
209 mpg->outleft -= cpysize; | |
210 continue; | |
211 } /* if */ | |
212 | |
213 /* need to decode more from the MP3 stream... */ | |
214 mpg->outpos = 0; | |
215 rc = decodeMP3(&mpg->mp, NULL, 0, mpg->outbuf, | |
216 sizeof (mpg->outbuf), &mpg->outleft); | |
217 if (rc == MP3_ERR) | |
218 { | |
219 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
220 return(bw); | |
221 } /* if */ | |
222 | |
223 else if (rc == MP3_NEED_MORE) | |
224 { | |
225 rc = SDL_RWread(internal->rw, mpg->inbuf, 1, sizeof (mpg->inbuf)); | |
226 if (rc == -1) | |
227 { | |
228 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
229 return(bw); | |
230 } /* if */ | |
268
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
231 |
261 | 232 else if (rc == 0) |
233 { | |
234 sample->flags |= SOUND_SAMPLEFLAG_EOF; | |
235 return(bw); | |
236 } /* else if */ | |
237 | |
268
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
238 /* make sure there isn't an ID3 tag. */ |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
239 /* |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
240 * !!! FIXME: This can fail under the following circumstances: |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
241 * 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
|
242 * 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
|
243 * 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
|
244 * 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
|
245 * 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
|
246 * 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
|
247 * Still, something SHOULD be done about this. |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
248 * 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
|
249 * 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
|
250 * 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
|
251 * 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
|
252 */ |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
253 if (rc >= 128) |
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 Uint8 *ptr = &mpg->inbuf[rc - 128]; |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
256 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
|
257 rc -= 128; /* disregard it. */ |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
258 } /* if */ |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
259 |
263 | 260 rc = decodeMP3(&mpg->mp, mpg->inbuf, rc, |
268
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
261 mpg->outbuf, sizeof (mpg->outbuf), |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
262 &mpg->outleft); |
261 | 263 if (rc == MP3_ERR) |
264 { | |
265 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
266 return(bw); | |
267 } /* if */ | |
268 } /* else if */ | |
269 } /* while */ | |
270 | |
271 return(bw); | |
272 } /* MPGLIB_read */ | |
273 | |
274 | |
275 static int MPGLIB_rewind(Sound_Sample *sample) | |
276 { | |
277 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
278 mpglib_t *mpg = ((mpglib_t *) internal->decoder_private); | |
279 BAIL_IF_MACRO(SDL_RWseek(internal->rw, 0, SEEK_SET) != 0, ERR_IO_ERROR, 0); | |
280 | |
281 /* this is just resetting some fields in a structure; it's very fast. */ | |
282 ExitMP3(&mpg->mp); | |
283 InitMP3(&mpg->mp); | |
284 mpg->outpos = mpg->outleft = 0; | |
285 return(1); | |
286 } /* MPGLIB_rewind */ | |
287 | |
288 | |
306
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
293
diff
changeset
|
289 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
|
290 { |
351 | 291 BAIL_MACRO("MPGLIB: Seeking not implemented", 0); |
306
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
293
diff
changeset
|
292 } /* MPGLIB_seek */ |
c97be6e1bd27
Added framework for Sound_Seek() support.
Ryan C. Gordon <icculus@icculus.org>
parents:
293
diff
changeset
|
293 |
261 | 294 #endif /* SOUND_SUPPORTS_MPGLIB */ |
295 | |
296 | |
297 /* end of mpglib.c ... */ | |
298 |