Mercurial > SDL_sound_CoreAudio
annotate decoders/mpg123.c @ 573:1911fc597c69
Set a WM caption, which the PulseAudio target can use for display purposes.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Sun, 24 Jan 2010 14:01:39 -0500 |
parents | 74405e7be04b |
children | 2e5e01679f32 |
rev | line source |
---|---|
562 | 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 * libmpg123 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 * libmpg123 is part of mpg123, and can be found in its original | |
26 * form at: http://www.mpg123.org/ | |
27 * | |
28 * Please see the file LICENSE.txt in the source's root directory. The included | |
29 * source code for libmpg123 falls under the LGPL, which is the same license | |
30 * as SDL_sound (so you can consider it a single work). | |
31 * | |
32 * This file written by Ryan C. Gordon. (icculus@icculus.org) | |
33 */ | |
34 | |
35 #if HAVE_CONFIG_H | |
36 # include <config.h> | |
37 #endif | |
38 | |
39 #ifdef SOUND_SUPPORTS_MPG123 | |
40 | |
41 #include <stdio.h> | |
42 #include <stdlib.h> | |
43 #include <string.h> | |
44 | |
45 #define MPG123_NO_CONFIGURE 1 | |
46 #include "libmpg123/mpg123.h" | |
47 | |
48 #include "SDL_sound.h" | |
49 | |
50 #define __SDL_SOUND_INTERNAL__ | |
51 #include "SDL_sound_internal.h" | |
52 | |
53 static int MPG123_init(void); | |
54 static void MPG123_quit(void); | |
55 static int MPG123_open(Sound_Sample *sample, const char *ext); | |
56 static void MPG123_close(Sound_Sample *sample); | |
57 static Uint32 MPG123_read(Sound_Sample *sample); | |
58 static int MPG123_rewind(Sound_Sample *sample); | |
59 static int MPG123_seek(Sound_Sample *sample, Uint32 ms); | |
60 | |
61 /* !!! FIXME: MPEG and MPG extensions? */ | |
62 static const char *extensions_mpg123[] = { "MP3", NULL }; | |
63 const Sound_DecoderFunctions __Sound_DecoderFunctions_MPG123 = | |
64 { | |
65 { | |
66 extensions_mpg123, | |
67 "MP3 decoding via internal libmpg123", | |
68 "Ryan C. Gordon <icculus@icculus.org>", | |
69 "http://www.icculus.org/SDL_sound/" | |
70 }, | |
71 | |
72 MPG123_init, /* init() method */ | |
73 MPG123_quit, /* quit() method */ | |
74 MPG123_open, /* open() method */ | |
75 MPG123_close, /* close() method */ | |
76 MPG123_read, /* read() method */ | |
77 MPG123_rewind, /* rewind() method */ | |
78 MPG123_seek /* seek() method */ | |
79 }; | |
80 | |
81 | |
82 /* this is what we store in our internal->decoder_private field... */ | |
83 typedef mpg123_handle mpg123_t; | |
84 | |
85 static SDL_mutex *mpg123_mutex = NULL; | |
86 static int mpg123_rwops_count = 0; | |
87 static SDL_RWops **mpg123_rwops = NULL; | |
88 | |
89 static void print_decoders(const char *kind, char **decoders) | |
90 { | |
91 SNDDBG(("%s:", kind)); | |
92 if (*decoders == NULL) | |
93 SNDDBG((" [none]")); | |
94 else | |
95 { | |
96 do | |
97 { | |
98 SNDDBG((" %s", *decoders)); | |
99 } while (*(++decoders)); | |
100 } /* else */ | |
564 | 101 SNDDBG(("\n")); |
562 | 102 } /* print_decoders */ |
103 | |
104 | |
105 static int MPG123_init(void) | |
106 { | |
107 int retval = 0; | |
108 assert(mpg123_mutex == NULL); | |
109 if (mpg123_init() == MPG123_OK) | |
110 { | |
111 char **supported = mpg123_supported_decoders(); | |
112 print_decoders("ALL MPG123 DECODERS", mpg123_decoders()); | |
113 print_decoders("SUPPORTED MPG123 DECODERS", mpg123_supported_decoders()); | |
114 if ((supported != NULL) && (*supported != NULL)) | |
115 { | |
116 mpg123_mutex = SDL_CreateMutex(); | |
117 if (mpg123_mutex != NULL) | |
118 retval = 1; /* at least one decoder available. */ | |
119 } /* if */ | |
120 } /* if */ | |
121 | |
122 return retval; | |
123 } /* MPG123_init */ | |
124 | |
125 | |
126 static void MPG123_quit(void) | |
127 { | |
128 mpg123_exit(); | |
129 SDL_DestroyMutex(mpg123_mutex); | |
130 mpg123_mutex = NULL; | |
131 free(mpg123_rwops); | |
132 mpg123_rwops = NULL; | |
133 mpg123_rwops_count = 0; | |
134 } /* MPG123_quit */ | |
135 | |
136 | |
137 /* bridge rwops reading to libmpg123 hooks. */ | |
138 static ssize_t rwread(int fd, void *buf, size_t len) | |
139 { | |
140 SDL_RWops *rw = NULL; | |
141 SDL_LockMutex(mpg123_mutex); | |
142 rw = mpg123_rwops[fd]; | |
143 SDL_UnlockMutex(mpg123_mutex); | |
144 return (ssize_t) SDL_RWread(rw, buf, 1, len); | |
145 } /* rwread */ | |
146 | |
147 | |
148 /* bridge rwops seeking to libmpg123 hooks. */ | |
149 static off_t rwseek(int fd, off_t pos, int whence) | |
150 { | |
151 SDL_RWops *rw = NULL; | |
152 SDL_LockMutex(mpg123_mutex); | |
153 rw = mpg123_rwops[fd]; | |
154 SDL_UnlockMutex(mpg123_mutex); | |
155 return (off_t) SDL_RWseek(rw, pos, whence); | |
156 } /* rwseek */ | |
157 | |
158 | |
159 static const char *set_error(mpg123_handle *mp, const int err) | |
160 { | |
161 char buffer[128]; | |
162 const char *str = NULL; | |
163 if ((err == MPG123_ERR) && (mp != NULL)) | |
164 str = mpg123_strerror(mp); | |
165 else | |
166 str = mpg123_plain_strerror(err); | |
167 | |
168 snprintf(buffer, sizeof (buffer), "MPG123: %s", str); | |
169 __Sound_SetError(buffer); | |
170 | |
171 return(NULL); /* this is for BAIL_MACRO to not try to reset the string. */ | |
172 } /* set_error */ | |
173 | |
174 | |
175 /* Make sure we are only given decoded data in a format we can handle. */ | |
176 static int set_formats(mpg123_handle *mp) | |
177 { | |
178 int rc = 0; | |
179 const long *rates = NULL; | |
180 size_t ratecount = 0; | |
181 const int channels = MPG123_STEREO | MPG123_MONO; | |
182 const int encodings = /* !!! FIXME: SDL 1.3 can do sint32 and float32. | |
183 MPG123_ENC_SIGNED_32 | MPG123_ENC_FLOAT_32 | */ | |
184 MPG123_ENC_SIGNED_8 | MPG123_ENC_UNSIGNED_8 | | |
185 MPG123_ENC_SIGNED_16 | MPG123_ENC_UNSIGNED_16; | |
186 | |
187 mpg123_rates(&rates, &ratecount); | |
188 | |
189 rc = mpg123_format_none(mp); | |
190 while ((ratecount--) && (rc == MPG123_OK)) | |
191 rc = mpg123_format(mp, *(rates++), channels, encodings); | |
192 | |
193 return(rc); | |
194 } /* set_formats */ | |
195 | |
196 | |
197 static int MPG123_open(Sound_Sample *sample, const char *ext) | |
198 { | |
199 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
200 mpg123_handle *mp = NULL; | |
201 long rate = 0; | |
202 int channels = 0; | |
203 int fmt = 0; | |
204 int rc = 0; | |
205 off_t len = 0; | |
206 int seekable = 0; | |
207 void *ptr = NULL; | |
208 int rwops = 0; | |
209 | |
210 /* !!! FIXME: so much tapdance because we need a pointer, not an int. */ | |
211 SDL_LockMutex(mpg123_mutex); | |
212 for (rwops = 0; rwops < mpg123_rwops_count; rwops++) | |
213 { | |
214 if (mpg123_rwops[rwops] == NULL) | |
215 break; | |
216 } /* for */ | |
217 if (rwops < mpg123_rwops_count) | |
218 ptr = mpg123_rwops; | |
219 else | |
220 { | |
221 mpg123_rwops_count++; | |
222 ptr = realloc(mpg123_rwops, sizeof (SDL_RWops *) * mpg123_rwops_count); | |
223 if (ptr != NULL) | |
224 mpg123_rwops = (SDL_RWops **) ptr; | |
225 } /* else */ | |
226 if (ptr != NULL) | |
227 mpg123_rwops[rwops] = internal->rw; | |
228 SDL_UnlockMutex(mpg123_mutex); | |
229 BAIL_IF_MACRO(ptr == NULL, ERR_OUT_OF_MEMORY, 0); | |
230 | |
231 if ((mp = mpg123_new(NULL, &rc)) == NULL) | |
232 goto mpg123_open_failed; | |
233 else if ((rc = set_formats(mp)) != MPG123_OK) | |
234 goto mpg123_open_failed; | |
235 else if ((rc = mpg123_replace_reader(mp, rwread, rwseek)) != MPG123_OK) | |
236 goto mpg123_open_failed; | |
237 else if ((rc = mpg123_open_fd(mp, rwops)) != MPG123_OK) | |
238 goto mpg123_open_failed; | |
239 else if ((rc = mpg123_scan(mp)) != MPG123_OK) | |
240 goto mpg123_open_failed; /* !!! FIXME: this may be wrong. */ | |
241 else if ((rc = mpg123_getformat(mp, &rate, &channels, &fmt)) != MPG123_OK) | |
242 goto mpg123_open_failed; | |
243 | |
244 if (mpg123_seek(mp, 0, SEEK_END) >= 0) /* can seek? */ | |
245 { | |
246 len = mpg123_tell(mp); | |
247 if ((rc = (int) mpg123_seek(mp, 0, SEEK_SET)) < 0) | |
248 goto mpg123_open_failed; | |
249 seekable = 1; | |
250 } /* if */ | |
251 | |
252 internal->decoder_private = mp; | |
253 sample->actual.rate = rate; | |
254 sample->actual.channels = channels; | |
255 | |
256 rc = MPG123_BAD_OUTFORMAT; /* in case this fails... */ | |
257 if (fmt == MPG123_ENC_SIGNED_8) | |
258 sample->actual.format = AUDIO_S8; | |
259 else if (fmt == MPG123_ENC_UNSIGNED_8) | |
260 sample->actual.format = AUDIO_U8; | |
261 else if (fmt == MPG123_ENC_SIGNED_16) | |
262 sample->actual.format = AUDIO_S16SYS; | |
263 else if (fmt == MPG123_ENC_UNSIGNED_16) | |
264 sample->actual.format = AUDIO_U16SYS; | |
265 /* !!! FIXME: SDL 1.3 can do sint32 and float32 ... | |
266 else if (fmt == MPG123_ENC_SIGNED_32) | |
267 sample->actual.format = AUDIO_S32SYS; | |
268 else if (fmt == MPG123_ENC_FLOAT_32) | |
269 sample->actual.format = AUDIO_F32SYS; | |
270 */ | |
271 else | |
272 goto mpg123_open_failed; | |
273 | |
566
74405e7be04b
Moved SNDDBG output a little later.
Ryan C. Gordon <icculus@icculus.org>
parents:
564
diff
changeset
|
274 SNDDBG(("MPG123: Accepting data stream.\n")); |
74405e7be04b
Moved SNDDBG output a little later.
Ryan C. Gordon <icculus@icculus.org>
parents:
564
diff
changeset
|
275 |
562 | 276 sample->flags = SOUND_SAMPLEFLAG_NONE; |
277 internal->total_time = -1; | |
278 if (seekable) | |
279 { | |
280 sample->flags |= SOUND_SAMPLEFLAG_CANSEEK; | |
281 internal->total_time = ((len / rate) * 1000) + | |
282 (((len % rate) * 1000) / rate); | |
283 } /* if */ | |
284 | |
285 return(1); /* we'll handle this data. */ | |
286 | |
287 mpg123_open_failed: | |
288 SDL_LockMutex(mpg123_mutex); | |
289 mpg123_rwops[rwops] = NULL; | |
290 if (rwops == mpg123_rwops_count) | |
291 mpg123_rwops_count--; | |
292 SDL_UnlockMutex(mpg123_mutex); | |
293 set_error(mp, rc); | |
294 mpg123_delete(mp); /* NULL is safe. */ | |
295 return(0); | |
296 } /* MPG123_open */ | |
297 | |
298 | |
299 static void MPG123_close(Sound_Sample *sample) | |
300 { | |
301 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
302 mpg123_t *mp = ((mpg123_t *) internal->decoder_private); | |
303 int i; | |
304 | |
305 SDL_LockMutex(mpg123_mutex); | |
306 for (i = 0; i < mpg123_rwops_count; i++) | |
307 { | |
308 if (mpg123_rwops[i] == internal->rw) | |
309 mpg123_rwops[i] = NULL; | |
310 } /* for */ | |
311 | |
312 for (i = mpg123_rwops_count-1; i >= 0; i--) | |
313 { | |
314 if (mpg123_rwops[i] != NULL) | |
315 break; | |
316 } /* for */ | |
317 mpg123_rwops_count = i + 1; | |
318 SDL_UnlockMutex(mpg123_mutex); | |
319 | |
320 mpg123_close(mp); /* don't need this at the moment, but it's safe. */ | |
321 mpg123_delete(mp); | |
322 } /* MPG123_close */ | |
323 | |
324 | |
325 static Uint32 MPG123_read(Sound_Sample *sample) | |
326 { | |
327 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
328 mpg123_t *mp = ((mpg123_t *) internal->decoder_private); | |
329 size_t bw = 0; | |
330 const int rc = mpg123_read(mp, (unsigned char *) internal->buffer, | |
331 internal->buffer_size, &bw); | |
332 if (rc == MPG123_DONE) | |
333 sample->flags |= SOUND_SAMPLEFLAG_EOF; | |
334 else if (rc != MPG123_OK) | |
335 { | |
336 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
337 set_error(mp, rc); | |
338 } /* else if */ | |
339 | |
340 return((Uint32) bw); | |
341 } /* MPG123_read */ | |
342 | |
343 | |
344 static int MPG123_rewind(Sound_Sample *sample) | |
345 { | |
346 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
347 mpg123_t *mp = ((mpg123_t *) internal->decoder_private); | |
348 const int rc = (int) mpg123_seek(mp, 0, SEEK_SET); | |
349 BAIL_IF_MACRO(rc < 0, set_error(mp, rc), 0); | |
350 return(1); | |
351 } /* MPG123_rewind */ | |
352 | |
353 | |
354 static int MPG123_seek(Sound_Sample *sample, Uint32 ms) | |
355 { | |
356 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
357 mpg123_t *mp = ((mpg123_t *) internal->decoder_private); | |
358 const float frames_per_ms = ((float) sample->actual.rate) / 1000.0f; | |
359 const off_t frame_offset = (off_t) (frames_per_ms * ((float) ms)); | |
360 const int rc = (int) mpg123_seek(mp, frame_offset , SEEK_SET); | |
361 BAIL_IF_MACRO(rc < 0, set_error(mp, rc), 0); | |
362 return(1); | |
363 } /* MPG123_seek */ | |
364 | |
365 #endif /* SOUND_SUPPORTS_MPG123 */ | |
366 | |
367 /* end of mpg123.c ... */ | |
368 |