diff options
author | René Stadler <mail@renestadler.de> | 2007-05-19 10:01:45 +0000 |
---|---|---|
committer | Tim-Philipp Müller <tim@centricular.net> | 2007-05-19 10:01:45 +0000 |
commit | 1645110f020d26eaa9700775dbedf646918fdd23 (patch) | |
tree | e58b53d9e4dd90f1e2b129bee0851a9695258950 /gst/replaygain/gstrganalysis.c | |
parent | 8d971fb7fab676c42e2bc64f4f0a5e633591ec72 (diff) | |
download | gst-plugins-bad-1645110f020d26eaa9700775dbedf646918fdd23.tar.gz gst-plugins-bad-1645110f020d26eaa9700775dbedf646918fdd23.tar.bz2 gst-plugins-bad-1645110f020d26eaa9700775dbedf646918fdd23.zip |
Add replaygain playback elements (#412710).
Original commit message from CVS:
Patch by: René Stadler <mail at renestadler de>
* docs/plugins/Makefile.am:
* docs/plugins/gst-plugins-bad-plugins-docs.sgml:
* docs/plugins/gst-plugins-bad-plugins-sections.txt:
* docs/plugins/inspect/plugin-replaygain.xml:
* gst/replaygain/Makefile.am:
* gst/replaygain/gstrganalysis.c: (gst_rg_analysis_class_init),
(gst_rg_analysis_start), (gst_rg_analysis_set_caps),
(gst_rg_analysis_transform_ip), (gst_rg_analysis_event),
(gst_rg_analysis_stop), (gst_rg_analysis_handle_tags),
(gst_rg_analysis_handle_eos), (gst_rg_analysis_track_result),
(gst_rg_analysis_album_result):
* gst/replaygain/gstrganalysis.h:
* gst/replaygain/gstrglimiter.c: (gst_rg_limiter_base_init),
(gst_rg_limiter_class_init), (gst_rg_limiter_init),
(gst_rg_limiter_set_property), (gst_rg_limiter_get_property),
(gst_rg_limiter_transform_ip):
* gst/replaygain/gstrglimiter.h:
* gst/replaygain/gstrgvolume.c: (gst_rg_volume_base_init),
(gst_rg_volume_class_init), (gst_rg_volume_init),
(gst_rg_volume_set_property), (gst_rg_volume_get_property),
(gst_rg_volume_dispose), (gst_rg_volume_change_state),
(gst_rg_volume_sink_event), (gst_rg_volume_tag_event),
(gst_rg_volume_reset), (gst_rg_volume_update_gain),
(gst_rg_volume_determine_gain):
* gst/replaygain/gstrgvolume.h:
* gst/replaygain/replaygain.c: (plugin_init):
* gst/replaygain/replaygain.h:
* gst/replaygain/rganalysis.h:
* tests/check/Makefile.am:
* tests/check/elements/.cvsignore:
* tests/check/elements/rganalysis.c: (send_eos_event),
(GST_START_TEST):
* tests/check/elements/rglimiter.c: (setup_rglimiter),
(cleanup_rglimiter), (set_playing_state), (create_test_buffer),
(verify_test_buffer), (GST_START_TEST), (rglimiter_suite), (main):
* tests/check/elements/rgvolume.c: (event_func), (setup_rgvolume),
(cleanup_rgvolume), (set_playing_state), (set_null_state),
(send_eos_event), (send_tag_event), (test_buffer_new),
(fail_unless_target_gain), (fail_unless_result_gain),
(fail_unless_gain), (GST_START_TEST), (rgvolume_suite), (main):
Add replaygain playback elements (#412710).
Diffstat (limited to 'gst/replaygain/gstrganalysis.c')
-rw-r--r-- | gst/replaygain/gstrganalysis.c | 298 |
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); |