]> git.sesse.net Git - vlc/blob - src/audio_output/filters.c
aout: fix and partly clean-up the "visual" callback
[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 static filter_t *FindFilter (vlc_object_t *obj, const char *type,
46                              const char *name,
47                              const audio_sample_format_t *infmt,
48                              const audio_sample_format_t *outfmt)
49 {
50     filter_t *filter = vlc_custom_create (obj, sizeof (*filter), type);
51     if (unlikely(filter == NULL))
52         return NULL;
53
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)
60     {
61         vlc_object_release (filter);
62         filter = NULL;
63     }
64     else
65         assert (filter->pf_audio_filter != NULL);
66     return filter;
67 }
68
69 static filter_t *FindConverter (vlc_object_t *obj,
70                                 const audio_sample_format_t *infmt,
71                                 const audio_sample_format_t *outfmt)
72 {
73     return FindFilter (obj, "audio converter", NULL, infmt, outfmt);
74 }
75
76 static filter_t *FindResampler (vlc_object_t *obj,
77                                 const audio_sample_format_t *infmt,
78                                 const audio_sample_format_t *outfmt)
79 {
80     return FindFilter (obj, "audio resampler", "$audio-resampler",
81                        infmt, outfmt);
82 }
83
84 /**
85  * Destroys a chain of audio filters.
86  */
87 static void aout_FiltersPipelineDestroy(filter_t *const *filters, unsigned n)
88 {
89     for( unsigned i = 0; i < n; i++ )
90     {
91         filter_t *p_filter = filters[i];
92
93         module_unneed( p_filter, p_filter->p_module );
94         vlc_object_release( p_filter );
95     }
96 }
97
98 static filter_t *TryFormat (vlc_object_t *obj, vlc_fourcc_t codec,
99                             audio_sample_format_t *restrict fmt)
100 {
101     audio_sample_format_t output = *fmt;
102
103     assert (codec != fmt->i_format);
104     output.i_format = codec;
105     aout_FormatPrepare (&output);
106
107     filter_t *filter = FindConverter (obj, fmt, &output);
108     if (filter != NULL)
109         *fmt = output;
110     return filter;
111 }
112
113 /**
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
122  */
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)
127 {
128     aout_FormatsPrint (obj, "conversion:", infmt, outfmt);
129     max -= *count;
130     filters += *count;
131
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;
136     unsigned n = 0;
137
138     /* Encapsulate or decode non-linear formats */
139     if (!AOUT_FMT_LINEAR(infmt) && infmt->i_format != outfmt->i_format)
140     {
141         if (n == max)
142             goto overflow;
143
144         filter_t *f = TryFormat (obj, VLC_CODEC_S32N, &input);
145         if (f == NULL)
146             f = TryFormat (obj, VLC_CODEC_FL32, &input);
147         if (f == NULL)
148         {
149             msg_Err (obj, "cannot find %s for conversion pipeline",
150                      "decoder");
151             goto error;
152         }
153
154         filters[n++] = f;
155     }
156     assert (AOUT_FMT_LINEAR(&input));
157
158     /* Remix channels */
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)
163         {
164             if (n == max)
165                 goto overflow;
166
167             filter_t *f = TryFormat (obj, VLC_CODEC_FL32, &input);
168             if (f == NULL)
169             {
170                 msg_Err (obj, "cannot find %s for conversion pipeline",
171                          "pre-mix converter");
172                 goto error;
173             }
174
175             filters[n++] = f;
176         }
177
178         if (n == max)
179             goto overflow;
180
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);
187
188         filter_t *f = FindConverter (obj, &input, &output);
189         if (f == NULL)
190         {
191             msg_Err (obj, "cannot find %s for conversion pipeline",
192                      "remixer");
193             goto error;
194         }
195
196         input = output;
197         filters[n++] = f;
198     }
199
200     /* Resample */
201     if (input.i_rate != outfmt->i_rate)
202     {   /* Resampling works with any linear format, but may be ugly. */
203         if (n == max)
204             goto overflow;
205
206         audio_sample_format_t output = input;
207         output.i_rate = outfmt->i_rate;
208
209         filter_t *f = FindConverter (obj, &input, &output);
210         if (f == NULL)
211         {
212             msg_Err (obj, "cannot find %s for conversion pipeline",
213                      "resampler");
214             goto error;
215         }
216
217         input = output;
218         filters[n++] = f;
219     }
220
221     /* Format */
222     if (input.i_format != outfmt->i_format)
223     {
224         if (max == 0)
225             goto overflow;
226
227         filter_t *f = TryFormat (obj, outfmt->i_format, &input);
228         if (f == NULL)
229         {
230             msg_Err (obj, "cannot find %s for conversion pipeline",
231                      "post-mix converter");
232             goto error;
233         }
234         filters[n++] = f;
235     }
236
237     msg_Dbg (obj, "conversion pipeline complete");
238     *count += n;
239     return 0;
240
241 overflow:
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);
245 error:
246     aout_FiltersPipelineDestroy (filters, n);
247     return -1;
248 }
249
250 #define aout_FiltersPipelineCreate(obj,f,n,m,i,o) \
251         aout_FiltersPipelineCreate(VLC_OBJECT(obj),f,n,m,i,o)
252
253 static inline bool ChangeFiltersString (vlc_object_t *aout, const char *var,
254                                         const char *filter, bool add)
255 {
256     return aout_ChangeFilterString (aout, aout, var, filter, add);
257 }
258
259 /**
260  * Filters an audio buffer through a chain of filters.
261  */
262 static block_t *aout_FiltersPipelinePlay(filter_t *const *filters,
263                                          unsigned count, block_t *block)
264 {
265     /* TODO: use filter chain */
266     for (unsigned i = 0; (i < count) && (block != NULL); i++)
267     {
268         filter_t *filter = filters[i];
269
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);
273     }
274     return block;
275 }
276
277 /** Callback for visualization selection */
278 static int VisualizationCallback (vlc_object_t *obj, const char *var,
279                                   vlc_value_t oldval, vlc_value_t newval,
280                                   void *data)
281 {
282     const char *mode = newval.psz_string;
283
284     if (!*mode)
285         mode = "none";
286     /* FIXME: This ugly hack enforced by visual effect-list, as is the need for
287      * separate "visual" (external) and "audio-visual" (internal) variables...
288      * The visual plugin should have one submodule per effect instead. */
289     if (strcasecmp (mode, "none") && strcasecmp (mode, "goom")
290      && strcasecmp (mode, "projectm") && strcasecmp (mode, "vsxu"))
291     {
292         var_Create (obj, "effect-list", VLC_VAR_STRING);
293         var_SetString (obj, "effect-list", mode);
294         mode = "visual";
295     }
296
297     var_SetString (obj, "audio-visual", mode);
298     aout_InputRequestRestart ((audio_output_t *)obj);
299     (void) var; (void) oldval; (void) data;
300     return VLC_SUCCESS;
301 }
302
303 static int EqualizerCallback (vlc_object_t *obj, char const *var,
304                               vlc_value_t oldval, vlc_value_t newval,
305                               void *data)
306 {
307     audio_output_t *aout = (audio_output_t *)obj;
308     char *mode = newval.psz_string;
309     bool ret;
310
311     if (!*mode)
312         ret = ChangeFiltersString (obj, "audio-filter", "equalizer", false);
313     else
314     {
315         var_Create (obj, "equalizer-preset", VLC_VAR_STRING);
316         var_SetString (obj, "equalizer-preset", mode);
317         ret = ChangeFiltersString (obj, "audio-filter", "equalizer", true);
318     }
319
320     /* That sucks */
321     if (ret)
322         aout_InputRequestRestart (aout);
323     (void) var; (void) oldval; (void) data;
324     return VLC_SUCCESS;
325 }
326
327 static vout_thread_t *RequestVout (void *data, vout_thread_t *vout,
328                                    video_format_t *fmt, bool recycle)
329 {
330     audio_output_t *aout = data;
331     vout_configuration_t cfg = {
332         .vout       = vout,
333         .input      = NULL,
334         .change_fmt = true,
335         .fmt        = fmt,
336         .dpb_size   = 1,
337     };
338
339     (void) recycle;
340     return vout_Request (aout, &cfg);
341 }
342
343 vout_thread_t *aout_filter_RequestVout (filter_t *filter, vout_thread_t *vout,
344                                         video_format_t *fmt)
345 {
346     /* NOTE: This only works from audio output.
347      * If you want to use visualization filters from another place, you will
348      * need to add a new pf_aout_request_vout callback or store a pointer
349      * to aout_request_vout_t inside filter_t (i.e. a level of indirection). */
350     aout_owner_t *owner = aout_owner ((audio_output_t *)filter->p_parent);
351     aout_request_vout_t *req = &owner->request_vout;
352
353     return req->pf_request_vout (req->p_private, vout, fmt,
354                                  owner->recycle_vout);
355 }
356
357 static filter_t *CreateFilter (vlc_object_t *parent, const char *name,
358                                const audio_sample_format_t *restrict infmt,
359                                const audio_sample_format_t *restrict outfmt,
360                                bool visu)
361 {
362     filter_t *filter = vlc_custom_create (parent, sizeof (*filter),
363                                           "audio filter");
364     if (unlikely(filter == NULL))
365         return NULL;
366
367     /*filter->p_owner = NOT NEEDED;*/
368     filter->fmt_in.i_codec = infmt->i_format;
369     filter->fmt_in.audio = *infmt;
370     filter->fmt_out.i_codec = outfmt->i_format;
371     filter->fmt_out.audio = *outfmt;
372
373     if (!visu)
374     {
375         filter->p_module = module_need (filter, "audio filter", name, true);
376         if (filter->p_module != NULL)
377             return filter;
378
379         /* If probing failed, formats shall not have been modified. */
380         assert (AOUT_FMTS_IDENTICAL(&filter->fmt_in.audio, infmt));
381         assert (AOUT_FMTS_IDENTICAL(&filter->fmt_out.audio, outfmt));
382     }
383
384     filter->p_module = module_need (filter, "visualization2", name, true);
385     if (filter->p_module != NULL)
386         return filter;
387
388     vlc_object_release (filter);
389     return NULL;
390 }
391
392 /**
393  * Sets up the audio filters.
394  */
395 int aout_FiltersNew (audio_output_t *aout,
396                      const audio_sample_format_t *restrict infmt,
397                      const audio_sample_format_t *restrict outfmt,
398                      const aout_request_vout_t *request_vout)
399 {
400     aout_owner_t *owner = aout_owner (aout);
401
402     /* Prepare format structure */
403     aout_FormatPrint (aout, "input", infmt);
404     audio_sample_format_t input_format = *infmt;
405     audio_sample_format_t output_format = *outfmt;
406
407     /* Now add user filters */
408     owner->nb_filters = 0;
409     owner->rate_filter = NULL;
410     owner->resampler = NULL;
411
412     var_AddCallback (aout, "visual", VisualizationCallback, NULL);
413     var_AddCallback (aout, "equalizer", EqualizerCallback, NULL);
414
415     if (!AOUT_FMT_LINEAR(outfmt))
416     {   /* Non-linear output: just convert formats, no filters/visu */
417         if (!AOUT_FMTS_IDENTICAL(infmt, outfmt))
418         {
419             aout_FormatsPrint (aout, "pass-through:", infmt, outfmt);
420             owner->filters[0] = FindConverter(VLC_OBJECT(aout), infmt, outfmt);
421             if (owner->filters[0] == NULL)
422             {
423                 msg_Err (aout, "cannot setup pass-through");
424                 goto error;
425             }
426             owner->nb_filters++;
427         }
428         return 0;
429     }
430
431     const char *scaletempo =
432         var_InheritBool (aout, "audio-time-stretch") ? "scaletempo" : NULL;
433     char *filters = var_InheritString (aout, "audio-filter");
434     char *visual = var_InheritString (aout, "audio-visual");
435     if (visual != NULL && !strcasecmp (visual, "none"))
436     {
437         free (visual);
438         visual = NULL;
439     }
440
441     if (request_vout != NULL)
442         owner->request_vout = *request_vout;
443     else
444     {
445         owner->request_vout.pf_request_vout = RequestVout;
446         owner->request_vout.p_private = aout;
447     }
448     owner->recycle_vout = visual != NULL;
449
450     /* parse user filter lists */
451     const char *list[AOUT_MAX_FILTERS];
452     unsigned n = 0;
453
454     if (scaletempo != NULL)
455         list[n++] = scaletempo;
456     if (filters != NULL)
457     {
458         char *p = filters, *name;
459         while ((name = strsep (&p, " :")) != NULL && n < AOUT_MAX_FILTERS)
460             list[n++] = name;
461     }
462     if (visual != NULL && n < AOUT_MAX_FILTERS)
463         list[n++] = visual;
464
465     for (unsigned i = 0; i < n; i++)
466     {
467         const char *name = list[i];
468
469         if (owner->nb_filters >= AOUT_MAX_FILTERS)
470         {
471             msg_Err (aout, "maximum of %u filters reached", AOUT_MAX_FILTERS);
472             msg_Err (aout, "cannot add user filter %s (skipped)", name);
473             break;
474         }
475
476         filter_t *filter = CreateFilter (VLC_OBJECT(aout), name,
477                                          &input_format, &output_format,
478                                          name == visual);
479         if (filter == NULL)
480         {
481             msg_Err (aout, "cannot add user filter %s (skipped)", name);
482             continue;
483         }
484
485         /* convert to the filter input format if necessary */
486         if (aout_FiltersPipelineCreate (aout, owner->filters,
487                                         &owner->nb_filters,
488                                         AOUT_MAX_FILTERS - 1,
489                                         &input_format, &filter->fmt_in.audio))
490         {
491             msg_Err (aout, "cannot add user filter %s (skipped)", name);
492             module_unneed (filter, filter->p_module);
493             vlc_object_release (filter);
494             continue;
495         }
496
497         assert (owner->nb_filters < AOUT_MAX_FILTERS);
498         owner->filters[owner->nb_filters++] = filter;
499         input_format = filter->fmt_out.audio;
500
501         if (name == scaletempo)
502             owner->rate_filter = filter;
503     }
504     free (visual);
505     free (filters);
506
507     /* convert to the output format (minus resampling) if necessary */
508     output_format.i_rate = input_format.i_rate;
509     if (aout_FiltersPipelineCreate (aout, owner->filters, &owner->nb_filters,
510                                     AOUT_MAX_FILTERS,
511                                     &input_format, &output_format))
512     {
513         msg_Err (aout, "cannot setup filtering pipeline");
514         goto error;
515     }
516     input_format = output_format;
517
518     /* insert the resampler */
519     output_format.i_rate = outfmt->i_rate;
520     assert (AOUT_FMTS_IDENTICAL(&output_format, outfmt));
521     owner->resampler = FindResampler (VLC_OBJECT(aout), &input_format,
522                                       &output_format);
523     if (owner->resampler == NULL && input_format.i_rate != outfmt->i_rate)
524     {
525         msg_Err (aout, "cannot setup a resampler");
526         goto error;
527     }
528     if (owner->rate_filter == NULL)
529         owner->rate_filter = owner->resampler;
530     owner->resampling = 0;
531
532     return 0;
533
534 error:
535     aout_FiltersPipelineDestroy (owner->filters, owner->nb_filters);
536     var_DelCallback (aout, "equalizer", EqualizerCallback, NULL);
537     var_DelCallback (aout, "visual", VisualizationCallback, NULL);
538     return -1;
539 }
540
541 /**
542  * Destroys the audio filters.
543  */
544 void aout_FiltersDelete (audio_output_t *aout)
545 {
546     aout_owner_t *owner = aout_owner (aout);
547
548     if (owner->resampler != NULL)
549         aout_FiltersPipelineDestroy (&owner->resampler, 1);
550     aout_FiltersPipelineDestroy (owner->filters, owner->nb_filters);
551     var_DelCallback (aout, "equalizer", EqualizerCallback, NULL);
552     var_DelCallback (aout, "visual", VisualizationCallback, NULL);
553
554     /* XXX We need to update recycle_vout before calling
555      * aout_FiltersPipelineDestroy().
556      * FIXME There may be a race condition if audio-visual is updated between
557      * aout_FiltersDestroy() and the next aout_FiltersNew().
558      */
559     char *visual = var_InheritString (aout, "audio-visual");
560     owner->recycle_vout = (visual != NULL) && *visual;
561     free (visual);
562 }
563
564 bool aout_FiltersAdjustResampling (audio_output_t *aout, int adjust)
565 {
566     aout_owner_t *owner = aout_owner (aout);
567
568     if (owner->resampler == NULL)
569         return false;
570
571     if (adjust)
572         owner->resampling += adjust;
573     else
574         owner->resampling = 0;
575     return owner->resampling != 0;
576 }
577
578 block_t *aout_FiltersPlay (audio_output_t *aout, block_t *block, int rate)
579 {
580     aout_owner_t *owner = aout_owner (aout);
581     int nominal_rate = 0;
582
583     if (rate != INPUT_RATE_DEFAULT)
584     {
585         filter_t *rate_filter = owner->rate_filter;
586
587         if (rate_filter == NULL)
588             goto drop; /* Without linear, non-nominal rate is impossible. */
589
590         /* Override input rate */
591         nominal_rate = rate_filter->fmt_in.audio.i_rate;
592         rate_filter->fmt_in.audio.i_rate =
593             (nominal_rate * INPUT_RATE_DEFAULT) / rate;
594     }
595
596     block = aout_FiltersPipelinePlay (owner->filters, owner->nb_filters,
597                                       block);
598     if (owner->resampler != NULL)
599     {   /* NOTE: the resampler needs to run even if resampling is 0.
600          * The decoder and output rates can still be different. */
601         owner->resampler->fmt_in.audio.i_rate += owner->resampling;
602         block = aout_FiltersPipelinePlay (&owner->resampler, 1, block);
603         owner->resampler->fmt_in.audio.i_rate -= owner->resampling;
604     }
605
606     if (nominal_rate != 0)
607     {   /* Restore input rate */
608         assert (owner->rate_filter != NULL);
609         owner->rate_filter->fmt_in.audio.i_rate = nominal_rate;
610     }
611     return block;
612
613 drop:
614     block_Release (block);
615     return NULL;
616 }