gst-plugins-good  1.20.3
About: GStreamer (Good Plugins) is a library for constructing of graphs of media-handling components. A set of good-quality plug-ins (under LGPL license).
  Fossies Dox: gst-plugins-good-1.20.3.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

audiofxbasefirfilter.c
Go to the documentation of this file.
1/* -*- c-basic-offset: 2 -*-
2 *
3 * GStreamer
4 * Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
5 * 2006 Dreamlab Technologies Ltd. <mathis.hofer@dreamlab.net>
6 * 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include <string.h>
30#include <math.h>
31#include <gst/gst.h>
32#include <gst/audio/gstaudiofilter.h>
33
35
36#define GST_CAT_DEFAULT gst_audio_fx_base_fir_filter_debug
38
39#define ALLOWED_CAPS \
40 "audio/x-raw, " \
41 " format=(string){"GST_AUDIO_NE(F32)","GST_AUDIO_NE(F64)"}, " \
42 " rate = (int) [ 1, MAX ], " \
43 " channels = (int) [ 1, MAX ], " \
44 " layout=(string) interleaved"
45
46/* Switch from time-domain to FFT convolution for kernels >= this */
47#define FFT_THRESHOLD 32
48
49enum
50{
51 PROP_0 = 0,
54};
55
56#define DEFAULT_LOW_LATENCY FALSE
57#define DEFAULT_DRAIN_ON_CHANGES TRUE
58
59#define gst_audio_fx_base_fir_filter_parent_class parent_class
60G_DEFINE_TYPE (GstAudioFXBaseFIRFilter, gst_audio_fx_base_fir_filter,
61 GST_TYPE_AUDIO_FILTER);
62
63static GstFlowReturn gst_audio_fx_base_fir_filter_transform (GstBaseTransform *
64 base, GstBuffer * inbuf, GstBuffer * outbuf);
65static gboolean gst_audio_fx_base_fir_filter_start (GstBaseTransform * base);
66static gboolean gst_audio_fx_base_fir_filter_stop (GstBaseTransform * base);
67static gboolean gst_audio_fx_base_fir_filter_sink_event (GstBaseTransform *
68 base, GstEvent * event);
69static gboolean gst_audio_fx_base_fir_filter_transform_size (GstBaseTransform *
70 base, GstPadDirection direction, GstCaps * caps, gsize size,
71 GstCaps * othercaps, gsize * othersize);
72static gboolean gst_audio_fx_base_fir_filter_setup (GstAudioFilter * base,
73 const GstAudioInfo * info);
74
75static gboolean gst_audio_fx_base_fir_filter_query (GstBaseTransform * trans,
76 GstPadDirection direction, GstQuery * quer);
77
78/*
79 * The code below calculates the linear convolution:
80 *
81 * y[t] = \sum_{u=0}^{M-1} x[t - u] * h[u]
82 *
83 * where y is the output, x is the input, M is the length
84 * of the filter kernel and h is the filter kernel. For x
85 * holds: x[t] == 0 \forall t < 0.
86 *
87 * The runtime complexity of this is O (M) per sample.
88 *
89 */
90#define DEFINE_PROCESS_FUNC(width,ctype) \
91static guint \
92process_##width (GstAudioFXBaseFIRFilter * self, const g##ctype * src, g##ctype * dst, guint input_samples) \
93{ \
94 gint channels = GST_AUDIO_FILTER_CHANNELS (self); \
95 TIME_DOMAIN_CONVOLUTION_BODY (channels); \
96}
97
98#define DEFINE_PROCESS_FUNC_FIXED_CHANNELS(width,channels,ctype) \
99static guint \
100process_##channels##_##width (GstAudioFXBaseFIRFilter * self, const g##ctype * src, g##ctype * dst, guint input_samples) \
101{ \
102 TIME_DOMAIN_CONVOLUTION_BODY (channels); \
103}
104
105#define TIME_DOMAIN_CONVOLUTION_BODY(channels) G_STMT_START { \
106 gint kernel_length = self->kernel_length; \
107 gint i, j, k, l; \
108 gint res_start; \
109 gint from_input; \
110 gint off; \
111 gdouble *buffer = self->buffer; \
112 gdouble *kernel = self->kernel; \
113 \
114 if (!buffer) { \
115 self->buffer_length = kernel_length * channels; \
116 self->buffer = buffer = g_new0 (gdouble, self->buffer_length); \
117 } \
118 \
119 input_samples *= channels; \
120 /* convolution */ \
121 for (i = 0; i < input_samples; i++) { \
122 dst[i] = 0.0; \
123 k = i % channels; \
124 l = i / channels; \
125 from_input = MIN (l, kernel_length-1); \
126 off = l * channels + k; \
127 for (j = 0; j <= from_input; j++) { \
128 dst[i] += src[off] * kernel[j]; \
129 off -= channels; \
130 } \
131 /* j == from_input && off == (l - j) * channels + k */ \
132 off += kernel_length * channels; \
133 for (; j < kernel_length; j++) { \
134 dst[i] += buffer[off] * kernel[j]; \
135 off -= channels; \
136 } \
137 } \
138 \
139 /* copy the tail of the current input buffer to the residue, while \
140 * keeping parts of the residue if the input buffer is smaller than \
141 * the kernel length */ \
142 /* from now on take kernel length as length over all channels */ \
143 kernel_length *= channels; \
144 if (input_samples < kernel_length) \
145 res_start = kernel_length - input_samples; \
146 else \
147 res_start = 0; \
148 \
149 for (i = 0; i < res_start; i++) \
150 buffer[i] = buffer[i + input_samples]; \
151 /* i == res_start */ \
152 for (; i < kernel_length; i++) \
153 buffer[i] = src[input_samples - kernel_length + i]; \
154 \
155 self->buffer_fill += kernel_length - res_start; \
156 if (self->buffer_fill > kernel_length) \
157 self->buffer_fill = kernel_length; \
158 \
159 return input_samples / channels; \
160} G_STMT_END
162DEFINE_PROCESS_FUNC (32, float);
170
171#undef TIME_DOMAIN_CONVOLUTION_BODY
172#undef DEFINE_PROCESS_FUNC
173#undef DEFINE_PROCESS_FUNC_FIXED_CHANNELS
174
175/* This implements FFT convolution and uses the overlap-save algorithm.
176 * See http://cnx.org/content/m12022/latest/ or your favorite
177 * digital signal processing book for details.
178 *
179 * In every pass the following is calculated:
180 *
181 * y = IFFT (FFT(x) * FFT(h))
182 *
183 * where y is the output in the time domain, x the
184 * input and h the filter kernel. * is the multiplication
185 * of complex numbers.
186 *
187 * Due to the circular convolution theorem this
188 * gives in the time domain:
189 *
190 * y[t] = \sum_{u=0}^{M-1} x[t - u] * h[u]
191 *
192 * where y is the output, M is the kernel length,
193 * x the periodically extended[0] input and h the
194 * filter kernel.
195 *
196 * ([0] Periodically extended means: )
197 * ( x[t] = x[t+kN] \forall k \in Z )
198 * ( where N is the length of x )
199 *
200 * This means:
201 * - Obviously x and h need to be of the same size for the FFT
202 * - The first M-1 output values are useless because they're
203 * built from 1 up to M-1 values from the end of the input
204 * (circular convolusion!).
205 * - The last M-1 input values are only used for 1 up to M-1
206 * output values, i.e. they need to be used again in the
207 * next pass for the first M-1 input values.
208 *
209 * => The first pass needs M-1 zeroes at the beginning of the
210 * input and the last M-1 input values of every pass need to
211 * be used as the first M-1 input values of the next pass.
212 *
213 * => x must be larger than h to give a useful number of output
214 * samples and h needs to be padded by zeroes at the end to give
215 * it virtually the same size as x (by M we denote the number of
216 * non-padding samples of h). If len(x)==len(h)==M only 1 output
217 * sample would be calculated per pass, len(x)==2*len(h) would
218 * give M+1 output samples, etc. Usually a factor between 4 and 8
219 * gives a low number of operations per output samples (see website
220 * given above).
221 *
222 * Overall this gives a runtime complexity per sample of
223 *
224 * ( N log N )
225 * O ( --------- ) compared to O (M) for the direct calculation.
226 * ( N - M + 1 )
227 */
228#define DEFINE_FFT_PROCESS_FUNC(width,ctype) \
229static guint \
230process_fft_##width (GstAudioFXBaseFIRFilter * self, const g##ctype * src, \
231 g##ctype * dst, guint input_samples) \
232{ \
233 gint channels = GST_AUDIO_FILTER_CHANNELS (self); \
234 FFT_CONVOLUTION_BODY (channels); \
236
237#define DEFINE_FFT_PROCESS_FUNC_FIXED_CHANNELS(width,channels,ctype) \
238static guint \
239process_fft_##channels##_##width (GstAudioFXBaseFIRFilter * self, const g##ctype * src, \
240 g##ctype * dst, guint input_samples) \
241{ \
242 FFT_CONVOLUTION_BODY (channels); \
244
245#define FFT_CONVOLUTION_BODY(channels) G_STMT_START { \
246 gint i, j; \
247 guint pass; \
248 guint kernel_length = self->kernel_length; \
249 guint block_length = self->block_length; \
250 guint buffer_length = self->buffer_length; \
251 guint real_buffer_length = buffer_length + kernel_length - 1; \
252 guint buffer_fill = self->buffer_fill; \
253 GstFFTF64 *fft = self->fft; \
254 GstFFTF64 *ifft = self->ifft; \
255 GstFFTF64Complex *frequency_response = self->frequency_response; \
256 GstFFTF64Complex *fft_buffer = self->fft_buffer; \
257 guint frequency_response_length = self->frequency_response_length; \
258 gdouble *buffer = self->buffer; \
259 guint generated = 0; \
260 gdouble re, im; \
261 \
262 if (!fft_buffer) \
263 self->fft_buffer = fft_buffer = \
264 g_new (GstFFTF64Complex, frequency_response_length); \
265 \
266 /* Buffer contains the time domain samples of input data for one chunk \
267 * plus some more space for the inverse FFT below. \
268 * \
269 * The samples are put at offset kernel_length, the inverse FFT \
270 * overwrites everything from offset 0 to length-kernel_length+1, keeping \
271 * the last kernel_length-1 samples for copying to the next processing \
272 * step. \
273 */ \
274 if (!buffer) { \
275 self->buffer_length = buffer_length = block_length; \
276 real_buffer_length = buffer_length + kernel_length - 1; \
277 \
278 self->buffer = buffer = g_new0 (gdouble, real_buffer_length * channels); \
279 \
280 /* Beginning has kernel_length-1 zeroes at the beginning */ \
281 self->buffer_fill = buffer_fill = kernel_length - 1; \
282 } \
283 \
284 g_assert (self->buffer_length == block_length); \
285 \
286 while (input_samples) { \
287 pass = MIN (buffer_length - buffer_fill, input_samples); \
288 \
289 /* Deinterleave channels */ \
290 for (i = 0; i < pass; i++) { \
291 for (j = 0; j < channels; j++) { \
292 buffer[real_buffer_length * j + buffer_fill + kernel_length - 1 + i] = \
293 src[i * channels + j]; \
294 } \
295 } \
296 buffer_fill += pass; \
297 src += channels * pass; \
298 input_samples -= pass; \
299 \
300 /* If we don't have a complete buffer go out */ \
301 if (buffer_fill < buffer_length) \
302 break; \
303 \
304 for (j = 0; j < channels; j++) { \
305 /* Calculate FFT of input block */ \
306 gst_fft_f64_fft (fft, \
307 buffer + real_buffer_length * j + kernel_length - 1, fft_buffer); \
308 \
309 /* Complex multiplication of input and filter spectrum */ \
310 for (i = 0; i < frequency_response_length; i++) { \
311 re = fft_buffer[i].r; \
312 im = fft_buffer[i].i; \
313 \
314 fft_buffer[i].r = \
315 re * frequency_response[i].r - \
316 im * frequency_response[i].i; \
317 fft_buffer[i].i = \
318 re * frequency_response[i].i + \
319 im * frequency_response[i].r; \
320 } \
321 \
322 /* Calculate inverse FFT of the result */ \
323 gst_fft_f64_inverse_fft (ifft, fft_buffer, \
324 buffer + real_buffer_length * j); \
325 \
326 /* Copy all except the first kernel_length-1 samples to the output */ \
327 for (i = 0; i < buffer_length - kernel_length + 1; i++) { \
328 dst[i * channels + j] = \
329 buffer[real_buffer_length * j + kernel_length - 1 + i]; \
330 } \
331 \
332 /* Copy the last kernel_length-1 samples to the beginning for the next block */ \
333 for (i = 0; i < kernel_length - 1; i++) { \
334 buffer[real_buffer_length * j + kernel_length - 1 + i] = \
335 buffer[real_buffer_length * j + buffer_length + i]; \
336 } \
337 } \
338 \
339 generated += buffer_length - kernel_length + 1; \
340 dst += channels * (buffer_length - kernel_length + 1); \
341 \
342 /* The the first kernel_length-1 samples are there already */ \
343 buffer_fill = kernel_length - 1; \
344 } \
345 \
346 /* Write back cached buffer_fill value */ \
347 self->buffer_fill = buffer_fill; \
348 \
349 return generated; \
350} G_STMT_END
351
352DEFINE_FFT_PROCESS_FUNC (32, float);
353DEFINE_FFT_PROCESS_FUNC (64, double);
354
357
360
361#undef FFT_CONVOLUTION_BODY
362#undef DEFINE_FFT_PROCESS_FUNC
363#undef DEFINE_FFT_PROCESS_FUNC_FIXED_CHANNELS
364
365/* Element class */
366static void
369{
370 gst_fft_f64_free (self->fft);
371 self->fft = NULL;
372 gst_fft_f64_free (self->ifft);
373 self->ifft = NULL;
374 g_free (self->frequency_response);
376 g_free (self->fft_buffer);
377 self->fft_buffer = NULL;
378
379 if (self->kernel && self->kernel_length >= FFT_THRESHOLD
380 && !self->low_latency) {
381 guint block_length, i;
382 gdouble *kernel_tmp, *kernel = self->kernel;
383
384 /* We process 4 * kernel_length samples per pass in FFT mode */
385 block_length = 4 * self->kernel_length;
386 block_length = gst_fft_next_fast_length (block_length);
387 self->block_length = block_length;
388
389 kernel_tmp = g_new0 (gdouble, block_length);
390 memcpy (kernel_tmp, kernel, self->kernel_length * sizeof (gdouble));
391
392 self->fft = gst_fft_f64_new (block_length, FALSE);
393 self->ifft = gst_fft_f64_new (block_length, TRUE);
394 self->frequency_response_length = block_length / 2 + 1;
395 self->frequency_response =
396 g_new (GstFFTF64Complex, self->frequency_response_length);
397 gst_fft_f64_fft (self->fft, kernel_tmp, self->frequency_response);
398 g_free (kernel_tmp);
399
400 /* Normalize to make sure IFFT(FFT(x)) == x */
401 for (i = 0; i < self->frequency_response_length; i++) {
402 self->frequency_response[i].r /= block_length;
403 self->frequency_response[i].i /= block_length;
404 }
405 }
406}
407
408/* Must be called with base transform lock! */
409static void
411 self, GstAudioFormat format, gint channels)
412{
413 switch (format) {
414 case GST_AUDIO_FORMAT_F32:
415 if (self->fft && !self->low_latency) {
416 if (channels == 1)
418 else if (channels == 2)
420 else
422 } else {
423 if (channels == 1)
425 else if (channels == 2)
427 else
429 }
430 break;
431 case GST_AUDIO_FORMAT_F64:
432 if (self->fft && !self->low_latency) {
433 if (channels == 1)
435 else if (channels == 2)
437 else
439 } else {
440 if (channels == 1)
442 else if (channels == 2)
444 else
447 break;
448 default:
449 self->process = NULL;
450 break;
451 }
452}
453
454static void
456{
458
459 g_free (self->buffer);
460 g_free (self->kernel);
461 gst_fft_f64_free (self->fft);
462 gst_fft_f64_free (self->ifft);
463 g_free (self->frequency_response);
464 g_free (self->fft_buffer);
465 g_mutex_clear (&self->lock);
466
467 G_OBJECT_CLASS (parent_class)->finalize (object);
468}
469
470static void
471gst_audio_fx_base_fir_filter_set_property (GObject * object, guint prop_id,
472 const GValue * value, GParamSpec * pspec)
473{
475
476 switch (prop_id) {
477 case PROP_LOW_LATENCY:{
478 gboolean low_latency;
479
480 if (GST_STATE (self) >= GST_STATE_PAUSED) {
481 g_warning ("Changing the \"low-latency\" property "
482 "is only allowed in states < PAUSED");
483 return;
484 }
485
486
487 g_mutex_lock (&self->lock);
488 low_latency = g_value_get_boolean (value);
489
490 if (self->low_latency != low_latency) {
491 self->low_latency = low_latency;
494 GST_AUDIO_FILTER_FORMAT (self), GST_AUDIO_FILTER_CHANNELS (self));
495 }
496 g_mutex_unlock (&self->lock);
497 break;
498 }
500 g_mutex_lock (&self->lock);
501 self->drain_on_changes = g_value_get_boolean (value);
502 g_mutex_unlock (&self->lock);
503 break;
504 }
505 default:
506 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
507 break;
508 }
509}
510
511static void
512gst_audio_fx_base_fir_filter_get_property (GObject * object, guint prop_id,
513 GValue * value, GParamSpec * pspec)
514{
516
517 switch (prop_id) {
518 case PROP_LOW_LATENCY:
519 g_value_set_boolean (value, self->low_latency);
520 break;
522 g_value_set_boolean (value, self->drain_on_changes);
523 break;
524 default:
525 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
526 break;
527 }
528}
529
530static void
532{
533 GObjectClass *gobject_class = (GObjectClass *) klass;
534 GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
535 GstAudioFilterClass *filter_class = (GstAudioFilterClass *) klass;
536 GstCaps *caps;
537
538 GST_DEBUG_CATEGORY_INIT (gst_audio_fx_base_fir_filter_debug,
539 "audiofxbasefirfilter", 0, "FIR filter base class");
540
541 gobject_class->finalize = gst_audio_fx_base_fir_filter_finalize;
542 gobject_class->set_property = gst_audio_fx_base_fir_filter_set_property;
543 gobject_class->get_property = gst_audio_fx_base_fir_filter_get_property;
544
545 /**
546 * GstAudioFXBaseFIRFilter:low-latency:
547 *
548 * Work in low-latency mode. This mode is much slower for large filter sizes
549 * but the latency is always only the pre-latency of the filter.
550 */
551 g_object_class_install_property (gobject_class, PROP_LOW_LATENCY,
552 g_param_spec_boolean ("low-latency", "Low latency",
553 "Operate in low latency mode. This mode is slower but the "
554 "latency will only be the filter pre-latency. "
555 "Can only be changed in states < PAUSED!", DEFAULT_LOW_LATENCY,
556 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
557
558 /**
559 * GstAudioFXBaseFIRFilter:drain-on-changes:
560 *
561 * Whether the filter should be drained when its coefficients change
562 *
563 * Note: Currently this only works if the kernel size is not changed!
564 * Support for drainless kernel size changes will be added in the future.
565 */
566 g_object_class_install_property (gobject_class, PROP_DRAIN_ON_CHANGES,
567 g_param_spec_boolean ("drain-on-changes", "Drain on changes",
568 "Drains the filter when its coefficients change",
570 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
571
572 caps = gst_caps_from_string (ALLOWED_CAPS);
573 gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass),
574 caps);
575 gst_caps_unref (caps);
576
577 trans_class->transform =
578 GST_DEBUG_FUNCPTR (gst_audio_fx_base_fir_filter_transform);
579 trans_class->start = GST_DEBUG_FUNCPTR (gst_audio_fx_base_fir_filter_start);
580 trans_class->stop = GST_DEBUG_FUNCPTR (gst_audio_fx_base_fir_filter_stop);
581 trans_class->sink_event =
582 GST_DEBUG_FUNCPTR (gst_audio_fx_base_fir_filter_sink_event);
583 trans_class->query = GST_DEBUG_FUNCPTR (gst_audio_fx_base_fir_filter_query);
584 trans_class->transform_size =
586 filter_class->setup = GST_DEBUG_FUNCPTR (gst_audio_fx_base_fir_filter_setup);
587
588 gst_type_mark_as_plugin_api (GST_TYPE_AUDIO_FX_BASE_FIR_FILTER, 0);
589}
590
591static void
593{
594 self->kernel = NULL;
595 self->buffer = NULL;
596 self->buffer_length = 0;
597
598 self->start_ts = GST_CLOCK_TIME_NONE;
599 self->start_off = GST_BUFFER_OFFSET_NONE;
600 self->nsamples_out = 0;
601 self->nsamples_in = 0;
602
605
606 g_mutex_init (&self->lock);
607}
608
609void
611{
612 GstBuffer *outbuf;
613 GstFlowReturn res;
614 gint rate = GST_AUDIO_FILTER_RATE (self);
615 gint channels = GST_AUDIO_FILTER_CHANNELS (self);
616 gint bps = GST_AUDIO_FILTER_BPS (self);
617 gint outsize, outsamples;
618 GstMapInfo map;
619 guint8 *in, *out;
620
621 if (channels == 0 || rate == 0 || self->nsamples_in == 0) {
622 self->buffer_fill = 0;
623 g_free (self->buffer);
624 self->buffer = NULL;
625 return;
626 }
627
628 /* Calculate the number of samples and their memory size that
629 * should be pushed from the residue */
630 outsamples = self->nsamples_in - (self->nsamples_out - self->latency);
631 if (outsamples <= 0) {
632 self->buffer_fill = 0;
633 g_free (self->buffer);
634 self->buffer = NULL;
635 return;
636 }
637 outsize = outsamples * channels * bps;
638
639 if (!self->fft || self->low_latency) {
640 gint64 diffsize, diffsamples;
641
642 /* Process the difference between latency and residue length samples
643 * to start at the actual data instead of starting at the zeros before
644 * when we only got one buffer smaller than latency */
645 diffsamples =
646 ((gint64) self->latency) - ((gint64) self->buffer_fill) / channels;
647 if (diffsamples > 0) {
648 diffsize = diffsamples * channels * bps;
649 in = g_new0 (guint8, diffsize);
650 out = g_new0 (guint8, diffsize);
651 self->nsamples_out += self->process (self, in, out, diffsamples);
652 g_free (in);
653 g_free (out);
654 }
655
656 outbuf = gst_buffer_new_and_alloc (outsize);
657
658 /* Convolve the residue with zeros to get the actual remaining data */
659 in = g_new0 (guint8, outsize);
660 gst_buffer_map (outbuf, &map, GST_MAP_READWRITE);
661 self->nsamples_out += self->process (self, in, map.data, outsamples);
662 gst_buffer_unmap (outbuf, &map);
663
664 g_free (in);
665 } else {
666 guint gensamples = 0;
667
668 outbuf = gst_buffer_new_and_alloc (outsize);
669 gst_buffer_map (outbuf, &map, GST_MAP_READWRITE);
670
671 while (gensamples < outsamples) {
672 guint step_insamples = self->block_length - self->buffer_fill;
673 guint8 *zeroes = g_new0 (guint8, step_insamples * channels * bps);
674 guint8 *out = g_new (guint8, self->block_length * channels * bps);
675 guint step_gensamples;
676
677 step_gensamples = self->process (self, zeroes, out, step_insamples);
678 g_free (zeroes);
679
680 memcpy (map.data + gensamples * bps, out, MIN (step_gensamples,
681 outsamples - gensamples) * bps);
682 gensamples += MIN (step_gensamples, outsamples - gensamples);
683
684 g_free (out);
685 }
686 self->nsamples_out += gensamples;
687
688 gst_buffer_unmap (outbuf, &map);
689 }
690
691 /* Set timestamp, offset, etc from the values we
692 * saved when processing the regular buffers */
693 if (GST_CLOCK_TIME_IS_VALID (self->start_ts))
694 GST_BUFFER_TIMESTAMP (outbuf) = self->start_ts;
695 else
696 GST_BUFFER_TIMESTAMP (outbuf) = 0;
697 GST_BUFFER_TIMESTAMP (outbuf) +=
698 gst_util_uint64_scale_int (self->nsamples_out - outsamples -
699 self->latency, GST_SECOND, rate);
700
701 GST_BUFFER_DURATION (outbuf) =
702 gst_util_uint64_scale_int (outsamples, GST_SECOND, rate);
703
704 if (self->start_off != GST_BUFFER_OFFSET_NONE) {
705 GST_BUFFER_OFFSET (outbuf) =
706 self->start_off + self->nsamples_out - outsamples - self->latency;
707 GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + outsamples;
708 }
709
710 GST_DEBUG_OBJECT (self,
711 "Pushing residue buffer of size %" G_GSIZE_FORMAT " with timestamp: %"
712 GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %"
713 G_GUINT64_FORMAT ", offset_end: %" G_GUINT64_FORMAT ", nsamples_out: %d",
714 gst_buffer_get_size (outbuf),
715 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
716 GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf),
717 GST_BUFFER_OFFSET_END (outbuf), outsamples);
718
719 res = gst_pad_push (GST_BASE_TRANSFORM_CAST (self)->srcpad, outbuf);
720
721 if (G_UNLIKELY (res != GST_FLOW_OK)) {
722 GST_WARNING_OBJECT (self, "failed to push residue");
724
725 self->buffer_fill = 0;
726}
727
728/* GstAudioFilter vmethod implementations */
729
730/* get notified of caps and plug in the correct process function */
731static gboolean
732gst_audio_fx_base_fir_filter_setup (GstAudioFilter * base,
733 const GstAudioInfo * info)
734{
736
737 g_mutex_lock (&self->lock);
738 if (self->buffer) {
740 g_free (self->buffer);
741 self->buffer = NULL;
742 self->buffer_fill = 0;
743 self->buffer_length = 0;
744 self->start_ts = GST_CLOCK_TIME_NONE;
745 self->start_off = GST_BUFFER_OFFSET_NONE;
746 self->nsamples_out = 0;
747 self->nsamples_in = 0;
748 }
749
751 GST_AUDIO_INFO_FORMAT (info), GST_AUDIO_INFO_CHANNELS (info));
752 g_mutex_unlock (&self->lock);
753
754 return (self->process != NULL);
755}
756
757/* GstBaseTransform vmethod implementations */
758
759static gboolean
760gst_audio_fx_base_fir_filter_transform_size (GstBaseTransform * base,
761 GstPadDirection direction, GstCaps * caps, gsize size, GstCaps * othercaps,
762 gsize * othersize)
763{
765 guint blocklen;
766 GstAudioInfo info;
767 gint bpf;
768
769 if (!self->fft || self->low_latency || direction == GST_PAD_SRC) {
770 *othersize = size;
771 return TRUE;
772 }
773
774 if (!gst_audio_info_from_caps (&info, caps))
775 return FALSE;
776
777 bpf = GST_AUDIO_INFO_BPF (&info);
778
779 size /= bpf;
780 blocklen = self->block_length - self->kernel_length + 1;
781 *othersize = ((size + blocklen - 1) / blocklen) * blocklen;
782 *othersize *= bpf;
783
784 return TRUE;
785}
786
787static GstFlowReturn
788gst_audio_fx_base_fir_filter_transform (GstBaseTransform * base,
789 GstBuffer * inbuf, GstBuffer * outbuf)
790{
792 GstClockTime timestamp, expected_timestamp;
793 gint channels = GST_AUDIO_FILTER_CHANNELS (self);
794 gint rate = GST_AUDIO_FILTER_RATE (self);
795 gint bps = GST_AUDIO_FILTER_BPS (self);
796 GstMapInfo inmap, outmap;
797 guint input_samples;
798 guint output_samples;
799 guint generated_samples;
800 guint64 output_offset;
801 gint64 diff = 0;
802 GstClockTime stream_time;
803
804 timestamp = GST_BUFFER_TIMESTAMP (outbuf);
805
806 if (!GST_CLOCK_TIME_IS_VALID (timestamp)
807 && !GST_CLOCK_TIME_IS_VALID (self->start_ts)) {
808 GST_ERROR_OBJECT (self, "Invalid timestamp");
809 return GST_FLOW_ERROR;
810 }
811
812 g_mutex_lock (&self->lock);
813 stream_time =
814 gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
815
816 GST_DEBUG_OBJECT (self, "sync to %" GST_TIME_FORMAT,
817 GST_TIME_ARGS (timestamp));
818
819 if (GST_CLOCK_TIME_IS_VALID (stream_time))
820 gst_object_sync_values (GST_OBJECT (self), stream_time);
821
822 g_return_val_if_fail (self->kernel != NULL, GST_FLOW_ERROR);
823 g_return_val_if_fail (channels != 0, GST_FLOW_ERROR);
824
825 if (GST_CLOCK_TIME_IS_VALID (self->start_ts))
826 expected_timestamp =
827 self->start_ts + gst_util_uint64_scale_int (self->nsamples_in,
828 GST_SECOND, rate);
829 else
830 expected_timestamp = GST_CLOCK_TIME_NONE;
831
832 /* Reset the residue if already existing on discont buffers */
833 if (GST_BUFFER_IS_DISCONT (inbuf)
834 || (GST_CLOCK_TIME_IS_VALID (expected_timestamp)
835 && (ABS (GST_CLOCK_DIFF (timestamp,
836 expected_timestamp)) > 5 * GST_MSECOND))) {
837 GST_DEBUG_OBJECT (self, "Discontinuity detected - flushing");
838 if (GST_CLOCK_TIME_IS_VALID (expected_timestamp))
840 self->buffer_fill = 0;
841 g_free (self->buffer);
842 self->buffer = NULL;
843 self->start_ts = timestamp;
844 self->start_off = GST_BUFFER_OFFSET (inbuf);
845 self->nsamples_out = 0;
846 self->nsamples_in = 0;
847 } else if (!GST_CLOCK_TIME_IS_VALID (self->start_ts)) {
848 self->start_ts = timestamp;
849 self->start_off = GST_BUFFER_OFFSET (inbuf);
850 }
851
852 gst_buffer_map (inbuf, &inmap, GST_MAP_READ);
853 gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
854
855 input_samples = (inmap.size / bps) / channels;
856 output_samples = (outmap.size / bps) / channels;
857
858 self->nsamples_in += input_samples;
859
860 generated_samples =
861 self->process (self, inmap.data, outmap.data, input_samples);
862
863 gst_buffer_unmap (inbuf, &inmap);
864 gst_buffer_unmap (outbuf, &outmap);
865
866 g_assert (generated_samples <= output_samples);
867 self->nsamples_out += generated_samples;
868 if (generated_samples == 0)
869 goto no_samples;
870
871 /* Calculate the number of samples we can push out now without outputting
872 * latency zeros in the beginning */
873 diff = ((gint64) self->nsamples_out) - ((gint64) self->latency);
874 if (diff < 0)
875 goto no_samples;
876
877 if (diff < generated_samples) {
878 gint64 tmp = diff;
879 diff = generated_samples - diff;
880 generated_samples = tmp;
881 } else {
882 diff = 0;
883 }
884
885 gst_buffer_resize (outbuf, diff * bps * channels,
886 generated_samples * bps * channels);
887
888 output_offset = self->nsamples_out - self->latency - generated_samples;
889 GST_BUFFER_TIMESTAMP (outbuf) =
890 self->start_ts + gst_util_uint64_scale_int (output_offset, GST_SECOND,
891 rate);
892 GST_BUFFER_DURATION (outbuf) =
893 gst_util_uint64_scale_int (output_samples, GST_SECOND, rate);
894 if (self->start_off != GST_BUFFER_OFFSET_NONE) {
895 GST_BUFFER_OFFSET (outbuf) = self->start_off + output_offset;
896 GST_BUFFER_OFFSET_END (outbuf) =
897 GST_BUFFER_OFFSET (outbuf) + generated_samples;
898 } else {
899 GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE;
900 GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE;
901 }
902 g_mutex_unlock (&self->lock);
903
904 GST_DEBUG_OBJECT (self,
905 "Pushing buffer of size %" G_GSIZE_FORMAT " with timestamp: %"
906 GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %"
907 G_GUINT64_FORMAT ", offset_end: %" G_GUINT64_FORMAT ", nsamples_out: %d",
908 gst_buffer_get_size (outbuf),
909 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
910 GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf),
911 GST_BUFFER_OFFSET_END (outbuf), generated_samples);
912
913 return GST_FLOW_OK;
915no_samples:
916 {
917 g_mutex_unlock (&self->lock);
918 return GST_BASE_TRANSFORM_FLOW_DROPPED;
919 }
920}
921
922static gboolean
923gst_audio_fx_base_fir_filter_start (GstBaseTransform * base)
924{
926
927 self->buffer_fill = 0;
928 g_free (self->buffer);
929 self->buffer = NULL;
930 self->start_ts = GST_CLOCK_TIME_NONE;
931 self->start_off = GST_BUFFER_OFFSET_NONE;
932 self->nsamples_out = 0;
933 self->nsamples_in = 0;
934
935 return TRUE;
936}
937
938static gboolean
939gst_audio_fx_base_fir_filter_stop (GstBaseTransform * base)
940{
943 g_free (self->buffer);
944 self->buffer = NULL;
945 self->buffer_length = 0;
946
947 return TRUE;
948}
949
950static gboolean
951gst_audio_fx_base_fir_filter_query (GstBaseTransform * trans,
952 GstPadDirection direction, GstQuery * query)
953{
955 gboolean res = TRUE;
956
957 switch (GST_QUERY_TYPE (query)) {
958 case GST_QUERY_LATENCY:
959 {
960 GstClockTime min, max;
961 gboolean live;
962 guint64 latency;
963 gint rate = GST_AUDIO_FILTER_RATE (self);
964
965 if (rate == 0) {
966 res = FALSE;
967 } else if ((res =
968 gst_pad_peer_query (GST_BASE_TRANSFORM (self)->sinkpad, query))) {
969 gst_query_parse_latency (query, &live, &min, &max);
970
971 GST_DEBUG_OBJECT (self, "Peer latency: min %"
972 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
973 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
974
975 if (self->fft && !self->low_latency)
976 latency = self->block_length - self->kernel_length + 1;
977 else
978 latency = self->latency;
979
980 /* add our own latency */
981 latency = gst_util_uint64_scale_round (latency, GST_SECOND, rate);
982
983 GST_DEBUG_OBJECT (self, "Our latency: %"
984 GST_TIME_FORMAT, GST_TIME_ARGS (latency));
985
986 min += latency;
987 if (max != GST_CLOCK_TIME_NONE)
988 max += latency;
989
990 GST_DEBUG_OBJECT (self, "Calculated total latency : min %"
991 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
992 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
993
994 gst_query_set_latency (query, live, min, max);
995 }
996 break;
997 }
998 default:
999 res =
1000 GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
1001 query);
1002 break;
1003 }
1004 return res;
1005}
1006
1007static gboolean
1008gst_audio_fx_base_fir_filter_sink_event (GstBaseTransform * base,
1009 GstEvent * event)
1010{
1012
1013 switch (GST_EVENT_TYPE (event)) {
1014 case GST_EVENT_EOS:
1016 self->start_ts = GST_CLOCK_TIME_NONE;
1017 self->start_off = GST_BUFFER_OFFSET_NONE;
1018 self->nsamples_out = 0;
1019 self->nsamples_in = 0;
1020 break;
1021 default:
1022 break;
1023 }
1024
1025 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (base, event);
1026}
1027
1028void
1030 gdouble * kernel, guint kernel_length, guint64 latency,
1031 const GstAudioInfo * info)
1032{
1033 gboolean latency_changed;
1034 GstAudioFormat format;
1035 gint channels;
1036
1037 g_return_if_fail (kernel != NULL);
1038 g_return_if_fail (self != NULL);
1039
1040 g_mutex_lock (&self->lock);
1041
1042 latency_changed = (self->latency != latency
1043 || (!self->low_latency && self->kernel_length < FFT_THRESHOLD
1044 && kernel_length >= FFT_THRESHOLD)
1045 || (!self->low_latency && self->kernel_length >= FFT_THRESHOLD
1046 && kernel_length < FFT_THRESHOLD));
1047
1048 /* FIXME: If the latency changes, the buffer size changes too and we
1049 * have to drain in any case until this is fixed in the future */
1050 if (self->buffer && (!self->drain_on_changes || latency_changed)) {
1052 self->start_ts = GST_CLOCK_TIME_NONE;
1053 self->start_off = GST_BUFFER_OFFSET_NONE;
1054 self->nsamples_out = 0;
1055 self->nsamples_in = 0;
1056 self->buffer_fill = 0;
1057 }
1058
1059 g_free (self->kernel);
1060 if (!self->drain_on_changes || latency_changed) {
1061 g_free (self->buffer);
1062 self->buffer = NULL;
1063 self->buffer_fill = 0;
1064 self->buffer_length = 0;
1065 }
1066
1067 self->kernel = kernel;
1068 self->kernel_length = kernel_length;
1069
1070 if (info) {
1071 format = GST_AUDIO_INFO_FORMAT (info);
1072 channels = GST_AUDIO_INFO_CHANNELS (info);
1073 } else {
1074 format = GST_AUDIO_FILTER_FORMAT (self);
1075 channels = GST_AUDIO_FILTER_CHANNELS (self);
1076 }
1077
1080
1081 if (latency_changed) {
1082 self->latency = latency;
1083 gst_element_post_message (GST_ELEMENT (self),
1084 gst_message_new_latency (GST_OBJECT (self)));
1085 }
1086
1087 g_mutex_unlock (&self->lock);
1088}
char * format
Definition: RaspiStill.c:190
static guint process_fft_1_64(GstAudioFXBaseFIRFilter *self, const gdouble *src, gdouble *dst, guint input_samples)
static gboolean gst_audio_fx_base_fir_filter_query(GstBaseTransform *trans, GstPadDirection direction, GstQuery *quer)
static void gst_audio_fx_base_fir_filter_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
static GstFlowReturn gst_audio_fx_base_fir_filter_transform(GstBaseTransform *base, GstBuffer *inbuf, GstBuffer *outbuf)
static guint process_32(GstAudioFXBaseFIRFilter *self, const gfloat *src, gfloat *dst, guint input_samples)
static guint process_2_64(GstAudioFXBaseFIRFilter *self, const gdouble *src, gdouble *dst, guint input_samples)
static guint process_64(GstAudioFXBaseFIRFilter *self, const gdouble *src, gdouble *dst, guint input_samples)
static void gst_audio_fx_base_fir_filter_class_init(GstAudioFXBaseFIRFilterClass *klass)
static void gst_audio_fx_base_fir_filter_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
#define DEFAULT_LOW_LATENCY
static guint process_2_32(GstAudioFXBaseFIRFilter *self, const gfloat *src, gfloat *dst, guint input_samples)
G_DEFINE_TYPE(GstAudioFXBaseFIRFilter, gst_audio_fx_base_fir_filter, GST_TYPE_AUDIO_FILTER)
static guint process_fft_64(GstAudioFXBaseFIRFilter *self, const gdouble *src, gdouble *dst, guint input_samples)
static guint process_1_32(GstAudioFXBaseFIRFilter *self, const gfloat *src, gfloat *dst, guint input_samples)
void gst_audio_fx_base_fir_filter_set_kernel(GstAudioFXBaseFIRFilter *self, gdouble *kernel, guint kernel_length, guint64 latency, const GstAudioInfo *info)
#define DEFINE_PROCESS_FUNC(width, ctype)
static void gst_audio_fx_base_fir_filter_finalize(GObject *object)
void gst_audio_fx_base_fir_filter_push_residue(GstAudioFXBaseFIRFilter *self)
#define DEFINE_FFT_PROCESS_FUNC_FIXED_CHANNELS(width, channels, ctype)
static void gst_audio_fx_base_fir_filter_init(GstAudioFXBaseFIRFilter *self)
#define DEFINE_FFT_PROCESS_FUNC(width, ctype)
static gboolean gst_audio_fx_base_fir_filter_transform_size(GstBaseTransform *base, GstPadDirection direction, GstCaps *caps, gsize size, GstCaps *othercaps, gsize *othersize)
static void gst_audio_fx_base_fir_filter_select_process_function(GstAudioFXBaseFIRFilter *self, GstAudioFormat format, gint channels)
#define DEFINE_PROCESS_FUNC_FIXED_CHANNELS(width, channels, ctype)
#define ALLOWED_CAPS
@ PROP_LOW_LATENCY
@ PROP_DRAIN_ON_CHANGES
static gboolean gst_audio_fx_base_fir_filter_setup(GstAudioFilter *base, const GstAudioInfo *info)
#define DEFAULT_DRAIN_ON_CHANGES
static gboolean gst_audio_fx_base_fir_filter_sink_event(GstBaseTransform *base, GstEvent *event)
static guint process_fft_2_32(GstAudioFXBaseFIRFilter *self, const gfloat *src, gfloat *dst, guint input_samples)
static guint process_fft_32(GstAudioFXBaseFIRFilter *self, const gfloat *src, gfloat *dst, guint input_samples)
static gboolean gst_audio_fx_base_fir_filter_stop(GstBaseTransform *base)
static guint process_fft_1_32(GstAudioFXBaseFIRFilter *self, const gfloat *src, gfloat *dst, guint input_samples)
#define GST_CAT_DEFAULT
static gboolean gst_audio_fx_base_fir_filter_start(GstBaseTransform *base)
GST_DEBUG_CATEGORY_STATIC(gst_audio_fx_base_fir_filter_debug)
static guint process_1_64(GstAudioFXBaseFIRFilter *self, const gdouble *src, gdouble *dst, guint input_samples)
static guint process_fft_2_64(GstAudioFXBaseFIRFilter *self, const gdouble *src, gdouble *dst, guint input_samples)
#define FFT_THRESHOLD
static void gst_audio_fx_base_fir_filter_calculate_frequency_response(GstAudioFXBaseFIRFilter *self)
#define GST_TYPE_AUDIO_FX_BASE_FIR_FILTER
#define GST_AUDIO_FX_BASE_FIR_FILTER(obj)
guint(* GstAudioFXBaseFIRFilterProcessFunc)(GstAudioFXBaseFIRFilter *, const guint8 *, guint8 *, guint)
#define parent_class
Definition: gstsbcparse.c:83
GstAudioFXBaseFIRFilterProcessFunc process
GstFFTF64Complex * frequency_response
GstFFTF64Complex * fft_buffer