]> git.sesse.net Git - vlc/blob - src/audio_output/output.c
aout: move volume/mute code to output.c
[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     owner->input = NULL;
100
101     /* Audio output module callbacks */
102     var_Create (aout, "volume", VLC_VAR_FLOAT);
103     var_AddCallback (aout, "volume", var_Copy, parent);
104     var_Create (aout, "mute", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
105     var_AddCallback (aout, "mute", var_Copy, parent);
106
107     aout->event.volume_report = aout_VolumeNotify;
108     aout->event.mute_report = aout_MuteNotify;
109     aout->event.policy_report = aout_PolicyNotify;
110     aout->event.gain_request = aout_GainNotify;
111
112     /* Audio output module initialization */
113     aout->start = NULL;
114     aout->stop = NULL;
115     aout->volume_set = NULL;
116     aout->mute_set = NULL;
117     owner->module = module_need (aout, "audio output", "$aout", false);
118     if (owner->module == NULL)
119     {
120         msg_Err (aout, "no suitable audio output module");
121         vlc_object_release (aout);
122         return NULL;
123     }
124
125     /*
126      * Persistent audio output variables
127      */
128     vlc_value_t val, text;
129     module_config_t *cfg;
130     char *str;
131
132     /* Visualizations */
133     var_Create (aout, "visual", VLC_VAR_STRING | VLC_VAR_HASCHOICE);
134     text.psz_string = _("Visualizations");
135     var_Change (aout, "visual", VLC_VAR_SETTEXT, &text, NULL);
136     val.psz_string = (char *)"";
137     text.psz_string = _("Disable");
138     var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
139     val.psz_string = (char *)"spectrometer";
140     text.psz_string = _("Spectrometer");
141     var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
142     val.psz_string = (char *)"scope";
143     text.psz_string = _("Scope");
144     var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
145     val.psz_string = (char *)"spectrum";
146     text.psz_string = _("Spectrum");
147     var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
148     val.psz_string = (char *)"vuMeter";
149     text.psz_string = _("Vu meter");
150     var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
151     /* Look for goom plugin */
152     if (module_exists ("goom"))
153     {
154         val.psz_string = (char *)"goom";
155         text.psz_string = (char *)"Goom";
156         var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
157     }
158     /* Look for libprojectM plugin */
159     if (module_exists ("projectm"))
160     {
161         val.psz_string = (char *)"projectm";
162         text.psz_string = (char*)"projectM";
163         var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
164     }
165     /* Look for VSXu plugin */
166     if (module_exists ("vsxu"))
167     {
168         val.psz_string = (char *)"vsxu";
169         text.psz_string = (char*)"Vovoid VSXu";
170         var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
171     }
172     str = var_GetNonEmptyString (aout, "effect-list");
173     if (str != NULL)
174     {
175         var_SetString (aout, "visual", str);
176         free (str);
177     }
178
179     /* Equalizer */
180     var_Create (aout, "equalizer", VLC_VAR_STRING | VLC_VAR_HASCHOICE);
181     text.psz_string = _("Equalizer");
182     var_Change (aout, "equalizer", VLC_VAR_SETTEXT, &text, NULL);
183     val.psz_string = (char*)"";
184     text.psz_string = _("Disable");
185     var_Change (aout, "equalizer", VLC_VAR_ADDCHOICE, &val, &text);
186     cfg = config_FindConfig (VLC_OBJECT(aout), "equalizer-preset");
187     if (likely(cfg != NULL))
188         for (unsigned i = 0; i < cfg->list_count; i++)
189         {
190             val.psz_string = cfg->list.psz[i];
191             text.psz_string = vlc_gettext(cfg->list_text[i]);
192             var_Change (aout, "equalizer", VLC_VAR_ADDCHOICE, &val, &text);
193         }
194
195     var_Create (aout, "audio-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
196     text.psz_string = _("Audio filters");
197     var_Change (aout, "audio-filter", VLC_VAR_SETTEXT, &text, NULL);
198
199
200     var_Create (aout, "audio-visual", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
201     text.psz_string = _("Audio visualizations");
202     var_Change (aout, "audio-visual", VLC_VAR_SETTEXT, &text, NULL);
203
204     /* Replay gain */
205     var_Create (aout, "audio-replay-gain-mode",
206                 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
207     text.psz_string = _("Replay gain");
208     var_Change (aout, "audio-replay-gain-mode", VLC_VAR_SETTEXT, &text, NULL);
209     cfg = config_FindConfig (VLC_OBJECT(aout), "audio-replay-gain-mode");
210     if (likely(cfg != NULL))
211         for (unsigned i = 0; i < cfg->list_count; i++)
212         {
213             val.psz_string = cfg->list.psz[i];
214             text.psz_string = vlc_gettext(cfg->list_text[i]);
215             var_Change (aout, "audio-replay-gain-mode", VLC_VAR_ADDCHOICE,
216                             &val, &text);
217         }
218
219     return aout;
220 }
221
222 /**
223  * Deinitializes an audio output module and destroys an audio output object.
224  */
225 void aout_Destroy (audio_output_t *aout)
226 {
227     aout_owner_t *owner = aout_owner (aout);
228
229     aout_lock (aout);
230     module_unneed (aout, owner->module);
231     /* Protect against late call from intf.c */
232     aout->volume_set = NULL;
233     aout->mute_set = NULL;
234     aout_unlock (aout);
235
236     var_DelCallback (aout, "mute", var_Copy, aout->p_parent);
237     var_SetFloat (aout, "volume", -1.f);
238     var_DelCallback (aout, "volume", var_Copy, aout->p_parent);
239     vlc_object_release (aout);
240 }
241
242 /**
243  * Destroys the audio output lock used (asynchronously) by interface functions.
244  */
245 static void aout_Destructor (vlc_object_t *obj)
246 {
247     audio_output_t *aout = (audio_output_t *)obj;
248     aout_owner_t *owner = aout_owner (aout);
249
250     vlc_mutex_destroy (&owner->lock);
251 }
252
253 /**
254  * Gets the volume of the audio output stream (independent of mute).
255  * \return Current audio volume (0. = silent, 1. = nominal),
256  * or a strictly negative value if undefined.
257  */
258 float aout_OutputVolumeGet (audio_output_t *aout)
259 {
260     return var_GetFloat (aout, "volume");
261 }
262
263 /**
264  * Sets the volume of the audio output stream.
265  * \note The mute status is not changed.
266  * \return 0 on success, -1 on failure.
267  */
268 int aout_OutputVolumeSet (audio_output_t *aout, float vol)
269 {
270     int ret = -1;
271
272     aout_lock (aout);
273     if (aout->volume_set != NULL)
274         ret = aout->volume_set (aout, vol);
275     aout_unlock (aout);
276     return ret;
277 }
278
279 /**
280  * Gets the audio output stream mute flag.
281  * \return 0 if not muted, 1 if muted, -1 if undefined.
282  */
283 int aout_OutputMuteGet (audio_output_t *aout)
284 {
285     return var_InheritBool (aout, "mute");
286 }
287
288 /**
289  * Sets the audio output stream mute flag.
290  * \return 0 on success, -1 on failure.
291  */
292 int aout_OutputMuteSet (audio_output_t *aout, bool mute)
293 {
294     int ret = -1;
295
296     aout_lock (aout);
297     if (aout->mute_set != NULL)
298         ret = aout->mute_set (aout, mute);
299     aout_unlock (aout);
300     return ret;
301 }
302
303 /**
304  * Starts an audio output stream.
305  * \param fmtp audio output stream format [IN/OUT]
306  * \warning The caller must hold the audio output lock.
307  */
308 int aout_OutputNew (audio_output_t *aout, const audio_sample_format_t *fmtp)
309 {
310     aout_owner_t *owner = aout_owner (aout);
311
312     audio_sample_format_t fmt = *fmtp;
313     aout_FormatPrepare (&fmt);
314
315     aout_assert_locked (aout);
316
317     if (aout->start (aout, &fmt))
318     {
319         msg_Err (aout, "module not functional");
320         return -1;
321     }
322
323     if (!var_Type (aout, "stereo-mode"))
324         var_Create (aout, "stereo-mode",
325                     VLC_VAR_INTEGER | VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT);
326
327     /* The user may have selected a different channels configuration. */
328     var_AddCallback (aout, "stereo-mode", aout_ChannelsRestart, NULL);
329     switch (var_GetInteger (aout, "stereo-mode"))
330     {
331         case AOUT_VAR_CHAN_RSTEREO:
332             fmt.i_original_channels |= AOUT_CHAN_REVERSESTEREO;
333              break;
334         case AOUT_VAR_CHAN_STEREO:
335             fmt.i_original_channels = AOUT_CHANS_STEREO;
336             break;
337         case AOUT_VAR_CHAN_LEFT:
338             fmt.i_original_channels = AOUT_CHAN_LEFT;
339             break;
340         case AOUT_VAR_CHAN_RIGHT:
341             fmt.i_original_channels = AOUT_CHAN_RIGHT;
342             break;
343         case AOUT_VAR_CHAN_DOLBYS:
344             fmt.i_original_channels = AOUT_CHANS_STEREO|AOUT_CHAN_DOLBYSTEREO;
345             break;
346         default:
347         {
348             if ((fmt.i_original_channels & AOUT_CHAN_PHYSMASK)
349                                                          != AOUT_CHANS_STEREO)
350                  break;
351
352             vlc_value_t val, txt;
353             val.i_int = 0;
354             var_Change (aout, "stereo-mode", VLC_VAR_DELCHOICE, &val, NULL);
355             txt.psz_string = _("Stereo audio mode");
356             var_Change (aout, "stereo-mode", VLC_VAR_SETTEXT, &txt, NULL);
357             if (fmt.i_original_channels & AOUT_CHAN_DOLBYSTEREO)
358             {
359                 val.i_int = AOUT_VAR_CHAN_DOLBYS;
360                 txt.psz_string = _("Dolby Surround");
361             }
362             else
363             {
364                 val.i_int = AOUT_VAR_CHAN_STEREO;
365                 txt.psz_string = _("Stereo");
366             }
367             var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
368             var_Change (aout, "stereo-mode", VLC_VAR_SETVALUE, &val, NULL);
369             val.i_int = AOUT_VAR_CHAN_LEFT;
370             txt.psz_string = _("Left");
371             var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
372             if (fmt.i_original_channels & AOUT_CHAN_DUALMONO)
373             {   /* Go directly to the left channel. */
374                 fmt.i_original_channels = AOUT_CHAN_LEFT;
375                 var_Change (aout, "stereo-mode", VLC_VAR_SETVALUE, &val, NULL);
376             }
377             val.i_int = AOUT_VAR_CHAN_RIGHT;
378             txt.psz_string = _("Right");
379             var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
380             val.i_int = AOUT_VAR_CHAN_RSTEREO;
381             txt.psz_string = _("Reverse stereo");
382             var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
383         }
384     }
385
386     aout_FormatPrepare (&fmt);
387     aout_FormatPrint (aout, "output", &fmt );
388
389     /* Choose the mixer format. */
390     owner->mixer_format = fmt;
391     if (!AOUT_FMT_LINEAR(&fmt))
392         owner->mixer_format.i_format = fmtp->i_format;
393     else
394     /* Most audio filters can only deal with single-precision,
395      * so lets always use that when hardware supports floating point. */
396     if( HAVE_FPU )
397         owner->mixer_format.i_format = VLC_CODEC_FL32;
398     else
399     /* Fallback to 16-bits. This avoids pointless conversion to and from
400      * 32-bits samples for the sole purpose of software mixing. */
401         owner->mixer_format.i_format = VLC_CODEC_S16N;
402
403     aout_FormatPrepare (&owner->mixer_format);
404     aout_FormatPrint (aout, "mixer", &owner->mixer_format);
405
406     /* Create filters. */
407     owner->nb_filters = 0;
408     if (aout_FiltersCreatePipeline (aout, owner->filters, &owner->nb_filters,
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_FiltersDestroyPipeline (owner->filters, owner->nb_filters);
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 = 0;
444
445     aout_assert_locked (aout);
446
447     aout_FiltersPlay (owner->filters, owner->nb_filters, &block);
448     if (block == NULL)
449         return;
450     if (block->i_buffer == 0)
451     {
452         block_Release (block);
453         return;
454     }
455
456     aout->play (aout, block, &drift);
457 /**
458  * Notifies the audio input of the drift from the requested audio
459  * playback timestamp (@ref block_t.i_pts) to the anticipated playback time
460  * as reported by the audio output hardware.
461  * Depending on the drift amplitude, the input core may ignore the drift
462  * trigger upsampling or downsampling, or even discard samples.
463  * Future VLC versions may instead adjust the input decoding speed.
464  *
465  * The audio output plugin is responsible for estimating the drift. A negative
466  * value means playback is ahead of the intended time and a positive value
467  * means playback is late from the intended time. In most cases, the audio
468  * output can estimate the delay until playback of the next sample to be
469  * queued. Then, before the block is queued:
470  *    drift = mdate() + delay - block->i_pts
471  * where mdate() + delay is the estimated time when the sample will be rendered
472  * and block->i_pts is the intended time.
473  */
474     if (drift < -AOUT_MAX_PTS_ADVANCE || +AOUT_MAX_PTS_DELAY < drift)
475     {
476         msg_Warn (aout, "not synchronized (%"PRId64" us), resampling",
477                   drift);
478         if (date_Get (&owner->sync.date) != VLC_TS_INVALID)
479             date_Move (&owner->sync.date, drift);
480     }
481 }
482
483 /**
484  * Notifies the audio output (if any) of pause/resume events.
485  * This enables the output to expedite pause, instead of waiting for its
486  * buffers to drain.
487  * \note This can only be called after a succesful aout_OutputNew().
488  * \warning The caller must hold the audio output lock.
489  */
490 void aout_OutputPause( audio_output_t *aout, bool pause, mtime_t date )
491 {
492     aout_assert_locked( aout );
493     if( aout->pause != NULL )
494         aout->pause( aout, pause, date );
495 }
496
497 /**
498  * Flushes or drains the audio output buffers.
499  * This enables the output to expedite seek and stop.
500  * \param wait if true, wait for buffer playback (i.e. drain),
501  *             if false, discard the buffers immediately (i.e. flush)
502  * \note This can only be called after a succesful aout_OutputNew().
503  * \warning The caller must hold the audio output lock.
504  */
505 void aout_OutputFlush( audio_output_t *aout, bool wait )
506 {
507     aout_assert_locked( aout );
508
509     if( aout->flush != NULL )
510         aout->flush( aout, wait );
511 }