mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-19 14:56:36 +00:00
volume: If a controller is used, use sample accurate property values
Fixes bug #609801.
This commit is contained in:
parent
ed3e1ab8b2
commit
5d0957525a
2 changed files with 255 additions and 31 deletions
|
@ -4,6 +4,7 @@
|
||||||
* GStreamer
|
* GStreamer
|
||||||
* Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
|
* Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
* Copyright (C) 2005 Andy Wingo <wingo@pobox.com>
|
* Copyright (C) 2005 Andy Wingo <wingo@pobox.com>
|
||||||
|
* Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Library General Public
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
@ -171,29 +172,42 @@ static void volume_before_transform (GstBaseTransform * base,
|
||||||
GstBuffer * buffer);
|
GstBuffer * buffer);
|
||||||
static GstFlowReturn volume_transform_ip (GstBaseTransform * base,
|
static GstFlowReturn volume_transform_ip (GstBaseTransform * base,
|
||||||
GstBuffer * outbuf);
|
GstBuffer * outbuf);
|
||||||
|
static gboolean volume_stop (GstBaseTransform * base);
|
||||||
static gboolean volume_setup (GstAudioFilter * filter,
|
static gboolean volume_setup (GstAudioFilter * filter,
|
||||||
GstRingBufferSpec * format);
|
GstRingBufferSpec * format);
|
||||||
|
|
||||||
static void volume_process_double (GstVolume * self, gpointer bytes,
|
static void volume_process_double (GstVolume * self, gpointer bytes,
|
||||||
guint n_bytes);
|
guint n_bytes);
|
||||||
|
static void volume_process_controlled_double (GstVolume * self, gpointer bytes,
|
||||||
|
gdouble * volume, guint channels, guint n_bytes);
|
||||||
static void volume_process_float (GstVolume * self, gpointer bytes,
|
static void volume_process_float (GstVolume * self, gpointer bytes,
|
||||||
guint n_bytes);
|
guint n_bytes);
|
||||||
|
static void volume_process_controlled_float (GstVolume * self, gpointer bytes,
|
||||||
|
gdouble * volume, guint channels, guint n_bytes);
|
||||||
static void volume_process_int32 (GstVolume * self, gpointer bytes,
|
static void volume_process_int32 (GstVolume * self, gpointer bytes,
|
||||||
guint n_bytes);
|
guint n_bytes);
|
||||||
static void volume_process_int32_clamp (GstVolume * self, gpointer bytes,
|
static void volume_process_int32_clamp (GstVolume * self, gpointer bytes,
|
||||||
guint n_bytes);
|
guint n_bytes);
|
||||||
|
static void volume_process_controlled_int32_clamp (GstVolume * self,
|
||||||
|
gpointer bytes, gdouble * volume, guint channels, guint n_bytes);
|
||||||
static void volume_process_int24 (GstVolume * self, gpointer bytes,
|
static void volume_process_int24 (GstVolume * self, gpointer bytes,
|
||||||
guint n_bytes);
|
guint n_bytes);
|
||||||
static void volume_process_int24_clamp (GstVolume * self, gpointer bytes,
|
static void volume_process_int24_clamp (GstVolume * self, gpointer bytes,
|
||||||
guint n_bytes);
|
guint n_bytes);
|
||||||
|
static void volume_process_controlled_int24_clamp (GstVolume * self,
|
||||||
|
gpointer bytes, gdouble * volume, guint channels, guint n_bytes);
|
||||||
static void volume_process_int16 (GstVolume * self, gpointer bytes,
|
static void volume_process_int16 (GstVolume * self, gpointer bytes,
|
||||||
guint n_bytes);
|
guint n_bytes);
|
||||||
static void volume_process_int16_clamp (GstVolume * self, gpointer bytes,
|
static void volume_process_int16_clamp (GstVolume * self, gpointer bytes,
|
||||||
guint n_bytes);
|
guint n_bytes);
|
||||||
|
static void volume_process_controlled_int16_clamp (GstVolume * self,
|
||||||
|
gpointer bytes, gdouble * volume, guint channels, guint n_bytes);
|
||||||
static void volume_process_int8 (GstVolume * self, gpointer bytes,
|
static void volume_process_int8 (GstVolume * self, gpointer bytes,
|
||||||
guint n_bytes);
|
guint n_bytes);
|
||||||
static void volume_process_int8_clamp (GstVolume * self, gpointer bytes,
|
static void volume_process_int8_clamp (GstVolume * self, gpointer bytes,
|
||||||
guint n_bytes);
|
guint n_bytes);
|
||||||
|
static void volume_process_controlled_int8_clamp (GstVolume * self,
|
||||||
|
gpointer bytes, gdouble * volume, guint channels, guint n_bytes);
|
||||||
|
|
||||||
|
|
||||||
/* helper functions */
|
/* helper functions */
|
||||||
|
@ -202,6 +216,7 @@ static gboolean
|
||||||
volume_choose_func (GstVolume * self)
|
volume_choose_func (GstVolume * self)
|
||||||
{
|
{
|
||||||
self->process = NULL;
|
self->process = NULL;
|
||||||
|
self->process_controlled = NULL;
|
||||||
|
|
||||||
if (GST_AUDIO_FILTER (self)->format.caps == NULL)
|
if (GST_AUDIO_FILTER (self)->format.caps == NULL)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -211,39 +226,43 @@ volume_choose_func (GstVolume * self)
|
||||||
switch (GST_AUDIO_FILTER (self)->format.width) {
|
switch (GST_AUDIO_FILTER (self)->format.width) {
|
||||||
case 32:
|
case 32:
|
||||||
/* only clamp if the gain is greater than 1.0
|
/* only clamp if the gain is greater than 1.0
|
||||||
* FIXME: current_vol_i can change while processing the buffer!
|
|
||||||
*/
|
*/
|
||||||
if (self->current_vol_i32 > VOLUME_UNITY_INT32)
|
if (self->current_vol_i32 > VOLUME_UNITY_INT32) {
|
||||||
self->process = volume_process_int32_clamp;
|
self->process = volume_process_int32_clamp;
|
||||||
else
|
} else {
|
||||||
self->process = volume_process_int32;
|
self->process = volume_process_int32;
|
||||||
|
}
|
||||||
|
self->process_controlled = volume_process_controlled_int32_clamp;
|
||||||
break;
|
break;
|
||||||
case 24:
|
case 24:
|
||||||
/* only clamp if the gain is greater than 1.0
|
/* only clamp if the gain is greater than 1.0
|
||||||
* FIXME: current_vol_i can change while processing the buffer!
|
|
||||||
*/
|
*/
|
||||||
if (self->current_vol_i24 > VOLUME_UNITY_INT24)
|
if (self->current_vol_i24 > VOLUME_UNITY_INT24) {
|
||||||
self->process = volume_process_int24_clamp;
|
self->process = volume_process_int24_clamp;
|
||||||
else
|
} else {
|
||||||
self->process = volume_process_int24;
|
self->process = volume_process_int24;
|
||||||
|
}
|
||||||
|
self->process_controlled = volume_process_controlled_int24_clamp;
|
||||||
break;
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
/* only clamp if the gain is greater than 1.0
|
/* only clamp if the gain is greater than 1.0
|
||||||
* FIXME: current_vol_i can change while processing the buffer!
|
|
||||||
*/
|
*/
|
||||||
if (self->current_vol_i16 > VOLUME_UNITY_INT16)
|
if (self->current_vol_i16 > VOLUME_UNITY_INT16) {
|
||||||
self->process = volume_process_int16_clamp;
|
self->process = volume_process_int16_clamp;
|
||||||
else
|
} else {
|
||||||
self->process = volume_process_int16;
|
self->process = volume_process_int16;
|
||||||
|
}
|
||||||
|
self->process_controlled = volume_process_controlled_int16_clamp;
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
/* only clamp if the gain is greater than 1.0
|
/* only clamp if the gain is greater than 1.0
|
||||||
* FIXME: current_vol_i can change while processing the buffer!
|
|
||||||
*/
|
*/
|
||||||
if (self->current_vol_i16 > VOLUME_UNITY_INT8)
|
if (self->current_vol_i16 > VOLUME_UNITY_INT8) {
|
||||||
self->process = volume_process_int8_clamp;
|
self->process = volume_process_int8_clamp;
|
||||||
else
|
} else {
|
||||||
self->process = volume_process_int8;
|
self->process = volume_process_int8;
|
||||||
|
}
|
||||||
|
self->process_controlled = volume_process_controlled_int8_clamp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -251,9 +270,11 @@ volume_choose_func (GstVolume * self)
|
||||||
switch (GST_AUDIO_FILTER (self)->format.width) {
|
switch (GST_AUDIO_FILTER (self)->format.width) {
|
||||||
case 32:
|
case 32:
|
||||||
self->process = volume_process_float;
|
self->process = volume_process_float;
|
||||||
|
self->process_controlled = volume_process_controlled_float;
|
||||||
break;
|
break;
|
||||||
case 64:
|
case 64:
|
||||||
self->process = volume_process_double;
|
self->process = volume_process_double;
|
||||||
|
self->process_controlled = volume_process_controlled_double;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -269,6 +290,7 @@ volume_update_volume (GstVolume * self, gfloat volume, gboolean mute)
|
||||||
{
|
{
|
||||||
gboolean passthrough;
|
gboolean passthrough;
|
||||||
gboolean res;
|
gboolean res;
|
||||||
|
GstController *controller;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (self, "configure mute %d, volume %f", mute, volume);
|
GST_DEBUG_OBJECT (self, "configure mute %d, volume %f", mute, volume);
|
||||||
|
|
||||||
|
@ -294,6 +316,13 @@ volume_update_volume (GstVolume * self, gfloat volume, gboolean mute)
|
||||||
passthrough = (self->current_vol_i16 == VOLUME_UNITY_INT16);
|
passthrough = (self->current_vol_i16 == VOLUME_UNITY_INT16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If a controller is used, never use passthrough mode
|
||||||
|
* because the property can change from 1.0 to something
|
||||||
|
* else in the middle of a buffer.
|
||||||
|
*/
|
||||||
|
controller = gst_object_get_controller (G_OBJECT (self));
|
||||||
|
passthrough = passthrough && (controller == NULL);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (self, "set passthrough %d", passthrough);
|
GST_DEBUG_OBJECT (self, "set passthrough %d", passthrough);
|
||||||
|
|
||||||
gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (self), passthrough);
|
gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (self), passthrough);
|
||||||
|
@ -439,6 +468,7 @@ gst_volume_class_init (GstVolumeClass * klass)
|
||||||
|
|
||||||
trans_class->before_transform = GST_DEBUG_FUNCPTR (volume_before_transform);
|
trans_class->before_transform = GST_DEBUG_FUNCPTR (volume_before_transform);
|
||||||
trans_class->transform_ip = GST_DEBUG_FUNCPTR (volume_transform_ip);
|
trans_class->transform_ip = GST_DEBUG_FUNCPTR (volume_transform_ip);
|
||||||
|
trans_class->stop = GST_DEBUG_FUNCPTR (volume_stop);
|
||||||
filter_class->setup = GST_DEBUG_FUNCPTR (volume_setup);
|
filter_class->setup = GST_DEBUG_FUNCPTR (volume_setup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,6 +508,23 @@ volume_process_double (GstVolume * self, gpointer bytes, guint n_bytes)
|
||||||
oil_scalarmultiply_f64_ns (data, data, &vol, num_samples);
|
oil_scalarmultiply_f64_ns (data, data, &vol, num_samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
volume_process_controlled_double (GstVolume * self, gpointer bytes,
|
||||||
|
gdouble * volume, guint channels, guint n_bytes)
|
||||||
|
{
|
||||||
|
gdouble *data = (gdouble *) bytes;
|
||||||
|
guint num_samples = n_bytes / (sizeof (gdouble) * channels);
|
||||||
|
guint i, j;
|
||||||
|
gdouble vol;
|
||||||
|
|
||||||
|
for (i = 0; i < num_samples; i++) {
|
||||||
|
vol = *volume++;
|
||||||
|
for (j = 0; j < channels; j++) {
|
||||||
|
*data++ *= vol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
volume_process_float (GstVolume * self, gpointer bytes, guint n_bytes)
|
volume_process_float (GstVolume * self, gpointer bytes, guint n_bytes)
|
||||||
{
|
{
|
||||||
|
@ -497,10 +544,27 @@ volume_process_float (GstVolume * self, gpointer bytes, guint n_bytes)
|
||||||
oil_scalarmultiply_f32_ns (data, data, &self->current_volume, num_samples);
|
oil_scalarmultiply_f32_ns (data, data, &self->current_volume, num_samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
volume_process_controlled_float (GstVolume * self, gpointer bytes,
|
||||||
|
gdouble * volume, guint channels, guint n_bytes)
|
||||||
|
{
|
||||||
|
gfloat *data = (gfloat *) bytes;
|
||||||
|
guint num_samples = n_bytes / (sizeof (gfloat) * channels);
|
||||||
|
guint i, j;
|
||||||
|
gdouble vol;
|
||||||
|
|
||||||
|
for (i = 0; i < num_samples; i++) {
|
||||||
|
vol = *volume++;
|
||||||
|
for (j = 0; j < channels; j++) {
|
||||||
|
*data++ *= vol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
volume_process_int32 (GstVolume * self, gpointer bytes, guint n_bytes)
|
volume_process_int32 (GstVolume * self, gpointer bytes, guint n_bytes)
|
||||||
{
|
{
|
||||||
gint *data = (gint *) bytes;
|
gint32 *data = (gint32 *) bytes;
|
||||||
guint i, num_samples;
|
guint i, num_samples;
|
||||||
gint64 val;
|
gint64 val;
|
||||||
|
|
||||||
|
@ -518,11 +582,11 @@ volume_process_int32 (GstVolume * self, gpointer bytes, guint n_bytes)
|
||||||
static void
|
static void
|
||||||
volume_process_int32_clamp (GstVolume * self, gpointer bytes, guint n_bytes)
|
volume_process_int32_clamp (GstVolume * self, gpointer bytes, guint n_bytes)
|
||||||
{
|
{
|
||||||
gint *data = (gint *) bytes;
|
gint32 *data = (gint32 *) bytes;
|
||||||
guint i, num_samples;
|
guint i, num_samples;
|
||||||
gint64 val;
|
gint64 val;
|
||||||
|
|
||||||
num_samples = n_bytes / sizeof (gint);
|
num_samples = n_bytes / sizeof (gint32);
|
||||||
|
|
||||||
for (i = 0; i < num_samples; i++) {
|
for (i = 0; i < num_samples; i++) {
|
||||||
/* we use bitshifting instead of dividing by UNITY_INT for speed */
|
/* we use bitshifting instead of dividing by UNITY_INT for speed */
|
||||||
|
@ -534,6 +598,24 @@ volume_process_int32_clamp (GstVolume * self, gpointer bytes, guint n_bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
volume_process_controlled_int32_clamp (GstVolume * self, gpointer bytes,
|
||||||
|
gdouble * volume, guint channels, guint n_bytes)
|
||||||
|
{
|
||||||
|
gint32 *data = (gint32 *) bytes;
|
||||||
|
guint i, j;
|
||||||
|
guint num_samples = n_bytes / (sizeof (gint32) * channels);
|
||||||
|
gdouble vol, val;
|
||||||
|
|
||||||
|
for (i = 0; i < num_samples; i++) {
|
||||||
|
vol = *volume++;
|
||||||
|
for (j = 0; j < channels; j++) {
|
||||||
|
val = *data * vol + 0.5;
|
||||||
|
*data++ = (gint32) CLAMP (val, VOLUME_MIN_INT32, VOLUME_MAX_INT32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
|
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
|
||||||
#define get_unaligned_i24(_x) ( (((guint8*)_x)[0]) | ((((guint8*)_x)[1]) << 8) | ((((gint8*)_x)[2]) << 16) )
|
#define get_unaligned_i24(_x) ( (((guint8*)_x)[0]) | ((((guint8*)_x)[1]) << 8) | ((((gint8*)_x)[2]) << 16) )
|
||||||
|
|
||||||
|
@ -600,13 +682,31 @@ volume_process_int24_clamp (GstVolume * self, gpointer bytes, guint n_bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
volume_process_controlled_int24_clamp (GstVolume * self, gpointer bytes,
|
||||||
|
gdouble * volume, guint channels, guint n_bytes)
|
||||||
|
{
|
||||||
|
gint8 *data = (gint8 *) bytes; /* treat the data as a byte stream */
|
||||||
|
guint i, j;
|
||||||
|
guint num_samples = n_bytes / (sizeof (gint8) * 3 * channels);
|
||||||
|
gdouble vol, val;
|
||||||
|
|
||||||
|
for (i = 0; i < num_samples; i++) {
|
||||||
|
vol = *volume++;
|
||||||
|
for (j = 0; j < channels; j++) {
|
||||||
|
val = get_unaligned_i24 (data) * vol + 0.5;
|
||||||
|
val = CLAMP (val, VOLUME_MIN_INT24, VOLUME_MAX_INT24);
|
||||||
|
write_unaligned_u24 (data, (gint32) val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
volume_process_int16 (GstVolume * self, gpointer bytes, guint n_bytes)
|
volume_process_int16 (GstVolume * self, gpointer bytes, guint n_bytes)
|
||||||
{
|
{
|
||||||
gint16 *data = (gint16 *) bytes;
|
gint16 *data = (gint16 *) bytes;
|
||||||
guint num_samples = n_bytes / sizeof (gint16);
|
guint num_samples = n_bytes / sizeof (gint16);
|
||||||
|
|
||||||
#if 1
|
|
||||||
guint i;
|
guint i;
|
||||||
gint val;
|
gint val;
|
||||||
|
|
||||||
|
@ -617,17 +717,6 @@ volume_process_int16 (GstVolume * self, gpointer bytes, guint n_bytes)
|
||||||
(gint16) ((self->current_vol_i16 *
|
(gint16) ((self->current_vol_i16 *
|
||||||
val) >> VOLUME_UNITY_INT16_BIT_SHIFT);
|
val) >> VOLUME_UNITY_INT16_BIT_SHIFT);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
/* FIXME: need oil_scalarmultiply_s16_ns ?
|
|
||||||
* https://bugs.freedesktop.org/show_bug.cgi?id=7060
|
|
||||||
* code below
|
|
||||||
* - crashes :/
|
|
||||||
* - real_vol_i is scaled by VOLUME_UNITY_INT16 and needs the bitshift
|
|
||||||
* time gst-launch 2>/dev/null audiotestsrc wave=7 num-buffers=100 ! volume volume=1.5 ! fakesink
|
|
||||||
*/
|
|
||||||
oil_scalarmult_s16 (data, 0, data, 0,
|
|
||||||
((gint16 *) (void *) (&self->current_vol_i)), num_samples);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -652,6 +741,24 @@ volume_process_int16_clamp (GstVolume * self, gpointer bytes, guint n_bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
volume_process_controlled_int16_clamp (GstVolume * self, gpointer bytes,
|
||||||
|
gdouble * volume, guint channels, guint n_bytes)
|
||||||
|
{
|
||||||
|
gint16 *data = (gint16 *) bytes;
|
||||||
|
guint i, j;
|
||||||
|
guint num_samples = n_bytes / (sizeof (gint16) * channels);
|
||||||
|
gdouble vol, val;
|
||||||
|
|
||||||
|
for (i = 0; i < num_samples; i++) {
|
||||||
|
vol = *volume++;
|
||||||
|
for (j = 0; j < channels; j++) {
|
||||||
|
val = *data * vol + 0.5;
|
||||||
|
*data++ = (gint16) CLAMP (val, VOLUME_MIN_INT16, VOLUME_MAX_INT16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
volume_process_int8 (GstVolume * self, gpointer bytes, guint n_bytes)
|
volume_process_int8 (GstVolume * self, gpointer bytes, guint n_bytes)
|
||||||
{
|
{
|
||||||
|
@ -687,6 +794,24 @@ volume_process_int8_clamp (GstVolume * self, gpointer bytes, guint n_bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
volume_process_controlled_int8_clamp (GstVolume * self, gpointer bytes,
|
||||||
|
gdouble * volume, guint channels, guint n_bytes)
|
||||||
|
{
|
||||||
|
gint8 *data = (gint8 *) bytes;
|
||||||
|
guint i, j;
|
||||||
|
guint num_samples = n_bytes / (sizeof (gint8) * channels);
|
||||||
|
gdouble val, vol;
|
||||||
|
|
||||||
|
for (i = 0; i < num_samples; i++) {
|
||||||
|
vol = *volume++;
|
||||||
|
for (j = 0; j < channels; j++) {
|
||||||
|
val = *data * vol + 0.5;
|
||||||
|
*data++ = (gint8) CLAMP (val, VOLUME_MIN_INT8, VOLUME_MAX_INT8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* GstBaseTransform vmethod implementations */
|
/* GstBaseTransform vmethod implementations */
|
||||||
|
|
||||||
/* get notified of caps and plug in the correct process function */
|
/* get notified of caps and plug in the correct process function */
|
||||||
|
@ -713,6 +838,23 @@ volume_setup (GstAudioFilter * filter, GstRingBufferSpec * format)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
volume_stop (GstBaseTransform * base)
|
||||||
|
{
|
||||||
|
GstVolume *self = GST_VOLUME (base);
|
||||||
|
|
||||||
|
g_free (self->volumes);
|
||||||
|
self->volumes = NULL;
|
||||||
|
self->volumes_count = 0;
|
||||||
|
|
||||||
|
g_free (self->mutes);
|
||||||
|
self->mutes = NULL;
|
||||||
|
self->mutes_count = 0;
|
||||||
|
|
||||||
|
return GST_CALL_PARENT_WITH_DEFAULT (GST_BASE_TRANSFORM_CLASS, stop, (base),
|
||||||
|
TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
volume_before_transform (GstBaseTransform * base, GstBuffer * buffer)
|
volume_before_transform (GstBaseTransform * base, GstBuffer * buffer)
|
||||||
{
|
{
|
||||||
|
@ -721,9 +863,6 @@ volume_before_transform (GstBaseTransform * base, GstBuffer * buffer)
|
||||||
gfloat volume;
|
gfloat volume;
|
||||||
gboolean mute;
|
gboolean mute;
|
||||||
|
|
||||||
/* FIXME: if controllers are bound, subdivide GST_BUFFER_SIZE into small
|
|
||||||
* chunks for smooth fades, what is small? 1/10th sec.
|
|
||||||
*/
|
|
||||||
timestamp = GST_BUFFER_TIMESTAMP (buffer);
|
timestamp = GST_BUFFER_TIMESTAMP (buffer);
|
||||||
timestamp =
|
timestamp =
|
||||||
gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
|
gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
|
||||||
|
@ -757,6 +896,7 @@ volume_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
|
||||||
GstVolume *self = GST_VOLUME (base);
|
GstVolume *self = GST_VOLUME (base);
|
||||||
guint8 *data;
|
guint8 *data;
|
||||||
guint size;
|
guint size;
|
||||||
|
GstControlSource *mute_csource, *volume_csource;
|
||||||
|
|
||||||
if (G_UNLIKELY (!self->negotiated))
|
if (G_UNLIKELY (!self->negotiated))
|
||||||
goto not_negotiated;
|
goto not_negotiated;
|
||||||
|
@ -769,7 +909,73 @@ volume_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
|
||||||
data = GST_BUFFER_DATA (outbuf);
|
data = GST_BUFFER_DATA (outbuf);
|
||||||
size = GST_BUFFER_SIZE (outbuf);
|
size = GST_BUFFER_SIZE (outbuf);
|
||||||
|
|
||||||
if (self->current_volume == 0.0) {
|
mute_csource = gst_object_get_control_source (G_OBJECT (self), "mute");
|
||||||
|
volume_csource = gst_object_get_control_source (G_OBJECT (self), "volume");
|
||||||
|
if (mute_csource || (volume_csource && !self->current_mute)) {
|
||||||
|
gint rate = GST_AUDIO_FILTER_CAST (self)->format.rate;
|
||||||
|
gint width = GST_AUDIO_FILTER_CAST (self)->format.width / 8;
|
||||||
|
gint channels = GST_AUDIO_FILTER_CAST (self)->format.channels;
|
||||||
|
guint nsamples = size / (width * channels);
|
||||||
|
GstClockTime interval = gst_util_uint64_scale_int (1, GST_SECOND, rate);
|
||||||
|
GstClockTime ts = GST_BUFFER_TIMESTAMP (outbuf);
|
||||||
|
|
||||||
|
ts = gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, ts);
|
||||||
|
|
||||||
|
if (self->mutes_count < nsamples) {
|
||||||
|
self->mutes = g_realloc (self->mutes, sizeof (gboolean) * nsamples);
|
||||||
|
self->mutes_count = nsamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->volumes_count < nsamples) {
|
||||||
|
self->volumes = g_realloc (self->volumes, sizeof (gdouble) * nsamples);
|
||||||
|
self->volumes_count = nsamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mute_csource) {
|
||||||
|
GstValueArray va = { "mute", nsamples, interval, (gpointer) self->mutes };
|
||||||
|
|
||||||
|
if (!gst_control_source_get_value_array (mute_csource, ts, &va))
|
||||||
|
goto controller_failure;
|
||||||
|
|
||||||
|
gst_object_unref (mute_csource);
|
||||||
|
mute_csource = NULL;
|
||||||
|
} else {
|
||||||
|
g_free (self->mutes);
|
||||||
|
self->mutes = NULL;
|
||||||
|
self->mutes_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (volume_csource) {
|
||||||
|
GstValueArray va =
|
||||||
|
{ "volume", nsamples, interval, (gpointer) self->volumes };
|
||||||
|
|
||||||
|
if (!gst_control_source_get_value_array (volume_csource, ts, &va))
|
||||||
|
goto controller_failure;
|
||||||
|
|
||||||
|
gst_object_unref (volume_csource);
|
||||||
|
volume_csource = NULL;
|
||||||
|
} else {
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
for (i = 0; i < nsamples; i++)
|
||||||
|
self->volumes[i] = self->current_volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mute_csource) {
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
for (i = 0; i < nsamples; i++)
|
||||||
|
self->volumes[i] *= (1.0 - self->mutes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->process_controlled (self, data, self->volumes, channels, size);
|
||||||
|
|
||||||
|
return GST_FLOW_OK;
|
||||||
|
} else if (volume_csource) {
|
||||||
|
gst_object_unref (volume_csource);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->current_volume == 0.0 || self->current_mute) {
|
||||||
memset (data, 0, size);
|
memset (data, 0, size);
|
||||||
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
|
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
|
||||||
} else if (self->current_volume != 1.0) {
|
} else if (self->current_volume != 1.0) {
|
||||||
|
@ -785,6 +991,17 @@ not_negotiated:
|
||||||
("No format was negotiated"), (NULL));
|
("No format was negotiated"), (NULL));
|
||||||
return GST_FLOW_NOT_NEGOTIATED;
|
return GST_FLOW_NOT_NEGOTIATED;
|
||||||
}
|
}
|
||||||
|
controller_failure:
|
||||||
|
{
|
||||||
|
if (mute_csource)
|
||||||
|
gst_object_unref (mute_csource);
|
||||||
|
if (volume_csource)
|
||||||
|
gst_object_unref (volume_csource);
|
||||||
|
|
||||||
|
GST_ELEMENT_ERROR (self, CORE, FAILED,
|
||||||
|
("Failed to get values from controller"), (NULL));
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* GStreamer
|
* GStreamer
|
||||||
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
|
* Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Library General Public
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
@ -54,6 +55,7 @@ struct _GstVolume {
|
||||||
GstAudioFilter element;
|
GstAudioFilter element;
|
||||||
|
|
||||||
void (*process)(GstVolume*, gpointer, guint);
|
void (*process)(GstVolume*, gpointer, guint);
|
||||||
|
void (*process_controlled)(GstVolume*, gpointer, gdouble *, guint, guint);
|
||||||
|
|
||||||
gboolean mute;
|
gboolean mute;
|
||||||
gfloat volume;
|
gfloat volume;
|
||||||
|
@ -68,6 +70,11 @@ struct _GstVolume {
|
||||||
|
|
||||||
GList *tracklist;
|
GList *tracklist;
|
||||||
gboolean negotiated;
|
gboolean negotiated;
|
||||||
|
|
||||||
|
gboolean *mutes;
|
||||||
|
guint mutes_count;
|
||||||
|
gdouble *volumes;
|
||||||
|
guint volumes_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstVolumeClass {
|
struct _GstVolumeClass {
|
||||||
|
|
Loading…
Reference in a new issue