1 /* GStreamer ReplayGain volume adjustment
3 * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
5 * gstrgvolume.c: Element to apply ReplayGain volume adjustment
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * SECTION:element-rgvolume
26 * @see_also: #GstRgLimiter, #GstRgAnalysis
28 * This element applies volume changes to streams as lined out in the proposed
29 * [ReplayGain standard](https://wiki.hydrogenaud.io/index.php?title=ReplayGain).
30 * It interprets the ReplayGain meta data tags and carries out the adjustment
31 * (by using a volume element internally).
33 * The relevant tags are:
34 * * #GST_TAG_TRACK_GAIN
35 * * #GST_TAG_TRACK_PEAK
36 * * #GST_TAG_ALBUM_GAIN
37 * * #GST_TAG_ALBUM_PEAK
38 * * #GST_TAG_REFERENCE_LEVEL
40 * The information carried by these tags must have been calculated beforehand by
41 * performing the ReplayGain analysis. This is implemented by the <link
42 * linkend="GstRgAnalysis">rganalysis</link> element.
44 * The signal compression/limiting recommendations outlined in the proposed
45 * standard are not implemented by this element. This has to be handled by
46 * separate elements because applications might want to have additional filters
47 * between the volume adjustment and the limiting stage. A basic limiter is
48 * included with this plugin: The <link linkend="GstRgLimiter">rglimiter</link>
49 * element applies -6 dB hard limiting as mentioned in the ReplayGain standard.
51 * ## Example launch line
53 * gst-launch-1.0 filesrc location=filename.ext ! decodebin ! audioconvert \
54 * ! rgvolume ! audioconvert ! audioresample ! alsasink
55 * ]| Playback of a file
64 #include <gst/pbutils/pbutils.h>
65 #include <gst/audio/audio.h>
68 #include "gstrgvolume.h"
69 #include "replaygain.h"
71 GST_DEBUG_CATEGORY_STATIC (gst_rg_volume_debug);
72 #define GST_CAT_DEFAULT gst_rg_volume_debug
77 #ifdef TIZEN_FEATURE_RGVOLUME_MODIFICATION
88 #ifdef TIZEN_FEATURE_RGVOLUME_MODIFICATION
89 #define DEFAULT_ENABLE_RGVOLUME TRUE
91 #define DEFAULT_ALBUM_MODE TRUE
92 #define DEFAULT_HEADROOM 0.0
93 #define DEFAULT_PRE_AMP 0.0
94 #define DEFAULT_FALLBACK_GAIN 0.0
96 #define DB_TO_LINEAR(x) pow (10., (x) / 20.)
97 #define LINEAR_TO_DB(x) (20. * log10 (x))
99 #define GAIN_FORMAT "+.02f dB"
100 #define PEAK_FORMAT ".06f"
102 #define VALID_GAIN(x) ((x) > -60.00 && (x) < 60.00)
103 #define VALID_PEAK(x) ((x) > 0.)
105 /* Same template caps as GstVolume, for I don't like having just ANY caps. */
107 #define FORMAT "{ "GST_AUDIO_NE(F32)","GST_AUDIO_NE(S16)" }"
109 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
112 GST_STATIC_CAPS ("audio/x-raw, "
113 "format = (string) " FORMAT ", "
114 "layout = (string) { interleaved, non-interleaved }, "
115 "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]"));
117 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
120 GST_STATIC_CAPS ("audio/x-raw, "
121 "format = (string) " FORMAT ", "
122 "layout = (string) { interleaved, non-interleaved }, "
123 "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]"));
125 #define gst_rg_volume_parent_class parent_class
126 G_DEFINE_TYPE (GstRgVolume, gst_rg_volume, GST_TYPE_BIN);
127 GST_ELEMENT_REGISTER_DEFINE (rgvolume, "rgvolume", GST_RANK_NONE,
130 static void gst_rg_volume_set_property (GObject * object, guint prop_id,
131 const GValue * value, GParamSpec * pspec);
132 static void gst_rg_volume_get_property (GObject * object, guint prop_id,
133 GValue * value, GParamSpec * pspec);
134 static void gst_rg_volume_dispose (GObject * object);
136 static GstStateChangeReturn gst_rg_volume_change_state (GstElement * element,
137 GstStateChange transition);
138 static gboolean gst_rg_volume_sink_event (GstPad * pad, GstObject * parent,
141 static GstEvent *gst_rg_volume_tag_event (GstRgVolume * self, GstEvent * event);
142 static void gst_rg_volume_reset (GstRgVolume * self);
143 static void gst_rg_volume_update_gain (GstRgVolume * self);
144 static inline void gst_rg_volume_determine_gain (GstRgVolume * self,
145 gdouble * target_gain, gdouble * result_gain);
148 gst_rg_volume_class_init (GstRgVolumeClass * klass)
150 GObjectClass *gobject_class;
151 GstElementClass *element_class;
152 GstBinClass *bin_class;
154 gobject_class = (GObjectClass *) klass;
156 gobject_class->set_property = gst_rg_volume_set_property;
157 gobject_class->get_property = gst_rg_volume_get_property;
158 gobject_class->dispose = gst_rg_volume_dispose;
160 #ifdef TIZEN_FEATURE_RGVOLUME_MODIFICATION
162 * GstRgVolume:enable-rgvolume:
164 * Whether to enable replaygain volume.
166 * If rgvulme is disabled, the rgvolume isn't affected by tag and properties.
168 g_object_class_install_property (gobject_class, PROP_ENABLE_RGVOLUME,
169 g_param_spec_boolean ("enable-rgvolume", "Enable rg volume",
170 "Whether to enable replaygain volume", DEFAULT_ENABLE_RGVOLUME,
171 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
175 * GstRgVolume:album-mode:
177 * Whether to prefer album gain over track gain.
179 * If set to %TRUE, use album gain instead of track gain if both are
180 * available. This keeps the relative loudness levels of tracks from the same
183 * If set to %FALSE, track mode is used instead. This effectively leads to
184 * more extensive normalization.
186 * If album mode is enabled but the album gain tag is absent in the stream,
187 * the track gain is used instead. If both gain tags are missing, the value
188 * of the #GstRgVolume:fallback-gain property is used instead.
190 g_object_class_install_property (gobject_class, PROP_ALBUM_MODE,
191 g_param_spec_boolean ("album-mode", "Album mode",
192 "Prefer album over track gain", DEFAULT_ALBUM_MODE,
193 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
195 * GstRgVolume:headroom:
197 * Extra headroom [dB]. This controls the amount by which the output can
198 * exceed digital full scale.
200 * Only set this to a value greater than 0.0 if signal compression/limiting of
201 * a suitable form is applied to the output (or output is brought into the
202 * correct range by some other transformation).
204 * This element internally uses a volume element, which also supports
205 * operating on integer audio formats. These formats do not allow exceeding
206 * digital full scale. If extra headroom is used, make sure that the raw
207 * audio data format is floating point (F32). Otherwise,
208 * clipping distortion might be introduced as part of the volume adjustment
211 g_object_class_install_property (gobject_class, PROP_HEADROOM,
212 g_param_spec_double ("headroom", "Headroom", "Extra headroom [dB]",
213 0., 60., DEFAULT_HEADROOM,
214 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
216 * GstRgVolume:pre-amp:
218 * Additional gain to apply globally [dB]. This controls the trade-off
219 * between uniformity of normalization and utilization of available dynamic
222 * Note that the default value is 0 dB because the ReplayGain reference value
223 * was adjusted by +6 dB (from 83 to 89 dB). The original proposal stated
224 * that a proper default pre-amp value is +6 dB, this translates to the used 0
227 g_object_class_install_property (gobject_class, PROP_PRE_AMP,
228 g_param_spec_double ("pre-amp", "Pre-amp", "Extra gain [dB]",
229 -60., 60., DEFAULT_PRE_AMP,
230 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
232 * GstRgVolume:fallback-gain:
234 * Fallback gain [dB] for streams missing ReplayGain tags.
236 g_object_class_install_property (gobject_class, PROP_FALLBACK_GAIN,
237 g_param_spec_double ("fallback-gain", "Fallback gain",
238 "Gain for streams missing tags [dB]",
239 -60., 60., DEFAULT_FALLBACK_GAIN,
240 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
242 * GstRgVolume:result-gain:
244 * Applied gain [dB]. This gain is applied to processed buffer data.
246 * This is set to the #GstRgVolume:target-gain if amplification by that amount
247 * can be applied safely. "Safely" means that the volume adjustment does not
248 * inflict clipping distortion. Should this not be the case, the result gain
249 * is set to an appropriately reduced value (by applying peak normalization).
250 * The proposed standard calls this "clipping prevention".
252 * The difference between target and result gain reflects the necessary amount
253 * of reduction. Applications can make use of this information to temporarily
254 * reduce the #GstRgVolume:pre-amp for subsequent streams, as recommended by
255 * the ReplayGain standard.
257 * Note that target and result gain differing for a great majority of streams
258 * indicates a problem: What happens in this case is that most streams receive
259 * peak normalization instead of amplification by the ideal replay gain. To
260 * prevent this, the #GstRgVolume:pre-amp has to be lowered and/or a limiter
261 * has to be used which facilitates the use of #GstRgVolume:headroom.
263 g_object_class_install_property (gobject_class, PROP_RESULT_GAIN,
264 g_param_spec_double ("result-gain", "Result-gain", "Applied gain [dB]",
265 -120., 120., 0., G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
267 * GstRgVolume:target-gain:
269 * Applicable gain [dB]. This gain is supposed to be applied.
271 * Depending on the value of the #GstRgVolume:album-mode property and the
272 * presence of ReplayGain tags in the stream, this is set according to one of
273 * these simple formulas:
276 * * #GstRgVolume:pre-amp + album gain of the stream
277 * * #GstRgVolume:pre-amp + track gain of the stream
278 * * #GstRgVolume:pre-amp + #GstRgVolume:fallback-gain
281 g_object_class_install_property (gobject_class, PROP_TARGET_GAIN,
282 g_param_spec_double ("target-gain", "Target-gain",
283 "Applicable gain [dB]", -120., 120., 0.,
284 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
286 element_class = (GstElementClass *) klass;
287 element_class->change_state = GST_DEBUG_FUNCPTR (gst_rg_volume_change_state);
289 bin_class = (GstBinClass *) klass;
290 /* Setting these to NULL makes gst_bin_add and _remove refuse to let anyone
291 * mess with our internals. */
292 bin_class->add_element = NULL;
293 bin_class->remove_element = NULL;
295 gst_element_class_add_static_pad_template (element_class, &src_template);
296 gst_element_class_add_static_pad_template (element_class, &sink_template);
297 gst_element_class_set_static_metadata (element_class, "ReplayGain volume",
298 "Filter/Effect/Audio",
299 "Apply ReplayGain volume adjustment",
300 "Ren\xc3\xa9 Stadler <mail@renestadler.de>");
302 GST_DEBUG_CATEGORY_INIT (gst_rg_volume_debug, "rgvolume", 0,
303 "ReplayGain volume element");
307 gst_rg_volume_init (GstRgVolume * self)
309 GObjectClass *volume_class;
310 GstPad *volume_pad, *ghost_pad;
312 #ifdef TIZEN_FEATURE_RGVOLUME_MODIFICATION
313 self->enable_rgvolume = DEFAULT_ENABLE_RGVOLUME;
315 self->album_mode = DEFAULT_ALBUM_MODE;
316 self->headroom = DEFAULT_HEADROOM;
317 self->pre_amp = DEFAULT_PRE_AMP;
318 self->fallback_gain = DEFAULT_FALLBACK_GAIN;
319 self->target_gain = 0.0;
320 self->result_gain = 0.0;
322 self->volume_element = gst_element_factory_make ("volume", "rgvolume-volume");
323 if (G_UNLIKELY (self->volume_element == NULL)) {
326 GST_WARNING_OBJECT (self, "could not create volume element");
327 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (self), "volume");
328 gst_element_post_message (GST_ELEMENT_CAST (self), msg);
330 /* Nothing else to do, we will refuse the state change from NULL to READY to
331 * indicate that something went very wrong. It is doubtful that someone
332 * attempts changing our state though, since we end up having no pads! */
336 volume_class = G_OBJECT_GET_CLASS (G_OBJECT (self->volume_element));
337 self->max_volume = G_PARAM_SPEC_DOUBLE
338 (g_object_class_find_property (volume_class, "volume"))->maximum;
340 GST_BIN_CLASS (parent_class)->add_element (GST_BIN_CAST (self),
341 self->volume_element);
343 volume_pad = gst_element_get_static_pad (self->volume_element, "sink");
344 ghost_pad = gst_ghost_pad_new_from_template ("sink", volume_pad,
345 GST_PAD_PAD_TEMPLATE (volume_pad));
346 gst_object_unref (volume_pad);
347 gst_pad_set_event_function (ghost_pad, gst_rg_volume_sink_event);
348 gst_element_add_pad (GST_ELEMENT_CAST (self), ghost_pad);
350 volume_pad = gst_element_get_static_pad (self->volume_element, "src");
351 ghost_pad = gst_ghost_pad_new_from_template ("src", volume_pad,
352 GST_PAD_PAD_TEMPLATE (volume_pad));
353 gst_object_unref (volume_pad);
354 gst_element_add_pad (GST_ELEMENT_CAST (self), ghost_pad);
358 gst_rg_volume_set_property (GObject * object, guint prop_id,
359 const GValue * value, GParamSpec * pspec)
361 GstRgVolume *self = GST_RG_VOLUME (object);
364 #ifdef TIZEN_FEATURE_RGVOLUME_MODIFICATION
365 case PROP_ENABLE_RGVOLUME:
366 self->enable_rgvolume = g_value_get_boolean (value);
369 case PROP_ALBUM_MODE:
370 self->album_mode = g_value_get_boolean (value);
373 self->headroom = g_value_get_double (value);
376 self->pre_amp = g_value_get_double (value);
378 case PROP_FALLBACK_GAIN:
379 self->fallback_gain = g_value_get_double (value);
382 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
386 gst_rg_volume_update_gain (self);
390 gst_rg_volume_get_property (GObject * object, guint prop_id,
391 GValue * value, GParamSpec * pspec)
393 GstRgVolume *self = GST_RG_VOLUME (object);
396 #ifdef TIZEN_FEATURE_RGVOLUME_MODIFICATION
397 case PROP_ENABLE_RGVOLUME:
398 g_value_set_boolean (value, self->enable_rgvolume);
401 case PROP_ALBUM_MODE:
402 g_value_set_boolean (value, self->album_mode);
405 g_value_set_double (value, self->headroom);
408 g_value_set_double (value, self->pre_amp);
410 case PROP_FALLBACK_GAIN:
411 g_value_set_double (value, self->fallback_gain);
413 case PROP_TARGET_GAIN:
414 g_value_set_double (value, self->target_gain);
415 #ifdef TIZEN_FEATURE_RGVOLUME_MODIFICATION
416 if (!self->enable_rgvolume)
417 g_value_set_double (value, 0.0);
420 case PROP_RESULT_GAIN:
421 g_value_set_double (value, self->result_gain);
422 #ifdef TIZEN_FEATURE_RGVOLUME_MODIFICATION
423 if (!self->enable_rgvolume)
424 g_value_set_double (value, 0.0);
428 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
434 gst_rg_volume_dispose (GObject * object)
436 GstRgVolume *self = GST_RG_VOLUME (object);
438 if (self->volume_element != NULL) {
439 /* Manually remove our child using the bin implementation of remove_element.
440 * This is needed because we prevent gst_bin_remove from working, which the
441 * parent dispose handler would use if we had any children left. */
442 GST_BIN_CLASS (parent_class)->remove_element (GST_BIN_CAST (self),
443 self->volume_element);
444 self->volume_element = NULL;
447 G_OBJECT_CLASS (parent_class)->dispose (object);
450 static GstStateChangeReturn
451 gst_rg_volume_change_state (GstElement * element, GstStateChange transition)
453 GstRgVolume *self = GST_RG_VOLUME (element);
454 GstStateChangeReturn res;
456 switch (transition) {
457 case GST_STATE_CHANGE_NULL_TO_READY:
459 if (G_UNLIKELY (self->volume_element == NULL)) {
460 /* Creating our child volume element in _init failed. */
461 return GST_STATE_CHANGE_FAILURE;
465 case GST_STATE_CHANGE_READY_TO_PAUSED:
467 gst_rg_volume_reset (self);
474 res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
479 /* Event function for the ghost sink pad. */
481 gst_rg_volume_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
484 GstEvent *send_event = event;
487 self = GST_RG_VOLUME (parent);
489 switch (GST_EVENT_TYPE (event)) {
492 GST_LOG_OBJECT (self, "received tag event");
494 send_event = gst_rg_volume_tag_event (self, event);
496 if (send_event == NULL)
497 GST_LOG_OBJECT (self, "all tags handled, dropping event");
503 gst_rg_volume_reset (self);
510 if (G_LIKELY (send_event != NULL))
511 res = gst_pad_event_default (pad, parent, send_event);
519 gst_rg_volume_tag_event (GstRgVolume * self, GstEvent * event)
521 GstTagList *tag_list;
522 gboolean has_track_gain, has_track_peak, has_album_gain, has_album_peak;
523 gboolean has_ref_level;
525 g_return_val_if_fail (event != NULL, NULL);
526 g_return_val_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_TAG, event);
528 gst_event_parse_tag (event, &tag_list);
530 if (gst_tag_list_is_empty (tag_list))
533 has_track_gain = gst_tag_list_get_double (tag_list, GST_TAG_TRACK_GAIN,
535 has_track_peak = gst_tag_list_get_double (tag_list, GST_TAG_TRACK_PEAK,
537 has_album_gain = gst_tag_list_get_double (tag_list, GST_TAG_ALBUM_GAIN,
539 has_album_peak = gst_tag_list_get_double (tag_list, GST_TAG_ALBUM_PEAK,
541 has_ref_level = gst_tag_list_get_double (tag_list, GST_TAG_REFERENCE_LEVEL,
542 &self->reference_level);
544 if (!has_track_gain && !has_track_peak && !has_album_gain && !has_album_peak)
547 if (has_ref_level && (has_track_gain || has_album_gain)
548 && (ABS (self->reference_level - RG_REFERENCE_LEVEL) > 1.e-6)) {
549 /* Log a message stating the amount of adjustment that is applied below. */
550 GST_DEBUG_OBJECT (self,
551 "compensating for reference level difference by %" GAIN_FORMAT,
552 RG_REFERENCE_LEVEL - self->reference_level);
554 if (has_track_gain) {
555 self->track_gain += RG_REFERENCE_LEVEL - self->reference_level;
557 if (has_album_gain) {
558 self->album_gain += RG_REFERENCE_LEVEL - self->reference_level;
561 /* Ignore values that are obviously invalid. */
562 if (G_UNLIKELY (has_track_gain && !VALID_GAIN (self->track_gain))) {
563 GST_DEBUG_OBJECT (self,
564 "ignoring bogus track gain value %" GAIN_FORMAT, self->track_gain);
565 has_track_gain = FALSE;
567 if (G_UNLIKELY (has_track_peak && !VALID_PEAK (self->track_peak))) {
568 GST_DEBUG_OBJECT (self,
569 "ignoring bogus track peak value %" PEAK_FORMAT, self->track_peak);
570 has_track_peak = FALSE;
572 if (G_UNLIKELY (has_album_gain && !VALID_GAIN (self->album_gain))) {
573 GST_DEBUG_OBJECT (self,
574 "ignoring bogus album gain value %" GAIN_FORMAT, self->album_gain);
575 has_album_gain = FALSE;
577 if (G_UNLIKELY (has_album_peak && !VALID_PEAK (self->album_peak))) {
578 GST_DEBUG_OBJECT (self,
579 "ignoring bogus album peak value %" PEAK_FORMAT, self->album_peak);
580 has_album_peak = FALSE;
583 /* Clamp peaks >1.0. Float based decoders can produce spurious samples >1.0,
584 * cutting these files back to 1.0 should not cause any audible distortion.
585 * This is most often seen with Vorbis files. */
586 if (has_track_peak && self->track_peak > 1.) {
587 GST_DEBUG_OBJECT (self,
588 "clamping track peak %" PEAK_FORMAT " to 1.0", self->track_peak);
589 self->track_peak = 1.0;
591 if (has_album_peak && self->album_peak > 1.) {
592 GST_DEBUG_OBJECT (self,
593 "clamping album peak %" PEAK_FORMAT " to 1.0", self->album_peak);
594 self->album_peak = 1.0;
597 self->has_track_gain |= has_track_gain;
598 self->has_track_peak |= has_track_peak;
599 self->has_album_gain |= has_album_gain;
600 self->has_album_peak |= has_album_peak;
602 tag_list = gst_tag_list_copy (tag_list);
603 gst_tag_list_remove_tag (tag_list, GST_TAG_TRACK_GAIN);
604 gst_tag_list_remove_tag (tag_list, GST_TAG_TRACK_PEAK);
605 gst_tag_list_remove_tag (tag_list, GST_TAG_ALBUM_GAIN);
606 gst_tag_list_remove_tag (tag_list, GST_TAG_ALBUM_PEAK);
607 gst_tag_list_remove_tag (tag_list, GST_TAG_REFERENCE_LEVEL);
609 gst_rg_volume_update_gain (self);
611 gst_event_unref (event);
612 if (gst_tag_list_is_empty (tag_list)) {
613 gst_tag_list_unref (tag_list);
617 return gst_event_new_tag (tag_list);
621 gst_rg_volume_reset (GstRgVolume * self)
623 self->has_track_gain = FALSE;
624 self->has_track_peak = FALSE;
625 self->has_album_gain = FALSE;
626 self->has_album_peak = FALSE;
628 self->reference_level = RG_REFERENCE_LEVEL;
630 gst_rg_volume_update_gain (self);
634 gst_rg_volume_update_gain (GstRgVolume * self)
636 gdouble target_gain, result_gain, result_volume;
637 gboolean target_gain_changed, result_gain_changed;
639 #ifdef TIZEN_FEATURE_RGVOLUME_MODIFICATION
640 if (!self->enable_rgvolume) {
641 g_object_set (self->volume_element, "volume", 1.0, NULL);
645 gst_rg_volume_determine_gain (self, &target_gain, &result_gain);
647 result_volume = DB_TO_LINEAR (result_gain);
649 /* Ensure that the result volume is within the range that the volume element
650 * can handle. Currently, the limit is 10. (+20 dB), which should not be
652 if (G_UNLIKELY (result_volume > self->max_volume)) {
653 GST_INFO_OBJECT (self,
654 "cannot handle result gain of %" GAIN_FORMAT " (%0.6f), adjusting",
655 result_gain, result_volume);
657 result_volume = self->max_volume;
658 result_gain = LINEAR_TO_DB (result_volume);
661 /* Direct comparison is OK in this case. */
662 if (target_gain == result_gain) {
663 GST_INFO_OBJECT (self,
664 "result gain is %" GAIN_FORMAT " (%0.6f), matching target",
665 result_gain, result_volume);
667 GST_INFO_OBJECT (self,
668 "result gain is %" GAIN_FORMAT " (%0.6f), target is %" GAIN_FORMAT,
669 result_gain, result_volume, target_gain);
672 target_gain_changed = (self->target_gain != target_gain);
673 result_gain_changed = (self->result_gain != result_gain);
675 self->target_gain = target_gain;
676 self->result_gain = result_gain;
678 g_object_set (self->volume_element, "volume", result_volume, NULL);
680 if (target_gain_changed)
681 g_object_notify ((GObject *) self, "target-gain");
682 if (result_gain_changed)
683 g_object_notify ((GObject *) self, "result-gain");
687 gst_rg_volume_determine_gain (GstRgVolume * self, gdouble * target_gain,
688 gdouble * result_gain)
692 if (!self->has_track_gain && !self->has_album_gain) {
694 GST_DEBUG_OBJECT (self, "using fallback gain");
695 gain = self->fallback_gain;
698 } else if ((self->album_mode && self->has_album_gain)
699 || (!self->album_mode && !self->has_track_gain)) {
701 gain = self->album_gain;
702 if (G_LIKELY (self->has_album_peak)) {
703 peak = self->album_peak;
705 GST_DEBUG_OBJECT (self, "album peak missing, assuming 1.0");
708 /* Falling back from track to album gain shouldn't really happen. */
709 if (G_UNLIKELY (!self->album_mode))
710 GST_INFO_OBJECT (self, "falling back to album gain");
713 /* !album_mode && !has_album_gain || album_mode && has_track_gain */
715 gain = self->track_gain;
716 if (G_LIKELY (self->has_track_peak)) {
717 peak = self->track_peak;
719 GST_DEBUG_OBJECT (self, "track peak missing, assuming 1.0");
722 if (self->album_mode)
723 GST_INFO_OBJECT (self, "falling back to track gain");
726 gain += self->pre_amp;
731 if (LINEAR_TO_DB (peak) + gain > self->headroom) {
732 *result_gain = LINEAR_TO_DB (1. / peak) + self->headroom;