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 static filter_t *FindFilter (vlc_object_t *obj, const char *type,
47 const audio_sample_format_t *infmt,
48 const audio_sample_format_t *outfmt)
50 filter_t *filter = vlc_custom_create (obj, sizeof (*filter), type);
51 if (unlikely(filter == NULL))
54 filter->fmt_in.audio = *infmt;
55 filter->fmt_in.i_codec = infmt->i_format;
56 filter->fmt_out.audio = *outfmt;
57 filter->fmt_out.i_codec = outfmt->i_format;
58 filter->p_module = module_need (filter, type, name, false);
59 if (filter->p_module == NULL)
61 vlc_object_release (filter);
65 assert (filter->pf_audio_filter != NULL);
69 static filter_t *FindConverter (vlc_object_t *obj,
70 const audio_sample_format_t *infmt,
71 const audio_sample_format_t *outfmt)
73 return FindFilter (obj, "audio converter", NULL, infmt, outfmt);
76 static filter_t *FindResampler (vlc_object_t *obj,
77 const audio_sample_format_t *infmt,
78 const audio_sample_format_t *outfmt)
80 return FindFilter (obj, "audio resampler", "$audio-resampler",
85 * Destroys a chain of audio filters.
87 static void aout_FiltersPipelineDestroy(filter_t *const *filters, unsigned n)
89 for( unsigned i = 0; i < n; i++ )
91 filter_t *p_filter = filters[i];
93 module_unneed( p_filter, p_filter->p_module );
94 vlc_object_release( p_filter );
98 static filter_t *TryFormat (vlc_object_t *obj, vlc_fourcc_t codec,
99 audio_sample_format_t *restrict fmt)
101 audio_sample_format_t output = *fmt;
103 assert (codec != fmt->i_format);
104 output.i_format = codec;
105 aout_FormatPrepare (&output);
107 filter_t *filter = FindConverter (obj, fmt, &output);
114 * Allocates audio format conversion filters
115 * @param obj parent VLC object for new filters
116 * @param filters table of filters [IN/OUT]
117 * @param count pointer to the number of filters in the table [IN/OUT]
118 * @param max size of filters table [IN]
119 * @param infmt input audio format
120 * @param outfmt output audio format
121 * @return 0 on success, -1 on failure
123 static int aout_FiltersPipelineCreate(vlc_object_t *obj, filter_t **filters,
124 unsigned *count, unsigned max,
125 const audio_sample_format_t *restrict infmt,
126 const audio_sample_format_t *restrict outfmt)
128 aout_FormatsPrint (obj, "conversion:", infmt, outfmt);
132 /* There is a lot of second guessing on what the conversion plugins can
133 * and cannot do. This seems hardly avoidable, the conversion problem need
134 * to be reduced somehow. */
135 audio_sample_format_t input = *infmt;
138 /* Encapsulate or decode non-linear formats */
139 if (!AOUT_FMT_LINEAR(infmt) && infmt->i_format != outfmt->i_format)
144 filter_t *f = TryFormat (obj, VLC_CODEC_FI32, &input);
146 f = TryFormat (obj, VLC_CODEC_FL32, &input);
149 msg_Err (obj, "cannot find %s for conversion pipeline",
156 assert (AOUT_FMT_LINEAR(&input));
159 if (infmt->i_physical_channels != outfmt->i_physical_channels
160 || infmt->i_original_channels != outfmt->i_original_channels)
161 { /* Remixing currently requires FL32... TODO: S16N */
162 if (input.i_format != VLC_CODEC_FL32)
167 filter_t *f = TryFormat (obj, VLC_CODEC_FL32, &input);
170 msg_Err (obj, "cannot find %s for conversion pipeline",
171 "pre-mix converter");
181 audio_sample_format_t output;
182 output.i_format = input.i_format;
183 output.i_rate = input.i_rate;
184 output.i_physical_channels = outfmt->i_physical_channels;
185 output.i_original_channels = outfmt->i_original_channels;
186 aout_FormatPrepare (&output);
188 filter_t *f = FindConverter (obj, &input, &output);
191 msg_Err (obj, "cannot find %s for conversion pipeline",
201 if (input.i_rate != outfmt->i_rate)
202 { /* Resampling works with any linear format, but may be ugly. */
206 audio_sample_format_t output = input;
207 output.i_rate = outfmt->i_rate;
209 filter_t *f = FindConverter (obj, &input, &output);
212 msg_Err (obj, "cannot find %s for conversion pipeline",
222 if (input.i_format != outfmt->i_format)
227 filter_t *f = TryFormat (obj, outfmt->i_format, &input);
230 msg_Err (obj, "cannot find %s for conversion pipeline",
231 "post-mix converter");
237 msg_Dbg (obj, "conversion pipeline complete");
242 msg_Err (obj, "maximum of %u conversion filters reached", max);
243 dialog_Fatal (obj, _("Audio filtering failed"),
244 _("The maximum number of filters (%u) was reached."), max);
246 aout_FiltersPipelineDestroy (filters, n);
250 #define aout_FiltersPipelineCreate(obj,f,n,m,i,o) \
251 aout_FiltersPipelineCreate(VLC_OBJECT(obj),f,n,m,i,o)
253 static inline bool ChangeFiltersString (vlc_object_t *aout, const char *var,
254 const char *filter, bool add)
256 return aout_ChangeFilterString (aout, aout, var, filter, add);
260 * Filters an audio buffer through a chain of filters.
262 static block_t *aout_FiltersPipelinePlay(filter_t *const *filters,
263 unsigned count, block_t *block)
265 /* TODO: use filter chain */
266 for (unsigned i = 0; (i < count) && (block != NULL); i++)
268 filter_t *filter = filters[i];
270 /* Please note that p_block->i_nb_samples & i_buffer
271 * shall be set by the filter plug-in. */
272 block = filter->pf_audio_filter (filter, block);
277 /** Callback for visualization selection */
278 static int VisualizationCallback (vlc_object_t *obj, char const *var,
279 vlc_value_t oldval, vlc_value_t newval,
282 audio_output_t *aout = (audio_output_t *)obj;
283 const char *mode = newval.psz_string;
287 ChangeFiltersString (obj, "audio-visual", "goom", false);
288 ChangeFiltersString (obj, "audio-visual", "visual", false);
289 ChangeFiltersString (obj, "audio-visual", "projectm", false);
290 ChangeFiltersString (obj, "audio-visual", "vsxu", false);
292 else if (!strcmp ("goom", mode))
294 ChangeFiltersString (obj, "audio-visual", "visual", false );
295 ChangeFiltersString (obj, "audio-visual", "goom", true );
296 ChangeFiltersString (obj, "audio-visual", "projectm", false );
297 ChangeFiltersString (obj, "audio-visual", "vsxu", false);
299 else if (!strcmp ("projectm", mode))
301 ChangeFiltersString (obj, "audio-visual", "visual", false);
302 ChangeFiltersString (obj, "audio-visual", "goom", false);
303 ChangeFiltersString (obj, "audio-visual", "projectm", true);
304 ChangeFiltersString (obj, "audio-visual", "vsxu", false);
306 else if (!strcmp ("vsxu", mode))
308 ChangeFiltersString (obj, "audio-visual", "visual", false);
309 ChangeFiltersString (obj, "audio-visual", "goom", false);
310 ChangeFiltersString (obj, "audio-visual", "projectm", false);
311 ChangeFiltersString (obj, "audio-visual", "vsxu", true);
315 var_Create (obj, "effect-list", VLC_VAR_STRING);
316 var_SetString (obj, "effect-list", mode);
318 ChangeFiltersString (obj, "audio-visual", "goom", false);
319 ChangeFiltersString (obj, "audio-visual", "visual", true);
320 ChangeFiltersString (obj, "audio-visual", "projectm", false);
323 aout_InputRequestRestart (aout);
324 (void) var; (void) oldval; (void) data;
328 static int EqualizerCallback (vlc_object_t *obj, char const *var,
329 vlc_value_t oldval, vlc_value_t newval,
332 audio_output_t *aout = (audio_output_t *)obj;
333 char *mode = newval.psz_string;
337 ret = ChangeFiltersString (obj, "audio-filter", "equalizer", false);
340 var_Create (obj, "equalizer-preset", VLC_VAR_STRING);
341 var_SetString (obj, "equalizer-preset", mode);
342 ret = ChangeFiltersString (obj, "audio-filter", "equalizer", true);
347 aout_InputRequestRestart (aout);
348 (void) var; (void) oldval; (void) data;
352 static vout_thread_t *RequestVout (void *data, vout_thread_t *vout,
353 video_format_t *fmt, bool recycle)
355 audio_output_t *aout = data;
356 vout_configuration_t cfg = {
365 return vout_Request (aout, &cfg);
368 vout_thread_t *aout_filter_RequestVout (filter_t *filter, vout_thread_t *vout,
371 /* NOTE: This only works from audio output.
372 * If you want to use visualization filters from another place, you will
373 * need to add a new pf_aout_request_vout callback or store a pointer
374 * to aout_request_vout_t inside filter_t (i.e. a level of indirection). */
375 aout_owner_t *owner = aout_owner ((audio_output_t *)filter->p_parent);
376 aout_request_vout_t *req = &owner->request_vout;
378 return req->pf_request_vout (req->p_private, vout, fmt,
379 owner->recycle_vout);
382 static filter_t *CreateFilter (vlc_object_t *parent, const char *name,
383 const audio_sample_format_t *restrict infmt,
384 const audio_sample_format_t *restrict outfmt,
387 filter_t *filter = vlc_custom_create (parent, sizeof (*filter),
389 if (unlikely(filter == NULL))
392 /*filter->p_owner = NOT NEEDED;*/
393 filter->fmt_in.i_codec = infmt->i_format;
394 filter->fmt_in.audio = *infmt;
395 filter->fmt_out.i_codec = outfmt->i_format;
396 filter->fmt_out.audio = *outfmt;
400 filter->p_module = module_need (filter, "audio filter", name, true);
401 if (filter->p_module != NULL)
404 /* If probing failed, formats shall not have been modified. */
405 assert (AOUT_FMTS_IDENTICAL(&filter->fmt_in.audio, infmt));
406 assert (AOUT_FMTS_IDENTICAL(&filter->fmt_out.audio, outfmt));
409 filter->p_module = module_need (filter, "visualization2", name, true);
410 if (filter->p_module != NULL)
413 vlc_object_release (filter);
418 * Sets up the audio filters.
420 int aout_FiltersNew (audio_output_t *aout,
421 const audio_sample_format_t *restrict infmt,
422 const audio_sample_format_t *restrict outfmt,
423 const aout_request_vout_t *request_vout)
425 aout_owner_t *owner = aout_owner (aout);
427 /* Prepare format structure */
428 aout_FormatPrint (aout, "input", infmt);
429 audio_sample_format_t input_format = *infmt;
430 audio_sample_format_t output_format = *outfmt;
432 /* Now add user filters */
433 owner->nb_filters = 0;
434 owner->rate_filter = NULL;
435 owner->resampler = NULL;
437 var_AddCallback (aout, "visual", VisualizationCallback, NULL);
438 var_AddCallback (aout, "equalizer", EqualizerCallback, NULL);
440 if (!AOUT_FMT_LINEAR(outfmt))
441 { /* Non-linear output: just convert formats, no filters/visu */
442 if (!AOUT_FMTS_IDENTICAL(infmt, outfmt))
444 aout_FormatsPrint (aout, "pass-through:", infmt, outfmt);
445 owner->filters[0] = FindConverter(VLC_OBJECT(aout), infmt, outfmt);
446 if (owner->filters[0] == NULL)
448 msg_Err (aout, "cannot setup pass-through");
456 const char *scaletempo =
457 var_InheritBool (aout, "audio-time-stretch") ? "scaletempo" : NULL;
458 char *filters = var_InheritString (aout, "audio-filter");
459 char *visual = var_InheritString (aout, "audio-visual");
461 if (request_vout != NULL)
462 owner->request_vout = *request_vout;
465 owner->request_vout.pf_request_vout = RequestVout;
466 owner->request_vout.p_private = aout;
468 owner->recycle_vout = (visual != NULL) && *visual;
470 /* parse user filter lists */
471 const char *list[AOUT_MAX_FILTERS];
474 if (scaletempo != NULL)
475 list[n++] = scaletempo;
478 char *p = filters, *name;
479 while ((name = strsep (&p, " :")) != NULL && n < AOUT_MAX_FILTERS)
482 if (visual != NULL && n < AOUT_MAX_FILTERS)
485 for (unsigned i = 0; i < n; i++)
487 const char *name = list[i];
489 if (owner->nb_filters >= AOUT_MAX_FILTERS)
491 msg_Err (aout, "maximum of %u filters reached", AOUT_MAX_FILTERS);
492 msg_Err (aout, "cannot add user filter %s (skipped)", name);
496 filter_t *filter = CreateFilter (VLC_OBJECT(aout), name,
497 &input_format, &output_format,
501 msg_Err (aout, "cannot add user filter %s (skipped)", name);
505 /* convert to the filter input format if necessary */
506 if (aout_FiltersPipelineCreate (aout, owner->filters,
508 AOUT_MAX_FILTERS - 1,
509 &input_format, &filter->fmt_in.audio))
511 msg_Err (aout, "cannot add user filter %s (skipped)", name);
512 module_unneed (filter, filter->p_module);
513 vlc_object_release (filter);
517 assert (owner->nb_filters < AOUT_MAX_FILTERS);
518 owner->filters[owner->nb_filters++] = filter;
519 input_format = filter->fmt_out.audio;
521 if (name == scaletempo)
522 owner->rate_filter = filter;
527 /* convert to the output format (minus resampling) if necessary */
528 output_format.i_rate = input_format.i_rate;
529 if (aout_FiltersPipelineCreate (aout, owner->filters, &owner->nb_filters,
531 &input_format, &output_format))
533 msg_Err (aout, "cannot setup filtering pipeline");
536 input_format = output_format;
538 /* insert the resampler */
539 output_format.i_rate = outfmt->i_rate;
540 assert (AOUT_FMTS_IDENTICAL(&output_format, outfmt));
541 owner->resampler = FindResampler (VLC_OBJECT(aout), &input_format,
543 if (owner->resampler == NULL && input_format.i_rate != outfmt->i_rate)
545 msg_Err (aout, "cannot setup a resampler");
548 if (owner->rate_filter == NULL)
549 owner->rate_filter = owner->resampler;
550 owner->resampling = 0;
555 aout_FiltersPipelineDestroy (owner->filters, owner->nb_filters);
556 var_DelCallback (aout, "equalizer", EqualizerCallback, NULL);
557 var_DelCallback (aout, "visual", VisualizationCallback, NULL);
562 * Destroys the audio filters.
564 void aout_FiltersDelete (audio_output_t *aout)
566 aout_owner_t *owner = aout_owner (aout);
568 if (owner->resampler != NULL)
569 aout_FiltersPipelineDestroy (&owner->resampler, 1);
570 aout_FiltersPipelineDestroy (owner->filters, owner->nb_filters);
571 var_DelCallback (aout, "equalizer", EqualizerCallback, NULL);
572 var_DelCallback (aout, "visual", VisualizationCallback, NULL);
574 /* XXX We need to update recycle_vout before calling
575 * aout_FiltersPipelineDestroy().
576 * FIXME There may be a race condition if audio-visual is updated between
577 * aout_FiltersDestroy() and the next aout_FiltersNew().
579 char *visual = var_InheritString (aout, "audio-visual");
580 owner->recycle_vout = (visual != NULL) && *visual;
584 bool aout_FiltersAdjustResampling (audio_output_t *aout, int adjust)
586 aout_owner_t *owner = aout_owner (aout);
588 if (owner->resampler == NULL)
592 owner->resampling += adjust;
594 owner->resampling = 0;
595 return owner->resampling != 0;
598 block_t *aout_FiltersPlay (audio_output_t *aout, block_t *block, int rate)
600 aout_owner_t *owner = aout_owner (aout);
601 int nominal_rate = 0;
603 if (rate != INPUT_RATE_DEFAULT)
605 filter_t *rate_filter = owner->rate_filter;
607 if (rate_filter == NULL)
608 goto drop; /* Without linear, non-nominal rate is impossible. */
610 /* Override input rate */
611 nominal_rate = rate_filter->fmt_in.audio.i_rate;
612 rate_filter->fmt_in.audio.i_rate =
613 (nominal_rate * INPUT_RATE_DEFAULT) / rate;
616 block = aout_FiltersPipelinePlay (owner->filters, owner->nb_filters,
618 if (owner->resampler != NULL)
619 { /* NOTE: the resampler needs to run even if resampling is 0.
620 * The decoder and output rates can still be different. */
621 owner->resampler->fmt_in.audio.i_rate += owner->resampling;
622 block = aout_FiltersPipelinePlay (&owner->resampler, 1, block);
623 owner->resampler->fmt_in.audio.i_rate -= owner->resampling;
626 if (nominal_rate != 0)
627 { /* Restore input rate */
628 assert (owner->rate_filter != NULL);
629 owner->rate_filter->fmt_in.audio.i_rate = nominal_rate;
634 block_Release (block);