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