Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / gst / audiofx / audiopanorama.c
1 /*
2  * GStreamer
3  * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
4  * Copyright (C) 2006 Sebastian Dröge <slomo@circular-chaos.org>
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-audiopanorama
24  *
25  * Stereo panorama effect with controllable pan position. One can choose between the default psychoacoustic panning method,
26  * which keeps the same perceived loudness, and a simple panning method that just controls the volume on one channel.
27  *
28  * <refsect2>
29  * <title>Example launch line</title>
30  * |[
31  * gst-launch audiotestsrc wave=saw ! audiopanorama panorama=-1.00 ! alsasink
32  * gst-launch filesrc location="melo1.ogg" ! oggdemux ! vorbisdec ! audioconvert ! audiopanorama panorama=-1.00 ! alsasink
33  * gst-launch audiotestsrc wave=saw ! audioconvert ! audiopanorama panorama=-1.00 ! audioconvert ! alsasink
34  * gst-launch audiotestsrc wave=saw ! audioconvert ! audiopanorama method=simple panorama=-0.50 ! 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/controller/gstcontroller.h>
46
47 #include "audiopanorama.h"
48
49 #define GST_CAT_DEFAULT gst_audio_panorama_debug
50 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
51
52 /* Filter signals and args */
53 enum
54 {
55   /* FILL ME */
56   LAST_SIGNAL
57 };
58
59 enum
60 {
61   PROP_0,
62   PROP_PANORAMA,
63   PROP_METHOD
64 };
65
66 enum
67 {
68   METHOD_PSYCHOACOUSTIC = 0,
69   METHOD_SIMPLE,
70   NUM_METHODS
71 };
72
73 #define GST_TYPE_AUDIO_PANORAMA_METHOD (gst_audio_panorama_method_get_type ())
74 static GType
75 gst_audio_panorama_method_get_type (void)
76 {
77   static GType gtype = 0;
78
79   if (gtype == 0) {
80     static const GEnumValue values[] = {
81       {METHOD_PSYCHOACOUSTIC, "Psychoacoustic Panning (default)",
82           "psychoacoustic"},
83       {METHOD_SIMPLE, "Simple Panning", "simple"},
84       {0, NULL, NULL}
85     };
86
87     gtype = g_enum_register_static ("GstAudioPanoramaMethod", values);
88   }
89   return gtype;
90 }
91
92 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
93     GST_PAD_SINK,
94     GST_PAD_ALWAYS,
95     GST_STATIC_CAPS ("audio/x-raw, "
96         "format = (string) { " GST_AUDIO_NE (S32) ", " GST_AUDIO_NE (S16) "}, "
97         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]")
98     );
99
100 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
101     GST_PAD_SRC,
102     GST_PAD_ALWAYS,
103     GST_STATIC_CAPS ("audio/x-raw, "
104         "format = (string) { " GST_AUDIO_NE (S32) ", " GST_AUDIO_NE (S16) "}, "
105         "rate = (int) [ 1, MAX ], " "channels = (int) 2")
106     );
107
108 G_DEFINE_TYPE (GstAudioPanorama, gst_audio_panorama, GST_TYPE_BASE_TRANSFORM);
109
110 static void gst_audio_panorama_set_property (GObject * object, guint prop_id,
111     const GValue * value, GParamSpec * pspec);
112 static void gst_audio_panorama_get_property (GObject * object, guint prop_id,
113     GValue * value, GParamSpec * pspec);
114
115 static gboolean gst_audio_panorama_get_unit_size (GstBaseTransform * base,
116     GstCaps * caps, gsize * size);
117 static GstCaps *gst_audio_panorama_transform_caps (GstBaseTransform * base,
118     GstPadDirection direction, GstCaps * caps, GstCaps * filter);
119 static gboolean gst_audio_panorama_set_caps (GstBaseTransform * base,
120     GstCaps * incaps, GstCaps * outcaps);
121
122 static void gst_audio_panorama_transform_m2s_int (GstAudioPanorama * filter,
123     gint16 * idata, gint16 * odata, guint num_samples);
124 static void gst_audio_panorama_transform_s2s_int (GstAudioPanorama * filter,
125     gint16 * idata, gint16 * odata, guint num_samples);
126 static void gst_audio_panorama_transform_m2s_float (GstAudioPanorama * filter,
127     gfloat * idata, gfloat * odata, guint num_samples);
128 static void gst_audio_panorama_transform_s2s_float (GstAudioPanorama * filter,
129     gfloat * idata, gfloat * odata, guint num_samples);
130
131 static void gst_audio_panorama_transform_m2s_int_simple (GstAudioPanorama *
132     filter, gint16 * idata, gint16 * odata, guint num_samples);
133 static void gst_audio_panorama_transform_s2s_int_simple (GstAudioPanorama *
134     filter, gint16 * idata, gint16 * odata, guint num_samples);
135 static void gst_audio_panorama_transform_m2s_float_simple (GstAudioPanorama *
136     filter, gfloat * idata, gfloat * odata, guint num_samples);
137 static void gst_audio_panorama_transform_s2s_float_simple (GstAudioPanorama *
138     filter, gfloat * idata, gfloat * odata, guint num_samples);
139
140 static GstFlowReturn gst_audio_panorama_transform (GstBaseTransform * base,
141     GstBuffer * inbuf, GstBuffer * outbuf);
142
143
144 /* Table with processing functions: [channels][format][method] */
145 static GstAudioPanoramaProcessFunc panorama_process_functions[2][2][2] = {
146   {
147         {(GstAudioPanoramaProcessFunc) gst_audio_panorama_transform_m2s_int,
148               (GstAudioPanoramaProcessFunc)
149             gst_audio_panorama_transform_m2s_int_simple},
150         {(GstAudioPanoramaProcessFunc) gst_audio_panorama_transform_m2s_float,
151               (GstAudioPanoramaProcessFunc)
152             gst_audio_panorama_transform_m2s_float_simple}
153       },
154   {
155         {(GstAudioPanoramaProcessFunc) gst_audio_panorama_transform_s2s_int,
156               (GstAudioPanoramaProcessFunc)
157             gst_audio_panorama_transform_s2s_int_simple},
158         {(GstAudioPanoramaProcessFunc) gst_audio_panorama_transform_s2s_float,
159               (GstAudioPanoramaProcessFunc)
160             gst_audio_panorama_transform_s2s_float_simple}
161       }
162 };
163
164 /* GObject vmethod implementations */
165
166 static void
167 gst_audio_panorama_class_init (GstAudioPanoramaClass * klass)
168 {
169   GObjectClass *gobject_class;
170   GstElementClass *gstelement_class;
171
172   GST_DEBUG_CATEGORY_INIT (gst_audio_panorama_debug, "audiopanorama", 0,
173       "audiopanorama element");
174
175   gobject_class = (GObjectClass *) klass;
176   gstelement_class = (GstElementClass *) klass;
177
178   gobject_class->set_property = gst_audio_panorama_set_property;
179   gobject_class->get_property = gst_audio_panorama_get_property;
180
181   g_object_class_install_property (gobject_class, PROP_PANORAMA,
182       g_param_spec_float ("panorama", "Panorama",
183           "Position in stereo panorama (-1.0 left -> 1.0 right)", -1.0, 1.0,
184           0.0,
185           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
186   /**
187    * GstAudioPanorama:method
188    *
189    * Panning method: psychoacoustic mode keeps the same perceived loudness,
190    * while simple mode just controls the volume of one channel. It's merely
191    * a matter of taste which method should be chosen. 
192    *
193    * Since: 0.10.6
194    **/
195   g_object_class_install_property (gobject_class, PROP_METHOD,
196       g_param_spec_enum ("method", "Panning method",
197           "Psychoacoustic mode keeps same perceived loudness, "
198           "simple mode just controls volume of one channel.",
199           GST_TYPE_AUDIO_PANORAMA_METHOD, METHOD_PSYCHOACOUSTIC,
200           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
201
202   gst_element_class_set_details_simple (gstelement_class, "Stereo positioning",
203       "Filter/Effect/Audio",
204       "Positions audio streams in the stereo panorama",
205       "Stefan Kost <ensonic@users.sf.net>");
206
207   gst_element_class_add_pad_template (gstelement_class,
208       gst_static_pad_template_get (&src_template));
209   gst_element_class_add_pad_template (gstelement_class,
210       gst_static_pad_template_get (&sink_template));
211
212   GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size =
213       GST_DEBUG_FUNCPTR (gst_audio_panorama_get_unit_size);
214   GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
215       GST_DEBUG_FUNCPTR (gst_audio_panorama_transform_caps);
216   GST_BASE_TRANSFORM_CLASS (klass)->set_caps =
217       GST_DEBUG_FUNCPTR (gst_audio_panorama_set_caps);
218   GST_BASE_TRANSFORM_CLASS (klass)->transform =
219       GST_DEBUG_FUNCPTR (gst_audio_panorama_transform);
220 }
221
222 static void
223 gst_audio_panorama_init (GstAudioPanorama * filter)
224 {
225
226   filter->panorama = 0;
227   filter->method = METHOD_PSYCHOACOUSTIC;
228   gst_audio_info_init (&filter->info);
229   filter->process = NULL;
230
231   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (filter), TRUE);
232 }
233
234 static gboolean
235 gst_audio_panorama_set_process_function (GstAudioPanorama * filter,
236     GstAudioInfo * info)
237 {
238   gint channel_index, format_index, method_index;
239   const GstAudioFormatInfo *finfo = info->finfo;
240
241   /* set processing function */
242   channel_index = GST_AUDIO_INFO_CHANNELS (info) - 1;
243   if (channel_index > 1 || channel_index < 0) {
244     filter->process = NULL;
245     return FALSE;
246   }
247
248   format_index = GST_AUDIO_FORMAT_INFO_IS_FLOAT (finfo) ? 1 : 0;
249
250   method_index = filter->method;
251   if (method_index >= NUM_METHODS || method_index < 0)
252     method_index = METHOD_PSYCHOACOUSTIC;
253
254   filter->process =
255       panorama_process_functions[channel_index][format_index][method_index];
256   return TRUE;
257 }
258
259 static void
260 gst_audio_panorama_set_property (GObject * object, guint prop_id,
261     const GValue * value, GParamSpec * pspec)
262 {
263   GstAudioPanorama *filter = GST_AUDIO_PANORAMA (object);
264
265   switch (prop_id) {
266     case PROP_PANORAMA:
267       filter->panorama = g_value_get_float (value);
268       break;
269     case PROP_METHOD:
270       filter->method = g_value_get_enum (value);
271       gst_audio_panorama_set_process_function (filter, &filter->info);
272       break;
273     default:
274       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
275       break;
276   }
277 }
278
279 static void
280 gst_audio_panorama_get_property (GObject * object, guint prop_id,
281     GValue * value, GParamSpec * pspec)
282 {
283   GstAudioPanorama *filter = GST_AUDIO_PANORAMA (object);
284
285   switch (prop_id) {
286     case PROP_PANORAMA:
287       g_value_set_float (value, filter->panorama);
288       break;
289     case PROP_METHOD:
290       g_value_set_enum (value, filter->method);
291       break;
292     default:
293       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
294       break;
295   }
296 }
297
298 /* GstBaseTransform vmethod implementations */
299
300 static gboolean
301 gst_audio_panorama_get_unit_size (GstBaseTransform * base, GstCaps * caps,
302     gsize * size)
303 {
304   gint width, channels;
305   GstStructure *structure;
306   gboolean ret;
307
308   g_assert (size);
309
310   /* this works for both float and int */
311   structure = gst_caps_get_structure (caps, 0);
312   ret = gst_structure_get_int (structure, "width", &width);
313   ret &= gst_structure_get_int (structure, "channels", &channels);
314
315   *size = width * channels / 8;
316
317   return ret;
318 }
319
320 static GstCaps *
321 gst_audio_panorama_transform_caps (GstBaseTransform * base,
322     GstPadDirection direction, GstCaps * caps, GstCaps * filter)
323 {
324   GstCaps *res;
325   GstStructure *structure;
326
327   /* transform caps gives one single caps so we can just replace
328    * the channel property with our range. */
329   res = gst_caps_copy (caps);
330   structure = gst_caps_get_structure (res, 0);
331   if (direction == GST_PAD_SRC) {
332     GST_INFO ("allow 1-2 channels");
333     gst_structure_set (structure, "channels", GST_TYPE_INT_RANGE, 1, 2, NULL);
334   } else {
335     GST_INFO ("allow 2 channels");
336     gst_structure_set (structure, "channels", G_TYPE_INT, 2, NULL);
337   }
338
339   return res;
340 }
341
342 static gboolean
343 gst_audio_panorama_set_caps (GstBaseTransform * base, GstCaps * incaps,
344     GstCaps * outcaps)
345 {
346   GstAudioPanorama *filter = GST_AUDIO_PANORAMA (base);
347   GstAudioInfo info;
348
349   /*GST_INFO ("incaps are %" GST_PTR_FORMAT, incaps); */
350   if (!gst_audio_info_from_caps (&info, incaps))
351     goto no_format;
352
353   GST_DEBUG ("try to process %d input with %d channels",
354       GST_AUDIO_INFO_FORMAT (&info), GST_AUDIO_INFO_CHANNELS (&info));
355
356   if (!gst_audio_panorama_set_process_function (filter, &info))
357     goto no_format;
358
359   filter->info = info;
360
361   return TRUE;
362
363 no_format:
364   {
365     GST_DEBUG ("invalid caps");
366     return FALSE;
367   }
368 }
369
370 /* psychoacoustic processing functions */
371 static void
372 gst_audio_panorama_transform_m2s_int (GstAudioPanorama * filter, gint16 * idata,
373     gint16 * odata, guint num_samples)
374 {
375   guint i;
376   gdouble val;
377   glong lval, rval;
378   gdouble rpan, lpan;
379
380   /* pan:  -1.0  0.0  1.0
381    * lpan:  1.0  0.5  0.0  
382    * rpan:  0.0  0.5  1.0
383    *
384    * FIXME: we should use -3db (1/sqtr(2)) for 50:50
385    */
386   rpan = (gdouble) (filter->panorama + 1.0) / 2.0;
387   lpan = 1.0 - rpan;
388
389   for (i = 0; i < num_samples; i++) {
390     val = (gdouble) * idata++;
391
392     lval = (glong) (val * lpan);
393     rval = (glong) (val * rpan);
394
395     *odata++ = (gint16) CLAMP (lval, G_MININT16, G_MAXINT16);
396     *odata++ = (gint16) CLAMP (rval, G_MININT16, G_MAXINT16);
397   }
398 }
399
400 static void
401 gst_audio_panorama_transform_s2s_int (GstAudioPanorama * filter, gint16 * idata,
402     gint16 * odata, guint num_samples)
403 {
404   guint i;
405   glong lval, rval;
406   gdouble lival, rival;
407   gdouble lrpan, llpan, rrpan, rlpan;
408
409   /* pan:  -1.0  0.0  1.0
410    * llpan: 1.0  1.0  0.0
411    * lrpan: 1.0  0.0  0.0
412    * rrpan: 0.0  1.0  1.0
413    * rlpan: 0.0  0.0  1.0
414    */
415   if (filter->panorama > 0) {
416     rlpan = (gdouble) filter->panorama;
417     llpan = 1.0 - rlpan;
418     lrpan = 0.0;
419     rrpan = 1.0;
420   } else {
421     rrpan = (gdouble) (1.0 + filter->panorama);
422     lrpan = 1.0 - rrpan;
423     rlpan = 0.0;
424     llpan = 1.0;
425   }
426
427   for (i = 0; i < num_samples; i++) {
428     lival = (gdouble) * idata++;
429     rival = (gdouble) * idata++;
430
431     lval = lival * llpan + rival * lrpan;
432     rval = lival * rlpan + rival * rrpan;
433
434     *odata++ = (gint16) CLAMP (lval, G_MININT16, G_MAXINT16);
435     *odata++ = (gint16) CLAMP (rval, G_MININT16, G_MAXINT16);
436   }
437 }
438
439 static void
440 gst_audio_panorama_transform_m2s_float (GstAudioPanorama * filter,
441     gfloat * idata, gfloat * odata, guint num_samples)
442 {
443   guint i;
444   gfloat val;
445   gdouble rpan, lpan;
446
447   /* pan:  -1.0  0.0  1.0
448    * lpan:  1.0  0.5  0.0  
449    * rpan:  0.0  0.5  1.0
450    *
451    * FIXME: we should use -3db (1/sqtr(2)) for 50:50
452    */
453   rpan = (gdouble) (filter->panorama + 1.0) / 2.0;
454   lpan = 1.0 - rpan;
455
456   for (i = 0; i < num_samples; i++) {
457     val = *idata++;
458
459     *odata++ = val * lpan;
460     *odata++ = val * rpan;
461   }
462 }
463
464 static void
465 gst_audio_panorama_transform_s2s_float (GstAudioPanorama * filter,
466     gfloat * idata, gfloat * odata, guint num_samples)
467 {
468   guint i;
469   gfloat lival, rival;
470   gdouble lrpan, llpan, rrpan, rlpan;
471
472   /* pan:  -1.0  0.0  1.0
473    * llpan: 1.0  1.0  0.0
474    * lrpan: 1.0  0.0  0.0
475    * rrpan: 0.0  1.0  1.0
476    * rlpan: 0.0  0.0  1.0
477    */
478   if (filter->panorama > 0) {
479     rlpan = (gdouble) filter->panorama;
480     llpan = 1.0 - rlpan;
481     lrpan = 0.0;
482     rrpan = 1.0;
483   } else {
484     rrpan = (gdouble) (1.0 + filter->panorama);
485     lrpan = 1.0 - rrpan;
486     rlpan = 0.0;
487     llpan = 1.0;
488   }
489
490   for (i = 0; i < num_samples; i++) {
491     lival = *idata++;
492     rival = *idata++;
493
494     *odata++ = lival * llpan + rival * lrpan;
495     *odata++ = lival * rlpan + rival * rrpan;
496   }
497 }
498
499 /* simple processing functions */
500 static void
501 gst_audio_panorama_transform_m2s_int_simple (GstAudioPanorama * filter,
502     gint16 * idata, gint16 * odata, guint num_samples)
503 {
504   guint i;
505   gdouble pan;
506   glong lval, rval;
507
508   if (filter->panorama > 0.0) {
509     pan = 1.0 - filter->panorama;
510     for (i = 0; i < num_samples; i++) {
511       rval = *idata++;
512       lval = (glong) ((gdouble) rval * pan);
513
514       *odata++ = (gint16) CLAMP (lval, G_MININT16, G_MAXINT16);
515       *odata++ = (gint16) rval;
516     }
517   } else {
518     pan = 1.0 + filter->panorama;
519     for (i = 0; i < num_samples; i++) {
520       lval = *idata++;
521       rval = (glong) ((gdouble) lval * pan);
522
523       *odata++ = (gint16) lval;
524       *odata++ = (gint16) CLAMP (rval, G_MININT16, G_MAXINT16);
525     }
526   }
527 }
528
529 static void
530 gst_audio_panorama_transform_s2s_int_simple (GstAudioPanorama * filter,
531     gint16 * idata, gint16 * odata, guint num_samples)
532 {
533   guint i;
534   glong lval, rval;
535   gdouble lival, rival, pan;
536
537   if (filter->panorama > 0.0) {
538     pan = 1.0 - filter->panorama;
539     for (i = 0; i < num_samples; i++) {
540       lival = (gdouble) * idata++;
541       rival = (gdouble) * idata++;
542
543       lval = (glong) (lival * pan);
544       rval = (glong) rival;
545
546       *odata++ = (gint16) CLAMP (lval, G_MININT16, G_MAXINT16);
547       *odata++ = (gint16) rval;
548     }
549   } else {
550     pan = 1.0 + filter->panorama;
551     for (i = 0; i < num_samples; i++) {
552       lival = (gdouble) * idata++;
553       rival = (gdouble) * idata++;
554
555       lval = (glong) lival;
556       rval = (glong) (rival * pan);
557
558       *odata++ = (gint16) lval;
559       *odata++ = (gint16) CLAMP (rval, G_MININT16, G_MAXINT16);
560     }
561   }
562 }
563
564 static void
565 gst_audio_panorama_transform_m2s_float_simple (GstAudioPanorama * filter,
566     gfloat * idata, gfloat * odata, guint num_samples)
567 {
568   guint i;
569   gfloat val, pan;
570
571   if (filter->panorama > 0.0) {
572     pan = 1.0 - filter->panorama;
573     for (i = 0; i < num_samples; i++) {
574       val = *idata++;
575
576       *odata++ = val * pan;
577       *odata++ = val;
578     }
579   } else {
580     pan = 1.0 + filter->panorama;
581     for (i = 0; i < num_samples; i++) {
582       val = *idata++;
583
584       *odata++ = val;
585       *odata++ = val * pan;
586     }
587   }
588 }
589
590 static void
591 gst_audio_panorama_transform_s2s_float_simple (GstAudioPanorama * filter,
592     gfloat * idata, gfloat * odata, guint num_samples)
593 {
594   guint i;
595   gfloat lival, rival, pan;
596
597   if (filter->panorama > 0.0) {
598     pan = 1.0 - filter->panorama;
599     for (i = 0; i < num_samples; i++) {
600       lival = *idata++;
601       rival = *idata++;
602
603       *odata++ = lival * pan;
604       *odata++ = rival;
605     }
606   } else {
607     pan = 1.0 + filter->panorama;
608     for (i = 0; i < num_samples; i++) {
609       lival = *idata++;
610       rival = *idata++;
611
612       *odata++ = lival;
613       *odata++ = rival * pan;
614     }
615   }
616 }
617
618 /* this function does the actual processing
619  */
620 static GstFlowReturn
621 gst_audio_panorama_transform (GstBaseTransform * base, GstBuffer * inbuf,
622     GstBuffer * outbuf)
623 {
624   GstAudioPanorama *filter = GST_AUDIO_PANORAMA (base);
625   GstClockTime timestamp, stream_time;
626   guint8 *indata, *outdata;
627   gsize insize, outsize;
628
629   timestamp = GST_BUFFER_TIMESTAMP (inbuf);
630   stream_time =
631       gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
632
633   GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
634       GST_TIME_ARGS (timestamp));
635
636   if (GST_CLOCK_TIME_IS_VALID (stream_time))
637     gst_object_sync_values (G_OBJECT (filter), stream_time);
638
639   indata = gst_buffer_map (inbuf, &insize, NULL, GST_MAP_READ);
640   outdata = gst_buffer_map (outbuf, &outsize, NULL, GST_MAP_WRITE);
641
642   if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP))) {
643     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
644     memset (outdata, 0, outsize);
645   } else {
646     guint num_samples = outsize / GST_AUDIO_INFO_BPF (&filter->info);
647
648     filter->process (filter, indata, outdata, num_samples);
649   }
650
651   gst_buffer_unmap (inbuf, indata, insize);
652   gst_buffer_unmap (outbuf, outdata, outsize);
653
654   return GST_FLOW_OK;
655 }