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