]> git.sesse.net Git - vlc/blob - src/audio_output/output.c
aout: move audio output module function (back) 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  * Starts an audio output stream.
255  * \param fmtp audio output stream format [IN/OUT]
256  * \warning The caller must hold the audio output lock.
257  */
258 int aout_OutputNew (audio_output_t *aout, const audio_sample_format_t *fmtp)
259 {
260     aout_owner_t *owner = aout_owner (aout);
261
262     audio_sample_format_t fmt = *fmtp;
263     aout_FormatPrepare (&fmt);
264
265     aout_assert_locked (aout);
266
267     if (aout->start (aout, &fmt))
268     {
269         msg_Err (aout, "module not functional");
270         return -1;
271     }
272
273     if (!var_Type (aout, "stereo-mode"))
274         var_Create (aout, "stereo-mode",
275                     VLC_VAR_INTEGER | VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT);
276
277     /* The user may have selected a different channels configuration. */
278     var_AddCallback (aout, "stereo-mode", aout_ChannelsRestart, NULL);
279     switch (var_GetInteger (aout, "stereo-mode"))
280     {
281         case AOUT_VAR_CHAN_RSTEREO:
282             fmt.i_original_channels |= AOUT_CHAN_REVERSESTEREO;
283              break;
284         case AOUT_VAR_CHAN_STEREO:
285             fmt.i_original_channels = AOUT_CHANS_STEREO;
286             break;
287         case AOUT_VAR_CHAN_LEFT:
288             fmt.i_original_channels = AOUT_CHAN_LEFT;
289             break;
290         case AOUT_VAR_CHAN_RIGHT:
291             fmt.i_original_channels = AOUT_CHAN_RIGHT;
292             break;
293         case AOUT_VAR_CHAN_DOLBYS:
294             fmt.i_original_channels = AOUT_CHANS_STEREO|AOUT_CHAN_DOLBYSTEREO;
295             break;
296         default:
297         {
298             if ((fmt.i_original_channels & AOUT_CHAN_PHYSMASK)
299                                                          != AOUT_CHANS_STEREO)
300                  break;
301
302             vlc_value_t val, txt;
303             val.i_int = 0;
304             var_Change (aout, "stereo-mode", VLC_VAR_DELCHOICE, &val, NULL);
305             txt.psz_string = _("Stereo audio mode");
306             var_Change (aout, "stereo-mode", VLC_VAR_SETTEXT, &txt, NULL);
307             if (fmt.i_original_channels & AOUT_CHAN_DOLBYSTEREO)
308             {
309                 val.i_int = AOUT_VAR_CHAN_DOLBYS;
310                 txt.psz_string = _("Dolby Surround");
311             }
312             else
313             {
314                 val.i_int = AOUT_VAR_CHAN_STEREO;
315                 txt.psz_string = _("Stereo");
316             }
317             var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
318             var_Change (aout, "stereo-mode", VLC_VAR_SETVALUE, &val, NULL);
319             val.i_int = AOUT_VAR_CHAN_LEFT;
320             txt.psz_string = _("Left");
321             var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
322             if (fmt.i_original_channels & AOUT_CHAN_DUALMONO)
323             {   /* Go directly to the left channel. */
324                 fmt.i_original_channels = AOUT_CHAN_LEFT;
325                 var_Change (aout, "stereo-mode", VLC_VAR_SETVALUE, &val, NULL);
326             }
327             val.i_int = AOUT_VAR_CHAN_RIGHT;
328             txt.psz_string = _("Right");
329             var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
330             val.i_int = AOUT_VAR_CHAN_RSTEREO;
331             txt.psz_string = _("Reverse stereo");
332             var_Change (aout, "stereo-mode", VLC_VAR_ADDCHOICE, &val, &txt);
333         }
334     }
335
336     aout_FormatPrepare (&fmt);
337     aout_FormatPrint (aout, "output", &fmt );
338
339     /* Choose the mixer format. */
340     owner->mixer_format = fmt;
341     if (!AOUT_FMT_LINEAR(&fmt))
342         owner->mixer_format.i_format = fmtp->i_format;
343     else
344     /* Most audio filters can only deal with single-precision,
345      * so lets always use that when hardware supports floating point. */
346     if( HAVE_FPU )
347         owner->mixer_format.i_format = VLC_CODEC_FL32;
348     else
349     /* Fallback to 16-bits. This avoids pointless conversion to and from
350      * 32-bits samples for the sole purpose of software mixing. */
351         owner->mixer_format.i_format = VLC_CODEC_S16N;
352
353     aout_FormatPrepare (&owner->mixer_format);
354     aout_FormatPrint (aout, "mixer", &owner->mixer_format);
355
356     /* Create filters. */
357     owner->nb_filters = 0;
358     if (aout_FiltersCreatePipeline (aout, owner->filters, &owner->nb_filters,
359                                     &owner->mixer_format, &fmt) < 0)
360     {
361         msg_Err (aout, "couldn't create audio output pipeline");
362         aout_OutputDelete (aout);
363         return -1;
364     }
365     return 0;
366 }
367
368 /**
369  * Stops the audio output stream (undoes aout_OutputNew()).
370  * \note This can only be called after a succesful aout_OutputNew().
371  * \warning The caller must hold the audio output lock.
372  */
373 void aout_OutputDelete (audio_output_t *aout)
374 {
375     aout_owner_t *owner = aout_owner (aout);
376
377     aout_assert_locked (aout);
378
379     var_DelCallback (aout, "stereo-mode", aout_ChannelsRestart, NULL);
380     if (aout->stop != NULL)
381         aout->stop (aout);
382     aout_FiltersDestroyPipeline (owner->filters, owner->nb_filters);
383 }
384
385 /**
386  * Plays a decoded audio buffer.
387  * \note This can only be called after a succesful aout_OutputNew().
388  * \warning The caller must hold the audio output lock.
389  */
390 void aout_OutputPlay (audio_output_t *aout, block_t *block)
391 {
392     aout_owner_t *owner = aout_owner (aout);
393     mtime_t drift = 0;
394
395     aout_assert_locked (aout);
396
397     aout_FiltersPlay (owner->filters, owner->nb_filters, &block);
398     if (block == NULL)
399         return;
400     if (block->i_buffer == 0)
401     {
402         block_Release (block);
403         return;
404     }
405
406     aout->play (aout, block, &drift);
407 /**
408  * Notifies the audio input of the drift from the requested audio
409  * playback timestamp (@ref block_t.i_pts) to the anticipated playback time
410  * as reported by the audio output hardware.
411  * Depending on the drift amplitude, the input core may ignore the drift
412  * trigger upsampling or downsampling, or even discard samples.
413  * Future VLC versions may instead adjust the input decoding speed.
414  *
415  * The audio output plugin is responsible for estimating the drift. A negative
416  * value means playback is ahead of the intended time and a positive value
417  * means playback is late from the intended time. In most cases, the audio
418  * output can estimate the delay until playback of the next sample to be
419  * queued. Then, before the block is queued:
420  *    drift = mdate() + delay - block->i_pts
421  * where mdate() + delay is the estimated time when the sample will be rendered
422  * and block->i_pts is the intended time.
423  */
424     if (drift < -AOUT_MAX_PTS_ADVANCE || +AOUT_MAX_PTS_DELAY < drift)
425     {
426         msg_Warn (aout, "not synchronized (%"PRId64" us), resampling",
427                   drift);
428         if (date_Get (&owner->sync.date) != VLC_TS_INVALID)
429             date_Move (&owner->sync.date, drift);
430     }
431 }
432
433 /**
434  * Notifies the audio output (if any) of pause/resume events.
435  * This enables the output to expedite pause, instead of waiting for its
436  * buffers to drain.
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_OutputPause( audio_output_t *aout, bool pause, mtime_t date )
441 {
442     aout_assert_locked( aout );
443     if( aout->pause != NULL )
444         aout->pause( aout, pause, date );
445 }
446
447 /**
448  * Flushes or drains the audio output buffers.
449  * This enables the output to expedite seek and stop.
450  * \param wait if true, wait for buffer playback (i.e. drain),
451  *             if false, discard the buffers immediately (i.e. flush)
452  * \note This can only be called after a succesful aout_OutputNew().
453  * \warning The caller must hold the audio output lock.
454  */
455 void aout_OutputFlush( audio_output_t *aout, bool wait )
456 {
457     aout_assert_locked( aout );
458
459     if( aout->flush != NULL )
460         aout->flush( aout, wait );
461 }