]> git.sesse.net Git - vlc/blob - modules/audio_output/alsa.c
coreaudio_lag_fix.patch
[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     /* VLC is currently unable to leverage ALSA softvol. No need for it. */
327                    | SND_PCM_NO_SOFTVOL;
328
329     int val = snd_pcm_open (&pcm, device, SND_PCM_STREAM_PLAYBACK, mode);
330 #if (SND_LIB_VERSION <= 0x010015)
331 # warning Please update alsa-lib to version > 1.0.21a.
332     var_Create (aout->p_libvlc, "alsa-working", VLC_VAR_BOOL);
333     if (val != 0 && var_GetBool (aout->p_libvlc, "alsa-working"))
334         dialog_Fatal (aout, "ALSA version problem",
335             "VLC failed to re-initialize your audio output device.\n"
336             "Please update alsa-lib to version 1.0.22 or higher "
337             "to fix this issue.");
338     var_SetBool (aout->p_libvlc, "alsa-working", !val);
339 #endif
340     if (val != 0)
341     {
342 #if (SND_LIB_VERSION <= 0x010017)
343 # warning Please update alsa-lib to version > 1.0.23.
344         var_Create (aout->p_libvlc, "alsa-broken", VLC_VAR_BOOL);
345         if (!var_GetBool (aout->p_libvlc, "alsa-broken"))
346         {
347             var_SetBool (aout->p_libvlc, "alsa-broken", true);
348             dialog_Fatal (aout, "Potential ALSA version problem",
349                 "VLC failed to initialize your audio output device (if any).\n"
350                 "Please update alsa-lib to version 1.0.24 or higher "
351                 "to try to fix this issue.");
352         }
353 #endif
354         msg_Err (aout, "cannot open ALSA device \"%s\": %s", device,
355                  snd_strerror (val));
356         dialog_Fatal (aout, _("Audio output failed"),
357                       _("The audio device \"%s\" could not be used:\n%s."),
358                       device, snd_strerror (val));
359         free (device);
360         free (sys);
361         return VLC_EGENERIC;
362     }
363     sys->pcm = pcm;
364
365     /* Print some potentially useful debug */
366     msg_Dbg (aout, "using ALSA device: %s", device);
367     DumpDevice (VLC_OBJECT(aout), pcm);
368
369     /* Get Initial hardware parameters */
370     snd_pcm_hw_params_t *hw;
371     unsigned param;
372
373     snd_pcm_hw_params_alloca (&hw);
374     snd_pcm_hw_params_any (pcm, hw);
375     Dump (aout, "initial hardware setup:\n", snd_pcm_hw_params_dump, hw);
376
377     /* Set sample format */
378     val = snd_pcm_hw_params_set_format (pcm, hw, pcm_format);
379     if (val == 0)
380         ;
381     else if (pcm_format != SND_PCM_FORMAT_FLOAT
382      && snd_pcm_hw_params_set_format (pcm, hw, SND_PCM_FORMAT_FLOAT) == 0)
383         fourcc = VLC_CODEC_FL32;
384     else if (pcm_format != SND_PCM_FORMAT_S32
385      && snd_pcm_hw_params_set_format (pcm, hw, SND_PCM_FORMAT_S32) == 0)
386         fourcc = VLC_CODEC_S32N;
387     else if (pcm_format != SND_PCM_FORMAT_S16
388      && snd_pcm_hw_params_set_format (pcm, hw, SND_PCM_FORMAT_S16) == 0)
389         fourcc = VLC_CODEC_S16N;
390     else
391     {
392         msg_Err (aout, "cannot set sample format: %s", snd_strerror (val));
393         goto error;
394     }
395
396     val = snd_pcm_hw_params_set_access (pcm, hw,
397                                         SND_PCM_ACCESS_RW_INTERLEAVED);
398     if (val)
399     {
400         msg_Err (aout, "cannot set access mode: %s", snd_strerror (val));
401         goto error;
402     }
403
404     /* Set channels count */
405     /* By default, ALSA plug will pad missing channels with zeroes, which is
406      * usually fine. However, it will also discard extraneous channels, which
407      * is not acceptable. Thus the user must configure the physically
408      * available channels, and VLC will downmix if needed. */
409     val = snd_pcm_hw_params_set_channels (pcm, hw, channels);
410     if (val)
411     {
412         msg_Err (aout, "cannot set %u channels: %s", channels,
413                  snd_strerror (val));
414         goto error;
415     }
416
417     /* Set sample rate */
418     unsigned rate = aout->format.i_rate;
419     val = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, NULL);
420     if (val)
421     {
422         msg_Err (aout, "cannot set sample rate: %s", snd_strerror (val));
423         goto error;
424     }
425     if (aout->format.i_rate != rate)
426         msg_Dbg (aout, "resampling from %d Hz to %d Hz",
427                  aout->format.i_rate, rate);
428
429     /* Set buffer size */
430     param = AOUT_MAX_ADVANCE_TIME;
431     val = snd_pcm_hw_params_set_buffer_time_near (pcm, hw, &param, NULL);
432     if (val)
433         msg_Warn (aout, "cannot set buffer duration near %u us: %s",
434                   param, snd_strerror (val));
435     val = snd_pcm_hw_params_set_buffer_time_last (pcm, hw, &param, NULL);
436     if (val)
437         msg_Warn (aout, "cannot set buffer duration: %s", snd_strerror (val));
438
439     /* Set number of periods (at least two) */
440     param = 2;
441     val = snd_pcm_hw_params_set_periods_min (pcm, hw, &param, NULL);
442     if (val)
443         msg_Warn (aout, "cannot set minimum of %u periods: %s", param,
444                   snd_strerror (val));
445     val = snd_pcm_hw_params_set_periods_first (pcm, hw, &param, NULL);
446     if (val)
447         msg_Warn (aout, "cannot set periods count near %u: %s", param,
448                   snd_strerror (val));
449
450     /* Commit hardware parameters */
451     val = snd_pcm_hw_params (pcm, hw);
452     if (val < 0)
453     {
454         msg_Err (aout, "cannot commit hardware parameters: %s",
455                  snd_strerror (val));
456         goto error;
457     }
458     Dump (aout, "final HW setup:\n", snd_pcm_hw_params_dump, hw);
459
460     /* Get Initial software parameters */
461     snd_pcm_sw_params_t *sw;
462
463     snd_pcm_sw_params_alloca (&sw);
464     snd_pcm_sw_params_current (pcm, sw);
465     Dump (aout, "initial software parameters:\n", snd_pcm_sw_params_dump, sw);
466
467     /* START REVISIT */
468     //snd_pcm_sw_params_set_avail_min( pcm, sw, i_period_size );
469     // FIXME: useful?
470     val = snd_pcm_sw_params_set_start_threshold (pcm, sw, 1);
471     if( val < 0 )
472     {
473         msg_Err( aout, "unable to set start threshold (%s)",
474                  snd_strerror( val ) );
475         goto error;
476     }
477     /* END REVISIT */
478
479     /* Commit software parameters. */
480     val = snd_pcm_sw_params (pcm, sw);
481     if (val)
482     {
483         msg_Err (aout, "cannot commit software parameters: %s",
484                  snd_strerror (val));
485         goto error;
486     }
487     Dump (aout, "final software parameters:\n", snd_pcm_sw_params_dump, sw);
488
489     val = snd_pcm_prepare (pcm);
490     if (val)
491     {
492         msg_Err (aout, "cannot prepare device: %s", snd_strerror (val));
493         goto error;
494     }
495
496     /* Setup audio_output_t */
497     aout->format.i_format = fourcc;
498     aout->format.i_rate = rate;
499     if (spdif)
500     {
501         aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE;
502         aout->format.i_frame_length = A52_FRAME_NB;
503         aout_VolumeNoneInit (aout);
504     }
505     else
506     {
507         aout->format.i_original_channels =
508         aout->format.i_physical_channels = map;
509         switch (popcount (map))
510         {
511             case 8:
512                 sys->reorder = Reorder71;
513                 break;
514             default:
515                 sys->reorder = NULL;
516         }
517
518         aout_VolumeSoftInit (aout);
519     }
520
521     aout->pf_play = Play;
522     if (snd_pcm_hw_params_can_pause (hw))
523         aout->pf_pause = Pause;
524     else
525     {
526         aout->pf_pause = PauseDummy;
527         msg_Warn (aout, "device cannot be paused");
528     }
529     aout->pf_flush = Flush;
530
531     Probe (obj, device);
532     free (device);
533     return 0;
534
535 error:
536     snd_pcm_close (pcm);
537     free (device);
538     free (sys);
539     return VLC_EGENERIC;
540 }
541
542 /**
543  * Queues one audio buffer to the hardware.
544  */
545 static void Play (audio_output_t *aout, block_t *block)
546 {
547     aout_sys_t *sys = aout->sys;
548
549     if (sys->reorder != NULL)
550         sys->reorder (block->p_buffer, block->i_nb_samples,
551                       aout->format.i_bitspersample / 8);
552
553     snd_pcm_t *pcm = sys->pcm;
554     snd_pcm_sframes_t frames;
555     snd_pcm_state_t state = snd_pcm_state (pcm);
556
557     if (snd_pcm_delay (pcm, &frames) == 0)
558     {
559         mtime_t delay = frames * CLOCK_FREQ / aout->format.i_rate;
560
561         if (state != SND_PCM_STATE_RUNNING)
562         {
563             delay = block->i_pts - (mdate () + delay);
564             if (delay > 0 && aout->format.i_format != VLC_CODEC_SPDIFL)
565             {
566                 frames = (delay * aout->format.i_rate) / CLOCK_FREQ;
567                 msg_Dbg (aout, "prepending %ld zeroes", frames);
568
569                 void *pad = calloc (frames, aout->format.i_bytes_per_frame);
570                 if (likely(pad != NULL))
571                 {
572                     snd_pcm_writei (pcm, pad, frames);
573                     free (pad);
574                     delay = 0;
575                 }
576             }
577             /* Lame fallback if zero padding does not work */
578             if (delay > 0)
579                 mwait (block->i_pts - delay);
580         }
581         else
582             aout_TimeReport (aout, block->i_pts - delay);
583     }
584
585     /* TODO: better overflow handling */
586     /* TODO: no period wake ups */
587
588     while (block->i_nb_samples > 0)
589     {
590         frames = snd_pcm_writei (pcm, block->p_buffer, block->i_nb_samples);
591         if (frames >= 0)
592         {
593             size_t bytes = snd_pcm_frames_to_bytes (pcm, frames);
594             block->i_nb_samples -= frames;
595             block->p_buffer += bytes;
596             block->i_buffer -= bytes;
597             // pts, length
598         }
599         else  
600         {
601             int val = snd_pcm_recover (pcm, frames, 1);
602             if (val)
603             {
604                 msg_Err (aout, "cannot recover playback stream: %s",
605                          snd_strerror (val));
606                 DumpDeviceStatus (aout, pcm);
607                 break;
608             }
609             msg_Warn (aout, "cannot write samples: %s", snd_strerror (frames));
610         }
611     }
612     block_Release (block);
613 }
614
615 /**
616  * Pauses/resumes the audio playback.
617  */
618 static void Pause (audio_output_t *aout, bool pause, mtime_t date)
619 {
620     snd_pcm_t *pcm = aout->sys->pcm;
621
622     int val = snd_pcm_pause (pcm, pause);
623     if (unlikely(val))
624         PauseDummy (aout, pause, date);
625 }
626
627 static void PauseDummy (audio_output_t *aout, bool pause, mtime_t date)
628 {
629     snd_pcm_t *pcm = aout->sys->pcm;
630
631     /* Stupid device cannot pause. Discard samples. */
632     if (pause)
633         snd_pcm_drop (pcm);
634     else
635         snd_pcm_prepare (pcm);
636     (void) date;
637 }
638
639 /**
640  * Flushes/drains the audio playback buffer.
641  */
642 static void Flush (audio_output_t *aout, bool wait)
643 {
644     snd_pcm_t *pcm = aout->sys->pcm;
645
646     if (wait)
647         snd_pcm_drain (pcm);
648     else
649         snd_pcm_drop (pcm);
650     snd_pcm_prepare (pcm);
651 }
652
653
654 /**
655  * Releases the audio output.
656  */
657 static void Close (vlc_object_t *obj)
658 {
659     audio_output_t *aout = (audio_output_t *)obj;
660     aout_sys_t *sys = aout->sys;
661     snd_pcm_t *pcm = aout->sys->pcm;
662
663     var_DelCallback (obj, "audio-device", aout_ChannelsRestart, NULL);
664     snd_pcm_drop (pcm);
665     snd_pcm_close (pcm);
666     free (sys);
667 }
668
669 /**
670  * Converts from VLC to ALSA order for 7.1.
671  * VLC has middle channels in position 2 and 3, ALSA in position 6 and 7.
672  */
673 static void Reorder71 (void *p, size_t n, unsigned size)
674 {
675     switch (size)
676     {
677         case 4:
678             for (uint64_t *ptr = p; n > 0; ptr += 4, n--)
679             {
680                 uint64_t middle = ptr[1], c_lfe = ptr[2], rear = ptr[3];
681                 ptr[1] = c_lfe; ptr[2] = rear; ptr[3] = middle;
682             }
683             break;
684         case 2:
685             for (uint32_t *ptr = p; n > 0; ptr += 4, n--)
686             {
687                 uint32_t middle = ptr[1], c_lfe = ptr[2], rear = ptr[3];
688                 ptr[1] = c_lfe; ptr[2] = rear; ptr[3] = middle;
689             }
690             break;
691
692         default:
693             for (uint16_t *ptr = p; n > 0; n--)
694             {
695                 uint16_t middle[size];
696                 memcpy (middle, ptr + size, size * 2);
697                 ptr += size;
698                 memcpy (ptr, ptr + size, size * 2);
699                 ptr += size;
700                 memcpy (ptr, ptr + size, size * 2);
701                 ptr += size;
702                 memcpy (ptr, middle, size * 2);
703                 ptr += size;
704             }
705             break;
706     }
707 }
708
709
710 /*****************************************************************************
711  * config variable callback
712  *****************************************************************************/
713 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
714                                vlc_value_t newval, vlc_value_t oldval, void *p_unused )
715 {
716     module_config_t *p_item;
717     (void)newval;
718     (void)oldval;
719     (void)p_unused;
720
721     p_item = config_FindConfig( p_this, psz_name );
722     if( !p_item ) return VLC_SUCCESS;
723
724     /* Clear-up the current list */
725     if( p_item->i_list )
726     {
727         int i;
728
729         /* Keep the first entrie */
730         for( i = 1; i < p_item->i_list; i++ )
731         {
732             free( (char *)p_item->ppsz_list[i] );
733             free( (char *)p_item->ppsz_list_text[i] );
734         }
735         /* TODO: Remove when no more needed */
736         p_item->ppsz_list[i] = NULL;
737         p_item->ppsz_list_text[i] = NULL;
738     }
739     p_item->i_list = 1;
740
741     GetDevices (p_this, p_item, "default");
742
743     /* Signal change to the interface */
744     p_item->b_dirty = true;
745
746     return VLC_SUCCESS;
747 }
748
749
750 static void GetDevices (vlc_object_t *obj, module_config_t *item,
751                         const char *prefs_dev)
752 {
753     void **hints;
754     bool hinted_default = false;
755     bool hinted_prefs = !strcmp (prefs_dev, "default");
756
757     msg_Dbg(obj, "Available ALSA PCM devices:");
758
759     if (snd_device_name_hint(-1, "pcm", &hints) < 0)
760         return;
761
762     for (size_t i = 0; hints[i] != NULL; i++)
763     {
764         void *hint = hints[i];
765
766         char *name = snd_device_name_get_hint(hint, "NAME");
767         if (unlikely(name == NULL))
768             continue;
769
770         char *desc = snd_device_name_get_hint(hint, "DESC");
771         if (desc != NULL)
772             for (char *lf = strchr(desc, '\n'); lf; lf = strchr(lf, '\n'))
773                  *lf = ' ';
774         msg_Dbg(obj, "%s (%s)", (desc != NULL) ? desc : name, name);
775
776         if (!strcmp (name, "default"))
777             hinted_default = true;
778         if (!strcmp (name, prefs_dev))
779             hinted_prefs = true;
780
781         if (item != NULL)
782         {
783             item->ppsz_list = xrealloc(item->ppsz_list,
784                                        (item->i_list + 2) * sizeof(char *));
785             item->ppsz_list_text = xrealloc(item->ppsz_list_text,
786                                           (item->i_list + 2) * sizeof(char *));
787             item->ppsz_list[item->i_list] = name;
788             if (desc == NULL)
789                 desc = strdup(name);
790             item->ppsz_list_text[item->i_list] = desc;
791             item->i_list++;
792         }
793         else
794         {
795             vlc_value_t val, text;
796
797             val.psz_string = name;
798             text.psz_string = desc;
799             var_Change(obj, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
800             free(desc);
801             free(name);
802         }
803     }
804
805     snd_device_name_free_hint(hints);
806
807     if (item != NULL)
808     {
809         item->ppsz_list[item->i_list] = NULL;
810         item->ppsz_list_text[item->i_list] = NULL;
811     }
812     else
813     {
814         vlc_value_t val, text;
815
816         if (!hinted_default)
817         {
818             val.psz_string = (char *)"default";
819             text.psz_string = (char *)N_("Default");
820             var_Change(obj, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
821         }
822
823         val.psz_string = (char *)prefs_dev;
824         if (!hinted_prefs)
825         {
826             text.psz_string = (char *)N_("VLC preferences");
827             var_Change(obj, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
828         }
829         var_Change(obj, "audio-device", VLC_VAR_SETVALUE, &val, NULL);
830     }
831 }