aout: filters: expose CreateFilter internally
[vlc.git] / src / audio_output / filters.c
blob11a77e95b1fd6f2ccbf4cd9f3700d1ed9d320052
1 /*****************************************************************************
2 * filters.c : audio output filters management
3 *****************************************************************************
4 * Copyright (C) 2002-2007 VLC authors and VideoLAN
6 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program 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
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 /*****************************************************************************
24 * Preamble
25 *****************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <math.h>
31 #include <string.h>
32 #include <assert.h>
34 #include <vlc_common.h>
35 #include <vlc_dialog.h>
36 #include <vlc_modules.h>
37 #include <vlc_aout.h>
38 #include <vlc_filter.h>
39 #include <libvlc.h>
40 #include "aout_internal.h"
41 #include "../video_output/vout_internal.h" /* for vout_Request */
43 filter_t *aout_filter_Create(vlc_object_t *obj, const filter_owner_t *restrict owner,
44 const char *type, const char *name,
45 const audio_sample_format_t *infmt,
46 const audio_sample_format_t *outfmt,
47 config_chain_t *cfg, bool const_fmt)
49 filter_t *filter = vlc_custom_create (obj, sizeof (*filter), type);
50 if (unlikely(filter == NULL))
51 return NULL;
53 if (owner != NULL)
54 filter->owner = *owner;
55 filter->p_cfg = cfg;
56 filter->fmt_in.audio = *infmt;
57 filter->fmt_in.i_codec = infmt->i_format;
58 filter->fmt_out.audio = *outfmt;
59 filter->fmt_out.i_codec = outfmt->i_format;
61 #ifndef NDEBUG
62 /* Assure that infmt/oufmt are well prepared and that channels
63 * configurations are valid*/
64 if( infmt->i_physical_channels != 0 )
65 assert( aout_FormatNbChannels( infmt ) == infmt->i_channels );
66 if( outfmt->i_physical_channels != 0 )
67 assert( aout_FormatNbChannels( outfmt ) == outfmt->i_channels );
68 #endif
70 filter->p_module = module_need (filter, type, name, false);
72 #ifndef NDEBUG
73 if (filter->p_module == NULL || const_fmt)
75 /* If probing failed, formats shall not have been modified. */
76 assert (AOUT_FMTS_IDENTICAL(&filter->fmt_in.audio, infmt));
77 assert (AOUT_FMTS_IDENTICAL(&filter->fmt_out.audio, outfmt));
79 #endif
81 if (filter->p_module == NULL)
83 vlc_object_delete(filter);
84 filter = NULL;
86 else
87 assert (filter->pf_audio_filter != NULL);
88 return filter;
91 static filter_t *FindConverter (vlc_object_t *obj,
92 const audio_sample_format_t *infmt,
93 const audio_sample_format_t *outfmt)
95 return aout_filter_Create(obj, NULL, "audio converter", NULL, infmt, outfmt,
96 NULL, true);
99 static filter_t *FindResampler (vlc_object_t *obj,
100 const audio_sample_format_t *infmt,
101 const audio_sample_format_t *outfmt)
103 char *modlist = var_InheritString(obj, "audio-resampler");
104 filter_t *filter = aout_filter_Create(obj, NULL, "audio resampler", modlist,
105 infmt, outfmt, NULL, true);
106 free(modlist);
107 return filter;
111 * Destroys a chain of audio filters.
113 static void aout_FiltersPipelineDestroy(filter_t *const *filters, unsigned n)
115 for( unsigned i = 0; i < n; i++ )
117 filter_t *p_filter = filters[i];
119 module_unneed( p_filter, p_filter->p_module );
120 vlc_object_delete(p_filter);
124 static filter_t *TryFormat (vlc_object_t *obj, vlc_fourcc_t codec,
125 audio_sample_format_t *restrict fmt)
127 audio_sample_format_t output = *fmt;
129 assert (codec != fmt->i_format);
130 output.i_format = codec;
131 aout_FormatPrepare (&output);
133 filter_t *filter = FindConverter (obj, fmt, &output);
134 if (filter != NULL)
135 *fmt = output;
136 return filter;
140 * Allocates audio format conversion filters
141 * @param obj parent VLC object for new filters
142 * @param filters table of filters [IN/OUT]
143 * @param count pointer to the number of filters in the table [IN/OUT]
144 * @param max size of filters table [IN]
145 * @param infmt input audio format
146 * @param outfmt output audio format
147 * @return 0 on success, -1 on failure
149 static int aout_FiltersPipelineCreate(vlc_object_t *obj, filter_t **filters,
150 unsigned *count, unsigned max,
151 const audio_sample_format_t *restrict infmt,
152 const audio_sample_format_t *restrict outfmt,
153 bool headphones)
155 aout_FormatsPrint (obj, "conversion:", infmt, outfmt);
156 max -= *count;
157 filters += *count;
159 /* There is a lot of second guessing on what the conversion plugins can
160 * and cannot do. This seems hardly avoidable, the conversion problem need
161 * to be reduced somehow. */
162 audio_sample_format_t input = *infmt;
163 unsigned n = 0;
165 if (!AOUT_FMT_LINEAR(&input))
167 msg_Err(obj, "Can't convert non linear input");
168 return -1;
171 /* Remix channels */
172 if (infmt->i_physical_channels != outfmt->i_physical_channels
173 || infmt->i_chan_mode != outfmt->i_chan_mode
174 || infmt->channel_type != outfmt->channel_type)
175 { /* Remixing currently requires FL32... TODO: S16N */
176 if (input.i_format != VLC_CODEC_FL32)
178 if (n == max)
179 goto overflow;
181 filter_t *f = TryFormat (obj, VLC_CODEC_FL32, &input);
182 if (f == NULL)
184 msg_Err (obj, "cannot find %s for conversion pipeline",
185 "pre-mix converter");
186 goto error;
189 filters[n++] = f;
192 if (n == max)
193 goto overflow;
195 audio_sample_format_t output;
196 output.i_format = input.i_format;
197 output.i_rate = input.i_rate;
198 output.i_physical_channels = outfmt->i_physical_channels;
199 output.channel_type = outfmt->channel_type;
200 output.i_chan_mode = outfmt->i_chan_mode;
201 aout_FormatPrepare (&output);
203 const char *filter_type =
204 infmt->channel_type != outfmt->channel_type ?
205 "audio renderer" : "audio converter";
207 config_chain_t *cfg = NULL;
208 if (headphones)
209 config_ChainParseOptions(&cfg, "{headphones=true}");
210 filter_t *f = aout_filter_Create(obj, NULL, filter_type, NULL,
211 &input, &output, cfg, true);
212 if (cfg)
213 config_ChainDestroy(cfg);
215 if (f == NULL)
217 msg_Err (obj, "cannot find %s for conversion pipeline",
218 "remixer");
219 goto error;
222 input = output;
223 filters[n++] = f;
226 /* Resample */
227 if (input.i_rate != outfmt->i_rate)
228 { /* Resampling works with any linear format, but may be ugly. */
229 if (n == max)
230 goto overflow;
232 audio_sample_format_t output = input;
233 output.i_rate = outfmt->i_rate;
235 filter_t *f = FindConverter (obj, &input, &output);
236 if (f == NULL)
238 msg_Err (obj, "cannot find %s for conversion pipeline",
239 "resampler");
240 goto error;
243 input = output;
244 filters[n++] = f;
247 /* Format */
248 if (input.i_format != outfmt->i_format)
250 if (max == 0)
251 goto overflow;
253 filter_t *f = TryFormat (obj, outfmt->i_format, &input);
254 if (f == NULL)
256 msg_Err (obj, "cannot find %s for conversion pipeline",
257 "post-mix converter");
258 goto error;
260 filters[n++] = f;
263 msg_Dbg (obj, "conversion pipeline complete");
264 *count += n;
265 return 0;
267 overflow:
268 msg_Err (obj, "maximum of %u conversion filters reached", max);
269 vlc_dialog_display_error (obj, _("Audio filtering failed"),
270 _("The maximum number of filters (%u) was reached."), max);
271 error:
272 aout_FiltersPipelineDestroy (filters, n);
273 return -1;
277 * Filters an audio buffer through a chain of filters.
279 static block_t *aout_FiltersPipelinePlay(filter_t *const *filters,
280 unsigned count, block_t *block)
282 /* TODO: use filter chain */
283 for (unsigned i = 0; (i < count) && (block != NULL); i++)
285 filter_t *filter = filters[i];
287 /* Please note that p_block->i_nb_samples & i_buffer
288 * shall be set by the filter plug-in. */
289 block = filter->pf_audio_filter (filter, block);
291 return block;
296 * Drain the chain of filters.
298 static block_t *aout_FiltersPipelineDrain(filter_t *const *filters,
299 unsigned count)
301 block_t *chain = NULL;
303 for (unsigned i = 0; i < count; i++)
305 filter_t *filter = filters[i];
307 block_t *block = filter_DrainAudio (filter);
308 if (block)
310 /* If there is a drained block, filter it through the following
311 * chain of filters */
312 if (i + 1 < count)
313 block = aout_FiltersPipelinePlay (&filters[i + 1],
314 count - i - 1, block);
315 if (block)
316 block_ChainAppend (&chain, block);
320 if (chain)
321 return block_ChainGather(chain);
322 else
323 return NULL;
327 * Flush the chain of filters.
329 static void aout_FiltersPipelineFlush(filter_t *const *filters,
330 unsigned count)
332 for (unsigned i = 0; i < count; i++)
333 filter_Flush (filters[i]);
336 static void aout_FiltersPipelineChangeViewpoint(filter_t *const *filters,
337 unsigned count,
338 const vlc_viewpoint_t *vp)
340 for (unsigned i = 0; i < count; i++)
341 filter_ChangeViewpoint (filters[i], vp);
344 #define AOUT_MAX_FILTERS 10
346 struct aout_filters
348 filter_t *rate_filter; /**< The filter adjusting samples count
349 (either the scaletempo filter or a resampler) */
350 filter_t *resampler; /**< The resampler */
351 int resampling; /**< Current resampling (Hz) */
352 vlc_clock_t *clock;
354 unsigned count; /**< Number of filters */
355 filter_t *tab[AOUT_MAX_FILTERS]; /**< Configured user filters
356 (e.g. equalization) and their conversions */
359 /** Callback for visualization selection */
360 static int VisualizationCallback (vlc_object_t *obj, const char *var,
361 vlc_value_t oldval, vlc_value_t newval,
362 void *data)
364 const char *mode = newval.psz_string;
366 if (!*mode)
367 mode = "none";
368 /* FIXME: This ugly hack enforced by visual effect-list, as is the need for
369 * separate "visual" (external) and "audio-visual" (internal) variables...
370 * The visual plugin should have one submodule per effect instead. */
371 if (strcasecmp (mode, "none") && strcasecmp (mode, "goom")
372 && strcasecmp (mode, "projectm") && strcasecmp (mode, "vsxu")
373 && strcasecmp (mode, "glspectrum"))
375 var_Create (obj, "effect-list", VLC_VAR_STRING);
376 var_SetString (obj, "effect-list", mode);
377 mode = "visual";
380 var_SetString (obj, "audio-visual", mode);
381 aout_InputRequestRestart ((audio_output_t *)obj);
382 (void) var; (void) oldval; (void) data;
383 return VLC_SUCCESS;
386 vout_thread_t *aout_filter_GetVout(filter_t *filter, const video_format_t *fmt)
388 vout_thread_t *vout = vout_Create(VLC_OBJECT(filter));
389 if (unlikely(vout == NULL))
390 return NULL;
392 video_format_t adj_fmt = *fmt;
393 vout_configuration_t cfg = {
394 .vout = vout, .clock = filter->owner.sys, .fmt = &adj_fmt,
397 video_format_AdjustColorSpace(&adj_fmt);
399 if (vout_Request(&cfg, NULL, NULL)) {
400 vout_Close(vout);
401 vout = NULL;
403 return vout;
406 static int AppendFilter(vlc_object_t *obj, const char *type, const char *name,
407 aout_filters_t *restrict filters,
408 audio_sample_format_t *restrict infmt,
409 const audio_sample_format_t *restrict outfmt,
410 config_chain_t *cfg)
412 const unsigned max = sizeof (filters->tab) / sizeof (filters->tab[0]);
413 if (filters->count >= max)
415 msg_Err (obj, "maximum of %u filters reached", max);
416 return -1;
419 const filter_owner_t owner = { .sys = filters->clock };
420 filter_t *filter = aout_filter_Create(obj, &owner, type, name,
421 infmt, outfmt, cfg, false);
422 if (filter == NULL)
424 msg_Err (obj, "cannot add user %s \"%s\" (skipped)", type, name);
425 return -1;
428 /* convert to the filter input format if necessary */
429 if (aout_FiltersPipelineCreate (obj, filters->tab, &filters->count,
430 max - 1, infmt, &filter->fmt_in.audio, false))
432 msg_Err (filter, "cannot add user %s \"%s\" (skipped)", type, name);
433 module_unneed (filter, filter->p_module);
434 vlc_object_delete(filter);
435 return -1;
438 assert (filters->count < max);
439 filters->tab[filters->count] = filter;
440 filters->count++;
441 *infmt = filter->fmt_out.audio;
442 return 0;
445 static int AppendRemapFilter(vlc_object_t *obj, aout_filters_t *restrict filters,
446 audio_sample_format_t *restrict infmt,
447 const audio_sample_format_t *restrict outfmt,
448 const int *wg4_remap)
450 char *name;
451 config_chain_t *cfg;
453 /* The remap audio filter use a different order than wg4 */
454 static const uint8_t wg4_to_remap[] = { 0, 2, 6, 7, 3, 5, 4, 1, 8 };
455 int remap[AOUT_CHAN_MAX];
456 bool needed = false;
457 for (int i = 0; i < AOUT_CHAN_MAX; ++i)
459 if (wg4_remap[i] != i)
460 needed = true;
461 remap[i] = wg4_remap[i] >= 0 ? wg4_to_remap[wg4_remap[i]] : -1;
463 if (!needed)
464 return 0;
466 char *str;
467 int ret = asprintf(&str, "remap{channel-left=%d,channel-right=%d,"
468 "channel-middleleft=%d,channel-middleright=%d,"
469 "channel-rearleft=%d,channel-rearright=%d,"
470 "channel-rearcenter=%d,channel-center=%d,"
471 "channel-lfe=%d,normalize=false}",
472 remap[0], remap[1], remap[2], remap[3], remap[4],
473 remap[5], remap[6], remap[7], remap[8]);
474 if (ret == -1)
475 return -1;
477 free(config_ChainCreate(&name, &cfg, str));
478 if (name != NULL && cfg != NULL)
479 ret = AppendFilter(obj, "audio filter", name, filters,
480 infmt, outfmt, cfg);
481 else
482 ret = -1;
484 free(str);
485 free(name);
486 if (cfg)
487 config_ChainDestroy(cfg);
488 return ret;
491 aout_filters_t *aout_FiltersNewWithClock(vlc_object_t *obj, const vlc_clock_t *clock,
492 const audio_sample_format_t *restrict infmt,
493 const audio_sample_format_t *restrict outfmt,
494 const aout_filters_cfg_t *cfg)
496 aout_filters_t *filters = malloc (sizeof (*filters));
497 if (unlikely(filters == NULL))
498 return NULL;
500 filters->rate_filter = NULL;
501 filters->resampler = NULL;
502 filters->resampling = 0;
503 filters->count = 0;
504 if (clock)
506 filters->clock = vlc_clock_CreateSlave(clock, AUDIO_ES);
507 if (!filters->clock)
508 goto error;
510 else
511 filters->clock = NULL;
513 /* Prepare format structure */
514 aout_FormatPrint (obj, "input", infmt);
515 audio_sample_format_t input_format = *infmt;
516 audio_sample_format_t output_format = *outfmt;
518 /* Callbacks (before reading values and also before return statement) */
519 var_AddCallback (obj, "visual", VisualizationCallback, NULL);
521 if (!AOUT_FMT_LINEAR(outfmt))
522 { /* Non-linear output: just convert formats, no filters/visu */
523 if (!AOUT_FMTS_IDENTICAL(infmt, outfmt))
525 aout_FormatsPrint (obj, "pass-through:", infmt, outfmt);
526 filters->tab[0] = FindConverter(obj, infmt, outfmt);
527 if (filters->tab[0] == NULL)
529 msg_Err (obj, "cannot setup pass-through");
530 goto error;
532 filters->count++;
534 return filters;
536 if (aout_FormatNbChannels(outfmt) == 0)
538 msg_Warn (obj, "No output channel mask, cannot setup filters");
539 goto error;
542 assert(output_format.channel_type == AUDIO_CHANNEL_TYPE_BITMAP);
543 if (input_format.channel_type != output_format.channel_type)
545 /* Do the channel type conversion before any filters since audio
546 * converters and filters handle only AUDIO_CHANNEL_TYPE_BITMAP */
548 /* convert to the output format (minus resampling) if necessary */
549 output_format.i_rate = input_format.i_rate;
550 if (aout_FiltersPipelineCreate (obj, filters->tab, &filters->count,
551 AOUT_MAX_FILTERS, &input_format, &output_format,
552 cfg->headphones))
554 msg_Warn (obj, "cannot setup audio renderer pipeline");
555 /* Fallback to bitmap without any conversions */
556 input_format.channel_type = AUDIO_CHANNEL_TYPE_BITMAP;
557 aout_FormatPrepare(&input_format);
559 else
560 input_format = output_format;
563 if (aout_FormatNbChannels(&input_format) == 0)
565 /* The input channel map is unknown, use the WAVE one and add a
566 * converter that will drop extra channels that are not handled by VLC
567 * */
568 msg_Info(obj, "unknown channel map, using the WAVE channel layout.");
570 assert(input_format.i_channels > 0);
571 audio_sample_format_t input_phys_format = input_format;
572 aout_SetWavePhysicalChannels(&input_phys_format);
574 filter_t *f = FindConverter (obj, &input_format, &input_phys_format);
575 if (f == NULL)
577 msg_Err (obj, "cannot find channel converter");
578 goto error;
581 input_format = input_phys_format;
582 filters->tab[filters->count++] = f;
585 assert(input_format.channel_type == AUDIO_CHANNEL_TYPE_BITMAP);
587 /* parse user filter lists */
588 if (var_InheritBool (obj, "audio-time-stretch"))
590 if (AppendFilter(obj, "audio filter", "scaletempo",
591 filters, &input_format, &output_format, NULL) == 0)
592 filters->rate_filter = filters->tab[filters->count - 1];
595 if (cfg != NULL)
597 AppendRemapFilter(obj, filters, &input_format, &output_format,
598 cfg->remap);
600 if (input_format.i_channels > 2 && cfg->headphones)
601 AppendFilter(obj, "audio filter", "binauralizer", filters,
602 &input_format, &output_format, NULL);
605 /* Now add user filters */
606 char *str = var_InheritString (obj, "audio-filter");
607 if (str != NULL)
609 char *p = str, *name;
610 while ((name = strsep (&p, " :")) != NULL)
612 AppendFilter(obj, "audio filter", name, filters,
613 &input_format, &output_format, NULL);
615 free (str);
618 char *visual = var_InheritString(obj, "audio-visual");
619 if (visual != NULL && strcasecmp(visual, "none"))
620 AppendFilter(obj, "visualization", visual, filters,
621 &input_format, &output_format, NULL);
622 free(visual);
624 /* convert to the output format (minus resampling) if necessary */
625 output_format.i_rate = input_format.i_rate;
626 if (aout_FiltersPipelineCreate (obj, filters->tab, &filters->count,
627 AOUT_MAX_FILTERS, &input_format, &output_format, false))
629 msg_Err (obj, "cannot setup filtering pipeline");
630 goto error;
632 input_format = output_format;
634 /* insert the resampler */
635 output_format.i_rate = outfmt->i_rate;
636 assert (AOUT_FMTS_IDENTICAL(&output_format, outfmt));
637 filters->resampler = FindResampler (obj, &input_format,
638 &output_format);
639 if (filters->resampler == NULL && input_format.i_rate != outfmt->i_rate)
641 msg_Err (obj, "cannot setup a resampler");
642 goto error;
644 if (filters->rate_filter == NULL)
645 filters->rate_filter = filters->resampler;
647 return filters;
649 error:
650 aout_FiltersPipelineDestroy (filters->tab, filters->count);
651 var_DelCallback(obj, "visual", VisualizationCallback, NULL);
652 if (filters->clock)
653 vlc_clock_Delete(filters->clock);
654 free (filters);
655 return NULL;
658 void aout_FiltersResetClock(aout_filters_t *filters)
660 assert(filters->clock);
661 vlc_clock_Reset(filters->clock);
664 void aout_FiltersSetClockDelay(aout_filters_t *filters, vlc_tick_t delay)
666 assert(filters->clock);
667 vlc_clock_SetDelay(filters->clock, delay);
670 #undef aout_FiltersNew
672 * Sets a chain of audio filters up.
673 * \param obj parent object for the filters
674 * \param infmt chain input format [IN]
675 * \param outfmt chain output format [IN]
676 * \param cfg a valid aout_filters_cfg_t struct or NULL.
677 * \return a filters chain or NULL on failure
679 aout_filters_t *aout_FiltersNew(vlc_object_t *obj,
680 const audio_sample_format_t *restrict infmt,
681 const audio_sample_format_t *restrict outfmt,
682 const aout_filters_cfg_t *cfg)
684 return aout_FiltersNewWithClock(obj, NULL, infmt, outfmt, cfg);
687 #undef aout_FiltersDelete
689 * Destroys a chain of audio filters.
690 * \param obj object used with aout_FiltersNew()
691 * \param filters chain to be destroyed
693 void aout_FiltersDelete (vlc_object_t *obj, aout_filters_t *filters)
695 if (filters->resampler != NULL)
696 aout_FiltersPipelineDestroy (&filters->resampler, 1);
697 aout_FiltersPipelineDestroy (filters->tab, filters->count);
698 var_DelCallback(obj, "visual", VisualizationCallback, NULL);
699 if (filters->clock)
700 vlc_clock_Delete(filters->clock);
701 free (filters);
704 bool aout_FiltersCanResample (aout_filters_t *filters)
706 return (filters->resampler != NULL);
709 bool aout_FiltersAdjustResampling (aout_filters_t *filters, int adjust)
711 if (filters->resampler == NULL)
712 return false;
714 if (adjust)
715 filters->resampling += adjust;
716 else
717 filters->resampling = 0;
718 return filters->resampling != 0;
721 block_t *aout_FiltersPlay(aout_filters_t *filters, block_t *block, float rate)
723 int nominal_rate = 0;
725 if (rate != 1.f)
727 filter_t *rate_filter = filters->rate_filter;
729 if (rate_filter == NULL)
730 goto drop; /* Without linear, non-nominal rate is impossible. */
732 /* Override input rate */
733 nominal_rate = rate_filter->fmt_in.audio.i_rate;
734 rate_filter->fmt_in.audio.i_rate = lroundf(nominal_rate * rate);
737 block = aout_FiltersPipelinePlay (filters->tab, filters->count, block);
738 if (filters->resampler != NULL)
739 { /* NOTE: the resampler needs to run even if resampling is 0.
740 * The decoder and output rates can still be different. */
741 filters->resampler->fmt_in.audio.i_rate += filters->resampling;
742 block = aout_FiltersPipelinePlay (&filters->resampler, 1, block);
743 filters->resampler->fmt_in.audio.i_rate -= filters->resampling;
746 if (nominal_rate != 0)
747 { /* Restore input rate */
748 assert (filters->rate_filter != NULL);
749 filters->rate_filter->fmt_in.audio.i_rate = nominal_rate;
751 return block;
753 drop:
754 block_Release (block);
755 return NULL;
758 block_t *aout_FiltersDrain (aout_filters_t *filters)
760 /* Drain the filters pipeline */
761 block_t *block = aout_FiltersPipelineDrain (filters->tab, filters->count);
763 if (filters->resampler != NULL)
765 block_t *chain = NULL;
767 filters->resampler->fmt_in.audio.i_rate += filters->resampling;
769 if (block)
771 /* Resample the drained block from the filters pipeline */
772 block = aout_FiltersPipelinePlay (&filters->resampler, 1, block);
773 if (block)
774 block_ChainAppend (&chain, block);
777 /* Drain the resampler filter */
778 block = aout_FiltersPipelineDrain (&filters->resampler, 1);
779 if (block)
780 block_ChainAppend (&chain, block);
782 filters->resampler->fmt_in.audio.i_rate -= filters->resampling;
784 return chain ? block_ChainGather (chain) : NULL;
786 else
787 return block;
790 void aout_FiltersFlush (aout_filters_t *filters)
792 aout_FiltersPipelineFlush (filters->tab, filters->count);
794 if (filters->resampler != NULL)
795 aout_FiltersPipelineFlush (&filters->resampler, 1);
798 void aout_FiltersChangeViewpoint (aout_filters_t *filters,
799 const vlc_viewpoint_t *vp)
801 aout_FiltersPipelineChangeViewpoint (filters->tab, filters->count, vp);