diff src/flac/replaygain.c @ 97:a19f24790f3c trunk

[svn] It compiles now.
author chainsaw
date Sat, 21 Oct 2006 18:02:01 -0700
parents 3da1b8942b8b
children
line wrap: on
line diff
--- a/src/flac/replaygain.c	Sat Oct 21 09:21:12 2006 -0700
+++ b/src/flac/replaygain.c	Sat Oct 21 18:02:01 2006 -0700
@@ -1,5 +1,5 @@
 /* grabbag - Convenience lib for various routines common to several tools
- * Copyright (C) 2002,2003,2004,2005  Josh Coalson
+ * Copyright (C) 2002,2003,2004,2005,2006  Josh Coalson
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -16,11 +16,15 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
 #include "grabbag.h"
 #include "replaygain_analysis.h"
 #include "FLAC/assert.h"
-#include "FLAC/file_decoder.h"
 #include "FLAC/metadata.h"
+#include "FLAC/stream_decoder.h"
 #include <locale.h>
 #include <math.h>
 #include <stdio.h>
@@ -41,23 +45,27 @@
 #endif
 #define local_max(a,b) ((a)>(b)?(a):(b))
 
-static const FLAC__byte *tag_title_gain_ = (FLAC__byte*)"REPLAYGAIN_TRACK_GAIN";
-static const FLAC__byte *tag_title_peak_ = (FLAC__byte*)"REPLAYGAIN_TRACK_PEAK";
-static const FLAC__byte *tag_album_gain_ = (FLAC__byte*)"REPLAYGAIN_ALBUM_GAIN";
-static const FLAC__byte *tag_album_peak_ = (FLAC__byte*)"REPLAYGAIN_ALBUM_PEAK";
+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 const char *gain_format_ = "%s=%+2.2f dB";
 
 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";
+const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK = (const FLAC__byte * const)"REPLAYGAIN_ALBUM_PEAK";
+
 
 static FLAC__bool get_file_stats_(const char *filename, struct stat *stats)
 {
@@ -82,8 +90,8 @@
 
 	FLAC__ASSERT(0 != block);
 	FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+	FLAC__ASSERT(0 != format);
 	FLAC__ASSERT(0 != name);
-	FLAC__ASSERT(0 != value);
 
 	buffer[sizeof(buffer)-1] = '\0';
 	/*
@@ -266,7 +274,7 @@
 	FLAC__bool error;
 } DecoderInstance;
 
-static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
+static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
 {
 	DecoderInstance *instance = (DecoderInstance*)client_data;
 	const unsigned bits_per_sample = frame->header.bits_per_sample;
@@ -295,7 +303,7 @@
 		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
 }
 
-static void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
+static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
 {
 	DecoderInstance *instance = (DecoderInstance*)client_data;
 
@@ -318,7 +326,7 @@
 	}
 }
 
-static void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
 {
 	DecoderInstance *instance = (DecoderInstance*)client_data;
 
@@ -330,7 +338,7 @@
 const char *grabbag__replaygain_analyze_file(const char *filename, float *title_gain, float *title_peak)
 {
 	DecoderInstance instance;
-	FLAC__FileDecoder *decoder = FLAC__file_decoder_new();
+	FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new();
 
 	if(0 == decoder)
 		return "memory allocation error";
@@ -338,27 +346,21 @@
 	instance.error = false;
 
 	/* It does these three by default but lets be explicit: */
-	FLAC__file_decoder_set_md5_checking(decoder, false);
-	FLAC__file_decoder_set_metadata_ignore_all(decoder);
-	FLAC__file_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO);
+	FLAC__stream_decoder_set_md5_checking(decoder, false);
+	FLAC__stream_decoder_set_metadata_ignore_all(decoder);
+	FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO);
 
-	FLAC__file_decoder_set_filename(decoder, filename);
-	FLAC__file_decoder_set_write_callback(decoder, write_callback_);
-	FLAC__file_decoder_set_metadata_callback(decoder, metadata_callback_);
-	FLAC__file_decoder_set_error_callback(decoder, error_callback_);
-	FLAC__file_decoder_set_client_data(decoder, &instance);
-
-	if(FLAC__file_decoder_init(decoder) != FLAC__FILE_DECODER_OK) {
-		FLAC__file_decoder_delete(decoder);
+	if(FLAC__stream_decoder_init_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &instance) != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
+		FLAC__stream_decoder_delete(decoder);
 		return "initializing decoder";
 	}
 
-	if(!FLAC__file_decoder_process_until_end_of_file(decoder) || instance.error) {
-		FLAC__file_decoder_delete(decoder);
+	if(!FLAC__stream_decoder_process_until_end_of_stream(decoder) || instance.error) {
+		FLAC__stream_decoder_delete(decoder);
 		return "decoding file";
 	}
 
-	FLAC__file_decoder_delete(decoder);
+	FLAC__stream_decoder_delete(decoder);
 
 	grabbag__replaygain_get_title(title_gain, title_peak);
 
@@ -369,6 +371,9 @@
 {
 	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;
 
@@ -378,20 +383,34 @@
 	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);
 	FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
 
 	if(
-		FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_album_gain_) < 0 ||
-		FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_album_peak_) < 0
+		FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN) < 0 ||
+		FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK) < 0
 	)
 		return "memory allocation error";
 
 	if(
-		!append_tag_(block, peak_format_, tag_album_peak_, album_peak) ||
-		!append_tag_(block, gain_format_, 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";
 
@@ -404,14 +423,14 @@
 	FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
 
 	if(
-		FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_title_gain_) < 0 ||
-		FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_title_peak_) < 0
+		FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN) < 0 ||
+		FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK) < 0
 	)
 		return "memory allocation error";
 
 	if(
-		!append_tag_(block, peak_format_, tag_title_peak_, title_peak) ||
-		!append_tag_(block, gain_format_, 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";
 
@@ -515,6 +534,26 @@
 	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;
@@ -570,7 +609,7 @@
 		return false;
 	q++;
 	memset(s, 0, sizeof(s)-1);
-	strncpy(s, q, local_min(sizeof(s)-1, (size_t)(entry->length - (q-p))));
+	strncpy(s, q, local_min(sizeof(s)-1, entry->length - (q-p)));
 
 	v = strtod(s, &end);
 	if(end == s)
@@ -580,22 +619,33 @@
 	return true;
 }
 
-FLAC__bool grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadata *block, FLAC__bool album_mode, 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);
 
-	if(0 > (gain_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)(album_mode? tag_album_gain_ : tag_title_gain_))))
-		return false;
-	if(0 > (peak_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)(album_mode? tag_album_peak_ : tag_title_peak_))))
-		return false;
+	/* 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, 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, reference, gain, peak);
 
 	if(!parse_double_(block->data.vorbis_comment.comments + gain_offset, gain))
-		return false;
+		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 false;
+		return !strict && grabbag__replaygain_load_from_vorbiscomment(block, !album_mode, /*strict=*/true, reference, gain, peak);
 
 	return true;
 }