1 /*****************************************************************************
2 * filters.c : audio output filters management
3 *****************************************************************************
4 * Copyright (C) 2002-2007 VLC authors and VideoLAN
7 * Authors: Christophe Massiot <massiot@via.ecp.fr>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_dialog.h>
36 #include <vlc_modules.h>
38 #include <vlc_filter.h>
39 #include <vlc_vout.h> /* for vout_Request */
40 #include <vlc_input.h>
43 #include "aout_internal.h"
45 /*****************************************************************************
46 * FindFilter: find an audio filter for a specific transformation
47 *****************************************************************************/
48 static filter_t * FindFilter( vlc_object_t *obj,
49 const audio_sample_format_t *infmt,
50 const audio_sample_format_t *outfmt )
52 static const char typename[] = "audio converter";
53 const char *type = "audio converter", *name = NULL;
56 p_filter = vlc_custom_create( obj, sizeof(*p_filter), typename );
58 if ( p_filter == NULL ) return NULL;
60 p_filter->fmt_in.audio = *infmt;
61 p_filter->fmt_in.i_codec = infmt->i_format;
62 p_filter->fmt_out.audio = *outfmt;
63 p_filter->fmt_out.i_codec = outfmt->i_format;
65 if( infmt->i_format == outfmt->i_format
66 && infmt->i_physical_channels == outfmt->i_physical_channels
67 && infmt->i_original_channels == outfmt->i_original_channels )
69 assert( infmt->i_rate != outfmt->i_rate );
70 type = "audio resampler";
71 name = "$audio-resampler";
74 p_filter->p_module = module_need( p_filter, type, name, false );
75 if ( p_filter->p_module == NULL )
77 vlc_object_release( p_filter );
81 assert( p_filter->pf_audio_filter );
86 * Destroys a chain of audio filters.
88 static void aout_FiltersPipelineDestroy(filter_t *const *filters, unsigned n)
90 for( unsigned i = 0; i < n; i++ )
92 filter_t *p_filter = filters[i];
94 module_unneed( p_filter, p_filter->p_module );
95 vlc_object_release( p_filter );
99 static filter_t *TryFormat (vlc_object_t *obj, vlc_fourcc_t codec,
100 audio_sample_format_t *restrict fmt)
102 audio_sample_format_t output = *fmt;
104 assert (codec != fmt->i_format);
105 output.i_format = codec;
106 aout_FormatPrepare (&output);
108 filter_t *filter = FindFilter (obj, fmt, &output);
115 * Allocates audio format conversion filters
116 * @param obj parent VLC object for new filters
117 * @param filters table of filters [IN/OUT]
118 * @param count pointer to the number of filters in the table [IN/OUT]
119 * @param max size of filters table [IN]
120 * @param infmt input audio format
121 * @param outfmt output audio format
122 * @return 0 on success, -1 on failure
124 static int aout_FiltersPipelineCreate(vlc_object_t *obj, filter_t **filters,
125 unsigned *count, unsigned max,
126 const audio_sample_format_t *restrict infmt,
127 const audio_sample_format_t *restrict outfmt)
129 aout_FormatsPrint (obj, "conversion:", infmt, outfmt);
133 /* There is a lot of second guessing on what the conversion plugins can
134 * and cannot do. This seems hardly avoidable, the conversion problem need
135 * to be reduced somehow. */
136 audio_sample_format_t input = *infmt;
137 bool same_codec = infmt->i_format == outfmt->i_format;
138 bool same_rate = infmt->i_rate == outfmt->i_rate;
139 bool same_mix = infmt->i_physical_channels == outfmt->i_physical_channels
140 && infmt->i_original_channels == outfmt->i_original_channels;
143 /* Encapsulate or decode non-linear formats */
144 if (!AOUT_FMT_LINEAR(infmt) && !same_codec)
150 if (!AOUT_FMT_LINEAR(outfmt))
151 f = TryFormat (obj, outfmt->i_format, &input);
153 f = TryFormat (obj, VLC_CODEC_FI32, &input);
155 f = TryFormat (obj, VLC_CODEC_FL32, &input);
158 msg_Err (obj, "cannot find %s for conversion pipeline",
164 same_codec = input.i_format == outfmt->i_format;
167 assert (AOUT_FMT_LINEAR(&input));
169 /* Conversion cannot be done in foreign endianess. */
170 /* TODO: convert to native endian if needed */
174 { /* Remixing currently requires FL32... TODO: S16N */
175 if (input.i_format != VLC_CODEC_FL32)
180 filter_t *f = TryFormat (obj, VLC_CODEC_FL32, &input);
183 msg_Err (obj, "cannot find %s for conversion pipeline",
184 "pre-mix converter");
189 same_codec = input.i_format == outfmt->i_format;
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.i_original_channels = outfmt->i_original_channels;
200 aout_FormatPrepare (&output);
202 filter_t *f = FindFilter (obj, &input, &output);
205 msg_Err (obj, "cannot find %s for conversion pipeline",
217 { /* Resampling works with any linear format, but may be ugly. */
221 audio_sample_format_t output = input;
222 output.i_rate = outfmt->i_rate;
224 filter_t *f = FindFilter (obj, &input, &output);
227 msg_Err (obj, "cannot find %s for conversion pipeline",
242 filter_t *f = TryFormat (obj, outfmt->i_format, &input);
245 msg_Err (obj, "cannot find %s for conversion pipeline",
246 "post-mix converter");
253 /* TODO: convert to foreign endian if needed */
255 msg_Dbg (obj, "conversion pipeline complete");
260 msg_Err (obj, "maximum of %u conversion filters reached", max);
261 dialog_Fatal (obj, _("Audio filtering failed"),
262 _("The maximum number of filters (%u) was reached."), max);
264 aout_FiltersPipelineDestroy (filters, n);
268 #define aout_FiltersPipelineCreate(obj,f,n,m,i,o) \
269 aout_FiltersPipelineCreate(VLC_OBJECT(obj),f,n,m,i,o)
271 static inline bool ChangeFiltersString (vlc_object_t *aout, const char *var,
272 const char *filter, bool add)
274 return aout_ChangeFilterString (aout, aout, var, filter, add);
278 * Filters an audio buffer through a chain of filters.
280 static block_t *aout_FiltersPipelinePlay(filter_t *const *filters,
281 unsigned count, block_t *block)
283 /* TODO: use filter chain */
284 for (unsigned i = 0; (i < count) && (block != NULL); i++)
286 filter_t *filter = filters[i];
288 /* Please note that p_block->i_nb_samples & i_buffer
289 * shall be set by the filter plug-in. */
290 block = filter->pf_audio_filter (filter, block);
295 /** Callback for visualization selection */
296 static int VisualizationCallback (vlc_object_t *obj, char const *var,
297 vlc_value_t oldval, vlc_value_t newval,
300 audio_output_t *aout = (audio_output_t *)obj;
301 const char *mode = newval.psz_string;
305 ChangeFiltersString (obj, "audio-visual", "goom", false);
306 ChangeFiltersString (obj, "audio-visual", "visual", false);
307 ChangeFiltersString (obj, "audio-visual", "projectm", false);
308 ChangeFiltersString (obj, "audio-visual", "vsxu", false);
310 else if (!strcmp ("goom", mode))
312 ChangeFiltersString (obj, "audio-visual", "visual", false );
313 ChangeFiltersString (obj, "audio-visual", "goom", true );
314 ChangeFiltersString (obj, "audio-visual", "projectm", false );
315 ChangeFiltersString (obj, "audio-visual", "vsxu", false);
317 else if (!strcmp ("projectm", mode))
319 ChangeFiltersString (obj, "audio-visual", "visual", false);
320 ChangeFiltersString (obj, "audio-visual", "goom", false);
321 ChangeFiltersString (obj, "audio-visual", "projectm", true);
322 ChangeFiltersString (obj, "audio-visual", "vsxu", false);
324 else if (!strcmp ("vsxu", mode))
326 ChangeFiltersString (obj, "audio-visual", "visual", false);
327 ChangeFiltersString (obj, "audio-visual", "goom", false);
328 ChangeFiltersString (obj, "audio-visual", "projectm", false);
329 ChangeFiltersString (obj, "audio-visual", "vsxu", true);
333 var_Create (obj, "effect-list", VLC_VAR_STRING);
334 var_SetString (obj, "effect-list", mode);
336 ChangeFiltersString (obj, "audio-visual", "goom", false);
337 ChangeFiltersString (obj, "audio-visual", "visual", true);
338 ChangeFiltersString (obj, "audio-visual", "projectm", false);
341 aout_InputRequestRestart (aout);
342 (void) var; (void) oldval; (void) data;
346 static int EqualizerCallback (vlc_object_t *obj, char const *var,
347 vlc_value_t oldval, vlc_value_t newval,
350 audio_output_t *aout = (audio_output_t *)obj;
351 char *mode = newval.psz_string;
355 ret = ChangeFiltersString (obj, "audio-filter", "equalizer", false);
358 var_Create (obj, "equalizer-preset", VLC_VAR_STRING);
359 var_SetString (obj, "equalizer-preset", mode);
360 ret = ChangeFiltersString (obj, "audio-filter", "equalizer", true);
365 aout_InputRequestRestart (aout);
366 (void) var; (void) oldval; (void) data;
370 static vout_thread_t *RequestVout (void *data, vout_thread_t *vout,
371 video_format_t *fmt, bool recycle)
373 audio_output_t *aout = data;
374 vout_configuration_t cfg = {
383 return vout_Request (aout, &cfg);
386 vout_thread_t *aout_filter_RequestVout (filter_t *filter, vout_thread_t *vout,
389 /* NOTE: This only works from audio output.
390 * If you want to use visualization filters from another place, you will
391 * need to add a new pf_aout_request_vout callback or store a pointer
392 * to aout_request_vout_t inside filter_t (i.e. a level of indirection). */
393 aout_owner_t *owner = aout_owner ((audio_output_t *)filter->p_parent);
394 aout_request_vout_t *req = &owner->request_vout;
396 return req->pf_request_vout (req->p_private, vout, fmt,
397 owner->recycle_vout);
400 static filter_t *CreateFilter (vlc_object_t *parent, const char *name,
401 const audio_sample_format_t *restrict infmt,
402 const audio_sample_format_t *restrict outfmt,
405 filter_t *filter = vlc_custom_create (parent, sizeof (*filter),
407 if (unlikely(filter == NULL))
410 /*filter->p_owner = NOT NEEDED;*/
411 filter->fmt_in.i_codec = infmt->i_format;
412 filter->fmt_in.audio = *infmt;
413 filter->fmt_out.i_codec = outfmt->i_format;
414 filter->fmt_out.audio = *outfmt;
418 filter->p_module = module_need (filter, "audio filter", name, true);
419 if (filter->p_module != NULL)
422 /* If probing failed, formats shall not have been modified. */
423 assert (AOUT_FMTS_IDENTICAL(&filter->fmt_in.audio, infmt));
424 assert (AOUT_FMTS_IDENTICAL(&filter->fmt_out.audio, outfmt));
427 filter->p_module = module_need (filter, "visualization2", name, true);
428 if (filter->p_module != NULL)
431 vlc_object_release (filter);
436 * Sets up the audio filters.
438 int aout_FiltersNew (audio_output_t *aout,
439 const audio_sample_format_t *restrict infmt,
440 const audio_sample_format_t *restrict outfmt,
441 const aout_request_vout_t *request_vout)
443 aout_owner_t *owner = aout_owner (aout);
445 /* Prepare format structure */
446 aout_FormatPrint (aout, "input", infmt);
447 audio_sample_format_t input_format = *infmt;
448 audio_sample_format_t output_format = *outfmt;
450 /* Now add user filters */
451 owner->nb_filters = 0;
452 owner->rate_filter = NULL;
453 owner->resampler = NULL;
455 var_AddCallback (aout, "visual", VisualizationCallback, NULL);
456 var_AddCallback (aout, "equalizer", EqualizerCallback, NULL);
458 if (!AOUT_FMT_LINEAR(outfmt))
461 const char *scaletempo =
462 var_InheritBool (aout, "audio-time-stretch") ? "scaletempo" : NULL;
463 char *filters = var_InheritString (aout, "audio-filter");
464 char *visual = var_InheritString (aout, "audio-visual");
466 if (request_vout != NULL)
467 owner->request_vout = *request_vout;
470 owner->request_vout.pf_request_vout = RequestVout;
471 owner->request_vout.p_private = aout;
473 owner->recycle_vout = (visual != NULL) && *visual;
475 /* parse user filter lists */
476 const char *list[AOUT_MAX_FILTERS];
479 if (scaletempo != NULL)
480 list[n++] = scaletempo;
483 char *p = filters, *name;
484 while ((name = strsep (&p, " :")) != NULL && n < AOUT_MAX_FILTERS)
487 if (visual != NULL && n < AOUT_MAX_FILTERS)
490 for (unsigned i = 0; i < n; i++)
492 const char *name = list[i];
494 if (owner->nb_filters >= AOUT_MAX_FILTERS)
496 msg_Err (aout, "maximum of %u filters reached", AOUT_MAX_FILTERS);
497 msg_Err (aout, "cannot add user filter %s (skipped)", name);
501 filter_t *filter = CreateFilter (VLC_OBJECT(aout), name,
502 &input_format, &output_format,
506 msg_Err (aout, "cannot add user filter %s (skipped)", name);
510 /* convert to the filter input format if necessary */
511 if (aout_FiltersPipelineCreate (aout, owner->filters,
513 AOUT_MAX_FILTERS - 1,
514 &input_format, &filter->fmt_in.audio))
516 msg_Err (aout, "cannot add user filter %s (skipped)", name);
517 module_unneed (filter, filter->p_module);
518 vlc_object_release (filter);
522 assert (owner->nb_filters < AOUT_MAX_FILTERS);
523 owner->filters[owner->nb_filters++] = filter;
524 input_format = filter->fmt_out.audio;
526 if (name == scaletempo)
527 owner->rate_filter = filter;
532 /* convert to the output format (minus resampling) if necessary */
533 output_format.i_rate = input_format.i_rate;
534 if (aout_FiltersPipelineCreate (aout, owner->filters, &owner->nb_filters,
536 &input_format, &output_format))
538 msg_Err (aout, "cannot setup filtering pipeline");
541 input_format = output_format;
543 /* insert the resampler */
544 output_format.i_rate = outfmt->i_rate;
545 assert (AOUT_FMTS_IDENTICAL(&output_format, outfmt));
547 unsigned rate_bak = input_format.i_rate;
548 if (output_format.i_rate == input_format.i_rate)
549 /* For historical reasons, a different rate is required to probe
550 * resampling filters. */
551 input_format.i_rate++;
552 owner->resampler = FindFilter (VLC_OBJECT(aout), &input_format,
554 if (owner->resampler == NULL)
556 msg_Err (aout, "cannot setup a resampler");
559 owner->resampler->fmt_in.audio.i_rate = rate_bak;
560 if (owner->rate_filter == NULL)
561 owner->rate_filter = owner->resampler;
562 owner->resampling = 0;
567 aout_FiltersPipelineDestroy (owner->filters, owner->nb_filters);
568 var_DelCallback (aout, "equalizer", EqualizerCallback, NULL);
569 var_DelCallback (aout, "visual", VisualizationCallback, NULL);
574 * Destroys the audio filters.
576 void aout_FiltersDelete (audio_output_t *aout)
578 aout_owner_t *owner = aout_owner (aout);
580 if (owner->resampler != NULL)
581 aout_FiltersPipelineDestroy (&owner->resampler, 1);
582 aout_FiltersPipelineDestroy (owner->filters, owner->nb_filters);
583 var_DelCallback (aout, "equalizer", EqualizerCallback, NULL);
584 var_DelCallback (aout, "visual", VisualizationCallback, NULL);
586 /* XXX We need to update recycle_vout before calling
587 * aout_FiltersPipelineDestroy().
588 * FIXME There may be a race condition if audio-visual is updated between
589 * aout_FiltersDestroy() and the next aout_FiltersNew().
591 char *visual = var_InheritString (aout, "audio-visual");
592 owner->recycle_vout = (visual != NULL) && *visual;
596 bool aout_FiltersAdjustResampling (audio_output_t *aout, int adjust)
598 aout_owner_t *owner = aout_owner (aout);
600 if (owner->resampler == NULL)
604 owner->resampling += adjust;
606 owner->resampling = 0;
607 return owner->resampling != 0;
610 block_t *aout_FiltersPlay (audio_output_t *aout, block_t *block, int rate)
612 aout_owner_t *owner = aout_owner (aout);
613 int nominal_rate = 0;
615 if (rate != INPUT_RATE_DEFAULT)
617 filter_t *rate_filter = owner->rate_filter;
619 if (rate_filter == NULL)
620 goto drop; /* Without linear, non-nominal rate is impossible. */
622 /* Override input rate */
623 nominal_rate = rate_filter->fmt_in.audio.i_rate;
624 rate_filter->fmt_in.audio.i_rate =
625 (nominal_rate * INPUT_RATE_DEFAULT) / rate;
628 block = aout_FiltersPipelinePlay (owner->filters, owner->nb_filters,
630 if (owner->resampler != NULL)
631 { /* NOTE: the resampler needs to run even if resampling is 0.
632 * The decoder and output rates can still be different. */
633 owner->resampler->fmt_in.audio.i_rate += owner->resampling;
634 block = aout_FiltersPipelinePlay (&owner->resampler, 1, block);
635 owner->resampler->fmt_in.audio.i_rate -= owner->resampling;
638 if (nominal_rate != 0)
639 { /* Restore input rate */
640 assert (owner->rate_filter != NULL);
641 owner->rate_filter->fmt_in.audio.i_rate = nominal_rate;
646 block_Release (block);