blob: 3ee2b352f11407ec44bb97f037e13296f84340f9 [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 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
Thom Johansenc91e0bb2005-10-13 11:32:52 +000020#include "codeclib.h"
Thom Johansen00599952005-10-12 16:25:59 +000021#include <codecs/libmad/mad.h>
Magnus Holmgren753a8972005-08-16 18:26:41 +000022#include <inttypes.h>
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000023
Jens Arnoldb8749fd2006-01-18 00:05:14 +000024CODEC_HEADER
25
Thom Johansene6021382005-10-27 11:32:02 +000026struct mad_stream stream IBSS_ATTR;
27struct mad_frame frame IBSS_ATTR;
28struct mad_synth synth IBSS_ATTR;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000029
30/* The following function is used inside libmad - let's hope it's never
31 called.
32*/
33
34void abort(void) {
35}
36
Thom Johansen10426db2005-06-22 21:18:05 +000037#define INPUT_CHUNK_SIZE 8192
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000038
Thom Johansene6021382005-10-27 11:32:02 +000039mad_fixed_t mad_frame_overlap[2][32][18] IBSS_ATTR;
40unsigned char mad_main_data[MAD_BUFFER_MDLEN] IBSS_ATTR;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000041/* TODO: what latency does layer 1 have? */
42int mpeg_latency[3] = { 0, 481, 529 };
Thom Johansen00599952005-10-12 16:25:59 +000043
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000044#ifdef USE_IRAM
45extern char iramcopy[];
46extern char iramstart[];
47extern char iramend[];
Jens Arnold07c42542006-01-08 22:50:14 +000048extern char iedata[];
49extern char iend[];
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000050#endif
51
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +000052struct codec_api *ci;
Magnus Holmgren753a8972005-08-16 18:26:41 +000053int64_t samplecount;
54int64_t samplesdone;
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +000055int stop_skip, start_skip;
56int current_stereo_mode = -1;
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +000057unsigned int current_frequency = 0;
58
59void recalc_samplecount(void)
60{
61 /* NOTE: currently this doesn't work, the below calculated samples_count
62 seems to be right, but sometimes we just don't have all the data we
63 need... */
64 if (ci->id3->frame_count) {
65 /* TODO: 1152 is the frame size in samples for MPEG1 layer 2 and layer 3,
66 it's probably not correct at all for MPEG2 and layer 1 */
Thom Johansen00599952005-10-12 16:25:59 +000067 samplecount = ((int64_t)ci->id3->frame_count) * 1152;
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +000068 } else {
Thom Johansen00599952005-10-12 16:25:59 +000069 samplecount = ((int64_t)ci->id3->length) * current_frequency / 1000;
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +000070 }
Magnus Holmgren753a8972005-08-16 18:26:41 +000071
72 samplecount -= start_skip + stop_skip;
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +000073}
74
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000075/* this is the codec entry point */
Thom Johansen00599952005-10-12 16:25:59 +000076enum codec_status codec_start(struct codec_api *api)
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000077{
Brandon Low1060e442006-01-18 20:22:03 +000078 int status = CODEC_OK;
Thom Johansen4d7e5df2005-10-13 12:15:31 +000079 long size;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000080 int file_end;
Thom Johansen6b8bd6b2005-11-02 19:02:25 +000081 int frame_skip; /* samples to skip current frame */
82 int samples_to_skip; /* samples to skip in total for this file (at start) */
Thom Johansen00599952005-10-12 16:25:59 +000083 char *inputbuffer;
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +000084
85 ci = api;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000086
87#ifdef USE_IRAM
Thom Johansen10426db2005-06-22 21:18:05 +000088 ci->memcpy(iramstart, iramcopy, iramend - iramstart);
Jens Arnold07c42542006-01-08 22:50:14 +000089 ci->memset(iedata, 0, iend - iedata);
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000090#endif
91
Thom Johansen00599952005-10-12 16:25:59 +000092 if (codec_init(api))
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000093 return CODEC_ERROR;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000094
95 /* Create a decoder instance */
96
Thom Johansen00599952005-10-12 16:25:59 +000097 ci->configure(CODEC_DSP_ENABLE, (bool *)true);
98 ci->configure(DSP_DITHER, (bool *)false);
Thom Johansena8b9fd12005-11-02 19:15:25 +000099 ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(MAD_F_FRACBITS));
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000100 ci->configure(DSP_SET_CLIP_MIN, (int *)-MAD_F_ONE);
101 ci->configure(DSP_SET_CLIP_MAX, (int *)(MAD_F_ONE - 1));
Thom Johansen00599952005-10-12 16:25:59 +0000102 ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*16));
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000103
Thom Johansen00599952005-10-12 16:25:59 +0000104 ci->memset(&stream, 0, sizeof(struct mad_stream));
105 ci->memset(&frame, 0, sizeof(struct mad_frame));
106 ci->memset(&synth, 0, sizeof(struct mad_synth));
Miika Pekkarinen6b807b22005-07-30 13:46:38 +0000107
Thom Johansen00599952005-10-12 16:25:59 +0000108 mad_stream_init(&stream);
109 mad_frame_init(&frame);
110 mad_synth_init(&synth);
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000111
112 /* We do this so libmad doesn't try to call codec_calloc() */
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000113 ci->memset(mad_frame_overlap, 0, sizeof(mad_frame_overlap));
Thom Johansen00599952005-10-12 16:25:59 +0000114 frame.overlap = &mad_frame_overlap;
115 stream.main_data = &mad_main_data;
116
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000117 /* This label might need to be moved above all the init code, but I don't
118 think reiniting the codec is necessary for MPEG. It might even be unwanted
119 for gapless playback */
Thom Johansen00599952005-10-12 16:25:59 +0000120next_track:
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000121 file_end = 0;
Miika Pekkarinen0d63cbb2005-07-10 20:37:36 +0000122 while (!*ci->taginfo_ready && !ci->stop_codec)
Miika Pekkarinenfbd40882005-07-11 18:47:47 +0000123 ci->sleep(1);
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000124
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000125 ci->configure(DSP_SET_FREQUENCY, (int *)ci->id3->frequency);
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +0000126 current_frequency = ci->id3->frequency;
Magnus Holmgren988ea2c2005-07-27 11:54:33 +0000127 codec_set_replaygain(ci->id3);
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000128
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000129 ci->request_buffer(&size, ci->id3->first_frame_offset);
130 ci->advance_buffer(size);
Miika Pekkarinen5c2c9912005-07-05 19:55:40 +0000131
132 if (ci->id3->lead_trim >= 0 && ci->id3->tail_trim >= 0) {
133 stop_skip = ci->id3->tail_trim - mpeg_latency[ci->id3->layer];
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000134 if (stop_skip < 0) stop_skip = 0;
Miika Pekkarinen5c2c9912005-07-05 19:55:40 +0000135 start_skip = ci->id3->lead_trim + mpeg_latency[ci->id3->layer];
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000136 } else {
137 stop_skip = 0;
138 /* We want to skip this amount anyway */
Miika Pekkarinen5c2c9912005-07-05 19:55:40 +0000139 start_skip = mpeg_latency[ci->id3->layer];
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000140 }
Magnus Holmgren988ea2c2005-07-27 11:54:33 +0000141
Thom Johansen00599952005-10-12 16:25:59 +0000142 samplesdone = ((int64_t)ci->id3->elapsed) * current_frequency / 1000;
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000143 samples_to_skip = start_skip;
Magnus Holmgren15e0aeb2005-08-13 08:02:38 +0000144 recalc_samplecount();
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +0000145
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000146 /* This is the decoding loop. */
147 while (1) {
Magnus Holmgren15e0aeb2005-08-13 08:02:38 +0000148 int framelength;
149
Thom Johansen10426db2005-06-22 21:18:05 +0000150 ci->yield();
Thom Johansen00599952005-10-12 16:25:59 +0000151 if (ci->stop_codec || ci->reload_codec)
152 break;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000153
154 if (ci->seek_time) {
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000155 int newpos;
156
Magnus Holmgren753a8972005-08-16 18:26:41 +0000157 samplesdone = ((int64_t) (ci->seek_time - 1))
158 * current_frequency / 1000;
Linus Nielsen Feltzing984712f2005-08-23 12:52:59 +0000159 newpos = ci->mp3_get_filepos(ci->seek_time-1) +
160 ci->id3->first_frame_offset;
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +0000161
Thom Johansen00599952005-10-12 16:25:59 +0000162 if (!ci->seek_buffer(newpos))
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +0000163 goto next_track;
Miika Pekkarinen8a7d1042005-08-21 18:12:31 +0000164 if (newpos == 0)
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000165 samples_to_skip = start_skip;
Miika Pekkarinen8a7d1042005-08-21 18:12:31 +0000166 ci->seek_complete();
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000167 }
168
169 /* Lock buffers */
Thom Johansen00599952005-10-12 16:25:59 +0000170 if (stream.error == 0) {
171 inputbuffer = ci->request_buffer(&size, INPUT_CHUNK_SIZE);
172 if (size == 0 || inputbuffer == NULL)
173 break;
Daniel Stenberg76667e22005-12-02 08:42:48 +0000174 mad_stream_buffer(&stream, (unsigned char *)inputbuffer, size);
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000175 }
176
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000177 if (mad_frame_decode(&frame, &stream)) {
Thom Johansen00599952005-10-12 16:25:59 +0000178 if (stream.error == MAD_FLAG_INCOMPLETE
179 || stream.error == MAD_ERROR_BUFLEN) {
Thom Johansen00599952005-10-12 16:25:59 +0000180 /* This makes the codec support partially corrupted files */
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000181 if (file_end == 30)
Thom Johansen00599952005-10-12 16:25:59 +0000182 break;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000183
184 /* Fill the buffer */
Magnus Holmgren0dac8b72005-12-22 21:53:21 +0000185 if (stream.next_frame)
186 ci->advance_buffer_loc((void *)stream.next_frame);
187 else
188 ci->advance_buffer(size);
Thom Johansen00599952005-10-12 16:25:59 +0000189 stream.error = 0;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000190 file_end++;
Thom Johansen00599952005-10-12 16:25:59 +0000191 continue;
192 } else if (MAD_RECOVERABLE(stream.error)) {
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000193 continue;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000194 } else {
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000195 /* Some other unrecoverable error */
Brandon Low1060e442006-01-18 20:22:03 +0000196 status = CODEC_ERROR;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000197 break;
198 }
Thom Johansen00599952005-10-12 16:25:59 +0000199 break;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000200 }
Miika Pekkarinen3eb962d2005-07-07 07:15:05 +0000201
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000202 file_end = 0;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000203
Thom Johansen00599952005-10-12 16:25:59 +0000204 mad_synth_frame(&synth, &frame);
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000205
206 /* We need to skip samples_to_skip samples from the start of every file
207 to properly support LAME style gapless MP3 files. samples_to_skip
208 might be larger than one frame. */
209 if (samples_to_skip < synth.pcm.length) {
210 /* skip just part of the frame */
211 frame_skip = samples_to_skip;
212 samples_to_skip = 0;
213 } else {
214 /* we need to skip an entire frame */
215 frame_skip = synth.pcm.length;
216 samples_to_skip -= synth.pcm.length;
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +0000217 }
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000218
219 framelength = synth.pcm.length - frame_skip;
Linus Nielsen Feltzing0da05342005-08-10 22:59:06 +0000220
Thom Johansen00599952005-10-12 16:25:59 +0000221 if (stop_skip > 0) {
Magnus Holmgren753a8972005-08-16 18:26:41 +0000222 int64_t max = samplecount - samplesdone;
Magnus Holmgren15e0aeb2005-08-13 08:02:38 +0000223
224 if (max < 0) max = 0;
Thom Johansen00599952005-10-12 16:25:59 +0000225 if (max < framelength) framelength = (int)max;
Magnus Holmgren97589112005-12-01 21:09:09 +0000226 if (framelength == 0 && frame_skip == 0) break;
Magnus Holmgren15e0aeb2005-08-13 08:02:38 +0000227 }
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000228
229 /* Check if sample rate and stereo settings changed in this frame. */
230 if (frame.header.samplerate != current_frequency) {
231 current_frequency = frame.header.samplerate;
232 ci->configure(DSP_SWITCH_FREQUENCY, (int *)current_frequency);
233 recalc_samplecount();
234 }
Thom Johansen00599952005-10-12 16:25:59 +0000235 if (MAD_NCHANNELS(&frame.header) == 2) {
Miika Pekkarinend54811f2005-07-02 16:52:30 +0000236 if (current_stereo_mode != STEREO_NONINTERLEAVED) {
237 ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_NONINTERLEAVED);
238 current_stereo_mode = STEREO_NONINTERLEAVED;
239 }
Miika Pekkarinend54811f2005-07-02 16:52:30 +0000240 } else {
241 if (current_stereo_mode != STEREO_MONO) {
242 ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_MONO);
243 current_stereo_mode = STEREO_MONO;
244 }
Miika Pekkarinend54811f2005-07-02 16:52:30 +0000245 }
Thom Johansen6b8bd6b2005-11-02 19:02:25 +0000246
247 /* Check if we can just skip the entire frame. */
248 if (frame_skip < synth.pcm.length) {
249 /* In case of a mono file, the second array will be ignored. */
250 ci->pcmbuf_insert_split(&synth.pcm.samples[0][frame_skip],
251 &synth.pcm.samples[1][frame_skip],
252 framelength * 4);
253 }
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000254
Thom Johansen00599952005-10-12 16:25:59 +0000255 if (stream.next_frame)
256 ci->advance_buffer_loc((void *)stream.next_frame);
Miika Pekkarinen3eb962d2005-07-07 07:15:05 +0000257 else
258 ci->advance_buffer(size);
Magnus Holmgren15e0aeb2005-08-13 08:02:38 +0000259
260 samplesdone += framelength;
Magnus Holmgren753a8972005-08-16 18:26:41 +0000261 ci->set_elapsed(samplesdone / (current_frequency / 1000));
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000262 }
Thom Johansen00599952005-10-12 16:25:59 +0000263 stream.error = 0;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000264
265 if (ci->request_next_track())
266 goto next_track;
Brandon Low1060e442006-01-18 20:22:03 +0000267
268 return status;
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000269}