make code more readable by extracting magic numbers fix interface range
[platform/upstream/gst-plugins-base.git] / gst / volume / gstvolume.c
1 /* -*- c-basic-offset: 2 -*-
2  * GStreamer
3  * Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 #include <string.h>
25 #include <gst/gst.h>
26 #include <gst/audio/audio.h>
27 #include <gst/control/control.h>
28 #include <gst/mixer/mixer.h>
29 #include "gstvolume.h"
30
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
34  */
35 #define VOLUME_UNITY_INT        8192            /* internal int for unity */
36 #define VOLUME_UNITY_BIT_SHIFT  13              /* number of bits to shift
37                                                    for unity */
38 #define VOLUME_MAX_FLOAT        4.0
39 #define VOLUME_MAX_INT16        32767
40 #define VOLUME_MIN_INT16        -32768
41
42 /* number of steps we use for the mixer interface to go from 0.0 to 1.0 */
43 # define VOLUME_STEPS           100
44
45 static GstElementDetails volume_details = {
46   "Volume",
47   "Filter/Effect/Audio",
48   "Set volume on audio/raw streams",
49   "Andy Wingo <apwingo@eos.ncsu.edu>",
50 };
51
52
53 /* Filter signals and args */
54 enum {
55   /* FILL ME */
56   LAST_SIGNAL
57 };
58
59 enum {
60   ARG_0,
61   ARG_SILENT,
62   ARG_MUTE,
63   ARG_VOLUME
64 };
65
66 static GstStaticPadTemplate volume_sink_factory =
67 GST_STATIC_PAD_TEMPLATE (
68   "sink",
69   GST_PAD_SINK,
70   GST_PAD_ALWAYS,
71   GST_STATIC_CAPS (
72     "audio/x-raw-float, "
73       "rate = (int) [ 1, MAX ], "
74       "channels = (int) [ 1, MAX ], "
75       "endianness = (int) BYTE_ORDER, "
76       "width = (int) 32, "
77       "buffer-frames = (int) [ 1, MAX]; "
78     "audio/x-raw-int, "
79       "channels = (int) [ 1, MAX ], "
80       "rate = (int) [ 1,  MAX ], "
81       "endianness = (int) BYTE_ORDER, "
82       "width = (int) 16, "
83       "depth = (int) 16, "
84       "signed = (bool) TRUE"
85   )
86 );
87
88 static GstStaticPadTemplate volume_src_factory =
89 GST_STATIC_PAD_TEMPLATE (
90   "src",
91   GST_PAD_SRC,
92   GST_PAD_ALWAYS,
93   GST_STATIC_CAPS (
94     "audio/x-raw-float, "
95       "rate = (int) [ 1, MAX ], "
96       "channels = (int) [ 1, MAX ], "
97       "endianness = (int) BYTE_ORDER, "
98       "width = (int) 32, "
99       "buffer-frames = (int) [ 1, MAX]; "
100     "audio/x-raw-int, "
101       "channels = (int) [ 1, MAX ], "
102       "rate = (int) [ 1,  MAX ], "
103       "endianness = (int) BYTE_ORDER, "
104       "width = (int) 16, "
105       "depth = (int) 16, "
106       "signed = (bool) TRUE"
107   )
108 );
109
110 static void             volume_base_init        (gpointer g_class);
111 static void             volume_class_init       (GstVolumeClass *klass);
112 static void             volume_init             (GstVolume *filter);
113
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);
118
119 static gboolean         volume_parse_caps          (GstVolume *filter, GstStructure *structure);
120
121 static void             volume_chain_float         (GstPad *pad, GstData *_data);
122 static void             volume_chain_int16         (GstPad *pad, GstData *_data);
123
124 static void gst_volume_interface_init (GstImplementsInterfaceClass *klass);
125 static void gst_volume_mixer_init (GstMixerClass *iface);
126
127 static GstElementClass *parent_class = NULL;
128 /*static guint gst_filter_signals[LAST_SIGNAL] = { 0 }; */
129
130 static gboolean
131 gst_volume_interface_supported (GstImplementsInterface *iface, GType type)
132 {
133   g_assert (type == GST_TYPE_MIXER);
134   return TRUE;
135 }
136
137 static void
138 gst_volume_interface_init (GstImplementsInterfaceClass *klass)
139 {
140   klass->supported = gst_volume_interface_supported;
141 }
142
143 static const GList *
144 gst_volume_list_tracks (GstMixer *mixer)
145 {
146   GstVolume *filter = GST_VOLUME (mixer);
147   
148   g_return_val_if_fail (filter != NULL, NULL);
149   g_return_val_if_fail (GST_IS_VOLUME (filter), NULL);
150   
151   return filter->tracklist;
152 }
153
154 static void
155 gst_volume_set_volume (GstMixer *mixer, GstMixerTrack *track,
156                        gint *volumes)
157 {
158   GstVolume *filter = GST_VOLUME (mixer);
159   
160   g_return_if_fail (filter != NULL);
161   g_return_if_fail (GST_IS_VOLUME (filter));
162   
163   gst_dpman_bypass_dparam (filter->dpman, "volume");
164   
165   filter->volume_f       = (gfloat) volumes[0] / VOLUME_STEPS;
166   filter->volume_i       = filter->volume_f * VOLUME_UNITY_INT;
167   
168   if (filter->mute) {
169     filter->real_vol_f = 0.0;
170     filter->real_vol_i = 0;
171   }
172   else {
173     filter->real_vol_f = filter->volume_f;
174     filter->real_vol_i = filter->volume_i;
175   }
176 }
177
178 static void
179 gst_volume_get_volume (GstMixer *mixer, GstMixerTrack *track,
180                        gint *volumes)
181 {
182   GstVolume *filter = GST_VOLUME (mixer);
183   
184   g_return_if_fail (filter != NULL);
185   g_return_if_fail (GST_IS_VOLUME (filter));
186   
187   volumes[0] = (gint) filter->volume_f * VOLUME_STEPS;
188 }
189
190 static void
191 gst_volume_set_mute (GstMixer *mixer, GstMixerTrack *track,
192                      gboolean mute)
193 {
194   GstVolume *filter = GST_VOLUME (mixer);
195   
196   g_return_if_fail (filter != NULL);
197   g_return_if_fail (GST_IS_VOLUME (filter));
198   
199   gst_dpman_bypass_dparam (filter->dpman, "volume");
200   
201   filter->mute = mute;
202   
203   if (filter->mute) {
204     filter->real_vol_f = 0.0;
205     filter->real_vol_i = 0;
206   }
207   else {
208     filter->real_vol_f = filter->volume_f;
209     filter->real_vol_i = filter->volume_i;
210   }
211 }
212
213 static void
214 gst_volume_mixer_init (GstMixerClass *klass)
215 {
216   GST_MIXER_TYPE (klass) = GST_MIXER_SOFTWARE;
217   
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;
223 }
224
225 static void
226 gst_volume_dispose (GObject *object)
227 {
228   GstVolume *volume;
229
230   volume = GST_VOLUME (object);
231   
232   if (volume->tracklist)
233     {
234       if (volume->tracklist->data)
235         g_object_unref (volume->tracklist->data);
236       g_list_free (volume->tracklist);
237       volume->tracklist = NULL;
238     }
239     
240   G_OBJECT_CLASS (parent_class)->dispose (object);
241 }
242
243 static GstPadLinkReturn
244 volume_connect (GstPad *pad, const GstCaps *caps)
245 {
246   GstVolume *filter;
247   GstPad *otherpad;
248   gint rate;
249   GstPadLinkReturn link_ret;
250   GstStructure *structure;
251   
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);
255
256   structure = gst_caps_get_structure (caps, 0);
257   gst_structure_get_int (structure, "rate", &rate);
258   
259   link_ret = gst_pad_try_set_caps (otherpad, caps);
260   if (GST_PAD_LINK_FAILED (link_ret)){
261     return link_ret;
262   }
263
264   if (!volume_parse_caps (filter, structure))
265     return GST_PAD_LINK_REFUSED;
266
267   gst_dpman_set_rate(filter->dpman, rate);
268   
269   return GST_PAD_LINK_OK;
270 }
271
272 static gboolean
273 volume_parse_caps (GstVolume *filter, GstStructure *structure)
274 {
275   const gchar *mimetype;
276   
277   g_return_val_if_fail(filter!=NULL,FALSE);
278   g_return_val_if_fail(structure!=NULL,FALSE);
279   
280   mimetype = gst_structure_get_name (structure);
281   
282   if (strcmp(mimetype, "audio/x-raw-int")==0) {
283     gst_pad_set_chain_function(filter->sinkpad,volume_chain_int16);
284     return TRUE;
285   }
286   
287   if (strcmp(mimetype, "audio/x-raw-float")==0) {
288     gst_pad_set_chain_function(filter->sinkpad,volume_chain_float);
289     return TRUE;
290   }
291
292   return FALSE;
293 }
294
295
296 GType
297 gst_volume_get_type(void) {
298   static GType volume_type = 0;
299
300   if (!volume_type) {
301     static const GTypeInfo volume_info = {
302       sizeof(GstVolumeClass),
303       volume_base_init,
304       NULL,
305       (GClassInitFunc)volume_class_init,
306       NULL,
307       NULL,
308       sizeof(GstVolume),
309       0,
310       (GInstanceInitFunc)volume_init
311     };
312     static const GInterfaceInfo voliface_info = {
313       (GInterfaceInitFunc) gst_volume_interface_init,
314       NULL,
315       NULL
316     };
317     static const GInterfaceInfo volmixer_info = {
318       (GInterfaceInitFunc) gst_volume_mixer_init,
319       NULL,
320       NULL
321     };
322     
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,
326                                  &voliface_info);
327     g_type_add_interface_static (volume_type,
328                                  GST_TYPE_MIXER,
329                                  &volmixer_info);
330   }
331   return volume_type;
332 }
333 static void
334 volume_base_init (gpointer g_class)
335 {
336   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
337   
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);
343 }
344 static void
345 volume_class_init (GstVolumeClass *klass)
346 {
347   GObjectClass *gobject_class;
348   GstElementClass *gstelement_class;
349
350   gobject_class = (GObjectClass*)klass;
351   gstelement_class = (GstElementClass*)klass;
352
353   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
354
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));
358   
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));
362   
363   gobject_class->set_property = volume_set_property;
364   gobject_class->get_property = volume_get_property;
365   gobject_class->dispose = gst_volume_dispose;
366 }
367
368 static void
369 volume_init (GstVolume *filter)
370 {
371   GstMixerTrack *track = NULL;
372   
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);
377   
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);
382   
383   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
384   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
385   
386   gst_pad_set_chain_function (filter->sinkpad, volume_chain_int16);
387   
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;
394
395   filter->dpman = gst_dpman_new ("volume_dpman", GST_ELEMENT(filter));
396   gst_dpman_add_required_dparam_callback (
397     filter->dpman, 
398     g_param_spec_int("mute","Mute","Mute the audio",
399                      0, 1, 0, G_PARAM_READWRITE),
400     "int",
401     volume_update_mute, 
402     filter
403   );
404   gst_dpman_add_required_dparam_callback (
405     filter->dpman, 
406     g_param_spec_float("volume","Volume","Volume of the audio",
407                        0.0, VOLUME_MAX_FLOAT, 1.0, G_PARAM_READWRITE),
408     "scalar",
409     volume_update_volume, 
410     filter
411   );
412   
413   track = g_object_new (GST_TYPE_MIXER_TRACK, NULL);
414   
415   if (GST_IS_MIXER_TRACK (track))
416     {
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);
423     }
424 }
425
426 static void
427 volume_chain_float (GstPad *pad, GstData *_data)
428 {
429   GstBuffer *buf = GST_BUFFER (_data);
430   GstVolume *filter;
431   GstBuffer *out_buf;
432   gfloat *data;
433   gint i, num_samples;
434
435   g_return_if_fail(GST_IS_PAD(pad));
436   g_return_if_fail(buf != NULL);
437   
438   filter = GST_VOLUME(GST_OBJECT_PARENT (pad));
439   g_return_if_fail(GST_IS_VOLUME(filter));
440
441   out_buf = gst_buffer_copy_on_write (buf);
442
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));
446   i = 0;
447     
448   while(GST_DPMAN_PROCESS(filter->dpman, i)) {
449     data[i++] *= filter->real_vol_f;
450   }
451   
452   gst_pad_push(filter->srcpad,GST_DATA (out_buf));
453   
454 }
455
456 static void
457 volume_chain_int16 (GstPad *pad, GstData *_data)
458 {
459   GstBuffer *buf = GST_BUFFER (_data);
460   GstVolume *filter;
461   GstBuffer *out_buf;
462   gint16 *data;
463   gint i, num_samples;
464
465   g_return_if_fail(GST_IS_PAD(pad));
466   g_return_if_fail(buf != NULL);
467   
468   filter = GST_VOLUME(GST_OBJECT_PARENT (pad));
469   g_return_if_fail(GST_IS_VOLUME(filter));
470
471   out_buf = gst_buffer_copy_on_write (buf);
472
473   data = (gint16 *) GST_BUFFER_DATA (out_buf);
474   g_assert (data);
475   num_samples = GST_BUFFER_SIZE(out_buf)/sizeof(gint16);
476   GST_DPMAN_PREPROCESS(filter->dpman, num_samples, GST_BUFFER_TIMESTAMP(out_buf));
477   i = 0;
478  
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);
485         i++;
486       }
487     }
488     else {
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);
492         i++;
493       }
494     }
495   }
496
497   gst_pad_push(filter->srcpad,GST_DATA (out_buf));   
498   
499 }
500
501 static void
502 volume_update_mute(const GValue *value, gpointer data)
503 {
504   GstVolume *filter = (GstVolume*)data;
505   g_return_if_fail(GST_IS_VOLUME(filter));
506
507   if (G_VALUE_HOLDS_BOOLEAN(value)){
508     filter->mute = g_value_get_boolean(value);
509   }
510   else if (G_VALUE_HOLDS_INT(value)){
511     filter->mute = (g_value_get_int(value) == 1);
512   }
513   
514   if (filter->mute){
515     filter->real_vol_f = 0.0;
516     filter->real_vol_i = 0;
517   }
518   else {
519     filter->real_vol_f = filter->volume_f;
520     filter->real_vol_i = filter->volume_i;
521   }
522 }
523
524 static void
525 volume_update_volume(const GValue *value, gpointer data)
526 {
527   GstVolume *filter = (GstVolume*)data;
528   g_return_if_fail(GST_IS_VOLUME(filter));
529
530   filter->volume_f       = g_value_get_float (value);
531   filter->volume_i       = filter->volume_f*VOLUME_UNITY_INT;
532   if (filter->mute){
533     filter->real_vol_f = 0.0;
534     filter->real_vol_i = 0;
535   }
536   else {
537     filter->real_vol_f = filter->volume_f;
538     filter->real_vol_i = filter->volume_i;
539   }
540 }
541
542 static void
543 volume_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
544 {
545   GstVolume *filter;
546
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);
550
551   switch (prop_id) 
552   {
553   case ARG_MUTE:
554     gst_dpman_bypass_dparam(filter->dpman, "mute");
555     volume_update_mute(value, filter);
556     break;
557   case ARG_VOLUME:
558     gst_dpman_bypass_dparam(filter->dpman, "volume");
559     volume_update_volume(value, filter);
560     break;
561   default:
562     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
563     break;
564   }
565 }
566
567 static void
568 volume_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
569 {
570   GstVolume *filter;
571
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);
575   
576   switch (prop_id) {
577   case ARG_MUTE:
578     g_value_set_boolean (value, filter->mute);
579     break;
580   case ARG_VOLUME:
581     g_value_set_float (value, filter->volume_f);
582     break;
583   default:
584     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
585     break;
586   }
587 }
588
589 static gboolean
590 plugin_init (GstPlugin *plugin)
591 {
592   gst_control_init(NULL,NULL);
593   
594   return gst_element_register (plugin, "volume", GST_RANK_PRIMARY, GST_TYPE_VOLUME);
595 }
596
597 GST_PLUGIN_DEFINE (
598   GST_VERSION_MAJOR,
599   GST_VERSION_MINOR,
600   "volume",
601   "element for controlling audio volume",
602   plugin_init,
603   VERSION,
604   GST_LICENSE,
605   GST_PACKAGE,
606   GST_ORIGIN
607 )