]> git.sesse.net Git - vlc/blob - src/audio_output/input.c
aout: report volume from aout plugin to core
[vlc] / src / audio_output / input.c
1 /*****************************************************************************
2  * input.c : internal management of input streams for the audio output
3  *****************************************************************************
4  * Copyright (C) 2002-2007 the VideoLAN team
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
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 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 General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <assert.h>
33
34 #include <vlc_common.h>
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <math.h>
39 #include <assert.h>
40
41 #include <vlc_input.h>
42 #include <vlc_vout.h>                  /* for vout_Request */
43 #include <vlc_modules.h>
44
45 #include <vlc_aout.h>
46 #include <vlc_filter.h>
47 #include <libvlc.h>
48
49 #include "aout_internal.h"
50
51 static void inputFailure( audio_output_t *, aout_input_t *, const char * );
52 static void inputDrop( aout_input_t *, aout_buffer_t * );
53 static void inputResamplingStop( aout_input_t *p_input );
54
55 static int VisualizationCallback( vlc_object_t *, char const *,
56                                   vlc_value_t, vlc_value_t, void * );
57 static int EqualizerCallback( vlc_object_t *, char const *,
58                               vlc_value_t, vlc_value_t, void * );
59 static int ReplayGainCallback( vlc_object_t *, char const *,
60                                vlc_value_t, vlc_value_t, void * );
61 static void ReplayGainSelect( audio_output_t *, aout_input_t * );
62
63 static vout_thread_t *RequestVout( void *,
64                                    vout_thread_t *, video_format_t *, bool );
65
66 /*****************************************************************************
67  * aout_InputNew : allocate a new input and rework the filter pipeline
68  *****************************************************************************/
69 int aout_InputNew( audio_output_t * p_aout, aout_input_t * p_input, const aout_request_vout_t *p_request_vout )
70 {
71     aout_owner_t *owner = aout_owner (p_aout);
72     audio_sample_format_t chain_input_format;
73     audio_sample_format_t chain_output_format;
74     vlc_value_t val, text;
75     char *psz_filters, *psz_visual, *psz_scaletempo;
76     int i_visual;
77
78     aout_FormatPrint( p_aout, "input", &p_input->input );
79
80     p_input->i_nb_resamplers = p_input->i_nb_filters = 0;
81
82     date_Init (&p_input->date, owner->mixer_format.i_rate, 1);
83     date_Set (&p_input->date, VLC_TS_INVALID);
84
85     /* */
86     if( p_request_vout )
87     {
88         p_input->request_vout = *p_request_vout;
89     }
90     else
91     {
92         p_input->request_vout.pf_request_vout = RequestVout;
93         p_input->request_vout.p_private = p_aout;
94     }
95
96     /* Prepare format structure */
97     chain_input_format  = p_input->input;
98     chain_output_format = owner->mixer_format;
99     chain_output_format.i_rate = p_input->input.i_rate;
100     aout_FormatPrepare( &chain_output_format );
101
102     /* Now add user filters */
103     if( var_Type( p_aout, "visual" ) == 0 )
104     {
105         var_Create( p_aout, "visual", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
106         text.psz_string = _("Visualizations");
107         var_Change( p_aout, "visual", VLC_VAR_SETTEXT, &text, NULL );
108         val.psz_string = (char*)""; text.psz_string = _("Disable");
109         var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
110         val.psz_string = (char*)"spectrometer"; text.psz_string = _("Spectrometer");
111         var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
112         val.psz_string = (char*)"scope"; text.psz_string = _("Scope");
113         var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
114         val.psz_string = (char*)"spectrum"; text.psz_string = _("Spectrum");
115         var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
116         val.psz_string = (char*)"vuMeter"; text.psz_string = _("Vu meter");
117         var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
118
119         /* Look for goom plugin */
120         if( module_exists( "goom" ) )
121         {
122             val.psz_string = (char*)"goom"; text.psz_string = (char*)"Goom";
123             var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
124         }
125
126         /* Look for libprojectM plugin */
127         if( module_exists( "projectm" ) )
128         {
129             val.psz_string = (char*)"projectm"; text.psz_string = (char*)"projectM";
130             var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text );
131         }
132
133         if( var_Get( p_aout, "effect-list", &val ) == VLC_SUCCESS )
134         {
135             var_SetString( p_aout, "visual", val.psz_string );
136             free( val.psz_string );
137         }
138         var_AddCallback( p_aout, "visual", VisualizationCallback, NULL );
139     }
140
141     if( var_Type( p_aout, "equalizer" ) == 0 )
142     {
143         module_config_t *p_config;
144         int i;
145
146         p_config = config_FindConfig( VLC_OBJECT(p_aout), "equalizer-preset" );
147         if( p_config && p_config->i_list )
148         {
149                var_Create( p_aout, "equalizer",
150                            VLC_VAR_STRING | VLC_VAR_HASCHOICE );
151             text.psz_string = _("Equalizer");
152             var_Change( p_aout, "equalizer", VLC_VAR_SETTEXT, &text, NULL );
153
154             val.psz_string = (char*)""; text.psz_string = _("Disable");
155             var_Change( p_aout, "equalizer", VLC_VAR_ADDCHOICE, &val, &text );
156
157             for( i = 0; i < p_config->i_list; i++ )
158             {
159                 val.psz_string = (char *)p_config->ppsz_list[i];
160                 text.psz_string = (char *)p_config->ppsz_list_text[i];
161                 var_Change( p_aout, "equalizer", VLC_VAR_ADDCHOICE,
162                             &val, &text );
163             }
164
165             var_AddCallback( p_aout, "equalizer", EqualizerCallback, NULL );
166         }
167     }
168
169     if( var_Type( p_aout, "audio-filter" ) == 0 )
170     {
171         var_Create( p_aout, "audio-filter",
172                     VLC_VAR_STRING | VLC_VAR_DOINHERIT );
173         text.psz_string = _("Audio filters");
174         var_Change( p_aout, "audio-filter", VLC_VAR_SETTEXT, &text, NULL );
175     }
176     if( var_Type( p_aout, "audio-visual" ) == 0 )
177     {
178         var_Create( p_aout, "audio-visual",
179                     VLC_VAR_STRING | VLC_VAR_DOINHERIT );
180         text.psz_string = _("Audio visualizations");
181         var_Change( p_aout, "audio-visual", VLC_VAR_SETTEXT, &text, NULL );
182     }
183
184     if( var_Type( p_aout, "audio-replay-gain-mode" ) == 0 )
185     {
186         module_config_t *p_config;
187         int i;
188
189         p_config = config_FindConfig( VLC_OBJECT(p_aout), "audio-replay-gain-mode" );
190         if( p_config && p_config->i_list )
191         {
192             var_Create( p_aout, "audio-replay-gain-mode",
193                         VLC_VAR_STRING | VLC_VAR_DOINHERIT );
194
195             text.psz_string = _("Replay gain");
196             var_Change( p_aout, "audio-replay-gain-mode", VLC_VAR_SETTEXT, &text, NULL );
197
198             for( i = 0; i < p_config->i_list; i++ )
199             {
200                 val.psz_string = (char *)p_config->ppsz_list[i];
201                 text.psz_string = (char *)p_config->ppsz_list_text[i];
202                 var_Change( p_aout, "audio-replay-gain-mode", VLC_VAR_ADDCHOICE,
203                             &val, &text );
204             }
205
206             var_AddCallback( p_aout, "audio-replay-gain-mode", ReplayGainCallback, NULL );
207         }
208     }
209     if( var_Type( p_aout, "audio-replay-gain-preamp" ) == 0 )
210     {
211         var_Create( p_aout, "audio-replay-gain-preamp",
212                     VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
213     }
214     if( var_Type( p_aout, "audio-replay-gain-default" ) == 0 )
215     {
216         var_Create( p_aout, "audio-replay-gain-default",
217                     VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
218     }
219     if( var_Type( p_aout, "audio-replay-gain-peak-protection" ) == 0 )
220     {
221         var_Create( p_aout, "audio-replay-gain-peak-protection",
222                     VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
223     }
224
225     psz_filters = var_GetString( p_aout, "audio-filter" );
226     psz_visual = var_GetString( p_aout, "audio-visual");
227     psz_scaletempo = var_InheritBool( p_aout, "audio-time-stretch" ) ? strdup( "scaletempo" ) : NULL;
228
229     p_input->b_recycle_vout = psz_visual && *psz_visual;
230
231     /* parse user filter lists */
232     char *const ppsz_array[] = { psz_scaletempo, psz_filters, psz_visual };
233     p_input->p_playback_rate_filter = NULL;
234
235     for( i_visual = 0; i_visual < 3 && !AOUT_FMT_NON_LINEAR(&chain_output_format); i_visual++ )
236     {
237         char *psz_next = NULL;
238         char *psz_parser = ppsz_array[i_visual];
239
240         if( psz_parser == NULL || !*psz_parser )
241             continue;
242
243         while( psz_parser && *psz_parser )
244         {
245             filter_t * p_filter = NULL;
246
247             if( p_input->i_nb_filters >= AOUT_MAX_FILTERS )
248             {
249                 msg_Dbg( p_aout, "max filters reached (%d)", AOUT_MAX_FILTERS );
250                 break;
251             }
252
253             while( *psz_parser == ' ' && *psz_parser == ':' )
254             {
255                 psz_parser++;
256             }
257             if( ( psz_next = strchr( psz_parser , ':'  ) ) )
258             {
259                 *psz_next++ = '\0';
260             }
261             if( *psz_parser =='\0' )
262             {
263                 break;
264             }
265
266             /* Create a VLC object */
267             p_filter = vlc_custom_create( p_aout, sizeof(*p_filter),
268                                           "audio filter" );
269             if( p_filter == NULL )
270             {
271                 msg_Err( p_aout, "cannot add user filter %s (skipped)",
272                          psz_parser );
273                 psz_parser = psz_next;
274                 continue;
275             }
276
277             p_filter->p_owner = malloc( sizeof(*p_filter->p_owner) );
278             p_filter->p_owner->p_aout  = p_aout;
279             p_filter->p_owner->p_input = p_input;
280
281             /* request format */
282             memcpy( &p_filter->fmt_in.audio, &chain_output_format,
283                     sizeof(audio_sample_format_t) );
284             p_filter->fmt_in.i_codec = chain_output_format.i_format;
285             memcpy( &p_filter->fmt_out.audio, &chain_output_format,
286                     sizeof(audio_sample_format_t) );
287             p_filter->fmt_out.i_codec = chain_output_format.i_format;
288             p_filter->pf_audio_buffer_new = aout_FilterBufferNew;
289
290             /* try to find the requested filter */
291             if( i_visual == 2 ) /* this can only be a visualization module */
292             {
293                 p_filter->p_module = module_need( p_filter, "visualization2",
294                                                   psz_parser, true );
295             }
296             else /* this can be a audio filter module as well as a visualization module */
297             {
298                 p_filter->p_module = module_need( p_filter, "audio filter",
299                                               psz_parser, true );
300
301                 if ( p_filter->p_module == NULL )
302                 {
303                     /* if the filter requested a special format, retry */
304                     if ( !( AOUT_FMTS_IDENTICAL( &p_filter->fmt_in.audio,
305                                                  &chain_input_format )
306                             && AOUT_FMTS_IDENTICAL( &p_filter->fmt_out.audio,
307                                                     &chain_output_format ) ) )
308                     {
309                         aout_FormatPrepare( &p_filter->fmt_in.audio );
310                         aout_FormatPrepare( &p_filter->fmt_out.audio );
311                         p_filter->p_module = module_need( p_filter,
312                                                           "audio filter",
313                                                           psz_parser, true );
314                     }
315                     /* try visual filters */
316                     else
317                     {
318                         memcpy( &p_filter->fmt_in.audio, &chain_output_format,
319                                 sizeof(audio_sample_format_t) );
320                         memcpy( &p_filter->fmt_out.audio, &chain_output_format,
321                                 sizeof(audio_sample_format_t) );
322                         p_filter->p_module = module_need( p_filter,
323                                                           "visualization2",
324                                                           psz_parser, true );
325                     }
326                 }
327             }
328
329             /* failure */
330             if ( p_filter->p_module == NULL )
331             {
332                 msg_Err( p_aout, "cannot add user filter %s (skipped)",
333                          psz_parser );
334
335                 free( p_filter->p_owner );
336                 vlc_object_release( p_filter );
337
338                 psz_parser = psz_next;
339                 continue;
340             }
341
342             /* complete the filter chain if necessary */
343             if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters,
344                                              &p_input->i_nb_filters,
345                                              &chain_input_format,
346                                              &p_filter->fmt_in.audio ) < 0 )
347             {
348                 msg_Err( p_aout, "cannot add user filter %s (skipped)",
349                          psz_parser );
350
351                 module_unneed( p_filter, p_filter->p_module );
352                 free( p_filter->p_owner );
353                 vlc_object_release( p_filter );
354
355                 psz_parser = psz_next;
356                 continue;
357             }
358
359             /* success */
360             p_input->pp_filters[p_input->i_nb_filters++] = p_filter;
361             memcpy( &chain_input_format, &p_filter->fmt_out.audio,
362                     sizeof( audio_sample_format_t ) );
363
364             if( i_visual == 0 ) /* scaletempo */
365                 p_input->p_playback_rate_filter = p_filter;
366
367             /* next filter if any */
368             psz_parser = psz_next;
369         }
370     }
371     free( psz_visual );
372     free( psz_filters );
373     free( psz_scaletempo );
374
375     /* complete the filter chain if necessary */
376     if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters,
377                                      &p_input->i_nb_filters,
378                                      &chain_input_format,
379                                      &chain_output_format ) < 0 )
380     {
381         inputFailure( p_aout, p_input, "couldn't set an input pipeline" );
382         return -1;
383     }
384
385     /* Create resamplers. */
386     if (!AOUT_FMT_NON_LINEAR(&owner->mixer_format))
387     {
388         chain_output_format.i_rate = (__MAX(p_input->input.i_rate,
389                                             owner->mixer_format.i_rate)
390                                  * (100 + AOUT_MAX_RESAMPLING)) / 100;
391         if ( chain_output_format.i_rate == owner->mixer_format.i_rate )
392         {
393             /* Just in case... */
394             chain_output_format.i_rate++;
395         }
396         if (aout_FiltersCreatePipeline (p_aout, p_input->pp_resamplers,
397                                         &p_input->i_nb_resamplers,
398                                         &chain_output_format,
399                                         &owner->mixer_format) < 0)
400         {
401             inputFailure( p_aout, p_input, "couldn't set a resampler pipeline");
402             return -1;
403         }
404
405         /* Setup the initial rate of the resampler */
406         p_input->pp_resamplers[0]->fmt_in.audio.i_rate = p_input->input.i_rate;
407     }
408     p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
409
410     if( ! p_input->p_playback_rate_filter && p_input->i_nb_resamplers > 0 )
411     {
412         p_input->p_playback_rate_filter = p_input->pp_resamplers[0];
413     }
414
415     ReplayGainSelect( p_aout, p_input );
416
417     /* Success */
418     p_input->b_error = false;
419     p_input->i_last_input_rate = INPUT_RATE_DEFAULT;
420
421     return 0;
422 }
423
424 /*****************************************************************************
425  * aout_InputDelete : delete an input
426  *****************************************************************************
427  * This function must be entered with the mixer lock.
428  *****************************************************************************/
429 int aout_InputDelete( audio_output_t * p_aout, aout_input_t * p_input )
430 {
431     aout_assert_locked( p_aout );
432     if ( p_input->b_error )
433         return 0;
434
435     /* XXX We need to update b_recycle_vout before calling aout_FiltersDestroyPipeline.
436      * FIXME They can be a race condition if audio-visual is updated between
437      * aout_InputDelete and aout_InputNew.
438      */
439     char *psz_visual = var_GetString( p_aout, "audio-visual");
440     p_input->b_recycle_vout = psz_visual && *psz_visual;
441     free( psz_visual );
442
443     aout_FiltersDestroyPipeline( p_input->pp_filters, p_input->i_nb_filters );
444     p_input->i_nb_filters = 0;
445     aout_FiltersDestroyPipeline( p_input->pp_resamplers,
446                                  p_input->i_nb_resamplers );
447     p_input->i_nb_resamplers = 0;
448
449     return 0;
450 }
451
452 /*****************************************************************************
453  * aout_InputCheckAndRestart : restart an input
454  *****************************************************************************
455  * This function must be entered with the input and mixer lock.
456  *****************************************************************************/
457 void aout_InputCheckAndRestart( audio_output_t * p_aout, aout_input_t * p_input )
458 {
459     aout_assert_locked( p_aout );
460
461     if( !p_input->b_restart )
462         return;
463
464     /* A little trick to avoid loosing our input properties */
465     date_t date = p_input->date;
466
467     aout_InputDelete( p_aout, p_input );
468     aout_InputNew( p_aout, p_input, &p_input->request_vout );
469
470     p_input->date = date;
471     p_input->b_restart = false;
472 }
473 /*****************************************************************************
474  * aout_InputPlay : play a buffer
475  *****************************************************************************
476  * This function must be entered with the input lock.
477  *****************************************************************************/
478 /* XXX Do not activate it !! */
479 //#define AOUT_PROCESS_BEFORE_CHEKS
480 block_t *aout_InputPlay( audio_output_t *p_aout, aout_input_t *p_input,
481                          block_t *p_buffer, int i_input_rate )
482 {
483     mtime_t start_date;
484
485     aout_assert_locked( p_aout );
486
487     if( i_input_rate != INPUT_RATE_DEFAULT && p_input->p_playback_rate_filter == NULL )
488     {
489         inputDrop( p_input, p_buffer );
490         return NULL;
491     }
492
493 #ifdef AOUT_PROCESS_BEFORE_CHEKS
494     /* Run pre-filters. */
495     aout_FiltersPlay( p_aout, p_input->pp_filters, p_input->i_nb_filters,
496                       &p_buffer );
497     if( !p_buffer )
498         return NULL;
499
500     /* Actually run the resampler now. */
501     if ( p_input->i_nb_resamplers > 0 )
502     {
503         const mtime_t i_date = p_buffer->i_pts;
504         aout_FiltersPlay( p_aout, p_input->pp_resamplers,
505                           p_input->i_nb_resamplers,
506                           &p_buffer );
507     }
508
509     if( !p_buffer )
510         return NULL;
511     if( p_buffer->i_nb_samples <= 0 )
512     {
513         block_Release( p_buffer );
514         return NULL;
515     }
516 #endif
517
518     /* Handle input rate change, but keep drift correction */
519     if( i_input_rate != p_input->i_last_input_rate )
520     {
521         unsigned int * const pi_rate = &p_input->p_playback_rate_filter->fmt_in.audio.i_rate;
522 #define F(r,ir) ( INPUT_RATE_DEFAULT * (r) / (ir) )
523         const int i_delta = *pi_rate - F(p_input->input.i_rate,p_input->i_last_input_rate);
524         *pi_rate = F(p_input->input.i_rate + i_delta, i_input_rate);
525 #undef F
526         p_input->i_last_input_rate = i_input_rate;
527     }
528
529     mtime_t now = mdate();
530
531     /* We don't care if someone changes the start date behind our back after
532      * this. We'll deal with that when pushing the buffer, and compensate
533      * with the next incoming buffer. */
534     start_date = date_Get (&p_input->date);
535
536     if ( start_date != VLC_TS_INVALID && start_date < now )
537     {
538         /* The decoder is _very_ late. This can only happen if the user
539          * pauses the stream (or if the decoder is buggy, which cannot
540          * happen :). */
541         msg_Warn( p_aout, "computed PTS is out of range (%"PRId64"), "
542                   "clearing out", now - start_date );
543         aout_OutputFlush( p_aout, false );
544         if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE )
545             msg_Warn( p_aout, "timing screwed, stopping resampling" );
546         inputResamplingStop( p_input );
547         p_buffer->i_flags |= BLOCK_FLAG_DISCONTINUITY;
548         start_date = VLC_TS_INVALID;
549     }
550
551     if ( p_buffer->i_pts < now + AOUT_MIN_PREPARE_TIME )
552     {
553         /* The decoder gives us f*cked up PTS. It's its business, but we
554          * can't present it anyway, so drop the buffer. */
555         msg_Warn( p_aout, "PTS is out of range (%"PRId64"), dropping buffer",
556                   now - p_buffer->i_pts );
557         inputDrop( p_input, p_buffer );
558         inputResamplingStop( p_input );
559         return NULL;
560     }
561
562     /* If the audio drift is too big then it's not worth trying to resample
563      * the audio. */
564     if( start_date == VLC_TS_INVALID )
565     {
566         start_date = p_buffer->i_pts;
567         date_Set (&p_input->date, start_date);
568     }
569
570     mtime_t drift = start_date - p_buffer->i_pts;
571
572     if( drift < -i_input_rate * 3 * AOUT_MAX_PTS_ADVANCE / INPUT_RATE_DEFAULT )
573     {
574         msg_Warn( p_aout, "buffer way too early (%"PRId64"), clearing queue",
575                   drift );
576         aout_OutputFlush( p_aout, false );
577         if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE )
578             msg_Warn( p_aout, "timing screwed, stopping resampling" );
579         inputResamplingStop( p_input );
580         p_buffer->i_flags |= BLOCK_FLAG_DISCONTINUITY;
581         start_date = p_buffer->i_pts;
582         date_Set (&p_input->date, start_date);
583         drift = 0;
584     }
585     else
586     if( drift > +i_input_rate * 3 * AOUT_MAX_PTS_DELAY / INPUT_RATE_DEFAULT )
587     {
588         msg_Warn( p_aout, "buffer way too late (%"PRId64"), dropping buffer",
589                   drift );
590         inputDrop( p_input, p_buffer );
591         return NULL;
592     }
593
594 #ifndef AOUT_PROCESS_BEFORE_CHEKS
595     /* Run pre-filters. */
596     aout_FiltersPlay( p_input->pp_filters, p_input->i_nb_filters, &p_buffer );
597     if( !p_buffer )
598         return NULL;
599 #endif
600
601     /* Run the resampler if needed.
602      * We first need to calculate the output rate of this resampler. */
603     if ( ( p_input->i_resampling_type == AOUT_RESAMPLING_NONE ) &&
604          ( drift < -AOUT_MAX_PTS_ADVANCE || drift > +AOUT_MAX_PTS_DELAY ) &&
605          p_input->i_nb_resamplers > 0 )
606     {
607         /* Can happen in several circumstances :
608          * 1. A problem at the input (clock drift)
609          * 2. A small pause triggered by the user
610          * 3. Some delay in the output stage, causing a loss of lip
611          *    synchronization
612          * Solution : resample the buffer to avoid a scratch.
613          */
614         p_input->i_resamp_start_date = now;
615         p_input->i_resamp_start_drift = (int)-drift;
616         p_input->i_resampling_type = (drift < 0) ? AOUT_RESAMPLING_DOWN
617                                                  : AOUT_RESAMPLING_UP;
618         msg_Warn( p_aout, (drift < 0)
619                   ? "buffer too early (%"PRId64"), down-sampling"
620                   : "buffer too late  (%"PRId64"), up-sampling", drift );
621     }
622
623     if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE )
624     {
625         /* Resampling has been triggered previously (because of dates
626          * mismatch). We want the resampling to happen progressively so
627          * it isn't too audible to the listener. */
628
629         if( p_input->i_resampling_type == AOUT_RESAMPLING_UP )
630             p_input->pp_resamplers[0]->fmt_in.audio.i_rate += 2; /* Hz */
631         else
632             p_input->pp_resamplers[0]->fmt_in.audio.i_rate -= 2; /* Hz */
633
634         /* Check if everything is back to normal, in which case we can stop the
635          * resampling */
636         unsigned int i_nominal_rate =
637           (p_input->pp_resamplers[0] == p_input->p_playback_rate_filter)
638           ? INPUT_RATE_DEFAULT * p_input->input.i_rate / i_input_rate
639           : p_input->input.i_rate;
640         if( p_input->pp_resamplers[0]->fmt_in.audio.i_rate == i_nominal_rate )
641         {
642             p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
643             msg_Warn( p_aout, "resampling stopped after %"PRIi64" usec "
644                       "(drift: %"PRIi64")",
645                       now - p_input->i_resamp_start_date,
646                       p_buffer->i_pts - start_date);
647         }
648         else if( abs( (int)(p_buffer->i_pts - start_date) ) <
649                  abs( p_input->i_resamp_start_drift ) / 2 )
650         {
651             /* if we reduced the drift from half, then it is time to switch
652              * back the resampling direction. */
653             if( p_input->i_resampling_type == AOUT_RESAMPLING_UP )
654                 p_input->i_resampling_type = AOUT_RESAMPLING_DOWN;
655             else
656                 p_input->i_resampling_type = AOUT_RESAMPLING_UP;
657             p_input->i_resamp_start_drift = 0;
658         }
659         else if( p_input->i_resamp_start_drift &&
660                  ( abs( (int)(p_buffer->i_pts - start_date) ) >
661                    abs( p_input->i_resamp_start_drift ) * 3 / 2 ) )
662         {
663             /* If the drift is increasing and not decreasing, than something
664              * is bad. We'd better stop the resampling right now. */
665             msg_Warn( p_aout, "timing screwed, stopping resampling" );
666             inputResamplingStop( p_input );
667             p_buffer->i_flags |= BLOCK_FLAG_DISCONTINUITY;
668         }
669     }
670
671 #ifndef AOUT_PROCESS_BEFORE_CHEKS
672     /* Actually run the resampler now. */
673     if ( p_input->i_nb_resamplers > 0 )
674     {
675         aout_FiltersPlay( p_input->pp_resamplers, p_input->i_nb_resamplers,
676                           &p_buffer );
677     }
678
679     if( !p_buffer )
680         return NULL;
681     if( p_buffer->i_nb_samples <= 0 )
682     {
683         block_Release( p_buffer );
684         return NULL;
685     }
686 #endif
687
688     p_buffer->i_pts = start_date;
689     date_Increment (&p_input->date, p_buffer->i_nb_samples);
690     return p_buffer;
691 }
692
693 /*****************************************************************************
694  * static functions
695  *****************************************************************************/
696
697 static void inputFailure( audio_output_t * p_aout, aout_input_t * p_input,
698                           const char * psz_error_message )
699 {
700     /* error message */
701     msg_Err( p_aout, "%s", psz_error_message );
702
703     /* clean up */
704     aout_FiltersDestroyPipeline( p_input->pp_filters, p_input->i_nb_filters );
705     aout_FiltersDestroyPipeline( p_input->pp_resamplers,
706                                  p_input->i_nb_resamplers );
707     var_Destroy( p_aout, "visual" );
708     var_Destroy( p_aout, "equalizer" );
709     var_Destroy( p_aout, "audio-filter" );
710     var_Destroy( p_aout, "audio-visual" );
711
712     var_Destroy( p_aout, "audio-replay-gain-mode" );
713     var_Destroy( p_aout, "audio-replay-gain-default" );
714     var_Destroy( p_aout, "audio-replay-gain-preamp" );
715     var_Destroy( p_aout, "audio-replay-gain-peak-protection" );
716
717     /* error flag */
718     p_input->b_error = 1;
719 }
720
721 static void inputDrop( aout_input_t *p_input, aout_buffer_t *p_buffer )
722 {
723     aout_BufferFree( p_buffer );
724
725     p_input->i_buffer_lost++;
726 }
727
728 static void inputResamplingStop( aout_input_t *p_input )
729 {
730     p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
731     if( p_input->i_nb_resamplers != 0 )
732     {
733         p_input->pp_resamplers[0]->fmt_in.audio.i_rate =
734             ( p_input->pp_resamplers[0] == p_input->p_playback_rate_filter )
735             ? INPUT_RATE_DEFAULT * p_input->input.i_rate / p_input->i_last_input_rate
736             : p_input->input.i_rate;
737     }
738 }
739
740 static vout_thread_t *RequestVout( void *p_private,
741                                    vout_thread_t *p_vout, video_format_t *p_fmt, bool b_recycle )
742 {
743     audio_output_t *p_aout = p_private;
744     VLC_UNUSED(b_recycle);
745     vout_configuration_t cfg = {
746         .vout       = p_vout,
747         .input      = NULL,
748         .change_fmt = true,
749         .fmt        = p_fmt,
750         .dpb_size   = 1,
751     };
752     return vout_Request( p_aout, &cfg );
753 }
754
755 vout_thread_t *aout_filter_RequestVout( filter_t *p_filter,
756                                         vout_thread_t *p_vout, video_format_t *p_fmt )
757 {
758     aout_input_t *p_input = p_filter->p_owner->p_input;
759     aout_request_vout_t *p_request = &p_input->request_vout;
760
761     /* XXX: this only works from audio input */
762     /* If you want to use visualization filters from another place, you will
763      * need to add a new pf_aout_request_vout callback or store a pointer
764      * to aout_request_vout_t inside filter_t (i.e. a level of indirection). */
765
766     return p_request->pf_request_vout( p_request->p_private,
767                                        p_vout, p_fmt, p_input->b_recycle_vout );
768 }
769
770 static int ChangeFiltersString( audio_output_t * p_aout, const char* psz_variable,
771                                  const char *psz_name, bool b_add )
772 {
773     return aout_ChangeFilterString( VLC_OBJECT(p_aout), p_aout,
774                                     psz_variable, psz_name, b_add ) ? 1 : 0;
775 }
776
777 static int VisualizationCallback( vlc_object_t *p_this, char const *psz_cmd,
778                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
779 {
780     audio_output_t *p_aout = (audio_output_t *)p_this;
781     char *psz_mode = newval.psz_string;
782     (void)psz_cmd; (void)oldval; (void)p_data;
783
784     if( !psz_mode || !*psz_mode )
785     {
786         ChangeFiltersString( p_aout, "audio-visual", "goom", false );
787         ChangeFiltersString( p_aout, "audio-visual", "visual", false );
788         ChangeFiltersString( p_aout, "audio-visual", "projectm", false );
789     }
790     else
791     {
792         if( !strcmp( "goom", psz_mode ) )
793         {
794             ChangeFiltersString( p_aout, "audio-visual", "visual", false );
795             ChangeFiltersString( p_aout, "audio-visual", "goom", true );
796             ChangeFiltersString( p_aout, "audio-visual", "projectm", false );
797         }
798         else if( !strcmp( "projectm", psz_mode ) )
799         {
800             ChangeFiltersString( p_aout, "audio-visual", "visual", false );
801             ChangeFiltersString( p_aout, "audio-visual", "goom", false );
802             ChangeFiltersString( p_aout, "audio-visual", "projectm", true );
803         }
804         else
805         {
806             var_Create( p_aout, "effect-list", VLC_VAR_STRING );
807             var_SetString( p_aout, "effect-list", psz_mode );
808
809             ChangeFiltersString( p_aout, "audio-visual", "goom", false );
810             ChangeFiltersString( p_aout, "audio-visual", "visual", true );
811             ChangeFiltersString( p_aout, "audio-visual", "projectm", false );
812         }
813     }
814
815     /* That sucks */
816     AoutInputsMarkToRestart( p_aout );
817
818     return VLC_SUCCESS;
819 }
820
821 static int EqualizerCallback( vlc_object_t *p_this, char const *psz_cmd,
822                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
823 {
824     audio_output_t *p_aout = (audio_output_t *)p_this;
825     char *psz_mode = newval.psz_string;
826     int i_ret;
827     (void)psz_cmd; (void)oldval; (void)p_data;
828
829     if( !psz_mode || !*psz_mode )
830     {
831         i_ret = ChangeFiltersString( p_aout, "audio-filter", "equalizer",
832                                      false );
833     }
834     else
835     {
836         var_Create( p_aout, "equalizer-preset", VLC_VAR_STRING );
837         var_SetString( p_aout, "equalizer-preset", psz_mode );
838         i_ret = ChangeFiltersString( p_aout, "audio-filter", "equalizer",
839                                      true );
840     }
841
842     /* That sucks */
843     if( i_ret == 1 )
844         AoutInputsMarkToRestart( p_aout );
845     return VLC_SUCCESS;
846 }
847
848 static int ReplayGainCallback( vlc_object_t *p_this, char const *psz_cmd,
849                                vlc_value_t oldval, vlc_value_t newval, void *p_data )
850 {
851     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
852     VLC_UNUSED(newval); VLC_UNUSED(p_data);
853     audio_output_t *aout = (audio_output_t *)p_this;
854     aout_owner_t *owner = aout_owner (aout);
855
856     aout_lock (aout);
857     if (owner->input != NULL)
858         ReplayGainSelect (aout, owner->input);
859     aout_unlock (aout);
860
861     return VLC_SUCCESS;
862 }
863
864 static void ReplayGainSelect( audio_output_t *p_aout, aout_input_t *p_input )
865 {
866     char *psz_replay_gain = var_GetNonEmptyString( p_aout,
867                                                    "audio-replay-gain-mode" );
868     int i_mode;
869     int i_use;
870     float f_gain;
871
872     p_input->multiplier = 1.0;
873
874     if( !psz_replay_gain )
875         return;
876
877     /* Find select mode */
878     if( !strcmp( psz_replay_gain, "track" ) )
879         i_mode = AUDIO_REPLAY_GAIN_TRACK;
880     else if( !strcmp( psz_replay_gain, "album" ) )
881         i_mode = AUDIO_REPLAY_GAIN_ALBUM;
882     else
883         i_mode = AUDIO_REPLAY_GAIN_MAX;
884
885     /* If the select mode is not available, prefer the other one */
886     i_use = i_mode;
887     if( i_use != AUDIO_REPLAY_GAIN_MAX && !p_input->replay_gain.pb_gain[i_use] )
888     {
889         for( i_use = 0; i_use < AUDIO_REPLAY_GAIN_MAX; i_use++ )
890         {
891             if( p_input->replay_gain.pb_gain[i_use] )
892                 break;
893         }
894     }
895
896     /* */
897     if( i_use != AUDIO_REPLAY_GAIN_MAX )
898         f_gain = p_input->replay_gain.pf_gain[i_use] + var_GetFloat( p_aout, "audio-replay-gain-preamp" );
899     else if( i_mode != AUDIO_REPLAY_GAIN_MAX )
900         f_gain = var_GetFloat( p_aout, "audio-replay-gain-default" );
901     else
902         f_gain = 0.0;
903     p_input->multiplier = pow( 10.0, f_gain / 20.0 );
904
905     /* */
906     if( p_input->replay_gain.pb_peak[i_use] &&
907         var_GetBool( p_aout, "audio-replay-gain-peak-protection" ) &&
908         p_input->replay_gain.pf_peak[i_use] * p_input->multiplier > 1.0 )
909     {
910         p_input->multiplier = 1.0f / p_input->replay_gain.pf_peak[i_use];
911     }
912
913     free( psz_replay_gain );
914 }