]> git.sesse.net Git - vlc/blob - src/audio_output/output.c
aout: simplify volume and mute variables life cycle
[vlc] / src / audio_output / output.c
1 /*****************************************************************************
2  * output.c : internal management of output streams for the audio output
3  *****************************************************************************
4  * Copyright (C) 2002-2004 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <math.h>
32
33 #include <vlc_common.h>
34 #include <vlc_aout.h>
35 #include <vlc_aout_intf.h>
36 #include <vlc_cpu.h>
37 #include <vlc_modules.h>
38
39 #include "libvlc.h"
40 #include "aout_internal.h"
41
42 /*****************************************************************************
43  * aout_OutputNew : allocate a new output and rework the filter pipeline
44  *****************************************************************************
45  * This function is entered with the mixer lock.
46  *****************************************************************************/
47 int aout_OutputNew( audio_output_t *p_aout,
48                     const audio_sample_format_t * p_format )
49 {
50     aout_owner_t *owner = aout_owner (p_aout);
51
52     aout_assert_locked( p_aout );
53     p_aout->format = *p_format;
54
55     aout_FormatPrepare( &p_aout->format );
56
57     /* Find the best output plug-in. */
58     owner->module = module_need (p_aout, "audio output", "$aout", false);
59     if (owner->module == NULL)
60     {
61         msg_Err( p_aout, "no suitable audio output module" );
62         return -1;
63     }
64
65     if ( var_Type( p_aout, "audio-channels" ) ==
66              (VLC_VAR_INTEGER | VLC_VAR_HASCHOICE) )
67     {
68         /* The user may have selected a different channels configuration. */
69         switch( var_InheritInteger( p_aout, "audio-channels" ) )
70         {
71             case AOUT_VAR_CHAN_RSTEREO:
72                 p_aout->format.i_original_channels |= AOUT_CHAN_REVERSESTEREO;
73                 break;
74             case AOUT_VAR_CHAN_STEREO:
75                 p_aout->format.i_original_channels =
76                                               AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
77                 break;
78             case AOUT_VAR_CHAN_LEFT:
79                 p_aout->format.i_original_channels = AOUT_CHAN_LEFT;
80                 break;
81             case AOUT_VAR_CHAN_RIGHT:
82                 p_aout->format.i_original_channels = AOUT_CHAN_RIGHT;
83                 break;
84             case AOUT_VAR_CHAN_DOLBYS:
85                 p_aout->format.i_original_channels =
86                       AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_DOLBYSTEREO;
87                 break;
88         }
89     }
90     else if ( p_aout->format.i_physical_channels == AOUT_CHAN_CENTER
91               && (p_aout->format.i_original_channels
92                    & AOUT_CHAN_PHYSMASK) == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) )
93     {
94         vlc_value_t val, text;
95
96         /* Mono - create the audio-channels variable. */
97         var_Create( p_aout, "audio-channels",
98                     VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
99         text.psz_string = _("Audio Channels");
100         var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL );
101
102         val.i_int = AOUT_VAR_CHAN_STEREO; text.psz_string = _("Stereo");
103         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
104         val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left");
105         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
106         val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right");
107         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
108         if ( p_aout->format.i_original_channels & AOUT_CHAN_DUALMONO )
109         {
110             /* Go directly to the left channel. */
111             p_aout->format.i_original_channels = AOUT_CHAN_LEFT;
112             var_SetInteger( p_aout, "audio-channels", AOUT_VAR_CHAN_LEFT );
113         }
114         var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart,
115                          NULL );
116     }
117     else if ( p_aout->format.i_physical_channels ==
118                (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)
119                 && (p_aout->format.i_original_channels &
120                      (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) )
121     {
122         vlc_value_t val, text;
123
124         /* Stereo - create the audio-channels variable. */
125         var_Create( p_aout, "audio-channels",
126                     VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
127         text.psz_string = _("Audio Channels");
128         var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL );
129
130         if ( p_aout->format.i_original_channels & AOUT_CHAN_DOLBYSTEREO )
131         {
132             val.i_int = AOUT_VAR_CHAN_DOLBYS;
133             text.psz_string = _("Dolby Surround");
134         }
135         else
136         {
137             val.i_int = AOUT_VAR_CHAN_STEREO;
138             text.psz_string = _("Stereo");
139         }
140         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
141         val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left");
142         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
143         val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right");
144         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
145         val.i_int = AOUT_VAR_CHAN_RSTEREO; text.psz_string=_("Reverse stereo");
146         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
147         if ( p_aout->format.i_original_channels & AOUT_CHAN_DUALMONO )
148         {
149             /* Go directly to the left channel. */
150             p_aout->format.i_original_channels = AOUT_CHAN_LEFT;
151             var_SetInteger( p_aout, "audio-channels", AOUT_VAR_CHAN_LEFT );
152         }
153         var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart,
154                          NULL );
155     }
156     var_TriggerCallback( p_aout, "intf-change" );
157
158     aout_FormatPrepare( &p_aout->format );
159     aout_FormatPrint( p_aout, "output", &p_aout->format );
160
161     /* Choose the mixer format. */
162     owner->mixer_format = p_aout->format;
163     if (!AOUT_FMT_LINEAR(&p_aout->format))
164         owner->mixer_format.i_format = p_format->i_format;
165     else
166     /* Most audio filters can only deal with single-precision,
167      * so lets always use that when hardware supports floating point. */
168     if( HAVE_FPU )
169         owner->mixer_format.i_format = VLC_CODEC_FL32;
170     else
171     /* Otherwise, audio filters will not work. Use fixed-point if the input has
172      * more than 16-bits depth. */
173     if( p_format->i_bitspersample > 16 || !AOUT_FMT_LINEAR(p_format))
174         owner->mixer_format.i_format = VLC_CODEC_FI32;
175     else
176     /* Fallback to 16-bits. This avoids pointless conversion to and from
177      * 32-bits samples for the sole purpose of software mixing. */
178         owner->mixer_format.i_format = VLC_CODEC_S16N;
179
180     aout_FormatPrepare (&owner->mixer_format);
181     aout_FormatPrint (p_aout, "mixer", &owner->mixer_format);
182
183     /* Create filters. */
184     owner->nb_filters = 0;
185     if (aout_FiltersCreatePipeline (p_aout, owner->filters,
186                                     &owner->nb_filters, &owner->mixer_format,
187                                     &p_aout->format) < 0)
188     {
189         msg_Err( p_aout, "couldn't create audio output pipeline" );
190         module_unneed (p_aout, owner->module);
191         owner->module = NULL;
192         return -1;
193     }
194     return 0;
195 }
196
197 /**
198  * Destroys the audio output plug-in instance.
199  */
200 void aout_OutputDelete (audio_output_t *aout)
201 {
202     aout_owner_t *owner = aout_owner (aout);
203
204     aout_assert_locked (aout);
205
206     if (owner->module == NULL)
207         return;
208
209     module_unneed (aout, owner->module);
210     /* Clear callbacks */
211     aout->pf_play = aout_DecDeleteBuffer; /* gruik */
212     aout->pf_pause = NULL;
213     aout->pf_flush = NULL;
214     aout_VolumeNoneInit (aout);
215     owner->module = NULL;
216     aout_FiltersDestroyPipeline (owner->filters, owner->nb_filters);
217 }
218
219 /**
220  * Plays a decoded audio buffer.
221  */
222 void aout_OutputPlay (audio_output_t *aout, block_t *block)
223 {
224     aout_owner_t *owner = aout_owner (aout);
225
226     aout_assert_locked (aout);
227
228     aout_FiltersPlay (owner->filters, owner->nb_filters, &block);
229     if (block == NULL)
230         return;
231     if (block->i_buffer == 0)
232     {
233         block_Release (block);
234         return;
235     }
236
237     aout->pf_play (aout, block);
238 }
239
240 /**
241  * Notifies the audio output (if any) of pause/resume events.
242  * This enables the output to expedite pause, instead of waiting for its
243  * buffers to drain.
244  */
245 void aout_OutputPause( audio_output_t *aout, bool pause, mtime_t date )
246 {
247     aout_assert_locked( aout );
248     if( aout->pf_pause != NULL )
249         aout->pf_pause( aout, pause, date );
250 }
251
252 /**
253  * Flushes or drains the audio output buffers.
254  * This enables the output to expedite seek and stop.
255  * @param wait if true, wait for buffer playback (i.e. drain),
256  *             if false, discard the buffers immediately (i.e. flush)
257  */
258 void aout_OutputFlush( audio_output_t *aout, bool wait )
259 {
260     aout_assert_locked( aout );
261
262     if( aout->pf_flush != NULL )
263         aout->pf_flush( aout, wait );
264 }
265
266
267 /*** Volume handling ***/
268
269 /**
270  * Dummy volume setter. This is the default volume setter.
271  */
272 static int aout_VolumeNoneSet (audio_output_t *aout, float volume, bool mute)
273 {
274     (void)aout; (void)volume; (void)mute;
275     return -1;
276 }
277
278 /**
279  * Configures the dummy volume setter.
280  * @note Audio output plugins for which volume is irrelevant
281  * should call this function during activation.
282  */
283 void aout_VolumeNoneInit (audio_output_t *aout)
284 {
285     /* aout_New() -safely- calls this function without the lock, before any
286      * other thread knows of this audio output instance.
287     aout_assert_locked (aout); */
288     aout->pf_volume_set = aout_VolumeNoneSet;
289 }
290
291 /**
292  * Volume setter for software volume.
293  */
294 static int aout_VolumeSoftSet (audio_output_t *aout, float volume, bool mute)
295 {
296     aout_owner_t *owner = aout_owner (aout);
297
298     aout_assert_locked (aout);
299
300     /* Cubic mapping from software volume to amplification factor.
301      * This provides a good tradeoff between low and high volume ranges.
302      *
303      * This code is only used for the VLC software mixer. If you change this
304      * formula, be sure to update the aout_VolumeHardInit()-based plugins also.
305      */
306     if (!mute)
307         volume = volume * volume * volume;
308     else
309         volume = 0.;
310
311     owner->volume.multiplier = volume;
312     return 0;
313 }
314
315 /**
316  * Configures the volume setter for software mixing
317  * and apply the default volume.
318  * @note Audio output plugins that cannot apply the volume
319  * should call this function during activation.
320  */
321 void aout_VolumeSoftInit (audio_output_t *aout)
322 {
323     audio_volume_t volume = var_GetInteger (aout, "volume");
324     bool mute = var_GetBool (aout, "mute");
325
326     aout_assert_locked (aout);
327     aout->pf_volume_set = aout_VolumeSoftSet;
328     aout_VolumeSoftSet (aout, volume / (float)AOUT_VOLUME_DEFAULT, mute);
329 }
330
331 /**
332  * Configures a custom volume setter. This is used by audio outputs that can
333  * control the hardware volume directly and/or emulate it internally.
334  * @param setter volume setter callback
335  * @param restore apply volume from VLC configuration immediately
336  */
337 void aout_VolumeHardInit (audio_output_t *aout, aout_volume_cb setter,
338                           bool restore)
339 {
340     aout_assert_locked (aout);
341     aout->pf_volume_set = setter;
342
343     if (restore)
344     {
345         float vol = var_GetInteger (aout, "volume")
346                   / (float)AOUT_VOLUME_DEFAULT;
347         setter (aout, vol, var_GetBool (aout, "mute"));
348     }
349 }
350
351 /**
352  * Supply or update the current custom ("hardware") volume.
353  * @note This only makes sense after calling aout_VolumeHardInit().
354  * @param setter volume setter callback
355  * @param volume current custom volume
356  * @param mute current mute flag
357  *
358  * @warning The caller (i.e. the audio output plug-in) is responsible for
359  * interlocking and synchronizing call to this function and to the
360  * audio_output_t.pf_volume_set callback. This ensures that VLC gets correct
361  * volume information (possibly with a latency).
362  */
363 void aout_VolumeHardSet (audio_output_t *aout, float volume, bool mute)
364 {
365     audio_volume_t vol = lroundf (volume * (float)AOUT_VOLUME_DEFAULT);
366
367     /* We cannot acquire the volume lock as this gets called from the audio
368      * output plug-in (it would cause a lock inversion). */
369     var_SetInteger (aout, "volume", vol);
370     var_SetBool (aout, "mute", mute);
371     var_TriggerCallback (aout, "intf-change");
372 }