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