]> git.sesse.net Git - vlc/blob - src/audio_output/filters.c
aout: rewrite and robustify conversion pipelines
[vlc] / src / audio_output / filters.c
1 /*****************************************************************************
2  * filters.c : audio output filters management
3  *****************************************************************************
4  * Copyright (C) 2002-2007 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <string.h>
32 #include <assert.h>
33
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 <vlc_vout.h>                  /* for vout_Request */
40 #include <vlc_input.h>
41
42 #include <libvlc.h>
43 #include "aout_internal.h"
44
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 )
51 {
52     static const char typename[] = "audio converter";
53     const char *type = "audio converter", *name = NULL;
54     filter_t * p_filter;
55
56     p_filter = vlc_custom_create( obj, sizeof(*p_filter), typename );
57
58     if ( p_filter == NULL ) return NULL;
59
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;
64
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 )
68     {
69         assert( infmt->i_rate != outfmt->i_rate );
70         type = "audio resampler";
71         name = "$audio-resampler";
72     }
73
74     p_filter->p_module = module_need( p_filter, type, name, false );
75     if ( p_filter->p_module == NULL )
76     {
77         vlc_object_release( p_filter );
78         return NULL;
79     }
80
81     assert( p_filter->pf_audio_filter );
82     return p_filter;
83 }
84
85 /**
86  * Destroys a chain of audio filters.
87  */
88 static void aout_FiltersPipelineDestroy(filter_t *const *filters, unsigned n)
89 {
90     for( unsigned i = 0; i < n; i++ )
91     {
92         filter_t *p_filter = filters[i];
93
94         module_unneed( p_filter, p_filter->p_module );
95         vlc_object_release( p_filter );
96     }
97 }
98
99 static filter_t *TryFormat (vlc_object_t *obj, vlc_fourcc_t codec,
100                             audio_sample_format_t *restrict fmt)
101 {
102     audio_sample_format_t output = *fmt;
103
104     assert (codec != fmt->i_format);
105     output.i_format = codec;
106     aout_FormatPrepare (&output);
107
108     filter_t *filter = FindFilter (obj, fmt, &output);
109     if (filter != NULL)
110         *fmt = output;
111     return filter;
112 }
113
114 /**
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
123  */
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)
128 {
129     aout_FormatsPrint (obj, "conversion:", infmt, outfmt);
130     max -= *count;
131     filters += *count;
132
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;
141     unsigned n = 0;
142
143     /* Encapsulate or decode non-linear formats */
144     if (!AOUT_FMT_LINEAR(infmt) && !same_codec)
145     {
146         if (n == max)
147             goto overflow;
148
149         filter_t *f = NULL;
150         if (!AOUT_FMT_LINEAR(outfmt))
151            f = TryFormat (obj, outfmt->i_format, &input);
152         if (f == NULL)
153             f = TryFormat (obj, VLC_CODEC_FI32, &input);
154         if (f == NULL)
155             f = TryFormat (obj, VLC_CODEC_FL32, &input);
156         if (f == NULL)
157         {
158             msg_Err (obj, "cannot find %s for conversion pipeline",
159                      "decoder");
160             goto error;
161         }
162
163         filters[n++] = f;
164         same_codec = input.i_format == outfmt->i_format;
165     }
166
167     assert (AOUT_FMT_LINEAR(&input));
168
169     /* Conversion cannot be done in foreign endianess. */
170     /* TODO: convert to native endian if needed */
171
172     /* Remix channels */
173     if (!same_mix)
174     {   /* Remixing currently requires FL32... TODO: S16N */
175         if (input.i_format != VLC_CODEC_FL32)
176         {
177             if (n == max)
178                 goto overflow;
179
180             filter_t *f = TryFormat (obj, VLC_CODEC_FL32, &input);
181             if (f == NULL)
182             {
183                 msg_Err (obj, "cannot find %s for conversion pipeline",
184                          "pre-mix converter");
185                 goto error;
186             }
187
188             filters[n++] = f;
189             same_codec = input.i_format == outfmt->i_format;
190         }
191
192         if (n == max)
193             goto overflow;
194
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);
201
202         filter_t *f = FindFilter (obj, &input, &output);
203         if (f == NULL)
204         {
205             msg_Err (obj, "cannot find %s for conversion pipeline",
206                      "remixer");
207             goto error;
208         }
209
210         input = output;
211         filters[n++] = f;
212         //same_mix = true;
213     }
214
215     /* Resample */
216     if (!same_rate)
217     {   /* Resampling works with any linear format, but may be ugly. */
218         if (n == max)
219             goto overflow;
220
221         audio_sample_format_t output = input;
222         output.i_rate = outfmt->i_rate;
223
224         filter_t *f = FindFilter (obj, &input, &output);
225         if (f == NULL)
226         {
227             msg_Err (obj, "cannot find %s for conversion pipeline",
228                      "resampler");
229             goto error;
230         }
231
232         input = output;
233         filters[n++] = f;
234         //same_rate = true;
235     }
236
237     if (!same_codec)
238     {
239         if (max == 0)
240             goto overflow;
241
242         filter_t *f = TryFormat (obj, outfmt->i_format, &input);
243         if (f == NULL)
244         {
245             msg_Err (obj, "cannot find %s for conversion pipeline",
246                      "post-mix converter");
247             goto error;
248         }
249         filters[n++] = f;
250         //same_codec = true;
251     }
252
253     /* TODO: convert to foreign endian if needed */
254
255     msg_Dbg (obj, "conversion pipeline complete");
256     *count += n;
257     return 0;
258
259 overflow:
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);
263 error:
264     aout_FiltersPipelineDestroy (filters, n);
265     return -1;
266 }
267
268 #define aout_FiltersPipelineCreate(obj,f,n,m,i,o) \
269         aout_FiltersPipelineCreate(VLC_OBJECT(obj),f,n,m,i,o)
270
271 static inline bool ChangeFiltersString (vlc_object_t *aout, const char *var,
272                                         const char *filter, bool add)
273 {
274     return aout_ChangeFilterString (aout, aout, var, filter, add);
275 }
276
277 /**
278  * Filters an audio buffer through a chain of filters.
279  */
280 static block_t *aout_FiltersPipelinePlay(filter_t *const *filters,
281                                          unsigned count, block_t *block)
282 {
283     /* TODO: use filter chain */
284     for (unsigned i = 0; (i < count) && (block != NULL); i++)
285     {
286         filter_t *filter = filters[i];
287
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);
291     }
292     return block;
293 }
294
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,
298                                   void *data)
299 {
300     audio_output_t *aout = (audio_output_t *)obj;
301     const char *mode = newval.psz_string;
302
303     if (!*mode)
304     {
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);
309     }
310     else if (!strcmp ("goom", mode))
311     {
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);
316     }
317     else if (!strcmp ("projectm", mode))
318     {
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);
323     }
324     else if (!strcmp ("vsxu", mode))
325     {
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);
330     }
331     else
332     {
333         var_Create (obj, "effect-list", VLC_VAR_STRING);
334         var_SetString (obj, "effect-list", mode);
335
336         ChangeFiltersString (obj, "audio-visual", "goom", false);
337         ChangeFiltersString (obj, "audio-visual", "visual", true);
338         ChangeFiltersString (obj, "audio-visual", "projectm", false);
339     }
340
341     aout_InputRequestRestart (aout);
342     (void) var; (void) oldval; (void) data;
343     return VLC_SUCCESS;
344 }
345
346 static int EqualizerCallback (vlc_object_t *obj, char const *var,
347                               vlc_value_t oldval, vlc_value_t newval,
348                               void *data)
349 {
350     audio_output_t *aout = (audio_output_t *)obj;
351     char *mode = newval.psz_string;
352     bool ret;
353
354     if (!*mode)
355         ret = ChangeFiltersString (obj, "audio-filter", "equalizer", false);
356     else
357     {
358         var_Create (obj, "equalizer-preset", VLC_VAR_STRING);
359         var_SetString (obj, "equalizer-preset", mode);
360         ret = ChangeFiltersString (obj, "audio-filter", "equalizer", true);
361     }
362
363     /* That sucks */
364     if (ret)
365         aout_InputRequestRestart (aout);
366     (void) var; (void) oldval; (void) data;
367     return VLC_SUCCESS;
368 }
369
370 static vout_thread_t *RequestVout (void *data, vout_thread_t *vout,
371                                    video_format_t *fmt, bool recycle)
372 {
373     audio_output_t *aout = data;
374     vout_configuration_t cfg = {
375         .vout       = vout,
376         .input      = NULL,
377         .change_fmt = true,
378         .fmt        = fmt,
379         .dpb_size   = 1,
380     };
381
382     (void) recycle;
383     return vout_Request (aout, &cfg);
384 }
385
386 vout_thread_t *aout_filter_RequestVout (filter_t *filter, vout_thread_t *vout,
387                                         video_format_t *fmt)
388 {
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;
395
396     return req->pf_request_vout (req->p_private, vout, fmt,
397                                  owner->recycle_vout);
398 }
399
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,
403                                bool visu)
404 {
405     filter_t *filter = vlc_custom_create (parent, sizeof (*filter),
406                                           "audio filter");
407     if (unlikely(filter == NULL))
408         return NULL;
409
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;
415
416     if (!visu)
417     {
418         filter->p_module = module_need (filter, "audio filter", name, true);
419         if (filter->p_module != NULL)
420             return filter;
421
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));
425     }
426
427     filter->p_module = module_need (filter, "visualization2", name, true);
428     if (filter->p_module != NULL)
429         return filter;
430
431     vlc_object_release (filter);
432     return NULL;
433 }
434
435 /**
436  * Sets up the audio filters.
437  */
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)
442 {
443     aout_owner_t *owner = aout_owner (aout);
444
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;
449
450     /* Now add user filters */
451     owner->nb_filters = 0;
452     owner->rate_filter = NULL;
453     owner->resampler = NULL;
454
455     var_AddCallback (aout, "visual", VisualizationCallback, NULL);
456     var_AddCallback (aout, "equalizer", EqualizerCallback, NULL);
457
458     if (!AOUT_FMT_LINEAR(outfmt))
459         return 0;
460
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");
465
466     if (request_vout != NULL)
467         owner->request_vout = *request_vout;
468     else
469     {
470         owner->request_vout.pf_request_vout = RequestVout;
471         owner->request_vout.p_private = aout;
472     }
473     owner->recycle_vout = (visual != NULL) && *visual;
474
475     /* parse user filter lists */
476     const char *list[AOUT_MAX_FILTERS];
477     unsigned n = 0;
478
479     if (scaletempo != NULL)
480         list[n++] = scaletempo;
481     if (filters != NULL)
482     {
483         char *p = filters, *name;
484         while ((name = strsep (&p, " :")) != NULL && n < AOUT_MAX_FILTERS)
485             list[n++] = name;
486     }
487     if (visual != NULL && n < AOUT_MAX_FILTERS)
488         list[n++] = visual;
489
490     for (unsigned i = 0; i < n; i++)
491     {
492         const char *name = list[i];
493
494         if (owner->nb_filters >= AOUT_MAX_FILTERS)
495         {
496             msg_Err (aout, "maximum of %u filters reached", AOUT_MAX_FILTERS);
497             msg_Err (aout, "cannot add user filter %s (skipped)", name);
498             break;
499         }
500
501         filter_t *filter = CreateFilter (VLC_OBJECT(aout), name,
502                                          &input_format, &output_format,
503                                          name == visual);
504         if (filter == NULL)
505         {
506             msg_Err (aout, "cannot add user filter %s (skipped)", name);
507             continue;
508         }
509
510         /* convert to the filter input format if necessary */
511         if (aout_FiltersPipelineCreate (aout, owner->filters,
512                                         &owner->nb_filters,
513                                         AOUT_MAX_FILTERS - 1,
514                                         &input_format, &filter->fmt_in.audio))
515         {
516             msg_Err (aout, "cannot add user filter %s (skipped)", name);
517             module_unneed (filter, filter->p_module);
518             vlc_object_release (filter);
519             continue;
520         }
521
522         assert (owner->nb_filters < AOUT_MAX_FILTERS);
523         owner->filters[owner->nb_filters++] = filter;
524         input_format = filter->fmt_out.audio;
525
526         if (name == scaletempo)
527             owner->rate_filter = filter;
528     }
529     free (visual);
530     free (filters);
531
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,
535                                     AOUT_MAX_FILTERS,
536                                     &input_format, &output_format))
537     {
538         msg_Err (aout, "cannot setup filtering pipeline");
539         goto error;
540     }
541     input_format = output_format;
542
543     /* insert the resampler */
544     output_format.i_rate = outfmt->i_rate;
545     assert (AOUT_FMTS_IDENTICAL(&output_format, outfmt));
546
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,
553                                    &output_format);
554     if (owner->resampler == NULL)
555     {
556         msg_Err (aout, "cannot setup a resampler");
557         goto error;
558     }
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;
563
564     return 0;
565
566 error:
567     aout_FiltersPipelineDestroy (owner->filters, owner->nb_filters);
568     var_DelCallback (aout, "equalizer", EqualizerCallback, NULL);
569     var_DelCallback (aout, "visual", VisualizationCallback, NULL);
570     return -1;
571 }
572
573 /**
574  * Destroys the audio filters.
575  */
576 void aout_FiltersDelete (audio_output_t *aout)
577 {
578     aout_owner_t *owner = aout_owner (aout);
579
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);
585
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().
590      */
591     char *visual = var_InheritString (aout, "audio-visual");
592     owner->recycle_vout = (visual != NULL) && *visual;
593     free (visual);
594 }
595
596 bool aout_FiltersAdjustResampling (audio_output_t *aout, int adjust)
597 {
598     aout_owner_t *owner = aout_owner (aout);
599
600     if (owner->resampler == NULL)
601         return false;
602
603     if (adjust)
604         owner->resampling += adjust;
605     else
606         owner->resampling = 0;
607     return owner->resampling != 0;
608 }
609
610 block_t *aout_FiltersPlay (audio_output_t *aout, block_t *block, int rate)
611 {
612     aout_owner_t *owner = aout_owner (aout);
613     int nominal_rate = 0;
614
615     if (rate != INPUT_RATE_DEFAULT)
616     {
617         filter_t *rate_filter = owner->rate_filter;
618
619         if (rate_filter == NULL)
620             goto drop; /* Without linear, non-nominal rate is impossible. */
621
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;
626     }
627
628     block = aout_FiltersPipelinePlay (owner->filters, owner->nb_filters,
629                                       block);
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;
636     }
637
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;
642     }
643     return block;
644
645 drop:
646     block_Release (block);
647     return NULL;
648 }