]> git.sesse.net Git - vlc/blob - src/audio_output/output.c
aout: remove old volume back-end
[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 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <math.h>
29
30 #include <vlc_common.h>
31 #include <vlc_aout.h>
32 #include <vlc_aout_intf.h>
33 #include <vlc_cpu.h>
34 #include <vlc_modules.h>
35
36 #include "libvlc.h"
37 #include "aout_internal.h"
38
39 /**
40  * Notifies the audio input of the drift from the requested audio
41  * playback timestamp (@ref block_t.i_pts) to the anticipated playback time
42  * as reported by the audio output hardware.
43  * Depending on the drift amplitude, the input core may ignore the drift
44  * trigger upsampling or downsampling, or even discard samples.
45  * Future VLC versions may instead adjust the input decoding speed.
46  *
47  * The audio output plugin is responsible for estimating the ideal current
48  * playback time defined as follows:
49  *  ideal time = buffer timestamp - (output latency + pending buffer duration)
50  *
51  * Practically, this is the PTS (block_t.i_pts) of the current buffer minus
52  * the latency reported by the output programming interface.
53  * Computing the estimated drift directly would probably be more intuitive.
54  * However the use of an absolute time value does not introduce extra
55  * measurement errors due to the CPU scheduling jitter and clock resolution.
56  * Furthermore, the ideal while it is an abstract value, is easy for most
57  * audio output plugins to compute.
58  * The following definition is equivalent but depends on the clock time:
59  *  ideal time = real time + drift
60
61  * @note If aout_LatencyReport() is never called, the core will assume that
62  * there is no drift.
63  *
64  * @param ideal estimated ideal time as defined above.
65  */
66 static void aout_OutputTimeReport (audio_output_t *aout, mtime_t ideal)
67 {
68     mtime_t delta = mdate() - ideal /* = -drift */;
69
70     aout_assert_locked (aout);
71     if (delta < -AOUT_MAX_PTS_ADVANCE || +AOUT_MAX_PTS_DELAY < delta)
72     {
73         aout_owner_t *owner = aout_owner (aout);
74
75         msg_Warn (aout, "not synchronized (%"PRId64" us), resampling",
76                   delta);
77         if (date_Get (&owner->sync.date) != VLC_TS_INVALID)
78             date_Move (&owner->sync.date, delta);
79     }
80 }
81
82 /**
83  * Supply or update the current custom ("hardware") volume.
84  * @note This only makes sense after calling aout_VolumeHardInit().
85  * @param volume current custom volume
86  *
87  * @warning The caller (i.e. the audio output plug-in) is responsible for
88  * interlocking and synchronizing call to this function and to the
89  * audio_output_t.pf_volume_set callback. This ensures that VLC gets correct
90  * volume information (possibly with a latency).
91  */
92 static void aout_OutputVolumeReport (audio_output_t *aout, float volume)
93 {
94     long vol = lroundf (volume * (float)AOUT_VOLUME_DEFAULT);
95
96     /* We cannot acquire the volume lock as this gets called from the audio
97      * output plug-in (it would cause a lock inversion). */
98     var_SetInteger (aout, "volume", vol);
99 }
100
101 static void aout_OutputMuteReport (audio_output_t *aout, bool mute)
102 {
103     var_SetBool (aout, "mute", mute);
104 }
105
106 static int aout_OutputGainRequest (audio_output_t *aout, float gain)
107 {
108     aout_owner_t *owner = aout_owner (aout);
109
110     aout_assert_locked (aout);
111     aout_volume_SetVolume (owner->volume, gain);
112     /* XXX: ideally, return -1 if format cannot be amplified */
113     return 0;
114 }
115
116 /*****************************************************************************
117  * aout_OutputNew : allocate a new output and rework the filter pipeline
118  *****************************************************************************
119  * This function is entered with the mixer lock.
120  *****************************************************************************/
121 int aout_OutputNew( audio_output_t *p_aout,
122                     const audio_sample_format_t * p_format )
123 {
124     aout_owner_t *owner = aout_owner (p_aout);
125
126     aout_assert_locked( p_aout );
127     p_aout->format = *p_format;
128     aout_FormatPrepare( &p_aout->format );
129
130     p_aout->event.time_report = aout_OutputTimeReport;
131     p_aout->event.volume_report = aout_OutputVolumeReport;
132     p_aout->event.mute_report = aout_OutputMuteReport;
133     p_aout->event.gain_request = aout_OutputGainRequest;
134
135     /* Find the best output plug-in. */
136     owner->module = module_need (p_aout, "audio output", "$aout", false);
137     if (owner->module == NULL)
138     {
139         msg_Err( p_aout, "no suitable audio output module" );
140         return -1;
141     }
142
143     if ( var_Type( p_aout, "audio-channels" ) ==
144              (VLC_VAR_INTEGER | VLC_VAR_HASCHOICE) )
145     {
146         /* The user may have selected a different channels configuration. */
147         switch( var_InheritInteger( p_aout, "audio-channels" ) )
148         {
149             case AOUT_VAR_CHAN_RSTEREO:
150                 p_aout->format.i_original_channels |= AOUT_CHAN_REVERSESTEREO;
151                 break;
152             case AOUT_VAR_CHAN_STEREO:
153                 p_aout->format.i_original_channels =
154                                               AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
155                 break;
156             case AOUT_VAR_CHAN_LEFT:
157                 p_aout->format.i_original_channels = AOUT_CHAN_LEFT;
158                 break;
159             case AOUT_VAR_CHAN_RIGHT:
160                 p_aout->format.i_original_channels = AOUT_CHAN_RIGHT;
161                 break;
162             case AOUT_VAR_CHAN_DOLBYS:
163                 p_aout->format.i_original_channels =
164                       AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_DOLBYSTEREO;
165                 break;
166         }
167     }
168     else if ( p_aout->format.i_physical_channels == AOUT_CHAN_CENTER
169               && (p_aout->format.i_original_channels
170                    & AOUT_CHAN_PHYSMASK) == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) )
171     {
172         vlc_value_t val, text;
173
174         /* Mono - create the audio-channels variable. */
175         var_Create( p_aout, "audio-channels",
176                     VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
177         text.psz_string = _("Audio Channels");
178         var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL );
179
180         val.i_int = AOUT_VAR_CHAN_STEREO; text.psz_string = _("Stereo");
181         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
182         val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left");
183         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
184         val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right");
185         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
186         if ( p_aout->format.i_original_channels & AOUT_CHAN_DUALMONO )
187         {
188             /* Go directly to the left channel. */
189             p_aout->format.i_original_channels = AOUT_CHAN_LEFT;
190             var_SetInteger( p_aout, "audio-channels", AOUT_VAR_CHAN_LEFT );
191         }
192         var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart,
193                          NULL );
194     }
195     else if ( p_aout->format.i_physical_channels ==
196                (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)
197                 && (p_aout->format.i_original_channels &
198                      (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) )
199     {
200         vlc_value_t val, text;
201
202         /* Stereo - create the audio-channels variable. */
203         var_Create( p_aout, "audio-channels",
204                     VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
205         text.psz_string = _("Audio Channels");
206         var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL );
207
208         if ( p_aout->format.i_original_channels & AOUT_CHAN_DOLBYSTEREO )
209         {
210             val.i_int = AOUT_VAR_CHAN_DOLBYS;
211             text.psz_string = _("Dolby Surround");
212         }
213         else
214         {
215             val.i_int = AOUT_VAR_CHAN_STEREO;
216             text.psz_string = _("Stereo");
217         }
218         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
219         val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left");
220         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
221         val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right");
222         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
223         val.i_int = AOUT_VAR_CHAN_RSTEREO; text.psz_string=_("Reverse stereo");
224         var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text );
225         if ( p_aout->format.i_original_channels & AOUT_CHAN_DUALMONO )
226         {
227             /* Go directly to the left channel. */
228             p_aout->format.i_original_channels = AOUT_CHAN_LEFT;
229             var_SetInteger( p_aout, "audio-channels", AOUT_VAR_CHAN_LEFT );
230         }
231         var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart,
232                          NULL );
233     }
234
235     aout_FormatPrepare( &p_aout->format );
236     aout_FormatPrint( p_aout, "output", &p_aout->format );
237
238     /* Choose the mixer format. */
239     owner->mixer_format = p_aout->format;
240     if (!AOUT_FMT_LINEAR(&p_aout->format))
241         owner->mixer_format.i_format = p_format->i_format;
242     else
243     /* Most audio filters can only deal with single-precision,
244      * so lets always use that when hardware supports floating point. */
245     if( HAVE_FPU )
246         owner->mixer_format.i_format = VLC_CODEC_FL32;
247     else
248     /* Fallback to 16-bits. This avoids pointless conversion to and from
249      * 32-bits samples for the sole purpose of software mixing. */
250         owner->mixer_format.i_format = VLC_CODEC_S16N;
251
252     aout_FormatPrepare (&owner->mixer_format);
253     aout_FormatPrint (p_aout, "mixer", &owner->mixer_format);
254
255     /* Create filters. */
256     owner->nb_filters = 0;
257     if (aout_FiltersCreatePipeline (p_aout, owner->filters,
258                                     &owner->nb_filters, &owner->mixer_format,
259                                     &p_aout->format) < 0)
260     {
261         msg_Err( p_aout, "couldn't create audio output pipeline" );
262         module_unneed (p_aout, owner->module);
263         owner->module = NULL;
264         return -1;
265     }
266     return 0;
267 }
268
269 /**
270  * Destroys the audio output plug-in instance.
271  */
272 void aout_OutputDelete (audio_output_t *aout)
273 {
274     aout_owner_t *owner = aout_owner (aout);
275
276     aout_assert_locked (aout);
277
278     if (owner->module == NULL)
279         return;
280
281     module_unneed (aout, owner->module);
282     /* Clear callbacks */
283     aout->pf_play = aout_DecDeleteBuffer; /* gruik */
284     aout->pf_pause = NULL;
285     aout->pf_flush = NULL;
286     aout->volume_set = NULL;
287     aout->mute_set = NULL;
288     owner->module = NULL;
289     aout_FiltersDestroyPipeline (owner->filters, owner->nb_filters);
290 }
291
292 /**
293  * Plays a decoded audio buffer.
294  */
295 void aout_OutputPlay (audio_output_t *aout, block_t *block)
296 {
297     aout_owner_t *owner = aout_owner (aout);
298
299     aout_assert_locked (aout);
300
301     aout_FiltersPlay (owner->filters, owner->nb_filters, &block);
302     if (block == NULL)
303         return;
304     if (block->i_buffer == 0)
305     {
306         block_Release (block);
307         return;
308     }
309
310     aout->pf_play (aout, block);
311 }
312
313 /**
314  * Notifies the audio output (if any) of pause/resume events.
315  * This enables the output to expedite pause, instead of waiting for its
316  * buffers to drain.
317  */
318 void aout_OutputPause( audio_output_t *aout, bool pause, mtime_t date )
319 {
320     aout_assert_locked( aout );
321     if( aout->pf_pause != NULL )
322         aout->pf_pause( aout, pause, date );
323 }
324
325 /**
326  * Flushes or drains the audio output buffers.
327  * This enables the output to expedite seek and stop.
328  * @param wait if true, wait for buffer playback (i.e. drain),
329  *             if false, discard the buffers immediately (i.e. flush)
330  */
331 void aout_OutputFlush( audio_output_t *aout, bool wait )
332 {
333     aout_assert_locked( aout );
334
335     if( aout->pf_flush != NULL )
336         aout->pf_flush( aout, wait );
337 }