1 /* -*- c-basic-offset: 2 -*-
3 * Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
26 #include <gst/audio/audio.h>
27 #include <gst/control/control.h>
28 #include <gst/mixer/mixer.h>
29 #include "gstvolume.h"
31 /* some defines for audio processing */
32 /* the volume factor is a range from 0.0 to (arbitrary) 4.0
33 * we map 1.0 to VOLUME_UNITY_INT
35 #define VOLUME_UNITY_INT 8192 /* internal int for unity */
36 #define VOLUME_UNITY_BIT_SHIFT 13 /* number of bits to shift
38 #define VOLUME_MAX_FLOAT 4.0
39 #define VOLUME_MAX_INT16 32767
40 #define VOLUME_MIN_INT16 -32768
42 /* number of steps we use for the mixer interface to go from 0.0 to 1.0 */
43 # define VOLUME_STEPS 100
45 static GstElementDetails volume_details = {
47 "Filter/Effect/Audio",
48 "Set volume on audio/raw streams",
49 "Andy Wingo <apwingo@eos.ncsu.edu>",
53 /* Filter signals and args */
66 static GstStaticPadTemplate volume_sink_factory =
67 GST_STATIC_PAD_TEMPLATE (
73 "rate = (int) [ 1, MAX ], "
74 "channels = (int) [ 1, MAX ], "
75 "endianness = (int) BYTE_ORDER, "
77 "buffer-frames = (int) [ 1, MAX]; "
79 "channels = (int) [ 1, MAX ], "
80 "rate = (int) [ 1, MAX ], "
81 "endianness = (int) BYTE_ORDER, "
84 "signed = (bool) TRUE"
88 static GstStaticPadTemplate volume_src_factory =
89 GST_STATIC_PAD_TEMPLATE (
95 "rate = (int) [ 1, MAX ], "
96 "channels = (int) [ 1, MAX ], "
97 "endianness = (int) BYTE_ORDER, "
99 "buffer-frames = (int) [ 1, MAX]; "
101 "channels = (int) [ 1, MAX ], "
102 "rate = (int) [ 1, MAX ], "
103 "endianness = (int) BYTE_ORDER, "
106 "signed = (bool) TRUE"
110 static void volume_base_init (gpointer g_class);
111 static void volume_class_init (GstVolumeClass *klass);
112 static void volume_init (GstVolume *filter);
114 static void volume_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
115 static void volume_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
116 static void volume_update_volume (const GValue *value, gpointer data);
117 static void volume_update_mute (const GValue *value, gpointer data);
119 static gboolean volume_parse_caps (GstVolume *filter, GstStructure *structure);
121 static void volume_chain_float (GstPad *pad, GstData *_data);
122 static void volume_chain_int16 (GstPad *pad, GstData *_data);
124 static void gst_volume_interface_init (GstImplementsInterfaceClass *klass);
125 static void gst_volume_mixer_init (GstMixerClass *iface);
127 static GstElementClass *parent_class = NULL;
128 /*static guint gst_filter_signals[LAST_SIGNAL] = { 0 }; */
131 gst_volume_interface_supported (GstImplementsInterface *iface, GType type)
133 g_assert (type == GST_TYPE_MIXER);
138 gst_volume_interface_init (GstImplementsInterfaceClass *klass)
140 klass->supported = gst_volume_interface_supported;
144 gst_volume_list_tracks (GstMixer *mixer)
146 GstVolume *filter = GST_VOLUME (mixer);
148 g_return_val_if_fail (filter != NULL, NULL);
149 g_return_val_if_fail (GST_IS_VOLUME (filter), NULL);
151 return filter->tracklist;
155 gst_volume_set_volume (GstMixer *mixer, GstMixerTrack *track,
158 GstVolume *filter = GST_VOLUME (mixer);
160 g_return_if_fail (filter != NULL);
161 g_return_if_fail (GST_IS_VOLUME (filter));
163 gst_dpman_bypass_dparam (filter->dpman, "volume");
165 filter->volume_f = (gfloat) volumes[0] / VOLUME_STEPS;
166 filter->volume_i = filter->volume_f * VOLUME_UNITY_INT;
169 filter->real_vol_f = 0.0;
170 filter->real_vol_i = 0;
173 filter->real_vol_f = filter->volume_f;
174 filter->real_vol_i = filter->volume_i;
179 gst_volume_get_volume (GstMixer *mixer, GstMixerTrack *track,
182 GstVolume *filter = GST_VOLUME (mixer);
184 g_return_if_fail (filter != NULL);
185 g_return_if_fail (GST_IS_VOLUME (filter));
187 volumes[0] = (gint) filter->volume_f * VOLUME_STEPS;
191 gst_volume_set_mute (GstMixer *mixer, GstMixerTrack *track,
194 GstVolume *filter = GST_VOLUME (mixer);
196 g_return_if_fail (filter != NULL);
197 g_return_if_fail (GST_IS_VOLUME (filter));
199 gst_dpman_bypass_dparam (filter->dpman, "volume");
204 filter->real_vol_f = 0.0;
205 filter->real_vol_i = 0;
208 filter->real_vol_f = filter->volume_f;
209 filter->real_vol_i = filter->volume_i;
214 gst_volume_mixer_init (GstMixerClass *klass)
216 GST_MIXER_TYPE (klass) = GST_MIXER_SOFTWARE;
218 /* default virtual functions */
219 klass->list_tracks = gst_volume_list_tracks;
220 klass->set_volume = gst_volume_set_volume;
221 klass->get_volume = gst_volume_get_volume;
222 klass->set_mute = gst_volume_set_mute;
226 gst_volume_dispose (GObject *object)
230 volume = GST_VOLUME (object);
232 if (volume->tracklist)
234 if (volume->tracklist->data)
235 g_object_unref (volume->tracklist->data);
236 g_list_free (volume->tracklist);
237 volume->tracklist = NULL;
240 G_OBJECT_CLASS (parent_class)->dispose (object);
243 static GstPadLinkReturn
244 volume_connect (GstPad *pad, const GstCaps *caps)
249 GstPadLinkReturn link_ret;
250 GstStructure *structure;
252 filter = GST_VOLUME (gst_pad_get_parent (pad));
253 g_return_val_if_fail (GST_IS_VOLUME (filter), GST_PAD_LINK_REFUSED);
254 otherpad = (pad == filter->srcpad ? filter->sinkpad : filter->srcpad);
256 structure = gst_caps_get_structure (caps, 0);
257 gst_structure_get_int (structure, "rate", &rate);
259 link_ret = gst_pad_try_set_caps (otherpad, caps);
260 if (GST_PAD_LINK_FAILED (link_ret)){
264 if (!volume_parse_caps (filter, structure))
265 return GST_PAD_LINK_REFUSED;
267 gst_dpman_set_rate(filter->dpman, rate);
269 return GST_PAD_LINK_OK;
273 volume_parse_caps (GstVolume *filter, GstStructure *structure)
275 const gchar *mimetype;
277 g_return_val_if_fail(filter!=NULL,FALSE);
278 g_return_val_if_fail(structure!=NULL,FALSE);
280 mimetype = gst_structure_get_name (structure);
282 if (strcmp(mimetype, "audio/x-raw-int")==0) {
283 gst_pad_set_chain_function(filter->sinkpad,volume_chain_int16);
287 if (strcmp(mimetype, "audio/x-raw-float")==0) {
288 gst_pad_set_chain_function(filter->sinkpad,volume_chain_float);
297 gst_volume_get_type(void) {
298 static GType volume_type = 0;
301 static const GTypeInfo volume_info = {
302 sizeof(GstVolumeClass),
305 (GClassInitFunc)volume_class_init,
310 (GInstanceInitFunc)volume_init
312 static const GInterfaceInfo voliface_info = {
313 (GInterfaceInitFunc) gst_volume_interface_init,
317 static const GInterfaceInfo volmixer_info = {
318 (GInterfaceInitFunc) gst_volume_mixer_init,
323 volume_type = g_type_register_static(GST_TYPE_ELEMENT, "GstVolume", &volume_info, 0);
324 g_type_add_interface_static (volume_type,
325 GST_TYPE_IMPLEMENTS_INTERFACE,
327 g_type_add_interface_static (volume_type,
334 volume_base_init (gpointer g_class)
336 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
338 gst_element_class_add_pad_template (element_class,
339 gst_static_pad_template_get (&volume_src_factory));
340 gst_element_class_add_pad_template (element_class,
341 gst_static_pad_template_get (&volume_sink_factory));
342 gst_element_class_set_details (element_class, &volume_details);
345 volume_class_init (GstVolumeClass *klass)
347 GObjectClass *gobject_class;
348 GstElementClass *gstelement_class;
350 gobject_class = (GObjectClass*)klass;
351 gstelement_class = (GstElementClass*)klass;
353 parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
355 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_MUTE,
356 g_param_spec_boolean("mute","mute","mute",
357 FALSE,G_PARAM_READWRITE));
359 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_VOLUME,
360 g_param_spec_float("volume","volume","volume",
361 0.0,VOLUME_MAX_FLOAT,1.0,G_PARAM_READWRITE));
363 gobject_class->set_property = volume_set_property;
364 gobject_class->get_property = volume_get_property;
365 gobject_class->dispose = gst_volume_dispose;
369 volume_init (GstVolume *filter)
371 GstMixerTrack *track = NULL;
373 filter->sinkpad = gst_pad_new_from_template (
374 gst_static_pad_template_get (&volume_sink_factory), "sink");
375 gst_pad_set_getcaps_function (filter->sinkpad, gst_pad_proxy_getcaps);
376 gst_pad_set_link_function (filter->sinkpad, volume_connect);
378 filter->srcpad = gst_pad_new_from_template (
379 gst_static_pad_template_get (&volume_src_factory), "src");
380 gst_pad_set_getcaps_function (filter->srcpad, gst_pad_proxy_getcaps);
381 gst_pad_set_link_function(filter->srcpad,volume_connect);
383 gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
384 gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
386 gst_pad_set_chain_function (filter->sinkpad, volume_chain_int16);
388 filter->mute = FALSE;
389 filter->volume_i = VOLUME_UNITY_INT;
390 filter->volume_f = 1.0;
391 filter->real_vol_i = VOLUME_UNITY_INT;
392 filter->real_vol_f = 1.0;
393 filter->tracklist = NULL;
395 filter->dpman = gst_dpman_new ("volume_dpman", GST_ELEMENT(filter));
396 gst_dpman_add_required_dparam_callback (
398 g_param_spec_int("mute","Mute","Mute the audio",
399 0, 1, 0, G_PARAM_READWRITE),
404 gst_dpman_add_required_dparam_callback (
406 g_param_spec_float("volume","Volume","Volume of the audio",
407 0.0, VOLUME_MAX_FLOAT, 1.0, G_PARAM_READWRITE),
409 volume_update_volume,
413 track = g_object_new (GST_TYPE_MIXER_TRACK, NULL);
415 if (GST_IS_MIXER_TRACK (track))
417 track->label = g_strdup ("volume");
418 track->num_channels = 1;
419 track->min_volume = 0;
420 track->max_volume = VOLUME_STEPS;
421 track->flags = GST_MIXER_TRACK_SOFTWARE;
422 filter->tracklist = g_list_append (filter->tracklist, track);
427 volume_chain_float (GstPad *pad, GstData *_data)
429 GstBuffer *buf = GST_BUFFER (_data);
435 g_return_if_fail(GST_IS_PAD(pad));
436 g_return_if_fail(buf != NULL);
438 filter = GST_VOLUME(GST_OBJECT_PARENT (pad));
439 g_return_if_fail(GST_IS_VOLUME(filter));
441 out_buf = gst_buffer_copy_on_write (buf);
443 data = (gfloat *)GST_BUFFER_DATA(out_buf);
444 num_samples = GST_BUFFER_SIZE(out_buf)/sizeof(gfloat);
445 GST_DPMAN_PREPROCESS(filter->dpman, num_samples, GST_BUFFER_TIMESTAMP(out_buf));
448 while(GST_DPMAN_PROCESS(filter->dpman, i)) {
449 data[i++] *= filter->real_vol_f;
452 gst_pad_push(filter->srcpad,GST_DATA (out_buf));
457 volume_chain_int16 (GstPad *pad, GstData *_data)
459 GstBuffer *buf = GST_BUFFER (_data);
465 g_return_if_fail(GST_IS_PAD(pad));
466 g_return_if_fail(buf != NULL);
468 filter = GST_VOLUME(GST_OBJECT_PARENT (pad));
469 g_return_if_fail(GST_IS_VOLUME(filter));
471 out_buf = gst_buffer_copy_on_write (buf);
473 data = (gint16 *) GST_BUFFER_DATA (out_buf);
475 num_samples = GST_BUFFER_SIZE(out_buf)/sizeof(gint16);
476 GST_DPMAN_PREPROCESS(filter->dpman, num_samples, GST_BUFFER_TIMESTAMP(out_buf));
479 while(GST_DPMAN_PROCESS(filter->dpman, i)) {
480 /* only clamp if the gain is greater than 1.0 */
481 if (filter->real_vol_i > VOLUME_UNITY_INT){
482 while (i < GST_DPMAN_NEXT_UPDATE_FRAME(filter->dpman)){
483 /* we use bitshifting instead of dividing by UNITY_INT for speed */
484 data[i] = (gint16) CLAMP((filter->real_vol_i * (gint) data[i]) >> VOLUME_UNITY_BIT_SHIFT, VOLUME_MIN_INT16, VOLUME_MAX_INT16);
489 while (i < GST_DPMAN_NEXT_UPDATE_FRAME(filter->dpman)){
490 /* we use bitshifting instead of dividing by UNITY_INT for speed */
491 data[i] = (gint16) ((filter->real_vol_i * (gint) data[i]) >> VOLUME_UNITY_BIT_SHIFT);
497 gst_pad_push(filter->srcpad,GST_DATA (out_buf));
502 volume_update_mute(const GValue *value, gpointer data)
504 GstVolume *filter = (GstVolume*)data;
505 g_return_if_fail(GST_IS_VOLUME(filter));
507 if (G_VALUE_HOLDS_BOOLEAN(value)){
508 filter->mute = g_value_get_boolean(value);
510 else if (G_VALUE_HOLDS_INT(value)){
511 filter->mute = (g_value_get_int(value) == 1);
515 filter->real_vol_f = 0.0;
516 filter->real_vol_i = 0;
519 filter->real_vol_f = filter->volume_f;
520 filter->real_vol_i = filter->volume_i;
525 volume_update_volume(const GValue *value, gpointer data)
527 GstVolume *filter = (GstVolume*)data;
528 g_return_if_fail(GST_IS_VOLUME(filter));
530 filter->volume_f = g_value_get_float (value);
531 filter->volume_i = filter->volume_f*VOLUME_UNITY_INT;
533 filter->real_vol_f = 0.0;
534 filter->real_vol_i = 0;
537 filter->real_vol_f = filter->volume_f;
538 filter->real_vol_i = filter->volume_i;
543 volume_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
547 /* it's not null if we got it, but it might not be ours */
548 g_return_if_fail(GST_IS_VOLUME(object));
549 filter = GST_VOLUME(object);
554 gst_dpman_bypass_dparam(filter->dpman, "mute");
555 volume_update_mute(value, filter);
558 gst_dpman_bypass_dparam(filter->dpman, "volume");
559 volume_update_volume(value, filter);
562 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
568 volume_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
572 /* it's not null if we got it, but it might not be ours */
573 g_return_if_fail(GST_IS_VOLUME(object));
574 filter = GST_VOLUME(object);
578 g_value_set_boolean (value, filter->mute);
581 g_value_set_float (value, filter->volume_f);
584 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
590 plugin_init (GstPlugin *plugin)
592 gst_control_init(NULL,NULL);
594 return gst_element_register (plugin, "volume", GST_RANK_PRIMARY, GST_TYPE_VOLUME);
601 "element for controlling audio volume",