brasero r826 - in trunk: . src src/plugins/transcode



Author: philippr
Date: Sun May 25 17:49:51 2008
New Revision: 826
URL: http://svn.gnome.org/viewvc/brasero?rev=826&view=rev

Log:
	Add normalization for audio:
	- one new plugin
	- modified transcode to comply with replaygain tags

	Added possibility to add arbitrary GValues to session and tracks for plugins

	* src/burn-job.c (brasero_job_tag_lookup), (brasero_job_tag_add):
	* src/burn-job.h:
	* src/burn-session.c (brasero_burn_session_tag_value_free),
	(brasero_burn_session_tag_add), (brasero_burn_session_tag_lookup),
	(brasero_burn_session_finalize):
	* src/burn-session.h:
	* src/burn-task-ctx.c (brasero_task_ctx_get_session):
	* src/burn-track.c (brasero_track_clean),
	(brasero_track_tag_value_free), (brasero_track_tag_add),
	(brasero_track_tag_lookup):
	* src/burn-track.h:
	* src/plugins/transcode/Makefile.am:
	* src/plugins/transcode/burn-normalize.c
	(brasero_normalize_set_next_track),
	(brasero_normalize_stop_pipeline), (brasero_normalize_stop),
	(foreach_tag), (brasero_normalize_song_end_reached),
	(brasero_normalize_bus_messages),
	(brasero_normalize_new_decoded_pad_cb),
	(brasero_normalize_build_pipeline), (brasero_normalize_start),
	(brasero_normalize_activate), (brasero_normalize_clock_tick),
	(brasero_normalize_init), (brasero_normalize_finalize),
	(brasero_normalize_class_init), (brasero_normalize_export_caps):
	* src/plugins/transcode/burn-normalize.h:
	* src/plugins/transcode/burn-transcode.c
	(brasero_transcode_send_volume_event),
	(brasero_transcode_create_volume),
	(brasero_transcode_create_pipeline),
	(brasero_transcode_stop_pipeline),
	(brasero_transcode_new_decoded_pad_cb):

Added:
   trunk/src/plugins/transcode/burn-normalize.c   (contents, props changed)
   trunk/src/plugins/transcode/burn-normalize.h   (contents, props changed)
Modified:
   trunk/ChangeLog
   trunk/src/burn-job.c
   trunk/src/burn-job.h
   trunk/src/burn-session.c
   trunk/src/burn-session.h
   trunk/src/burn-task-ctx.c
   trunk/src/burn-track.c
   trunk/src/burn-track.h
   trunk/src/plugins/transcode/Makefile.am
   trunk/src/plugins/transcode/burn-transcode.c

Modified: trunk/src/burn-job.c
==============================================================================
--- trunk/src/burn-job.c	(original)
+++ trunk/src/burn-job.c	Sun May 25 17:49:51 2008
@@ -791,6 +791,47 @@
 	iface->clock_tick = brasero_job_item_clock_tick;
 }
 
+BraseroBurnResult
+brasero_job_tag_lookup (BraseroJob *self,
+			const gchar *tag,
+			GValue **value)
+{
+	BraseroJobPrivate *priv;
+	BraseroBurnSession *session;
+
+	BRASERO_JOB_DEBUG (self);
+
+	priv = BRASERO_JOB_PRIVATE (self);
+
+	session = brasero_task_ctx_get_session (priv->ctx);
+	return brasero_burn_session_tag_lookup (session,
+						tag,
+						value);
+}
+
+BraseroBurnResult
+brasero_job_tag_add (BraseroJob *self,
+		     const gchar *tag,
+		     GValue *value)
+{
+	BraseroJobPrivate *priv;
+	BraseroBurnSession *session;
+
+	BRASERO_JOB_DEBUG (self);
+
+	priv = BRASERO_JOB_PRIVATE (self);
+
+	if (!brasero_job_is_last_active (self))
+		return BRASERO_BURN_ERR;
+
+	session = brasero_task_ctx_get_session (priv->ctx);
+	brasero_burn_session_tag_add (session,
+				      tag,
+				      value);
+
+	return BRASERO_BURN_OK;
+}
+
 /**
  * Means a job successfully completed its task.
  * track can be NULL, depending on whether or not the job created a track.

Modified: trunk/src/burn-job.h
==============================================================================
--- trunk/src/burn-job.h	(original)
+++ trunk/src/burn-job.h	Sun May 25 17:49:51 2008
@@ -205,6 +205,20 @@
 			 GError **error);
 
 /**
+ * Each tag can be retrieved by any job
+ */
+
+BraseroBurnResult
+brasero_job_tag_lookup (BraseroJob *job,
+			const gchar *tag,
+			GValue **value);
+
+BraseroBurnResult
+brasero_job_tag_add (BraseroJob *job,
+		     const gchar *tag,
+		     GValue *value);
+
+/**
  * Used to give job results and tell when a job has finished
  */
 

Modified: trunk/src/burn-session.c
==============================================================================
--- trunk/src/burn-session.c	(original)
+++ trunk/src/burn-session.c	Sun May 25 17:49:51 2008
@@ -85,6 +85,8 @@
 
 	BraseroTrackType input;
 
+	GHashTable *tags;
+
 	guint src_added_sig;
 	guint src_removed_sig;
 	guint dest_added_sig;
@@ -1083,6 +1085,59 @@
 	return priv->settings->label;
 }
 
+static void
+brasero_burn_session_tag_value_free (gpointer user_data)
+{
+	GValue *value = user_data;
+
+	g_value_reset (value);
+	g_free (value);
+}
+
+BraseroBurnResult
+brasero_burn_session_tag_add (BraseroBurnSession *self,
+			      const gchar *tag,
+			      GValue *value)
+{
+	BraseroBurnSessionPrivate *priv;
+
+	g_return_val_if_fail (BRASERO_IS_BURN_SESSION (self), BRASERO_BURN_ERR);
+
+	priv = BRASERO_BURN_SESSION_PRIVATE (self);
+	if (!priv->tags)
+		priv->tags = g_hash_table_new_full (g_str_hash,
+						    g_str_equal,
+						    g_free,
+						    brasero_burn_session_tag_value_free);
+	g_hash_table_insert (priv->tags, g_strdup (tag), value);
+	return BRASERO_BURN_OK;
+}
+
+BraseroBurnResult
+brasero_burn_session_tag_lookup (BraseroBurnSession *self,
+				 const gchar *tag,
+				 GValue **value)
+{
+	BraseroBurnSessionPrivate *priv;
+	gpointer data;
+
+	g_return_val_if_fail (BRASERO_IS_BURN_SESSION (self), BRASERO_BURN_ERR);
+
+	priv = BRASERO_BURN_SESSION_PRIVATE (self);
+	if (!value)
+		return BRASERO_BURN_ERR;
+
+	if (!priv->tags)
+		return BRASERO_BURN_ERR;
+
+	data = g_hash_table_lookup (priv->tags, tag);
+	if (!data)
+		return BRASERO_BURN_ERR;
+
+	*value = data;
+	return BRASERO_BURN_OK;
+}
+
 /**
  * Used to save and restore settings/sources
  */
@@ -1615,6 +1670,11 @@
 
 	priv = BRASERO_BURN_SESSION_PRIVATE (object);
 
+	if (priv->tags) {
+		g_hash_table_destroy (priv->tags);
+		priv->tags = NULL;
+	}
+
 	if (priv->dest_added_sig) {
 		g_signal_handler_disconnect (priv->settings->burner,
 					     priv->dest_added_sig);

Modified: trunk/src/burn-session.h
==============================================================================
--- trunk/src/burn-session.h	(original)
+++ trunk/src/burn-session.h	Sun May 25 17:49:51 2008
@@ -95,7 +95,15 @@
 brasero_burn_session_set_label (BraseroBurnSession *session,
 				const gchar *label);
 
+BraseroBurnResult
+brasero_burn_session_tag_lookup (BraseroBurnSession *session,
+				 const gchar *tag,
+				 GValue **value);
 
+BraseroBurnResult
+brasero_burn_session_tag_add (BraseroBurnSession *session,
+			      const gchar *tag,
+			      GValue *value);
 /**
  * 
  */

Modified: trunk/src/burn-task-ctx.c
==============================================================================
--- trunk/src/burn-task-ctx.c	(original)
+++ trunk/src/burn-task-ctx.c	Sun May 25 17:49:51 2008
@@ -201,6 +201,8 @@
 {
 	BraseroTaskCtxPrivate *priv;
 
+	g_return_val_if_fail (BRASERO_IS_TASK_CTX (self), NULL);
+
 	priv = BRASERO_TASK_CTX_PRIVATE (self);
 	if (!priv->session)
 		return NULL;

Modified: trunk/src/burn-track.c
==============================================================================
--- trunk/src/burn-track.c	(original)
+++ trunk/src/burn-track.c	Sun May 25 17:49:51 2008
@@ -47,6 +47,8 @@
 
 	int ref;
 
+	GHashTable *tags;
+
 	gchar *checksum;
 	BraseroChecksumType checksum_type;
 };
@@ -209,6 +211,11 @@
 {
 	g_return_if_fail (track != NULL);
 
+	if (track->tags) {
+		g_hash_table_destroy (track->tags);
+		track->tags = NULL;
+	}
+
 	if (track->type.type == BRASERO_TRACK_TYPE_AUDIO) {
 		BraseroTrackAudio *audio = (BraseroTrackAudio *) track;
 
@@ -1115,3 +1122,50 @@
 
 	return BRASERO_BURN_OK;
 }
+
+/**
+ * Can be used to set arbitrary data
+ */
+static void
+brasero_track_tag_value_free (gpointer user_data)
+{
+	GValue *value = user_data;
+
+	g_value_reset (value);
+	g_free (value);
+}
+
+BraseroBurnResult
+brasero_track_tag_add (BraseroTrack *track,
+		       const gchar *tag,
+		       GValue *value)
+{
+	if (!track->tags)
+		track->tags = g_hash_table_new_full (g_str_hash,
+						     g_str_equal,
+						     g_free,
+						     brasero_track_tag_value_free);
+	g_hash_table_insert (track->tags, g_strdup (tag), value);
+	return BRASERO_BURN_OK;
+}
+
+BraseroBurnResult
+brasero_track_tag_lookup (BraseroTrack *track,
+			  const gchar *tag,
+			  GValue **value)
+{
+	gpointer data;
+
+	if (!track->tags)
+		return BRASERO_BURN_ERR;
+
+	data = g_hash_table_lookup (track->tags, tag);
+	if (!data)
+		return BRASERO_BURN_ERR;
+
+	if (value)
+		*value = data;
+
+	return BRASERO_BURN_OK;
+}
+

Modified: trunk/src/burn-track.h
==============================================================================
--- trunk/src/burn-track.h	(original)
+++ trunk/src/burn-track.h	Sun May 25 17:49:51 2008
@@ -292,6 +292,20 @@
 brasero_track_get_data_file_num (BraseroTrack *track,
 				 gint64 *num_files);
 
+/**
+ *
+ */
+
+BraseroBurnResult
+brasero_track_tag_add (BraseroTrack *track,
+		       const gchar *tag,
+		       GValue *value);
+
+BraseroBurnResult
+brasero_track_tag_lookup (BraseroTrack *track,
+			  const gchar *tag,
+			  GValue **value);
+
 G_END_DECLS
 
 #endif /* _BURN_TRACK_H */

Modified: trunk/src/plugins/transcode/Makefile.am
==============================================================================
--- trunk/src/plugins/transcode/Makefile.am	(original)
+++ trunk/src/plugins/transcode/Makefile.am	Sun May 25 17:49:51 2008
@@ -1,5 +1,3 @@
-plugindir = $(libdir)/brasero/plugins
-
 DISABLE_DEPRECATED = -DG_DISABLE_DEPRECATED
 
 INCLUDES = \
@@ -17,8 +15,16 @@
 
 AM_CFLAGS = -g
 
-plugin_LTLIBRARIES = libbrasero-transcode.la
+transcodedir = $(libdir)/brasero/plugins
+transcode_LTLIBRARIES = libbrasero-transcode.la
 
-libbrasero_transcode_la_SOURCES = burn-transcode.c burn-transcode.h
+libbrasero_transcode_la_SOURCES = burn-transcode.c burn-transcode.h burn-normalize.h
 libbrasero_transcode_la_LIBADD = $(BRASERO_BASE_LIBS) $(BRASERO_GSTREAMER_CFLAGS)
 libbrasero_transcode_la_LDFLAGS = -module -avoid-version
+
+normalizedir = $(libdir)/brasero/plugins
+normalize_LTLIBRARIES = libbrasero-normalize.la
+
+libbrasero_normalize_la_SOURCES = burn-normalize.c burn-normalize.h
+libbrasero_normalize_la_LIBADD = $(BRASERO_BASE_LIBS) $(BRASERO_GSTREAMER_CFLAGS)
+libbrasero_normalize_la_LDFLAGS = -module -avoid-version

Added: trunk/src/plugins/transcode/burn-normalize.c
==============================================================================
--- (empty file)
+++ trunk/src/plugins/transcode/burn-normalize.c	Sun May 25 17:49:51 2008
@@ -0,0 +1,569 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * brasero-normalize.c
+ * Copyright (C) Rouquier Philippe 2008 <bonfire-app wanadoo fr>
+ * 
+ * brasero-normalize.c is free software.
+ * 
+ * You may redistribute it and/or modify it under the terms of the
+ * GNU General Public License, as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ * 
+ * brasero-normalize.c is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with brasero-normalize.c.  If not, write to:
+ * 	The Free Software Foundation, Inc.,
+ * 	51 Franklin Street, Fifth Floor
+ * 	Boston, MA  02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+
+#include <gst/gst.h>
+
+#include "burn-basics.h"
+#include "burn-job.h"
+#include "burn-plugin.h"
+#include "burn-normalize.h"
+
+BRASERO_PLUGIN_BOILERPLATE (BraseroNormalize, brasero_normalize, BRASERO_TYPE_JOB, BraseroJob);
+
+typedef struct _BraseroNormalizePrivate BraseroNormalizePrivate;
+struct _BraseroNormalizePrivate
+{
+	GstElement *pipeline;
+	GstElement *analysis;
+	GstElement *decode;
+	GstElement *source;
+
+	GSList *tracks;
+	BraseroTrack *track;
+
+	gdouble album_peak;
+	gdouble album_gain;
+	gdouble track_peak;
+	gdouble track_gain;
+};
+
+#define BRASERO_NORMALIZE_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), BRASERO_TYPE_NORMALIZE, BraseroNormalizePrivate))
+
+static GObjectClass *parent_class = NULL;
+
+
+static gboolean
+brasero_normalize_bus_messages (GstBus *bus,
+				GstMessage *msg,
+				BraseroNormalize *normalize);
+
+static gboolean
+brasero_normalize_set_next_track (BraseroJob *job,
+				  BraseroTrack *track,
+				  GError **error)
+{
+	gchar *uri;
+	GstBus *bus = NULL;
+	GstElement *source;
+	BraseroNormalizePrivate *priv;
+
+	priv = BRASERO_NORMALIZE_PRIVATE (job);
+
+	/* destroy previous source */
+	if (priv->source) {
+		gst_element_unlink (priv->source, priv->decode);
+		gst_bin_remove (GST_BIN (priv->pipeline), priv->source);
+		priv->source = NULL;
+	}
+
+	/* create a new one */
+	uri = brasero_track_get_audio_source (track, TRUE);
+	source = gst_element_make_from_uri (GST_URI_SRC, uri, NULL);
+	if (source == NULL) {
+		g_set_error (error,
+			     BRASERO_BURN_ERROR,
+			     BRASERO_BURN_ERROR_GENERAL,
+			     _("source can't be created"));
+		return FALSE;
+	}
+	gst_bin_add (GST_BIN (priv->pipeline), source);
+	g_object_set (source,
+		      "typefind", FALSE,
+		      NULL);
+
+	priv->source = source;
+	gst_element_link_many (source, priv->decode, NULL);
+
+	/* reconnect to the bus */	
+	bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
+	gst_bus_add_watch (bus,
+			   (GstBusFunc) brasero_normalize_bus_messages,
+			   job);
+	gst_object_unref (bus);
+
+	priv->track = track;
+
+	return TRUE;
+}
+
+static void
+brasero_normalize_stop_pipeline (BraseroNormalize *normalize)
+{
+	BraseroNormalizePrivate *priv;
+
+	priv = BRASERO_NORMALIZE_PRIVATE (normalize);
+	if (!priv->pipeline)
+		return;
+
+	gst_element_set_state (priv->pipeline, GST_STATE_NULL);
+	gst_object_unref (GST_OBJECT (priv->pipeline));
+	priv->pipeline = NULL;
+	priv->analysis = NULL;
+	priv->decode = NULL;
+	priv->source = NULL;
+}
+
+static BraseroBurnResult
+brasero_normalize_stop (BraseroJob *job,
+			GError **error)
+{
+	BraseroNormalizePrivate *priv;
+
+	priv = BRASERO_NORMALIZE_PRIVATE (job);
+
+	brasero_normalize_stop_pipeline (BRASERO_NORMALIZE (job));
+	if (priv->tracks) {
+		g_slist_free (priv->tracks);
+		priv->tracks = NULL;
+	}
+
+	priv->track = NULL;
+
+	return BRASERO_BURN_OK;
+}
+
+static void
+foreach_tag (const GstTagList *list,
+	     const gchar *tag,
+	     BraseroNormalize *normalize)
+{
+	gdouble value = 0.0;
+	BraseroNormalizePrivate *priv;
+
+	priv = BRASERO_NORMALIZE_PRIVATE (normalize);
+
+	/* Those next two are generated at the end only */
+	if (!strcmp (tag, GST_TAG_ALBUM_GAIN)) {
+		gst_tag_list_get_double (list, tag, &value);
+		priv->album_gain = value;
+	}
+	else if (!strcmp (tag, GST_TAG_ALBUM_PEAK)) {
+		gst_tag_list_get_double (list, tag, &value);
+		priv->album_peak = value;
+	}
+	else if (!strcmp (tag, GST_TAG_TRACK_PEAK)) {
+		gst_tag_list_get_double (list, tag, &value);
+		priv->track_peak = value;
+	}
+	else if (!strcmp (tag, GST_TAG_TRACK_GAIN)) {
+		gst_tag_list_get_double (list, tag, &value);
+		priv->track_gain = value;
+	}
+}
+
+static void
+brasero_normalize_song_end_reached (BraseroNormalize *normalize)
+{
+	GValue *value;
+	BraseroTrack *track;
+	GError *error = NULL;
+	BraseroNormalizePrivate *priv;
+
+	priv = BRASERO_NORMALIZE_PRIVATE (normalize);
+	
+	/* finished track: set tags */
+	BRASERO_JOB_LOG (normalize,
+			 "Setting track peak (%lf) and gain (%lf)",
+			 priv->track_peak,
+			 priv->track_gain);
+
+	value = g_new0 (GValue, 1);
+	g_value_init (value, G_TYPE_DOUBLE);
+	g_value_set_double (value, priv->track_peak);
+	brasero_track_tag_add (priv->track,
+			       BRASERO_TRACK_PEAK_VALUE,
+			       value);
+
+	value = g_new0 (GValue, 1);
+	g_value_init (value, G_TYPE_DOUBLE);
+	g_value_set_double (value, priv->track_gain);
+	brasero_track_tag_add (priv->track,
+			       BRASERO_TRACK_GAIN_VALUE,
+			       value);
+
+	priv->track_peak = 0.0;
+	priv->track_gain = 0.0;
+
+	if (!priv->tracks) {
+		BRASERO_JOB_LOG (normalize,
+				 "Setting album peak (%lf) and gain (%lf)",
+				 priv->album_peak,
+				 priv->album_gain);
+
+		/* finished: set tags */
+		value = g_new0 (GValue, 1);
+		g_value_init (value, G_TYPE_DOUBLE);
+		g_value_set_double (value, priv->album_peak);
+		brasero_job_tag_add (BRASERO_JOB (normalize),
+				     BRASERO_ALBUM_PEAK_VALUE,
+				     value);
+
+		value = g_new0 (GValue, 1);
+		g_value_init (value, G_TYPE_DOUBLE);
+		g_value_set_double (value, priv->album_gain);
+		brasero_job_tag_add (BRASERO_JOB (normalize),
+				     BRASERO_ALBUM_GAIN_VALUE,
+				     value);
+
+		brasero_job_finished_session (BRASERO_JOB (normalize));
+		return;
+	}
+
+	/* jump to next track */
+	gst_element_set_locked_state (priv->analysis, TRUE);
+	gst_element_set_state (priv->pipeline, GST_STATE_NULL);
+
+	track = priv->tracks->data;
+	priv->tracks = g_slist_remove (priv->tracks, track);
+	if (!brasero_normalize_set_next_track (BRASERO_JOB (normalize), track, &error)) {
+		gst_element_set_locked_state (priv->analysis, FALSE);
+		brasero_job_error (BRASERO_JOB (normalize), error);
+		return;
+	}
+
+	gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
+	gst_element_set_locked_state (priv->analysis, FALSE);
+}
+
+static gboolean
+brasero_normalize_bus_messages (GstBus *bus,
+				GstMessage *msg,
+				BraseroNormalize *normalize)
+{
+	BraseroNormalizePrivate *priv;
+	GstTagList *tags = NULL;
+	GError *error = NULL;
+	gchar *debug;
+
+	priv = BRASERO_NORMALIZE_PRIVATE (normalize);
+	switch (GST_MESSAGE_TYPE (msg)) {
+	case GST_MESSAGE_TAG:
+		/* This is the information we've been waiting for.
+		 * NOTE: levels for whole album is delivered at the end */
+		gst_message_parse_tag (msg, &tags);
+		gst_tag_list_foreach (tags, (GstTagForeachFunc) foreach_tag, normalize);
+		gst_tag_list_free (tags);
+		return TRUE;
+
+	case GST_MESSAGE_ERROR:
+		gst_message_parse_error (msg, &error, &debug);
+		BRASERO_JOB_LOG (normalize, debug);
+		g_free (debug);
+
+	        brasero_job_error (BRASERO_JOB (normalize), error);
+		return FALSE;
+
+	case GST_MESSAGE_EOS:
+		brasero_normalize_song_end_reached (normalize);
+		return FALSE;
+
+	case GST_MESSAGE_STATE_CHANGED:
+		break;
+
+	default:
+		return TRUE;
+	}
+
+	return TRUE;
+}
+
+static void
+brasero_normalize_new_decoded_pad_cb (GstElement *decode,
+				      GstPad *pad,
+				      gboolean arg2,
+				      GstElement *convert)
+{
+	GstPad *sink;
+	GstCaps *caps;
+	GstStructure *structure;
+
+	sink = gst_element_get_pad (convert, "sink");
+	if (GST_PAD_IS_LINKED (sink))
+		return;
+
+	/* make sure we only have audio */
+	caps = gst_pad_get_caps (pad);
+	if (!caps)
+		return;
+
+	structure = gst_caps_get_structure (caps, 0);
+	if (structure && g_strrstr (gst_structure_get_name (structure), "audio"))
+		gst_pad_link (pad, sink);
+
+	gst_object_unref (sink);
+	gst_caps_unref (caps);
+}
+
+static gboolean
+brasero_normalize_build_pipeline (BraseroNormalize *normalize,
+				  GError **error)
+{
+	GstElement *decode;
+	GstElement *pipeline;
+	GstElement *sink = NULL;
+	GstElement *convert = NULL;
+	GstElement *analysis = NULL;
+	GstElement *resample = NULL;
+	BraseroNormalizePrivate *priv;
+
+	priv = BRASERO_NORMALIZE_PRIVATE (normalize);
+
+	BRASERO_JOB_LOG (normalize, "Creating new pipeline");
+
+	/* create filesrc ! decodebin ! audioresample ! audioconvert ! rganalysis ! fakesink */
+	pipeline = gst_pipeline_new (NULL);
+	priv->pipeline = pipeline;
+
+	/* NOTE: a new source is created at start of every track */
+
+	/* decode */
+	decode = gst_element_factory_make ("decodebin", NULL);
+	if (decode == NULL) {
+		g_set_error (error,
+			     BRASERO_BURN_ERROR,
+			     BRASERO_BURN_ERROR_GENERAL,
+			     _("decode can't be created"));
+		goto error;
+	}
+	gst_bin_add (GST_BIN (pipeline), decode);
+	priv->decode = decode;
+
+	/* audioconvert */
+	convert = gst_element_factory_make ("audioconvert", NULL);
+	if (convert == NULL) {
+		g_set_error (error,
+			     BRASERO_BURN_ERROR,
+			     BRASERO_BURN_ERROR_GENERAL,
+			     _("audioconvert can't be created"));
+		goto error;
+	}
+	gst_bin_add (GST_BIN (pipeline), convert);
+
+	/* audioresample */
+	resample = gst_element_factory_make ("audioresample", NULL);
+	if (resample == NULL) {
+		g_set_error (error,
+			     BRASERO_BURN_ERROR,
+			     BRASERO_BURN_ERROR_GENERAL,
+			     _("audioresample can't be created"));
+		goto error;
+	}
+	gst_bin_add (GST_BIN (pipeline), resample);
+
+	/* rganalysis: set the number of tracks to be expected */
+	analysis = gst_element_factory_make ("rganalysis", NULL);
+	if (analysis == NULL) {
+		g_set_error (error,
+			     BRASERO_BURN_ERROR,
+			     BRASERO_BURN_ERROR_GENERAL,
+			     _("rganalysis can't be created"));
+		goto error;
+	}
+	priv->analysis = analysis;
+	gst_bin_add (GST_BIN (pipeline), analysis);
+
+	/* sink */
+	sink = gst_element_factory_make ("fakesink", NULL);
+	if (!sink) {
+		g_set_error (error,
+			     BRASERO_BURN_ERROR,
+			     BRASERO_BURN_ERROR_GENERAL,
+			     _("sink can't be created"));
+		goto error;
+	}
+	gst_bin_add (GST_BIN (pipeline), sink);
+	g_object_set (sink,
+		      "sync", FALSE,
+		      NULL);
+
+	/* link everything */
+	g_signal_connect (G_OBJECT (decode),
+			  "new-decoded-pad",
+			  G_CALLBACK (brasero_normalize_new_decoded_pad_cb),
+			  resample);
+	gst_element_link_many (resample,
+			       convert,
+			       analysis,
+			       sink,
+			       NULL);
+	return TRUE;
+
+error:
+
+	if (error && (*error))
+		BRASERO_JOB_LOG (normalize,
+				 "can't create object : %s \n",
+				 (*error)->message);
+
+	gst_object_unref (GST_OBJECT (pipeline));
+	return FALSE;
+}
+
+static BraseroBurnResult
+brasero_normalize_start (BraseroJob *job,
+			 GError **error)
+{
+	BraseroNormalizePrivate *priv;
+	BraseroTrack *track;
+
+	priv = BRASERO_NORMALIZE_PRIVATE (job);
+
+	priv->album_gain = -1.0;
+	priv->album_peak = -1.0;
+
+	/* get tracks */
+	brasero_job_get_tracks (job, &priv->tracks);
+	if (!priv->tracks)
+		return BRASERO_BURN_ERR;
+
+	priv->tracks = g_slist_copy (priv->tracks);
+	track = priv->tracks->data;
+	priv->tracks = g_slist_remove (priv->tracks, track);
+
+	if (!brasero_normalize_build_pipeline (BRASERO_NORMALIZE (job), error))
+		return BRASERO_BURN_ERR;
+
+	g_object_set (priv->analysis,
+		      "num-tracks", g_slist_length (priv->tracks),
+		      NULL);
+
+	if (!brasero_normalize_set_next_track (job, track, error))
+		return BRASERO_BURN_ERR;
+
+	/* ready to go */
+	brasero_job_set_current_action (job,
+					BRASERO_BURN_ACTION_ANALYSING,
+					_("Normalizing tracks"),
+					FALSE);
+	gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
+
+	return BRASERO_BURN_OK;
+}
+
+static BraseroBurnResult
+brasero_normalize_activate (BraseroJob *job,
+			    GError **error)
+{
+	GSList *tracks;
+	BraseroJobAction action;
+
+	brasero_job_get_action (job, &action);
+	if (action != BRASERO_JOB_ACTION_IMAGE)
+		return BRASERO_BURN_NOT_RUNNING;
+
+	/* check we have more than one track */
+	brasero_job_get_tracks (job, &tracks);
+	if (g_slist_length (tracks) < 2)
+		return BRASERO_BURN_NOT_RUNNING;
+
+	return BRASERO_BURN_OK;
+}
+
+static BraseroBurnResult
+brasero_normalize_clock_tick (BraseroJob *job)
+{
+	gint64 position = 0.0;
+	gint64 duration = 0.0;
+	BraseroNormalizePrivate *priv;
+	GstFormat format = GST_FORMAT_TIME;
+
+	priv = BRASERO_NORMALIZE_PRIVATE (job);
+
+	gst_element_query_duration (priv->pipeline, &format, &duration);
+	gst_element_query_position (priv->pipeline, &format, &position);
+
+	if (duration > 0) {
+		GSList *tracks;
+		gdouble progress;
+
+		brasero_job_get_tracks (job, &tracks);
+		progress = (gdouble) position / (gdouble) duration;
+
+		if (tracks) {
+			gdouble num_tracks;
+
+			num_tracks = g_slist_length (tracks);
+			progress = (gdouble) (num_tracks - 1.0 - (gdouble) g_slist_length (priv->tracks) + progress) / (gdouble) num_tracks;
+			brasero_job_set_progress (job, progress);
+		}
+	}
+
+	return BRASERO_BURN_OK;
+}
+
+static void
+brasero_normalize_init (BraseroNormalize *object)
+{}
+
+static void
+brasero_normalize_finalize (GObject *object)
+{
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+brasero_normalize_class_init (BraseroNormalizeClass *klass)
+{
+	GObjectClass* object_class = G_OBJECT_CLASS (klass);
+	BraseroJobClass *job_class = BRASERO_JOB_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (BraseroNormalizePrivate));
+
+	object_class->finalize = brasero_normalize_finalize;
+
+	job_class->activate = brasero_normalize_activate;
+	job_class->start = brasero_normalize_start;
+	job_class->clock_tick = brasero_normalize_clock_tick;
+	job_class->stop = brasero_normalize_stop;
+}
+
+static BraseroBurnResult
+brasero_normalize_export_caps (BraseroPlugin *plugin, gchar **error)
+{
+	GSList *input;
+
+	brasero_plugin_define (plugin,
+			       "normalize",
+			       _("Normalize allows to set consistent sound levels between tracks"),
+			       "Philippe Rouquier",
+			       0);
+
+	input = brasero_caps_audio_new (BRASERO_PLUGIN_IO_ACCEPT_FILE,
+					BRASERO_AUDIO_FORMAT_UNDEFINED);
+	brasero_plugin_process_caps (plugin, input);
+	brasero_plugin_set_process_flags (plugin, BRASERO_PLUGIN_RUN_FIRST);
+	g_slist_free (input);
+
+	return BRASERO_BURN_OK;
+}

Added: trunk/src/plugins/transcode/burn-normalize.h
==============================================================================
--- (empty file)
+++ trunk/src/plugins/transcode/burn-normalize.h	Sun May 25 17:49:51 2008
@@ -0,0 +1,48 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * brasero-normalize.c
+ * Copyright (C) Rouquier Philippe 2008 <bonfire-app wanadoo fr>
+ * 
+ * brasero-normalize.c is free software.
+ * 
+ * You may redistribute it and/or modify it under the terms of the
+ * GNU General Public License, as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ * 
+ * brasero-normalize.c is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with brasero-normalize.c.  If not, write to:
+ * 	The Free Software Foundation, Inc.,
+ * 	51 Franklin Street, Fifth Floor
+ * 	Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _BRASERO_NORMALIZE_H_
+#define _BRASERO_NORMALIZE_H_
+
+#include <glib-object.h>
+
+#include "burn-job.h"
+
+G_BEGIN_DECLS
+
+#define BRASERO_TYPE_NORMALIZE             (brasero_normalize_get_type ())
+#define BRASERO_NORMALIZE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), BRASERO_TYPE_NORMALIZE, BraseroNormalize))
+#define BRASERO_NORMALIZE_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), BRASERO_TYPE_NORMALIZE, BraseroNormalizeClass))
+#define BRASERO_IS_NORMALIZE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BRASERO_TYPE_NORMALIZE))
+#define BRASERO_IS_NORMALIZE_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), BRASERO_TYPE_NORMALIZE))
+#define BRASERO_NORMALIZE_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), BRASERO_TYPE_NORMALIZE, BraseroNormalizeClass))
+
+#define BRASERO_ALBUM_PEAK_VALUE	"peak_value"
+#define BRASERO_ALBUM_GAIN_VALUE	"gain_value"
+#define BRASERO_TRACK_PEAK_VALUE	"peak_value"
+#define BRASERO_TRACK_GAIN_VALUE	"gain_value"
+
+G_END_DECLS
+
+#endif /* _BRASERO_NORMALIZE_H_ */

Modified: trunk/src/plugins/transcode/burn-transcode.c
==============================================================================
--- trunk/src/plugins/transcode/burn-transcode.c	(original)
+++ trunk/src/plugins/transcode/burn-transcode.c	Sun May 25 17:49:51 2008
@@ -43,6 +43,7 @@
 #include "burn-job.h"
 #include "burn-plugin.h"
 #include "burn-transcode.h"
+#include "burn-normalize.h"
 
 BRASERO_PLUGIN_BOILERPLATE (BraseroTranscode, brasero_transcode, BRASERO_TYPE_JOB, BraseroJob);
 
@@ -52,15 +53,18 @@
 static void brasero_transcode_new_decoded_pad_cb (GstElement *decode,
 						  GstPad *pad,
 						  gboolean arg2,
-						  GstElement *convert);
+						  BraseroTranscode *transcode);
 
 struct BraseroTranscodePrivate {
 	GstElement *pipeline;
 	GstElement *convert;
-	GstElement *decode;
 	GstElement *source;
+	GstElement *decode;
 	GstElement *sink;
 
+	/* element to link decode to */
+	GstElement *link;
+
 	gint pad_size;
 	gint pad_fd;
 	gint pad_id;
@@ -191,8 +195,68 @@
 	return BRASERO_BURN_OK;
 }
 
+static void
+brasero_transcode_send_volume_event (BraseroTranscode *transcode)
+{
+	BraseroTranscodePrivate *priv;
+	gdouble track_peak = 0.0;
+	gdouble track_gain = 0.0;
+	GstTagList *tag_list;
+	BraseroTrack *track;
+	GstEvent *event;
+	GValue *value;
+
+	priv = BRASERO_TRANSCODE_PRIVATE (transcode);
+
+	brasero_job_get_current_track (BRASERO_JOB (transcode), &track);
+
+	BRASERO_JOB_LOG (transcode, "Sending audio levels tags");
+	if (brasero_track_tag_lookup (track, BRASERO_TRACK_PEAK_VALUE, &value) == BRASERO_BURN_OK)
+		track_peak = g_value_get_double (value);
+
+	if (brasero_track_tag_lookup (track, BRASERO_TRACK_GAIN_VALUE, &value) == BRASERO_BURN_OK)
+		track_gain = g_value_get_double (value);
+
+	/* it's possible we fail */
+	tag_list = gst_tag_list_new ();
+	gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
+			  GST_TAG_TRACK_GAIN, track_gain,
+			  GST_TAG_TRACK_PEAK, track_peak,
+			  NULL);
+
+	/* NOTE: that event is goind downstream */
+	event = gst_event_new_tag (tag_list);
+	if (!gst_element_send_event (priv->convert, event))
+		BRASERO_JOB_LOG (transcode, "Couldn't send tags to rgvolume");
+
+	BRASERO_JOB_LOG (transcode, "Set %lf %lf", track_gain, track_peak);
+}
+
+static GstElement *
+brasero_transcode_create_volume (BraseroTranscode *transcode,
+				 BraseroTrack *track)
+{
+	GstElement *volume = NULL;
+
+	/* see if we need a volume object */
+	if (brasero_track_tag_lookup (track, BRASERO_TRACK_PEAK_VALUE, NULL) == BRASERO_BURN_OK
+	||  brasero_track_tag_lookup (track, BRASERO_TRACK_GAIN_VALUE, NULL) == BRASERO_BURN_OK) {
+		BRASERO_JOB_LOG (transcode, "Found audio levels tags");
+		volume = gst_element_factory_make ("rgvolume", NULL);
+		g_object_set (volume,
+			      "album-mode", FALSE,
+			      NULL);
+
+		if (!volume)
+			BRASERO_JOB_LOG (transcode, "rgvolume object couldn't be created");
+	}
+
+	return volume;
+}
+
 static gboolean
-brasero_transcode_create_pipeline (BraseroTranscode *transcode, GError **error)
+brasero_transcode_create_pipeline (BraseroTranscode *transcode,
+				   GError **error)
 {
 	gchar *uri;
 	GstPad *sinkpad;
@@ -204,6 +268,7 @@
 	GstElement *sink = NULL;
 	BraseroJobAction action;
 	GstElement *filter = NULL;
+	GstElement *volume = NULL;
 	GstElement *convert = NULL;
 	BraseroTrack *track = NULL;
 	GstElement *resample = NULL;
@@ -219,14 +284,14 @@
 	if (priv->pipeline) {
 		gst_element_set_state (priv->pipeline, GST_STATE_NULL);
 		gst_object_unref (G_OBJECT (priv->pipeline));
-		priv->pipeline = NULL;
+		priv->link = NULL;
 		priv->sink = NULL;
 		priv->source = NULL;
 		priv->convert = NULL;
 		priv->pipeline = NULL;
 	}
 
-	/* create three types of pipeline according to the needs:
+	/* create three types of pipeline according to the needs: (possibly adding grvolume)
 	 * - filesrc ! decodebin ! audioconvert ! fakesink (find size)
 	 * - filesrc ! decodebin ! audioresample ! audioconvert ! audio/x-raw-int,rate=44100,width=16,depth=16,endianness=4321,signed ! filesink
 	 * - filesrc ! decodebin ! audioresample ! audioconvert ! audio/x-raw-int,rate=44100,width=16,depth=16,endianness=4321,signed ! fdsink
@@ -263,6 +328,8 @@
 		break;
 
 	case BRASERO_JOB_ACTION_IMAGE:
+		volume = brasero_transcode_create_volume (transcode, track);
+
 		if (brasero_job_get_fd_out (BRASERO_JOB (transcode), NULL) != BRASERO_BURN_OK) {
 			gchar *output;
 
@@ -362,24 +429,37 @@
 
 	if (action == BRASERO_JOB_ACTION_IMAGE) {
 		gst_element_link_many (source, decode, NULL);
+		priv->link = resample;
 		g_signal_connect (G_OBJECT (decode),
 				  "new-decoded-pad",
 				  G_CALLBACK (brasero_transcode_new_decoded_pad_cb),
-				  resample);
-		gst_element_link_many (resample,
-				       convert,
-				       filter,
-				       sink,
-				       NULL);
+				  transcode);
+
+		if (volume) {
+			gst_bin_add (GST_BIN (pipeline), volume);
+			gst_element_link_many (resample,
+					       convert,
+					       volume,
+					       filter,
+					       sink,
+					       NULL);
+		}
+		else
+			gst_element_link_many (resample,
+					       convert,
+					       filter,
+					       sink,
+					       NULL);
 	}
 	else {
 		gst_element_link (source, decode);
 		gst_element_link (convert, sink);
 
+		priv->link = convert;
 		g_signal_connect (G_OBJECT (decode),
 				  "new-decoded-pad",
 				  G_CALLBACK (brasero_transcode_new_decoded_pad_cb),
-				  convert);
+				  transcode);
 	}
 
 	priv->sink = sink;
@@ -686,7 +766,8 @@
 
 	gst_element_set_state (priv->pipeline, GST_STATE_NULL);
 	gst_object_unref (GST_OBJECT (priv->pipeline));
-	priv->pipeline = NULL;
+
+	priv->link = NULL;
 	priv->sink = NULL;
 	priv->source = NULL;
 	priv->convert = NULL;
@@ -1262,13 +1343,16 @@
 brasero_transcode_new_decoded_pad_cb (GstElement *decode,
 				      GstPad *pad,
 				      gboolean arg2,
-				      GstElement *convert)
+				      BraseroTranscode *transcode)
 {
 	GstPad *sink;
 	GstCaps *caps;
 	GstStructure *structure;
+	BraseroTranscodePrivate *priv;
+
+	priv = BRASERO_TRANSCODE_PRIVATE (transcode);
 
-	sink = gst_element_get_pad (convert, "sink");
+	sink = gst_element_get_pad (priv->link, "sink");
 	if (GST_PAD_IS_LINKED (sink))
 		return;
 
@@ -1277,6 +1361,9 @@
 	if (!caps)
 		return;
 
+	/* before linking pads (before any data reach grvolume), send tags */
+	brasero_transcode_send_volume_event (transcode);
+
 	structure = gst_caps_get_structure (caps, 0);
 	if (structure
 	&&  g_strrstr (gst_structure_get_name (structure), "audio"))



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]