Merge remote-tracking branch 'origin/master' into 0.11
[platform/upstream/gst-plugins-good.git] / gst / audiofx / audioinvert.c
1 /* 
2  * GStreamer
3  * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
4  * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /**
23  * SECTION:element-audioinvert
24  *
25  * Swaps upper and lower half of audio samples. Mixing an inverted sample on top of
26  * the original with a slight delay can produce effects that sound like resonance.
27  * Creating a stereo sample from a mono source, with one channel inverted produces wide-stereo sounds.
28  *
29  * <refsect2>
30  * <title>Example launch line</title>
31  * |[
32  * gst-launch audiotestsrc wave=saw ! audioinvert invert=0.4 ! alsasink
33  * gst-launch filesrc location="melo1.ogg" ! oggdemux ! vorbisdec ! audioconvert ! audioinvert invert=0.4 ! alsasink
34  * gst-launch audiotestsrc wave=saw ! audioconvert ! audioinvert invert=0.4 ! audioconvert ! alsasink
35  * ]|
36  * </refsect2>
37  */
38
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42
43 #include <gst/gst.h>
44 #include <gst/base/gstbasetransform.h>
45 #include <gst/audio/audio.h>
46 #include <gst/audio/gstaudiofilter.h>
47
48 #include "audioinvert.h"
49
50 #define GST_CAT_DEFAULT gst_audio_invert_debug
51 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
52
53 /* Filter signals and args */
54 enum
55 {
56   /* FILL ME */
57   LAST_SIGNAL
58 };
59
60 enum
61 {
62   PROP_0,
63   PROP_DEGREE
64 };
65
66 #define ALLOWED_CAPS \
67     "audio/x-raw,"                                                     \
68     " format=(string) {"GST_AUDIO_NE(S16)","GST_AUDIO_NE(F32)"},"  \
69     " rate=(int)[1,MAX],"                                              \
70     " channels=(int)[1,MAX] "
71
72 G_DEFINE_TYPE (GstAudioInvert, gst_audio_invert, GST_TYPE_AUDIO_FILTER);
73
74 static void gst_audio_invert_set_property (GObject * object, guint prop_id,
75     const GValue * value, GParamSpec * pspec);
76 static void gst_audio_invert_get_property (GObject * object, guint prop_id,
77     GValue * value, GParamSpec * pspec);
78
79 static gboolean gst_audio_invert_setup (GstAudioFilter * filter,
80     const GstAudioInfo * info);
81 static GstFlowReturn gst_audio_invert_transform_ip (GstBaseTransform * base,
82     GstBuffer * buf);
83
84 static void gst_audio_invert_transform_int (GstAudioInvert * filter,
85     gint16 * data, guint num_samples);
86 static void gst_audio_invert_transform_float (GstAudioInvert * filter,
87     gfloat * data, guint num_samples);
88
89 /* GObject vmethod implementations */
90
91 static void
92 gst_audio_invert_class_init (GstAudioInvertClass * klass)
93 {
94   GObjectClass *gobject_class;
95   GstElementClass *gstelement_class;
96   GstCaps *caps;
97
98   GST_DEBUG_CATEGORY_INIT (gst_audio_invert_debug, "audioinvert", 0,
99       "audioinvert element");
100
101   gobject_class = (GObjectClass *) klass;
102   gstelement_class = (GstElementClass *) klass;
103
104   gobject_class->set_property = gst_audio_invert_set_property;
105   gobject_class->get_property = gst_audio_invert_get_property;
106
107   g_object_class_install_property (gobject_class, PROP_DEGREE,
108       g_param_spec_float ("degree", "Degree",
109           "Degree of inversion", 0.0, 1.0,
110           0.0,
111           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
112
113   gst_element_class_set_details_simple (gstelement_class, "Audio inversion",
114       "Filter/Effect/Audio",
115       "Swaps upper and lower half of audio samples",
116       "Sebastian Dröge <slomo@circular-chaos.org>");
117
118   caps = gst_caps_from_string (ALLOWED_CAPS);
119   gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass),
120       caps);
121   gst_caps_unref (caps);
122
123   GST_AUDIO_FILTER_CLASS (klass)->setup =
124       GST_DEBUG_FUNCPTR (gst_audio_invert_setup);
125   GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
126       GST_DEBUG_FUNCPTR (gst_audio_invert_transform_ip);
127 }
128
129 static void
130 gst_audio_invert_init (GstAudioInvert * filter)
131 {
132   filter->degree = 0.0;
133   gst_base_transform_set_in_place (GST_BASE_TRANSFORM (filter), TRUE);
134   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (filter), TRUE);
135 }
136
137 static void
138 gst_audio_invert_set_property (GObject * object, guint prop_id,
139     const GValue * value, GParamSpec * pspec)
140 {
141   GstAudioInvert *filter = GST_AUDIO_INVERT (object);
142
143   switch (prop_id) {
144     case PROP_DEGREE:
145       filter->degree = g_value_get_float (value);
146       gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (filter),
147           filter->degree == 0.0);
148       break;
149     default:
150       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
151       break;
152   }
153 }
154
155 static void
156 gst_audio_invert_get_property (GObject * object, guint prop_id,
157     GValue * value, GParamSpec * pspec)
158 {
159   GstAudioInvert *filter = GST_AUDIO_INVERT (object);
160
161   switch (prop_id) {
162     case PROP_DEGREE:
163       g_value_set_float (value, filter->degree);
164       break;
165     default:
166       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
167       break;
168   }
169 }
170
171 /* GstAudioFilter vmethod implementations */
172
173 static gboolean
174 gst_audio_invert_setup (GstAudioFilter * base, const GstAudioInfo * info)
175 {
176   GstAudioInvert *filter = GST_AUDIO_INVERT (base);
177   gboolean ret = TRUE;
178
179   switch (GST_AUDIO_INFO_FORMAT (info)) {
180     case GST_AUDIO_FORMAT_S16:
181       filter->process = (GstAudioInvertProcessFunc)
182           gst_audio_invert_transform_int;
183       break;
184     case GST_AUDIO_FORMAT_F32:
185       filter->process = (GstAudioInvertProcessFunc)
186           gst_audio_invert_transform_float;
187       break;
188     default:
189       ret = FALSE;
190       break;
191   }
192   return ret;
193 }
194
195 static void
196 gst_audio_invert_transform_int (GstAudioInvert * filter,
197     gint16 * data, guint num_samples)
198 {
199   gint i;
200   gfloat dry = 1.0 - filter->degree;
201   glong val;
202
203   for (i = 0; i < num_samples; i++) {
204     val = (*data) * dry + (-1 - (*data)) * filter->degree;
205     *data++ = (gint16) CLAMP (val, G_MININT16, G_MAXINT16);
206   }
207 }
208
209 static void
210 gst_audio_invert_transform_float (GstAudioInvert * filter,
211     gfloat * data, guint num_samples)
212 {
213   gint i;
214   gfloat dry = 1.0 - filter->degree;
215   glong val;
216
217   for (i = 0; i < num_samples; i++) {
218     val = (*data) * dry - (*data) * filter->degree;
219     *data++ = val;
220   }
221 }
222
223 /* GstBaseTransform vmethod implementations */
224 static GstFlowReturn
225 gst_audio_invert_transform_ip (GstBaseTransform * base, GstBuffer * buf)
226 {
227   GstAudioInvert *filter = GST_AUDIO_INVERT (base);
228   guint num_samples;
229   GstClockTime timestamp, stream_time;
230   guint8 *data;
231   gsize size;
232
233   timestamp = GST_BUFFER_TIMESTAMP (buf);
234   stream_time =
235       gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
236
237   GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
238       GST_TIME_ARGS (timestamp));
239
240   if (GST_CLOCK_TIME_IS_VALID (stream_time))
241     gst_object_sync_values (GST_OBJECT (filter), stream_time);
242
243   if (gst_base_transform_is_passthrough (base) ||
244       G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP)))
245     return GST_FLOW_OK;
246
247   data = gst_buffer_map (buf, &size, NULL, GST_MAP_READWRITE);
248   num_samples = size / GST_AUDIO_FILTER_BPS (filter);
249
250   filter->process (filter, data, num_samples);
251
252   gst_buffer_unmap (buf, data, size);
253
254   return GST_FLOW_OK;
255 }