]> git.sesse.net Git - vlc/blob - src/audio_output/intf.c
Fix [17200]
[vlc] / src / audio_output / intf.c
1 /*****************************************************************************
2  * intf.c : audio output API towards the interface modules
3  *****************************************************************************
4  * Copyright (C) 2002-2004 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 #include <vlc/vlc.h>
29
30 #include <stdio.h>
31 #include <stdlib.h>                            /* calloc(), malloc(), free() */
32 #include <string.h>
33
34 #include "audio_output.h"
35 #include "aout_internal.h"
36
37
38 /*
39  * Volume management
40  *
41  * The hardware volume cannot be set if the output module gets deleted, so
42  * we must take the mixer lock. The software volume cannot be set while the
43  * mixer is running, so we need the mixer lock (too).
44  *
45  * Here is a schematic of the i_volume range :
46  *
47  * |------------------------------+---------------------------------------|
48  * 0                           pi_soft                                   1024
49  *
50  * Between 0 and pi_soft, the volume is done in hardware by the output
51  * module. Above, the output module will change p_aout->mixer.i_multiplier
52  * (done in software). This scaling may result * in cropping errors and
53  * should be avoided as much as possible.
54  *
55  * It is legal to have *pi_soft == 0, and do everything in software.
56  * It is also legal to have *pi_soft == 1024, and completely avoid
57  * software scaling. However, some streams (esp. A/52) are encoded with
58  * a very low volume and users may complain.
59  */
60
61 /*****************************************************************************
62  * aout_VolumeGet : get the volume of the output device
63  *****************************************************************************/
64 int __aout_VolumeGet( vlc_object_t * p_object, audio_volume_t * pi_volume )
65 {
66     int i_result = 0;
67     aout_instance_t * p_aout = vlc_object_find( p_object, VLC_OBJECT_AOUT,
68                                                 FIND_ANYWHERE );
69
70     if ( pi_volume == NULL ) return -1;
71
72     if ( p_aout == NULL )
73     {
74         *pi_volume = (audio_volume_t)config_GetInt( p_object, "volume" );
75         return 0;
76     }
77
78     vlc_mutex_lock( &p_aout->mixer_lock );
79     if ( !p_aout->mixer.b_error )
80     {
81         i_result = p_aout->output.pf_volume_get( p_aout, pi_volume );
82     }
83     else
84     {
85         *pi_volume = (audio_volume_t)config_GetInt( p_object, "volume" );
86     }
87     vlc_mutex_unlock( &p_aout->mixer_lock );
88
89     vlc_object_release( p_aout );
90     return i_result;
91 }
92
93 /*****************************************************************************
94  * aout_VolumeSet : set the volume of the output device
95  *****************************************************************************/
96 int __aout_VolumeSet( vlc_object_t * p_object, audio_volume_t i_volume )
97 {
98     vlc_value_t val;
99     aout_instance_t *p_aout = vlc_object_find( p_object, VLC_OBJECT_AOUT, FIND_ANYWHERE );
100     int i_result = 0;
101
102     config_PutInt( p_object, "volume", i_volume );
103
104     val.b_bool = VLC_TRUE;
105     var_Set( p_object->p_libvlc, "volume-change", val );
106
107     if ( p_aout == NULL ) return 0;
108
109     vlc_mutex_lock( &p_aout->mixer_lock );
110     if ( !p_aout->mixer.b_error )
111     {
112         i_result = p_aout->output.pf_volume_set( p_aout, i_volume );
113     }
114     vlc_mutex_unlock( &p_aout->mixer_lock );
115
116     var_Set( p_aout, "intf-change", val );
117     vlc_object_release( p_aout );
118     return i_result;
119 }
120
121 /*****************************************************************************
122  * aout_VolumeInfos : get the boundary pi_soft
123  *****************************************************************************/
124 int __aout_VolumeInfos( vlc_object_t * p_object, audio_volume_t * pi_soft )
125 {
126     aout_instance_t * p_aout = vlc_object_find( p_object, VLC_OBJECT_AOUT,
127                                                 FIND_ANYWHERE );
128     int i_result;
129
130     if ( p_aout == NULL ) return 0;
131
132     vlc_mutex_lock( &p_aout->mixer_lock );
133     if ( p_aout->mixer.b_error )
134     {
135         /* The output module is destroyed. */
136         i_result = -1;
137     }
138     else
139     {
140         i_result = p_aout->output.pf_volume_infos( p_aout, pi_soft );
141     }
142     vlc_mutex_unlock( &p_aout->mixer_lock );
143
144     vlc_object_release( p_aout );
145     return i_result;
146 }
147
148 /*****************************************************************************
149  * aout_VolumeUp : raise the output volume
150  *****************************************************************************
151  * If pi_volume != NULL, *pi_volume will contain the volume at the end of the
152  * function.
153  *****************************************************************************/
154 int __aout_VolumeUp( vlc_object_t * p_object, int i_nb_steps,
155                    audio_volume_t * pi_volume )
156 {
157     aout_instance_t * p_aout = vlc_object_find( p_object, VLC_OBJECT_AOUT,
158                                                 FIND_ANYWHERE );
159     int i_result = 0, i_volume = 0, i_volume_step = 0;
160
161     i_volume_step = config_GetInt( p_object->p_libvlc, "volume-step" );
162     i_volume = config_GetInt( p_object, "volume" );
163     i_volume += i_volume_step * i_nb_steps;
164     if ( i_volume > AOUT_VOLUME_MAX )
165     {
166         i_volume = AOUT_VOLUME_MAX;
167     }
168     config_PutInt( p_object, "volume", i_volume );
169     var_Create( p_object->p_libvlc_global, "saved-volume", VLC_VAR_INTEGER );
170     var_SetInteger( p_object->p_libvlc_global, "saved-volume" ,
171                     (audio_volume_t) i_volume );
172     if ( pi_volume != NULL ) *pi_volume = (audio_volume_t) i_volume;
173
174     if ( p_aout == NULL ) return 0;
175
176     vlc_mutex_lock( &p_aout->mixer_lock );
177     if ( !p_aout->mixer.b_error )
178     {
179         i_result = p_aout->output.pf_volume_set( p_aout,
180                                                 (audio_volume_t) i_volume );
181     }
182     vlc_mutex_unlock( &p_aout->mixer_lock );
183
184     vlc_object_release( p_aout );
185     return i_result;
186 }
187
188 /*****************************************************************************
189  * aout_VolumeDown : lower the output volume
190  *****************************************************************************
191  * If pi_volume != NULL, *pi_volume will contain the volume at the end of the
192  * function.
193  *****************************************************************************/
194 int __aout_VolumeDown( vlc_object_t * p_object, int i_nb_steps,
195                      audio_volume_t * pi_volume )
196 {
197     aout_instance_t * p_aout = vlc_object_find( p_object, VLC_OBJECT_AOUT,
198                                                 FIND_ANYWHERE );
199     int i_result = 0, i_volume = 0, i_volume_step = 0;
200
201     i_volume_step = config_GetInt( p_object->p_libvlc, "volume-step" );
202     i_volume = config_GetInt( p_object, "volume" );
203     i_volume -= i_volume_step * i_nb_steps;
204     if ( i_volume < AOUT_VOLUME_MIN )
205     {
206         i_volume = AOUT_VOLUME_MIN;
207     }
208     config_PutInt( p_object, "volume", i_volume );
209     var_Create( p_object->p_libvlc_global, "saved-volume", VLC_VAR_INTEGER );
210     var_SetInteger( p_object->p_libvlc_global, "saved-volume", (audio_volume_t) i_volume );
211     if ( pi_volume != NULL ) *pi_volume = (audio_volume_t) i_volume;
212
213     if ( p_aout == NULL ) return 0;
214
215     vlc_mutex_lock( &p_aout->mixer_lock );
216     if ( !p_aout->mixer.b_error )
217     {
218         i_result = p_aout->output.pf_volume_set( p_aout, (audio_volume_t) i_volume );
219     }
220     vlc_mutex_unlock( &p_aout->mixer_lock );
221
222     vlc_object_release( p_aout );
223     return i_result;
224 }
225
226 /*****************************************************************************
227  * aout_VolumeMute : Mute/un-mute the output volume
228  *****************************************************************************
229  * If pi_volume != NULL, *pi_volume will contain the volume at the end of the
230  * function (muted => 0).
231  *****************************************************************************/
232 int __aout_VolumeMute( vlc_object_t * p_object, audio_volume_t * pi_volume )
233 {
234     int i_result;
235     audio_volume_t i_volume;
236
237     i_volume = (audio_volume_t)config_GetInt( p_object, "volume" );
238     if ( i_volume != 0 )
239     {
240         /* Mute */
241         i_result = aout_VolumeSet( p_object, AOUT_VOLUME_MIN );
242         var_Create( p_object->p_libvlc_global, "saved-volume", VLC_VAR_INTEGER );
243         var_SetInteger( p_object->p_libvlc_global, "saved-volume", (int)i_volume );
244         if ( pi_volume != NULL ) *pi_volume = AOUT_VOLUME_MIN;
245     }
246     else
247     {
248         /* Un-mute */
249         var_Create( p_object->p_libvlc_global, "saved-volume", VLC_VAR_INTEGER );
250         i_volume = (audio_volume_t)var_GetInteger( p_object->p_libvlc_global,
251                                                    "saved-volume" );
252         i_result = aout_VolumeSet( p_object, i_volume );
253         if ( pi_volume != NULL ) *pi_volume = i_volume;
254     }
255
256     return i_result;
257 }
258
259 /*
260  * The next functions are not supposed to be called by the interface, but
261  * are placeholders for software-only scaling.
262  */
263
264 /* Meant to be called by the output plug-in's Open(). */
265 void aout_VolumeSoftInit( aout_instance_t * p_aout )
266 {
267     int i_volume;
268
269     p_aout->output.pf_volume_infos = aout_VolumeSoftInfos;
270     p_aout->output.pf_volume_get = aout_VolumeSoftGet;
271     p_aout->output.pf_volume_set = aout_VolumeSoftSet;
272
273     i_volume = config_GetInt( p_aout, "volume" );
274     if ( i_volume < AOUT_VOLUME_MIN )
275     {
276         i_volume = AOUT_VOLUME_DEFAULT;
277     }
278     else if ( i_volume > AOUT_VOLUME_MAX )
279     {
280         i_volume = AOUT_VOLUME_MAX;
281     }
282
283     aout_VolumeSoftSet( p_aout, (audio_volume_t)i_volume );
284 }
285
286 /* Placeholder for pf_volume_infos(). */
287 int aout_VolumeSoftInfos( aout_instance_t * p_aout, audio_volume_t * pi_soft )
288 {
289     *pi_soft = 0;
290     return 0;
291 }
292
293 /* Placeholder for pf_volume_get(). */
294 int aout_VolumeSoftGet( aout_instance_t * p_aout, audio_volume_t * pi_volume )
295 {
296     *pi_volume = p_aout->output.i_volume;
297     return 0;
298 }
299
300
301 /* Placeholder for pf_volume_set(). */
302 int aout_VolumeSoftSet( aout_instance_t * p_aout, audio_volume_t i_volume )
303 {
304     aout_MixerMultiplierSet( p_aout, (float)i_volume / AOUT_VOLUME_DEFAULT );
305     p_aout->output.i_volume = i_volume;
306     return 0;
307 }
308
309 /*
310  * The next functions are not supposed to be called by the interface, but
311  * are placeholders for unsupported scaling.
312  */
313
314 /* Meant to be called by the output plug-in's Open(). */
315 void aout_VolumeNoneInit( aout_instance_t * p_aout )
316 {
317     p_aout->output.pf_volume_infos = aout_VolumeNoneInfos;
318     p_aout->output.pf_volume_get = aout_VolumeNoneGet;
319     p_aout->output.pf_volume_set = aout_VolumeNoneSet;
320 }
321
322 /* Placeholder for pf_volume_infos(). */
323 int aout_VolumeNoneInfos( aout_instance_t * p_aout, audio_volume_t * pi_soft )
324 {
325     return -1;
326 }
327
328 /* Placeholder for pf_volume_get(). */
329 int aout_VolumeNoneGet( aout_instance_t * p_aout, audio_volume_t * pi_volume )
330 {
331     return -1;
332 }
333
334 /* Placeholder for pf_volume_set(). */
335 int aout_VolumeNoneSet( aout_instance_t * p_aout, audio_volume_t i_volume )
336 {
337     return -1;
338 }
339
340
341 /*
342  * Pipelines management
343  */
344
345 /*****************************************************************************
346  * aout_Restart : re-open the output device and rebuild the input and output
347  *                pipelines
348  *****************************************************************************
349  * This function is used whenever the parameters of the output plug-in are
350  * changed (eg. selecting S/PDIF or PCM).
351  *****************************************************************************/
352 int aout_Restart( aout_instance_t * p_aout )
353 {
354     int i;
355     vlc_bool_t b_error = 0;
356
357     vlc_mutex_lock( &p_aout->mixer_lock );
358
359     if ( p_aout->i_nb_inputs == 0 )
360     {
361         vlc_mutex_unlock( &p_aout->mixer_lock );
362         msg_Err( p_aout, "no decoder thread" );
363         return -1;
364     }
365
366     /* Lock all inputs. */
367     for ( i = 0; i < p_aout->i_nb_inputs; i++ )
368     {
369         vlc_mutex_lock( &p_aout->pp_inputs[i]->lock );
370         aout_InputDelete( p_aout, p_aout->pp_inputs[i] );
371     }
372
373     aout_MixerDelete( p_aout );
374
375     /* Re-open the output plug-in. */
376     aout_OutputDelete( p_aout );
377
378     if ( aout_OutputNew( p_aout, &p_aout->pp_inputs[0]->input ) == -1 )
379     {
380         /* Release all locks and report the error. */
381         for ( i = 0; i < p_aout->i_nb_inputs; i++ )
382         {
383             vlc_mutex_unlock( &p_aout->pp_inputs[i]->lock );
384         }
385         vlc_mutex_unlock( &p_aout->mixer_lock );
386         return -1;
387     }
388
389     if ( aout_MixerNew( p_aout ) == -1 )
390     {
391         aout_OutputDelete( p_aout );
392         for ( i = 0; i < p_aout->i_nb_inputs; i++ )
393         {
394             vlc_mutex_unlock( &p_aout->pp_inputs[i]->lock );
395         }
396         vlc_mutex_unlock( &p_aout->mixer_lock );
397         return -1;
398     }
399
400     /* Re-open all inputs. */
401     for ( i = 0; i < p_aout->i_nb_inputs; i++ )
402     {
403         aout_input_t * p_input = p_aout->pp_inputs[i];
404
405         b_error |= aout_InputNew( p_aout, p_input );
406         p_input->b_changed = 1;
407         vlc_mutex_unlock( &p_input->lock );
408     }
409
410     vlc_mutex_unlock( &p_aout->mixer_lock );
411
412     return b_error;
413 }
414
415 /*****************************************************************************
416  * aout_FindAndRestart : find the audio output instance and restart
417  *****************************************************************************
418  * This is used for callbacks of the configuration variables, and we believe
419  * that when those are changed, it is a significant change which implies
420  * rebuilding the audio-device and audio-channels variables.
421  *****************************************************************************/
422 int aout_FindAndRestart( vlc_object_t * p_this, const char *psz_name,
423                          vlc_value_t oldval, vlc_value_t val, void *p_data )
424 {
425     aout_instance_t * p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT,
426                                                 FIND_ANYWHERE );
427
428     if ( p_aout == NULL ) return VLC_SUCCESS;
429
430     if ( var_Type( p_aout, "audio-device" ) != 0 )
431     {
432         var_Destroy( p_aout, "audio-device" );
433     }
434     if ( var_Type( p_aout, "audio-channels" ) != 0 )
435     {
436         var_Destroy( p_aout, "audio-channels" );
437     }
438
439     aout_Restart( p_aout );
440     vlc_object_release( p_aout );
441
442     return VLC_SUCCESS;
443 }
444
445 /*****************************************************************************
446  * aout_ChannelsRestart : change the audio device or channels and restart
447  *****************************************************************************/
448 int aout_ChannelsRestart( vlc_object_t * p_this, const char * psz_variable,
449                           vlc_value_t old_value, vlc_value_t new_value,
450                           void * unused )
451 {
452     aout_instance_t * p_aout = (aout_instance_t *)p_this;
453
454     if ( !strcmp( psz_variable, "audio-device" ) )
455     {
456         /* This is supposed to be a significant change and supposes
457          * rebuilding the channel choices. */
458         if ( var_Type( p_aout, "audio-channels" ) >= 0 )
459         {
460             var_Destroy( p_aout, "audio-channels" );
461         }
462     }
463     aout_Restart( p_aout );
464     return 0;
465 }
466
467 /** Enable or disable an audio filter
468  * \param p_this a vlc object
469  * \param psz_name name of the filter
470  * \param b_add are we adding or removing the filter ?
471  */
472 void aout_EnableFilter( vlc_object_t *p_this, const char *psz_name,
473                         vlc_bool_t b_add )
474 {
475     char *psz_parser, *psz_string;
476     aout_instance_t * p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT,
477                                                 FIND_ANYWHERE );
478
479     if( p_aout )
480         psz_string = var_GetString( p_aout, "audio-filter" );
481     else
482         psz_string = config_GetPsz( p_this, "audio-filter" );
483
484     if( !psz_string ) psz_string = strdup("");
485
486     psz_parser = strstr( psz_string, psz_name );
487
488     if( b_add )
489     {
490         if( !psz_parser )
491         {
492             psz_parser = psz_string;
493             asprintf( &psz_string, (*psz_string) ? "%s:%s" : "%s%s",
494                             psz_string, psz_name );
495             free( psz_parser );
496         }
497         else
498         {
499             vlc_object_release( p_aout );
500             return;
501         }
502     }
503     else
504     {
505         if( psz_parser )
506         {
507             memmove( psz_parser, psz_parser + strlen(psz_name) +
508                             (*(psz_parser + strlen(psz_name)) == ':' ? 1 : 0 ),
509                             strlen(psz_parser + strlen(psz_name)) + 1 );
510
511             if( *(psz_string+strlen(psz_string ) -1 ) == ':' )
512             {
513                 *(psz_string+strlen(psz_string ) -1 ) = '\0';
514             }
515          }
516          else
517          {
518              free( psz_string );
519              return;
520          }
521     }
522
523     if( p_aout == NULL )
524         config_PutPsz( p_this, "audio-filter", psz_string );
525     else
526     {
527         var_SetString( p_aout, "audio-filter", psz_string );
528         for( int i = 0; i < p_aout->i_nb_inputs; i++ )
529             p_aout->pp_inputs[i]->b_restart = VLC_TRUE;
530         vlc_object_release( p_aout );
531     }
532     free( psz_string );
533 }
534
535 /**
536  * Change audio visualization
537  * -1 goes backwards, +1 goes forward
538  */
539 char *aout_VisualChange( vlc_object_t *p_this, int i_skip )
540 {
541     return strdup("foobar");
542 }