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