Merge branch '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 #include <gst/controller/gstcontroller.h>
48
49 #include "audioinvert.h"
50
51 #define GST_CAT_DEFAULT gst_audio_invert_debug
52 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
53
54 /* Filter signals and args */
55 enum
56 {
57   /* FILL ME */
58   LAST_SIGNAL
59 };
60
61 enum
62 {
63   PROP_0,
64   PROP_DEGREE
65 };
66
67 #define ALLOWED_CAPS \
68     "audio/x-raw-int,"                                                \
69     " depth=(int)16,"                                                 \
70     " width=(int)16,"                                                 \
71     " endianness=(int)BYTE_ORDER,"                                    \
72     " signed=(bool)TRUE,"                                             \
73     " rate=(int)[1,MAX],"                                             \
74     " channels=(int)[1,MAX]; "                                        \
75     "audio/x-raw-float,"                                              \
76     " width=(int)32,"                                                 \
77     " endianness=(int)BYTE_ORDER,"                                    \
78     " rate=(int)[1,MAX],"                                             \
79     " channels=(int)[1,MAX]"
80
81 G_DEFINE_TYPE (GstAudioInvert, gst_audio_invert, GST_TYPE_AUDIO_FILTER);
82
83 static void gst_audio_invert_set_property (GObject * object, guint prop_id,
84     const GValue * value, GParamSpec * pspec);
85 static void gst_audio_invert_get_property (GObject * object, guint prop_id,
86     GValue * value, GParamSpec * pspec);
87
88 static gboolean gst_audio_invert_setup (GstAudioFilter * filter,
89     GstRingBufferSpec * format);
90 static GstFlowReturn gst_audio_invert_transform_ip (GstBaseTransform * base,
91     GstBuffer * buf);
92
93 static void gst_audio_invert_transform_int (GstAudioInvert * filter,
94     gint16 * data, guint num_samples);
95 static void gst_audio_invert_transform_float (GstAudioInvert * filter,
96     gfloat * data, guint num_samples);
97
98 /* GObject vmethod implementations */
99
100 static void
101 gst_audio_invert_class_init (GstAudioInvertClass * klass)
102 {
103   GObjectClass *gobject_class;
104   GstElementClass *gstelement_class;
105   GstCaps *caps;
106
107   GST_DEBUG_CATEGORY_INIT (gst_audio_invert_debug, "audioinvert", 0,
108       "audioinvert element");
109
110   gobject_class = (GObjectClass *) klass;
111   gstelement_class = (GstElementClass *) klass;
112
113   gobject_class->set_property = gst_audio_invert_set_property;
114   gobject_class->get_property = gst_audio_invert_get_property;
115
116   g_object_class_install_property (gobject_class, PROP_DEGREE,
117       g_param_spec_float ("degree", "Degree",
118           "Degree of inversion", 0.0, 1.0,
119           0.0,
120           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
121
122   gst_element_class_set_details_simple (gstelement_class, "Audio inversion",
123       "Filter/Effect/Audio",
124       "Swaps upper and lower half of audio samples",
125       "Sebastian Dröge <slomo@circular-chaos.org>");
126
127   caps = gst_caps_from_string (ALLOWED_CAPS);
128   gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass),
129       caps);
130   gst_caps_unref (caps);
131
132   GST_AUDIO_FILTER_CLASS (klass)->setup =
133       GST_DEBUG_FUNCPTR (gst_audio_invert_setup);
134   GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
135       GST_DEBUG_FUNCPTR (gst_audio_invert_transform_ip);
136 }
137
138 static void
139 gst_audio_invert_init (GstAudioInvert * filter)
140 {
141   filter->degree = 0.0;
142   gst_base_transform_set_in_place (GST_BASE_TRANSFORM (filter), TRUE);
143   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (filter), TRUE);
144 }
145
146 static void
147 gst_audio_invert_set_property (GObject * object, guint prop_id,
148     const GValue * value, GParamSpec * pspec)
149 {
150   GstAudioInvert *filter = GST_AUDIO_INVERT (object);
151
152   switch (prop_id) {
153     case PROP_DEGREE:
154       filter->degree = g_value_get_float (value);
155       gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (filter),
156           filter->degree == 0.0);
157       break;
158     default:
159       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160       break;
161   }
162 }
163
164 static void
165 gst_audio_invert_get_property (GObject * object, guint prop_id,
166     GValue * value, GParamSpec * pspec)
167 {
168   GstAudioInvert *filter = GST_AUDIO_INVERT (object);
169
170   switch (prop_id) {
171     case PROP_DEGREE:
172       g_value_set_float (value, filter->degree);
173       break;
174     default:
175       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
176       break;
177   }
178 }
179
180 /* GstAudioFilter vmethod implementations */
181
182 static gboolean
183 gst_audio_invert_setup (GstAudioFilter * base, GstRingBufferSpec * format)
184 {
185   GstAudioInvert *filter = GST_AUDIO_INVERT (base);
186   gboolean ret = TRUE;
187
188   if (format->type == GST_BUFTYPE_FLOAT && format->width == 32)
189     filter->process = (GstAudioInvertProcessFunc)
190         gst_audio_invert_transform_float;
191   else if (format->type == GST_BUFTYPE_LINEAR && format->width == 16)
192     filter->process = (GstAudioInvertProcessFunc)
193         gst_audio_invert_transform_int;
194   else
195     ret = FALSE;
196
197   return ret;
198 }
199
200 static void
201 gst_audio_invert_transform_int (GstAudioInvert * filter,
202     gint16 * data, guint num_samples)
203 {
204   gint i;
205   gfloat dry = 1.0 - filter->degree;
206   glong val;
207
208   for (i = 0; i < num_samples; i++) {
209     val = (*data) * dry + (-1 - (*data)) * filter->degree;
210     *data++ = (gint16) CLAMP (val, G_MININT16, G_MAXINT16);
211   }
212 }
213
214 static void
215 gst_audio_invert_transform_float (GstAudioInvert * filter,
216     gfloat * data, guint num_samples)
217 {
218   gint i;
219   gfloat dry = 1.0 - filter->degree;
220   glong val;
221
222   for (i = 0; i < num_samples; i++) {
223     val = (*data) * dry - (*data) * filter->degree;
224     *data++ = val;
225   }
226 }
227
228 /* GstBaseTransform vmethod implementations */
229 static GstFlowReturn
230 gst_audio_invert_transform_ip (GstBaseTransform * base, GstBuffer * buf)
231 {
232   GstAudioInvert *filter = GST_AUDIO_INVERT (base);
233   guint num_samples;
234   GstClockTime timestamp, stream_time;
235   guint8 *data;
236   gsize size;
237
238   timestamp = GST_BUFFER_TIMESTAMP (buf);
239   stream_time =
240       gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
241
242   GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
243       GST_TIME_ARGS (timestamp));
244
245   if (GST_CLOCK_TIME_IS_VALID (stream_time))
246     gst_object_sync_values (G_OBJECT (filter), stream_time);
247
248   if (gst_base_transform_is_passthrough (base) ||
249       G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP)))
250     return GST_FLOW_OK;
251
252   data = gst_buffer_map (buf, &size, NULL, GST_MAP_READWRITE);
253   num_samples = size / (GST_AUDIO_FILTER (filter)->format.width / 8);
254
255   filter->process (filter, data, num_samples);
256
257   gst_buffer_unmap (buf, data, size);
258
259   return GST_FLOW_OK;
260 }