]> git.sesse.net Git - vlc/blob - modules/audio_output/alsa.c
Improve error message
[vlc] / modules / audio_output / alsa.c
1 /*****************************************************************************
2  * alsa.c : alsa plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2001 the VideoLAN team
5  * $Id$
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 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36
37 #include <errno.h>                                                 /* ENOMEM */
38 #include <vlc_dialog.h>
39
40 #include <vlc_aout.h>
41 #include <vlc_cpu.h>
42
43 /* ALSA part
44    Note: we use the new API which is available since 0.9.0beta10a. */
45 #define ALSA_PCM_NEW_HW_PARAMS_API
46 #define ALSA_PCM_NEW_SW_PARAMS_API
47 #include <alsa/asoundlib.h>
48 #include <alsa/version.h>
49
50 /*#define ALSA_DEBUG*/
51
52 /*****************************************************************************
53  * aout_sys_t: ALSA audio output method descriptor
54  *****************************************************************************
55  * This structure is part of the audio output thread descriptor.
56  * It describes the ALSA specific properties of an audio device.
57  *****************************************************************************/
58 struct aout_sys_t
59 {
60     snd_pcm_t         * p_snd_pcm;
61     unsigned int                 i_period_time;
62
63 #ifdef ALSA_DEBUG
64     snd_output_t      * p_snd_stderr;
65 #endif
66
67     mtime_t      start_date;
68     vlc_thread_t thread;
69     vlc_sem_t    wait;
70 };
71
72 #define A52_FRAME_NB 1536
73
74 /* These values are in frames.
75    To convert them to a number of bytes you have to multiply them by the
76    number of channel(s) (eg. 2 for stereo) and the size of a sample (eg.
77    2 for int16_t). */
78 #define ALSA_DEFAULT_PERIOD_SIZE        1024
79 #define ALSA_DEFAULT_BUFFER_SIZE        ( ALSA_DEFAULT_PERIOD_SIZE << 8 )
80 #define ALSA_SPDIF_PERIOD_SIZE          A52_FRAME_NB
81 #define ALSA_SPDIF_BUFFER_SIZE          ( ALSA_SPDIF_PERIOD_SIZE << 4 )
82 /* Why << 4 ? --Meuuh */
83 /* Why not ? --Bozo */
84 /* Right. --Meuuh */
85
86 #define DEFAULT_ALSA_DEVICE N_("default")
87
88 /*****************************************************************************
89  * Local prototypes
90  *****************************************************************************/
91 static int   Open         ( vlc_object_t * );
92 static void  Close        ( vlc_object_t * );
93 static void  Play         ( aout_instance_t * );
94 static void* ALSAThread   ( void * );
95 static void  ALSAFill     ( aout_instance_t * );
96 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
97                                 vlc_value_t newval, vlc_value_t oldval, void *p_unused );
98
99 /*****************************************************************************
100  * Module descriptor
101  *****************************************************************************/
102 static const char *const ppsz_devices[] = { "default" };
103 static const char *const ppsz_devices_text[] = { N_("Default") };
104 vlc_module_begin ()
105     set_shortname( "ALSA" )
106     set_description( N_("ALSA audio output") )
107     set_category( CAT_AUDIO )
108     set_subcategory( SUBCAT_AUDIO_AOUT )
109     add_string( "alsa-audio-device", DEFAULT_ALSA_DEVICE, aout_FindAndRestart,
110                 N_("ALSA Device Name"), NULL, false )
111         add_deprecated_alias( "alsadev" )   /* deprecated since 0.9.3 */
112         change_string_list( ppsz_devices, ppsz_devices_text, FindDevicesCallback )
113         change_action_add( FindDevicesCallback, N_("Refresh list") )
114
115     set_capability( "audio output", 150 )
116     set_callbacks( Open, Close )
117 vlc_module_end ()
118
119 /*****************************************************************************
120  * Probe: probe the audio device for available formats and channels
121  *****************************************************************************/
122 static void Probe( aout_instance_t * p_aout,
123                    const char * psz_device, const char * psz_iec_device,
124                    int *pi_snd_pcm_format )
125 {
126     struct aout_sys_t * p_sys = p_aout->output.p_sys;
127     vlc_value_t val, text;
128     int i_ret;
129
130     var_Create ( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
131     text.psz_string = _("Audio Device");
132     var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
133
134     /* We'll open the audio device in non blocking mode so we can just exit
135      * when it is already in use, but for the real stuff we'll still use
136      * the blocking mode */
137
138     /* Now test linear PCM capabilities */
139     if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
140                                  SND_PCM_STREAM_PLAYBACK,
141                                  SND_PCM_NONBLOCK ) ) )
142     {
143         int i_channels;
144         snd_pcm_hw_params_t * p_hw;
145         snd_pcm_hw_params_alloca (&p_hw);
146
147         if ( snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) < 0 )
148         {
149             msg_Warn( p_aout, "unable to retrieve initial hardware parameters"
150                               ", disabling linear PCM audio" );
151             snd_pcm_close( p_sys->p_snd_pcm );
152             var_Destroy( p_aout, "audio-device" );
153             return;
154         }
155
156         if ( snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
157                                            *pi_snd_pcm_format ) < 0 )
158         {
159             int i_snd_rc = -1;
160
161             if( *pi_snd_pcm_format != SND_PCM_FORMAT_S16 )
162             {
163                 *pi_snd_pcm_format = SND_PCM_FORMAT_S16;
164                 i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
165                                                     p_hw, *pi_snd_pcm_format );
166             }
167             if ( i_snd_rc < 0 )
168             {
169                 msg_Warn( p_aout, "unable to set stream sample size and "
170                           "word order, disabling linear PCM audio" );
171                 snd_pcm_close( p_sys->p_snd_pcm );
172                 var_Destroy( p_aout, "audio-device" );
173                 return;
174             }
175         }
176
177         i_channels = aout_FormatNbChannels( &p_aout->output.output );
178
179         while ( i_channels > 0 )
180         {
181             if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw,
182                                                    i_channels ) )
183             {
184                 switch ( i_channels )
185                 {
186                 case 1:
187                     val.i_int = AOUT_VAR_MONO;
188                     text.psz_string = _("Mono");
189                     var_Change( p_aout, "audio-device",
190                                 VLC_VAR_ADDCHOICE, &val, &text );
191                     break;
192                 case 2:
193                     val.i_int = AOUT_VAR_STEREO;
194                     text.psz_string = _("Stereo");
195                     var_Change( p_aout, "audio-device",
196                                 VLC_VAR_ADDCHOICE, &val, &text );
197                     var_Set( p_aout, "audio-device", val );
198                     break;
199                 case 4:
200                     val.i_int = AOUT_VAR_2F2R;
201                     text.psz_string = _("2 Front 2 Rear");
202                     var_Change( p_aout, "audio-device",
203                                 VLC_VAR_ADDCHOICE, &val, &text );
204                     break;
205                 case 6:
206                     val.i_int = AOUT_VAR_5_1;
207                     text.psz_string = "5.1";
208                     var_Change( p_aout, "audio-device",
209                                 VLC_VAR_ADDCHOICE, &val, &text );
210                     break;
211                 }
212             }
213
214             --i_channels;
215         }
216
217         /* Special case for mono on stereo only boards */
218         i_channels = aout_FormatNbChannels( &p_aout->output.output );
219         var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
220         if( val.i_int <= 0 && i_channels == 1 )
221         {
222             if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw, 2 ))
223             {
224                 val.i_int = AOUT_VAR_STEREO;
225                 text.psz_string = (char*)N_("Stereo");
226                 var_Change( p_aout, "audio-device",
227                             VLC_VAR_ADDCHOICE, &val, &text );
228                 var_Set( p_aout, "audio-device", val );
229             }
230         }
231
232         /* Close the previously opened device */
233         snd_pcm_close( p_sys->p_snd_pcm );
234     }
235     else if ( i_ret == -EBUSY )
236     {
237         msg_Warn( p_aout, "audio device: %s is already in use", psz_device );
238     }
239
240     /* Test for S/PDIF device if needed */
241     if ( psz_iec_device )
242     {
243         /* Opening the device should be enough */
244         if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
245                                      SND_PCM_STREAM_PLAYBACK,
246                                      SND_PCM_NONBLOCK ) ) )
247         {
248             val.i_int = AOUT_VAR_SPDIF;
249             text.psz_string = (char*)N_("A/52 over S/PDIF");
250             var_Change( p_aout, "audio-device",
251                         VLC_VAR_ADDCHOICE, &val, &text );
252             if( config_GetInt( p_aout, "spdif" ) )
253                 var_Set( p_aout, "audio-device", val );
254
255             snd_pcm_close( p_sys->p_snd_pcm );
256         }
257         else if ( i_ret == -EBUSY )
258         {
259             msg_Warn( p_aout, "audio device: %s is already in use",
260                       psz_iec_device );
261         }
262     }
263
264     var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
265 #if (SND_LIB_VERSION <= 0x010015)
266 # warning Please update alsa-lib to version > 1.0.21a.
267     var_Create( p_aout->p_libvlc, "alsa-working", VLC_VAR_BOOL );
268     if( val.i_int <= 0 )
269     {
270         if( var_GetBool( p_aout->p_libvlc, "alsa-working" ) )
271             dialog_FatalWait( p_aout, "ALSA version problem",
272                 "VLC failed to re-initialize your sound output device.\n"
273                 "Please update alsa-lib to version 1.0.22 or higher "
274                 "to fix this issue." );
275     }
276     else
277         var_SetBool( p_aout->p_libvlc, "alsa-working", true );
278 #endif
279     if( val.i_int <= 0 )
280     {
281         /* Probe() has failed. */
282         msg_Dbg( p_aout, "failed to find a usable ALSA configuration" );
283         var_Destroy( p_aout, "audio-device" );
284         return;
285     }
286
287     /* Add final settings to the variable */
288     var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
289     var_SetBool( p_aout, "intf-change", true );
290 }
291
292 /*****************************************************************************
293  * Open: create a handle and open an alsa device
294  *****************************************************************************
295  * This function opens an alsa device, through the alsa API.
296  *
297  * Note: the only heap-allocated string is psz_device. All the other pointers
298  * are references to psz_device or to stack-allocated data.
299  *****************************************************************************/
300 static int Open( vlc_object_t *p_this )
301 {
302     aout_instance_t * p_aout = (aout_instance_t *)p_this;
303     struct aout_sys_t * p_sys;
304     vlc_value_t val;
305
306     char psz_default_iec_device[128]; /* Buffer used to store the default
307                                          S/PDIF device */
308     char * psz_device, * psz_iec_device; /* device names for PCM and S/PDIF
309                                             output */
310
311     int i_vlc_pcm_format; /* Audio format for VLC's data */
312     int i_snd_pcm_format; /* Audio format for ALSA's data */
313
314     snd_pcm_uframes_t i_buffer_size = 0;
315     snd_pcm_uframes_t i_period_size = 0;
316     int i_channels = 0;
317
318     snd_pcm_hw_params_t *p_hw;
319     snd_pcm_sw_params_t *p_sw;
320
321     int i_snd_rc = -1;
322     unsigned int i_old_rate;
323     bool b_retry = true;
324
325     /* Allocate structures */
326     p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
327     if( p_sys == NULL )
328         return VLC_ENOMEM;
329
330     /* Get device name */
331     if( (psz_device = config_GetPsz( p_aout, "alsa-audio-device" )) == NULL )
332     {
333         msg_Err( p_aout, "no audio device given (maybe \"default\" ?)" );
334         dialog_Fatal( p_aout, _("No Audio Device"), "%s",
335                         _("No audio device name was given. You might want to " \
336                           "enter \"default\".") );
337         free( p_sys );
338         return VLC_EGENERIC;
339     }
340
341     /* Choose the IEC device for S/PDIF output:
342        if the device is overriden by the user then it will be the one
343        otherwise we compute the default device based on the output format. */
344     if( AOUT_FMT_NON_LINEAR( &p_aout->output.output )
345         && !strcmp( psz_device, DEFAULT_ALSA_DEVICE ) )
346     {
347         snprintf( psz_default_iec_device, sizeof(psz_default_iec_device),
348                   "iec958:AES0=0x%x,AES1=0x%x,AES2=0x%x,AES3=0x%x",
349                   IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO,
350                   IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
351                   0,
352                   ( p_aout->output.output.i_rate == 48000 ?
353                     IEC958_AES3_CON_FS_48000 :
354                     ( p_aout->output.output.i_rate == 44100 ?
355                       IEC958_AES3_CON_FS_44100 : IEC958_AES3_CON_FS_32000 ) ) );
356         psz_iec_device = psz_default_iec_device;
357     }
358     else if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
359     {
360         psz_iec_device = psz_device;
361     }
362     else
363     {
364         psz_iec_device = NULL;
365     }
366
367     /* Choose the linear PCM format (read the comment above about FPU
368        and float32) */
369     if( HAVE_FPU )
370     {
371         i_vlc_pcm_format = VLC_CODEC_FL32;
372         i_snd_pcm_format = SND_PCM_FORMAT_FLOAT;
373     }
374     else
375     {
376         i_vlc_pcm_format = VLC_CODEC_S16N;
377         i_snd_pcm_format = SND_PCM_FORMAT_S16;
378     }
379
380     /* If the variable doesn't exist then it's the first time we're called
381        and we have to probe the available audio formats and channels */
382     if ( var_Type( p_aout, "audio-device" ) == 0 )
383     {
384         Probe( p_aout, psz_device, psz_iec_device, &i_snd_pcm_format );
385     }
386
387     if ( var_Get( p_aout, "audio-device", &val ) < 0 )
388     {
389         free( p_sys );
390         free( psz_device );
391         return VLC_EGENERIC;
392     }
393
394     p_aout->output.output.i_format = i_vlc_pcm_format;
395     if ( val.i_int == AOUT_VAR_5_1 )
396     {
397         p_aout->output.output.i_physical_channels
398             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
399                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
400                | AOUT_CHAN_LFE;
401         free( psz_device );
402         psz_device = strdup( "surround51" );
403     }
404     else if ( val.i_int == AOUT_VAR_2F2R )
405     {
406         p_aout->output.output.i_physical_channels
407             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
408                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
409         free( psz_device );
410         psz_device = strdup( "surround40" );
411     }
412     else if ( val.i_int == AOUT_VAR_STEREO )
413     {
414         p_aout->output.output.i_physical_channels
415             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
416     }
417     else if ( val.i_int == AOUT_VAR_MONO )
418     {
419         p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
420     }
421     else if( val.i_int != AOUT_VAR_SPDIF )
422     {
423         /* This should not happen ! */
424         msg_Err( p_aout, "internal: can't find audio-device (%i)", val.i_int );
425         free( p_sys );
426         free( psz_device );
427         return VLC_EGENERIC;
428     }
429
430 #ifdef ALSA_DEBUG
431     snd_output_stdio_attach( &p_sys->p_snd_stderr, stderr, 0 );
432 #endif
433
434     /* Open the device */
435     if ( val.i_int == AOUT_VAR_SPDIF )
436     {
437         if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
438                             SND_PCM_STREAM_PLAYBACK, 0 ) ) < 0 )
439         {
440             msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
441                              psz_iec_device, snd_strerror( i_snd_rc ) );
442             dialog_Fatal( p_aout, _("Audio output failed"),
443                             _("VLC could not open the ALSA device \"%s\" (%s)."),
444                             psz_iec_device, snd_strerror( i_snd_rc ) );
445             free( p_sys );
446             free( psz_device );
447             return VLC_EGENERIC;
448         }
449         i_buffer_size = ALSA_SPDIF_BUFFER_SIZE;
450         i_snd_pcm_format = SND_PCM_FORMAT_S16;
451         i_channels = 2;
452
453         i_vlc_pcm_format = VLC_CODEC_SPDIFL;
454         p_aout->output.i_nb_samples = i_period_size = ALSA_SPDIF_PERIOD_SIZE;
455         p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
456         p_aout->output.output.i_frame_length = A52_FRAME_NB;
457
458         aout_VolumeNoneInit( p_aout );
459     }
460     else
461     {
462         int i;
463
464         msg_Dbg( p_aout, "opening ALSA device `%s'", psz_device );
465
466         /* Since it seems snd_pcm_close hasn't really released the device at
467           the time it returns, probe if the device is available in loop for 1s.
468           We cannot use blocking mode since the we would wait indefinitely when
469           switching from a dmx device to surround51. */
470
471         for( i = 10; i >= 0; i-- )
472         {
473             if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
474                    SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK ) ) == -EBUSY )
475             {
476                 if( i ) msleep( 100000 /* 100ms */ );
477                 else
478                 {
479                     msg_Err( p_aout, "audio device: %s is already in use",
480                               psz_device );
481                     dialog_Fatal( p_aout, _("Audio output failed"),
482                                     _("The audio device \"%s\" is already in use."),
483                                     psz_device );
484                 }
485                 continue;
486             }
487             break;
488         }
489         if( i_snd_rc < 0 )
490         {
491             msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
492                              psz_device, snd_strerror( i_snd_rc ) );
493             dialog_Fatal( p_aout, _("Audio output failed"),
494                             _("VLC could not open the ALSA device \"%s\" (%s)."),
495                             psz_device, snd_strerror( i_snd_rc ) );
496             free( p_sys );
497             free( psz_device );
498             return VLC_EGENERIC;
499         }
500
501         /* We want blocking mode */
502         snd_pcm_nonblock( p_sys->p_snd_pcm, 0 );
503
504         i_buffer_size = ALSA_DEFAULT_BUFFER_SIZE;
505         i_channels = aout_FormatNbChannels( &p_aout->output.output );
506
507         p_aout->output.i_nb_samples = i_period_size = ALSA_DEFAULT_PERIOD_SIZE;
508
509         aout_VolumeSoftInit( p_aout );
510     }
511
512     /* Free psz_device so that all the remaining data is stack-allocated */
513     free( psz_device );
514
515     p_aout->output.pf_play = Play;
516
517     snd_pcm_hw_params_alloca(&p_hw);
518     snd_pcm_sw_params_alloca(&p_sw);
519
520     /* Due to some bugs in alsa with some drivers, we need to retry in s16l
521        if snd_pcm_hw_params fails in fl32 */
522     while ( b_retry )
523     {
524         b_retry = false;
525
526         /* Get Initial hardware parameters */
527         if ( ( i_snd_rc = snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) ) < 0 )
528         {
529             msg_Err( p_aout, "unable to retrieve initial hardware parameters (%s)",
530                          snd_strerror( i_snd_rc ) );
531             goto error;
532         }
533
534         /* Set format. */
535         if ( ( i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
536                                                     i_snd_pcm_format ) ) < 0 )
537         {
538             if( i_snd_pcm_format != SND_PCM_FORMAT_S16 )
539             {
540                 i_snd_pcm_format = SND_PCM_FORMAT_S16;
541                 i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
542                                                      p_hw, i_snd_pcm_format );
543             }
544             if ( i_snd_rc < 0 )
545             {
546                 msg_Err( p_aout, "unable to set stream sample size and "
547                      "word order (%s)", snd_strerror( i_snd_rc ) );
548                 goto error;
549             }
550         }
551         if( i_vlc_pcm_format != VLC_CODEC_SPDIFL )
552         switch( i_snd_pcm_format )
553         {
554         case SND_PCM_FORMAT_FLOAT:
555             i_vlc_pcm_format = VLC_CODEC_FL32;
556             break;
557         case SND_PCM_FORMAT_S16:
558             i_vlc_pcm_format = VLC_CODEC_S16N;
559             break;
560         }
561         p_aout->output.output.i_format = i_vlc_pcm_format;
562
563         if ( ( i_snd_rc = snd_pcm_hw_params_set_access( p_sys->p_snd_pcm, p_hw,
564                                     SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 )
565         {
566             msg_Err( p_aout, "unable to set interleaved stream format (%s)",
567                              snd_strerror( i_snd_rc ) );
568             goto error;
569         }
570
571         /* Set channels. */
572         if ( ( i_snd_rc = snd_pcm_hw_params_set_channels( p_sys->p_snd_pcm, p_hw,
573                                                       i_channels ) ) < 0 )
574         {
575             msg_Err( p_aout, "unable to set number of output channels (%s)",
576                              snd_strerror( i_snd_rc ) );
577             goto error;
578         }
579
580         /* Set rate. */
581         i_old_rate = p_aout->output.output.i_rate;
582         i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw,
583                                                 &p_aout->output.output.i_rate,
584                                                 NULL );
585         if( i_snd_rc < 0 || p_aout->output.output.i_rate != i_old_rate )
586         {
587             msg_Warn( p_aout, "The rate %d Hz is not supported by your " \
588                 "hardware. Using %d Hz instead.\n", i_old_rate, \
589                 p_aout->output.output.i_rate );
590         }
591
592         /* Set period size. */
593         if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm,
594                                     p_hw, &i_period_size, NULL ) ) < 0 )
595         {
596             msg_Err( p_aout, "unable to set period size (%s)",
597                          snd_strerror( i_snd_rc ) );
598             goto error;
599         }
600         p_aout->output.i_nb_samples = i_period_size;
601
602 /* Set buffer size. */
603         if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm,
604                                     p_hw, &i_buffer_size ) ) < 0 )
605         {
606             msg_Err( p_aout, "unable to set buffer size (%s)",
607                          snd_strerror( i_snd_rc ) );
608             goto error;
609         }
610
611         /* Commit hardware parameters. */
612         if ( ( i_snd_rc = snd_pcm_hw_params( p_sys->p_snd_pcm, p_hw ) ) < 0 )
613         {
614             if ( b_retry == false &&
615                                 i_snd_pcm_format == SND_PCM_FORMAT_FLOAT)
616             {
617                 b_retry = true;
618                 i_snd_pcm_format = SND_PCM_FORMAT_S16;
619                 p_aout->output.output.i_format = VLC_CODEC_S16N;
620                 msg_Warn( p_aout, "unable to commit hardware configuration "
621                                   "with fl32 samples. Retrying with s16l (%s)",                                     snd_strerror( i_snd_rc ) );
622             }
623             else
624             {
625                 msg_Err( p_aout, "unable to commit hardware configuration (%s)",
626                          snd_strerror( i_snd_rc ) );
627                 goto error;
628             }
629         }
630     }
631
632     if( ( i_snd_rc = snd_pcm_hw_params_get_period_time( p_hw,
633                                     &p_sys->i_period_time, NULL ) ) < 0 )
634     {
635         msg_Err( p_aout, "unable to get period time (%s)",
636                          snd_strerror( i_snd_rc ) );
637         goto error;
638     }
639
640     /* Get Initial software parameters */
641     snd_pcm_sw_params_current( p_sys->p_snd_pcm, p_sw );
642
643     i_snd_rc = snd_pcm_sw_params_set_sleep_min( p_sys->p_snd_pcm, p_sw, 0 );
644
645     i_snd_rc = snd_pcm_sw_params_set_avail_min( p_sys->p_snd_pcm, p_sw,
646                                                 p_aout->output.i_nb_samples );
647     /* start playing when one period has been written */
648     i_snd_rc = snd_pcm_sw_params_set_start_threshold( p_sys->p_snd_pcm, p_sw,
649                                                       ALSA_DEFAULT_PERIOD_SIZE);
650     if( i_snd_rc < 0 )
651     {
652         msg_Err( p_aout, "unable to set start threshold (%s)",
653                           snd_strerror( i_snd_rc ) );
654         goto error;
655     }
656
657     /* Commit software parameters. */
658     if ( snd_pcm_sw_params( p_sys->p_snd_pcm, p_sw ) < 0 )
659     {
660         msg_Err( p_aout, "unable to set software configuration" );
661         goto error;
662     }
663
664 #ifdef ALSA_DEBUG
665     snd_output_printf( p_sys->p_snd_stderr, "\nALSA hardware setup:\n\n" );
666     snd_pcm_dump_hw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
667     snd_output_printf( p_sys->p_snd_stderr, "\nALSA software setup:\n\n" );
668     snd_pcm_dump_sw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
669     snd_output_printf( p_sys->p_snd_stderr, "\n" );
670 #endif
671
672     p_sys->start_date = 0;
673     vlc_sem_init( &p_sys->wait, 0 );
674
675     /* Create ALSA thread and wait for its readiness. */
676     if( vlc_clone( &p_sys->thread, ALSAThread, p_aout,
677                    VLC_THREAD_PRIORITY_OUTPUT ) )
678     {
679         msg_Err( p_aout, "cannot create ALSA thread (%m)" );
680         vlc_sem_destroy( &p_sys->wait );
681         goto error;
682     }
683
684     return 0;
685
686 error:
687     snd_pcm_close( p_sys->p_snd_pcm );
688 #ifdef ALSA_DEBUG
689     snd_output_close( p_sys->p_snd_stderr );
690 #endif
691     free( p_sys );
692     return VLC_EGENERIC;
693 }
694
695 static void PlayIgnore( aout_instance_t *p_aout )
696 {   /* Already playing - nothing to do */
697     (void) p_aout;
698 }
699
700 /*****************************************************************************
701  * Play: start playback
702  *****************************************************************************/
703 static void Play( aout_instance_t *p_aout )
704 {
705     p_aout->output.pf_play = PlayIgnore;
706
707     /* get the playing date of the first aout buffer */
708     p_aout->output.p_sys->start_date =
709         aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
710
711     /* wake up the audio output thread */
712     sem_post( &p_aout->output.p_sys->wait );
713 }
714
715 /*****************************************************************************
716  * Close: close the ALSA device
717  *****************************************************************************/
718 static void Close( vlc_object_t *p_this )
719 {
720     aout_instance_t *p_aout = (aout_instance_t *)p_this;
721     struct aout_sys_t * p_sys = p_aout->output.p_sys;
722     int i_snd_rc;
723
724     /* Make sure that the thread will stop once it is waken up */
725     vlc_cancel( p_sys->thread );
726     vlc_join( p_sys->thread, NULL );
727     vlc_sem_destroy( &p_sys->wait );
728
729     /* */
730     i_snd_rc = snd_pcm_close( p_sys->p_snd_pcm );
731
732     if( i_snd_rc > 0 )
733     {
734         msg_Err( p_aout, "failed closing ALSA device (%s)",
735                          snd_strerror( i_snd_rc ) );
736     }
737
738 #ifdef ALSA_DEBUG
739     snd_output_close( p_sys->p_snd_stderr );
740 #endif
741
742     free( p_sys );
743 }
744
745 static void pcm_drop(void *pcm)
746 {
747     snd_pcm_drop(pcm);
748 }
749
750 /*****************************************************************************
751  * ALSAThread: asynchronous thread used to DMA the data to the device
752  *****************************************************************************/
753 static void* ALSAThread( void *data )
754 {
755     aout_instance_t * p_aout = data;
756     struct aout_sys_t * p_sys = p_aout->output.p_sys;
757
758     /* Wait for the exact time to start playing (avoids resampling) */
759     vlc_sem_wait( &p_sys->wait );
760     mwait( p_sys->start_date - AOUT_PTS_TOLERANCE / 4 );
761
762     vlc_cleanup_push( pcm_drop, p_sys->p_snd_pcm );
763     for(;;)
764         ALSAFill( p_aout );
765
766     assert(0);
767     vlc_cleanup_pop();
768 }
769
770 /*****************************************************************************
771  * ALSAFill: function used to fill the ALSA buffer as much as possible
772  *****************************************************************************/
773 static void ALSAFill( aout_instance_t * p_aout )
774 {
775     struct aout_sys_t * p_sys = p_aout->output.p_sys;
776     snd_pcm_t *p_pcm = p_sys->p_snd_pcm;
777     snd_pcm_status_t * p_status;
778     int i_snd_rc;
779     mtime_t next_date;
780
781     int canc = vlc_savecancel();
782     /* Fill in the buffer until space or audio output buffer shortage */
783
784     /* Get the status */
785     snd_pcm_status_alloca(&p_status);
786     i_snd_rc = snd_pcm_status( p_pcm, p_status );
787     if( i_snd_rc < 0 )
788     {
789         msg_Err( p_aout, "cannot get device status" );
790         goto error;
791     }
792
793     /* Handle buffer underruns and get the status again */
794     if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN )
795     {
796         /* Prepare the device */
797         i_snd_rc = snd_pcm_prepare( p_pcm );
798         if( i_snd_rc )
799         {
800             msg_Err( p_aout, "cannot recover from buffer underrun" );
801             goto error;
802         }
803
804         msg_Dbg( p_aout, "recovered from buffer underrun" );
805
806         /* Get the new status */
807         i_snd_rc = snd_pcm_status( p_pcm, p_status );
808         if( i_snd_rc < 0 )
809         {
810             msg_Err( p_aout, "cannot get device status after recovery" );
811             goto error;
812         }
813
814         /* Underrun, try to recover as quickly as possible */
815         next_date = mdate();
816     }
817     else
818     {
819         /* Here the device should be in RUNNING state, p_status is valid. */
820         snd_pcm_sframes_t delay = snd_pcm_status_get_delay( p_status );
821         if( delay == 0 ) /* workaround buggy alsa drivers */
822             if( snd_pcm_delay( p_pcm, &delay ) < 0 )
823                 delay = 0; /* FIXME: use a positive minimal delay */
824
825         size_t i_bytes = snd_pcm_frames_to_bytes( p_pcm, delay );
826         mtime_t delay_us = CLOCK_FREQ * i_bytes
827                 / p_aout->output.output.i_bytes_per_frame
828                 / p_aout->output.output.i_rate
829                 * p_aout->output.output.i_frame_length;
830
831 #ifdef ALSA_DEBUG
832         snd_pcm_state_t state = snd_pcm_status_get_state( p_status );
833         if( state != SND_PCM_STATE_RUNNING )
834             msg_Err( p_aout, "pcm status (%d) != RUNNING", state );
835
836         msg_Dbg( p_aout, "Delay is %ld frames (%zu bytes)", delay, i_bytes );
837
838         msg_Dbg( p_aout, "Bytes per frame: %d", p_aout->output.output.i_bytes_per_frame );
839         msg_Dbg( p_aout, "Rate: %d", p_aout->output.output.i_rate );
840         msg_Dbg( p_aout, "Frame length: %d", p_aout->output.output.i_frame_length );
841         msg_Dbg( p_aout, "Next date: in %"PRId64" microseconds", delay_us );
842 #endif
843         next_date = mdate() + delay_us;
844     }
845
846     block_t *p_buffer = aout_OutputNextBuffer( p_aout, next_date,
847            (p_aout->output.output.i_format ==  VLC_CODEC_SPDIFL) );
848
849     /* Audio output buffer shortage -> stop the fill process and wait */
850     if( p_buffer == NULL )
851         goto error;
852
853     for (;;)
854     {
855         int n = snd_pcm_poll_descriptors_count(p_pcm);
856         struct pollfd ufd[n];
857         unsigned short revents;
858
859         snd_pcm_poll_descriptors(p_pcm, ufd, n);
860         do
861         {
862             vlc_restorecancel(canc);
863             poll(ufd, n, -1);
864             canc = vlc_savecancel();
865             snd_pcm_poll_descriptors_revents(p_pcm, ufd, n, &revents);
866         }
867         while(!revents);
868
869         if(revents & POLLOUT)
870         {
871             i_snd_rc = snd_pcm_writei( p_pcm, p_buffer->p_buffer,
872                                        p_buffer->i_nb_samples );
873             if( i_snd_rc != -ESTRPIPE )
874                 break;
875         }
876
877         /* a suspend event occurred
878          * (stream is suspended and waiting for an application recovery) */
879         msg_Dbg( p_aout, "entering in suspend mode, trying to resume..." );
880
881         while( ( i_snd_rc = snd_pcm_resume( p_pcm ) ) == -EAGAIN )
882         {
883             vlc_restorecancel(canc);
884             msleep(CLOCK_FREQ); /* device still suspended, wait... */
885             canc = vlc_savecancel();
886         }
887
888         if( i_snd_rc < 0 )
889             /* Device does not support resuming, restart it */
890             i_snd_rc = snd_pcm_prepare( p_pcm );
891
892     }
893
894     if( i_snd_rc < 0 )
895         msg_Err( p_aout, "cannot write: %s", snd_strerror( i_snd_rc ) );
896
897     vlc_restorecancel(canc);
898     block_Release( p_buffer );
899     return;
900
901 error:
902     if( i_snd_rc < 0 )
903         msg_Err( p_aout, "ALSA error: %s", snd_strerror( i_snd_rc ) );
904
905     vlc_restorecancel(canc);
906     msleep(p_sys->i_period_time / 2);
907 }
908
909 static void GetDevicesForCard( module_config_t *p_item, int i_card );
910 static void GetDevices( module_config_t *p_item );
911
912 /*****************************************************************************
913  * config variable callback
914  *****************************************************************************/
915 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
916                                vlc_value_t newval, vlc_value_t oldval, void *p_unused )
917 {
918     module_config_t *p_item;
919     int i;
920     (void)newval;
921     (void)oldval;
922     (void)p_unused;
923
924     p_item = config_FindConfig( p_this, psz_name );
925     if( !p_item ) return VLC_SUCCESS;
926
927     /* Clear-up the current list */
928     if( p_item->i_list )
929     {
930         /* Keep the first entrie */
931         for( i = 1; i < p_item->i_list; i++ )
932         {
933             free( (char *)p_item->ppsz_list[i] );
934             free( (char *)p_item->ppsz_list_text[i] );
935         }
936         /* TODO: Remove when no more needed */
937         p_item->ppsz_list[i] = NULL;
938         p_item->ppsz_list_text[i] = NULL;
939     }
940     p_item->i_list = 1;
941
942     GetDevices( p_item );
943
944     /* Signal change to the interface */
945     p_item->b_dirty = true;
946
947     return VLC_SUCCESS;
948 }
949
950
951 static void GetDevicesForCard( module_config_t *p_item, int i_card )
952 {
953     int i_pcm_device = -1;
954     int i_err = 0;
955     snd_pcm_info_t *p_pcm_info;
956     snd_ctl_t *p_ctl;
957     char psz_dev[64];
958     char *psz_card_name;
959
960     sprintf( psz_dev, "hw:%i", i_card );
961
962     if( ( i_err = snd_ctl_open( &p_ctl, psz_dev, 0 ) ) < 0 )
963         return;
964
965     if( ( i_err = snd_card_get_name( i_card, &psz_card_name ) ) != 0)
966         psz_card_name = _("Unknown soundcard");
967
968     snd_pcm_info_alloca( &p_pcm_info );
969
970     for (;;)
971     {
972         char *psz_device, *psz_descr;
973         if( ( i_err = snd_ctl_pcm_next_device( p_ctl, &i_pcm_device ) ) < 0 )
974             i_pcm_device = -1;
975         if( i_pcm_device < 0 )
976             break;
977
978         snd_pcm_info_set_device( p_pcm_info, i_pcm_device );
979         snd_pcm_info_set_subdevice( p_pcm_info, 0 );
980         snd_pcm_info_set_stream( p_pcm_info, SND_PCM_STREAM_PLAYBACK );
981
982         if( ( i_err = snd_ctl_pcm_info( p_ctl, p_pcm_info ) ) < 0 )
983         {
984             if( i_err != -ENOENT )
985             {
986                 /*printf( "get_devices_for_card(): "
987                          "snd_ctl_pcm_info() "
988                          "failed (%d:%d): %s.\n", i_card,
989                          i_pcm_device, snd_strerror( -i_err ) );*/
990             }
991             continue;
992         }
993
994         if( asprintf( &psz_device, "hw:%d,%d", i_card, i_pcm_device ) == -1 )
995             break;
996         if( asprintf( &psz_descr, "%s: %s (%s)", psz_card_name,
997                   snd_pcm_info_get_name(p_pcm_info), psz_device ) == -1 )
998         {
999             free( psz_device );
1000             break;
1001         }
1002
1003         p_item->ppsz_list =
1004             (char **)realloc( p_item->ppsz_list,
1005                               (p_item->i_list + 2) * sizeof(char *) );
1006         p_item->ppsz_list_text =
1007             (char **)realloc( p_item->ppsz_list_text,
1008                               (p_item->i_list + 2) * sizeof(char *) );
1009         p_item->ppsz_list[ p_item->i_list ] = psz_device;
1010         p_item->ppsz_list_text[ p_item->i_list ] = psz_descr;
1011         p_item->i_list++;
1012         p_item->ppsz_list[ p_item->i_list ] = NULL;
1013         p_item->ppsz_list_text[ p_item->i_list ] = NULL;
1014     }
1015
1016     snd_ctl_close( p_ctl );
1017 }
1018
1019
1020
1021 static void GetDevices( module_config_t *p_item )
1022 {
1023     int i_card = -1;
1024     int i_err = 0;
1025
1026     if( ( i_err = snd_card_next( &i_card ) ) != 0 )
1027     {
1028         /*printf( "snd_card_next() failed: %s", snd_strerror( -i_err ) );*/
1029         return;
1030     }
1031
1032     while( i_card > -1 )
1033     {
1034         GetDevicesForCard( p_item, i_card );
1035         if( ( i_err = snd_card_next( &i_card ) ) != 0 )
1036         {
1037             /*printf( "snd_card_next() failed: %s", snd_strerror( -i_err ) );*/
1038             break;
1039         }
1040     }
1041 }