]> git.sesse.net Git - vlc/blob - src/audio_output/output.c
aout: separate time and play callbacks
[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 <vlc_common.h>
29 #include <vlc_aout.h>
30 #include <vlc_modules.h>
31 #include <vlc_cpu.h>
32
33 #include "libvlc.h"
34 #include "aout_internal.h"
35
36 /* Local functions */
37 static void aout_Destructor( vlc_object_t * p_this );
38
39 static int var_Copy (vlc_object_t *src, const char *name, vlc_value_t prev,
40                      vlc_value_t value, void *data)
41 {
42     vlc_object_t *dst = data;
43
44     (void) src; (void) prev;
45     return var_Set (dst, name, value);
46 }
47
48 /**
49  * Supply or update the current custom ("hardware") volume.
50  * @note This only makes sense after calling aout_VolumeHardInit().
51  * @param volume current custom volume
52  *
53  * @warning The caller (i.e. the audio output plug-in) is responsible for
54  * interlocking and synchronizing call to this function and to the
55  * audio_output_t.volume_set callback. This ensures that VLC gets correct
56  * volume information (possibly with a latency).
57  */
58 static void aout_VolumeNotify (audio_output_t *aout, float volume)
59 {
60     var_SetFloat (aout, "volume", volume);
61 }
62
63 static void aout_MuteNotify (audio_output_t *aout, bool mute)
64 {
65     var_SetBool (aout, "mute", mute);
66 }
67
68 static void aout_PolicyNotify (audio_output_t *aout, bool cork)
69 {
70     (cork ? var_IncInteger : var_DecInteger) (aout->p_parent, "corks");
71 }
72
73 static int aout_GainNotify (audio_output_t *aout, float gain)
74 {
75     aout_owner_t *owner = aout_owner (aout);
76
77     aout_assert_locked (aout);
78     aout_volume_SetVolume (owner->volume, gain);
79     /* XXX: ideally, return -1 if format cannot be amplified */
80     return 0;
81 }
82
83 #undef aout_New
84 /**
85  * Creates an audio output object and initializes an output module.
86  */
87 audio_output_t *aout_New (vlc_object_t *parent)
88 {
89     audio_output_t *aout = vlc_custom_create (parent, sizeof (aout_instance_t),
90                                               "audio output");
91     if (unlikely(aout == NULL))
92         return NULL;
93
94     aout_owner_t *owner = aout_owner (aout);
95
96     vlc_mutex_init (&owner->lock);
97     vlc_object_set_destructor (aout, aout_Destructor);
98
99     /* Audio output module callbacks */
100     var_Create (aout, "volume", VLC_VAR_FLOAT);
101     var_AddCallback (aout, "volume", var_Copy, parent);
102     var_Create (aout, "mute", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
103     var_AddCallback (aout, "mute", var_Copy, parent);
104
105     aout->event.volume_report = aout_VolumeNotify;
106     aout->event.mute_report = aout_MuteNotify;
107     aout->event.policy_report = aout_PolicyNotify;
108     aout->event.gain_request = aout_GainNotify;
109
110     /* Audio output module initialization */
111     aout->start = NULL;
112     aout->stop = NULL;
113     aout->volume_set = NULL;
114     aout->mute_set = NULL;
115     owner->module = module_need (aout, "audio output", "$aout", false);
116     if (owner->module == NULL)
117     {
118         msg_Err (aout, "no suitable audio output module");
119         vlc_object_release (aout);
120         return NULL;
121     }
122
123     /*
124      * Persistent audio output variables
125      */
126     vlc_value_t val, text;
127     module_config_t *cfg;
128     char *str;
129
130     /* Visualizations */
131     var_Create (aout, "visual", VLC_VAR_STRING | VLC_VAR_HASCHOICE);
132     text.psz_string = _("Visualizations");
133     var_Change (aout, "visual", VLC_VAR_SETTEXT, &text, NULL);
134     val.psz_string = (char *)"";
135     text.psz_string = _("Disable");
136     var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
137     val.psz_string = (char *)"spectrometer";
138     text.psz_string = _("Spectrometer");
139     var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
140     val.psz_string = (char *)"scope";
141     text.psz_string = _("Scope");
142     var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
143     val.psz_string = (char *)"spectrum";
144     text.psz_string = _("Spectrum");
145     var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
146     val.psz_string = (char *)"vuMeter";
147     text.psz_string = _("Vu meter");
148     var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
149     /* Look for goom plugin */
150     if (module_exists ("goom"))
151     {
152         val.psz_string = (char *)"goom";
153         text.psz_string = (char *)"Goom";
154         var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
155     }
156     /* Look for libprojectM plugin */
157     if (module_exists ("projectm"))
158     {
159         val.psz_string = (char *)"projectm";
160         text.psz_string = (char*)"projectM";
161         var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
162     }
163     /* Look for VSXu plugin */
164     if (module_exists ("vsxu"))
165     {
166         val.psz_string = (char *)"vsxu";
167         text.psz_string = (char*)"Vovoid VSXu";
168         var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
169     }
170     str = var_GetNonEmptyString (aout, "effect-list");
171     if (str != NULL)
172     {
173         var_SetString (aout, "visual", str);
174         free (str);
175     }
176
177     /* Equalizer */
178     var_Create (aout, "equalizer", VLC_VAR_STRING | VLC_VAR_HASCHOICE);
179     text.psz_string = _("Equalizer");
180     var_Change (aout, "equalizer", VLC_VAR_SETTEXT, &text, NULL);
181     val.psz_string = (char*)"";
182     text.psz_string = _("Disable");
183     var_Change (aout, "equalizer", VLC_VAR_ADDCHOICE, &val, &text);
184     cfg = config_FindConfig (VLC_OBJECT(aout), "equalizer-preset");
185     if (likely(cfg != NULL))
186         for (unsigned i = 0; i < cfg->list_count; i++)
187         {
188             val.psz_string = cfg->list.psz[i];
189             text.psz_string = vlc_gettext(cfg->list_text[i]);
190             var_Change (aout, "equalizer", VLC_VAR_ADDCHOICE, &val, &text);
191         }
192
193     var_Create (aout, "audio-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
194     text.psz_string = _("Audio filters");
195     var_Change (aout, "audio-filter", VLC_VAR_SETTEXT, &text, NULL);
196
197
198     var_Create (aout, "audio-visual", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
199     text.psz_string = _("Audio visualizations");
200     var_Change (aout, "audio-visual", VLC_VAR_SETTEXT, &text, NULL);
201
202     /* Replay gain */
203     var_Create (aout, "audio-replay-gain-mode",
204                 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
205     text.psz_string = _("Replay gain");
206     var_Change (aout, "audio-replay-gain-mode", VLC_VAR_SETTEXT, &text, NULL);
207     cfg = config_FindConfig (VLC_OBJECT(aout), "audio-replay-gain-mode");
208     if (likely(cfg != NULL))
209         for (unsigned i = 0; i < cfg->list_count; i++)
210         {
211             val.psz_string = cfg->list.psz[i];
212             text.psz_string = vlc_gettext(cfg->list_text[i]);
213             var_Change (aout, "audio-replay-gain-mode", VLC_VAR_ADDCHOICE,
214                             &val, &text);
215         }
216
217     return aout;
218 }
219
220 /**
221  * Deinitializes an audio output module and destroys an audio output object.
222  */
223 void aout_Destroy (audio_output_t *aout)
224 {
225     aout_owner_t *owner = aout_owner (aout);
226
227     aout_lock (aout);
228     module_unneed (aout, owner->module);
229     /* Protect against late call from intf.c */
230     aout->volume_set = NULL;
231     aout->mute_set = NULL;
232     aout_unlock (aout);
233
234     var_DelCallback (aout, "mute", var_Copy, aout->p_parent);
235     var_SetFloat (aout, "volume", -1.f);
236     var_DelCallback (aout, "volume", var_Copy, aout->p_parent);
237     vlc_object_release (aout);
238 }
239
240 /**
241  * Destroys the audio output lock used (asynchronously) by interface functions.
242  */
243 static void aout_Destructor (vlc_object_t *obj)
244 {
245     audio_output_t *aout = (audio_output_t *)obj;
246     aout_owner_t *owner = aout_owner (aout);
247
248     vlc_mutex_destroy (&owner->lock);
249 }
250
251 /**
252  * Gets the volume of the audio output stream (independent of mute).
253  * \return Current audio volume (0. = silent, 1. = nominal),
254  * or a strictly negative value if undefined.
255  */
256 float aout_VolumeGet (audio_output_t *aout)
257 {
258     return var_GetFloat (aout, "volume");
259 }
260
261 /**
262  * Sets the volume of the audio output stream.
263  * \note The mute status is not changed.
264  * \return 0 on success, -1 on failure.
265  */
266 int aout_VolumeSet (audio_output_t *aout, float vol)
267 {
268     int ret = -1;
269
270     aout_lock (aout);
271     if (aout->volume_set != NULL)
272         ret = aout->volume_set (aout, vol);
273     aout_unlock (aout);
274     return ret;
275 }
276
277 /**
278  * Gets the audio output stream mute flag.
279  * \return 0 if not muted, 1 if muted, -1 if undefined.
280  */
281 int aout_MuteGet (audio_output_t *aout)
282 {
283     return var_InheritBool (aout, "mute");
284 }
285
286 /**
287  * Sets the audio output stream mute flag.
288  * \return 0 on success, -1 on failure.
289  */
290 int aout_MuteSet (audio_output_t *aout, bool mute)
291 {
292     int ret = -1;
293
294     aout_lock (aout);
295     if (aout->mute_set != NULL)
296         ret = aout->mute_set (aout, mute);
297     aout_unlock (aout);
298     return ret;
299 }
300
301 /**
302  * Starts an audio output stream.
303  * \param fmtp audio output stream format [IN/OUT]
304  * \warning The caller must hold the audio output lock.
305  */
306 int aout_OutputNew (audio_output_t *aout, const audio_sample_format_t *fmtp)
307 {
308     aout_owner_t *owner = aout_owner (aout);
309
310     audio_sample_format_t fmt = *fmtp;
311     aout_FormatPrepare (&fmt);
312
313     aout_assert_locked (aout);
314
315     if (aout->start (aout, &fmt))
316     {
317         msg_Err (aout, "module not functional");
318         return -1;
319     }
320
321     if (!var_Type (aout, "stereo-mode"))
322         var_Create (aout, "stereo-mode",
323                     VLC_VAR_INTEGER | VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT);
324
325     /* The user may have selected a different channels configuration. */
326     var_AddCallback (aout, "stereo-mode", aout_ChannelsRestart, NULL);
327     switch (var_GetInteger (aout, "stereo-mode"))
328     {
329         case AOUT_VAR_CHAN_RSTEREO:
330             fmt.i_original_channels |= AOUT_CHAN_REVERSESTEREO;
331              break;
332         case AOUT_VAR_CHAN_STEREO:
333             fmt.i_original_channels = AOUT_CHANS_STEREO;
334             break;
335         case AOUT_VAR_CHAN_LEFT:
336             fmt.i_original_channels = AOUT_CHAN_LEFT;
337             break;
338         case AOUT_VAR_CHAN_RIGHT:
339             fmt.i_original_channels = AOUT_CHAN_RIGHT;
340             break;
341         case AOUT_VAR_CHAN_DOLBYS:
342             fmt.i_original_channels = AOUT_CHANS_STEREO|AOUT_CHAN_DOLBYSTEREO;
343             break;
344         default:
345         {
346             if ((fmt.i_original_channels & AOUT_CHAN_PHYSMASK)
347                                                          != AOUT_CHANS_STEREO)
348                  break;
349
350             vlc_value_t val, txt;
351             val.i_int = 0;
352             var_Change (aout, "stereo-mode", VLC_VAR_DELCHOICE, &val, NULL);
353             txt.psz_string = _("Stereo audio mode");
354             var_Change (aout, "stereo-mode", VLC_VAR_SETTEXT, &txt, NULL);
355             if (fmt.i_original_channels & AOUT_CHAN_DOLBYSTEREO)
356             {
357                 val.i_int = AOUT_VAR_CHAN_DOLBYS;
358                 txt.psz_string = _("Dolby Surround");
359             }
360             else
361             {
362                 val.i_int = AOUT_VAR_CHAN_STEREO;
363                 txt.psz_string = _("Stereo");
364             }
365             var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
366             var_Change (aout, "stereo-mode", VLC_VAR_SETVALUE, &val, NULL);
367             val.i_int = AOUT_VAR_CHAN_LEFT;
368             txt.psz_string = _("Left");
369             var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
370             if (fmt.i_original_channels & AOUT_CHAN_DUALMONO)
371             {   /* Go directly to the left channel. */
372                 fmt.i_original_channels = AOUT_CHAN_LEFT;
373                 var_Change (aout, "stereo-mode", VLC_VAR_SETVALUE, &val, NULL);
374             }
375             val.i_int = AOUT_VAR_CHAN_RIGHT;
376             txt.psz_string = _("Right");
377             var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
378             val.i_int = AOUT_VAR_CHAN_RSTEREO;
379             txt.psz_string = _("Reverse stereo");
380             var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
381         }
382     }
383
384     aout_FormatPrepare (&fmt);
385     aout_FormatPrint (aout, "output", &fmt );
386
387     /* Choose the mixer format. */
388     owner->mixer_format = fmt;
389     if (!AOUT_FMT_LINEAR(&fmt))
390         owner->mixer_format.i_format = fmtp->i_format;
391     else
392     /* Most audio filters can only deal with single-precision,
393      * so lets always use that when hardware supports floating point. */
394     if( HAVE_FPU )
395         owner->mixer_format.i_format = VLC_CODEC_FL32;
396     else
397     /* Fallback to 16-bits. This avoids pointless conversion to and from
398      * 32-bits samples for the sole purpose of software mixing. */
399         owner->mixer_format.i_format = VLC_CODEC_S16N;
400
401     aout_FormatPrepare (&owner->mixer_format);
402     aout_FormatPrint (aout, "mixer", &owner->mixer_format);
403
404     /* Create converters. */
405     owner->nb_converters = 0;
406     if (aout_FiltersPipelineCreate (aout, owner->converters,
407                                     &owner->nb_converters,
408                     sizeof (owner->converters) / sizeof (owner->converters[0]),
409                                     &owner->mixer_format, &fmt) < 0)
410     {
411         msg_Err (aout, "couldn't create audio output pipeline");
412         aout_OutputDelete (aout);
413         return -1;
414     }
415     return 0;
416 }
417
418 /**
419  * Stops the audio output stream (undoes aout_OutputNew()).
420  * \note This can only be called after a succesful aout_OutputNew().
421  * \warning The caller must hold the audio output lock.
422  */
423 void aout_OutputDelete (audio_output_t *aout)
424 {
425     aout_owner_t *owner = aout_owner (aout);
426
427     aout_assert_locked (aout);
428
429     var_DelCallback (aout, "stereo-mode", aout_ChannelsRestart, NULL);
430     if (aout->stop != NULL)
431         aout->stop (aout);
432     aout_FiltersPipelineDestroy (owner->converters, owner->nb_converters);
433 }
434
435 /**
436  * Plays a decoded audio buffer.
437  * \note This can only be called after a succesful aout_OutputNew().
438  * \warning The caller must hold the audio output lock.
439  */
440 void aout_OutputPlay (audio_output_t *aout, block_t *block)
441 {
442     aout_owner_t *owner = aout_owner (aout);
443     mtime_t drift;
444
445     aout_assert_locked (aout);
446
447     block = aout_FiltersPipelinePlay (owner->converters, owner->nb_converters,
448                                       block);
449     if (block == NULL)
450         return;
451     if (block->i_buffer == 0)
452     {
453         block_Release (block);
454         return;
455     }
456
457     if (aout->time_get != NULL && aout->time_get (aout, &drift) == 0)
458         drift -= block->i_pts;
459     else
460         drift = 0;
461
462     aout->play (aout, block);
463 /**
464  * Notifies the audio input of the drift from the requested audio
465  * playback timestamp (@ref block_t.i_pts) to the anticipated playback time
466  * as reported by the audio output hardware.
467  * Depending on the drift amplitude, the input core may ignore the drift
468  * trigger upsampling or downsampling, or even discard samples.
469  * Future VLC versions may instead adjust the input decoding speed.
470  *
471  * The audio output plugin is responsible for estimating the time. Typically,
472  * the audio output can estimate the total buffer delay. Then:
473  *    pts = mdate() + delay
474  */
475     if (drift < -AOUT_MAX_PTS_ADVANCE || +AOUT_MAX_PTS_DELAY < drift)
476     {
477         msg_Warn (aout, "not synchronized (%"PRId64" us), resampling",
478                   drift);
479         if (date_Get (&owner->sync.date) != VLC_TS_INVALID)
480             date_Move (&owner->sync.date, drift);
481     }
482 }
483
484 /**
485  * Notifies the audio output (if any) of pause/resume events.
486  * This enables the output to expedite pause, instead of waiting for its
487  * buffers to drain.
488  * \note This can only be called after a succesful aout_OutputNew().
489  * \warning The caller must hold the audio output lock.
490  */
491 void aout_OutputPause( audio_output_t *aout, bool pause, mtime_t date )
492 {
493     aout_assert_locked( aout );
494     if( aout->pause != NULL )
495         aout->pause( aout, pause, date );
496 }
497
498 /**
499  * Flushes or drains the audio output buffers.
500  * This enables the output to expedite seek and stop.
501  * \param wait if true, wait for buffer playback (i.e. drain),
502  *             if false, discard the buffers immediately (i.e. flush)
503  * \note This can only be called after a succesful aout_OutputNew().
504  * \warning The caller must hold the audio output lock.
505  */
506 void aout_OutputFlush( audio_output_t *aout, bool wait )
507 {
508     aout_assert_locked( aout );
509
510     if( aout->flush != NULL )
511         aout->flush( aout, wait );
512 }