• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 /**
23  * SECTION:element-audioinvert
24  * @title: audioinvert
25  *
26  * Swaps upper and lower half of audio samples. Mixing an inverted sample on top of
27  * the original with a slight delay can produce effects that sound like resonance.
28  * Creating a stereo sample from a mono source, with one channel inverted produces wide-stereo sounds.
29  *
30  * ## Example launch line
31  * |[
32  * gst-launch-1.0 audiotestsrc wave=saw ! audioinvert degree=0.4 ! alsasink
33  * gst-launch-1.0 filesrc location="melo1.ogg" ! oggdemux ! vorbisdec ! audioconvert ! audioinvert degree=0.4 ! alsasink
34  * gst-launch-1.0 audiotestsrc wave=saw ! audioconvert ! audioinvert degree=0.4 ! audioconvert ! alsasink
35  * ]|
36  *
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     " layout=(string) {interleaved, non-interleaved}"
72 
73 G_DEFINE_TYPE (GstAudioInvert, gst_audio_invert, GST_TYPE_AUDIO_FILTER);
74 GST_ELEMENT_REGISTER_DEFINE (audioinvert, "audioinvert",
75     GST_RANK_NONE, GST_TYPE_AUDIO_INVERT);
76 
77 static void gst_audio_invert_set_property (GObject * object, guint prop_id,
78     const GValue * value, GParamSpec * pspec);
79 static void gst_audio_invert_get_property (GObject * object, guint prop_id,
80     GValue * value, GParamSpec * pspec);
81 
82 static gboolean gst_audio_invert_setup (GstAudioFilter * filter,
83     const GstAudioInfo * info);
84 static GstFlowReturn gst_audio_invert_transform_ip (GstBaseTransform * base,
85     GstBuffer * buf);
86 
87 static void gst_audio_invert_transform_int (GstAudioInvert * filter,
88     gint16 * data, guint num_samples);
89 static void gst_audio_invert_transform_float (GstAudioInvert * filter,
90     gfloat * data, guint num_samples);
91 
92 /* GObject vmethod implementations */
93 
94 static void
gst_audio_invert_class_init(GstAudioInvertClass * klass)95 gst_audio_invert_class_init (GstAudioInvertClass * klass)
96 {
97   GObjectClass *gobject_class;
98   GstElementClass *gstelement_class;
99   GstCaps *caps;
100 
101   GST_DEBUG_CATEGORY_INIT (gst_audio_invert_debug, "audioinvert", 0,
102       "audioinvert element");
103 
104   gobject_class = (GObjectClass *) klass;
105   gstelement_class = (GstElementClass *) klass;
106 
107   gobject_class->set_property = gst_audio_invert_set_property;
108   gobject_class->get_property = gst_audio_invert_get_property;
109 
110   g_object_class_install_property (gobject_class, PROP_DEGREE,
111       g_param_spec_float ("degree", "Degree",
112           "Degree of inversion", 0.0, 1.0,
113           0.0,
114           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
115 
116   gst_element_class_set_static_metadata (gstelement_class, "Audio inversion",
117       "Filter/Effect/Audio",
118       "Swaps upper and lower half of audio samples",
119       "Sebastian Dröge <slomo@circular-chaos.org>");
120 
121   caps = gst_caps_from_string (ALLOWED_CAPS);
122   gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass),
123       caps);
124   gst_caps_unref (caps);
125 
126   GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
127       GST_DEBUG_FUNCPTR (gst_audio_invert_transform_ip);
128   GST_BASE_TRANSFORM_CLASS (klass)->transform_ip_on_passthrough = FALSE;
129 
130   GST_AUDIO_FILTER_CLASS (klass)->setup =
131       GST_DEBUG_FUNCPTR (gst_audio_invert_setup);
132 }
133 
134 static void
gst_audio_invert_init(GstAudioInvert * filter)135 gst_audio_invert_init (GstAudioInvert * filter)
136 {
137   filter->degree = 0.0;
138   gst_base_transform_set_in_place (GST_BASE_TRANSFORM (filter), TRUE);
139   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (filter), TRUE);
140 }
141 
142 static void
gst_audio_invert_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)143 gst_audio_invert_set_property (GObject * object, guint prop_id,
144     const GValue * value, GParamSpec * pspec)
145 {
146   GstAudioInvert *filter = GST_AUDIO_INVERT (object);
147 
148   switch (prop_id) {
149     case PROP_DEGREE:
150       filter->degree = g_value_get_float (value);
151       gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (filter),
152           filter->degree == 0.0);
153       break;
154     default:
155       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
156       break;
157   }
158 }
159 
160 static void
gst_audio_invert_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)161 gst_audio_invert_get_property (GObject * object, guint prop_id,
162     GValue * value, GParamSpec * pspec)
163 {
164   GstAudioInvert *filter = GST_AUDIO_INVERT (object);
165 
166   switch (prop_id) {
167     case PROP_DEGREE:
168       g_value_set_float (value, filter->degree);
169       break;
170     default:
171       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
172       break;
173   }
174 }
175 
176 /* GstAudioFilter vmethod implementations */
177 
178 static gboolean
gst_audio_invert_setup(GstAudioFilter * base,const GstAudioInfo * info)179 gst_audio_invert_setup (GstAudioFilter * base, const GstAudioInfo * info)
180 {
181   GstAudioInvert *filter = GST_AUDIO_INVERT (base);
182   gboolean ret = TRUE;
183 
184   switch (GST_AUDIO_INFO_FORMAT (info)) {
185     case GST_AUDIO_FORMAT_S16:
186       filter->process = (GstAudioInvertProcessFunc)
187           gst_audio_invert_transform_int;
188       break;
189     case GST_AUDIO_FORMAT_F32:
190       filter->process = (GstAudioInvertProcessFunc)
191           gst_audio_invert_transform_float;
192       break;
193     default:
194       ret = FALSE;
195       break;
196   }
197   return ret;
198 }
199 
200 static void
gst_audio_invert_transform_int(GstAudioInvert * filter,gint16 * data,guint num_samples)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
gst_audio_invert_transform_float(GstAudioInvert * filter,gfloat * data,guint num_samples)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
gst_audio_invert_transform_ip(GstBaseTransform * base,GstBuffer * buf)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   GstMapInfo map;
236 
237   timestamp = GST_BUFFER_TIMESTAMP (buf);
238   stream_time =
239       gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
240 
241   GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
242       GST_TIME_ARGS (timestamp));
243 
244   if (GST_CLOCK_TIME_IS_VALID (stream_time))
245     gst_object_sync_values (GST_OBJECT (filter), stream_time);
246 
247   if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP)))
248     return GST_FLOW_OK;
249 
250   gst_buffer_map (buf, &map, GST_MAP_READWRITE);
251   num_samples = map.size / GST_AUDIO_FILTER_BPS (filter);
252 
253   filter->process (filter, map.data, num_samples);
254 
255   gst_buffer_unmap (buf, &map);
256 
257   return GST_FLOW_OK;
258 }
259