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