2 * Copyright (c) 2006-2011 Ed Schouten <ed@80386.nl>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * @file audio_format_mp3.c
28 * @brief MP3 decompression routines.
36 #include "audio_file.h"
37 #include "audio_format.h"
38 #include "audio_output.h"
41 * @brief Private MP3 data stored in the audio file structure.
47 struct mad_stream mstream
;
51 struct mad_frame mframe
;
53 * @brief Frame header.
55 struct mad_header mheader
;
59 struct mad_synth msynth
;
61 * @brief Progress timer.
65 * @brief Sample offset.
69 * @brief Length of the file.
76 unsigned char buf_input
[65536];
80 * File and tag matching
84 * @brief Test if an opened file is an MP3 file.
87 mp3_match(FILE *fp
, const char *ext
)
92 /* Also match (broken) *.mp3 files */
93 if (ext
!= NULL
&& strcmp(ext
, "mp3") == 0)
96 if (fread(buf
, sizeof buf
, 1, fp
) != 1) {
101 /* Match 1: first twelve bits high */
102 if (buf
[0] == 0xff && (buf
[1] & 0xf0) == 0xf0)
105 /* Match 2: ID3 header */
106 if (buf
[0] == 'I' && buf
[1] == 'D' && buf
[2] == '3')
116 * @brief Read the ID3 tag from an MP3 file.
119 mp3_readtags(struct audio_file
*fd
)
124 struct id3_file
*id3f
;
126 id3_ucs4_t
const *str
;
129 /* Duplicate the current filehandle */
130 tmpfd
= dup(fileno(fd
->fp
));
134 /* Backing up the original offset */
135 orig_off
= lseek(tmpfd
, 0, SEEK_CUR
);
137 id3f
= id3_file_fdopen(tmpfd
, ID3_FILE_MODE_READONLY
);
142 tag
= id3_file_tag(id3f
);
146 for (i
= 0; i
< tag
->nframes
; i
++) {
147 if (strncmp("TPE1", tag
->frames
[i
]->id
, 4) == 0) {
149 } else if (strncmp("TIT2", tag
->frames
[i
]->id
, 4) == 0) {
151 #ifdef BUILD_SCROBBLER
152 } else if (strncmp("TALB", tag
->frames
[i
]->id
, 4) == 0) {
154 #endif /* BUILD_SCROBBLER */
158 if (id3_field_getnstrings(&tag
->frames
[i
]->fields
[1]) != 0) {
159 str
= id3_field_getstrings(&tag
->frames
[i
]->fields
[1], 0);
161 *dst
= (char*)id3_ucs4_utf8duplicate(str
);
166 id3_file_close(id3f
);
168 lseek(tmpfd
, orig_off
, SEEK_SET
);
173 * MP3 frame decoding routines
177 * @brief Refill the databuffer when it's drained, copying the last
178 * partial frame to the beginning.
181 mp3_fill_buffer(struct audio_file
*fd
)
183 struct mp3_drv_data
*data
= fd
->drv_data
;
184 size_t offset
, filledlen
, readlen
;
186 if (data
->mstream
.next_frame
== NULL
) {
187 /* Place contents at the beginning */
191 * We still need to save the fragmented frame. Copy it
192 * to the beginning and load as much as possible.
194 offset
= data
->mstream
.bufend
- data
->mstream
.next_frame
;
195 memmove(data
->buf_input
, data
->mstream
.next_frame
, offset
);
198 readlen
= sizeof data
->buf_input
- offset
;
200 filledlen
= fread(data
->buf_input
+ offset
, 1, readlen
, fd
->fp
);
205 if (filledlen
< readlen
) {
207 * Place some null bytes at the end
208 * because we may overrun it
210 memset(data
->buf_input
+ offset
+ filledlen
, 0x00000000,
212 filledlen
+= MAD_BUFFER_GUARD
;
215 /* Return the length of the entire buffer */
216 return (offset
+ filledlen
);
220 * @brief Try to decode a frame, refilling the buffer if needed.
223 mp3_read_frame(struct audio_file
*fd
)
225 struct mp3_drv_data
*data
= fd
->drv_data
;
228 /* Get the next frame */
230 /* We've run out of data. Read from file */
231 if ((data
->mstream
.buffer
== NULL
) ||
232 (data
->mstream
.error
== MAD_ERROR_BUFLEN
)) {
233 if ((buflen
= mp3_fill_buffer(fd
)) == 0)
237 /* Load it back in */
238 mad_stream_buffer(&data
->mstream
, data
->buf_input
,
240 data
->mstream
.error
= MAD_ERROR_NONE
;
243 if (mad_header_decode(&data
->mheader
, &data
->mstream
) == 0) {
244 /* Update the audio timer */
245 mad_timer_add(&data
->mtimer
,
246 data
->mframe
.header
.duration
);
247 fd
->time_cur
= data
->mtimer
.seconds
;
248 data
->mframe
.header
= data
->mheader
;
252 if (!MAD_RECOVERABLE(data
->mstream
.error
) &&
253 (data
->mstream
.error
!= MAD_ERROR_BUFLEN
)) {
254 /* Unrecoverable error - bail out */
262 * @brief Convert a fixed point sample to a short.
264 static inline int16_t
265 mp3_fixed_to_short(mad_fixed_t fixed
)
267 if (fixed
>= MAD_F_ONE
)
269 else if (fixed
<= -MAD_F_ONE
)
272 return (fixed
>> (MAD_F_FRACBITS
- 15));
276 * @brief Rewind the current audio file handle to the beginning.
279 mp3_rewind(struct audio_file
*fd
)
281 struct mp3_drv_data
*data
= fd
->drv_data
;
288 mad_stream_init(&data
->mstream
);
289 mad_frame_init(&data
->mframe
);
290 mad_header_init(&data
->mheader
);
291 mad_synth_init(&data
->msynth
);
292 mad_timer_reset(&data
->mtimer
);
296 * @brief Calculate the length of the current audio file.
299 mp3_calc_length(struct audio_file
*fd
)
301 struct mp3_drv_data
*data
= fd
->drv_data
;
304 /* Go to the end of the file */
306 if (mp3_read_frame(fd
) != 0) {
308 fd
->time_len
= data
->mtimer
.seconds
;
309 data
->flen
= ftello(fd
->fp
);
312 } while (data
->mtimer
.seconds
< 100);
314 /* Extrapolate the time. Not really accurate, but good enough */
315 curpos
= ftello(fd
->fp
);
316 fseek(fd
->fp
, 0, SEEK_END
);
317 data
->flen
= ftello(fd
->fp
);
319 fd
->time_len
= ((double)data
->flen
/ curpos
) *
320 data
->mtimer
.seconds
;
322 /* Go back to the start */
331 mp3_open(struct audio_file
*fd
, const char *ext
)
333 struct mp3_drv_data
*data
;
335 /* Don't match other files */
337 if (mp3_match(fd
->fp
, ext
) != 0)
342 data
= g_slice_new(struct mp3_drv_data
);
343 fd
->drv_data
= (void *)data
;
353 mp3_close(struct audio_file
*fd
)
355 struct mp3_drv_data
*data
= fd
->drv_data
;
357 mad_frame_finish(&data
->mframe
);
358 mad_stream_finish(&data
->mstream
);
359 mad_synth_finish(&data
->msynth
);
361 g_slice_free(struct mp3_drv_data
, data
);
365 mp3_read(struct audio_file
*fd
, int16_t *buf
, size_t len
)
367 struct mp3_drv_data
*data
= fd
->drv_data
;
372 /* Get a new frame when we haven't go one */
373 if (data
->cursample
== 0) {
374 if (mp3_read_frame(fd
) != 0)
376 if (mad_frame_decode(&data
->mframe
, &data
->mstream
) == -1)
379 /* We can now set the sample rate */
380 fd
->srate
= data
->mframe
.header
.samplerate
;
381 fd
->channels
= MAD_NCHANNELS(&data
->mframe
.header
);
383 mad_synth_frame(&data
->msynth
, &data
->mframe
);
386 while ((data
->cursample
< data
->msynth
.pcm
.length
) &&
388 /* Write out all channels */
389 for (i
= 0; i
< MAD_NCHANNELS(&data
->mframe
.header
); i
++) {
390 buf
[written
++] = mp3_fixed_to_short(
391 data
->msynth
.pcm
.samples
[i
][data
->cursample
]);
397 /* Move on to the next frame */
398 if (data
->cursample
== data
->msynth
.pcm
.length
)
401 } while (written
< len
);
408 mp3_seek(struct audio_file
*fd
, int len
, int rel
)
410 struct mp3_drv_data
*data
= fd
->drv_data
;
418 /* Calculate the new relative position */
419 len
= CLAMP(len
, 0, (int)fd
->time_len
);
420 newpos
= ((double)len
/ fd
->time_len
) * data
->flen
;
422 /* Seek to the new position */
424 fseek(fd
->fp
, newpos
, SEEK_SET
);
425 data
->mtimer
.seconds
= len
;
426 data
->mtimer
.fraction
= 0;
427 fd
->time_cur
= data
->mtimer
.seconds
;