aboutsummaryrefslogtreecommitdiff
path: root/src/decoder/mad_decoder_plugin.c
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2010-01-04 21:36:33 +0100
committerMax Kellermann <max@duempel.org>2010-01-04 21:45:32 +0100
commit9b9abff97272b52f133ff23addd58b6a90a49a73 (patch)
tree1790e9c10485b30a2ea84ce5923d26bf8c91c688 /src/decoder/mad_decoder_plugin.c
parentc69cc31de0818f8bf42431dcbc1f9de4451a0456 (diff)
renamed decoder plugin sources
Make it X_decoder_plugin.c.
Diffstat (limited to 'src/decoder/mad_decoder_plugin.c')
-rw-r--r--src/decoder/mad_decoder_plugin.c1235
1 files changed, 1235 insertions, 0 deletions
diff --git a/src/decoder/mad_decoder_plugin.c b/src/decoder/mad_decoder_plugin.c
new file mode 100644
index 00000000..87dfbeab
--- /dev/null
+++ b/src/decoder/mad_decoder_plugin.c
@@ -0,0 +1,1235 @@
+/*
+ * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "decoder_api.h"
+#include "conf.h"
+#include "tag_id3.h"
+#include "audio_check.h"
+
+#include <assert.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <glib.h>
+#include <mad.h>
+
+#ifdef HAVE_ID3TAG
+#include <id3tag.h>
+#endif
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "mad"
+
+#define FRAMES_CUSHION 2000
+
+#define READ_BUFFER_SIZE 40960
+
+enum mp3_action {
+ DECODE_SKIP = -3,
+ DECODE_BREAK = -2,
+ DECODE_CONT = -1,
+ DECODE_OK = 0
+};
+
+enum muteframe {
+ MUTEFRAME_NONE,
+ MUTEFRAME_SKIP,
+ MUTEFRAME_SEEK
+};
+
+/* the number of samples of silence the decoder inserts at start */
+#define DECODERDELAY 529
+
+#define DEFAULT_GAPLESS_MP3_PLAYBACK true
+
+static bool gapless_playback;
+
+static inline int32_t
+mad_fixed_to_24_sample(mad_fixed_t sample)
+{
+ enum {
+ bits = 24,
+ MIN = -MAD_F_ONE,
+ MAX = MAD_F_ONE - 1
+ };
+
+ /* round */
+ sample = sample + (1L << (MAD_F_FRACBITS - bits));
+
+ /* clip */
+ if (sample > MAX)
+ sample = MAX;
+ else if (sample < MIN)
+ sample = MIN;
+
+ /* quantize */
+ return sample >> (MAD_F_FRACBITS + 1 - bits);
+}
+
+static void
+mad_fixed_to_24_buffer(int32_t *dest, const struct mad_synth *synth,
+ unsigned int start, unsigned int end,
+ unsigned int num_channels)
+{
+ unsigned int i, c;
+
+ for (i = start; i < end; ++i) {
+ for (c = 0; c < num_channels; ++c)
+ *dest++ = mad_fixed_to_24_sample(synth->pcm.samples[c][i]);
+ }
+}
+
+static bool
+mp3_plugin_init(G_GNUC_UNUSED const struct config_param *param)
+{
+ gapless_playback = config_get_bool(CONF_GAPLESS_MP3_PLAYBACK,
+ DEFAULT_GAPLESS_MP3_PLAYBACK);
+ return true;
+}
+
+#define MP3_DATA_OUTPUT_BUFFER_SIZE 2048
+
+struct mp3_data {
+ struct mad_stream stream;
+ struct mad_frame frame;
+ struct mad_synth synth;
+ mad_timer_t timer;
+ unsigned char input_buffer[READ_BUFFER_SIZE];
+ int32_t output_buffer[MP3_DATA_OUTPUT_BUFFER_SIZE];
+ float total_time;
+ float elapsed_time;
+ float seek_where;
+ enum muteframe mute_frame;
+ long *frame_offsets;
+ mad_timer_t *times;
+ unsigned long highest_frame;
+ unsigned long max_frames;
+ unsigned long current_frame;
+ unsigned int drop_start_frames;
+ unsigned int drop_end_frames;
+ unsigned int drop_start_samples;
+ unsigned int drop_end_samples;
+ bool found_replay_gain;
+ bool found_xing;
+ bool found_first_frame;
+ bool decoded_first_frame;
+ unsigned long bit_rate;
+ struct decoder *decoder;
+ struct input_stream *input_stream;
+ enum mad_layer layer;
+};
+
+static void
+mp3_data_init(struct mp3_data *data, struct decoder *decoder,
+ struct input_stream *input_stream)
+{
+ data->mute_frame = MUTEFRAME_NONE;
+ data->highest_frame = 0;
+ data->max_frames = 0;
+ data->frame_offsets = NULL;
+ data->times = NULL;
+ data->current_frame = 0;
+ data->drop_start_frames = 0;
+ data->drop_end_frames = 0;
+ data->drop_start_samples = 0;
+ data->drop_end_samples = 0;
+ data->found_replay_gain = false;
+ data->found_xing = false;
+ data->found_first_frame = false;
+ data->decoded_first_frame = false;
+ data->decoder = decoder;
+ data->input_stream = input_stream;
+ data->layer = 0;
+
+ mad_stream_init(&data->stream);
+ mad_stream_options(&data->stream, MAD_OPTION_IGNORECRC);
+ mad_frame_init(&data->frame);
+ mad_synth_init(&data->synth);
+ mad_timer_reset(&data->timer);
+}
+
+static bool mp3_seek(struct mp3_data *data, long offset)
+{
+ if (!input_stream_seek(data->input_stream, offset, SEEK_SET, NULL))
+ return false;
+
+ mad_stream_buffer(&data->stream, data->input_buffer, 0);
+ (data->stream).error = 0;
+
+ return true;
+}
+
+static bool
+mp3_fill_buffer(struct mp3_data *data)
+{
+ size_t remaining, length;
+ unsigned char *dest;
+
+ if (data->stream.next_frame != NULL) {
+ remaining = data->stream.bufend - data->stream.next_frame;
+ memmove(data->input_buffer, data->stream.next_frame,
+ remaining);
+ dest = (data->input_buffer) + remaining;
+ length = READ_BUFFER_SIZE - remaining;
+ } else {
+ remaining = 0;
+ length = READ_BUFFER_SIZE;
+ dest = data->input_buffer;
+ }
+
+ /* we've exhausted the read buffer, so give up!, these potential
+ * mp3 frames are way too big, and thus unlikely to be mp3 frames */
+ if (length == 0)
+ return false;
+
+ length = decoder_read(data->decoder, data->input_stream, dest, length);
+ if (length == 0)
+ return false;
+
+ mad_stream_buffer(&data->stream, data->input_buffer,
+ length + remaining);
+ (data->stream).error = 0;
+
+ return true;
+}
+
+#ifdef HAVE_ID3TAG
+/* Parse mp3 RVA2 frame. Shamelessly stolen from madplay. */
+static int parse_rva2(struct id3_tag * tag, struct replay_gain_info * replay_gain_info)
+{
+ struct id3_frame const * frame;
+
+ id3_latin1_t const *id;
+ id3_byte_t const *data;
+ id3_length_t length;
+ int found;
+
+ enum {
+ CHANNEL_OTHER = 0x00,
+ CHANNEL_MASTER_VOLUME = 0x01,
+ CHANNEL_FRONT_RIGHT = 0x02,
+ CHANNEL_FRONT_LEFT = 0x03,
+ CHANNEL_BACK_RIGHT = 0x04,
+ CHANNEL_BACK_LEFT = 0x05,
+ CHANNEL_FRONT_CENTRE = 0x06,
+ CHANNEL_BACK_CENTRE = 0x07,
+ CHANNEL_SUBWOOFER = 0x08
+ };
+
+ found = 0;
+
+ /* relative volume adjustment information */
+
+ frame = id3_tag_findframe(tag, "RVA2", 0);
+ if (!frame) return 0;
+
+ id = id3_field_getlatin1(id3_frame_field(frame, 0));
+ data = id3_field_getbinarydata(id3_frame_field(frame, 1),
+ &length);
+
+ if (!id || !data) return 0;
+
+ /*
+ * "The 'identification' string is used to identify the
+ * situation and/or device where this adjustment should apply.
+ * The following is then repeated for every channel
+ *
+ * Type of channel $xx
+ * Volume adjustment $xx xx
+ * Bits representing peak $xx
+ * Peak volume $xx (xx ...)"
+ */
+
+ while (length >= 4) {
+ unsigned int peak_bytes;
+
+ peak_bytes = (data[3] + 7) / 8;
+ if (4 + peak_bytes > length)
+ break;
+
+ if (data[0] == CHANNEL_MASTER_VOLUME) {
+ signed int voladj_fixed;
+ double voladj_float;
+
+ /*
+ * "The volume adjustment is encoded as a fixed
+ * point decibel value, 16 bit signed integer
+ * representing (adjustment*512), giving +/- 64
+ * dB with a precision of 0.001953125 dB."
+ */
+
+ voladj_fixed = (data[1] << 8) | (data[2] << 0);
+ voladj_fixed |= -(voladj_fixed & 0x8000);
+
+ voladj_float = (double) voladj_fixed / 512;
+
+ replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak = voladj_float;
+ replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak = voladj_float;
+
+ g_debug("parseRVA2: Relative Volume "
+ "%+.1f dB adjustment (%s)\n",
+ voladj_float, id);
+
+ found = 1;
+ break;
+ }
+
+ data += 4 + peak_bytes;
+ length -= 4 + peak_bytes;
+ }
+
+ return found;
+}
+#endif
+
+#ifdef HAVE_ID3TAG
+static struct replay_gain_info *
+parse_id3_replay_gain_info(struct id3_tag *tag)
+{
+ int i;
+ char *key;
+ char *value;
+ struct id3_frame *frame;
+ bool found = false;
+ struct replay_gain_info *replay_gain_info;
+
+ replay_gain_info = replay_gain_info_new();
+
+ for (i = 0; (frame = id3_tag_findframe(tag, "TXXX", i)); i++) {
+ if (frame->nfields < 3)
+ continue;
+
+ key = (char *)
+ id3_ucs4_latin1duplicate(id3_field_getstring
+ (&frame->fields[1]));
+ value = (char *)
+ id3_ucs4_latin1duplicate(id3_field_getstring
+ (&frame->fields[2]));
+
+ if (g_ascii_strcasecmp(key, "replaygain_track_gain") == 0) {
+ replay_gain_info->tuples[REPLAY_GAIN_TRACK].gain = atof(value);
+ found = true;
+ } else if (g_ascii_strcasecmp(key, "replaygain_album_gain") == 0) {
+ replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain = atof(value);
+ found = true;
+ } else if (g_ascii_strcasecmp(key, "replaygain_track_peak") == 0) {
+ replay_gain_info->tuples[REPLAY_GAIN_TRACK].peak = atof(value);
+ found = true;
+ } else if (g_ascii_strcasecmp(key, "replaygain_album_peak") == 0) {
+ replay_gain_info->tuples[REPLAY_GAIN_ALBUM].peak = atof(value);
+ found = true;
+ }
+
+ free(key);
+ free(value);
+ }
+
+ if (!found) {
+ /* fall back on RVA2 if no replaygain tags found */
+ found = parse_rva2(tag, replay_gain_info);
+ }
+
+ if (found)
+ return replay_gain_info;
+ replay_gain_info_free(replay_gain_info);
+ return NULL;
+}
+#endif
+
+static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
+ struct tag **mpd_tag)
+{
+#ifdef HAVE_ID3TAG
+ struct id3_tag *id3_tag = NULL;
+ id3_length_t count;
+ id3_byte_t const *id3_data;
+ id3_byte_t *allocated = NULL;
+
+ count = data->stream.bufend - data->stream.this_frame;
+
+ if (tagsize <= count) {
+ id3_data = data->stream.this_frame;
+ mad_stream_skip(&(data->stream), tagsize);
+ } else {
+ allocated = g_malloc(tagsize);
+ memcpy(allocated, data->stream.this_frame, count);
+ mad_stream_skip(&(data->stream), count);
+
+ while (count < tagsize) {
+ size_t len;
+
+ len = decoder_read(data->decoder, data->input_stream,
+ allocated + count, tagsize - count);
+ if (len == 0)
+ break;
+ else
+ count += len;
+ }
+
+ if (count != tagsize) {
+ g_debug("error parsing ID3 tag");
+ g_free(allocated);
+ return;
+ }
+
+ id3_data = allocated;
+ }
+
+ id3_tag = id3_tag_parse(id3_data, tagsize);
+ if (id3_tag == NULL) {
+ g_free(allocated);
+ return;
+ }
+
+ if (mpd_tag) {
+ struct tag *tmp_tag = tag_id3_import(id3_tag);
+ if (tmp_tag != NULL) {
+ if (*mpd_tag != NULL)
+ tag_free(*mpd_tag);
+ *mpd_tag = tmp_tag;
+ }
+ }
+
+ if (data->decoder != NULL) {
+ struct replay_gain_info *tmp_rgi =
+ parse_id3_replay_gain_info(id3_tag);
+ if (tmp_rgi != NULL) {
+ decoder_replay_gain(data->decoder, tmp_rgi);
+ replay_gain_info_free(tmp_rgi);
+ data->found_replay_gain = true;
+ }
+ }
+
+ id3_tag_delete(id3_tag);
+
+ g_free(allocated);
+#else /* !HAVE_ID3TAG */
+ (void)mpd_tag;
+
+ /* This code is enabled when libid3tag is disabled. Instead
+ of parsing the ID3 frame, it just skips it. */
+
+ mad_stream_skip(&data->stream, tagsize);
+#endif
+}
+
+#ifndef HAVE_ID3TAG
+/**
+ * This function emulates libid3tag when it is disabled. Instead of
+ * doing a real analyzation of the frame, it just checks whether the
+ * frame begins with the string "ID3". If so, it returns the full
+ * length.
+ */
+static signed long
+id3_tag_query(const void *p0, size_t length)
+{
+ const char *p = p0;
+
+ return length > 3 && memcmp(p, "ID3", 3) == 0
+ ? length
+ : 0;
+}
+#endif /* !HAVE_ID3TAG */
+
+static enum mp3_action
+decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag)
+{
+ enum mad_layer layer;
+
+ if ((data->stream).buffer == NULL
+ || (data->stream).error == MAD_ERROR_BUFLEN) {
+ if (!mp3_fill_buffer(data))
+ return DECODE_BREAK;
+ }
+ if (mad_header_decode(&data->frame.header, &data->stream)) {
+ if ((data->stream).error == MAD_ERROR_LOSTSYNC &&
+ (data->stream).this_frame) {
+ signed long tagsize = id3_tag_query((data->stream).
+ this_frame,
+ (data->stream).
+ bufend -
+ (data->stream).
+ this_frame);
+
+ if (tagsize > 0) {
+ if (tag && !(*tag)) {
+ mp3_parse_id3(data, (size_t)tagsize,
+ tag);
+ } else {
+ mad_stream_skip(&(data->stream),
+ tagsize);
+ }
+ return DECODE_CONT;
+ }
+ }
+ if (MAD_RECOVERABLE((data->stream).error)) {
+ return DECODE_SKIP;
+ } else {
+ if ((data->stream).error == MAD_ERROR_BUFLEN)
+ return DECODE_CONT;
+ else {
+ g_warning("unrecoverable frame level error "
+ "(%s).\n",
+ mad_stream_errorstr(&data->stream));
+ return DECODE_BREAK;
+ }
+ }
+ }
+
+ layer = data->frame.header.layer;
+ if (!data->layer) {
+ if (layer != MAD_LAYER_II && layer != MAD_LAYER_III) {
+ /* Only layer 2 and 3 have been tested to work */
+ return DECODE_SKIP;
+ }
+ data->layer = layer;
+ } else if (layer != data->layer) {
+ /* Don't decode frames with a different layer than the first */
+ return DECODE_SKIP;
+ }
+
+ return DECODE_OK;
+}
+
+static enum mp3_action
+decodeNextFrame(struct mp3_data *data)
+{
+ if ((data->stream).buffer == NULL
+ || (data->stream).error == MAD_ERROR_BUFLEN) {
+ if (!mp3_fill_buffer(data))
+ return DECODE_BREAK;
+ }
+ if (mad_frame_decode(&data->frame, &data->stream)) {
+ if ((data->stream).error == MAD_ERROR_LOSTSYNC) {
+ signed long tagsize = id3_tag_query((data->stream).
+ this_frame,
+ (data->stream).
+ bufend -
+ (data->stream).
+ this_frame);
+ if (tagsize > 0) {
+ mad_stream_skip(&(data->stream), tagsize);
+ return DECODE_CONT;
+ }
+ }
+ if (MAD_RECOVERABLE((data->stream).error)) {
+ return DECODE_SKIP;
+ } else {
+ if ((data->stream).error == MAD_ERROR_BUFLEN)
+ return DECODE_CONT;
+ else {
+ g_warning("unrecoverable frame level error "
+ "(%s).\n",
+ mad_stream_errorstr(&data->stream));
+ return DECODE_BREAK;
+ }
+ }
+ }
+
+ return DECODE_OK;
+}
+
+/* xing stuff stolen from alsaplayer, and heavily modified by jat */
+#define XI_MAGIC (('X' << 8) | 'i')
+#define NG_MAGIC (('n' << 8) | 'g')
+#define IN_MAGIC (('I' << 8) | 'n')
+#define FO_MAGIC (('f' << 8) | 'o')
+
+enum xing_magic {
+ XING_MAGIC_XING, /* VBR */
+ XING_MAGIC_INFO /* CBR */
+};
+
+struct xing {
+ long flags; /* valid fields (see below) */
+ unsigned long frames; /* total number of frames */
+ unsigned long bytes; /* total number of bytes */
+ unsigned char toc[100]; /* 100-point seek table */
+ long scale; /* VBR quality */
+ enum xing_magic magic; /* header magic */
+};
+
+enum {
+ XING_FRAMES = 0x00000001L,
+ XING_BYTES = 0x00000002L,
+ XING_TOC = 0x00000004L,
+ XING_SCALE = 0x00000008L
+};
+
+struct version {
+ unsigned major;
+ unsigned minor;
+};
+
+struct lame {
+ char encoder[10]; /* 9 byte encoder name/version ("LAME3.97b") */
+ struct version version; /* struct containing just the version */
+ float peak; /* replaygain peak */
+ float track_gain; /* replaygain track gain */
+ float album_gain; /* replaygain album gain */
+ int encoder_delay; /* # of added samples at start of mp3 */
+ int encoder_padding; /* # of added samples at end of mp3 */
+ int crc; /* CRC of the first 190 bytes of this frame */
+};
+
+static bool
+parse_xing(struct xing *xing, struct mad_bitptr *ptr, int *oldbitlen)
+{
+ unsigned long bits;
+ int bitlen;
+ int bitsleft;
+ int i;
+
+ bitlen = *oldbitlen;
+
+ if (bitlen < 16)
+ return false;
+
+ bits = mad_bit_read(ptr, 16);
+ bitlen -= 16;
+
+ if (bits == XI_MAGIC) {
+ if (bitlen < 16)
+ return false;
+
+ if (mad_bit_read(ptr, 16) != NG_MAGIC)
+ return false;
+
+ bitlen -= 16;
+ xing->magic = XING_MAGIC_XING;
+ } else if (bits == IN_MAGIC) {
+ if (bitlen < 16)
+ return false;
+
+ if (mad_bit_read(ptr, 16) != FO_MAGIC)
+ return false;
+
+ bitlen -= 16;
+ xing->magic = XING_MAGIC_INFO;
+ }
+ else if (bits == NG_MAGIC) xing->magic = XING_MAGIC_XING;
+ else if (bits == FO_MAGIC) xing->magic = XING_MAGIC_INFO;
+ else
+ return false;
+
+ if (bitlen < 32)
+ return false;
+ xing->flags = mad_bit_read(ptr, 32);
+ bitlen -= 32;
+
+ if (xing->flags & XING_FRAMES) {
+ if (bitlen < 32)
+ return false;
+ xing->frames = mad_bit_read(ptr, 32);
+ bitlen -= 32;
+ }
+
+ if (xing->flags & XING_BYTES) {
+ if (bitlen < 32)
+ return false;
+ xing->bytes = mad_bit_read(ptr, 32);
+ bitlen -= 32;
+ }
+
+ if (xing->flags & XING_TOC) {
+ if (bitlen < 800)
+ return false;
+ for (i = 0; i < 100; ++i) xing->toc[i] = mad_bit_read(ptr, 8);
+ bitlen -= 800;
+ }
+
+ if (xing->flags & XING_SCALE) {
+ if (bitlen < 32)
+ return false;
+ xing->scale = mad_bit_read(ptr, 32);
+ bitlen -= 32;
+ }
+
+ /* Make sure we consume no less than 120 bytes (960 bits) in hopes that
+ * the LAME tag is found there, and not right after the Xing header */
+ bitsleft = 960 - ((*oldbitlen) - bitlen);
+ if (bitsleft < 0)
+ return false;
+ else if (bitsleft > 0) {
+ mad_bit_read(ptr, bitsleft);
+ bitlen -= bitsleft;
+ }
+
+ *oldbitlen = bitlen;
+
+ return true;
+}
+
+static bool
+parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen)
+{
+ int adj = 0;
+ int name;
+ int orig;
+ int sign;
+ int gain;
+ int i;
+
+ /* Unlike the xing header, the lame tag has a fixed length. Fail if
+ * not all 36 bytes (288 bits) are there. */
+ if (*bitlen < 288)
+ return false;
+
+ for (i = 0; i < 9; i++)
+ lame->encoder[i] = (char)mad_bit_read(ptr, 8);
+ lame->encoder[9] = '\0';
+
+ *bitlen -= 72;
+
+ /* This is technically incorrect, since the encoder might not be lame.
+ * But there's no other way to determine if this is a lame tag, and we
+ * wouldn't want to go reading a tag that's not there. */
+ if (!g_str_has_prefix(lame->encoder, "LAME"))
+ return false;
+
+ if (sscanf(lame->encoder+4, "%u.%u",
+ &lame->version.major, &lame->version.minor) != 2)
+ return false;
+
+ g_debug("detected LAME version %i.%i (\"%s\")\n",
+ lame->version.major, lame->version.minor, lame->encoder);
+
+ /* The reference volume was changed from the 83dB used in the
+ * ReplayGain spec to 89dB in lame 3.95.1. Bump the gain for older
+ * versions, since everyone else uses 89dB instead of 83dB.
+ * Unfortunately, lame didn't differentiate between 3.95 and 3.95.1, so
+ * it's impossible to make the proper adjustment for 3.95.
+ * Fortunately, 3.95 was only out for about a day before 3.95.1 was
+ * released. -- tmz */
+ if (lame->version.major < 3 ||
+ (lame->version.major == 3 && lame->version.minor < 95))
+ adj = 6;
+
+ mad_bit_read(ptr, 16);
+
+ lame->peak = mad_f_todouble(mad_bit_read(ptr, 32) << 5); /* peak */
+ g_debug("LAME peak found: %f\n", lame->peak);
+
+ lame->track_gain = 0;
+ name = mad_bit_read(ptr, 3); /* gain name */
+ orig = mad_bit_read(ptr, 3); /* gain originator */
+ sign = mad_bit_read(ptr, 1); /* sign bit */
+ gain = mad_bit_read(ptr, 9); /* gain*10 */
+ if (gain && name == 1 && orig != 0) {
+ lame->track_gain = ((sign ? -gain : gain) / 10.0) + adj;
+ g_debug("LAME track gain found: %f\n", lame->track_gain);
+ }
+
+ /* tmz reports that this isn't currently written by any version of lame
+ * (as of 3.97). Since we have no way of testing it, don't use it.
+ * Wouldn't want to go blowing someone's ears just because we read it
+ * wrong. :P -- jat */
+ lame->album_gain = 0;
+#if 0
+ name = mad_bit_read(ptr, 3); /* gain name */
+ orig = mad_bit_read(ptr, 3); /* gain originator */
+ sign = mad_bit_read(ptr, 1); /* sign bit */
+ gain = mad_bit_read(ptr, 9); /* gain*10 */
+ if (gain && name == 2 && orig != 0) {
+ lame->album_gain = ((sign ? -gain : gain) / 10.0) + adj;
+ g_debug("LAME album gain found: %f\n", lame->track_gain);
+ }
+#else
+ mad_bit_read(ptr, 16);
+#endif
+
+ mad_bit_read(ptr, 16);
+
+ lame->encoder_delay = mad_bit_read(ptr, 12);
+ lame->encoder_padding = mad_bit_read(ptr, 12);
+
+ g_debug("encoder delay is %i, encoder padding is %i\n",
+ lame->encoder_delay, lame->encoder_padding);
+
+ mad_bit_read(ptr, 80);
+
+ lame->crc = mad_bit_read(ptr, 16);
+
+ *bitlen -= 216;
+
+ return true;
+}
+
+static inline float
+mp3_frame_duration(const struct mad_frame *frame)
+{
+ return mad_timer_count(frame->header.duration,
+ MAD_UNITS_MILLISECONDS) / 1000.0;
+}
+
+static goffset
+mp3_this_frame_offset(const struct mp3_data *data)
+{
+ goffset offset = data->input_stream->offset;
+
+ if (data->stream.this_frame != NULL)
+ offset -= data->stream.bufend - data->stream.this_frame;
+ else
+ offset -= data->stream.bufend - data->stream.buffer;
+
+ return offset;
+}
+
+static goffset
+mp3_rest_including_this_frame(const struct mp3_data *data)
+{
+ return data->input_stream->size - mp3_this_frame_offset(data);
+}
+
+/**
+ * Attempt to calulcate the length of the song from filesize
+ */
+static void
+mp3_filesize_to_song_length(struct mp3_data *data)
+{
+ goffset rest = mp3_rest_including_this_frame(data);
+
+ if (rest > 0) {
+ float frame_duration = mp3_frame_duration(&data->frame);
+
+ data->total_time = (rest * 8.0) / (data->frame).header.bitrate;
+ data->max_frames = data->total_time / frame_duration +
+ FRAMES_CUSHION;
+ } else {
+ data->max_frames = FRAMES_CUSHION;
+ data->total_time = 0;
+ }
+}
+
+static bool
+mp3_decode_first_frame(struct mp3_data *data, struct tag **tag)
+{
+ struct xing xing;
+ struct lame lame;
+ struct mad_bitptr ptr;
+ int bitlen;
+ enum mp3_action ret;
+
+ /* stfu gcc */
+ memset(&xing, 0, sizeof(struct xing));
+ xing.flags = 0;
+
+ while (true) {
+ do {
+ ret = decode_next_frame_header(data, tag);
+ } while (ret == DECODE_CONT);
+ if (ret == DECODE_BREAK)
+ return false;
+ if (ret == DECODE_SKIP) continue;
+
+ do {
+ ret = decodeNextFrame(data);
+ } while (ret == DECODE_CONT);
+ if (ret == DECODE_BREAK)
+ return false;
+ if (ret == DECODE_OK) break;
+ }
+
+ ptr = data->stream.anc_ptr;
+ bitlen = data->stream.anc_bitlen;
+
+ mp3_filesize_to_song_length(data);
+
+ /*
+ * if an xing tag exists, use that!
+ */
+ if (parse_xing(&xing, &ptr, &bitlen)) {
+ data->found_xing = true;
+ data->mute_frame = MUTEFRAME_SKIP;
+
+ if ((xing.flags & XING_FRAMES) && xing.frames) {
+ mad_timer_t duration = data->frame.header.duration;
+ mad_timer_multiply(&duration, xing.frames);
+ data->total_time = ((float)mad_timer_count(duration, MAD_UNITS_MILLISECONDS)) / 1000;
+ data->max_frames = xing.frames;
+ }
+
+ if (parse_lame(&lame, &ptr, &bitlen)) {
+ if (gapless_playback &&
+ data->input_stream->seekable) {
+ data->drop_start_samples = lame.encoder_delay +
+ DECODERDELAY;
+ data->drop_end_samples = lame.encoder_padding;
+ }
+
+ /* Album gain isn't currently used. See comment in
+ * parse_lame() for details. -- jat */
+ if (data->decoder != NULL &&
+ !data->found_replay_gain &&
+ lame.track_gain) {
+ struct replay_gain_info *rgi
+ = replay_gain_info_new();
+ rgi->tuples[REPLAY_GAIN_TRACK].gain = lame.track_gain;
+ rgi->tuples[REPLAY_GAIN_TRACK].peak = lame.peak;
+ decoder_replay_gain(data->decoder, rgi);
+ replay_gain_info_free(rgi);
+ }
+ }
+ }
+
+ if (!data->max_frames)
+ return false;
+
+ if (data->max_frames > 8 * 1024 * 1024) {
+ g_warning("mp3 file header indicates too many frames: %lu\n",
+ data->max_frames);
+ return false;
+ }
+
+ data->frame_offsets = g_malloc(sizeof(long) * data->max_frames);
+ data->times = g_malloc(sizeof(mad_timer_t) * data->max_frames);
+
+ return true;
+}
+
+static void mp3_data_finish(struct mp3_data *data)
+{
+ mad_synth_finish(&data->synth);
+ mad_frame_finish(&data->frame);
+ mad_stream_finish(&data->stream);
+
+ g_free(data->frame_offsets);
+ g_free(data->times);
+}
+
+/* this is primarily used for getting total time for tags */
+static int
+mad_decoder_total_file_time(struct input_stream *is)
+{
+ struct mp3_data data;
+ int ret;
+
+ mp3_data_init(&data, NULL, is);
+ if (!mp3_decode_first_frame(&data, NULL))
+ ret = -1;
+ else
+ ret = data.total_time + 0.5;
+ mp3_data_finish(&data);
+
+ return ret;
+}
+
+static bool
+mp3_open(struct input_stream *is, struct mp3_data *data,
+ struct decoder *decoder, struct tag **tag)
+{
+ mp3_data_init(data, decoder, is);
+ *tag = NULL;
+ if (!mp3_decode_first_frame(data, tag)) {
+ mp3_data_finish(data);
+ if (tag && *tag)
+ tag_free(*tag);
+ return false;
+ }
+
+ return true;
+}
+
+static long
+mp3_time_to_frame(const struct mp3_data *data, double t)
+{
+ unsigned long i;
+
+ for (i = 0; i < data->highest_frame; ++i) {
+ double frame_time =
+ mad_timer_count(data->times[i],
+ MAD_UNITS_MILLISECONDS) / 1000.;
+ if (frame_time >= t)
+ break;
+ }
+
+ return i;
+}
+
+static void
+mp3_update_timer_next_frame(struct mp3_data *data)
+{
+ if (data->current_frame >= data->highest_frame) {
+ /* record this frame's properties in
+ data->frame_offsets (for seeking) and
+ data->times */
+ data->bit_rate = (data->frame).header.bitrate;
+
+ if (data->current_frame >= data->max_frames)
+ /* cap data->current_frame */
+ data->current_frame = data->max_frames - 1;
+ else
+ data->highest_frame++;
+
+ data->frame_offsets[data->current_frame] =
+ mp3_this_frame_offset(data);
+
+ mad_timer_add(&data->timer, (data->frame).header.duration);
+ data->times[data->current_frame] = data->timer;
+ } else
+ /* get the new timer value from data->times */
+ data->timer = data->times[data->current_frame];
+
+ data->current_frame++;
+ data->elapsed_time =
+ mad_timer_count(data->timer, MAD_UNITS_MILLISECONDS) / 1000.0;
+}
+
+/**
+ * Sends the synthesized current frame via decoder_data().
+ */
+static enum decoder_command
+mp3_send_pcm(struct mp3_data *data, unsigned i, unsigned pcm_length)
+{
+ unsigned max_samples;
+
+ max_samples = sizeof(data->output_buffer) /
+ sizeof(data->output_buffer[0]) /
+ MAD_NCHANNELS(&(data->frame).header);
+
+ while (i < pcm_length) {
+ enum decoder_command cmd;
+ unsigned int num_samples = pcm_length - i;
+ if (num_samples > max_samples)
+ num_samples = max_samples;
+
+ i += num_samples;
+
+ mad_fixed_to_24_buffer(data->output_buffer,
+ &data->synth,
+ i - num_samples, i,
+ MAD_NCHANNELS(&(data->frame).header));
+ num_samples *= MAD_NCHANNELS(&(data->frame).header);
+
+ cmd = decoder_data(data->decoder, data->input_stream,
+ data->output_buffer,
+ sizeof(data->output_buffer[0]) * num_samples,
+ data->bit_rate / 1000);
+ if (cmd != DECODE_COMMAND_NONE)
+ return cmd;
+ }
+
+ return DECODE_COMMAND_NONE;
+}
+
+/**
+ * Synthesize the current frame and send it via decoder_data().
+ */
+static enum decoder_command
+mp3_synth_and_send(struct mp3_data *data)
+{
+ unsigned i, pcm_length;
+ enum decoder_command cmd;
+
+ mad_synth_frame(&data->synth, &data->frame);
+
+ if (!data->found_first_frame) {
+ unsigned int samples_per_frame = data->synth.pcm.length;
+ data->drop_start_frames = data->drop_start_samples / samples_per_frame;
+ data->drop_end_frames = data->drop_end_samples / samples_per_frame;
+ data->drop_start_samples = data->drop_start_samples % samples_per_frame;
+ data->drop_end_samples = data->drop_end_samples % samples_per_frame;
+ data->found_first_frame = true;
+ }
+
+ if (data->drop_start_frames > 0) {
+ data->drop_start_frames--;
+ return DECODE_COMMAND_NONE;
+ } else if ((data->drop_end_frames > 0) &&
+ (data->current_frame == (data->max_frames + 1 - data->drop_end_frames))) {
+ /* stop decoding, effectively dropping all remaining
+ frames */
+ return DECODE_COMMAND_STOP;
+ }
+
+ if (!data->decoded_first_frame) {
+ i = data->drop_start_samples;
+ data->decoded_first_frame = true;
+ } else
+ i = 0;
+
+ pcm_length = data->synth.pcm.length;
+ if (data->drop_end_samples &&
+ (data->current_frame == data->max_frames - data->drop_end_frames)) {
+ if (data->drop_end_samples >= pcm_length)
+ pcm_length = 0;
+ else
+ pcm_length -= data->drop_end_samples;
+ }
+
+ cmd = mp3_send_pcm(data, i, pcm_length);
+ if (cmd != DECODE_COMMAND_NONE)
+ return cmd;
+
+ if (data->drop_end_samples &&
+ (data->current_frame == data->max_frames - data->drop_end_frames))
+ /* stop decoding, effectively dropping
+ * all remaining samples */
+ return DECODE_COMMAND_STOP;
+
+ return DECODE_COMMAND_NONE;
+}
+
+static bool
+mp3_read(struct mp3_data *data)
+{
+ struct decoder *decoder = data->decoder;
+ enum mp3_action ret;
+ enum decoder_command cmd;
+
+ mp3_update_timer_next_frame(data);
+
+ switch (data->mute_frame) {
+ case MUTEFRAME_SKIP:
+ data->mute_frame = MUTEFRAME_NONE;
+ break;
+ case MUTEFRAME_SEEK:
+ if (data->elapsed_time >= data->seek_where)
+ data->mute_frame = MUTEFRAME_NONE;
+ break;
+ case MUTEFRAME_NONE:
+ cmd = mp3_synth_and_send(data);
+ if (cmd == DECODE_COMMAND_SEEK) {
+ unsigned long j;
+
+ assert(data->input_stream->seekable);
+
+ j = mp3_time_to_frame(data,
+ decoder_seek_where(decoder));
+ if (j < data->highest_frame) {
+ if (mp3_seek(data, data->frame_offsets[j])) {
+ data->current_frame = j;
+ decoder_command_finished(decoder);
+ } else
+ decoder_seek_error(decoder);
+ } else {
+ data->seek_where = decoder_seek_where(decoder);
+ data->mute_frame = MUTEFRAME_SEEK;
+ decoder_command_finished(decoder);
+ }
+ } else if (cmd != DECODE_COMMAND_NONE)
+ return false;
+ }
+
+ while (true) {
+ bool skip = false;
+
+ do {
+ struct tag *tag = NULL;
+
+ ret = decode_next_frame_header(data, &tag);
+
+ if (tag != NULL) {
+ decoder_tag(decoder, data->input_stream, tag);
+ tag_free(tag);
+ }
+ } while (ret == DECODE_CONT);
+ if (ret == DECODE_BREAK)
+ return false;
+ else if (ret == DECODE_SKIP)
+ skip = true;
+
+ if (data->mute_frame == MUTEFRAME_NONE) {
+ do {
+ ret = decodeNextFrame(data);
+ } while (ret == DECODE_CONT);
+ if (ret == DECODE_BREAK)
+ return false;
+ }
+
+ if (!skip && ret == DECODE_OK)
+ break;
+ }
+
+ return ret != DECODE_BREAK;
+}
+
+static void
+mp3_decode(struct decoder *decoder, struct input_stream *input_stream)
+{
+ struct mp3_data data;
+ GError *error = NULL;
+ struct tag *tag = NULL;
+ struct audio_format audio_format;
+
+ if (!mp3_open(input_stream, &data, decoder, &tag)) {
+ if (decoder_get_command(decoder) == DECODE_COMMAND_NONE)
+ g_warning
+ ("Input does not appear to be a mp3 bit stream.\n");
+ return;
+ }
+
+ if (!audio_format_init_checked(&audio_format,
+ data.frame.header.samplerate,
+ SAMPLE_FORMAT_S24_P32,
+ MAD_NCHANNELS(&data.frame.header),
+ &error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+
+ if (tag != NULL)
+ tag_free(tag);
+ mp3_data_finish(&data);
+ return;
+ }
+
+ decoder_initialized(decoder, &audio_format,
+ data.input_stream->seekable, data.total_time);
+
+ if (tag != NULL) {
+ decoder_tag(decoder, input_stream, tag);
+ tag_free(tag);
+ }
+
+ while (mp3_read(&data)) ;
+
+ if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK &&
+ data.mute_frame == MUTEFRAME_SEEK)
+ decoder_command_finished(decoder);
+
+ mp3_data_finish(&data);
+}
+
+static struct tag *
+mad_decoder_stream_tag(struct input_stream *is)
+{
+ struct tag *tag;
+ int total_time;
+
+ total_time = mad_decoder_total_file_time(is);
+ if (total_time < 0)
+ return NULL;
+
+ tag = tag_new();
+ tag->time = total_time;
+ return tag;
+}
+
+static const char *const mp3_suffixes[] = { "mp3", "mp2", NULL };
+static const char *const mp3_mime_types[] = { "audio/mpeg", NULL };
+
+const struct decoder_plugin mad_decoder_plugin = {
+ .name = "mad",
+ .init = mp3_plugin_init,
+ .stream_decode = mp3_decode,
+ .stream_tag = mad_decoder_stream_tag,
+ .suffixes = mp3_suffixes,
+ .mime_types = mp3_mime_types
+};