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: a1294110be2127cd0f43c2244c33c1453fbaeb75 $
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 <stdlib.h>
29 #include <assert.h>
30 
31 #include <vlc_common.h>
32 #include <vlc_aout.h>
33 #include <vlc_modules.h>
34 
35 #include "libvlc.h"
36 #include "aout_internal.h"
37 
38 static const char unset_str[1] = ""; /* Non-NULL constant string pointer */
39 
40 struct aout_dev
41 {
42     aout_dev_t *next;
43     char *name;
44     char id[1];
45 };
46 
47 
48 /* Local functions */
aout_OutputAssertLocked(audio_output_t * aout)49 static void aout_OutputAssertLocked (audio_output_t *aout)
50 {
51     aout_owner_t *owner = aout_owner (aout);
52 
53     vlc_assert_locked (&owner->lock);
54 }
55 
56 static void aout_Destructor( vlc_object_t * p_this );
57 
var_Copy(vlc_object_t * src,const char * name,vlc_value_t prev,vlc_value_t value,void * data)58 static int var_Copy (vlc_object_t *src, const char *name, vlc_value_t prev,
59                      vlc_value_t value, void *data)
60 {
61     vlc_object_t *dst = data;
62 
63     (void) src; (void) prev;
64     return var_Set (dst, name, value);
65 }
66 
var_CopyDevice(vlc_object_t * src,const char * name,vlc_value_t prev,vlc_value_t value,void * data)67 static int var_CopyDevice (vlc_object_t *src, const char *name,
68                            vlc_value_t prev, vlc_value_t value, void *data)
69 {
70     vlc_object_t *dst = data;
71 
72     (void) src; (void) name; (void) prev;
73     return var_Set (dst, "audio-device", value);
74 }
75 
76 /**
77  * Supply or update the current custom ("hardware") volume.
78  * @note This only makes sense after calling aout_VolumeHardInit().
79  * @param volume current custom volume
80  *
81  * @warning The caller (i.e. the audio output plug-in) is responsible for
82  * interlocking and synchronizing call to this function and to the
83  * audio_output_t.volume_set callback. This ensures that VLC gets correct
84  * volume information (possibly with a latency).
85  */
aout_VolumeNotify(audio_output_t * aout,float volume)86 static void aout_VolumeNotify (audio_output_t *aout, float volume)
87 {
88     var_SetFloat (aout, "volume", volume);
89 }
90 
aout_MuteNotify(audio_output_t * aout,bool mute)91 static void aout_MuteNotify (audio_output_t *aout, bool mute)
92 {
93     var_SetBool (aout, "mute", mute);
94 }
95 
aout_PolicyNotify(audio_output_t * aout,bool cork)96 static void aout_PolicyNotify (audio_output_t *aout, bool cork)
97 {
98     (cork ? var_IncInteger : var_DecInteger) (aout->obj.parent, "corks");
99 }
100 
aout_DeviceNotify(audio_output_t * aout,const char * id)101 static void aout_DeviceNotify (audio_output_t *aout, const char *id)
102 {
103     var_SetString (aout, "device", (id != NULL) ? id : "");
104 }
105 
aout_HotplugNotify(audio_output_t * aout,const char * id,const char * name)106 static void aout_HotplugNotify (audio_output_t *aout,
107                                 const char *id, const char *name)
108 {
109     aout_owner_t *owner = aout_owner (aout);
110     aout_dev_t *dev, **pp = &owner->dev.list;
111 
112     vlc_mutex_lock (&owner->dev.lock);
113     while ((dev = *pp) != NULL)
114     {
115         if (!strcmp (id, dev->id))
116             break;
117         pp = &dev->next;
118     }
119 
120     if (name != NULL)
121     {
122         if (dev == NULL) /* Added device */
123         {
124             dev = malloc (sizeof (*dev) + strlen (id));
125             if (unlikely(dev == NULL))
126                 goto out;
127             dev->next = NULL;
128             strcpy (dev->id, id);
129             *pp = dev;
130             owner->dev.count++;
131         }
132         else /* Modified device */
133             free (dev->name);
134         dev->name = strdup (name);
135     }
136     else
137     {
138         if (dev != NULL) /* Removed device */
139         {
140             owner->dev.count--;
141             *pp = dev->next;
142             free (dev->name);
143             free (dev);
144         }
145     }
146 out:
147     vlc_mutex_unlock (&owner->dev.lock);
148 }
149 
aout_RestartNotify(audio_output_t * aout,unsigned mode)150 static void aout_RestartNotify (audio_output_t *aout, unsigned mode)
151 {
152     aout_RequestRestart (aout, mode);
153 }
154 
aout_GainNotify(audio_output_t * aout,float gain)155 static int aout_GainNotify (audio_output_t *aout, float gain)
156 {
157     aout_owner_t *owner = aout_owner (aout);
158 
159     aout_OutputAssertLocked (aout);
160     aout_volume_SetVolume (owner->volume, gain);
161     /* XXX: ideally, return -1 if format cannot be amplified */
162     return 0;
163 }
164 
FilterCallback(vlc_object_t * obj,const char * var,vlc_value_t prev,vlc_value_t cur,void * data)165 static int FilterCallback (vlc_object_t *obj, const char *var,
166                            vlc_value_t prev, vlc_value_t cur, void *data)
167 {
168     if (strcmp(prev.psz_string, cur.psz_string))
169         aout_InputRequestRestart ((audio_output_t *)obj);
170     (void) var; (void) data;
171     return VLC_SUCCESS;
172 }
173 
StereoModeCallback(vlc_object_t * obj,const char * varname,vlc_value_t oldval,vlc_value_t newval,void * data)174 static int StereoModeCallback (vlc_object_t *obj, const char *varname,
175                                vlc_value_t oldval, vlc_value_t newval, void *data)
176 {
177     audio_output_t *aout = (audio_output_t *)obj;
178     (void)varname; (void)oldval; (void)newval; (void)data;
179 
180     aout_RestartRequest (aout, AOUT_RESTART_STEREOMODE);
181     return 0;
182 }
183 
ViewpointCallback(vlc_object_t * obj,const char * var,vlc_value_t prev,vlc_value_t cur,void * data)184 static int ViewpointCallback (vlc_object_t *obj, const char *var,
185                               vlc_value_t prev, vlc_value_t cur, void *data)
186 {
187     if( cur.p_address != NULL )
188         aout_ChangeViewpoint((audio_output_t *)obj, cur.p_address );
189     (void) var; (void) data; (void) prev;
190     return VLC_SUCCESS;
191 }
192 
193 #undef aout_New
194 /**
195  * Creates an audio output object and initializes an output module.
196  */
aout_New(vlc_object_t * parent)197 audio_output_t *aout_New (vlc_object_t *parent)
198 {
199     vlc_value_t val, text;
200 
201     audio_output_t *aout = vlc_custom_create (parent, sizeof (aout_instance_t),
202                                               "audio output");
203     if (unlikely(aout == NULL))
204         return NULL;
205 
206     aout_owner_t *owner = aout_owner (aout);
207 
208     vlc_mutex_init (&owner->lock);
209     vlc_mutex_init (&owner->req.lock);
210     vlc_mutex_init (&owner->dev.lock);
211     vlc_mutex_init (&owner->vp.lock);
212     vlc_viewpoint_init (&owner->vp.value);
213     atomic_init (&owner->vp.update, false);
214     owner->req.device = (char *)unset_str;
215     owner->req.volume = -1.f;
216     owner->req.mute = -1;
217 
218     vlc_object_set_destructor (aout, aout_Destructor);
219 
220     /* Audio output module callbacks */
221     var_Create (aout, "volume", VLC_VAR_FLOAT);
222     var_AddCallback (aout, "volume", var_Copy, parent);
223     var_Create (aout, "mute", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
224     var_AddCallback (aout, "mute", var_Copy, parent);
225     var_Create (aout, "device", VLC_VAR_STRING);
226     var_AddCallback (aout, "device", var_CopyDevice, parent);
227     /* TODO: 3.0 HACK: only way to signal DTS_HD to aout modules. */
228     var_Create (aout, "dtshd", VLC_VAR_BOOL);
229 
230     aout->event.volume_report = aout_VolumeNotify;
231     aout->event.mute_report = aout_MuteNotify;
232     aout->event.policy_report = aout_PolicyNotify;
233     aout->event.device_report = aout_DeviceNotify;
234     aout->event.hotplug_report = aout_HotplugNotify;
235     aout->event.gain_request = aout_GainNotify;
236     aout->event.restart_request = aout_RestartNotify;
237 
238     /* Audio output module initialization */
239     aout->start = NULL;
240     aout->stop = NULL;
241     aout->volume_set = NULL;
242     aout->mute_set = NULL;
243     aout->device_select = NULL;
244     owner->module = module_need (aout, "audio output", "$aout", false);
245     if (owner->module == NULL)
246     {
247         msg_Err (aout, "no suitable audio output module");
248         vlc_object_release (aout);
249         return NULL;
250     }
251 
252     /*
253      * Persistent audio output variables
254      */
255     module_config_t *cfg;
256     char *str;
257 
258     /* Visualizations */
259     var_Create (aout, "visual", VLC_VAR_STRING);
260     text.psz_string = _("Visualizations");
261     var_Change (aout, "visual", VLC_VAR_SETTEXT, &text, NULL);
262     val.psz_string = (char *)"";
263     text.psz_string = _("Disable");
264     var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
265     val.psz_string = (char *)"spectrometer";
266     text.psz_string = _("Spectrometer");
267     var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
268     val.psz_string = (char *)"scope";
269     text.psz_string = _("Scope");
270     var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
271     val.psz_string = (char *)"spectrum";
272     text.psz_string = _("Spectrum");
273     var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
274     val.psz_string = (char *)"vuMeter";
275     text.psz_string = _("VU meter");
276     var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
277     /* Look for goom plugin */
278     if (module_exists ("goom"))
279     {
280         val.psz_string = (char *)"goom";
281         text.psz_string = (char *)"Goom";
282         var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
283     }
284     /* Look for libprojectM plugin */
285     if (module_exists ("projectm"))
286     {
287         val.psz_string = (char *)"projectm";
288         text.psz_string = (char*)"projectM";
289         var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
290     }
291     /* Look for VSXu plugin */
292     if (module_exists ("vsxu"))
293     {
294         val.psz_string = (char *)"vsxu";
295         text.psz_string = (char*)"Vovoid VSXu";
296         var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
297     }
298     /* Look for glspectrum plugin */
299     if (module_exists ("glspectrum"))
300     {
301         val.psz_string = (char *)"glspectrum";
302         text.psz_string = (char*)"3D spectrum";
303         var_Change (aout, "visual", VLC_VAR_ADDCHOICE, &val, &text);
304     }
305     str = var_GetNonEmptyString (aout, "effect-list");
306     if (str != NULL)
307     {
308         var_SetString (aout, "visual", str);
309         free (str);
310     }
311 
312     var_Create (aout, "audio-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
313     var_AddCallback (aout, "audio-filter", FilterCallback, NULL);
314     text.psz_string = _("Audio filters");
315     var_Change (aout, "audio-filter", VLC_VAR_SETTEXT, &text, NULL);
316 
317     var_Create (aout, "viewpoint", VLC_VAR_ADDRESS );
318     var_AddCallback (aout, "viewpoint", ViewpointCallback, NULL);
319 
320     var_Create (aout, "audio-visual", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
321     text.psz_string = _("Audio visualizations");
322     var_Change (aout, "audio-visual", VLC_VAR_SETTEXT, &text, NULL);
323 
324     /* Replay gain */
325     var_Create (aout, "audio-replay-gain-mode",
326                 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
327     text.psz_string = _("Replay gain");
328     var_Change (aout, "audio-replay-gain-mode", VLC_VAR_SETTEXT, &text, NULL);
329     cfg = config_FindConfig("audio-replay-gain-mode");
330     if (likely(cfg != NULL))
331         for (unsigned i = 0; i < cfg->list_count; i++)
332         {
333             val.psz_string = (char *)cfg->list.psz[i];
334             text.psz_string = vlc_gettext(cfg->list_text[i]);
335             var_Change (aout, "audio-replay-gain-mode", VLC_VAR_ADDCHOICE,
336                             &val, &text);
337         }
338 
339     /* Stereo mode */
340     var_Create (aout, "stereo-mode", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
341     owner->initial_stereo_mode = var_GetInteger (aout, "stereo-mode");
342 
343     var_AddCallback (aout, "stereo-mode", StereoModeCallback, NULL);
344     vlc_value_t txt;
345     txt.psz_string = _("Stereo audio mode");
346     var_Change (aout, "stereo-mode", VLC_VAR_SETTEXT, &txt, NULL);
347 
348     /* Equalizer */
349     var_Create (aout, "equalizer-preamp", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
350     var_Create (aout, "equalizer-bands", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
351     var_Create (aout, "equalizer-preset", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
352 
353     return aout;
354 }
355 
356 /**
357  * Deinitializes an audio output module and destroys an audio output object.
358  */
aout_Destroy(audio_output_t * aout)359 void aout_Destroy (audio_output_t *aout)
360 {
361     aout_owner_t *owner = aout_owner (aout);
362 
363     aout_OutputLock (aout);
364     module_unneed (aout, owner->module);
365     /* Protect against late call from intf.c */
366     aout->volume_set = NULL;
367     aout->mute_set = NULL;
368     aout->device_select = NULL;
369     aout_OutputUnlock (aout);
370 
371     var_DelCallback (aout, "viewpoint", ViewpointCallback, NULL);
372     var_DelCallback (aout, "audio-filter", FilterCallback, NULL);
373     var_DelCallback (aout, "device", var_CopyDevice, aout->obj.parent);
374     var_DelCallback (aout, "mute", var_Copy, aout->obj.parent);
375     var_SetFloat (aout, "volume", -1.f);
376     var_DelCallback (aout, "volume", var_Copy, aout->obj.parent);
377     var_DelCallback (aout, "stereo-mode", StereoModeCallback, NULL);
378     vlc_object_release (aout);
379 }
380 
381 /**
382  * Destroys the audio output lock used (asynchronously) by interface functions.
383  */
aout_Destructor(vlc_object_t * obj)384 static void aout_Destructor (vlc_object_t *obj)
385 {
386     audio_output_t *aout = (audio_output_t *)obj;
387     aout_owner_t *owner = aout_owner (aout);
388 
389     vlc_mutex_destroy (&owner->dev.lock);
390     for (aout_dev_t *dev = owner->dev.list, *next; dev != NULL; dev = next)
391     {
392         next = dev->next;
393         free (dev->name);
394         free (dev);
395     }
396 
397     assert (owner->req.device == unset_str);
398     vlc_mutex_destroy (&owner->vp.lock);
399     vlc_mutex_destroy (&owner->req.lock);
400     vlc_mutex_destroy (&owner->lock);
401 }
402 
aout_PrepareStereoMode(audio_output_t * aout,audio_sample_format_t * restrict fmt,aout_filters_cfg_t * filters_cfg,audio_channel_type_t input_chan_type,unsigned i_nb_input_channels,int i_forced_stereo_mode)403 static void aout_PrepareStereoMode (audio_output_t *aout,
404                                     audio_sample_format_t *restrict fmt,
405                                     aout_filters_cfg_t *filters_cfg,
406                                     audio_channel_type_t input_chan_type,
407                                     unsigned i_nb_input_channels,
408                                     int i_forced_stereo_mode)
409 {
410     /* Fill Stereo mode choices */
411     var_Change (aout, "stereo-mode", VLC_VAR_CLEARCHOICES, NULL, NULL);
412     vlc_value_t val, txt, default_val = { .i_int = AOUT_VAR_CHAN_UNSET };
413     val.i_int = 0;
414 
415     if (!AOUT_FMT_LINEAR(fmt) || i_nb_input_channels == 1)
416         return;
417 
418     val.i_int = AOUT_VAR_CHAN_MONO;
419     txt.psz_string = _("Mono");
420     var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
421 
422     if (i_nb_input_channels != 2)
423     {
424         val.i_int = AOUT_VAR_CHAN_UNSET;
425         txt.psz_string = _("Original");
426         var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
427     }
428     if (fmt->i_chan_mode & AOUT_CHANMODE_DOLBYSTEREO)
429     {
430         val.i_int = AOUT_VAR_CHAN_DOLBYS;
431         txt.psz_string = _("Dolby Surround");
432     }
433     else
434     {
435         val.i_int = AOUT_VAR_CHAN_STEREO;
436         txt.psz_string = _("Stereo");
437     }
438     var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
439 
440     if (i_nb_input_channels == 2)
441     {
442         default_val.i_int = val.i_int; /* Stereo or Dolby Surround */
443 
444         val.i_int = AOUT_VAR_CHAN_LEFT;
445         txt.psz_string = _("Left");
446         var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
447         val.i_int = AOUT_VAR_CHAN_RIGHT;
448         txt.psz_string = _("Right");
449         var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
450 
451         val.i_int = AOUT_VAR_CHAN_RSTEREO;
452         txt.psz_string = _("Reverse stereo");
453         var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
454     }
455 
456     if (input_chan_type == AUDIO_CHANNEL_TYPE_AMBISONICS
457      || i_nb_input_channels > 2)
458     {
459         val.i_int = AOUT_VAR_CHAN_HEADPHONES;
460         txt.psz_string = _("Headphones");
461         var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
462 
463         if (i_forced_stereo_mode == AOUT_VAR_CHAN_UNSET
464          && aout->current_sink_info.headphones)
465         {
466             i_forced_stereo_mode = AOUT_VAR_CHAN_HEADPHONES;
467             default_val.i_int = val.i_int;
468             var_Change (aout, "stereo-mode", VLC_VAR_SETVALUE, &default_val,
469                         NULL);
470         }
471     }
472 
473     /* The user may have selected a different channels configuration. */
474     switch (i_forced_stereo_mode)
475     {
476         case AOUT_VAR_CHAN_RSTEREO:
477             filters_cfg->remap[AOUT_CHANIDX_LEFT] = AOUT_CHANIDX_RIGHT;
478             filters_cfg->remap[AOUT_CHANIDX_RIGHT] = AOUT_CHANIDX_LEFT;
479             break;
480         case AOUT_VAR_CHAN_STEREO:
481             break;
482         case AOUT_VAR_CHAN_LEFT:
483             filters_cfg->remap[AOUT_CHANIDX_RIGHT] = AOUT_CHANIDX_DISABLE;
484             break;
485         case AOUT_VAR_CHAN_RIGHT:
486             filters_cfg->remap[AOUT_CHANIDX_LEFT] = AOUT_CHANIDX_DISABLE;
487             break;
488         case AOUT_VAR_CHAN_DOLBYS:
489             fmt->i_chan_mode = AOUT_CHANMODE_DOLBYSTEREO;
490             break;
491         case AOUT_VAR_CHAN_HEADPHONES:
492             filters_cfg->headphones = true;
493             break;
494         case AOUT_VAR_CHAN_MONO:
495             /* Remix all channels into one */
496             for (size_t i = 0; i < AOUT_CHANIDX_MAX; ++ i)
497                 filters_cfg->remap[i] = AOUT_CHANIDX_LEFT;
498             break;
499         default:
500             if (i_nb_input_channels == 2
501              && fmt->i_chan_mode & AOUT_CHANMODE_DUALMONO)
502             {   /* Go directly to the left channel. */
503                 filters_cfg->remap[AOUT_CHANIDX_RIGHT] = AOUT_CHANIDX_DISABLE;
504                 default_val.i_int = val.i_int = AOUT_VAR_CHAN_LEFT;
505             }
506             var_Change (aout, "stereo-mode", VLC_VAR_SETVALUE, &default_val,
507                         NULL);
508             break;
509     }
510 }
511 
512 /**
513  * Starts an audio output stream.
514  * \param fmt audio output stream format [IN/OUT]
515  * \warning The caller must hold the audio output lock.
516  */
aout_OutputNew(audio_output_t * aout,audio_sample_format_t * restrict fmt,aout_filters_cfg_t * filters_cfg)517 int aout_OutputNew (audio_output_t *aout, audio_sample_format_t *restrict fmt,
518                     aout_filters_cfg_t *filters_cfg)
519 {
520     aout_OutputAssertLocked (aout);
521 
522     audio_channel_type_t input_chan_type = fmt->channel_type;
523     int i_forced_stereo_mode = AOUT_VAR_CHAN_UNSET;
524     unsigned i_nb_input_channels = fmt->i_channels;
525 
526     /* Ideally, the audio filters would be created before the audio output,
527      * and the ideal audio format would be the output of the filters chain.
528      * But that scheme would not really play well with digital pass-through. */
529     if (AOUT_FMT_LINEAR(fmt))
530     {
531         if (fmt->channel_type == AUDIO_CHANNEL_TYPE_BITMAP
532          && aout_FormatNbChannels(fmt) == 0)
533         {
534             /* The output channel map is unknown, use the WAVE one. */
535             assert(fmt->i_channels > 0);
536             aout_SetWavePhysicalChannels(fmt);
537         }
538 
539         if (fmt->channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS)
540         {
541             /* Set the maximum of channels to render ambisonics contents. The
542              * aout module will still be free to select less channels in order
543              * to respect the sink setup. */
544             fmt->i_physical_channels = AOUT_CHANS_7_1;
545         }
546 
547         /* Try to stay in integer domain if possible for no/slow FPU. */
548         fmt->i_format = (fmt->i_bitspersample > 16) ? VLC_CODEC_FL32
549                                                     : VLC_CODEC_S16N;
550 
551         i_forced_stereo_mode = var_GetInteger (aout, "stereo-mode");
552         if (i_forced_stereo_mode != AOUT_VAR_CHAN_UNSET)
553         {
554             if (i_forced_stereo_mode == AOUT_VAR_CHAN_LEFT
555              || i_forced_stereo_mode == AOUT_VAR_CHAN_RIGHT)
556                 fmt->i_physical_channels = AOUT_CHAN_CENTER;
557             else
558                 fmt->i_physical_channels = AOUT_CHANS_STEREO;
559         }
560 
561         aout_FormatPrepare (fmt);
562         assert (aout_FormatNbChannels(fmt) > 0);
563     }
564 
565     aout->current_sink_info.headphones = false;
566 
567     if (aout->start (aout, fmt))
568     {
569         msg_Err (aout, "module not functional");
570         return -1;
571     }
572 
573     aout_PrepareStereoMode (aout, fmt, filters_cfg, input_chan_type,
574                             i_nb_input_channels, i_forced_stereo_mode);
575 
576     aout_FormatPrepare (fmt);
577     assert (fmt->i_bytes_per_frame > 0 && fmt->i_frame_length > 0);
578     aout_FormatPrint (aout, "output", fmt);
579     return 0;
580 }
581 
582 /**
583  * Stops the audio output stream (undoes aout_OutputNew()).
584  * \note This can only be called after a successful aout_OutputNew().
585  * \warning The caller must hold the audio output lock.
586  */
aout_OutputDelete(audio_output_t * aout)587 void aout_OutputDelete (audio_output_t *aout)
588 {
589     aout_OutputAssertLocked (aout);
590 
591     if (aout->stop != NULL)
592         aout->stop (aout);
593 }
594 
aout_OutputTimeGet(audio_output_t * aout,mtime_t * delay)595 int aout_OutputTimeGet (audio_output_t *aout, mtime_t *delay)
596 {
597     aout_OutputAssertLocked (aout);
598 
599     if (aout->time_get == NULL)
600         return -1;
601     return aout->time_get (aout, delay);
602 }
603 
604 /**
605  * Plays a decoded audio buffer.
606  * \note This can only be called after a successful aout_OutputNew().
607  * \warning The caller must hold the audio output lock.
608  */
aout_OutputPlay(audio_output_t * aout,block_t * block)609 void aout_OutputPlay (audio_output_t *aout, block_t *block)
610 {
611     aout_OutputAssertLocked (aout);
612 #ifndef NDEBUG
613     aout_owner_t *owner = aout_owner (aout);
614     assert (owner->mixer_format.i_frame_length > 0);
615     assert (block->i_buffer == 0 || block->i_buffer / block->i_nb_samples ==
616             owner->mixer_format.i_bytes_per_frame /
617             owner->mixer_format.i_frame_length);
618 #endif
619     aout->play (aout, block);
620 }
621 
PauseDefault(audio_output_t * aout,bool pause,mtime_t date)622 static void PauseDefault (audio_output_t *aout, bool pause, mtime_t date)
623 {
624     if (pause)
625         aout_OutputFlush (aout, false);
626     (void) date;
627 }
628 
629 /**
630  * Notifies the audio output (if any) of pause/resume events.
631  * This enables the output to expedite pause, instead of waiting for its
632  * buffers to drain.
633  * \note This can only be called after a successful aout_OutputNew().
634  * \warning The caller must hold the audio output lock.
635  */
aout_OutputPause(audio_output_t * aout,bool pause,mtime_t date)636 void aout_OutputPause( audio_output_t *aout, bool pause, mtime_t date )
637 {
638     aout_OutputAssertLocked (aout);
639     ((aout->pause != NULL) ? aout->pause : PauseDefault) (aout, pause, date);
640 }
641 
642 /**
643  * Flushes or drains the audio output buffers.
644  * This enables the output to expedite seek and stop.
645  * \param wait if true, wait for buffer playback (i.e. drain),
646  *             if false, discard the buffers immediately (i.e. flush)
647  * \note This can only be called after a successful aout_OutputNew().
648  * \warning The caller must hold the audio output lock.
649  */
aout_OutputFlush(audio_output_t * aout,bool wait)650 void aout_OutputFlush( audio_output_t *aout, bool wait )
651 {
652     aout_OutputAssertLocked( aout );
653     aout->flush (aout, wait);
654 }
655 
aout_OutputVolumeSet(audio_output_t * aout,float vol)656 static int aout_OutputVolumeSet (audio_output_t *aout, float vol)
657 {
658     aout_OutputAssertLocked (aout);
659     return (aout->volume_set != NULL) ? aout->volume_set (aout, vol) : -1;
660 }
661 
aout_OutputMuteSet(audio_output_t * aout,bool mute)662 static int aout_OutputMuteSet (audio_output_t *aout, bool mute)
663 {
664     aout_OutputAssertLocked (aout);
665     return (aout->mute_set != NULL) ? aout->mute_set (aout, mute) : -1;
666 }
667 
aout_OutputDeviceSet(audio_output_t * aout,const char * id)668 static int aout_OutputDeviceSet (audio_output_t *aout, const char *id)
669 {
670     aout_OutputAssertLocked (aout);
671     return (aout->device_select != NULL) ? aout->device_select (aout, id) : -1;
672 }
673 
aout_OutputLock(audio_output_t * aout)674 void aout_OutputLock (audio_output_t *aout)
675 {
676     aout_owner_t *owner = aout_owner (aout);
677 
678     vlc_mutex_lock (&owner->lock);
679 }
680 
aout_OutputTryLock(audio_output_t * aout)681 static int aout_OutputTryLock (audio_output_t *aout)
682 {
683     aout_owner_t *owner = aout_owner (aout);
684 
685     return vlc_mutex_trylock (&owner->lock);
686 }
687 
aout_OutputUnlock(audio_output_t * aout)688 void aout_OutputUnlock (audio_output_t *aout)
689 {
690     aout_owner_t *owner = aout_owner (aout);
691 
692     vlc_assert_locked (&owner->lock);
693     vlc_mutex_lock (&owner->req.lock);
694 
695     if (owner->req.device != unset_str)
696     {
697         aout_OutputDeviceSet (aout, owner->req.device);
698         free (owner->req.device);
699         owner->req.device = (char *)unset_str;
700     }
701 
702     if (owner->req.volume >= 0.f)
703     {
704         aout_OutputVolumeSet (aout, owner->req.volume);
705         owner->req.volume = -1.f;
706     }
707 
708     if (owner->req.mute >= 0)
709     {
710         aout_OutputMuteSet (aout, owner->req.mute);
711         owner->req.mute = -1;
712     }
713 
714     vlc_mutex_unlock (&owner->lock);
715     /* If another thread is blocked waiting for owner->req.lock at this point,
716      * this aout_OutputUnlock() call will not see and apply its change request.
717      * The other thread will need to apply the change request itself, which
718      * implies it is able to (try-)lock owner->lock. Therefore this thread must
719      * release owner->lock _before_ owner->req.lock. Do not reorder!!! */
720     vlc_mutex_unlock (&owner->req.lock);
721 }
722 
723 /**
724  * Gets the volume of the audio output stream (independent of mute).
725  * \return Current audio volume (0. = silent, 1. = nominal),
726  * or a strictly negative value if undefined.
727  */
aout_VolumeGet(audio_output_t * aout)728 float aout_VolumeGet (audio_output_t *aout)
729 {
730     return var_GetFloat (aout, "volume");
731 }
732 
733 /**
734  * Sets the volume of the audio output stream.
735  * \note The mute status is not changed.
736  * \return 0 on success, -1 on failure (TODO).
737  */
aout_VolumeSet(audio_output_t * aout,float vol)738 int aout_VolumeSet (audio_output_t *aout, float vol)
739 {
740     aout_owner_t *owner = aout_owner (aout);
741 
742     assert (vol >= 0.f);
743     vlc_mutex_lock (&owner->req.lock);
744     owner->req.volume = vol;
745     vlc_mutex_unlock (&owner->req.lock);
746 
747     if (aout_OutputTryLock (aout) == 0)
748         aout_OutputUnlock (aout);
749     return 0;
750 }
751 
752 /**
753  * Raises the volume.
754  * \param value how much to increase (> 0) or decrease (< 0) the volume
755  * \param volp if non-NULL, will contain contain the resulting volume
756  */
aout_VolumeUpdate(audio_output_t * aout,int value,float * volp)757 int aout_VolumeUpdate (audio_output_t *aout, int value, float *volp)
758 {
759     int ret = -1;
760     float stepSize = var_InheritFloat (aout, "volume-step") / (float)AOUT_VOLUME_DEFAULT;
761     float delta = value * stepSize;
762     float vol = aout_VolumeGet (aout);
763 
764     if (vol >= 0.f)
765     {
766         vol += delta;
767         if (vol < 0.f)
768             vol = 0.f;
769         if (vol > 2.f)
770             vol = 2.f;
771         vol = (roundf (vol / stepSize)) * stepSize;
772         if (volp != NULL)
773             *volp = vol;
774         ret = aout_VolumeSet (aout, vol);
775     }
776     return ret;
777 }
778 
779 /**
780  * Gets the audio output stream mute flag.
781  * \return 0 if not muted, 1 if muted, -1 if undefined.
782  */
aout_MuteGet(audio_output_t * aout)783 int aout_MuteGet (audio_output_t *aout)
784 {
785     return var_InheritBool (aout, "mute");
786 }
787 
788 /**
789  * Sets the audio output stream mute flag.
790  * \return 0 on success, -1 on failure (TODO).
791  */
aout_MuteSet(audio_output_t * aout,bool mute)792 int aout_MuteSet (audio_output_t *aout, bool mute)
793 {
794     aout_owner_t *owner = aout_owner (aout);
795 
796     vlc_mutex_lock (&owner->req.lock);
797     owner->req.mute = mute;
798     vlc_mutex_unlock (&owner->req.lock);
799 
800     if (aout_OutputTryLock (aout) == 0)
801         aout_OutputUnlock (aout);
802     return 0;
803 }
804 
805 /**
806  * Gets the currently selected device.
807  * \return the selected device ID (caller must free() it)
808  *         NULL if no device is selected or in case of error.
809  */
aout_DeviceGet(audio_output_t * aout)810 char *aout_DeviceGet (audio_output_t *aout)
811 {
812     return var_GetNonEmptyString (aout, "device");
813 }
814 
815 /**
816  * Selects an audio output device.
817  * \param id device ID to select, or NULL for the default device
818  * \return zero on success, non-zero on error (TODO).
819  */
aout_DeviceSet(audio_output_t * aout,const char * id)820 int aout_DeviceSet (audio_output_t *aout, const char *id)
821 {
822     aout_owner_t *owner = aout_owner (aout);
823 
824     char *dev = NULL;
825     if (id != NULL)
826     {
827         dev = strdup (id);
828         if (unlikely(dev == NULL))
829             return -1;
830     }
831 
832     vlc_mutex_lock (&owner->req.lock);
833     if (owner->req.device != unset_str)
834         free (owner->req.device);
835     owner->req.device = dev;
836     vlc_mutex_unlock (&owner->req.lock);
837 
838     if (aout_OutputTryLock (aout) == 0)
839         aout_OutputUnlock (aout);
840     return 0;
841 }
842 
843 /**
844  * Enumerates possible audio output devices.
845  *
846  * The function will heap-allocate two tables of heap-allocated strings;
847  * the caller is responsible for freeing all strings and both tables.
848  *
849  * \param ids pointer to a table of device identifiers [OUT]
850  * \param names pointer to a table of device human-readable descriptions [OUT]
851  * \return the number of devices, or negative on error.
852  * \note In case of error, *ids and *names are undefined.
853  */
aout_DevicesList(audio_output_t * aout,char *** ids,char *** names)854 int aout_DevicesList (audio_output_t *aout, char ***ids, char ***names)
855 {
856     aout_owner_t *owner = aout_owner (aout);
857     char **tabid, **tabname;
858     unsigned i = 0;
859 
860     vlc_mutex_lock (&owner->dev.lock);
861     tabid = vlc_alloc (owner->dev.count, sizeof (*tabid));
862     tabname = vlc_alloc (owner->dev.count, sizeof (*tabname));
863 
864     if (unlikely(tabid == NULL || tabname == NULL))
865         goto error;
866 
867     *ids = tabid;
868     *names = tabname;
869 
870     for (aout_dev_t *dev = owner->dev.list; dev != NULL; dev = dev->next)
871     {
872         tabid[i] = strdup(dev->id);
873         if (unlikely(tabid[i] == NULL))
874             goto error;
875 
876         tabname[i] = strdup(dev->name);
877         if (unlikely(tabname[i] == NULL))
878         {
879             free(tabid[i]);
880             goto error;
881         }
882 
883         i++;
884     }
885     vlc_mutex_unlock (&owner->dev.lock);
886 
887     return i;
888 
889 error:
890     vlc_mutex_unlock(&owner->dev.lock);
891     while (i > 0)
892     {
893         i--;
894         free(tabname[i]);
895         free(tabid[i]);
896     }
897     free(tabname);
898     free(tabid);
899     return -1;
900 }
901