From a2a3c8c0a331899018c59567f6abb684aeab926e Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Sat, 14 Mar 2009 17:16:12 -0400 Subject: [PATCH 2/2] Read ReplayGain values from ID3 tags Support ReplayGain data in common ID3 tags. The order of preference for ReplayGain data is now ID3, APE, and lastly the LAME tag. --- ChangeLog | 5 ++ src/mp3file.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 150 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index efa5d06..ba7c9ca 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,11 @@ 2009-03-17 Todd Zullinger * src/mp3file.c: + Add support for reading ReplayGain data in common ID3 tags. + The order of preference for ReplayGain data is now ID3, APE, + and lastly the LAME tag. + + * src/mp3file.c: Prefer Audiophile (Album) over Radio (Track) gain. Adjust the ordering of the tags checked. We now check the APE diff --git a/src/mp3file.c b/src/mp3file.c index 7e8599b..5a82f9b 100644 --- a/src/mp3file.c +++ b/src/mp3file.c @@ -2081,6 +2081,124 @@ rg_fail: return FALSE; } +/* + * mp3_get_track_id3_replaygain: + * + * @path: location of the file + * @gd: #GainData structure + * + * Read the specified file and scan for ReplayGain information in + * common ID3v2 tags. + * + */ +gboolean mp3_get_track_id3_replaygain (const gchar *path, GainData *gd) +{ + int i; + double d; + char *ep, *key, *val; + struct id3_file *id3file; + struct id3_tag *id3tag; + struct id3_frame *frame; + + g_return_val_if_fail (path, FALSE); + g_return_val_if_fail (gd, FALSE); + + gd->radio_gain = 0; + gd->audiophile_gain = 0; + gd->peak_signal = 0; + gd->radio_gain_set = FALSE; + gd->audiophile_gain_set = FALSE; + gd->peak_signal_set = FALSE; + + if (!(id3file = id3_file_open (path, ID3_FILE_MODE_READONLY))) + { + gchar *fbuf = charset_to_utf8 (path); + g_print(_("ERROR while opening file: '%s' (%s).\n"), + fbuf, g_strerror(errno)); + g_free (fbuf); + return FALSE; + } + + if (!(id3tag = id3_file_tag (id3file))) + { + id3_file_close (id3file); + return FALSE; + } + + for (i = 0; (frame = id3_tag_findframe (id3tag, "TXXX", i)); i++) + { + if (gd->radio_gain_set && gd->audiophile_gain_set + && gd->peak_signal_set) + break; + + if (frame->nfields < 3) + continue; + + key = (char *)id3_ucs4_utf8duplicate (id3_field_getstring + (&frame->fields[1])); + + val = (char *)id3_ucs4_utf8duplicate (id3_field_getstring + (&frame->fields[2])); + + if (g_ascii_strcasecmp (key, "replaygain_album_gain") == 0) + { + d = g_ascii_strtod (val, &ep); + if (!g_ascii_strncasecmp (ep, " dB", 3)) + { + gd->audiophile_gain = d; + gd->audiophile_gain_set = TRUE; +#if LOCALDEBUG + printf ("album gain (id3): %f\n", gd->audiophile_gain); +#endif + } + } + else if (g_ascii_strcasecmp (key, "replaygain_album_peak") == 0) + { + d = g_ascii_strtod (val, NULL); + d *= 0x800000; + gd->peak_signal = (guint32) floor (d + 0.5); + gd->peak_signal_set = TRUE; +#if LOCALDEBUG + printf ("album peak signal (id3): %f\n", + (double)gd->peak_signal / 0x800000); +#endif + } + else if (g_ascii_strcasecmp (key, "replaygain_track_gain") == 0) + { + d = g_ascii_strtod (val, &ep); + if (!g_ascii_strncasecmp (ep, " dB", 3)) + { + gd->radio_gain = d; + gd->radio_gain_set = TRUE; +#if LOCALDEBUG + printf ("radio gain (id3): %f\n", gd->radio_gain); +#endif + } + } + else if (g_ascii_strcasecmp (key, "replaygain_track_peak") == 0) + { + d = g_ascii_strtod (val, NULL); + d *= 0x800000; + gd->peak_signal = (guint32) floor (d + 0.5); + gd->peak_signal_set = TRUE; +#if LOCALDEBUG + printf ("radio peak signal (id3): %f\n", + (double)gd->peak_signal / 0x800000); +#endif + } + + g_free (key); + g_free (val); + } + + id3_file_close (id3file); + + if (!gd->radio_gain_set && !gd->audiophile_gain_set + && !gd->peak_signal_set) + return FALSE; + return TRUE; +} + /* * mp3_get_track_ape_replaygain: * @@ -2302,19 +2420,20 @@ rg_fail: #include - - - - -/** +/* * mp3_read_soundcheck: * - * try to read the ReplayGain values from the LAME or Ape Tags and set - * the track's soundcheck field accordingly. - * * @path: localtion of the file * @track: structure holding track information * + * Try to read ReplayGain values and set the track's soundcheck field + * accordingly. If an ID3 tag is present and contains replaygain + * fields (in the format used by Foobar2k and others), the values are + * read from that tag. If no ID3 tag is present, an APE tag is + * checked and used if possible. Lastly, the LAME tag is checked. In + * all cases, audiophile (aka album) gain is preferred over radio (aka + * track) gain if both gain types are set. + * * The function always rereads the gain from the file. * * Returns TRUE if the soundcheck field could be set. @@ -2331,6 +2450,24 @@ gboolean mp3_read_soundcheck (const gchar *path, Track *track) gd.audiophile_gain_set = FALSE; gd.peak_signal_set = FALSE; + mp3_get_track_id3_replaygain (path, &gd); + if (gd.audiophile_gain_set) + { + track->soundcheck = replaygain_to_soundcheck (gd.audiophile_gain); +#if LOCALDEBUG + printf("using id3 album gain\n"); +#endif + return TRUE; + } + if (gd.radio_gain_set) + { + track->soundcheck = replaygain_to_soundcheck (gd.radio_gain); +#if LOCALDEBUG + printf("using id3 radio gain\n"); +#endif + return TRUE; + } + mp3_get_track_ape_replaygain (path, &gd); if (gd.audiophile_gain_set) { -- 1.6.2