]> git.sesse.net Git - vlc/blob - src/audio_output/filters.c
aout: drop remnants of foreign endian support (refs #7764)
[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_FI32, &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, char const *var,
279                                   vlc_value_t oldval, vlc_value_t newval,
280                                   void *data)
281 {
282     audio_output_t *aout = (audio_output_t *)obj;
283     const char *mode = newval.psz_string;
284
285     if (!*mode)
286     {
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);
291     }
292     else if (!strcmp ("goom", mode))
293     {
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);
298     }
299     else if (!strcmp ("projectm", mode))
300     {
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);
305     }
306     else if (!strcmp ("vsxu", mode))
307     {
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);
312     }
313     else
314     {
315         var_Create (obj, "effect-list", VLC_VAR_STRING);
316         var_SetString (obj, "effect-list", mode);
317
318         ChangeFiltersString (obj, "audio-visual", "goom", false);
319         ChangeFiltersString (obj, "audio-visual", "visual", true);
320         ChangeFiltersString (obj, "audio-visual", "projectm", false);
321     }
322
323     aout_InputRequestRestart (aout);
324     (void) var; (void) oldval; (void) data;
325     return VLC_SUCCESS;
326 }
327
328 static int EqualizerCallback (vlc_object_t *obj, char const *var,
329                               vlc_value_t oldval, vlc_value_t newval,
330                               void *data)
331 {
332     audio_output_t *aout = (audio_output_t *)obj;
333     char *mode = newval.psz_string;
334     bool ret;
335
336     if (!*mode)
337         ret = ChangeFiltersString (obj, "audio-filter", "equalizer", false);
338     else
339     {
340         var_Create (obj, "equalizer-preset", VLC_VAR_STRING);
341         var_SetString (obj, "equalizer-preset", mode);
342         ret = ChangeFiltersString (obj, "audio-filter", "equalizer", true);
343     }
344
345     /* That sucks */
346     if (ret)
347         aout_InputRequestRestart (aout);
348     (void) var; (void) oldval; (void) data;
349     return VLC_SUCCESS;
350 }
351
352 static vout_thread_t *RequestVout (void *data, vout_thread_t *vout,
353                                    video_format_t *fmt, bool recycle)
354 {
355     audio_output_t *aout = data;
356     vout_configuration_t cfg = {
357         .vout       = vout,
358         .input      = NULL,
359         .change_fmt = true,
360         .fmt        = fmt,
361         .dpb_size   = 1,
362     };
363
364     (void) recycle;
365     return vout_Request (aout, &cfg);
366 }
367
368 vout_thread_t *aout_filter_RequestVout (filter_t *filter, vout_thread_t *vout,
369                                         video_format_t *fmt)
370 {
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;
377
378     return req->pf_request_vout (req->p_private, vout, fmt,
379                                  owner->recycle_vout);
380 }
381
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,
385                                bool visu)
386 {
387     filter_t *filter = vlc_custom_create (parent, sizeof (*filter),
388                                           "audio filter");
389     if (unlikely(filter == NULL))
390         return NULL;
391
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;
397
398     if (!visu)
399     {
400         filter->p_module = module_need (filter, "audio filter", name, true);
401         if (filter->p_module != NULL)
402             return filter;
403
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));
407     }
408
409     filter->p_module = module_need (filter, "visualization2", name, true);
410     if (filter->p_module != NULL)
411         return filter;
412
413     vlc_object_release (filter);
414     return NULL;
415 }
416
417 /**
418  * Sets up the audio filters.
419  */
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)
424 {
425     aout_owner_t *owner = aout_owner (aout);
426
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;
431
432     /* Now add user filters */
433     owner->nb_filters = 0;
434     owner->rate_filter = NULL;
435     owner->resampler = NULL;
436
437     var_AddCallback (aout, "visual", VisualizationCallback, NULL);
438     var_AddCallback (aout, "equalizer", EqualizerCallback, NULL);
439
440     if (!AOUT_FMT_LINEAR(outfmt))
441     {   /* Non-linear output: just convert formats, no filters/visu */
442         if (!AOUT_FMTS_IDENTICAL(infmt, outfmt))
443         {
444             aout_FormatsPrint (aout, "pass-through:", infmt, outfmt);
445             owner->filters[0] = FindConverter(VLC_OBJECT(aout), infmt, outfmt);
446             if (owner->filters[0] == NULL)
447             {
448                 msg_Err (aout, "cannot setup pass-through");
449                 goto error;
450             }
451             owner->nb_filters++;
452         }
453         return 0;
454     }
455
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");
460
461     if (request_vout != NULL)
462         owner->request_vout = *request_vout;
463     else
464     {
465         owner->request_vout.pf_request_vout = RequestVout;
466         owner->request_vout.p_private = aout;
467     }
468     owner->recycle_vout = (visual != NULL) && *visual;
469
470     /* parse user filter lists */
471     const char *list[AOUT_MAX_FILTERS];
472     unsigned n = 0;
473
474     if (scaletempo != NULL)
475         list[n++] = scaletempo;
476     if (filters != NULL)
477     {
478         char *p = filters, *name;
479         while ((name = strsep (&p, " :")) != NULL && n < AOUT_MAX_FILTERS)
480             list[n++] = name;
481     }
482     if (visual != NULL && n < AOUT_MAX_FILTERS)
483         list[n++] = visual;
484
485     for (unsigned i = 0; i < n; i++)
486     {
487         const char *name = list[i];
488
489         if (owner->nb_filters >= AOUT_MAX_FILTERS)
490         {
491             msg_Err (aout, "maximum of %u filters reached", AOUT_MAX_FILTERS);
492             msg_Err (aout, "cannot add user filter %s (skipped)", name);
493             break;
494         }
495
496         filter_t *filter = CreateFilter (VLC_OBJECT(aout), name,
497                                          &input_format, &output_format,
498                                          name == visual);
499         if (filter == NULL)
500         {
501             msg_Err (aout, "cannot add user filter %s (skipped)", name);
502             continue;
503         }
504
505         /* convert to the filter input format if necessary */
506         if (aout_FiltersPipelineCreate (aout, owner->filters,
507                                         &owner->nb_filters,
508                                         AOUT_MAX_FILTERS - 1,
509                                         &input_format, &filter->fmt_in.audio))
510         {
511             msg_Err (aout, "cannot add user filter %s (skipped)", name);
512             module_unneed (filter, filter->p_module);
513             vlc_object_release (filter);
514             continue;
515         }
516
517         assert (owner->nb_filters < AOUT_MAX_FILTERS);
518         owner->filters[owner->nb_filters++] = filter;
519         input_format = filter->fmt_out.audio;
520
521         if (name == scaletempo)
522             owner->rate_filter = filter;
523     }
524     free (visual);
525     free (filters);
526
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,
530                                     AOUT_MAX_FILTERS,
531                                     &input_format, &output_format))
532     {
533         msg_Err (aout, "cannot setup filtering pipeline");
534         goto error;
535     }
536     input_format = output_format;
537
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,
542                                       &output_format);
543     if (owner->resampler == NULL && input_format.i_rate != outfmt->i_rate)
544     {
545         msg_Err (aout, "cannot setup a resampler");
546         goto error;
547     }
548     if (owner->rate_filter == NULL)
549         owner->rate_filter = owner->resampler;
550     owner->resampling = 0;
551
552     return 0;
553
554 error:
555     aout_FiltersPipelineDestroy (owner->filters, owner->nb_filters);
556     var_DelCallback (aout, "equalizer", EqualizerCallback, NULL);
557     var_DelCallback (aout, "visual", VisualizationCallback, NULL);
558     return -1;
559 }
560
561 /**
562  * Destroys the audio filters.
563  */
564 void aout_FiltersDelete (audio_output_t *aout)
565 {
566     aout_owner_t *owner = aout_owner (aout);
567
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);
573
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().
578      */
579     char *visual = var_InheritString (aout, "audio-visual");
580     owner->recycle_vout = (visual != NULL) && *visual;
581     free (visual);
582 }
583
584 bool aout_FiltersAdjustResampling (audio_output_t *aout, int adjust)
585 {
586     aout_owner_t *owner = aout_owner (aout);
587
588     if (owner->resampler == NULL)
589         return false;
590
591     if (adjust)
592         owner->resampling += adjust;
593     else
594         owner->resampling = 0;
595     return owner->resampling != 0;
596 }
597
598 block_t *aout_FiltersPlay (audio_output_t *aout, block_t *block, int rate)
599 {
600     aout_owner_t *owner = aout_owner (aout);
601     int nominal_rate = 0;
602
603     if (rate != INPUT_RATE_DEFAULT)
604     {
605         filter_t *rate_filter = owner->rate_filter;
606
607         if (rate_filter == NULL)
608             goto drop; /* Without linear, non-nominal rate is impossible. */
609
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;
614     }
615
616     block = aout_FiltersPipelinePlay (owner->filters, owner->nb_filters,
617                                       block);
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;
624     }
625
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;
630     }
631     return block;
632
633 drop:
634     block_Release (block);
635     return NULL;
636 }