gst_plugins_good/gst/audiofx/audiofxbasefirfilter.c
changeset 27 d43ce56a1534
parent 23 29ecd5cb86b3
child 31 aec498aab1d3
equal deleted inserted replaced
23:29ecd5cb86b3 27:d43ce56a1534
     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 }