diff options
Diffstat (limited to '')
-rw-r--r-- | src/decoder/mad_decoder_plugin.c (renamed from src/decoder/mad_plugin.c) | 288 |
1 files changed, 117 insertions, 171 deletions
diff --git a/src/decoder/mad_plugin.c b/src/decoder/mad_decoder_plugin.c index 9b3259485..a11d1b020 100644 --- a/src/decoder/mad_plugin.c +++ b/src/decoder/mad_decoder_plugin.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2009 The Music Player Daemon Project + * 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 @@ -17,10 +17,12 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "../decoder_api.h" -#include "../conf.h" #include "config.h" +#include "decoder_api.h" +#include "conf.h" #include "tag_id3.h" +#include "tag_rva2.h" +#include "audio_check.h" #include <assert.h> #include <unistd.h> @@ -125,6 +127,7 @@ struct mp3_data { 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; @@ -148,6 +151,7 @@ mp3_data_init(struct mp3_data *data, struct decoder *decoder, 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; @@ -164,7 +168,7 @@ mp3_data_init(struct mp3_data *data, struct decoder *decoder, static bool mp3_seek(struct mp3_data *data, long offset) { - if (!input_stream_seek(data->input_stream, offset, SEEK_SET)) + if (!input_stream_seek(data->input_stream, offset, SEEK_SET, NULL)) return false; mad_stream_buffer(&data->stream, data->input_buffer, 0); @@ -208,105 +212,17 @@ mp3_fill_buffer(struct mp3_data *data) } #ifdef HAVE_ID3TAG -/* Parse mp3 RVA2 frame. Shamelessly stolen from madplay. */ static bool -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; - - 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 - }; - - /* relative volume adjustment information */ - - frame = id3_tag_findframe(tag, "RVA2", 0); - if (frame == NULL) - return false; - - id = id3_field_getlatin1(id3_frame_field(frame, 0)); - data = id3_field_getbinarydata(id3_frame_field(frame, 1), - &length); - - if (id == NULL || data == NULL) - return false; - - /* - * "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].gain = voladj_float; - replay_gain_info->tuples[REPLAY_GAIN_ALBUM].gain = voladj_float; - - g_debug("parseRVA2: Relative Volume " - "%+.1f dB adjustment (%s)\n", - voladj_float, id); - - return true; - } - - data += 4 + peak_bytes; - length -= 4 + peak_bytes; - } - - return false; -} -#endif - -#ifdef HAVE_ID3TAG -static struct replay_gain_info * -parse_id3_replay_gain_info(struct id3_tag *tag) +parse_id3_replay_gain_info(struct replay_gain_info *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(); + replay_gain_info_init(replay_gain_info); for (i = 0; (frame = id3_tag_findframe(tag, "TXXX", i)); i++) { if (frame->nfields < 3) @@ -337,21 +253,55 @@ parse_id3_replay_gain_info(struct id3_tag *tag) free(value); } - if (!found) { + return found || /* fall back on RVA2 if no replaygain tags found */ - found = parse_rva2(tag, replay_gain_info); + tag_rva2_parse(tag, replay_gain_info); +} +#endif + +#ifdef HAVE_ID3TAG +static bool +parse_id3_mixramp(char **mixramp_start, char **mixramp_end, + struct id3_tag *tag) +{ + int i; + char *key; + char *value; + struct id3_frame *frame; + bool found = false; + + *mixramp_start = NULL; + *mixramp_end = NULL; + + 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, "mixramp_start") == 0) { + *mixramp_start = g_strdup(value); + found = true; + } else if (g_ascii_strcasecmp(key, "mixramp_end") == 0) { + *mixramp_end = g_strdup(value); + found = true; + } + + free(key); + free(value); } - if (found) - return replay_gain_info; - replay_gain_info_free(replay_gain_info); - return NULL; + return found; } #endif static void mp3_parse_id3(struct mp3_data *data, size_t tagsize, - struct tag **mpd_tag, - struct replay_gain_info **replay_gain_info_r) + struct tag **mpd_tag) { #ifdef HAVE_ID3TAG struct id3_tag *id3_tag = NULL; @@ -404,13 +354,20 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize, } } - if (replay_gain_info_r) { - struct replay_gain_info *tmp_rgi = - parse_id3_replay_gain_info(id3_tag); - if (tmp_rgi != NULL) { - if (*replay_gain_info_r) - replay_gain_info_free(*replay_gain_info_r); - *replay_gain_info_r = tmp_rgi; + if (data->decoder != NULL) { + struct replay_gain_info rgi; + char *mixramp_start; + char *mixramp_end; + float replay_gain_db = 0; + + if (parse_id3_replay_gain_info(&rgi, id3_tag)) { + replay_gain_db = decoder_replay_gain(data->decoder, &rgi); + data->found_replay_gain = true; + } + if (parse_id3_mixramp(&mixramp_start, &mixramp_end, id3_tag)) { + g_debug("setting mixramp_tags"); + decoder_mixramp(data->decoder, replay_gain_db, + mixramp_start, mixramp_end); } } @@ -419,7 +376,6 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize, g_free(allocated); #else /* !HAVE_ID3TAG */ (void)mpd_tag; - (void)replay_gain_info_r; /* This code is enabled when libid3tag is disabled. Instead of parsing the ID3 frame, it just skips it. */ @@ -467,8 +423,7 @@ id3_tag_query(const void *p0, size_t length) #endif /* !HAVE_ID3TAG */ static enum mp3_action -decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag, - G_GNUC_UNUSED struct replay_gain_info **replay_gain_info_r) +decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag) { enum mad_layer layer; @@ -490,7 +445,7 @@ decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag, if (tagsize > 0) { if (tag && !(*tag)) { mp3_parse_id3(data, (size_t)tagsize, - tag, replay_gain_info_r); + tag); } else { mad_stream_skip(&(data->stream), tagsize); @@ -798,10 +753,10 @@ mp3_frame_duration(const struct mad_frame *frame) MAD_UNITS_MILLISECONDS) / 1000.0; } -static off_t +static goffset mp3_this_frame_offset(const struct mp3_data *data) { - off_t offset = data->input_stream->offset; + goffset offset = data->input_stream->offset; if (data->stream.this_frame != NULL) offset -= data->stream.bufend - data->stream.this_frame; @@ -811,7 +766,7 @@ mp3_this_frame_offset(const struct mp3_data *data) return offset; } -static off_t +static goffset mp3_rest_including_this_frame(const struct mp3_data *data) { return data->input_stream->size - mp3_this_frame_offset(data); @@ -823,7 +778,7 @@ mp3_rest_including_this_frame(const struct mp3_data *data) static void mp3_filesize_to_song_length(struct mp3_data *data) { - off_t rest = mp3_rest_including_this_frame(data); + goffset rest = mp3_rest_including_this_frame(data); if (rest > 0) { float frame_duration = mp3_frame_duration(&data->frame); @@ -838,8 +793,7 @@ mp3_filesize_to_song_length(struct mp3_data *data) } static bool -mp3_decode_first_frame(struct mp3_data *data, struct tag **tag, - struct replay_gain_info **replay_gain_info_r) +mp3_decode_first_frame(struct mp3_data *data, struct tag **tag) { struct xing xing; struct lame lame; @@ -853,8 +807,7 @@ mp3_decode_first_frame(struct mp3_data *data, struct tag **tag, while (true) { do { - ret = decode_next_frame_header(data, tag, - replay_gain_info_r); + ret = decode_next_frame_header(data, tag); } while (ret == DECODE_CONT); if (ret == DECODE_BREAK) return false; @@ -897,14 +850,17 @@ mp3_decode_first_frame(struct mp3_data *data, struct tag **tag, /* Album gain isn't currently used. See comment in * parse_lame() for details. -- jat */ - if (replay_gain_info_r && !*replay_gain_info_r && + if (data->decoder != NULL && + !data->found_replay_gain && lame.track_gain) { - *replay_gain_info_r = replay_gain_info_new(); - (*replay_gain_info_r)->tuples[REPLAY_GAIN_TRACK].gain = lame.track_gain; - (*replay_gain_info_r)->tuples[REPLAY_GAIN_TRACK].peak = lame.peak; + struct replay_gain_info rgi; + replay_gain_info_init(&rgi); + rgi.tuples[REPLAY_GAIN_TRACK].gain = lame.track_gain; + rgi.tuples[REPLAY_GAIN_TRACK].peak = lame.peak; + decoder_replay_gain(data->decoder, &rgi); } } - } + } if (!data->max_frames) return false; @@ -932,33 +888,29 @@ static void mp3_data_finish(struct mp3_data *data) } /* this is primarily used for getting total time for tags */ -static int mp3_total_file_time(const char *file) +static int +mad_decoder_total_file_time(struct input_stream *is) { - struct input_stream input_stream; struct mp3_data data; int ret; - if (!input_stream_open(&input_stream, file)) - return -1; - mp3_data_init(&data, NULL, &input_stream); - if (!mp3_decode_first_frame(&data, NULL, NULL)) + 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); - input_stream_close(&input_stream); return ret; } static bool mp3_open(struct input_stream *is, struct mp3_data *data, - struct decoder *decoder, struct tag **tag, - struct replay_gain_info **replay_gain_info_r) + struct decoder *decoder, struct tag **tag) { mp3_data_init(data, decoder, is); *tag = NULL; - if (!mp3_decode_first_frame(data, tag, replay_gain_info_r)) { + if (!mp3_decode_first_frame(data, tag)) { mp3_data_finish(data); if (tag && *tag) tag_free(*tag); @@ -1017,8 +969,7 @@ mp3_update_timer_next_frame(struct mp3_data *data) * Sends the synthesized current frame via decoder_data(). */ static enum decoder_command -mp3_send_pcm(struct mp3_data *data, unsigned i, unsigned pcm_length, - struct replay_gain_info *replay_gain_info) +mp3_send_pcm(struct mp3_data *data, unsigned i, unsigned pcm_length) { unsigned max_samples; @@ -1043,9 +994,7 @@ mp3_send_pcm(struct mp3_data *data, unsigned i, unsigned pcm_length, cmd = decoder_data(data->decoder, data->input_stream, data->output_buffer, sizeof(data->output_buffer[0]) * num_samples, - data->elapsed_time, - data->bit_rate / 1000, - replay_gain_info); + data->bit_rate / 1000); if (cmd != DECODE_COMMAND_NONE) return cmd; } @@ -1057,8 +1006,7 @@ mp3_send_pcm(struct mp3_data *data, unsigned i, unsigned pcm_length, * Synthesize the current frame and send it via decoder_data(). */ static enum decoder_command -mp3_synth_and_send(struct mp3_data *data, - struct replay_gain_info *replay_gain_info) +mp3_synth_and_send(struct mp3_data *data) { unsigned i, pcm_length; enum decoder_command cmd; @@ -1099,7 +1047,7 @@ mp3_synth_and_send(struct mp3_data *data, pcm_length -= data->drop_end_samples; } - cmd = mp3_send_pcm(data, i, pcm_length, replay_gain_info); + cmd = mp3_send_pcm(data, i, pcm_length); if (cmd != DECODE_COMMAND_NONE) return cmd; @@ -1113,7 +1061,7 @@ mp3_synth_and_send(struct mp3_data *data, } static bool -mp3_read(struct mp3_data *data, struct replay_gain_info **replay_gain_info_r) +mp3_read(struct mp3_data *data) { struct decoder *decoder = data->decoder; enum mp3_action ret; @@ -1130,9 +1078,7 @@ mp3_read(struct mp3_data *data, struct replay_gain_info **replay_gain_info_r) data->mute_frame = MUTEFRAME_NONE; break; case MUTEFRAME_NONE: - cmd = mp3_synth_and_send(data, - replay_gain_info_r != NULL - ? *replay_gain_info_r : NULL); + cmd = mp3_synth_and_send(data); if (cmd == DECODE_COMMAND_SEEK) { unsigned long j; @@ -1161,8 +1107,7 @@ mp3_read(struct mp3_data *data, struct replay_gain_info **replay_gain_info_r) do { struct tag *tag = NULL; - ret = decode_next_frame_header(data, &tag, - replay_gain_info_r); + ret = decode_next_frame_header(data, &tag); if (tag != NULL) { decoder_tag(decoder, data->input_stream, tag); @@ -1189,29 +1134,34 @@ mp3_read(struct mp3_data *data, struct replay_gain_info **replay_gain_info_r) return ret != DECODE_BREAK; } -static void mp3_audio_format(struct mp3_data *data, struct audio_format *af) -{ - af->bits = 24; - af->sample_rate = (data->frame).header.samplerate; - af->channels = MAD_NCHANNELS(&(data->frame).header); -} - static void mp3_decode(struct decoder *decoder, struct input_stream *input_stream) { struct mp3_data data; + GError *error = NULL; struct tag *tag = NULL; - struct replay_gain_info *replay_gain_info = NULL; struct audio_format audio_format; - if (!mp3_open(input_stream, &data, decoder, &tag, &replay_gain_info)) { + 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; } - mp3_audio_format(&data, &audio_format); + 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); @@ -1221,24 +1171,20 @@ mp3_decode(struct decoder *decoder, struct input_stream *input_stream) tag_free(tag); } - while (mp3_read(&data, &replay_gain_info)) ; - - if (replay_gain_info) - replay_gain_info_free(replay_gain_info); + while (mp3_read(&data)) ; mp3_data_finish(&data); } -static struct tag *mp3_tag_dup(const char *file) +static struct tag * +mad_decoder_stream_tag(struct input_stream *is) { struct tag *tag; int total_time; - total_time = mp3_total_file_time(file); - if (total_time < 0) { - g_debug("Failed to get total song time from: %s", file); + total_time = mad_decoder_total_file_time(is); + if (total_time < 0) return NULL; - } tag = tag_new(); tag->time = total_time; @@ -1252,7 +1198,7 @@ const struct decoder_plugin mad_decoder_plugin = { .name = "mad", .init = mp3_plugin_init, .stream_decode = mp3_decode, - .tag_dup = mp3_tag_dup, + .stream_tag = mad_decoder_stream_tag, .suffixes = mp3_suffixes, .mime_types = mp3_mime_types }; |