summaryrefslogtreecommitdiff
path: root/ext/opus/gstopusdec.c
diff options
context:
space:
mode:
authorVincent Penquerc'h <vincent.penquerch@collabora.co.uk>2011-11-23 13:22:12 +0000
committerVincent Penquerc'h <vincent.penquerch@collabora.co.uk>2011-11-23 14:03:30 +0000
commit4bd5ef9bc5c09a363ebd7fc7e1236a77e939bc69 (patch)
tree26cbcb28da35187b205964d49201afb5f02fd552 /ext/opus/gstopusdec.c
parentb80f52a729cbebeb67808765d990ae83544d86f7 (diff)
downloadgstreamer-plugins-bad-4bd5ef9bc5c09a363ebd7fc7e1236a77e939bc69.tar.gz
opusdec: implement replay gain
It would ideally be better to leave this to a rgvolume element, but we don't control the pipeline. So do it by default, and allow disabling it via a property, so the correct volume should always be output.
Diffstat (limited to 'ext/opus/gstopusdec.c')
-rw-r--r--ext/opus/gstopusdec.c55
1 files changed, 53 insertions, 2 deletions
diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c
index 7d70fea18..e85a6b747 100644
--- a/ext/opus/gstopusdec.c
+++ b/ext/opus/gstopusdec.c
@@ -41,6 +41,7 @@
# include "config.h"
#endif
+#include <math.h>
#include <string.h>
#include <gst/tag/tag.h>
#include "gstopusheader.h"
@@ -67,12 +68,16 @@ GST_STATIC_PAD_TEMPLATE ("sink",
GST_STATIC_CAPS ("audio/x-opus")
);
+#define DB_TO_LINEAR(x) pow (10., (x) / 20.)
+
#define DEFAULT_USE_INBAND_FEC FALSE
+#define DEFAULT_APPLY_GAIN TRUE
enum
{
PROP_0,
- PROP_USE_INBAND_FEC
+ PROP_USE_INBAND_FEC,
+ PROP_APPLY_GAIN
};
GST_BOILERPLATE (GstOpusDec, gst_opus_dec, GstAudioDecoder,
@@ -131,6 +136,11 @@ gst_opus_dec_class_init (GstOpusDecClass * klass)
"Use forward error correction if available", DEFAULT_USE_INBAND_FEC,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_APPLY_GAIN,
+ g_param_spec_boolean ("apply-gain", "Apply gain",
+ "Apply gain if any is specified in the header", DEFAULT_APPLY_GAIN,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
GST_DEBUG_CATEGORY_INIT (opusdec_debug, "opusdec", 0,
"opus decoding element");
}
@@ -150,6 +160,7 @@ gst_opus_dec_reset (GstOpusDec * dec)
dec->primed = FALSE;
dec->pre_skip = 0;
+ dec->r128_gain = 0;
}
static void
@@ -158,6 +169,7 @@ gst_opus_dec_init (GstOpusDec * dec, GstOpusDecClass * g_class)
dec->sample_rate = 0;
dec->n_channels = 0;
dec->use_inband_fec = FALSE;
+ dec->apply_gain = DEFAULT_APPLY_GAIN;
gst_opus_dec_reset (dec);
}
@@ -190,6 +202,18 @@ gst_opus_dec_stop (GstAudioDecoder * dec)
return TRUE;
}
+static double
+gst_opus_dec_get_r128_gain (gint16 r128_gain)
+{
+ return r128_gain / (double) (1 << 8);
+}
+
+static double
+gst_opus_dec_get_r128_volume (gint16 r128_gain)
+{
+ return DB_TO_LINEAR (gst_opus_dec_get_r128_gain (r128_gain));
+}
+
static GstFlowReturn
gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf)
{
@@ -198,7 +222,11 @@ gst_opus_dec_parse_header (GstOpusDec * dec, GstBuffer * buf)
g_return_val_if_fail (GST_BUFFER_SIZE (buf) >= 19, GST_FLOW_ERROR);
dec->pre_skip = GST_READ_UINT16_LE (GST_BUFFER_DATA (buf) + 10);
- GST_INFO_OBJECT (dec, "Found pre-skip of %u samples", dec->pre_skip);
+ dec->r128_gain = GST_READ_UINT16_LE (GST_BUFFER_DATA (buf) + 14);
+ dec->r128_gain_volume = gst_opus_dec_get_r128_volume (dec->r128_gain);
+ GST_INFO_OBJECT (dec,
+ "Found pre-skip of %u samples, R128 gain %d (volume %f)",
+ dec->pre_skip, dec->r128_gain, dec->r128_gain_volume);
return GST_FLOW_OK;
}
@@ -357,6 +385,23 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer)
}
}
+ /* Apply gain */
+ /* Would be better off leaving this to a volume element, as this is
+ a naive conversion that does too many int/float conversions.
+ However, we don't have control over the pipeline...
+ So make it optional if the user program wants to use a volume,
+ but do it by default so the correct volume goes out by default */
+ if (dec->apply_gain && outbuf && dec->r128_gain) {
+ unsigned int i, nsamples = GST_BUFFER_SIZE (outbuf) / 2;
+ double volume = dec->r128_gain_volume;
+ gint16 *samples = (gint16 *) GST_BUFFER_DATA (outbuf);
+ GST_DEBUG_OBJECT (dec, "Applying gain: volume %f", volume);
+ for (i = 0; i < nsamples; ++i) {
+ int sample = (int) (samples[i] * volume + 0.5);
+ samples[i] = sample < -32768 ? -32768 : sample > 32767 ? 32767 : sample;
+ }
+ }
+
res = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (dec), outbuf, 1);
if (res != GST_FLOW_OK)
@@ -505,6 +550,9 @@ gst_opus_dec_get_property (GObject * object, guint prop_id, GValue * value,
case PROP_USE_INBAND_FEC:
g_value_set_boolean (value, dec->use_inband_fec);
break;
+ case PROP_APPLY_GAIN:
+ g_value_set_boolean (value, dec->apply_gain);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -521,6 +569,9 @@ gst_opus_dec_set_property (GObject * object, guint prop_id,
case PROP_USE_INBAND_FEC:
dec->use_inband_fec = g_value_get_boolean (value);
break;
+ case PROP_APPLY_GAIN:
+ dec->apply_gain = g_value_get_boolean (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;