summaryrefslogtreecommitdiffstats
path: root/gst/replaygain/gstrganalysis.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst/replaygain/gstrganalysis.c')
-rw-r--r--gst/replaygain/gstrganalysis.c298
1 files changed, 150 insertions, 148 deletions
diff --git a/gst/replaygain/gstrganalysis.c b/gst/replaygain/gstrganalysis.c
index 9ad50e0d..24367786 100644
--- a/gst/replaygain/gstrganalysis.c
+++ b/gst/replaygain/gstrganalysis.c
@@ -22,103 +22,29 @@
/**
* SECTION:element-rganalysis
+ * @see_also: <link linkend="GstRgVolume">rgvolume</link>
*
* <refsect2>
* <para>
- * GstRgAnalysis analyzes raw audio sample data in accordance with the
- * proposed <ulink url="http://replaygain.org">ReplayGain
- * standard</ulink> for calculating the ideal replay gain for music
- * tracks and albums. The element is designed as a pass-through
- * filter that never modifies any data. As it receives an EOS event,
- * it finalizes the ongoing analysis and generates a tag list
- * containing the results. It is sent downstream with a TAG event and
- * posted on the message bus with a TAG message. The EOS event is
- * forwarded as normal afterwards. Result tag lists at least contain
- * the tags #GST_TAG_TRACK_GAIN and #GST_TAG_TRACK_PEAK.
+ * This element analyzes raw audio sample data in accordance with the proposed
+ * <ulink url="http://replaygain.org">ReplayGain standard</ulink> for
+ * calculating the ideal replay gain for music tracks and albums. The element
+ * is designed as a pass-through filter that never modifies any data. As it
+ * receives an EOS event, it finalizes the ongoing analysis and generates a tag
+ * list containing the results. It is sent downstream with a tag event and
+ * posted on the message bus with a tag message. The EOS event is forwarded as
+ * normal afterwards. Result tag lists at least contain the tags
+ * #GST_TAG_TRACK_GAIN, #GST_TAG_TRACK_PEAK and #GST_TAG_REFERENCE_LEVEL.
* </para>
- * <title>Album processing</title>
* <para>
- * Analyzing several streams sequentially and assigning them a common
- * result gain is known as "album processing". If this gain is used
- * during playback (by switching to "album mode"), all tracks receive
- * the same amplification. This keeps the relative volume levels
- * between the tracks intact. To enable this, set the <link
- * linkend="GstRgAnalysis--num-tracks">num-tracks</link> property to
- * the number of streams that will be processed as album tracks.
- * Every time an EOS event is received, the value of this property
- * will be decremented by one. As it reaches zero, it is assumed that
- * the last track of the album finished. The tag list for the final
- * stream will contain the additional tags #GST_TAG_ALBUM_GAIN and
- * #GST_TAG_ALBUM_PEAK. All other streams just get the two track tags
- * posted because the values for the album tags are not known before
- * all tracks are analyzed. Applications need to make sure that the
- * album gain and peak values are also associated with the other
- * tracks when storing the results. It is thus a bit more complex to
- * implement, but should not be avoided since the album gain is
- * generally more valuable for use during playback than the track
- * gain.
- * </para>
- * <title>Skipping processing</title>
- * <para>
- * For assisting transcoder/converter applications, the element can
- * silently skip the processing of streams that already contain the
- * necessary meta data tags. Data will flow as usual but the element
- * will not consume CPU time and will not generate result tags. To
- * enable possible skipping, set the <link
- * linkend="GstRgAnalysis--forced">forced</link> property to #FALSE.
- * If used in conjunction with album processing, the element will skip
- * the number of remaining album tracks if a full set of tags is found
- * for the first track. If a subsequent track of the album is missing
- * tags, processing cannot start again. If this is undesired, your
- * application has to scan all files beforehand and enable forcing of
- * processing if needed.
- * </para>
- * <title>Tips</title>
- * <itemizedlist>
- * <listitem><para>
- * Because the generated metadata tags become available at the end of
- * streams, downstream muxer and encoder elements are normally unable
- * to save them in their output since they generally save metadata in
- * the file header. Therefore, it is often necessary that
- * applications read the results in a bus event handler for the tag
- * message. Obtaining the values this way is always needed for album
- * processing since the album gain and peak values need to be
- * associated with all tracks of an album, not just the last one.
- * </para></listitem>
- * <listitem><para>
- * To perform album processing, the element has to preserve data
- * between streams. This cannot survive a state change to the NULL or
- * READY state. If you change your pipeline's state to NULL or READY
- * between tracks, lock the rganalysis element's state using
- * gst_element_set_locked_state() when it is in PAUSED or PLAYING. As
- * with any other element, don't forget to unlock it again and set it
- * to the NULL state before dropping the last reference.
- * </para></listitem>
- * <listitem><para>
- * If the total number of album tracks is unknown beforehand, set the
- * num-tracks property to some large value like #G_MAXINT (or set it
- * to >= 2 before each track starts). Before the last track ends, set
- * the property value to 1.
- * </para></listitem>
- * </itemizedlist>
- * <title>Compliance</title>
- * <para>
- * Analyzing the ReplayGain pink noise reference waveform will compute
- * a result of +6.00dB instead of the expected 0.00dB because the
- * default reference level is 89dB. To obtain values as lined out in
- * the original proposal of ReplayGain, set the <link
- * linkend="GstRgAnalysis--reference-level">reference-level</link>
- * property to 83. Almost all software uses 89dB as a reference
- * however, which works against the tendency of the algorithm to
- * advise to drastically lower the volume of music with a highly
- * compressed dynamic range and high average output levels. This
- * tendency is normally to be fought during playback (if wanted), by
- * using a default pre-amp value of at least +6.00dB. At one point,
- * the majority of analyzer implementations switched to 89dB which
- * moved this adjustment to the analyzing/metadata writing process.
- * This change has been acknowledged by the author of the ReplayGain
- * proposal, however at the time of this writing, the webpage is still
- * not updated.
+ * Because the generated metadata tags become available at the end of streams,
+ * downstream muxer and encoder elements are normally unable to save them in
+ * their output since they generally save metadata in the file header.
+ * Therefore, it is often necessary that applications read the results in a bus
+ * event handler for the tag message. Obtaining the values this way is always
+ * needed for <link linkend="GstRgAnalysis--num-tracks">album processing</link>
+ * since the album gain and peak values need to be associated with all tracks of
+ * an album, not just the last one.
* </para>
* <title>Example launch lines</title>
* <para>Analyze a simple test waveform:</para>
@@ -127,18 +53,26 @@
* </programlisting>
* <para>Analyze a given file:</para>
* <programlisting>
- * gst-launch -t filesrc location="Some file.ogg" ! decodebin ! audioconvert ! audioresample ! rganalysis ! fakesink
+ * gst-launch -t filesrc location="Some file.ogg" ! decodebin \
+ * ! audioconvert ! audioresample ! rganalysis ! fakesink
* </programlisting>
* <para>Analyze the pink noise reference file:</para>
* <programlisting>
- * gst-launch -t gnomevfssrc location=http://replaygain.hydrogenaudio.org/ref_pink.wav ! wavparse ! rganalysis ! fakesink
+ * gst-launch -t gnomevfssrc location=http://replaygain.hydrogenaudio.org/ref_pink.wav \
+ * ! wavparse ! rganalysis ! fakesink
* </programlisting>
+ * <para>
+ * The above launch line yields a result gain of +6 dB (instead of the expected
+ * +0 dB). This is not in error, refer to the <link
+ * linkend="GstRgAnalysis--reference-level">reference-level</link> property
+ * documentation for more information.
+ * </para>
* <title>Acknowledgements</title>
* <para>
* This element is based on code used in the <ulink
- * url="http://sjeng.org/vorbisgain.html">vorbisgain</ulink> program
- * and many others. The relevant parts are copyrighted by David
- * Robinson, Glen Sawyer and Frank Klemm.
+ * url="http://sjeng.org/vorbisgain.html">vorbisgain</ulink> program and many
+ * others. The relevant parts are copyrighted by David Robinson, Glen Sawyer
+ * and Frank Klemm.
* </para>
* </refsect2>
*/
@@ -147,11 +81,11 @@
#include <config.h>
#endif
-#include <string.h>
#include <gst/gst.h>
#include <gst/base/gstbasetransform.h>
#include "gstrganalysis.h"
+#include "replaygain.h"
GST_DEBUG_CATEGORY_STATIC (gst_rg_analysis_debug);
#define GST_CAT_DEFAULT gst_rg_analysis_debug
@@ -254,18 +188,93 @@ gst_rg_analysis_class_init (GstRgAnalysisClass * klass)
gobject_class->set_property = gst_rg_analysis_set_property;
gobject_class->get_property = gst_rg_analysis_get_property;
+ /**
+ * GstRgAnalysis:num-tracks:
+ *
+ * Number of remaining album tracks.
+ *
+ * Analyzing several streams sequentially and assigning them a common result
+ * gain is known as "album processing". If this gain is used during playback
+ * (by switching to "album mode"), all tracks of an album receive the same
+ * amplification. This keeps the relative volume levels between the tracks
+ * intact. To enable this, set this property to the number of streams that
+ * will be processed as album tracks.
+ *
+ * Every time an EOS event is received, the value of this property is
+ * decremented by one. As it reaches zero, it is assumed that the last track
+ * of the album finished. The tag list for the final stream will contain the
+ * additional tags #GST_TAG_ALBUM_GAIN and #GST_TAG_ALBUM_PEAK. All other
+ * streams just get the two track tags posted because the values for the album
+ * tags are not known before all tracks are analyzed. Applications need to
+ * ensure that the album gain and peak values are also associated with the
+ * other tracks when storing the results.
+ *
+ * If the total number of album tracks is unknown beforehand, just ensure that
+ * the value is greater than 1 before each track starts. Then before the end
+ * of the last track, set it to the value 1.
+ *
+ * To perform album processing, the element has to preserve data between
+ * streams. This cannot survive a state change to the NULL or READY state.
+ * If you change your pipeline's state to NULL or READY between tracks, lock
+ * the element's state using gst_element_set_locked_state() when it is in
+ * PAUSED or PLAYING.
+ */
g_object_class_install_property (gobject_class, PROP_NUM_TRACKS,
g_param_spec_int ("num-tracks", "Number of album tracks",
- "Number of remaining tracks in the album",
- 0, G_MAXINT, 0, G_PARAM_READWRITE));
+ "Number of remaining album tracks", 0, G_MAXINT, 0,
+ G_PARAM_READWRITE));
+ /**
+ * GstRgAnalysis:forced:
+ *
+ * Whether to analyze streams even when ReplayGain tags exist.
+ *
+ * For assisting transcoder/converter applications, the element can silently
+ * skip the processing of streams that already contain the necessary tags.
+ * Data will flow as usual but the element will not consume CPU time and will
+ * not generate result tags. To enable possible skipping, set this property
+ * to #FALSE.
+ *
+ * If used in conjunction with <link linkend="GstRgAnalysis--num-tracks">album
+ * processing</link>, the element will skip the number of remaining album
+ * tracks if a full set of tags is found for the first track. If a subsequent
+ * track of the album is missing tags, processing cannot start again. If this
+ * is undesired, the application has to scan all files beforehand and enable
+ * forcing of processing if needed.
+ */
g_object_class_install_property (gobject_class, PROP_FORCED,
- g_param_spec_boolean ("forced", "Force processing",
- "Analyze streams even when ReplayGain tags exist",
+ g_param_spec_boolean ("forced", "Forced",
+ "Analyze even if ReplayGain tags exist",
FORCED_DEFAULT, G_PARAM_READWRITE));
+ /**
+ * GstRgAnalysis:reference-level:
+ *
+ * Reference level [dB].
+ *
+ * Analyzing the ReplayGain pink noise reference waveform computes a result of
+ * +6 dB instead of the expected 0 dB. This is because the default reference
+ * level is 89 dB. To obtain values as lined out in the original proposal of
+ * ReplayGain, set this property to 83.
+ *
+ * Almost all software uses 89 dB as a reference however, and this value has
+ * become the new official value. That is to say, while the change has been
+ * acclaimed by the author of the ReplayGain proposal, the <ulink
+ * url="http://replaygain.org">webpage</ulink> is still outdated at the time
+ * of this writing.
+ *
+ * The value was changed because the original proposal recommends a default
+ * pre-amp value of +6 dB for playback. This seemed a bit odd, as it means
+ * that the algorithm has the general tendency to produce adjustment values
+ * that are 6 dB too low. Bumping the reference level by 6 dB compensated for
+ * this.
+ *
+ * The problem of the reference level being ambiguous for lack of concise
+ * standardization is to be solved by adopting the #GST_TAG_REFERENCE_LEVEL
+ * tag, which allows to store the used value alongside the gain values.
+ */
g_object_class_install_property (gobject_class, PROP_REFERENCE_LEVEL,
g_param_spec_double ("reference-level", "Reference level",
- "Reference level in dB (83.0 for original proposal)",
- 0.0, G_MAXDOUBLE, RG_REFERENCE_LEVEL, G_PARAM_READWRITE));
+ "Reference level [dB]", 0.0, 150., RG_REFERENCE_LEVEL,
+ G_PARAM_READWRITE));
trans_class = (GstBaseTransformClass *) klass;
trans_class->start = GST_DEBUG_FUNCPTR (gst_rg_analysis_start);
@@ -346,7 +355,7 @@ gst_rg_analysis_start (GstBaseTransform * base)
filter->ctx = rg_analysis_new ();
filter->analyze = NULL;
- GST_DEBUG_OBJECT (filter, "Started");
+ GST_LOG_OBJECT (filter, "started");
return TRUE;
}
@@ -357,7 +366,7 @@ gst_rg_analysis_set_caps (GstBaseTransform * base, GstCaps * in_caps,
{
GstRgAnalysis *filter = GST_RG_ANALYSIS (base);
GstStructure *structure;
- const gchar *mime_type;
+ const gchar *name;
gint n_channels, sample_rate, sample_bit_size, sample_size;
g_return_val_if_fail (filter->ctx != NULL, FALSE);
@@ -367,7 +376,7 @@ gst_rg_analysis_set_caps (GstBaseTransform * base, GstCaps * in_caps,
in_caps, out_caps);
structure = gst_caps_get_structure (in_caps, 0);
- mime_type = gst_structure_get_name (structure);
+ name = gst_structure_get_name (structure);
if (!gst_structure_get_int (structure, "width", &sample_bit_size)
|| !gst_structure_get_int (structure, "channels", &n_channels)
@@ -381,7 +390,7 @@ gst_rg_analysis_set_caps (GstBaseTransform * base, GstCaps * in_caps,
goto invalid_format;
sample_size = sample_bit_size / 8;
- if (strcmp (mime_type, "audio/x-raw-float") == 0) {
+ if (g_str_equal (name, "audio/x-raw-float")) {
if (sample_size != sizeof (gfloat))
goto invalid_format;
@@ -398,7 +407,7 @@ gst_rg_analysis_set_caps (GstBaseTransform * base, GstCaps * in_caps,
else
goto invalid_format;
- } else if (strcmp (mime_type, "audio/x-raw-int") == 0) {
+ } else if (g_str_equal (name, "audio/x-raw-int")) {
if (sample_size != sizeof (gint16))
goto invalid_format;
@@ -437,13 +446,13 @@ gst_rg_analysis_transform_ip (GstBaseTransform * base, GstBuffer * buf)
{
GstRgAnalysis *filter = GST_RG_ANALYSIS (base);
- g_return_val_if_fail (filter->ctx != NULL, GST_FLOW_ERROR);
- g_return_val_if_fail (filter->analyze != NULL, GST_FLOW_ERROR);
+ g_return_val_if_fail (filter->ctx != NULL, GST_FLOW_WRONG_STATE);
+ g_return_val_if_fail (filter->analyze != NULL, GST_FLOW_NOT_NEGOTIATED);
if (filter->skip)
return GST_FLOW_OK;
- GST_DEBUG_OBJECT (filter, "Processing buffer of size %u",
+ GST_LOG_OBJECT (filter, "processing buffer of size %u",
GST_BUFFER_SIZE (buf));
filter->analyze (filter->ctx, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
@@ -463,11 +472,11 @@ gst_rg_analysis_event (GstBaseTransform * base, GstEvent * event)
case GST_EVENT_EOS:
{
- GST_DEBUG_OBJECT (filter, "Received EOS event");
+ GST_LOG_OBJECT (filter, "received EOS event");
gst_rg_analysis_handle_eos (filter);
- GST_DEBUG_OBJECT (filter, "Passing on EOS event");
+ GST_LOG_OBJECT (filter, "passing on EOS event");
break;
}
@@ -498,7 +507,7 @@ gst_rg_analysis_stop (GstBaseTransform * base)
rg_analysis_destroy (filter->ctx);
filter->ctx = NULL;
- GST_DEBUG_OBJECT (filter, "Stopped");
+ GST_LOG_OBJECT (filter, "stopped");
return TRUE;
}
@@ -514,13 +523,13 @@ gst_rg_analysis_handle_tags (GstRgAnalysis * filter,
filter->ignore_tags = FALSE;
if (filter->skip && album_processing) {
- GST_INFO_OBJECT (filter, "Ignoring TAG event: Skipping album");
+ GST_DEBUG_OBJECT (filter, "ignoring tag event: skipping album");
return;
} else if (filter->skip) {
- GST_INFO_OBJECT (filter, "Ignoring TAG event: Skipping track");
+ GST_DEBUG_OBJECT (filter, "ignoring tag event: skipping track");
return;
} else if (filter->ignore_tags) {
- GST_INFO_OBJECT (filter, "Ignoring TAG event: Cannot skip anyways");
+ GST_DEBUG_OBJECT (filter, "ignoring tag event: cannot skip anyways");
return;
}
@@ -534,30 +543,31 @@ gst_rg_analysis_handle_tags (GstRgAnalysis * filter,
GST_TAG_ALBUM_PEAK, &dummy);
if (!(filter->has_track_gain && filter->has_track_peak)) {
- GST_INFO_OBJECT (filter, "Track tags not complete yet");
+ GST_DEBUG_OBJECT (filter, "track tags not complete yet");
return;
}
if (album_processing && !(filter->has_album_gain && filter->has_album_peak)) {
- GST_INFO_OBJECT (filter, "Album tags not complete yet");
+ GST_DEBUG_OBJECT (filter, "album tags not complete yet");
return;
}
if (filter->forced) {
- GST_INFO_OBJECT (filter,
- "Existing tags are sufficient, but processing anyway (forced)");
+ GST_DEBUG_OBJECT (filter,
+ "existing tags are sufficient, but processing anyway (forced)");
return;
}
filter->skip = TRUE;
rg_analysis_reset (filter->ctx);
- if (!album_processing)
- GST_INFO_OBJECT (filter,
- "Existing tags are sufficient, will not process this track");
- else
- GST_INFO_OBJECT (filter,
- "Existing tags are sufficient, will not process this album");
+ if (!album_processing) {
+ GST_DEBUG_OBJECT (filter,
+ "existing tags are sufficient, will not process this track");
+ } else {
+ GST_DEBUG_OBJECT (filter,
+ "existing tags are sufficient, will not process this album");
+ }
}
static void
@@ -599,7 +609,9 @@ gst_rg_analysis_handle_eos (GstRgAnalysis * filter)
rg_analysis_reset_album (filter->ctx);
if (track_success || album_success) {
- GST_DEBUG_OBJECT (filter, "Posting tag list with results");
+ GST_LOG_OBJECT (filter, "posting tag list with results");
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
+ GST_TAG_REFERENCE_LEVEL, filter->reference_level, NULL);
/* This steals our reference to the list: */
gst_element_found_tags_for_pad (GST_ELEMENT (filter),
GST_BASE_TRANSFORM_SRC_PAD (GST_BASE_TRANSFORM (filter)), tag_list);
@@ -609,11 +621,12 @@ gst_rg_analysis_handle_eos (GstRgAnalysis * filter)
if (album_processing) {
filter->num_tracks--;
- if (!album_finished)
- GST_INFO_OBJECT (filter, "Album not finished yet (num-tracks is now %u)",
+ if (!album_finished) {
+ GST_DEBUG_OBJECT (filter, "album not finished yet (num-tracks is now %u)",
filter->num_tracks);
- else
- GST_INFO_OBJECT (filter, "Album finished (num-tracks is now 0)");
+ } else {
+ GST_DEBUG_OBJECT (filter, "album finished (num-tracks is now 0)");
+ }
}
if (album_processing)
@@ -631,10 +644,10 @@ gst_rg_analysis_track_result (GstRgAnalysis * filter, GstTagList ** tag_list)
if (track_success) {
track_gain += filter->reference_level - RG_REFERENCE_LEVEL;
- GST_INFO_OBJECT (filter, "Track gain is %+.2f dB, peak %.6f", track_gain,
+ GST_INFO_OBJECT (filter, "track gain is %+.2f dB, peak %.6f", track_gain,
track_peak);
} else {
- GST_INFO_OBJECT (filter, "Track was too short to analyze");
+ GST_INFO_OBJECT (filter, "track was too short to analyze");
}
if (track_success) {
@@ -658,10 +671,10 @@ gst_rg_analysis_album_result (GstRgAnalysis * filter, GstTagList ** tag_list)
if (album_success) {
album_gain += filter->reference_level - RG_REFERENCE_LEVEL;
- GST_INFO_OBJECT (filter, "Album gain is %+.2f dB, peak %.6f", album_gain,
+ GST_INFO_OBJECT (filter, "album gain is %+.2f dB, peak %.6f", album_gain,
album_peak);
} else {
- GST_INFO_OBJECT (filter, "Album was too short to analyze");
+ GST_INFO_OBJECT (filter, "album was too short to analyze");
}
if (album_success) {
@@ -673,14 +686,3 @@ gst_rg_analysis_album_result (GstRgAnalysis * filter, GstTagList ** tag_list)
return album_success;
}
-
-static gboolean
-plugin_init (GstPlugin * plugin)
-{
- return gst_element_register (plugin, "rganalysis", GST_RANK_NONE,
- GST_TYPE_RG_ANALYSIS);
-}
-
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "replaygain",
- "ReplayGain analysis", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
- GST_PACKAGE_ORIGIN);