diff options
author | Pauli Virtanen <pav@iki.fi> | 2008-12-28 13:02:34 +0100 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2008-12-28 13:02:34 +0100 |
commit | 65b18644e1dc9f62b29b7b761b6409d68221721f (patch) | |
tree | 4915488e99b016ac6db6aa7c93926328e314d943 /src/decoder/mp3_plugin.c | |
parent | ed9668f638ec3a31096821648f289b8a243f4ab2 (diff) | |
download | mpd-65b18644e1dc9f62b29b7b761b6409d68221721f.tar.gz mpd-65b18644e1dc9f62b29b7b761b6409d68221721f.tar.xz mpd-65b18644e1dc9f62b29b7b761b6409d68221721f.zip |
Add RVA2 tag support to MPD
This patch adds RVA2 (relative volume adjustment) tag
support to mpd, as a fallback if no replaygain tags are
found. The code is almost directly from madplay (GPL).
RVA2 tags are generated for example by the "normalize" utility.
Updated by: Avuton Olrich <avuton@gmail.com>
Diffstat (limited to 'src/decoder/mp3_plugin.c')
-rw-r--r-- | src/decoder/mp3_plugin.c | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/src/decoder/mp3_plugin.c b/src/decoder/mp3_plugin.c index 3a908299f..6fedf3766 100644 --- a/src/decoder/mp3_plugin.c +++ b/src/decoder/mp3_plugin.c @@ -203,6 +203,95 @@ mp3_fill_buffer(struct mp3_data *data) } #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) { @@ -244,6 +333,11 @@ parse_id3_replay_gain_info(struct id3_tag *tag) 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); |