blob: 1181c1e1fb2055ee4d910da4539a33e7504f472a [file] [log] [blame]
Daniel Stenberg1dd672f2005-06-22 19:41:30 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 Dave Chapman
11 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
Thom Johansenc91e0bb2005-10-13 11:32:52 +000022#include "codeclib.h"
Thom Johansen00599952005-10-12 16:25:59 +000023#include <codecs/libmad/mad.h>
Magnus Holmgren753a8972005-08-16 18:26:41 +000024#include <inttypes.h>
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000025
Jens Arnoldb8749fd2006-01-18 00:05:14 +000026CODEC_HEADER
27
Thom Johansene6021382005-10-27 11:32:02 +000028struct mad_stream stream IBSS_ATTR;
29struct mad_frame frame IBSS_ATTR;
30struct mad_synth synth IBSS_ATTR;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000031
32/* The following function is used inside libmad - let's hope it's never
33 called.
34*/
35
36void abort(void) {
37}
38
Thom Johansen10426db2005-06-22 21:18:05 +000039#define INPUT_CHUNK_SIZE 8192
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000040
Thom Johansene6021382005-10-27 11:32:02 +000041mad_fixed_t mad_frame_overlap[2][32][18] IBSS_ATTR;
42unsigned char mad_main_data[MAD_BUFFER_MDLEN] IBSS_ATTR;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000043/* TODO: what latency does layer 1 have? */
44int mpeg_latency[3] = { 0, 481, 529 };
Dave Chapmanfa5caa02006-05-20 09:57:55 +000045int mpeg_framesize[3] = {384, 1152, 1152};
Thom Johansen00599952005-10-12 16:25:59 +000046
Miika Pekkarinen6d6ca6b2006-01-22 10:25:07 +000047void init_mad(void)
48{
49 ci->memset(&stream, 0, sizeof(struct mad_stream));
50 ci->memset(&frame, 0, sizeof(struct mad_frame));
51 ci->memset(&synth, 0, sizeof(struct mad_synth));
Dave Chapmanfa5caa02006-05-20 09:57:55 +000052
Miika Pekkarinen6d6ca6b2006-01-22 10:25:07 +000053 mad_stream_init(&stream);
54 mad_frame_init(&frame);
55 mad_synth_init(&synth);
56
57 /* We do this so libmad doesn't try to call codec_calloc() */
58 ci->memset(mad_frame_overlap, 0, sizeof(mad_frame_overlap));
59 frame.overlap = &mad_frame_overlap;
60 stream.main_data = &mad_main_data;
61}
62
Michael Sevakis4855e732008-03-29 06:36:53 +000063int get_file_pos(int newtime)
64{
65 int pos = -1;
66 struct mp3entry *id3 = ci->id3;
67
68 if (id3->vbr) {
69 if (id3->has_toc) {
70 /* Use the TOC to find the new position */
71 unsigned int percent, remainder;
72 int curtoc, nexttoc, plen;
73
74 percent = (newtime*100) / id3->length;
75 if (percent > 99)
76 percent = 99;
77
78 curtoc = id3->toc[percent];
79
80 if (percent < 99) {
81 nexttoc = id3->toc[percent+1];
82 } else {
83 nexttoc = 256;
84 }
85
86 pos = (id3->filesize/256)*curtoc;
87
88 /* Use the remainder to get a more accurate position */
89 remainder = (newtime*100) % id3->length;
90 remainder = (remainder*100) / id3->length;
91 plen = (nexttoc - curtoc)*(id3->filesize/256);
92 pos += (plen/100)*remainder;
93 } else {
94 /* No TOC exists, estimate the new position */
95 pos = (id3->filesize / (id3->length / 1000)) *
96 (newtime / 1000);
97 }
98 } else if (id3->bitrate) {
99 pos = newtime * (id3->bitrate / 8);
100 } else {
101 return -1;
102 }
103
104 pos += id3->first_frame_offset;
105
106 /* Don't seek right to the end of the file so that we can
107 transition properly to the next song */
108 if (pos >= (int)(id3->filesize - id3->id3v1len))
109 pos = id3->filesize - id3->id3v1len - 1;
110
111 return pos;
112}
113
Nicolas Pennequin73a71a42008-04-03 12:13:03 +0000114static void set_elapsed(struct mp3entry* id3)
115{
116 unsigned long offset = id3->offset > id3->first_frame_offset ?
117 id3->offset - id3->first_frame_offset : 0;
118
119 if ( id3->vbr ) {
120 if ( id3->has_toc ) {
121 /* calculate elapsed time using TOC */
122 int i;
123 unsigned int remainder, plen, relpos, nextpos;
124
125 /* find wich percent we're at */
126 for (i=0; i<100; i++ )
127 if ( offset < id3->toc[i] * (id3->filesize / 256) )
128 break;
129
130 i--;
131 if (i < 0)
132 i = 0;
133
134 relpos = id3->toc[i];
135
136 if (i < 99)
137 nextpos = id3->toc[i+1];
138 else
139 nextpos = 256;
140
141 remainder = offset - (relpos * (id3->filesize / 256));
142
143 /* set time for this percent (divide before multiply to prevent
144 overflow on long files. loss of precision is negligible on
145 short files) */
146 id3->elapsed = i * (id3->length / 100);
147
148 /* calculate remainder time */
149 plen = (nextpos - relpos) * (id3->filesize / 256);
150 id3->elapsed += (((remainder * 100) / plen) *
151 (id3->length / 10000));
152 }
153 else {
154 /* no TOC exists. set a rough estimate using average bitrate */
155 int tpk = id3->length /
156 ((id3->filesize - id3->first_frame_offset - id3->id3v1len) /
157 1024);
158 id3->elapsed = offset / 1024 * tpk;
159 }
160 }
161 else
162 {
163 /* constant bitrate, use exact calculation */
164 if (id3->bitrate != 0)
165 id3->elapsed = offset / (id3->bitrate / 8);
166 }
167}
168
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000169/* this is the codec entry point */
Tomasz Malesinski80da8b12006-11-26 18:31:41 +0000170enum codec_status codec_main(void)
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000171{
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000172 int status;
Brandon Lowc76904b2006-03-24 14:02:27 +0000173 size_t size;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000174 int file_end;
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000175 int samples_to_skip; /* samples to skip in total for this file (at start) */
Thom Johansen00599952005-10-12 16:25:59 +0000176 char *inputbuffer;
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000177 int64_t samplesdone;
178 int stop_skip, start_skip;
179 int current_stereo_mode = -1;
180 unsigned long current_frequency = 0;
181 int framelength;
182 int padding = MAD_BUFFER_GUARD; /* to help mad decode the last frame */
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +0000183
Tomasz Malesinski80da8b12006-11-26 18:31:41 +0000184 if (codec_init())
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000185 return CODEC_ERROR;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000186
187 /* Create a decoder instance */
188
Michael Sevakis97f369a2007-02-10 16:34:16 +0000189 ci->configure(DSP_SET_SAMPLE_DEPTH, MAD_F_FRACBITS);
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000190
191next_track:
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000192 status = CODEC_OK;
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000193
194 /* Reinitializing seems to be necessary to avoid playback quircks when seeking. */
Miika Pekkarinen6d6ca6b2006-01-22 10:25:07 +0000195 init_mad();
Thom Johansen00599952005-10-12 16:25:59 +0000196
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000197 file_end = 0;
Miika Pekkarinen0d63cbb2005-07-10 20:37:36 +0000198 while (!*ci->taginfo_ready && !ci->stop_codec)
Miika Pekkarinenfbd40882005-07-11 18:47:47 +0000199 ci->sleep(1);
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000200
Michael Sevakis97f369a2007-02-10 16:34:16 +0000201 ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +0000202 current_frequency = ci->id3->frequency;
Magnus Holmgren988ea2c2005-07-27 11:54:33 +0000203 codec_set_replaygain(ci->id3);
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000204
Nicolas Pennequin73a71a42008-04-03 12:13:03 +0000205 if (ci->id3->offset) {
Brandon Low3f699302007-11-05 18:15:52 +0000206 ci->seek_buffer(ci->id3->offset);
Nicolas Pennequin73a71a42008-04-03 12:13:03 +0000207 set_elapsed(ci->id3);
208 }
Brandon Low3f699302007-11-05 18:15:52 +0000209 else
210 ci->seek_buffer(ci->id3->first_frame_offset);
Miika Pekkarinen5c2c9912005-07-05 19:55:40 +0000211
212 if (ci->id3->lead_trim >= 0 && ci->id3->tail_trim >= 0) {
213 stop_skip = ci->id3->tail_trim - mpeg_latency[ci->id3->layer];
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000214 if (stop_skip < 0) stop_skip = 0;
Miika Pekkarinen5c2c9912005-07-05 19:55:40 +0000215 start_skip = ci->id3->lead_trim + mpeg_latency[ci->id3->layer];
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000216 } else {
217 stop_skip = 0;
218 /* We want to skip this amount anyway */
Miika Pekkarinen5c2c9912005-07-05 19:55:40 +0000219 start_skip = mpeg_latency[ci->id3->layer];
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000220 }
Magnus Holmgren988ea2c2005-07-27 11:54:33 +0000221
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000222 /* Libmad will not decode the last frame without 8 bytes of extra padding
223 in the buffer. So, we can trick libmad into not decoding the last frame
224 if we are to skip it entirely and then cut the appropriate samples from
225 final frame that we did decode. Note, if all tags (ID3, APE) are not
226 properly stripped from the end of the file, this trick will not work. */
227 if (stop_skip >= mpeg_framesize[ci->id3->layer]) {
228 padding = 0;
229 stop_skip -= mpeg_framesize[ci->id3->layer];
230 } else {
231 padding = MAD_BUFFER_GUARD;
232 }
233
Thom Johansen00599952005-10-12 16:25:59 +0000234 samplesdone = ((int64_t)ci->id3->elapsed) * current_frequency / 1000;
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000235
236 /* Don't skip any samples unless we start at the beginning. */
237 if (samplesdone > 0)
238 samples_to_skip = 0;
239 else
240 samples_to_skip = start_skip;
241
242 framelength = 0;
243
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000244 /* This is the decoding loop. */
245 while (1) {
Thom Johansen10426db2005-06-22 21:18:05 +0000246 ci->yield();
Brandon Lowebadcc62006-04-15 02:03:11 +0000247 if (ci->stop_codec || ci->new_track)
Thom Johansen00599952005-10-12 16:25:59 +0000248 break;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000249
250 if (ci->seek_time) {
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000251 int newpos;
Miika Pekkarinen0e159f12006-01-20 22:02:44 +0000252
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000253 samplesdone = ((int64_t)(ci->seek_time-1))*current_frequency/1000;
254
255 if (ci->seek_time-1 == 0) {
Magnus Holmgrend5e11572006-05-01 11:54:04 +0000256 newpos = ci->id3->first_frame_offset;
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000257 samples_to_skip = start_skip;
258 } else {
Michael Sevakis4855e732008-03-29 06:36:53 +0000259 newpos = get_file_pos(ci->seek_time-1);
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000260 samples_to_skip = 0;
261 }
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +0000262
Thom Johansen00599952005-10-12 16:25:59 +0000263 if (!ci->seek_buffer(newpos))
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000264 break;
Miika Pekkarinen8a7d1042005-08-21 18:12:31 +0000265 ci->seek_complete();
Miika Pekkarinen6d6ca6b2006-01-22 10:25:07 +0000266 init_mad();
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000267 framelength = 0;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000268 }
269
270 /* Lock buffers */
Thom Johansen00599952005-10-12 16:25:59 +0000271 if (stream.error == 0) {
272 inputbuffer = ci->request_buffer(&size, INPUT_CHUNK_SIZE);
273 if (size == 0 || inputbuffer == NULL)
274 break;
Brandon Lowf42459f2006-04-22 15:13:53 +0000275 mad_stream_buffer(&stream, (unsigned char *)inputbuffer,
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000276 size + padding);
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000277 }
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000278
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000279 if (mad_frame_decode(&frame, &stream)) {
Thom Johansen00599952005-10-12 16:25:59 +0000280 if (stream.error == MAD_FLAG_INCOMPLETE
281 || stream.error == MAD_ERROR_BUFLEN) {
Thom Johansen00599952005-10-12 16:25:59 +0000282 /* This makes the codec support partially corrupted files */
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000283 if (file_end == 30)
Thom Johansen00599952005-10-12 16:25:59 +0000284 break;
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000285
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000286 /* Fill the buffer */
Brandon Low41949902006-04-22 14:48:05 +0000287 if (stream.next_frame)
Magnus Holmgren0dac8b72005-12-22 21:53:21 +0000288 ci->advance_buffer_loc((void *)stream.next_frame);
289 else
290 ci->advance_buffer(size);
Thom Johansen00599952005-10-12 16:25:59 +0000291 stream.error = 0;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000292 file_end++;
Thom Johansen00599952005-10-12 16:25:59 +0000293 continue;
294 } else if (MAD_RECOVERABLE(stream.error)) {
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000295 continue;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000296 } else {
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000297 /* Some other unrecoverable error */
Brandon Low1060e442006-01-18 20:22:03 +0000298 status = CODEC_ERROR;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000299 break;
300 }
Thom Johansen00599952005-10-12 16:25:59 +0000301 break;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000302 }
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000303
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000304 file_end = 0;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000305
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000306 /* Do the pcmbuf insert here. Note, this is the PREVIOUS frame's pcm
307 data (not the one just decoded above). When we exit the decoding
308 loop we will need to process the final frame that was decoded. */
309 if (framelength > 0) {
310 /* In case of a mono file, the second array will be ignored. */
Michael Sevakisaba6ca02007-02-07 00:51:50 +0000311 ci->pcmbuf_insert(&synth.pcm.samples[0][samples_to_skip],
312 &synth.pcm.samples[1][samples_to_skip],
313 framelength);
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000314
315 /* Only skip samples for the first frame added. */
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000316 samples_to_skip = 0;
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +0000317 }
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000318
319 mad_synth_frame(&synth, &frame);
320
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000321 /* Check if sample rate and stereo settings changed in this frame. */
322 if (frame.header.samplerate != current_frequency) {
323 current_frequency = frame.header.samplerate;
Michael Sevakis97f369a2007-02-10 16:34:16 +0000324 ci->configure(DSP_SWITCH_FREQUENCY, current_frequency);
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000325 }
Thom Johansen00599952005-10-12 16:25:59 +0000326 if (MAD_NCHANNELS(&frame.header) == 2) {
Miika Pekkarinend54811f2005-07-02 16:52:30 +0000327 if (current_stereo_mode != STEREO_NONINTERLEAVED) {
Michael Sevakis97f369a2007-02-10 16:34:16 +0000328 ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED);
Miika Pekkarinend54811f2005-07-02 16:52:30 +0000329 current_stereo_mode = STEREO_NONINTERLEAVED;
330 }
Miika Pekkarinend54811f2005-07-02 16:52:30 +0000331 } else {
332 if (current_stereo_mode != STEREO_MONO) {
Michael Sevakis97f369a2007-02-10 16:34:16 +0000333 ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
Miika Pekkarinend54811f2005-07-02 16:52:30 +0000334 current_stereo_mode = STEREO_MONO;
335 }
Miika Pekkarinend54811f2005-07-02 16:52:30 +0000336 }
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000337
Thom Johansen00599952005-10-12 16:25:59 +0000338 if (stream.next_frame)
339 ci->advance_buffer_loc((void *)stream.next_frame);
Miika Pekkarinen3eb962d2005-07-07 07:15:05 +0000340 else
341 ci->advance_buffer(size);
Magnus Holmgren15e0aeb2005-08-13 08:02:38 +0000342
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000343 framelength = synth.pcm.length - samples_to_skip;
344 if (framelength < 0) {
345 framelength = 0;
346 samples_to_skip -= synth.pcm.length;
347 }
348
Magnus Holmgren15e0aeb2005-08-13 08:02:38 +0000349 samplesdone += framelength;
Magnus Holmgren753a8972005-08-16 18:26:41 +0000350 ci->set_elapsed(samplesdone / (current_frequency / 1000));
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000351 }
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000352
353 /* Finish the remaining decoded frame.
354 Cut the required samples from the end. */
355 if (framelength > stop_skip)
Michael Sevakisaba6ca02007-02-07 00:51:50 +0000356 ci->pcmbuf_insert(synth.pcm.samples[0], synth.pcm.samples[1],
357 framelength - stop_skip);
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000358
Thom Johansen00599952005-10-12 16:25:59 +0000359 stream.error = 0;
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000360
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000361 if (ci->request_next_track())
362 goto next_track;
Dave Chapmanfa5caa02006-05-20 09:57:55 +0000363
Brandon Low1060e442006-01-18 20:22:03 +0000364 return status;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000365}