diff --git a/doc/html/changelog.html b/doc/html/changelog.html index de28f625..4c412232 100644 --- a/doc/html/changelog.html +++ b/doc/html/changelog.html @@ -68,6 +68,7 @@
  • Large file (>2GB) support everywhere
  • Much better recovery for corrupted files
  • flac now supports FLAC as input to the encoder (i.e. can re-encode FLAC to FLAC) and preserve all the metadata like tags, etc.
  • +
  • Support for new REPLAYGAIN_REFERENCE_LOUDNESS tag.
  • In the developer libraries, the interface has been simplfied by merging the three decoding layers into a single class; ditto for the encoders.
  • diff --git a/include/share/grabbag/replaygain.h b/include/share/grabbag/replaygain.h index d0f50f6e..1a053d79 100644 --- a/include/share/grabbag/replaygain.h +++ b/include/share/grabbag/replaygain.h @@ -35,6 +35,7 @@ extern "C" { extern const unsigned GRABBAG__REPLAYGAIN_MAX_TAG_SPACE_REQUIRED; +extern const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS; /* = "REPLAYGAIN_REFERENCE_LOUDNESS" */ extern const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN; /* = "REPLAYGAIN_TRACK_GAIN" */ extern const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK; /* = "REPLAYGAIN_TRACK_PEAK" */ extern const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN; /* = "REPLAYGAIN_ALBUM_GAIN" */ @@ -53,13 +54,15 @@ void grabbag__replaygain_get_title(float *gain, float *peak); /* These three functions return an error string on error, or NULL if successful */ const char *grabbag__replaygain_analyze_file(const char *filename, float *title_gain, float *title_peak); const char *grabbag__replaygain_store_to_vorbiscomment(FLAC__StreamMetadata *block, float album_gain, float album_peak, float title_gain, float title_peak); +const char *grabbag__replaygain_store_to_vorbiscomment_reference(FLAC__StreamMetadata *block); const char *grabbag__replaygain_store_to_vorbiscomment_album(FLAC__StreamMetadata *block, float album_gain, float album_peak); const char *grabbag__replaygain_store_to_vorbiscomment_title(FLAC__StreamMetadata *block, float title_gain, float title_peak); const char *grabbag__replaygain_store_to_file(const char *filename, float album_gain, float album_peak, float title_gain, float title_peak, FLAC__bool preserve_modtime); +const char *grabbag__replaygain_store_to_file_reference(const char *filename, FLAC__bool preserve_modtime); const char *grabbag__replaygain_store_to_file_album(const char *filename, float album_gain, float album_peak, FLAC__bool preserve_modtime); const char *grabbag__replaygain_store_to_file_title(const char *filename, float title_gain, float title_peak, FLAC__bool preserve_modtime); -FLAC__bool grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadata *block, FLAC__bool album_mode, FLAC__bool strict, double *gain, double *peak); +FLAC__bool grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadata *block, FLAC__bool album_mode, FLAC__bool strict, double *reference, double *gain, double *peak); double grabbag__replaygain_compute_scale_factor(double peak, double gain, double preamp, FLAC__bool prevent_clipping); #ifdef __cplusplus diff --git a/src/flac/decode.c b/src/flac/decode.c index 426f008e..a7a122ff 100644 --- a/src/flac/decode.c +++ b/src/flac/decode.c @@ -1102,8 +1102,8 @@ void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMet } else if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { if (decoder_session->replaygain.spec.apply) { - double gain, peak; - if (!(decoder_session->replaygain.apply = grabbag__replaygain_load_from_vorbiscomment(metadata, decoder_session->replaygain.spec.use_album_gain, /*strict=*/false, &gain, &peak))) { + double reference, gain, peak; + if (!(decoder_session->replaygain.apply = grabbag__replaygain_load_from_vorbiscomment(metadata, decoder_session->replaygain.spec.use_album_gain, /*strict=*/false, &reference, &gain, &peak))) { flac__utils_printf(stderr, 1, "%s: WARNING: can't get %s (or even %s) ReplayGain tags\n", decoder_session->inbasefilename, decoder_session->replaygain.spec.use_album_gain? "album":"track", decoder_session->replaygain.spec.use_album_gain? "track":"album"); } else { diff --git a/src/flac/main.c b/src/flac/main.c index d06d1f0a..45106d5a 100644 --- a/src/flac/main.c +++ b/src/flac/main.c @@ -536,7 +536,7 @@ int do_it() return 2; } if(0 != (error = grabbag__replaygain_store_to_file_album(outfilename, album_gain, album_peak, /*preserve_modtime=*/true))) { - flac__utils_printf(stderr, 1, "%s: ERROR writing ReplayGain album tags\n", outfilename); + flac__utils_printf(stderr, 1, "%s: ERROR writing ReplayGain album tags (%s)\n", outfilename, error); retval = 1; } } @@ -1606,7 +1606,7 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_ fmt= AIF; is_aifc = true; } - else if(!strncmp((const char *)lookahead, FLAC__STREAM_SYNC_STRING, sizeof(FLAC__STREAM_SYNC_STRING))) + else if(!memcmp(lookahead, FLAC__STREAM_SYNC_STRING, sizeof(FLAC__STREAM_SYNC_STRING))) fmt= FLAC; else { if(fmt != RAW) @@ -1761,12 +1761,15 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_ float title_gain, title_peak; const char *error; grabbag__replaygain_get_title(&title_gain, &title_peak); - if(0 != (error = grabbag__replaygain_store_to_file_title(internal_outfilename, title_gain, title_peak, /*preserve_modtime=*/true))) { - flac__utils_printf(stderr, 1, "%s: ERROR writing ReplayGain title tags\n", outfilename); + if( + 0 != (error = grabbag__replaygain_store_to_file_reference(internal_outfilename? internal_outfilename : outfilename, /*preserve_modtime=*/true)) || + 0 != (error = grabbag__replaygain_store_to_file_title(internal_outfilename? internal_outfilename : outfilename, title_gain, title_peak, /*preserve_modtime=*/true)) + ) { + flac__utils_printf(stderr, 1, "%s: ERROR writing ReplayGain reference/title tags (%s)\n", outfilename, error); } } if(strcmp(infilename, "-")) - grabbag__file_copy_metadata(infilename, internal_outfilename); + grabbag__file_copy_metadata(infilename, internal_outfilename? internal_outfilename : outfilename); } } diff --git a/src/plugin_common/replaygain.c b/src/plugin_common/replaygain.c index 8818d17a..753a1900 100644 --- a/src/plugin_common/replaygain.c +++ b/src/plugin_common/replaygain.c @@ -27,6 +27,7 @@ #include "share/grabbag.h" void FLAC_plugin__replaygain_get_from_file(const char *filename, + double *reference, FLAC__bool *reference_set, double *track_gain, FLAC__bool *track_gain_set, double *album_gain, FLAC__bool *album_gain_set, double *track_peak, FLAC__bool *track_peak_set, @@ -43,11 +44,11 @@ void FLAC_plugin__replaygain_get_from_file(const char *filename, if(FLAC__metadata_simple_iterator_get_block_type(iterator) == FLAC__METADATA_TYPE_VORBIS_COMMENT) { FLAC__StreamMetadata *block = FLAC__metadata_simple_iterator_get_block(iterator); if(0 != block) { - if(grabbag__replaygain_load_from_vorbiscomment(block, /*album_mode=*/false, /*strict=*/true, track_gain, track_peak)) { - *track_gain_set = *track_peak_set = true; + if(grabbag__replaygain_load_from_vorbiscomment(block, /*album_mode=*/false, /*strict=*/true, reference, track_gain, track_peak)) { + *reference_set = *track_gain_set = *track_peak_set = true; } - if(grabbag__replaygain_load_from_vorbiscomment(block, /*album_mode=*/true, /*strict=*/true, album_gain, album_peak)) { - *album_gain_set = *album_peak_set = true; + if(grabbag__replaygain_load_from_vorbiscomment(block, /*album_mode=*/true, /*strict=*/true, reference, album_gain, album_peak)) { + *reference_set = *album_gain_set = *album_peak_set = true; } FLAC__metadata_object_delete(block); got_vorbis_comments = true; diff --git a/src/plugin_common/replaygain.h b/src/plugin_common/replaygain.h index d7b84e75..c54c8bd1 100644 --- a/src/plugin_common/replaygain.h +++ b/src/plugin_common/replaygain.h @@ -23,6 +23,7 @@ #include "FLAC/ordinals.h" void FLAC_plugin__replaygain_get_from_file(const char *filename, + double *reference, FLAC__bool *reference_set, double *track_gain, FLAC__bool *track_gain_set, double *album_gain, FLAC__bool *album_gain_set, double *track_peak, FLAC__bool *track_peak_set, diff --git a/src/plugin_winamp2/playback.c b/src/plugin_winamp2/playback.c index c2f23892..eec4b887 100644 --- a/src/plugin_winamp2/playback.c +++ b/src/plugin_winamp2/playback.c @@ -87,8 +87,8 @@ static void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__St } else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { - double gain, peak; - if (grabbag__replaygain_load_from_vorbiscomment(metadata, cfg.replaygain.album_mode, /*strict=*/false, &gain, &peak)) + double reference, gain, peak; + if (grabbag__replaygain_load_from_vorbiscomment(metadata, cfg.replaygain.album_mode, /*strict=*/false, &reference, &gain, &peak)) { stream_data->has_replaygain = true; stream_data->replay_scale = grabbag__replaygain_compute_scale_factor(peak, gain, (double)cfg.replaygain.preamp, !cfg.replaygain.hard_limit); diff --git a/src/plugin_xmms/fileinfo.c b/src/plugin_xmms/fileinfo.c index 09c32050..ecb1e85a 100644 --- a/src/plugin_xmms/fileinfo.c +++ b/src/plugin_xmms/fileinfo.c @@ -41,7 +41,7 @@ static GtkWidget *window = NULL; static GList *genre_list = NULL; static GtkWidget *filename_entry, *tag_frame; static GtkWidget *title_entry, *artist_entry, *album_entry, *date_entry, *tracknum_entry, *comment_entry; -static GtkWidget *replaygain_track_gain, *replaygain_album_gain, *replaygain_track_peak, *replaygain_album_peak; +static GtkWidget *replaygain_reference, *replaygain_track_gain, *replaygain_album_gain, *replaygain_track_peak, *replaygain_album_peak; static GtkWidget *genre_combo; static GtkWidget *flac_samplerate, *flac_channels, *flac_bits_per_sample, *flac_blocksize, *flac_filesize, *flac_samples, *flac_bitrate; @@ -244,22 +244,26 @@ static void show_replaygain() /* known limitation: If only one of gain and peak is set, neither will be shown. This is true for * both track and album replaygain tags. Written so it will be easy to fix, with some trouble. */ + gtk_label_set_text(GTK_LABEL(replaygain_reference), ""); gtk_label_set_text(GTK_LABEL(replaygain_track_gain), ""); gtk_label_set_text(GTK_LABEL(replaygain_album_gain), ""); gtk_label_set_text(GTK_LABEL(replaygain_track_peak), ""); gtk_label_set_text(GTK_LABEL(replaygain_album_peak), ""); - double track_gain, track_peak, album_gain, album_peak; - FLAC__bool track_gain_set, track_peak_set, album_gain_set, album_peak_set; + double reference, track_gain, track_peak, album_gain, album_peak; + FLAC__bool reference_set, track_gain_set, track_peak_set, album_gain_set, album_peak_set; FLAC_plugin__replaygain_get_from_file( current_filename, + &reference, &reference_set, &track_gain, &track_gain_set, &album_gain, &album_gain_set, &track_peak, &track_peak_set, &album_peak, &album_peak_set ); + if(reference_set) + label_set_text(replaygain_reference, _("ReplayGain Reference Loudness: %2.1f dB"), reference); if(track_gain_set) label_set_text(replaygain_track_gain, _("ReplayGain Track Gain: %+2.2f dB"), track_gain); if(album_gain_set) @@ -438,6 +442,11 @@ void FLAC_XMMS__file_info_box(char *filename) gtk_label_set_justify(GTK_LABEL(flac_bitrate), GTK_JUSTIFY_LEFT); gtk_box_pack_start(GTK_BOX(flac_box), flac_bitrate, FALSE, FALSE, 0); + replaygain_reference = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(replaygain_reference), 0, 0); + gtk_label_set_justify(GTK_LABEL(replaygain_reference), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(flac_box), replaygain_reference, FALSE, FALSE, 0); + replaygain_track_gain = gtk_label_new(""); gtk_misc_set_alignment(GTK_MISC(replaygain_track_gain), 0, 0); gtk_label_set_justify(GTK_LABEL(replaygain_track_gain), GTK_JUSTIFY_LEFT); diff --git a/src/plugin_xmms/plugin.c b/src/plugin_xmms/plugin.c index 4e5b9a17..ba0219af 100644 --- a/src/plugin_xmms/plugin.c +++ b/src/plugin_xmms/plugin.c @@ -662,8 +662,8 @@ void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMe } } else if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { - double gain, peak; - if(grabbag__replaygain_load_from_vorbiscomment(metadata, flac_cfg.output.replaygain.album_mode, /*strict=*/false, &gain, &peak)) { + double reference, gain, peak; + if(grabbag__replaygain_load_from_vorbiscomment(metadata, flac_cfg.output.replaygain.album_mode, /*strict=*/false, &reference, &gain, &peak)) { stream_data->has_replaygain = true; stream_data->replay_scale = grabbag__replaygain_compute_scale_factor(peak, gain, (double)flac_cfg.output.replaygain.preamp, /*prevent_clipping=*/!flac_cfg.output.replaygain.hard_limit); } diff --git a/src/share/grabbag/replaygain.c b/src/share/grabbag/replaygain.c index 28457a6b..f10f3156 100644 --- a/src/share/grabbag/replaygain.c +++ b/src/share/grabbag/replaygain.c @@ -45,19 +45,22 @@ #endif #define local_max(a,b) ((a)>(b)?(a):(b)) -static const char *peak_format_ = "%s=%1.8f"; +static const char *reference_format_ = "%s=%2.1f dB"; static const char *gain_format_ = "%s=%+2.2f dB"; +static const char *peak_format_ = "%s=%1.8f"; static double album_peak_, title_peak_; -const unsigned GRABBAG__REPLAYGAIN_MAX_TAG_SPACE_REQUIRED = 148; +const unsigned GRABBAG__REPLAYGAIN_MAX_TAG_SPACE_REQUIRED = 190; /* + FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 29 + 1 + 8 + FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 10 + FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 12 + FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 10 + FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 12 */ +const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS = (const FLAC__byte * const)"REPLAYGAIN_REFERENCE_LOUDNESS"; const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN = (const FLAC__byte * const)"REPLAYGAIN_TRACK_GAIN"; const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK = (const FLAC__byte * const)"REPLAYGAIN_TRACK_PEAK"; const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN = (const FLAC__byte * const)"REPLAYGAIN_ALBUM_GAIN"; @@ -368,6 +371,9 @@ const char *grabbag__replaygain_store_to_vorbiscomment(FLAC__StreamMetadata *blo { const char *error; + if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_reference(block))) + return error; + if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_title(block, title_gain, title_peak))) return error; @@ -377,6 +383,20 @@ const char *grabbag__replaygain_store_to_vorbiscomment(FLAC__StreamMetadata *blo return 0; } +const char *grabbag__replaygain_store_to_vorbiscomment_reference(FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != block); + FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + if(FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS) < 0) + return "memory allocation error"; + + if(!append_tag_(block, reference_format_, GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS, ReplayGainReferenceLoudness)) + return "memory allocation error"; + + return 0; +} + const char *grabbag__replaygain_store_to_vorbiscomment_album(FLAC__StreamMetadata *block, float album_gain, float album_peak) { FLAC__ASSERT(0 != block); @@ -389,8 +409,8 @@ const char *grabbag__replaygain_store_to_vorbiscomment_album(FLAC__StreamMetadat return "memory allocation error"; if( - !append_tag_(block, peak_format_, GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK, album_peak) || - !append_tag_(block, gain_format_, GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN, album_gain) + !append_tag_(block, gain_format_, GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN, album_gain) || + !append_tag_(block, peak_format_, GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK, album_peak) ) return "memory allocation error"; @@ -409,8 +429,8 @@ const char *grabbag__replaygain_store_to_vorbiscomment_title(FLAC__StreamMetadat return "memory allocation error"; if( - !append_tag_(block, peak_format_, GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK, title_peak) || - !append_tag_(block, gain_format_, GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN, title_gain) + !append_tag_(block, gain_format_, GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN, title_gain) || + !append_tag_(block, peak_format_, GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK, title_peak) ) return "memory allocation error"; @@ -514,6 +534,26 @@ const char *grabbag__replaygain_store_to_file(const char *filename, float album_ return 0; } +const char *grabbag__replaygain_store_to_file_reference(const char *filename, FLAC__bool preserve_modtime) +{ + FLAC__Metadata_Chain *chain; + FLAC__StreamMetadata *block; + const char *error; + + if(0 != (error = store_to_file_pre_(filename, &chain, &block))) + return error; + + if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_reference(block))) { + FLAC__metadata_chain_delete(chain); + return error; + } + + if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime))) + return error; + + return 0; +} + const char *grabbag__replaygain_store_to_file_album(const char *filename, float album_gain, float album_peak, FLAC__bool preserve_modtime) { FLAC__Metadata_Chain *chain; @@ -579,22 +619,33 @@ static FLAC__bool parse_double_(const FLAC__StreamMetadata_VorbisComment_Entry * return true; } -FLAC__bool grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadata *block, FLAC__bool album_mode, FLAC__bool strict, double *gain, double *peak) +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 gain_offset, peak_offset; + int reference_offset, gain_offset, peak_offset; FLAC__ASSERT(0 != block); + FLAC__ASSERT(0 != reference); + FLAC__ASSERT(0 != gain); + FLAC__ASSERT(0 != peak); FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + /* Default to current level until overridden by a detected tag; this + * will always be true until we change replaygain_analysis.c + */ + *reference = ReplayGainReferenceLoudness; + + 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, gain, peak); + return !strict && grabbag__replaygain_load_from_vorbiscomment(block, !album_mode, /*strict=*/true, reference, gain, peak); 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, gain, peak); + return !strict && grabbag__replaygain_load_from_vorbiscomment(block, !album_mode, /*strict=*/true, reference, gain, peak); if(!parse_double_(block->data.vorbis_comment.comments + gain_offset, gain)) - return !strict && grabbag__replaygain_load_from_vorbiscomment(block, !album_mode, /*strict=*/true, gain, peak); + 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, gain, peak); + return !strict && grabbag__replaygain_load_from_vorbiscomment(block, !album_mode, /*strict=*/true, reference, gain, peak); return true; }