]> git.sesse.net Git - vlc/blob - modules/audio_output/alsa.c
config: improve/fix the callback prototype for config item choices
[vlc] / modules / audio_output / alsa.c
1 /*****************************************************************************
2  * alsa.c : alsa plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2010 the VideoLAN team
5  * Copyright (C) 2009-2011 RĂ©mi Denis-Courmont
6  *
7  * Authors: Henri Fallon <henri@videolan.org> - Original Author
8  *          Jeffrey Baker <jwbaker@acm.org> - Port to ALSA 1.0 API
9  *          John Paul Lorenti <jpl31@columbia.edu> - Device selection
10  *          Arnaud de Bossoreille de Ribou <bozo@via.ecp.fr> - S/PDIF and aout3
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <assert.h>
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_dialog.h>
36 #include <vlc_aout.h>
37 #include <vlc_cpu.h>
38
39 #include <alsa/asoundlib.h>
40 #include <alsa/version.h>
41
42 /** Private data for an ALSA PCM playback stream */
43 struct aout_sys_t
44 {
45     snd_pcm_t *pcm;
46     void (*reorder) (void *, size_t, unsigned);
47     float soft_gain;
48     bool soft_mute;
49 };
50
51 #include "volume.h"
52
53 #define A52_FRAME_NB 1536
54
55 static int Open (vlc_object_t *);
56 static void Close (vlc_object_t *);
57 static int EnumDevices (vlc_object_t *, char const *, char ***, char ***);
58 static void GetDevices (vlc_object_t *, const char *);
59
60 #define AUDIO_DEV_TEXT N_("Audio output device")
61 #define AUDIO_DEV_LONGTEXT N_("Audio output device (using ALSA syntax).")
62
63 #define AUDIO_CHAN_TEXT N_("Audio output channels")
64 #define AUDIO_CHAN_LONGTEXT N_("Channels available for audio output. " \
65     "If the input has more channels than the output, it will be down-mixed. " \
66     "This parameter is ignored when digital pass-through is active.")
67 static const int channels[] = {
68     AOUT_CHAN_CENTER, AOUT_CHANS_STEREO, AOUT_CHANS_4_0, AOUT_CHANS_4_1,
69     AOUT_CHANS_5_0, AOUT_CHANS_5_1, AOUT_CHANS_7_1,
70 };
71 static const char *const channels_text[] = {
72     N_("Mono"), N_("Stereo"), N_("Surround 4.0"), N_("Surround 4.1"),
73     N_("Surround 5.0"), N_("Surround 5.1"), N_("Surround 7.1"),
74 };
75
76 vlc_module_begin ()
77     set_shortname( "ALSA" )
78     set_description( N_("ALSA audio output") )
79     set_category( CAT_AUDIO )
80     set_subcategory( SUBCAT_AUDIO_AOUT )
81     add_string ("alsa-audio-device", "default",
82                 AUDIO_DEV_TEXT, AUDIO_DEV_LONGTEXT, false)
83         change_string_cb (EnumDevices)
84     add_integer ("alsa-audio-channels", AOUT_CHANS_FRONT,
85                  AUDIO_CHAN_TEXT, AUDIO_CHAN_LONGTEXT, false)
86         change_integer_list (channels, channels_text)
87     add_sw_gain ()
88     set_capability( "audio output", 150 )
89     set_callbacks( Open, Close )
90 vlc_module_end ()
91
92
93 /** Helper for ALSA -> VLC debugging output */
94 static void Dump (vlc_object_t *obj, const char *msg,
95                   int (*cb)(void *, snd_output_t *), void *p)
96 {
97     snd_output_t *output;
98     char *str;
99
100     if (unlikely(snd_output_buffer_open (&output)))
101         return;
102
103     int val = cb (p, output);
104     if (val)
105     {
106         msg_Warn (obj, "cannot get info: %s", snd_strerror (val));
107         return;
108     }
109
110     size_t len = snd_output_buffer_string (output, &str);
111     if (len > 0 && str[len - 1])
112         len--; /* strip trailing newline */
113     msg_Dbg (obj, "%s%.*s", msg, (int)len, str);
114     snd_output_close (output);
115 }
116 #define Dump(o, m, cb, p) \
117         Dump(VLC_OBJECT(o), m, (int (*)(void *, snd_output_t *))(cb), p)
118
119 static void DumpDevice (vlc_object_t *obj, snd_pcm_t *pcm)
120 {
121     snd_pcm_info_t *info;
122
123     Dump (obj, " ", snd_pcm_dump, pcm);
124     snd_pcm_info_alloca (&info);
125     if (snd_pcm_info (pcm, info) == 0)
126     {
127         msg_Dbg (obj, " device name   : %s", snd_pcm_info_get_name (info));
128         msg_Dbg (obj, " device ID     : %s", snd_pcm_info_get_id (info));
129         msg_Dbg (obj, " subdevice name: %s",
130                 snd_pcm_info_get_subdevice_name (info));
131     }
132 }
133
134 static void DumpDeviceStatus (vlc_object_t *obj, snd_pcm_t *pcm)
135 {
136     snd_pcm_status_t *status;
137
138     snd_pcm_status_alloca (&status);
139     snd_pcm_status (pcm, status);
140     Dump (obj, "current status:\n", snd_pcm_status_dump, status);
141 }
142 #define DumpDeviceStatus(o, p) DumpDeviceStatus(VLC_OBJECT(o), p)
143
144 static int DeviceChanged (vlc_object_t *obj, const char *varname,
145                           vlc_value_t prev, vlc_value_t cur, void *data)
146 {
147     aout_ChannelsRestart (obj, varname, prev, cur, data);
148
149     if (!var_Type (obj, "alsa-audio-device"))
150         var_Create (obj, "alsa-audio-device", VLC_VAR_STRING);
151     var_SetString (obj, "alsa-audio-device", cur.psz_string);
152     return VLC_SUCCESS;
153 }
154
155 static void Play (audio_output_t *, block_t *, mtime_t *);
156 static void Pause (audio_output_t *, bool, mtime_t);
157 static void PauseDummy (audio_output_t *, bool, mtime_t);
158 static void Flush (audio_output_t *, bool);
159 static void Reorder71 (void *, size_t, unsigned);
160
161 /** Initializes an ALSA playback stream */
162 static int Open (vlc_object_t *obj)
163 {
164     audio_output_t *aout = (audio_output_t *)obj;
165
166     /* Get device name */
167     char *device = var_InheritString (aout, "alsa-audio-device");
168     if (unlikely(device == NULL))
169         return VLC_ENOMEM;
170
171     snd_pcm_format_t pcm_format; /* ALSA sample format */
172     vlc_fourcc_t fourcc = aout->format.i_format;
173     bool spdif = false;
174
175     switch (fourcc)
176     {
177         case VLC_CODEC_F64B:
178             pcm_format = SND_PCM_FORMAT_FLOAT64_BE;
179             break;
180         case VLC_CODEC_F64L:
181             pcm_format = SND_PCM_FORMAT_FLOAT64_LE;
182             break;
183         case VLC_CODEC_F32B:
184             pcm_format = SND_PCM_FORMAT_FLOAT_BE;
185             break;
186         case VLC_CODEC_F32L:
187             pcm_format = SND_PCM_FORMAT_FLOAT_LE;
188             break;
189         case VLC_CODEC_S32B:
190             pcm_format = SND_PCM_FORMAT_S32_BE;
191             break;
192         case VLC_CODEC_S32L:
193             pcm_format = SND_PCM_FORMAT_S32_LE;
194             break;
195         case VLC_CODEC_S24B:
196             pcm_format = SND_PCM_FORMAT_S24_3BE;
197             break;
198         case VLC_CODEC_S24L:
199             pcm_format = SND_PCM_FORMAT_S24_3LE;
200             break;
201         case VLC_CODEC_U24B:
202             pcm_format = SND_PCM_FORMAT_U24_3BE;
203             break;
204         case VLC_CODEC_U24L:
205             pcm_format = SND_PCM_FORMAT_U24_3LE;
206             break;
207         case VLC_CODEC_S16B:
208             pcm_format = SND_PCM_FORMAT_S16_BE;
209             break;
210         case VLC_CODEC_S16L:
211             pcm_format = SND_PCM_FORMAT_S16_LE;
212             break;
213         case VLC_CODEC_U16B:
214             pcm_format = SND_PCM_FORMAT_U16_BE;
215             break;
216         case VLC_CODEC_U16L:
217             pcm_format = SND_PCM_FORMAT_U16_LE;
218             break;
219         case VLC_CODEC_S8:
220             pcm_format = SND_PCM_FORMAT_S8;
221             break;
222         case VLC_CODEC_U8:
223             pcm_format = SND_PCM_FORMAT_U8;
224             break;
225         default:
226             if (AOUT_FMT_SPDIF(&aout->format))
227                 spdif = var_InheritBool (aout, "spdif");
228             if (spdif)
229             {
230                 fourcc = VLC_CODEC_SPDIFL;
231                 pcm_format = SND_PCM_FORMAT_S16;
232             }
233             else
234             if (HAVE_FPU)
235             {
236                 fourcc = VLC_CODEC_FL32;
237                 pcm_format = SND_PCM_FORMAT_FLOAT;
238             }
239             else
240             {
241                 fourcc = VLC_CODEC_S16N;
242                 pcm_format = SND_PCM_FORMAT_S16;
243             }
244     }
245
246     /* ALSA channels */
247     /* XXX: maybe this should be shared with other dumb outputs */
248     uint32_t map = var_InheritInteger (aout, "alsa-audio-channels");
249     map &= aout->format.i_physical_channels;
250     if (unlikely(map == 0)) /* WTH? */
251         map = AOUT_CHANS_STEREO;
252
253     unsigned channels = popcount (map);
254     if (channels < aout_FormatNbChannels (&aout->format))
255         msg_Dbg (aout, "downmixing from %u to %u channels",
256                  aout_FormatNbChannels (&aout->format), channels);
257     else
258         msg_Dbg (aout, "keeping %u channels", channels);
259
260     /* Choose the IEC device for S/PDIF output:
261        if the device is overridden by the user then it will be the one.
262        Otherwise we compute the default device based on the output format. */
263     if (spdif && !strcmp (device, "default"))
264     {
265         unsigned aes3;
266
267         switch (aout->format.i_rate)
268         {
269 #define FS(freq) \
270             case freq: aes3 = IEC958_AES3_CON_FS_ ## freq; break;
271             FS( 44100) /* def. */ FS( 48000) FS( 32000)
272             FS( 22050)            FS( 24000)
273             FS( 88200) FS(768000) FS( 96000)
274             FS(176400)            FS(192000)
275 #undef FS
276             default:
277                 aes3 = IEC958_AES3_CON_FS_NOTID;
278                 break;
279         }
280
281         free (device);
282         if (asprintf (&device,
283                       "iec958:AES0=0x%x,AES1=0x%x,AES2=0x%x,AES3=0x%x",
284                       IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO,
285                       IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
286                       0, aes3) == -1)
287             return VLC_ENOMEM;
288     }
289
290     /* Allocate structures */
291     aout_sys_t *sys = malloc (sizeof (*sys));
292     if (unlikely(sys == NULL))
293     {
294         free (device);
295         return VLC_ENOMEM;
296     }
297     aout->sys = sys;
298
299     /* Open the device */
300     snd_pcm_t *pcm;
301     /* VLC always has a resampler. No need for ALSA's. */
302     const int mode = SND_PCM_NO_AUTO_RESAMPLE;
303
304     int val = snd_pcm_open (&pcm, device, SND_PCM_STREAM_PLAYBACK, mode);
305 #if (SND_LIB_VERSION <= 0x010015)
306 # warning Please update alsa-lib to version > 1.0.21a.
307     var_Create (aout->p_libvlc, "alsa-working", VLC_VAR_BOOL);
308     if (val != 0 && var_GetBool (aout->p_libvlc, "alsa-working"))
309         dialog_Fatal (aout, "ALSA version problem",
310             "VLC failed to re-initialize your audio output device.\n"
311             "Please update alsa-lib to version 1.0.22 or higher "
312             "to fix this issue.");
313     var_SetBool (aout->p_libvlc, "alsa-working", !val);
314 #endif
315     if (val != 0)
316     {
317 #if (SND_LIB_VERSION <= 0x010017)
318 # warning Please update alsa-lib to version > 1.0.23.
319         var_Create (aout->p_libvlc, "alsa-broken", VLC_VAR_BOOL);
320         if (!var_GetBool (aout->p_libvlc, "alsa-broken"))
321         {
322             var_SetBool (aout->p_libvlc, "alsa-broken", true);
323             dialog_Fatal (aout, "Potential ALSA version problem",
324                 "VLC failed to initialize your audio output device (if any).\n"
325                 "Please update alsa-lib to version 1.0.24 or higher "
326                 "to try to fix this issue.");
327         }
328 #endif
329         msg_Err (aout, "cannot open ALSA device \"%s\": %s", device,
330                  snd_strerror (val));
331         dialog_Fatal (aout, _("Audio output failed"),
332                       _("The audio device \"%s\" could not be used:\n%s."),
333                       device, snd_strerror (val));
334         free (device);
335         free (sys);
336         return VLC_EGENERIC;
337     }
338     sys->pcm = pcm;
339
340     /* Print some potentially useful debug */
341     msg_Dbg (aout, "using ALSA device: %s", device);
342     DumpDevice (VLC_OBJECT(aout), pcm);
343
344     /* Get Initial hardware parameters */
345     snd_pcm_hw_params_t *hw;
346     unsigned param;
347
348     snd_pcm_hw_params_alloca (&hw);
349     snd_pcm_hw_params_any (pcm, hw);
350     Dump (aout, "initial hardware setup:\n", snd_pcm_hw_params_dump, hw);
351
352     val = snd_pcm_hw_params_set_rate_resample(pcm, hw, 0);
353     if (val)
354     {
355         msg_Err (aout, "cannot disable resampling: %s", snd_strerror (val));
356         goto error;
357     }
358
359     val = snd_pcm_hw_params_set_access (pcm, hw,
360                                         SND_PCM_ACCESS_RW_INTERLEAVED);
361     if (val)
362     {
363         msg_Err (aout, "cannot set access mode: %s", snd_strerror (val));
364         goto error;
365     }
366
367     /* Set sample format */
368     if (snd_pcm_hw_params_test_format (pcm, hw, pcm_format) == 0)
369         ;
370     else
371     if (snd_pcm_hw_params_test_format (pcm, hw, SND_PCM_FORMAT_FLOAT) == 0)
372     {
373         fourcc = VLC_CODEC_FL32;
374         pcm_format = SND_PCM_FORMAT_FLOAT;
375     }
376     else
377     if (snd_pcm_hw_params_test_format (pcm, hw, SND_PCM_FORMAT_S32) == 0)
378     {
379         fourcc = VLC_CODEC_S32N;
380         pcm_format = SND_PCM_FORMAT_S32;
381     }
382     else
383     if (snd_pcm_hw_params_test_format (pcm, hw, SND_PCM_FORMAT_S16) == 0)
384     {
385         fourcc = VLC_CODEC_S16N;
386         pcm_format = SND_PCM_FORMAT_S16;
387     }
388     else
389     {
390         msg_Err (aout, "no supported sample format");
391         goto error;
392     }
393
394     val = snd_pcm_hw_params_set_format (pcm, hw, pcm_format);
395     if (val)
396     {
397         msg_Err (aout, "cannot set sample format: %s", snd_strerror (val));
398         goto error;
399     }
400
401     /* Set channels count */
402     /* By default, ALSA plug will pad missing channels with zeroes, which is
403      * usually fine. However, it will also discard extraneous channels, which
404      * is not acceptable. Thus the user must configure the physically
405      * available channels, and VLC will downmix if needed. */
406     val = snd_pcm_hw_params_set_channels (pcm, hw, channels);
407     if (val)
408     {
409         msg_Err (aout, "cannot set %u channels: %s", channels,
410                  snd_strerror (val));
411         goto error;
412     }
413
414     /* Set sample rate */
415     unsigned rate = aout->format.i_rate;
416     val = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, NULL);
417     if (val)
418     {
419         msg_Err (aout, "cannot set sample rate: %s", snd_strerror (val));
420         goto error;
421     }
422     if (aout->format.i_rate != rate)
423         msg_Dbg (aout, "resampling from %d Hz to %d Hz",
424                  aout->format.i_rate, rate);
425
426     /* Set buffer size */
427     param = AOUT_MAX_ADVANCE_TIME;
428     val = snd_pcm_hw_params_set_buffer_time_near (pcm, hw, &param, NULL);
429     if (val)
430     {
431         msg_Err (aout, "cannot set buffer duration: %s", snd_strerror (val));
432         goto error;
433     }
434 #if 0
435     val = snd_pcm_hw_params_get_buffer_time (hw, &param, NULL);
436     if (val)
437     {
438         msg_Warn (aout, "cannot get buffer time: %s", snd_strerror(val));
439         param = AOUT_MIN_PREPARE_TIME;
440     }
441     else
442         param /= 2;
443 #else /* work-around for period-long latency outputs (e.g. PulseAudio): */
444     param = AOUT_MIN_PREPARE_TIME;
445 #endif
446     val = snd_pcm_hw_params_set_period_time_near (pcm, hw, &param, NULL);
447     if (val)
448     {
449         msg_Err (aout, "cannot set period: %s", snd_strerror (val));
450         goto error;
451     }
452
453     /* Commit hardware parameters */
454     val = snd_pcm_hw_params (pcm, hw);
455     if (val < 0)
456     {
457         msg_Err (aout, "cannot commit hardware parameters: %s",
458                  snd_strerror (val));
459         goto error;
460     }
461     Dump (aout, "final HW setup:\n", snd_pcm_hw_params_dump, hw);
462
463     /* Get Initial software parameters */
464     snd_pcm_sw_params_t *sw;
465
466     snd_pcm_sw_params_alloca (&sw);
467     snd_pcm_sw_params_current (pcm, sw);
468     Dump (aout, "initial software parameters:\n", snd_pcm_sw_params_dump, sw);
469
470     /* START REVISIT */
471     //snd_pcm_sw_params_set_avail_min( pcm, sw, i_period_size );
472     // FIXME: useful?
473     val = snd_pcm_sw_params_set_start_threshold (pcm, sw, 1);
474     if( val < 0 )
475     {
476         msg_Err( aout, "unable to set start threshold (%s)",
477                  snd_strerror( val ) );
478         goto error;
479     }
480     /* END REVISIT */
481
482     /* Commit software parameters. */
483     val = snd_pcm_sw_params (pcm, sw);
484     if (val)
485     {
486         msg_Err (aout, "cannot commit software parameters: %s",
487                  snd_strerror (val));
488         goto error;
489     }
490     Dump (aout, "final software parameters:\n", snd_pcm_sw_params_dump, sw);
491
492     val = snd_pcm_prepare (pcm);
493     if (val)
494     {
495         msg_Err (aout, "cannot prepare device: %s", snd_strerror (val));
496         goto error;
497     }
498
499     /* Setup audio_output_t */
500     aout->format.i_format = fourcc;
501     aout->format.i_rate = rate;
502     sys->reorder = NULL;
503     if (spdif)
504     {
505         aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE;
506         aout->format.i_frame_length = A52_FRAME_NB;
507         aout->volume_set = NULL;
508         aout->mute_set = NULL;
509     }
510     else
511     {
512         aout->format.i_original_channels =
513         aout->format.i_physical_channels = map;
514         switch (popcount (map))
515         {
516             case 8:
517                 sys->reorder = Reorder71;
518                 break;
519         }
520
521         aout_SoftVolumeInit (aout);
522     }
523
524     aout->pf_play = Play;
525     if (snd_pcm_hw_params_can_pause (hw))
526         aout->pf_pause = Pause;
527     else
528     {
529         aout->pf_pause = PauseDummy;
530         msg_Warn (aout, "device cannot be paused");
531     }
532     aout->pf_flush = Flush;
533
534     /* Setup audio-device choices */
535     {
536         vlc_value_t text;
537
538         var_Create (obj, "audio-device", VLC_VAR_STRING | VLC_VAR_HASCHOICE);
539         text.psz_string = _("Audio Device");
540         var_Change (obj, "audio-device", VLC_VAR_SETTEXT, &text, NULL);
541
542         GetDevices (obj, device);
543     }
544     var_AddCallback (obj, "audio-device", DeviceChanged, NULL);
545
546     free (device);
547     return 0;
548
549 error:
550     snd_pcm_close (pcm);
551     free (device);
552     free (sys);
553     return VLC_EGENERIC;
554 }
555
556 /**
557  * Queues one audio buffer to the hardware.
558  */
559 static void Play (audio_output_t *aout, block_t *block,
560                   mtime_t *restrict drift)
561 {
562     aout_sys_t *sys = aout->sys;
563
564     if (sys->reorder != NULL)
565         sys->reorder (block->p_buffer, block->i_nb_samples,
566                       aout->format.i_bitspersample / 8);
567
568     snd_pcm_t *pcm = sys->pcm;
569     snd_pcm_sframes_t frames;
570     snd_pcm_state_t state = snd_pcm_state (pcm);
571
572     if (snd_pcm_delay (pcm, &frames) == 0)
573     {
574         mtime_t delay = frames * CLOCK_FREQ / aout->format.i_rate;
575         delay += mdate () - block->i_pts;
576
577         if (state != SND_PCM_STATE_RUNNING)
578         {
579             if (delay < 0)
580             {
581                 if (aout->format.i_format != VLC_CODEC_SPDIFL)
582                 {
583                     frames = (delay * aout->format.i_rate) / -CLOCK_FREQ;
584                     msg_Dbg (aout, "prepending %ld zeroes", frames);
585
586                     void *z = calloc (frames, aout->format.i_bytes_per_frame);
587                     if (likely(z != NULL))
588                     {
589                         snd_pcm_writei (pcm, z, frames);
590                         free (z);
591                         delay = 0;
592                     }
593                 }
594                 /* Lame fallback if zero padding does not work */
595                 if (delay < 0)
596                 {
597                     msg_Dbg (aout, "deferring start (%"PRId64" us)", -delay);
598                     msleep (-delay);
599                 }
600             }
601             else
602                 msg_Dbg (aout, "starting late (%"PRId64" us)", delay);
603         }
604         else
605             *drift = delay;
606     }
607
608     /* TODO: better overflow handling */
609     /* TODO: no period wake ups */
610
611     while (block->i_nb_samples > 0)
612     {
613         frames = snd_pcm_writei (pcm, block->p_buffer, block->i_nb_samples);
614         if (frames >= 0)
615         {
616             size_t bytes = snd_pcm_frames_to_bytes (pcm, frames);
617             block->i_nb_samples -= frames;
618             block->p_buffer += bytes;
619             block->i_buffer -= bytes;
620             // pts, length
621         }
622         else  
623         {
624             int val = snd_pcm_recover (pcm, frames, 1);
625             if (val)
626             {
627                 msg_Err (aout, "cannot recover playback stream: %s",
628                          snd_strerror (val));
629                 DumpDeviceStatus (aout, pcm);
630                 break;
631             }
632             msg_Warn (aout, "cannot write samples: %s", snd_strerror (frames));
633         }
634     }
635     block_Release (block);
636 }
637
638 /**
639  * Pauses/resumes the audio playback.
640  */
641 static void Pause (audio_output_t *aout, bool pause, mtime_t date)
642 {
643     snd_pcm_t *pcm = aout->sys->pcm;
644
645     int val = snd_pcm_pause (pcm, pause);
646     if (unlikely(val))
647         PauseDummy (aout, pause, date);
648 }
649
650 static void PauseDummy (audio_output_t *aout, bool pause, mtime_t date)
651 {
652     snd_pcm_t *pcm = aout->sys->pcm;
653
654     /* Stupid device cannot pause. Discard samples. */
655     if (pause)
656         snd_pcm_drop (pcm);
657     else
658         snd_pcm_prepare (pcm);
659     (void) date;
660 }
661
662 /**
663  * Flushes/drains the audio playback buffer.
664  */
665 static void Flush (audio_output_t *aout, bool wait)
666 {
667     snd_pcm_t *pcm = aout->sys->pcm;
668
669     if (wait)
670         snd_pcm_drain (pcm);
671     else
672         snd_pcm_drop (pcm);
673     snd_pcm_prepare (pcm);
674 }
675
676
677 /**
678  * Releases the audio output.
679  */
680 static void Close (vlc_object_t *obj)
681 {
682     audio_output_t *aout = (audio_output_t *)obj;
683     aout_sys_t *sys = aout->sys;
684     snd_pcm_t *pcm = aout->sys->pcm;
685
686     var_DelCallback (obj, "audio-device", DeviceChanged, NULL);
687     var_Destroy (obj, "audio-device");
688
689     snd_pcm_drop (pcm);
690     snd_pcm_close (pcm);
691     free (sys);
692 }
693
694 /**
695  * Converts from VLC to ALSA order for 7.1.
696  * VLC has middle channels in position 2 and 3, ALSA in position 6 and 7.
697  */
698 static void Reorder71 (void *p, size_t n, unsigned size)
699 {
700     switch (size)
701     {
702         case 4:
703             for (uint64_t *ptr = p; n > 0; ptr += 4, n--)
704             {
705                 uint64_t middle = ptr[1], c_lfe = ptr[2], rear = ptr[3];
706                 ptr[1] = c_lfe; ptr[2] = rear; ptr[3] = middle;
707             }
708             break;
709         case 2:
710             for (uint32_t *ptr = p; n > 0; ptr += 4, n--)
711             {
712                 uint32_t middle = ptr[1], c_lfe = ptr[2], rear = ptr[3];
713                 ptr[1] = c_lfe; ptr[2] = rear; ptr[3] = middle;
714             }
715             break;
716
717         default:
718             for (uint16_t *ptr = p; n > 0; n--)
719             {
720                 uint16_t middle[size];
721                 memcpy (middle, ptr + size, size * 2);
722                 ptr += size;
723                 memcpy (ptr, ptr + size, size * 2);
724                 ptr += size;
725                 memcpy (ptr, ptr + size, size * 2);
726                 ptr += size;
727                 memcpy (ptr, middle, size * 2);
728                 ptr += size;
729             }
730             break;
731     }
732 }
733
734
735 /**
736  * Enumerates ALSA output devices.
737  */
738 static int EnumDevices(vlc_object_t *obj, char const *varname,
739                        char ***restrict vp, char ***restrict tp)
740 {
741     unsigned n = 0;
742
743     char **names = xmalloc(sizeof (*names));
744     char **descs = xmalloc(sizeof (*names));
745     names[0] = strdup ("default");
746     descs[0] = strdup (N_("Default"));
747     n++;
748     if (unlikely(names[0] == NULL || descs[0] == NULL))
749         abort();
750
751     void **hints;
752     if (snd_device_name_hint(-1, "pcm", &hints) < 0)
753         return n;
754
755     for (size_t i = 0; hints[i] != NULL; i++)
756     {
757         void *hint = hints[i];
758
759         char *name = snd_device_name_get_hint(hint, "NAME");
760         if (unlikely(name == NULL))
761             continue;
762
763         char *desc = snd_device_name_get_hint(hint, "DESC");
764         if (desc == NULL)
765         {
766             free (name);
767             continue;
768         }
769
770         names = xrealloc (names, (n + 1) * sizeof (*names));
771         descs = xrealloc (descs, (n + 1) * sizeof (*descs));
772         names[n] = name;
773         descs[n] = desc;
774         n++;
775     }
776     snd_device_name_free_hint(hints);
777
778     (void) obj; (void) varname;
779     *vp = names;
780     *tp = descs;
781     return n;
782 }
783
784
785 static void GetDevices(vlc_object_t *obj, const char *prefs_dev)
786 {
787     void **hints;
788
789     msg_Dbg(obj, "Available ALSA PCM devices:");
790     if (snd_device_name_hint(-1, "pcm", &hints) < 0)
791         return;
792
793     vlc_value_t val, text;
794     bool hinted_default = false;
795     bool hinted_prefs = !strcmp (prefs_dev, "default");
796
797     for (size_t i = 0; hints[i] != NULL; i++)
798     {
799         void *hint = hints[i];
800
801         char *name = snd_device_name_get_hint(hint, "NAME");
802         if (unlikely(name == NULL))
803             continue;
804
805         char *desc = snd_device_name_get_hint(hint, "DESC");
806         if (desc != NULL)
807             for (char *lf = strchr(desc, '\n'); lf; lf = strchr(lf, '\n'))
808                  *lf = ' ';
809         msg_Dbg(obj, "%s (%s)", (desc != NULL) ? desc : name, name);
810
811         if (!strcmp (name, "default"))
812             hinted_default = true;
813         if (!strcmp (name, prefs_dev))
814             hinted_prefs = true;
815
816         val.psz_string = name;
817         text.psz_string = desc;
818         var_Change(obj, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
819         free(desc);
820         free(name);
821     }
822
823     snd_device_name_free_hint(hints);
824
825     if (!hinted_default)
826     {
827         val.psz_string = (char *)"default";
828         text.psz_string = (char *)N_("Default");
829         var_Change(obj, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
830     }
831
832     val.psz_string = (char *)prefs_dev;
833     if (!hinted_prefs)
834     {
835         text.psz_string = (char *)N_("VLC preferences");
836         var_Change(obj, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
837     }
838     var_Change(obj, "audio-device", VLC_VAR_SETVALUE, &val, NULL);
839 }