Mercurial > SDL_sound_CoreAudio
annotate decoders/mpglib.c @ 278:dfe4a46f88f7
Updated.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Thu, 14 Mar 2002 16:38:32 +0000 |
parents | 493dd0173f3d |
children | ee6e1f8bfae9 |
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 * | |
30 * mpglib is part of mpg123, which can be found in its original form at: | |
31 * http://www.mpg123.de/ | |
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); | |
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 */ | |
80 MPGLIB_rewind /* rewind() method */ | |
81 }; | |
82 | |
83 | |
84 /* this is what we store in our internal->decoder_private field... */ | |
85 typedef struct | |
86 { | |
87 struct mpstr mp; | |
88 Uint8 inbuf[16384]; | |
89 Uint8 outbuf[8192]; | |
90 int outleft; | |
91 int outpos; | |
92 } mpglib_t; | |
93 | |
94 | |
95 | |
96 static int MPGLIB_init(void) | |
97 { | |
98 return(1); /* always succeeds. */ | |
99 } /* MPGLIB_init */ | |
100 | |
101 | |
102 static void MPGLIB_quit(void) | |
103 { | |
104 /* it's a no-op. */ | |
105 } /* MPGLIB_quit */ | |
106 | |
107 | |
108 static int MPGLIB_open(Sound_Sample *sample, const char *ext) | |
109 { | |
110 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
111 mpglib_t *mpg = NULL; | |
112 | |
113 /* | |
114 * If I understand things correctly, MP3 files don't really have any | |
115 * magic header we can check for. The MP3 player is expected to just | |
116 * pick the first thing that looks like a valid frame and start | |
117 * playing from there. | |
118 * | |
119 * So here's what we do: If the caller insists that this is really | |
120 * MP3 we'll take his word for it. Otherwise, use the same test as | |
121 * SDL_mixer does and check if the stream starts with something that | |
122 * looks like a frame. | |
123 * | |
124 * A frame begins with 11 bits of frame sync (all bits must be set), | |
125 * followed by a two-bit MPEG Audio version ID: | |
126 * | |
127 * 00 - MPEG Version 2.5 (later extension of MPEG 2) | |
128 * 01 - reserved | |
129 * 10 - MPEG Version 2 (ISO/IEC 13818-3) | |
130 * 11 - MPEG Version 1 (ISO/IEC 11172-3) | |
131 * | |
132 * Apparently we don't handle MPEG Version 2.5. | |
133 */ | |
134 if (__Sound_strcasecmp(ext, "MP3") != 0) | |
135 { | |
136 Uint8 mp3_magic[2]; | |
137 | |
138 if (SDL_RWread(internal->rw, mp3_magic, sizeof (mp3_magic), 1) != 1) | |
139 { | |
140 Sound_SetError("MP3: Could not read MP3 magic."); | |
141 return(0); | |
142 } /*if */ | |
143 | |
144 if (mp3_magic[0] != 0xFF || (mp3_magic[1] & 0xF0) != 0xF0) | |
145 { | |
146 Sound_SetError("MP3: Not an MP3 stream."); | |
147 return(0); | |
148 } /* if */ | |
149 | |
150 /* !!! FIXME: If the seek fails, we'll probably miss a frame */ | |
151 SDL_RWseek(internal->rw, -sizeof (mp3_magic), SEEK_CUR); | |
152 } /* if */ | |
153 | |
154 mpg = (mpglib_t *) malloc(sizeof (mpglib_t)); | |
155 BAIL_IF_MACRO(mpg == NULL, ERR_OUT_OF_MEMORY, 0); | |
156 mpg->outpos = mpg->outleft = 0; | |
157 InitMP3(&mpg->mp); | |
158 | |
159 SNDDBG(("MPGLIB: Accepting data stream.\n")); | |
160 | |
271
493dd0173f3d
Added a FIXME comment.
Ryan C. Gordon <icculus@icculus.org>
parents:
268
diff
changeset
|
161 /* !!! FIXME: Determine what format mpglib is spitting out... */ |
261 | 162 internal->decoder_private = mpg; |
163 sample->actual.rate = 44100; | |
164 sample->actual.channels = 2; | |
165 sample->actual.format = AUDIO_S16LSB; | |
166 sample->flags = SOUND_SAMPLEFLAG_NONE; | |
167 | |
168 return(1); /* we'll handle this data. */ | |
169 } /* MPGLIB_open */ | |
170 | |
171 | |
172 static void MPGLIB_close(Sound_Sample *sample) | |
173 { | |
174 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
175 mpglib_t *mpg = ((mpglib_t *) internal->decoder_private); | |
176 ExitMP3(&mpg->mp); | |
177 free(mpg); | |
178 } /* MPGLIB_close */ | |
179 | |
180 | |
181 static Uint32 MPGLIB_read(Sound_Sample *sample) | |
182 { | |
183 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
184 mpglib_t *mpg = ((mpglib_t *) internal->decoder_private); | |
185 Uint32 bw = 0; | |
186 int rc; | |
187 | |
188 while (bw < internal->buffer_size) | |
189 { | |
190 if (mpg->outleft > 0) | |
191 { | |
192 Uint16 cpysize = internal->buffer_size - bw; | |
193 if (cpysize > mpg->outleft) | |
194 cpysize = mpg->outleft; | |
195 memcpy(((Uint8 *) internal->buffer) + bw, | |
196 mpg->outbuf + mpg->outpos, cpysize); | |
197 bw += cpysize; | |
198 mpg->outpos += cpysize; | |
199 mpg->outleft -= cpysize; | |
200 continue; | |
201 } /* if */ | |
202 | |
203 /* need to decode more from the MP3 stream... */ | |
204 mpg->outpos = 0; | |
205 rc = decodeMP3(&mpg->mp, NULL, 0, mpg->outbuf, | |
206 sizeof (mpg->outbuf), &mpg->outleft); | |
207 if (rc == MP3_ERR) | |
208 { | |
209 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
210 return(bw); | |
211 } /* if */ | |
212 | |
213 else if (rc == MP3_NEED_MORE) | |
214 { | |
215 rc = SDL_RWread(internal->rw, mpg->inbuf, 1, sizeof (mpg->inbuf)); | |
216 if (rc == -1) | |
217 { | |
218 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
219 return(bw); | |
220 } /* if */ | |
268
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
221 |
261 | 222 else if (rc == 0) |
223 { | |
224 sample->flags |= SOUND_SAMPLEFLAG_EOF; | |
225 return(bw); | |
226 } /* else if */ | |
227 | |
268
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
228 /* make sure there isn't an ID3 tag. */ |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
229 /* |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
230 * !!! FIXME: This can fail under the following circumstances: |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
231 * 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
|
232 * 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
|
233 * 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
|
234 * 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
|
235 * 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
|
236 * 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
|
237 * Still, something SHOULD be done about this. |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
238 * 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
|
239 * 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
|
240 * 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
|
241 * 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
|
242 */ |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
243 if (rc >= 128) |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
244 { |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
245 Uint8 *ptr = &mpg->inbuf[rc - 128]; |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
246 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
|
247 rc -= 128; /* disregard it. */ |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
248 } /* if */ |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
249 |
263 | 250 rc = decodeMP3(&mpg->mp, mpg->inbuf, rc, |
268
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
251 mpg->outbuf, sizeof (mpg->outbuf), |
9b89bb587f8f
Detect and discard ID3 tags.
Ryan C. Gordon <icculus@icculus.org>
parents:
263
diff
changeset
|
252 &mpg->outleft); |
261 | 253 if (rc == MP3_ERR) |
254 { | |
255 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
256 return(bw); | |
257 } /* if */ | |
258 } /* else if */ | |
259 } /* while */ | |
260 | |
261 return(bw); | |
262 } /* MPGLIB_read */ | |
263 | |
264 | |
265 static int MPGLIB_rewind(Sound_Sample *sample) | |
266 { | |
267 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
268 mpglib_t *mpg = ((mpglib_t *) internal->decoder_private); | |
269 BAIL_IF_MACRO(SDL_RWseek(internal->rw, 0, SEEK_SET) != 0, ERR_IO_ERROR, 0); | |
270 | |
271 /* this is just resetting some fields in a structure; it's very fast. */ | |
272 ExitMP3(&mpg->mp); | |
273 InitMP3(&mpg->mp); | |
274 mpg->outpos = mpg->outleft = 0; | |
275 return(1); | |
276 } /* MPGLIB_rewind */ | |
277 | |
278 | |
279 #endif /* SOUND_SUPPORTS_MPGLIB */ | |
280 | |
281 | |
282 /* end of mpglib.c ... */ | |
283 |