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