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