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