Use C locale when reading ReplayGain tag
authorUlrich Klauer <ulrich@chirlu.de>
Thu, 11 Apr 2013 20:07:49 +0000 (22:07 +0200)
committerErik de Castro Lopo <erikd@mega-nerd.com>
Mon, 29 Apr 2013 23:48:44 +0000 (09:48 +1000)
When a locale is in effect that does not use the point as the decimal
mark (e.g., sv_SE or de_DE, which use a comma) and a ReplayGain tag is
read for --apply-replaygain-which-is-not-lossless, the gain value was
misinterpreted (e.g., "-2.29" truncated to "-2"). This is fixed by
resetting the locale to "C" temporarily, based on Josh Coalson's fix
of the dual case (writing ReplayGain tag) in commit cda02d3.

Patch by hhaamu@gmail.com, taken from the Debian patch tracker for
flac 1.2.1-6 (13_replaygain_c_locale.patch).

http://sourceforge.net/p/flac/bugs/380/

Signed-off-by: Erik de Castro Lopo <erikd@mega-nerd.com>
src/share/grabbag/replaygain.c

index 43f1be0..347319f 100644 (file)
@@ -606,6 +606,8 @@ static FLAC__bool parse_double_(const FLAC__StreamMetadata_VorbisComment_Entry *
 FLAC__bool grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadata *block, FLAC__bool album_mode, FLAC__bool strict, double *reference, double *gain, double *peak)
 {
        int reference_offset, gain_offset, peak_offset;
+       char *saved_locale;
+       FLAC__bool res = true;
 
        FLAC__ASSERT(0 != block);
        FLAC__ASSERT(0 != reference);
@@ -618,20 +620,36 @@ FLAC__bool grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadat
         */
        *reference = ReplayGainReferenceLoudness;
 
+       /*
+        * We need to save the old locale and switch to "C" because the locale
+        * influences the formatting of %f and we want it a certain way.
+        */
+       saved_locale = strdup(setlocale(LC_ALL, 0));
+       if (0 == saved_locale)
+               return false;
+       setlocale(LC_ALL, "C");
+
        if(0 <= (reference_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS)))
                (void)parse_double_(block->data.vorbis_comment.comments + reference_offset, reference);
 
        if(0 > (gain_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)(album_mode? GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN : GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN))))
-               return !strict && grabbag__replaygain_load_from_vorbiscomment(block, !album_mode, /*strict=*/true, reference, gain, peak);
+               res = false;
        if(0 > (peak_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)(album_mode? GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK : GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK))))
-               return !strict && grabbag__replaygain_load_from_vorbiscomment(block, !album_mode, /*strict=*/true, reference, gain, peak);
+               res = false;
 
-       if(!parse_double_(block->data.vorbis_comment.comments + gain_offset, gain))
-               return !strict && grabbag__replaygain_load_from_vorbiscomment(block, !album_mode, /*strict=*/true, reference, gain, peak);
-       if(!parse_double_(block->data.vorbis_comment.comments + peak_offset, peak))
-               return !strict && grabbag__replaygain_load_from_vorbiscomment(block, !album_mode, /*strict=*/true, reference, gain, peak);
+       if(res && !parse_double_(block->data.vorbis_comment.comments + gain_offset, gain))
+               res = false;
+       if(res && !parse_double_(block->data.vorbis_comment.comments + peak_offset, peak))
+               res = false;
 
-       return true;
+       setlocale(LC_ALL, saved_locale);
+       free(saved_locale);
+
+       /* something failed; retry with strict */
+       if (!res && !strict)
+               res = grabbag__replaygain_load_from_vorbiscomment(block, !album_mode, /*strict=*/true, reference, gain, peak);
+
+       return res;
 }
 
 double grabbag__replaygain_compute_scale_factor(double peak, double gain, double preamp, FLAC__bool prevent_clipping)