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., 59 Temple Place - Suite 330, |
|
21 * Boston, MA 02111-1307, USA. |
|
22 * |
|
23 * |
|
24 * TODO: - Implement the convolution in place, probably only makes sense |
|
25 * when using FFT convolution as currently the convolution itself |
|
26 * is probably the bottleneck |
|
27 * - Maybe allow cascading the filter to get a better stopband attenuation. |
|
28 * Can be done by convolving a filter kernel with itself |
|
29 */ |
|
30 |
|
31 #ifdef HAVE_CONFIG_H |
|
32 #include "config.h" |
|
33 #endif |
|
34 |
|
35 #include <string.h> |
|
36 #include <math.h> |
|
37 #include <gst/gst.h> |
|
38 #include <gst/audio/gstaudiofilter.h> |
|
39 #include <gst/controller/gstcontroller.h> |
|
40 |
|
41 #include "audiofxbasefirfilter.h" |
|
42 |
|
43 #define GST_CAT_DEFAULT gst_audio_fx_base_fir_filter_debug |
|
44 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); |
|
45 |
|
46 #define ALLOWED_CAPS \ |
|
47 "audio/x-raw-float, " \ |
|
48 " width = (int) { 32, 64 }, " \ |
|
49 " endianness = (int) BYTE_ORDER, " \ |
|
50 " rate = (int) [ 1, MAX ], " \ |
|
51 " channels = (int) [ 1, MAX ]" |
|
52 |
|
53 #define DEBUG_INIT(bla) \ |
|
54 GST_DEBUG_CATEGORY_INIT (gst_audio_fx_base_fir_filter_debug, "audiofxbasefirfilter", 0, \ |
|
55 "FIR filter base class"); |
|
56 |
|
57 GST_BOILERPLATE_FULL (GstAudioFXBaseFIRFilter, gst_audio_fx_base_fir_filter, |
|
58 GstAudioFilter, GST_TYPE_AUDIO_FILTER, DEBUG_INIT); |
|
59 |
|
60 static GstFlowReturn gst_audio_fx_base_fir_filter_transform (GstBaseTransform * |
|
61 base, GstBuffer * inbuf, GstBuffer * outbuf); |
|
62 static gboolean gst_audio_fx_base_fir_filter_start (GstBaseTransform * base); |
|
63 static gboolean gst_audio_fx_base_fir_filter_stop (GstBaseTransform * base); |
|
64 static gboolean gst_audio_fx_base_fir_filter_event (GstBaseTransform * base, |
|
65 GstEvent * event); |
|
66 static gboolean gst_audio_fx_base_fir_filter_setup (GstAudioFilter * base, |
|
67 GstRingBufferSpec * format); |
|
68 |
|
69 static gboolean gst_audio_fx_base_fir_filter_query (GstPad * pad, |
|
70 GstQuery * query); |
|
71 static const GstQueryType *gst_audio_fx_base_fir_filter_query_type (GstPad * |
|
72 pad); |
|
73 |
|
74 /* Element class */ |
|
75 |
|
76 static void |
|
77 gst_audio_fx_base_fir_filter_dispose (GObject * object) |
|
78 { |
|
79 GstAudioFXBaseFIRFilter *self = GST_AUDIO_FX_BASE_FIR_FILTER (object); |
|
80 |
|
81 if (self->residue) { |
|
82 g_free (self->residue); |
|
83 self->residue = NULL; |
|
84 } |
|
85 |
|
86 if (self->kernel) { |
|
87 g_free (self->kernel); |
|
88 self->kernel = NULL; |
|
89 } |
|
90 |
|
91 G_OBJECT_CLASS (parent_class)->dispose (object); |
|
92 } |
|
93 |
|
94 static void |
|
95 gst_audio_fx_base_fir_filter_base_init (gpointer g_class) |
|
96 { |
|
97 GstCaps *caps; |
|
98 |
|
99 caps = gst_caps_from_string (ALLOWED_CAPS); |
|
100 gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (g_class), |
|
101 caps); |
|
102 gst_caps_unref (caps); |
|
103 } |
|
104 |
|
105 static void |
|
106 gst_audio_fx_base_fir_filter_class_init (GstAudioFXBaseFIRFilterClass * klass) |
|
107 { |
|
108 GObjectClass *gobject_class = (GObjectClass *) klass; |
|
109 GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass; |
|
110 GstAudioFilterClass *filter_class = (GstAudioFilterClass *) klass; |
|
111 |
|
112 gobject_class->dispose = gst_audio_fx_base_fir_filter_dispose; |
|
113 |
|
114 trans_class->transform = |
|
115 GST_DEBUG_FUNCPTR (gst_audio_fx_base_fir_filter_transform); |
|
116 trans_class->start = GST_DEBUG_FUNCPTR (gst_audio_fx_base_fir_filter_start); |
|
117 trans_class->stop = GST_DEBUG_FUNCPTR (gst_audio_fx_base_fir_filter_stop); |
|
118 trans_class->event = GST_DEBUG_FUNCPTR (gst_audio_fx_base_fir_filter_event); |
|
119 filter_class->setup = GST_DEBUG_FUNCPTR (gst_audio_fx_base_fir_filter_setup); |
|
120 } |
|
121 |
|
122 static void |
|
123 gst_audio_fx_base_fir_filter_init (GstAudioFXBaseFIRFilter * self, |
|
124 GstAudioFXBaseFIRFilterClass * g_class) |
|
125 { |
|
126 self->kernel = NULL; |
|
127 self->residue = NULL; |
|
128 |
|
129 self->next_ts = GST_CLOCK_TIME_NONE; |
|
130 self->next_off = GST_BUFFER_OFFSET_NONE; |
|
131 |
|
132 gst_pad_set_query_function (GST_BASE_TRANSFORM (self)->srcpad, |
|
133 gst_audio_fx_base_fir_filter_query); |
|
134 gst_pad_set_query_type_function (GST_BASE_TRANSFORM (self)->srcpad, |
|
135 gst_audio_fx_base_fir_filter_query_type); |
|
136 } |
|
137 |
|
138 #define DEFINE_PROCESS_FUNC(width,ctype) \ |
|
139 static void \ |
|
140 process_##width (GstAudioFXBaseFIRFilter * self, g##ctype * src, g##ctype * dst, guint input_samples) \ |
|
141 { \ |
|
142 gint kernel_length = self->kernel_length; \ |
|
143 gint i, j, k, l; \ |
|
144 gint channels = GST_AUDIO_FILTER (self)->format.channels; \ |
|
145 gint res_start; \ |
|
146 \ |
|
147 /* convolution */ \ |
|
148 for (i = 0; i < input_samples; i++) { \ |
|
149 dst[i] = 0.0; \ |
|
150 k = i % channels; \ |
|
151 l = i / channels; \ |
|
152 for (j = 0; j < kernel_length; j++) \ |
|
153 if (l < j) \ |
|
154 dst[i] += \ |
|
155 self->residue[(kernel_length + l - j) * channels + \ |
|
156 k] * self->kernel[j]; \ |
|
157 else \ |
|
158 dst[i] += src[(l - j) * channels + k] * self->kernel[j]; \ |
|
159 } \ |
|
160 \ |
|
161 /* copy the tail of the current input buffer to the residue, while \ |
|
162 * keeping parts of the residue if the input buffer is smaller than \ |
|
163 * the kernel length */ \ |
|
164 if (input_samples < kernel_length * channels) \ |
|
165 res_start = kernel_length * channels - input_samples; \ |
|
166 else \ |
|
167 res_start = 0; \ |
|
168 \ |
|
169 for (i = 0; i < res_start; i++) \ |
|
170 self->residue[i] = self->residue[i + input_samples]; \ |
|
171 for (i = res_start; i < kernel_length * channels; i++) \ |
|
172 self->residue[i] = src[input_samples - kernel_length * channels + i]; \ |
|
173 \ |
|
174 self->residue_length += kernel_length * channels - res_start; \ |
|
175 if (self->residue_length > kernel_length * channels) \ |
|
176 self->residue_length = kernel_length * channels; \ |
|
177 } |
|
178 |
|
179 DEFINE_PROCESS_FUNC (32, float); |
|
180 DEFINE_PROCESS_FUNC (64, double); |
|
181 |
|
182 #undef DEFINE_PROCESS_FUNC |
|
183 |
|
184 void |
|
185 gst_audio_fx_base_fir_filter_push_residue (GstAudioFXBaseFIRFilter * self) |
|
186 { |
|
187 GstBuffer *outbuf; |
|
188 GstFlowReturn res; |
|
189 gint rate = GST_AUDIO_FILTER (self)->format.rate; |
|
190 gint channels = GST_AUDIO_FILTER (self)->format.channels; |
|
191 gint outsize, outsamples; |
|
192 gint diffsize, diffsamples; |
|
193 guint8 *in, *out; |
|
194 |
|
195 if (channels == 0 || rate == 0) { |
|
196 self->residue_length = 0; |
|
197 return; |
|
198 } |
|
199 |
|
200 /* Calculate the number of samples and their memory size that |
|
201 * should be pushed from the residue */ |
|
202 outsamples = MIN (self->latency, self->residue_length / channels); |
|
203 outsize = outsamples * channels * (GST_AUDIO_FILTER (self)->format.width / 8); |
|
204 if (outsize == 0) { |
|
205 self->residue_length = 0; |
|
206 return; |
|
207 } |
|
208 |
|
209 /* Process the difference between latency and residue_length samples |
|
210 * to start at the actual data instead of starting at the zeros before |
|
211 * when we only got one buffer smaller than latency */ |
|
212 diffsamples = self->latency - self->residue_length / channels; |
|
213 diffsize = |
|
214 diffsamples * channels * (GST_AUDIO_FILTER (self)->format.width / 8); |
|
215 if (diffsize > 0) { |
|
216 in = g_new0 (guint8, diffsize); |
|
217 out = g_new0 (guint8, diffsize); |
|
218 self->process (self, in, out, diffsamples * channels); |
|
219 g_free (in); |
|
220 g_free (out); |
|
221 } |
|
222 |
|
223 res = gst_pad_alloc_buffer (GST_BASE_TRANSFORM (self)->srcpad, |
|
224 GST_BUFFER_OFFSET_NONE, outsize, |
|
225 GST_PAD_CAPS (GST_BASE_TRANSFORM (self)->srcpad), &outbuf); |
|
226 |
|
227 if (G_UNLIKELY (res != GST_FLOW_OK)) { |
|
228 GST_WARNING_OBJECT (self, "failed allocating buffer of %d bytes", outsize); |
|
229 self->residue_length = 0; |
|
230 return; |
|
231 } |
|
232 |
|
233 /* Convolve the residue with zeros to get the actual remaining data */ |
|
234 in = g_new0 (guint8, outsize); |
|
235 self->process (self, in, GST_BUFFER_DATA (outbuf), outsamples * channels); |
|
236 g_free (in); |
|
237 |
|
238 /* Set timestamp, offset, etc from the values we |
|
239 * saved when processing the regular buffers */ |
|
240 if (GST_CLOCK_TIME_IS_VALID (self->next_ts)) |
|
241 GST_BUFFER_TIMESTAMP (outbuf) = self->next_ts; |
|
242 else |
|
243 GST_BUFFER_TIMESTAMP (outbuf) = 0; |
|
244 GST_BUFFER_DURATION (outbuf) = |
|
245 gst_util_uint64_scale (outsamples, GST_SECOND, rate); |
|
246 self->next_ts += gst_util_uint64_scale (outsamples, GST_SECOND, rate); |
|
247 |
|
248 if (self->next_off != GST_BUFFER_OFFSET_NONE) { |
|
249 GST_BUFFER_OFFSET (outbuf) = self->next_off; |
|
250 GST_BUFFER_OFFSET_END (outbuf) = self->next_off + outsamples; |
|
251 self->next_off = GST_BUFFER_OFFSET_END (outbuf); |
|
252 } |
|
253 |
|
254 GST_DEBUG_OBJECT (self, "Pushing residue buffer of size %d with timestamp: %" |
|
255 GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %lld," |
|
256 " offset_end: %lld, nsamples: %d", GST_BUFFER_SIZE (outbuf), |
|
257 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), |
|
258 GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf), |
|
259 GST_BUFFER_OFFSET_END (outbuf), outsamples); |
|
260 |
|
261 res = gst_pad_push (GST_BASE_TRANSFORM (self)->srcpad, outbuf); |
|
262 |
|
263 if (G_UNLIKELY (res != GST_FLOW_OK)) { |
|
264 GST_WARNING_OBJECT (self, "failed to push residue"); |
|
265 } |
|
266 |
|
267 self->residue_length = 0; |
|
268 } |
|
269 |
|
270 /* GstAudioFilter vmethod implementations */ |
|
271 |
|
272 /* get notified of caps and plug in the correct process function */ |
|
273 static gboolean |
|
274 gst_audio_fx_base_fir_filter_setup (GstAudioFilter * base, |
|
275 GstRingBufferSpec * format) |
|
276 { |
|
277 GstAudioFXBaseFIRFilter *self = GST_AUDIO_FX_BASE_FIR_FILTER (base); |
|
278 gboolean ret = TRUE; |
|
279 |
|
280 if (self->residue) { |
|
281 gst_audio_fx_base_fir_filter_push_residue (self); |
|
282 g_free (self->residue); |
|
283 self->residue = NULL; |
|
284 self->residue_length = 0; |
|
285 self->next_ts = GST_CLOCK_TIME_NONE; |
|
286 self->next_off = GST_BUFFER_OFFSET_NONE; |
|
287 } |
|
288 |
|
289 if (format->width == 32) |
|
290 self->process = (GstAudioFXBaseFIRFilterProcessFunc) process_32; |
|
291 else if (format->width == 64) |
|
292 self->process = (GstAudioFXBaseFIRFilterProcessFunc) process_64; |
|
293 else |
|
294 ret = FALSE; |
|
295 |
|
296 return TRUE; |
|
297 } |
|
298 |
|
299 /* GstBaseTransform vmethod implementations */ |
|
300 |
|
301 static GstFlowReturn |
|
302 gst_audio_fx_base_fir_filter_transform (GstBaseTransform * base, |
|
303 GstBuffer * inbuf, GstBuffer * outbuf) |
|
304 { |
|
305 GstAudioFXBaseFIRFilter *self = GST_AUDIO_FX_BASE_FIR_FILTER (base); |
|
306 GstClockTime timestamp; |
|
307 gint channels = GST_AUDIO_FILTER (self)->format.channels; |
|
308 gint rate = GST_AUDIO_FILTER (self)->format.rate; |
|
309 gint input_samples = |
|
310 GST_BUFFER_SIZE (outbuf) / (GST_AUDIO_FILTER (self)->format.width / 8); |
|
311 gint output_samples = input_samples; |
|
312 gint diff = 0; |
|
313 |
|
314 timestamp = GST_BUFFER_TIMESTAMP (outbuf); |
|
315 if (!GST_CLOCK_TIME_IS_VALID (timestamp)) { |
|
316 GST_ERROR_OBJECT (self, "Invalid timestamp"); |
|
317 return GST_FLOW_ERROR; |
|
318 } |
|
319 |
|
320 gst_object_sync_values (G_OBJECT (self), timestamp); |
|
321 |
|
322 g_return_val_if_fail (self->kernel != NULL, GST_FLOW_ERROR); |
|
323 g_return_val_if_fail (channels != 0, GST_FLOW_ERROR); |
|
324 |
|
325 if (!self->residue) |
|
326 self->residue = g_new0 (gdouble, self->kernel_length * channels); |
|
327 |
|
328 /* Reset the residue if already existing on discont buffers */ |
|
329 if (GST_BUFFER_IS_DISCONT (inbuf) || (GST_CLOCK_TIME_IS_VALID (self->next_ts) |
|
330 && timestamp - gst_util_uint64_scale (MIN (self->latency, |
|
331 self->residue_length / channels), GST_SECOND, |
|
332 rate) - self->next_ts > 5 * GST_MSECOND)) { |
|
333 GST_DEBUG_OBJECT (self, "Discontinuity detected - flushing"); |
|
334 if (GST_CLOCK_TIME_IS_VALID (self->next_ts)) |
|
335 gst_audio_fx_base_fir_filter_push_residue (self); |
|
336 self->residue_length = 0; |
|
337 self->next_ts = timestamp; |
|
338 self->next_off = GST_BUFFER_OFFSET (inbuf); |
|
339 } else if (!GST_CLOCK_TIME_IS_VALID (self->next_ts)) { |
|
340 self->next_ts = timestamp; |
|
341 self->next_off = GST_BUFFER_OFFSET (inbuf); |
|
342 } |
|
343 |
|
344 /* Calculate the number of samples we can push out now without outputting |
|
345 * latency zeros in the beginning */ |
|
346 diff = self->latency * channels - self->residue_length; |
|
347 if (diff > 0) |
|
348 output_samples -= diff; |
|
349 |
|
350 self->process (self, GST_BUFFER_DATA (inbuf), GST_BUFFER_DATA (outbuf), |
|
351 input_samples); |
|
352 |
|
353 if (output_samples <= 0) { |
|
354 return GST_BASE_TRANSFORM_FLOW_DROPPED; |
|
355 } |
|
356 |
|
357 GST_BUFFER_TIMESTAMP (outbuf) = self->next_ts; |
|
358 GST_BUFFER_DURATION (outbuf) = |
|
359 gst_util_uint64_scale (output_samples / channels, GST_SECOND, rate); |
|
360 GST_BUFFER_OFFSET (outbuf) = self->next_off; |
|
361 if (GST_BUFFER_OFFSET_IS_VALID (outbuf)) |
|
362 GST_BUFFER_OFFSET_END (outbuf) = self->next_off + output_samples / channels; |
|
363 else |
|
364 GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE; |
|
365 |
|
366 if (output_samples < input_samples) { |
|
367 GST_BUFFER_DATA (outbuf) += |
|
368 diff * (GST_AUDIO_FILTER (self)->format.width / 8); |
|
369 GST_BUFFER_SIZE (outbuf) -= |
|
370 diff * (GST_AUDIO_FILTER (self)->format.width / 8); |
|
371 } |
|
372 |
|
373 self->next_ts += GST_BUFFER_DURATION (outbuf); |
|
374 self->next_off = GST_BUFFER_OFFSET_END (outbuf); |
|
375 |
|
376 GST_DEBUG_OBJECT (self, "Pushing buffer of size %d with timestamp: %" |
|
377 GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %lld," |
|
378 " offset_end: %lld, nsamples: %d", GST_BUFFER_SIZE (outbuf), |
|
379 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), |
|
380 GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf), |
|
381 GST_BUFFER_OFFSET_END (outbuf), output_samples / channels); |
|
382 |
|
383 return GST_FLOW_OK; |
|
384 } |
|
385 |
|
386 static gboolean |
|
387 gst_audio_fx_base_fir_filter_start (GstBaseTransform * base) |
|
388 { |
|
389 GstAudioFXBaseFIRFilter *self = GST_AUDIO_FX_BASE_FIR_FILTER (base); |
|
390 |
|
391 self->residue_length = 0; |
|
392 self->next_ts = GST_CLOCK_TIME_NONE; |
|
393 self->next_off = GST_BUFFER_OFFSET_NONE; |
|
394 |
|
395 return TRUE; |
|
396 } |
|
397 |
|
398 static gboolean |
|
399 gst_audio_fx_base_fir_filter_stop (GstBaseTransform * base) |
|
400 { |
|
401 GstAudioFXBaseFIRFilter *self = GST_AUDIO_FX_BASE_FIR_FILTER (base); |
|
402 |
|
403 g_free (self->residue); |
|
404 self->residue = NULL; |
|
405 |
|
406 return TRUE; |
|
407 } |
|
408 |
|
409 static gboolean |
|
410 gst_audio_fx_base_fir_filter_query (GstPad * pad, GstQuery * query) |
|
411 { |
|
412 GstAudioFXBaseFIRFilter *self = |
|
413 GST_AUDIO_FX_BASE_FIR_FILTER (gst_pad_get_parent (pad)); |
|
414 gboolean res = TRUE; |
|
415 |
|
416 switch (GST_QUERY_TYPE (query)) { |
|
417 case GST_QUERY_LATENCY: |
|
418 { |
|
419 GstClockTime min, max; |
|
420 gboolean live; |
|
421 guint64 latency; |
|
422 GstPad *peer; |
|
423 gint rate = GST_AUDIO_FILTER (self)->format.rate; |
|
424 |
|
425 if (rate == 0) { |
|
426 res = FALSE; |
|
427 } else if ((peer = gst_pad_get_peer (GST_BASE_TRANSFORM (self)->sinkpad))) { |
|
428 if ((res = gst_pad_query (peer, query))) { |
|
429 gst_query_parse_latency (query, &live, &min, &max); |
|
430 |
|
431 GST_DEBUG_OBJECT (self, "Peer latency: min %" |
|
432 GST_TIME_FORMAT " max %" GST_TIME_FORMAT, |
|
433 GST_TIME_ARGS (min), GST_TIME_ARGS (max)); |
|
434 |
|
435 /* add our own latency */ |
|
436 latency = gst_util_uint64_scale (self->latency, GST_SECOND, rate); |
|
437 |
|
438 GST_DEBUG_OBJECT (self, "Our latency: %" |
|
439 GST_TIME_FORMAT, GST_TIME_ARGS (latency)); |
|
440 |
|
441 min += latency; |
|
442 if (max != GST_CLOCK_TIME_NONE) |
|
443 max += latency; |
|
444 |
|
445 GST_DEBUG_OBJECT (self, "Calculated total latency : min %" |
|
446 GST_TIME_FORMAT " max %" GST_TIME_FORMAT, |
|
447 GST_TIME_ARGS (min), GST_TIME_ARGS (max)); |
|
448 |
|
449 gst_query_set_latency (query, live, min, max); |
|
450 } |
|
451 gst_object_unref (peer); |
|
452 } |
|
453 break; |
|
454 } |
|
455 default: |
|
456 res = gst_pad_query_default (pad, query); |
|
457 break; |
|
458 } |
|
459 gst_object_unref (self); |
|
460 return res; |
|
461 } |
|
462 |
|
463 static const GstQueryType * |
|
464 gst_audio_fx_base_fir_filter_query_type (GstPad * pad) |
|
465 { |
|
466 static const GstQueryType types[] = { |
|
467 GST_QUERY_LATENCY, |
|
468 0 |
|
469 }; |
|
470 |
|
471 return types; |
|
472 } |
|
473 |
|
474 static gboolean |
|
475 gst_audio_fx_base_fir_filter_event (GstBaseTransform * base, GstEvent * event) |
|
476 { |
|
477 GstAudioFXBaseFIRFilter *self = GST_AUDIO_FX_BASE_FIR_FILTER (base); |
|
478 |
|
479 switch (GST_EVENT_TYPE (event)) { |
|
480 case GST_EVENT_EOS: |
|
481 gst_audio_fx_base_fir_filter_push_residue (self); |
|
482 self->next_ts = GST_CLOCK_TIME_NONE; |
|
483 self->next_off = GST_BUFFER_OFFSET_NONE; |
|
484 break; |
|
485 default: |
|
486 break; |
|
487 } |
|
488 |
|
489 return GST_BASE_TRANSFORM_CLASS (parent_class)->event (base, event); |
|
490 } |
|
491 |
|
492 void |
|
493 gst_audio_fx_base_fir_filter_set_kernel (GstAudioFXBaseFIRFilter * self, |
|
494 gdouble * kernel, guint kernel_length, guint64 latency) |
|
495 { |
|
496 g_return_if_fail (kernel != NULL); |
|
497 g_return_if_fail (self != NULL); |
|
498 |
|
499 GST_BASE_TRANSFORM_LOCK (self); |
|
500 if (self->residue) { |
|
501 gst_audio_fx_base_fir_filter_push_residue (self); |
|
502 self->next_ts = GST_CLOCK_TIME_NONE; |
|
503 self->next_off = GST_BUFFER_OFFSET_NONE; |
|
504 self->residue_length = 0; |
|
505 } |
|
506 |
|
507 g_free (self->kernel); |
|
508 g_free (self->residue); |
|
509 |
|
510 self->kernel = kernel; |
|
511 self->kernel_length = kernel_length; |
|
512 |
|
513 if (GST_AUDIO_FILTER (self)->format.channels) { |
|
514 self->residue = |
|
515 g_new0 (gdouble, |
|
516 kernel_length * GST_AUDIO_FILTER (self)->format.channels); |
|
517 self->residue_length = 0; |
|
518 } |
|
519 |
|
520 if (self->latency != latency) { |
|
521 self->latency = latency; |
|
522 gst_element_post_message (GST_ELEMENT (self), |
|
523 gst_message_new_latency (GST_OBJECT (self))); |
|
524 } |
|
525 |
|
526 GST_BASE_TRANSFORM_UNLOCK (self); |
|
527 } |
|