gstreamer/gst/volume/gstvolume.c

1107 lines
32 KiB
C
Raw Normal View History

/* -*- c-basic-offset: 2 -*-
* vi:si:et:sw=2:sts=8:ts=8:expandtab
*
* GStreamer
* Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
* 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
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:element-volume
*
* The volume element changes the volume of the audio data.
Cleanup Plugin docs. Link to signals and properties. Fix sub-section titles. Drop mentining that all our example pipe... Original commit message from CVS: * docs/plugins/gst-plugins-base-plugins-docs.sgml: * docs/plugins/gst-plugins-base-plugins-overrides.txt: * docs/plugins/gst-plugins-base-plugins-sections.txt: * docs/plugins/gst-plugins-base-plugins.args: * docs/plugins/gst-plugins-base-plugins.hierarchy: * docs/plugins/gst-plugins-base-plugins.interfaces: * docs/plugins/gst-plugins-base-plugins.prerequisites: * docs/plugins/gst-plugins-base-plugins.signals: * docs/plugins/inspect/plugin-adder.xml: * docs/plugins/inspect/plugin-alsa.xml: * docs/plugins/inspect/plugin-audioconvert.xml: * docs/plugins/inspect/plugin-audiorate.xml: * docs/plugins/inspect/plugin-audioresample.xml: * docs/plugins/inspect/plugin-audiotestsrc.xml: * docs/plugins/inspect/plugin-cdparanoia.xml: * docs/plugins/inspect/plugin-decodebin.xml: * docs/plugins/inspect/plugin-ffmpegcolorspace.xml: * docs/plugins/inspect/plugin-gdp.xml: * docs/plugins/inspect/plugin-gnomevfs.xml: * docs/plugins/inspect/plugin-libvisual.xml: * docs/plugins/inspect/plugin-ogg.xml: * docs/plugins/inspect/plugin-pango.xml: * docs/plugins/inspect/plugin-playback.xml: * docs/plugins/inspect/plugin-queue2.xml: * docs/plugins/inspect/plugin-subparse.xml: * docs/plugins/inspect/plugin-tcp.xml: * docs/plugins/inspect/plugin-theora.xml: * docs/plugins/inspect/plugin-typefindfunctions.xml: * docs/plugins/inspect/plugin-uridecodebin.xml: * docs/plugins/inspect/plugin-video4linux.xml: * docs/plugins/inspect/plugin-videorate.xml: * docs/plugins/inspect/plugin-videoscale.xml: * docs/plugins/inspect/plugin-videotestsrc.xml: * docs/plugins/inspect/plugin-volume.xml: * docs/plugins/inspect/plugin-vorbis.xml: * docs/plugins/inspect/plugin-ximagesink.xml: * docs/plugins/inspect/plugin-xvimagesink.xml: * ext/alsa/gstalsamixer.c: * ext/alsa/gstalsasink.c: * ext/alsa/gstalsasrc.c: * ext/gio/gstgiosink.c: * ext/gio/gstgiosrc.c: * ext/gio/gstgiostreamsink.c: * ext/gio/gstgiostreamsrc.c: * ext/gnomevfs/gstgnomevfssink.c: * ext/gnomevfs/gstgnomevfssrc.c: * ext/ogg/gstoggdemux.c: * ext/ogg/gstoggmux.c: * ext/pango/gstclockoverlay.c: * ext/pango/gsttextoverlay.c: * ext/pango/gsttextrender.c: * ext/pango/gsttimeoverlay.c: * ext/theora/theoradec.c: * ext/theora/theoraenc.c: * ext/theora/theoraparse.c: * ext/vorbis/vorbisdec.c: * ext/vorbis/vorbisenc.c: * ext/vorbis/vorbisparse.c: * ext/vorbis/vorbistag.c: * gst/adder/gstadder.c: * gst/audioconvert/gstaudioconvert.c: * gst/audioresample/gstaudioresample.c: * gst/audiotestsrc/gstaudiotestsrc.c: * gst/ffmpegcolorspace/gstffmpegcolorspace.c: * gst/gdp/gstgdpdepay.c: * gst/gdp/gstgdppay.c: * gst/playback/gstdecodebin2.c: * gst/playback/gstplaybin.c: * gst/playback/gstplaybin2.c: * gst/playback/gstqueue2.c: * gst/playback/gsturidecodebin.c: * gst/tcp/gstmultifdsink.c: * gst/tcp/gsttcpserversink.c: * gst/videorate/gstvideorate.c: * gst/videoscale/gstvideoscale.c: * gst/videotestsrc/gstvideotestsrc.c: * gst/volume/gstvolume.c: * sys/ximage/ximagesink.c: * sys/xvimage/xvimagesink.c: Cleanup Plugin docs. Link to signals and properties. Fix sub-section titles. Drop mentining that all our example pipelines are "simple" pipelines.
2008-07-11 06:10:24 +00:00
*
* <refsect2>
* <title>Example launch line</title>
Cleanup Plugin docs. Link to signals and properties. Fix sub-section titles. Drop mentining that all our example pipe... Original commit message from CVS: * docs/plugins/gst-plugins-base-plugins-docs.sgml: * docs/plugins/gst-plugins-base-plugins-overrides.txt: * docs/plugins/gst-plugins-base-plugins-sections.txt: * docs/plugins/gst-plugins-base-plugins.args: * docs/plugins/gst-plugins-base-plugins.hierarchy: * docs/plugins/gst-plugins-base-plugins.interfaces: * docs/plugins/gst-plugins-base-plugins.prerequisites: * docs/plugins/gst-plugins-base-plugins.signals: * docs/plugins/inspect/plugin-adder.xml: * docs/plugins/inspect/plugin-alsa.xml: * docs/plugins/inspect/plugin-audioconvert.xml: * docs/plugins/inspect/plugin-audiorate.xml: * docs/plugins/inspect/plugin-audioresample.xml: * docs/plugins/inspect/plugin-audiotestsrc.xml: * docs/plugins/inspect/plugin-cdparanoia.xml: * docs/plugins/inspect/plugin-decodebin.xml: * docs/plugins/inspect/plugin-ffmpegcolorspace.xml: * docs/plugins/inspect/plugin-gdp.xml: * docs/plugins/inspect/plugin-gnomevfs.xml: * docs/plugins/inspect/plugin-libvisual.xml: * docs/plugins/inspect/plugin-ogg.xml: * docs/plugins/inspect/plugin-pango.xml: * docs/plugins/inspect/plugin-playback.xml: * docs/plugins/inspect/plugin-queue2.xml: * docs/plugins/inspect/plugin-subparse.xml: * docs/plugins/inspect/plugin-tcp.xml: * docs/plugins/inspect/plugin-theora.xml: * docs/plugins/inspect/plugin-typefindfunctions.xml: * docs/plugins/inspect/plugin-uridecodebin.xml: * docs/plugins/inspect/plugin-video4linux.xml: * docs/plugins/inspect/plugin-videorate.xml: * docs/plugins/inspect/plugin-videoscale.xml: * docs/plugins/inspect/plugin-videotestsrc.xml: * docs/plugins/inspect/plugin-volume.xml: * docs/plugins/inspect/plugin-vorbis.xml: * docs/plugins/inspect/plugin-ximagesink.xml: * docs/plugins/inspect/plugin-xvimagesink.xml: * ext/alsa/gstalsamixer.c: * ext/alsa/gstalsasink.c: * ext/alsa/gstalsasrc.c: * ext/gio/gstgiosink.c: * ext/gio/gstgiosrc.c: * ext/gio/gstgiostreamsink.c: * ext/gio/gstgiostreamsrc.c: * ext/gnomevfs/gstgnomevfssink.c: * ext/gnomevfs/gstgnomevfssrc.c: * ext/ogg/gstoggdemux.c: * ext/ogg/gstoggmux.c: * ext/pango/gstclockoverlay.c: * ext/pango/gsttextoverlay.c: * ext/pango/gsttextrender.c: * ext/pango/gsttimeoverlay.c: * ext/theora/theoradec.c: * ext/theora/theoraenc.c: * ext/theora/theoraparse.c: * ext/vorbis/vorbisdec.c: * ext/vorbis/vorbisenc.c: * ext/vorbis/vorbisparse.c: * ext/vorbis/vorbistag.c: * gst/adder/gstadder.c: * gst/audioconvert/gstaudioconvert.c: * gst/audioresample/gstaudioresample.c: * gst/audiotestsrc/gstaudiotestsrc.c: * gst/ffmpegcolorspace/gstffmpegcolorspace.c: * gst/gdp/gstgdpdepay.c: * gst/gdp/gstgdppay.c: * gst/playback/gstdecodebin2.c: * gst/playback/gstplaybin.c: * gst/playback/gstplaybin2.c: * gst/playback/gstqueue2.c: * gst/playback/gsturidecodebin.c: * gst/tcp/gstmultifdsink.c: * gst/tcp/gsttcpserversink.c: * gst/videorate/gstvideorate.c: * gst/videoscale/gstvideoscale.c: * gst/videotestsrc/gstvideotestsrc.c: * gst/volume/gstvolume.c: * sys/ximage/ximagesink.c: * sys/xvimage/xvimagesink.c: Cleanup Plugin docs. Link to signals and properties. Fix sub-section titles. Drop mentining that all our example pipelines are "simple" pipelines.
2008-07-11 06:10:24 +00:00
* |[
* gst-launch -v -m audiotestsrc ! volume volume=0.5 ! level ! fakesink silent=TRUE
Cleanup Plugin docs. Link to signals and properties. Fix sub-section titles. Drop mentining that all our example pipe... Original commit message from CVS: * docs/plugins/gst-plugins-base-plugins-docs.sgml: * docs/plugins/gst-plugins-base-plugins-overrides.txt: * docs/plugins/gst-plugins-base-plugins-sections.txt: * docs/plugins/gst-plugins-base-plugins.args: * docs/plugins/gst-plugins-base-plugins.hierarchy: * docs/plugins/gst-plugins-base-plugins.interfaces: * docs/plugins/gst-plugins-base-plugins.prerequisites: * docs/plugins/gst-plugins-base-plugins.signals: * docs/plugins/inspect/plugin-adder.xml: * docs/plugins/inspect/plugin-alsa.xml: * docs/plugins/inspect/plugin-audioconvert.xml: * docs/plugins/inspect/plugin-audiorate.xml: * docs/plugins/inspect/plugin-audioresample.xml: * docs/plugins/inspect/plugin-audiotestsrc.xml: * docs/plugins/inspect/plugin-cdparanoia.xml: * docs/plugins/inspect/plugin-decodebin.xml: * docs/plugins/inspect/plugin-ffmpegcolorspace.xml: * docs/plugins/inspect/plugin-gdp.xml: * docs/plugins/inspect/plugin-gnomevfs.xml: * docs/plugins/inspect/plugin-libvisual.xml: * docs/plugins/inspect/plugin-ogg.xml: * docs/plugins/inspect/plugin-pango.xml: * docs/plugins/inspect/plugin-playback.xml: * docs/plugins/inspect/plugin-queue2.xml: * docs/plugins/inspect/plugin-subparse.xml: * docs/plugins/inspect/plugin-tcp.xml: * docs/plugins/inspect/plugin-theora.xml: * docs/plugins/inspect/plugin-typefindfunctions.xml: * docs/plugins/inspect/plugin-uridecodebin.xml: * docs/plugins/inspect/plugin-video4linux.xml: * docs/plugins/inspect/plugin-videorate.xml: * docs/plugins/inspect/plugin-videoscale.xml: * docs/plugins/inspect/plugin-videotestsrc.xml: * docs/plugins/inspect/plugin-volume.xml: * docs/plugins/inspect/plugin-vorbis.xml: * docs/plugins/inspect/plugin-ximagesink.xml: * docs/plugins/inspect/plugin-xvimagesink.xml: * ext/alsa/gstalsamixer.c: * ext/alsa/gstalsasink.c: * ext/alsa/gstalsasrc.c: * ext/gio/gstgiosink.c: * ext/gio/gstgiosrc.c: * ext/gio/gstgiostreamsink.c: * ext/gio/gstgiostreamsrc.c: * ext/gnomevfs/gstgnomevfssink.c: * ext/gnomevfs/gstgnomevfssrc.c: * ext/ogg/gstoggdemux.c: * ext/ogg/gstoggmux.c: * ext/pango/gstclockoverlay.c: * ext/pango/gsttextoverlay.c: * ext/pango/gsttextrender.c: * ext/pango/gsttimeoverlay.c: * ext/theora/theoradec.c: * ext/theora/theoraenc.c: * ext/theora/theoraparse.c: * ext/vorbis/vorbisdec.c: * ext/vorbis/vorbisenc.c: * ext/vorbis/vorbisparse.c: * ext/vorbis/vorbistag.c: * gst/adder/gstadder.c: * gst/audioconvert/gstaudioconvert.c: * gst/audioresample/gstaudioresample.c: * gst/audiotestsrc/gstaudiotestsrc.c: * gst/ffmpegcolorspace/gstffmpegcolorspace.c: * gst/gdp/gstgdpdepay.c: * gst/gdp/gstgdppay.c: * gst/playback/gstdecodebin2.c: * gst/playback/gstplaybin.c: * gst/playback/gstplaybin2.c: * gst/playback/gstqueue2.c: * gst/playback/gsturidecodebin.c: * gst/tcp/gstmultifdsink.c: * gst/tcp/gsttcpserversink.c: * gst/videorate/gstvideorate.c: * gst/videoscale/gstvideoscale.c: * gst/videotestsrc/gstvideotestsrc.c: * gst/volume/gstvolume.c: * sys/ximage/ximagesink.c: * sys/xvimage/xvimagesink.c: Cleanup Plugin docs. Link to signals and properties. Fix sub-section titles. Drop mentining that all our example pipelines are "simple" pipelines.
2008-07-11 06:10:24 +00:00
* ]| This pipeline shows that the level of audiotestsrc has been halved
* (peak values are around -6 dB and RMS around -9 dB) compared to
* the same pipeline without the volume element.
* </refsect2>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <gst/gst.h>
#include <gst/base/gstbasetransform.h>
#include <gst/audio/audio.h>
#include <gst/interfaces/mixer.h>
#include <gst/controller/gstcontroller.h>
#include <gst/audio/audio.h>
#include <gst/audio/gstaudiofilter.h>
2010-06-07 06:48:15 +00:00
#include "gstvolumeorc.h"
#include "gstvolume.h"
/* some defines for audio processing */
/* the volume factor is a range from 0.0 to (arbitrary) VOLUME_MAX_DOUBLE = 10.0
* we map 1.0 to VOLUME_UNITY_INT*
*/
#define VOLUME_UNITY_INT8 32 /* internal int for unity 2^(8-3) */
#define VOLUME_UNITY_INT8_BIT_SHIFT 5 /* number of bits to shift for unity */
#define VOLUME_UNITY_INT16 8192 /* internal int for unity 2^(16-3) */
#define VOLUME_UNITY_INT16_BIT_SHIFT 13 /* number of bits to shift for unity */
#define VOLUME_UNITY_INT24 2097152 /* internal int for unity 2^(24-3) */
#define VOLUME_UNITY_INT24_BIT_SHIFT 21 /* number of bits to shift for unity */
#define VOLUME_UNITY_INT32 134217728 /* internal int for unity 2^(32-5) */
#define VOLUME_UNITY_INT32_BIT_SHIFT 27
#define VOLUME_MAX_DOUBLE 10.0
#define VOLUME_MAX_INT8 G_MAXINT8
#define VOLUME_MIN_INT8 G_MININT8
#define VOLUME_MAX_INT16 G_MAXINT16
#define VOLUME_MIN_INT16 G_MININT16
#define VOLUME_MAX_INT24 8388607
#define VOLUME_MIN_INT24 -8388608
#define VOLUME_MAX_INT32 G_MAXINT32
#define VOLUME_MIN_INT32 G_MININT32
/* number of steps we use for the mixer interface to go from 0.0 to 1.0 */
# define VOLUME_STEPS 100
#define GST_CAT_DEFAULT gst_volume_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
/* Filter signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
#define DEFAULT_PROP_MUTE FALSE
#define DEFAULT_PROP_VOLUME 1.0
enum
{
PROP_0,
PROP_MUTE,
PROP_VOLUME
};
#define ALLOWED_CAPS \
"audio/x-raw-float, " \
"rate = (int) [ 1, MAX ], " \
"channels = (int) [ 1, MAX ], " \
"endianness = (int) BYTE_ORDER, " \
"width = (int) {32, 64}; " \
"audio/x-raw-int, " \
"channels = (int) [ 1, MAX ], " \
"rate = (int) [ 1, MAX ], " \
"endianness = (int) BYTE_ORDER, " \
"width = (int) 8, " \
"depth = (int) 8, " \
"signed = (bool) TRUE; " \
"audio/x-raw-int, " \
"channels = (int) [ 1, MAX ], " \
"rate = (int) [ 1, MAX ], " \
"endianness = (int) BYTE_ORDER, " \
"width = (int) 16, " \
"depth = (int) 16, " \
"signed = (bool) TRUE; " \
"audio/x-raw-int, " \
"channels = (int) [ 1, MAX ], " \
"rate = (int) [ 1, MAX ], " \
"endianness = (int) BYTE_ORDER, " \
"width = (int) 24, " \
"depth = (int) 24, " \
"signed = (bool) TRUE; " \
"audio/x-raw-int, " \
"channels = (int) [ 1, MAX ], " \
"rate = (int) [ 1, MAX ], " \
"endianness = (int) BYTE_ORDER, " \
"width = (int) 32, " \
"depth = (int) 32, " \
"signed = (bool) TRUE"
static void gst_volume_interface_init (GstImplementsInterfaceClass * klass);
static void gst_volume_mixer_init (GstMixerClass * iface);
#define _init_interfaces(type) \
{ \
static const GInterfaceInfo voliface_info = { \
(GInterfaceInitFunc) gst_volume_interface_init, \
NULL, \
NULL \
}; \
static const GInterfaceInfo volmixer_info = { \
(GInterfaceInitFunc) gst_volume_mixer_init, \
NULL, \
NULL \
}; \
static const GInterfaceInfo svol_info = { \
NULL, \
NULL, \
NULL \
}; \
\
g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, \
&voliface_info); \
g_type_add_interface_static (type, GST_TYPE_MIXER, &volmixer_info); \
g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info); \
}
GST_BOILERPLATE_FULL (GstVolume, gst_volume, GstAudioFilter,
GST_TYPE_AUDIO_FILTER, _init_interfaces);
static void volume_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void volume_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void volume_before_transform (GstBaseTransform * base,
GstBuffer * buffer);
static GstFlowReturn volume_transform_ip (GstBaseTransform * base,
GstBuffer * outbuf);
static gboolean volume_stop (GstBaseTransform * base);
static gboolean volume_setup (GstAudioFilter * filter,
GstRingBufferSpec * format);
2010-02-13 00:08:05 +00:00
static void volume_process_double (GstVolume * self, gpointer bytes,
guint n_bytes);
static void volume_process_controlled_double (GstVolume * self, gpointer bytes,
gdouble * volume, guint channels, guint n_bytes);
2010-02-13 00:08:05 +00:00
static void volume_process_float (GstVolume * self, gpointer bytes,
guint n_bytes);
static void volume_process_controlled_float (GstVolume * self, gpointer bytes,
gdouble * volume, guint channels, guint n_bytes);
2010-02-13 00:08:05 +00:00
static void volume_process_int32 (GstVolume * self, gpointer bytes,
guint n_bytes);
2010-02-13 00:08:05 +00:00
static void 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);
2010-02-13 00:08:05 +00:00
static void volume_process_int24 (GstVolume * self, gpointer bytes,
guint n_bytes);
2010-02-13 00:08:05 +00:00
static void 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);
2010-02-13 00:08:05 +00:00
static void volume_process_int16 (GstVolume * self, gpointer bytes,
guint n_bytes);
2010-02-13 00:08:05 +00:00
static void 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);
2010-02-13 00:08:05 +00:00
static void volume_process_int8 (GstVolume * self, gpointer bytes,
guint n_bytes);
2010-02-13 00:08:05 +00:00
static void 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);
/* helper functions */
static gboolean
2010-02-13 00:08:05 +00:00
volume_choose_func (GstVolume * self)
{
2010-02-13 00:08:05 +00:00
self->process = NULL;
self->process_controlled = NULL;
2010-02-13 00:08:05 +00:00
if (GST_AUDIO_FILTER (self)->format.caps == NULL)
return FALSE;
2010-02-13 00:08:05 +00:00
switch (GST_AUDIO_FILTER (self)->format.type) {
case GST_BUFTYPE_LINEAR:
2010-02-13 00:08:05 +00:00
switch (GST_AUDIO_FILTER (self)->format.width) {
case 32:
/* only clamp if the gain is greater than 1.0
*/
if (self->current_vol_i32 > VOLUME_UNITY_INT32) {
2010-02-13 00:08:05 +00:00
self->process = volume_process_int32_clamp;
} else {
2010-02-13 00:08:05 +00:00
self->process = volume_process_int32;
}
self->process_controlled = volume_process_controlled_int32_clamp;
break;
case 24:
/* only clamp if the gain is greater than 1.0
*/
if (self->current_vol_i24 > VOLUME_UNITY_INT24) {
2010-02-13 00:08:05 +00:00
self->process = volume_process_int24_clamp;
} else {
2010-02-13 00:08:05 +00:00
self->process = volume_process_int24;
}
self->process_controlled = volume_process_controlled_int24_clamp;
break;
case 16:
/* only clamp if the gain is greater than 1.0
*/
if (self->current_vol_i16 > VOLUME_UNITY_INT16) {
2010-02-13 00:08:05 +00:00
self->process = volume_process_int16_clamp;
} else {
2010-02-13 00:08:05 +00:00
self->process = volume_process_int16;
}
self->process_controlled = volume_process_controlled_int16_clamp;
break;
case 8:
/* only clamp if the gain is greater than 1.0
*/
if (self->current_vol_i16 > VOLUME_UNITY_INT8) {
2010-02-13 00:08:05 +00:00
self->process = volume_process_int8_clamp;
} else {
2010-02-13 00:08:05 +00:00
self->process = volume_process_int8;
}
self->process_controlled = volume_process_controlled_int8_clamp;
break;
}
break;
case GST_BUFTYPE_FLOAT:
2010-02-13 00:08:05 +00:00
switch (GST_AUDIO_FILTER (self)->format.width) {
case 32:
2010-02-13 00:08:05 +00:00
self->process = volume_process_float;
self->process_controlled = volume_process_controlled_float;
break;
case 64:
2010-02-13 00:08:05 +00:00
self->process = volume_process_double;
self->process_controlled = volume_process_controlled_double;
break;
}
break;
default:
break;
}
2010-02-13 00:08:05 +00:00
return (self->process != NULL);
}
static gboolean
2010-02-13 00:08:05 +00:00
volume_update_volume (GstVolume * self, gfloat volume, gboolean mute)
{
gboolean passthrough;
gboolean res;
GstController *controller;
2010-02-13 00:08:05 +00:00
GST_DEBUG_OBJECT (self, "configure mute %d, volume %f", mute, volume);
if (mute) {
2010-02-13 00:08:05 +00:00
self->current_mute = TRUE;
self->current_volume = 0.0;
2010-02-13 00:08:05 +00:00
self->current_vol_i8 = 0;
self->current_vol_i16 = 0;
self->current_vol_i24 = 0;
self->current_vol_i32 = 0;
passthrough = FALSE;
} else {
2010-02-13 00:08:05 +00:00
self->current_mute = FALSE;
self->current_volume = volume;
2010-02-13 00:08:05 +00:00
self->current_vol_i8 = volume * VOLUME_UNITY_INT8;
self->current_vol_i16 = volume * VOLUME_UNITY_INT16;
self->current_vol_i24 = volume * VOLUME_UNITY_INT24;
self->current_vol_i32 = volume * VOLUME_UNITY_INT32;
2010-02-13 00:08:05 +00:00
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);
2010-02-13 00:08:05 +00:00
GST_DEBUG_OBJECT (self, "set passthrough %d", passthrough);
2010-02-13 00:08:05 +00:00
gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (self), passthrough);
2010-02-13 00:08:05 +00:00
res = self->negotiated = volume_choose_func (self);
return res;
}
/* Mixer interface */
static gboolean
gst_volume_interface_supported (GstImplementsInterface * iface, GType type)
{
return (type == GST_TYPE_MIXER || type == GST_TYPE_STREAM_VOLUME);
}
static void
gst_volume_interface_init (GstImplementsInterfaceClass * klass)
{
klass->supported = gst_volume_interface_supported;
}
static const GList *
gst_volume_list_tracks (GstMixer * mixer)
{
2010-02-13 00:08:05 +00:00
GstVolume *self = GST_VOLUME (mixer);
2010-02-13 00:08:05 +00:00
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (GST_IS_VOLUME (self), NULL);
2010-02-13 00:08:05 +00:00
return self->tracklist;
}
static void
gst_volume_set_volume (GstMixer * mixer, GstMixerTrack * track, gint * volumes)
{
2010-02-13 00:08:05 +00:00
GstVolume *self = GST_VOLUME (mixer);
2010-02-13 00:08:05 +00:00
g_return_if_fail (self != NULL);
g_return_if_fail (GST_IS_VOLUME (self));
2010-02-13 00:08:05 +00:00
GST_OBJECT_LOCK (self);
self->volume = (gfloat) volumes[0] / VOLUME_STEPS;
GST_OBJECT_UNLOCK (self);
}
static void
gst_volume_get_volume (GstMixer * mixer, GstMixerTrack * track, gint * volumes)
{
2010-02-13 00:08:05 +00:00
GstVolume *self = GST_VOLUME (mixer);
2010-02-13 00:08:05 +00:00
g_return_if_fail (self != NULL);
g_return_if_fail (GST_IS_VOLUME (self));
2010-02-13 00:08:05 +00:00
GST_OBJECT_LOCK (self);
volumes[0] = (gint) self->volume * VOLUME_STEPS;
GST_OBJECT_UNLOCK (self);
}
static void
gst_volume_set_mute (GstMixer * mixer, GstMixerTrack * track, gboolean mute)
{
2010-02-13 00:08:05 +00:00
GstVolume *self = GST_VOLUME (mixer);
2010-02-13 00:08:05 +00:00
g_return_if_fail (self != NULL);
g_return_if_fail (GST_IS_VOLUME (self));
2010-02-13 00:08:05 +00:00
GST_OBJECT_LOCK (self);
self->mute = mute;
GST_OBJECT_UNLOCK (self);
}
static void
gst_volume_mixer_init (GstMixerClass * klass)
{
GST_MIXER_TYPE (klass) = GST_MIXER_SOFTWARE;
/* default virtual functions */
klass->list_tracks = gst_volume_list_tracks;
klass->set_volume = gst_volume_set_volume;
klass->get_volume = gst_volume_get_volume;
klass->set_mute = gst_volume_set_mute;
}
/* Element class */
static void
gst_volume_dispose (GObject * object)
{
GstVolume *volume = GST_VOLUME (object);
if (volume->tracklist) {
if (volume->tracklist->data)
g_object_unref (volume->tracklist->data);
g_list_free (volume->tracklist);
volume->tracklist = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_volume_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
GstAudioFilterClass *filter_class = GST_AUDIO_FILTER_CLASS (g_class);
GstCaps *caps;
gst_element_class_set_details_simple (element_class, "Volume",
"Filter/Effect/Audio",
"Set volume on audio/raw streams", "Andy Wingo <wingo@pobox.com>");
caps = gst_caps_from_string (ALLOWED_CAPS);
gst_audio_filter_class_add_pad_templates (filter_class, caps);
gst_caps_unref (caps);
}
static void
gst_volume_class_init (GstVolumeClass * klass)
{
GObjectClass *gobject_class;
GstBaseTransformClass *trans_class;
GstAudioFilterClass *filter_class;
gobject_class = (GObjectClass *) klass;
trans_class = (GstBaseTransformClass *) klass;
filter_class = (GstAudioFilterClass *) (klass);
gobject_class->set_property = volume_set_property;
gobject_class->get_property = volume_get_property;
gobject_class->dispose = gst_volume_dispose;
g_object_class_install_property (gobject_class, PROP_MUTE,
g_param_spec_boolean ("mute", "Mute", "mute channel",
DEFAULT_PROP_MUTE,
Use G_PARAM_STATIC_STRINGS everywhere for GParamSpecs that use static strings (i.e. all). This gives us less memory u... Original commit message from CVS: * configure.ac: * ext/alsa/gstalsamixerelement.c: (gst_alsa_mixer_element_class_init): * ext/alsa/gstalsasink.c: (gst_alsasink_class_init): * ext/alsa/gstalsasrc.c: (gst_alsasrc_class_init): * ext/cdparanoia/gstcdparanoiasrc.c: (gst_cd_paranoia_src_class_init): * ext/gio/gstgiosink.c: (gst_gio_sink_class_init): * ext/gio/gstgiosrc.c: (gst_gio_src_class_init): * ext/gio/gstgiostreamsink.c: (gst_gio_stream_sink_class_init): * ext/gio/gstgiostreamsrc.c: (gst_gio_stream_src_class_init): * ext/gnomevfs/gstgnomevfssink.c: (gst_gnome_vfs_sink_class_init): * ext/gnomevfs/gstgnomevfssrc.c: (gst_gnome_vfs_src_class_init): * ext/ogg/gstoggmux.c: (gst_ogg_mux_class_init): * ext/pango/gsttextoverlay.c: (gst_text_overlay_class_init): * ext/pango/gsttextrender.c: (gst_text_render_class_init): * ext/theora/theoradec.c: (gst_theora_dec_class_init): * ext/theora/theoraenc.c: (gst_theora_enc_class_init): * ext/theora/theoraparse.c: (gst_theora_parse_class_init): * ext/vorbis/vorbisenc.c: (gst_vorbis_enc_class_init): * gst-libs/gst/audio/gstaudiofiltertemplate.c: (gst_audio_filter_template_class_init): * gst-libs/gst/audio/gstbaseaudiosink.c: (gst_base_audio_sink_class_init): * gst-libs/gst/audio/gstbaseaudiosrc.c: (gst_base_audio_src_class_init): * gst-libs/gst/cdda/gstcddabasesrc.c: (gst_cdda_base_src_class_init): * gst-libs/gst/interfaces/mixertrack.c: (gst_mixer_track_class_init): * gst-libs/gst/rtp/gstbasertpdepayload.c: (gst_base_rtp_depayload_class_init): * gst-libs/gst/rtp/gstbasertppayload.c: (gst_basertppayload_class_init): * gst/audioconvert/gstaudioconvert.c: (gst_audio_convert_class_init): * gst/audiorate/gstaudiorate.c: (gst_audio_rate_class_init): * gst/audioresample/gstaudioresample.c: (gst_audioresample_class_init): * gst/audiotestsrc/gstaudiotestsrc.c: (gst_audio_test_src_class_init): * gst/gdp/gstgdppay.c: (gst_gdp_pay_class_init): * gst/playback/gstdecodebin2.c: (gst_decode_bin_class_init): * gst/playback/gstplaybasebin.c: (gst_play_base_bin_class_init), (preroll_unlinked): * gst/playback/gstplaybin.c: (gst_play_bin_class_init): * gst/playback/gstplaybin2.c: (gst_play_bin_class_init): * gst/playback/gstplaysink.c: (gst_play_sink_class_init): * gst/playback/gstqueue2.c: (gst_queue_class_init): * gst/playback/gststreaminfo.c: (gst_stream_info_class_init): * gst/playback/gststreamselector.c: (gst_selector_pad_class_init), (gst_stream_selector_class_init): * gst/playback/gsturidecodebin.c: (gst_uri_decode_bin_class_init): * gst/subparse/gstsubparse.c: (gst_sub_parse_class_init): * gst/tcp/gstmultifdsink.c: (gst_multi_fd_sink_class_init): * gst/tcp/gsttcpclientsink.c: (gst_tcp_client_sink_class_init): * gst/tcp/gsttcpclientsrc.c: (gst_tcp_client_src_class_init): * gst/tcp/gsttcpserversink.c: (gst_tcp_server_sink_class_init): * gst/tcp/gsttcpserversrc.c: (gst_tcp_server_src_class_init): * gst/videorate/gstvideorate.c: (gst_video_rate_class_init): * gst/videoscale/gstvideoscale.c: (gst_video_scale_class_init): * gst/videotestsrc/gstvideotestsrc.c: (gst_video_test_src_class_init): * gst/volume/gstvolume.c: (gst_volume_class_init): * sys/v4l/gstv4lelement.c: (gst_v4lelement_class_init): * sys/v4l/gstv4lmjpegsink.c: (gst_v4lmjpegsink_class_init): * sys/v4l/gstv4lmjpegsrc.c: (gst_v4lmjpegsrc_class_init): * sys/v4l/gstv4lsrc.c: (gst_v4lsrc_class_init): * sys/ximage/ximagesink.c: (gst_ximagesink_class_init): * sys/xvimage/xvimagesink.c: (gst_xvimagesink_class_init): Use G_PARAM_STATIC_STRINGS everywhere for GParamSpecs that use static strings (i.e. all). This gives us less memory usage, fewer allocations and thus less memory defragmentation. Depend on core CVS for this. Fixes bug #523806.
2008-03-22 15:00:53 +00:00
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_VOLUME,
g_param_spec_double ("volume", "Volume", "volume factor, 1.0=100%",
0.0, VOLUME_MAX_DOUBLE, DEFAULT_PROP_VOLUME,
Use G_PARAM_STATIC_STRINGS everywhere for GParamSpecs that use static strings (i.e. all). This gives us less memory u... Original commit message from CVS: * configure.ac: * ext/alsa/gstalsamixerelement.c: (gst_alsa_mixer_element_class_init): * ext/alsa/gstalsasink.c: (gst_alsasink_class_init): * ext/alsa/gstalsasrc.c: (gst_alsasrc_class_init): * ext/cdparanoia/gstcdparanoiasrc.c: (gst_cd_paranoia_src_class_init): * ext/gio/gstgiosink.c: (gst_gio_sink_class_init): * ext/gio/gstgiosrc.c: (gst_gio_src_class_init): * ext/gio/gstgiostreamsink.c: (gst_gio_stream_sink_class_init): * ext/gio/gstgiostreamsrc.c: (gst_gio_stream_src_class_init): * ext/gnomevfs/gstgnomevfssink.c: (gst_gnome_vfs_sink_class_init): * ext/gnomevfs/gstgnomevfssrc.c: (gst_gnome_vfs_src_class_init): * ext/ogg/gstoggmux.c: (gst_ogg_mux_class_init): * ext/pango/gsttextoverlay.c: (gst_text_overlay_class_init): * ext/pango/gsttextrender.c: (gst_text_render_class_init): * ext/theora/theoradec.c: (gst_theora_dec_class_init): * ext/theora/theoraenc.c: (gst_theora_enc_class_init): * ext/theora/theoraparse.c: (gst_theora_parse_class_init): * ext/vorbis/vorbisenc.c: (gst_vorbis_enc_class_init): * gst-libs/gst/audio/gstaudiofiltertemplate.c: (gst_audio_filter_template_class_init): * gst-libs/gst/audio/gstbaseaudiosink.c: (gst_base_audio_sink_class_init): * gst-libs/gst/audio/gstbaseaudiosrc.c: (gst_base_audio_src_class_init): * gst-libs/gst/cdda/gstcddabasesrc.c: (gst_cdda_base_src_class_init): * gst-libs/gst/interfaces/mixertrack.c: (gst_mixer_track_class_init): * gst-libs/gst/rtp/gstbasertpdepayload.c: (gst_base_rtp_depayload_class_init): * gst-libs/gst/rtp/gstbasertppayload.c: (gst_basertppayload_class_init): * gst/audioconvert/gstaudioconvert.c: (gst_audio_convert_class_init): * gst/audiorate/gstaudiorate.c: (gst_audio_rate_class_init): * gst/audioresample/gstaudioresample.c: (gst_audioresample_class_init): * gst/audiotestsrc/gstaudiotestsrc.c: (gst_audio_test_src_class_init): * gst/gdp/gstgdppay.c: (gst_gdp_pay_class_init): * gst/playback/gstdecodebin2.c: (gst_decode_bin_class_init): * gst/playback/gstplaybasebin.c: (gst_play_base_bin_class_init), (preroll_unlinked): * gst/playback/gstplaybin.c: (gst_play_bin_class_init): * gst/playback/gstplaybin2.c: (gst_play_bin_class_init): * gst/playback/gstplaysink.c: (gst_play_sink_class_init): * gst/playback/gstqueue2.c: (gst_queue_class_init): * gst/playback/gststreaminfo.c: (gst_stream_info_class_init): * gst/playback/gststreamselector.c: (gst_selector_pad_class_init), (gst_stream_selector_class_init): * gst/playback/gsturidecodebin.c: (gst_uri_decode_bin_class_init): * gst/subparse/gstsubparse.c: (gst_sub_parse_class_init): * gst/tcp/gstmultifdsink.c: (gst_multi_fd_sink_class_init): * gst/tcp/gsttcpclientsink.c: (gst_tcp_client_sink_class_init): * gst/tcp/gsttcpclientsrc.c: (gst_tcp_client_src_class_init): * gst/tcp/gsttcpserversink.c: (gst_tcp_server_sink_class_init): * gst/tcp/gsttcpserversrc.c: (gst_tcp_server_src_class_init): * gst/videorate/gstvideorate.c: (gst_video_rate_class_init): * gst/videoscale/gstvideoscale.c: (gst_video_scale_class_init): * gst/videotestsrc/gstvideotestsrc.c: (gst_video_test_src_class_init): * gst/volume/gstvolume.c: (gst_volume_class_init): * sys/v4l/gstv4lelement.c: (gst_v4lelement_class_init): * sys/v4l/gstv4lmjpegsink.c: (gst_v4lmjpegsink_class_init): * sys/v4l/gstv4lmjpegsrc.c: (gst_v4lmjpegsrc_class_init): * sys/v4l/gstv4lsrc.c: (gst_v4lsrc_class_init): * sys/ximage/ximagesink.c: (gst_ximagesink_class_init): * sys/xvimage/xvimagesink.c: (gst_xvimagesink_class_init): Use G_PARAM_STATIC_STRINGS everywhere for GParamSpecs that use static strings (i.e. all). This gives us less memory usage, fewer allocations and thus less memory defragmentation. Depend on core CVS for this. Fixes bug #523806.
2008-03-22 15:00:53 +00:00
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
trans_class->before_transform = GST_DEBUG_FUNCPTR (volume_before_transform);
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);
}
static void
2010-02-13 00:08:05 +00:00
gst_volume_init (GstVolume * self, GstVolumeClass * g_class)
{
GstMixerTrack *track = NULL;
2010-02-13 00:08:05 +00:00
self->mute = DEFAULT_PROP_MUTE;;
self->volume = DEFAULT_PROP_VOLUME;
2010-02-13 00:08:05 +00:00
self->tracklist = NULL;
self->negotiated = FALSE;
track = g_object_new (GST_TYPE_MIXER_TRACK, NULL);
if (GST_IS_MIXER_TRACK (track)) {
track->label = g_strdup ("volume");
track->num_channels = 1;
track->min_volume = 0;
track->max_volume = VOLUME_STEPS;
track->flags = GST_MIXER_TRACK_SOFTWARE;
2010-02-13 00:08:05 +00:00
self->tracklist = g_list_append (self->tracklist, track);
}
2010-02-13 00:08:05 +00:00
gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (self), TRUE);
}
#define USE_ORC
static void
2010-02-13 00:08:05 +00:00
volume_process_double (GstVolume * self, gpointer bytes, guint n_bytes)
{
gdouble *data = (gdouble *) bytes;
guint num_samples = n_bytes / sizeof (gdouble);
2010-06-07 06:48:15 +00:00
int i;
2010-02-13 00:08:05 +00:00
gdouble vol = self->current_volume;
2010-06-07 06:48:15 +00:00
for (i = 0; i < num_samples; i++) {
data[i] *= vol;
}
}
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
2010-02-13 00:08:05 +00:00
volume_process_float (GstVolume * self, gpointer bytes, guint n_bytes)
{
gfloat *data = (gfloat *) bytes;
guint num_samples = n_bytes / sizeof (gfloat);
2010-06-07 06:48:15 +00:00
#ifndef broken
int i;
for (i = 0; i < num_samples; i++) {
2010-06-07 06:48:15 +00:00
*data++ *= self->current_volume;
}
2010-06-07 06:48:15 +00:00
#else
/* FIXME compiler doesn't set the float parameter correctly */
orc_scalarmultiply_f32_ns (data, data, self->current_volume, num_samples);
#endif
}
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
2010-02-13 00:08:05 +00:00
volume_process_int32 (GstVolume * self, gpointer bytes, guint n_bytes)
{
gint32 *data = (gint32 *) bytes;
guint i, num_samples;
gint64 val;
num_samples = n_bytes / sizeof (gint);
for (i = 0; i < num_samples; i++) {
/* we use bitshifting instead of dividing by UNITY_INT for speed */
val = (gint64) * data;
val =
2010-02-13 00:08:05 +00:00
(((gint64) self->current_vol_i32 *
val) >> VOLUME_UNITY_INT32_BIT_SHIFT);
*data++ = (gint32) val;
}
}
static void
2010-02-13 00:08:05 +00:00
volume_process_int32_clamp (GstVolume * self, gpointer bytes, guint n_bytes)
{
gint32 *data = (gint32 *) bytes;
guint i, num_samples;
gint64 val;
num_samples = n_bytes / sizeof (gint32);
for (i = 0; i < num_samples; i++) {
/* we use bitshifting instead of dividing by UNITY_INT for speed */
val = (gint64) * data;
val =
2010-02-13 00:08:05 +00:00
(((gint64) self->current_vol_i32 *
val) >> VOLUME_UNITY_INT32_BIT_SHIFT);
*data++ = (gint32) CLAMP (val, VOLUME_MIN_INT32, VOLUME_MAX_INT32);
}
}
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;
*data++ = (gint32) CLAMP (val, VOLUME_MIN_INT32, VOLUME_MAX_INT32);
}
}
}
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
#define get_unaligned_i24(_x) ( (((guint8*)_x)[0]) | ((((guint8*)_x)[1]) << 8) | ((((gint8*)_x)[2]) << 16) )
#define write_unaligned_u24(_x,samp) \
G_STMT_START { \
*(_x)++ = samp & 0xFF; \
*(_x)++ = (samp >> 8) & 0xFF; \
*(_x)++ = (samp >> 16) & 0xFF; \
} G_STMT_END
#else /* BIG ENDIAN */
#define get_unaligned_i24(_x) ( (((guint8*)_x)[2]) | ((((guint8*)_x)[1]) << 8) | ((((gint8*)_x)[0]) << 16) )
#define write_unaligned_u24(_x,samp) \
G_STMT_START { \
*(_x)++ = (samp >> 16) & 0xFF; \
*(_x)++ = (samp >> 8) & 0xFF; \
*(_x)++ = samp & 0xFF; \
} G_STMT_END
#endif
static void
2010-02-13 00:08:05 +00:00
volume_process_int24 (GstVolume * self, gpointer bytes, guint n_bytes)
{
gint8 *data = (gint8 *) bytes; /* treat the data as a byte stream */
guint i, num_samples;
guint32 samp;
gint64 val;
num_samples = n_bytes / (sizeof (gint8) * 3);
for (i = 0; i < num_samples; i++) {
samp = get_unaligned_i24 (data);
val = (gint32) samp;
val =
2010-02-13 00:08:05 +00:00
(((gint64) self->current_vol_i24 *
val) >> VOLUME_UNITY_INT24_BIT_SHIFT);
samp = (guint32) val;
/* write the value back into the stream */
write_unaligned_u24 (data, samp);
}
}
static void
2010-02-13 00:08:05 +00:00
volume_process_int24_clamp (GstVolume * self, gpointer bytes, guint n_bytes)
{
gint8 *data = (gint8 *) bytes; /* treat the data as a byte stream */
guint i, num_samples;
guint32 samp;
gint64 val;
num_samples = n_bytes / (sizeof (gint8) * 3);
for (i = 0; i < num_samples; i++) {
samp = get_unaligned_i24 (data);
val = (gint32) samp;
val =
2010-02-13 00:08:05 +00:00
(((gint64) self->current_vol_i24 *
val) >> VOLUME_UNITY_INT24_BIT_SHIFT);
samp = (guint32) CLAMP (val, VOLUME_MIN_INT24, VOLUME_MAX_INT24);
/* write the value back into the stream */
write_unaligned_u24 (data, samp);
}
}
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;
val = CLAMP (val, VOLUME_MIN_INT24, VOLUME_MAX_INT24);
write_unaligned_u24 (data, (gint32) val);
}
}
}
static void
2010-02-13 00:08:05 +00:00
volume_process_int16 (GstVolume * self, gpointer bytes, guint n_bytes)
{
gint16 *data = (gint16 *) bytes;
guint num_samples = n_bytes / sizeof (gint16);
#ifndef USE_ORC
guint i;
gint val;
/* hard coded in volume.orc */
g_assert (VOLUME_UNITY_INT16_BIT_SHIFT == 13);
for (i = 0; i < num_samples; i++) {
/* we use bitshifting instead of dividing by UNITY_INT for speed */
val = (gint) * data;
*data++ =
2010-02-13 00:08:05 +00:00
(gint16) ((self->current_vol_i16 *
val) >> VOLUME_UNITY_INT16_BIT_SHIFT);
}
#else
2010-06-07 06:48:15 +00:00
/* FIXME */
orc_process_int16 (data, self->current_vol_i16, num_samples);
2010-06-07 06:48:15 +00:00
#endif
}
static void
2010-02-13 00:08:05 +00:00
volume_process_int16_clamp (GstVolume * self, gpointer bytes, guint n_bytes)
{
gint16 *data = (gint16 *) bytes;
2010-06-07 06:48:15 +00:00
guint num_samples = n_bytes / sizeof (gint16);
#ifndef USE_ORC
2010-06-07 06:48:15 +00:00
int i;
2010-06-07 06:48:15 +00:00
/* hard coded in volume.orc */
g_assert (VOLUME_UNITY_INT16_BIT_SHIFT == 13);
/* FIXME: oil_scalarmultiply_s16_ns ?
* https://bugs.freedesktop.org/show_bug.cgi?id=7060
*/
for (i = 0; i < num_samples; i++) {
/* we use bitshifting instead of dividing by UNITY_INT for speed */
2010-06-07 06:48:15 +00:00
int val = (gint) * data;
*data++ =
2010-02-13 00:08:05 +00:00
(gint16) CLAMP ((self->current_vol_i16 *
val) >> VOLUME_UNITY_INT16_BIT_SHIFT, VOLUME_MIN_INT16,
VOLUME_MAX_INT16);
}
#else
2010-06-07 06:48:15 +00:00
/* FIXME */
orc_process_int16_clamp (data, self->current_vol_i16, num_samples);
2010-06-07 06:48:15 +00:00
#endif
}
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;
*data++ = (gint16) CLAMP (val, VOLUME_MIN_INT16, VOLUME_MAX_INT16);
}
}
}
static void
2010-02-13 00:08:05 +00:00
volume_process_int8 (GstVolume * self, gpointer bytes, guint n_bytes)
{
gint8 *data = (gint8 *) bytes;
guint num_samples = n_bytes / sizeof (gint8);
#ifndef USE_ORC
2010-06-07 06:48:15 +00:00
int i;
for (i = 0; i < num_samples; i++) {
/* we use bitshifting instead of dividing by UNITY_INT for speed */
2010-06-07 06:48:15 +00:00
int val = (gint) * data;
*data++ =
2010-02-13 00:08:05 +00:00
(gint8) ((self->current_vol_i8 * val) >> VOLUME_UNITY_INT8_BIT_SHIFT);
}
#else
2010-06-07 06:48:15 +00:00
/* FIXME */
/* hard coded in volume.orc */
g_assert (VOLUME_UNITY_INT8_BIT_SHIFT == 5);
orc_process_int8 (data, self->current_vol_i8, num_samples);
2010-06-07 06:48:15 +00:00
#endif
}
static void
2010-02-13 00:08:05 +00:00
volume_process_int8_clamp (GstVolume * self, gpointer bytes, guint n_bytes)
{
gint8 *data = (gint8 *) bytes;
2010-06-07 06:48:15 +00:00
guint num_samples = n_bytes / sizeof (gint8);
#ifndef USE_ORC
2010-06-07 06:48:15 +00:00
int i;
2010-06-07 06:48:15 +00:00
/* hard coded in volume.orc */
g_assert (VOLUME_UNITY_INT8_BIT_SHIFT == 5);
for (i = 0; i < num_samples; i++) {
/* we use bitshifting instead of dividing by UNITY_INT for speed */
2010-06-07 06:48:15 +00:00
int val = (gint) * data;
*data++ =
2010-02-13 00:08:05 +00:00
(gint8) CLAMP ((self->current_vol_i8 *
val) >> VOLUME_UNITY_INT8_BIT_SHIFT, VOLUME_MIN_INT8,
VOLUME_MAX_INT8);
}
#else
2010-06-07 06:48:15 +00:00
/* FIXME */
orc_process_int8_clamp (data, self->current_vol_i8, num_samples);
2010-06-07 06:48:15 +00:00
#endif
}
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;
*data++ = (gint8) CLAMP (val, VOLUME_MIN_INT8, VOLUME_MAX_INT8);
}
}
}
/* GstBaseTransform vmethod implementations */
/* get notified of caps and plug in the correct process function */
static gboolean
volume_setup (GstAudioFilter * filter, GstRingBufferSpec * format)
{
gboolean res;
2010-02-13 00:08:05 +00:00
GstVolume *self = GST_VOLUME (filter);
gfloat volume;
gboolean mute;
2010-02-13 00:08:05 +00:00
GST_OBJECT_LOCK (self);
volume = self->volume;
mute = self->mute;
GST_OBJECT_UNLOCK (self);
2010-02-13 00:08:05 +00:00
res = volume_update_volume (self, volume, mute);
if (!res) {
2010-02-13 00:08:05 +00:00
GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
("Invalid incoming format"), (NULL));
}
2010-02-13 00:08:05 +00:00
self->negotiated = 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
volume_before_transform (GstBaseTransform * base, GstBuffer * buffer)
{
GstClockTime timestamp;
2010-02-13 00:08:05 +00:00
GstVolume *self = GST_VOLUME (base);
gfloat volume;
gboolean mute;
timestamp = GST_BUFFER_TIMESTAMP (buffer);
timestamp =
gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
GST_DEBUG_OBJECT (base, "sync to %" GST_TIME_FORMAT,
GST_TIME_ARGS (timestamp));
if (GST_CLOCK_TIME_IS_VALID (timestamp))
2010-02-13 00:08:05 +00:00
gst_object_sync_values (G_OBJECT (self), timestamp);
/* get latest values */
2010-02-13 00:08:05 +00:00
GST_OBJECT_LOCK (self);
volume = self->volume;
mute = self->mute;
GST_OBJECT_UNLOCK (self);
2010-02-13 00:08:05 +00:00
if ((volume != self->current_volume) || (mute != self->current_mute)) {
/* the volume or mute was updated, update our internal state before
* we continue processing. */
2010-02-13 00:08:05 +00:00
volume_update_volume (self, volume, mute);
}
}
/* call the plugged-in process function for this instance
* needs to be done with this indirection since volume_transform is
* a class-global method
*/
static GstFlowReturn
volume_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
{
2010-02-13 00:08:05 +00:00
GstVolume *self = GST_VOLUME (base);
guint8 *data;
guint size;
GstControlSource *mute_csource, *volume_csource;
2010-02-13 00:08:05 +00:00
if (G_UNLIKELY (!self->negotiated))
goto not_negotiated;
/* don't process data in passthrough-mode */
if (gst_base_transform_is_passthrough (base) ||
GST_BUFFER_FLAG_IS_SET (outbuf, GST_BUFFER_FLAG_GAP))
return GST_FLOW_OK;
data = GST_BUFFER_DATA (outbuf);
size = GST_BUFFER_SIZE (outbuf);
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 && mute_csource) {
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);
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
2010-02-13 00:08:05 +00:00
} else if (self->current_volume != 1.0) {
self->process (self, data, size);
}
return GST_FLOW_OK;
/* ERRORS */
not_negotiated:
{
2010-02-13 00:08:05 +00:00
GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
("No format was negotiated"), (NULL));
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
volume_set_property (GObject * object, guint prop_id, const GValue * value,
GParamSpec * pspec)
{
2010-02-13 00:08:05 +00:00
GstVolume *self = GST_VOLUME (object);
switch (prop_id) {
case PROP_MUTE:
2010-02-13 00:08:05 +00:00
GST_OBJECT_LOCK (self);
self->mute = g_value_get_boolean (value);
GST_OBJECT_UNLOCK (self);
break;
case PROP_VOLUME:
2010-02-13 00:08:05 +00:00
GST_OBJECT_LOCK (self);
self->volume = g_value_get_double (value);
GST_OBJECT_UNLOCK (self);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
volume_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
2010-02-13 00:08:05 +00:00
GstVolume *self = GST_VOLUME (object);
switch (prop_id) {
case PROP_MUTE:
2010-02-13 00:08:05 +00:00
GST_OBJECT_LOCK (self);
g_value_set_boolean (value, self->mute);
GST_OBJECT_UNLOCK (self);
break;
case PROP_VOLUME:
2010-02-13 00:08:05 +00:00
GST_OBJECT_LOCK (self);
g_value_set_double (value, self->volume);
GST_OBJECT_UNLOCK (self);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
plugin_init (GstPlugin * plugin)
{
/* initialize gst controller library */
gst_controller_init (NULL, NULL);
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "volume", 0, "Volume gain");
/* ref class from a thread-safe context to work around missing bit of
* thread-safety in GObject */
g_type_class_ref (GST_TYPE_MIXER_TRACK);
return gst_element_register (plugin, "volume", GST_RANK_NONE,
GST_TYPE_VOLUME);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"volume",
"plugin for controlling audio volume",
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);