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