]> git.sesse.net Git - vlc/blob - modules/audio_output/alsa.c
* modules/demux/a52sys.c: forgot to remove this file.
[vlc] / modules / audio_output / alsa.c
1 /*****************************************************************************
2  * alsa.c : alsa plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2001 VideoLAN
5  * $Id: alsa.c,v 1.40 2004/02/20 19:21:23 gbazin Exp $
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     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
95 /*****************************************************************************
96  * Module descriptor
97  *****************************************************************************/
98 vlc_module_begin();
99     set_description( _("ALSA audio output") );
100     add_string( "alsadev", DEFAULT_ALSA_DEVICE, aout_FindAndRestart,
101                 N_("ALSA Device Name"), NULL, VLC_FALSE );
102     set_capability( "audio output", 150 );
103     set_callbacks( Open, Close );
104 vlc_module_end();
105
106 /*****************************************************************************
107  * Probe: probe the audio device for available formats and channels
108  *****************************************************************************/
109 static void Probe( aout_instance_t * p_aout,
110                    const char * psz_device, const char * psz_iec_device,
111                    int *pi_snd_pcm_format )
112 {
113     struct aout_sys_t * p_sys = p_aout->output.p_sys;
114     vlc_value_t val, text;
115     int i_ret;
116
117     var_Create ( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
118     text.psz_string = _("Audio Device");
119     var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
120
121     /* We'll open the audio device in non blocking mode so we can just exit
122      * when it is already in use, but for the real stuff we'll still use
123      * the blocking mode */
124
125     /* Now test linear PCM capabilities */
126     if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
127                                  SND_PCM_STREAM_PLAYBACK,
128                                  SND_PCM_NONBLOCK ) ) )
129     {
130         int i_channels;
131         snd_pcm_hw_params_t * p_hw;
132         snd_pcm_hw_params_alloca (&p_hw);
133
134         if ( snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) < 0 )
135         {
136             msg_Warn( p_aout, "unable to retrieve initial hardware parameters"
137                               ", disabling linear PCM audio" );
138             snd_pcm_close( p_sys->p_snd_pcm );
139             var_Destroy( p_aout, "audio-device" );
140             return;
141         }
142
143         if ( snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
144                                            *pi_snd_pcm_format ) < 0 )
145         {
146             if( *pi_snd_pcm_format != SND_PCM_FORMAT_S16 )
147             {
148                 *pi_snd_pcm_format = SND_PCM_FORMAT_S16;
149                 if ( snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
150                                                    *pi_snd_pcm_format ) < 0 )
151                 {
152                     msg_Warn( p_aout, "unable to set stream sample size and "
153                               "word order, disabling linear PCM audio" );
154                     snd_pcm_close( p_sys->p_snd_pcm );
155                     var_Destroy( p_aout, "audio-device" );
156                     return;
157                 }
158             }
159         }
160
161         i_channels = aout_FormatNbChannels( &p_aout->output.output );
162
163         while ( i_channels > 0 )
164         {
165             if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw,
166                                                    i_channels ) )
167             {
168                 switch ( i_channels )
169                 {
170                 case 1:
171                     val.i_int = AOUT_VAR_MONO;
172                     text.psz_string = N_("Mono");
173                     var_Change( p_aout, "audio-device",
174                                 VLC_VAR_ADDCHOICE, &val, &text );
175                     break;
176                 case 2:
177                     val.i_int = AOUT_VAR_STEREO;
178                     text.psz_string = N_("Stereo");
179                     var_Change( p_aout, "audio-device",
180                                 VLC_VAR_ADDCHOICE, &val, &text );
181                     var_Set( p_aout, "audio-device", val );
182                     break;
183                 case 4:
184                     val.i_int = AOUT_VAR_2F2R;
185                     text.psz_string = N_("2 Front 2 Rear");
186                     var_Change( p_aout, "audio-device",
187                                 VLC_VAR_ADDCHOICE, &val, &text );
188                     break;
189                 case 6:
190                     val.i_int = AOUT_VAR_5_1;
191                     text.psz_string = N_("5.1");
192                     var_Change( p_aout, "audio-device",
193                                 VLC_VAR_ADDCHOICE, &val, &text );
194                     break;
195                 }
196             }
197
198             --i_channels;
199         }
200
201         /* Close the previously opened device */
202         snd_pcm_close( p_sys->p_snd_pcm );
203     }
204     else if ( i_ret == -EBUSY )
205     {
206         msg_Warn( p_aout, "audio device: %s is already in use", psz_device );
207     }
208
209     /* Test for S/PDIF device if needed */
210     if ( psz_iec_device )
211     {
212         /* Opening the device should be enough */
213         if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
214                                      SND_PCM_STREAM_PLAYBACK,
215                                      SND_PCM_NONBLOCK ) ) )
216         {
217             val.i_int = AOUT_VAR_SPDIF;
218             text.psz_string = N_("A/52 over S/PDIF");
219             var_Change( p_aout, "audio-device",
220                         VLC_VAR_ADDCHOICE, &val, &text );
221             if( config_GetInt( p_aout, "spdif" ) )
222                 var_Set( p_aout, "audio-device", val );
223
224             snd_pcm_close( p_sys->p_snd_pcm );
225         }
226         else if ( i_ret == -EBUSY )
227         {
228             msg_Warn( p_aout, "audio device: %s is already in use",
229                       psz_iec_device );
230         }
231     }
232
233     var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
234     if( val.i_int <= 0 )
235     {
236         /* Probe() has failed. */
237         var_Destroy( p_aout, "audio-device" );
238         return;
239     }
240
241     /* Add final settings to the variable */
242     var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
243     val.b_bool = VLC_TRUE;
244     var_Set( p_aout, "intf-change", val );
245 }
246
247 /*****************************************************************************
248  * Open: create a handle and open an alsa device
249  *****************************************************************************
250  * This function opens an alsa device, through the alsa API.
251  *
252  * Note: the only heap-allocated string is psz_device. All the other pointers
253  * are references to psz_device or to stack-allocated data.
254  *****************************************************************************/
255 static int Open( vlc_object_t *p_this )
256 {
257     aout_instance_t * p_aout = (aout_instance_t *)p_this;
258     struct aout_sys_t * p_sys;
259     vlc_value_t val;
260
261     char psz_default_iec_device[128]; /* Buffer used to store the default
262                                          S/PDIF device */
263     char * psz_device, * psz_iec_device; /* device names for PCM and S/PDIF
264                                             output */
265
266     int i_vlc_pcm_format; /* Audio format for VLC's data */
267     int i_snd_pcm_format; /* Audio format for ALSA's data */
268
269     snd_pcm_uframes_t i_buffer_size = 0;
270     snd_pcm_uframes_t i_period_size = 0;
271     int i_channels = 0;
272
273     snd_pcm_hw_params_t *p_hw;
274     snd_pcm_sw_params_t *p_sw;
275
276     int i_snd_rc = -1;
277
278     /* Allocate structures */
279     p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
280     if( p_sys == NULL )
281     {
282         msg_Err( p_aout, "out of memory" );
283         return VLC_ENOMEM;
284     }
285     p_sys->b_playing = VLC_FALSE;
286     p_sys->start_date = 0;
287     p_sys->p_status = (snd_pcm_status_t *)malloc(snd_pcm_status_sizeof());
288     vlc_cond_init( p_aout, &p_sys->wait );
289     vlc_mutex_init( p_aout, &p_sys->lock );
290
291     /* Get device name */
292     if( (psz_device = config_GetPsz( p_aout, "alsadev" )) == NULL )
293     {
294         msg_Err( p_aout, "no audio device given (maybe \"default\" ?)" );
295         free( p_sys );
296         return VLC_EGENERIC;
297     }
298
299     /* Choose the IEC device for S/PDIF output:
300        if the device is overriden by the user then it will be the one
301        otherwise we compute the default device based on the output format. */
302     if( AOUT_FMT_NON_LINEAR( &p_aout->output.output )
303         && !strcmp( psz_device, DEFAULT_ALSA_DEVICE ) )
304     {
305         snprintf( psz_default_iec_device, sizeof(psz_default_iec_device),
306                   "iec958:AES0=0x%x,AES1=0x%x,AES2=0x%x,AES3=0x%x",
307                   IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO,
308                   IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
309                   0,
310                   ( p_aout->output.output.i_rate == 48000 ?
311                     IEC958_AES3_CON_FS_48000 :
312                     ( p_aout->output.output.i_rate == 44100 ?
313                       IEC958_AES3_CON_FS_44100 : IEC958_AES3_CON_FS_32000 ) ) );
314         psz_iec_device = psz_default_iec_device;
315     }
316     else if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
317     {
318         psz_iec_device = psz_device;
319     }
320     else
321     {
322         psz_iec_device = NULL;
323     }
324
325     /* Choose the linear PCM format (read the comment above about FPU
326        and float32) */
327     if( p_aout->p_libvlc->i_cpu & CPU_CAPABILITY_FPU )
328     {
329         i_vlc_pcm_format = VLC_FOURCC('f','l','3','2');
330         i_snd_pcm_format = SND_PCM_FORMAT_FLOAT;
331     }
332     else
333     {
334         i_vlc_pcm_format = AOUT_FMT_S16_NE;
335         i_snd_pcm_format = SND_PCM_FORMAT_S16;
336     }
337
338     /* If the variable doesn't exist then it's the first time we're called
339        and we have to probe the available audio formats and channels */
340     if ( var_Type( p_aout, "audio-device" ) == 0 )
341     {
342         Probe( p_aout, psz_device, psz_iec_device, &i_snd_pcm_format );
343         switch( i_snd_pcm_format )
344         {
345         case SND_PCM_FORMAT_FLOAT:
346             i_vlc_pcm_format = VLC_FOURCC('f','l','3','2');
347             break;
348         case SND_PCM_FORMAT_S16:
349             i_vlc_pcm_format = AOUT_FMT_S16_NE;
350             break;
351         }
352     }
353
354     if ( var_Get( p_aout, "audio-device", &val ) < 0 )
355     {
356         free( p_sys );
357         free( psz_device );
358         return VLC_EGENERIC;
359     }
360
361     if ( val.i_int == AOUT_VAR_SPDIF )
362     {
363         p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
364     }
365     else if ( val.i_int == AOUT_VAR_5_1 )
366     {
367         p_aout->output.output.i_format = i_vlc_pcm_format;
368         p_aout->output.output.i_physical_channels
369             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
370                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
371                | AOUT_CHAN_LFE;
372         free( psz_device );
373         psz_device = strdup( "surround51" );
374     }
375     else if ( val.i_int == AOUT_VAR_2F2R )
376     {
377         p_aout->output.output.i_format = i_vlc_pcm_format;
378         p_aout->output.output.i_physical_channels
379             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
380                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
381         free( psz_device );
382         psz_device = strdup( "surround40" );
383     }
384     else if ( val.i_int == AOUT_VAR_STEREO )
385     {
386         p_aout->output.output.i_format = i_vlc_pcm_format;
387         p_aout->output.output.i_physical_channels
388             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
389     }
390     else if ( val.i_int == AOUT_VAR_MONO )
391     {
392         p_aout->output.output.i_format = i_vlc_pcm_format;
393         p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
394     }
395
396     else
397     {
398         /* This should not happen ! */
399         msg_Err( p_aout, "internal: can't find audio-device (%i)", val.i_int );
400         free( p_sys );
401         return VLC_EGENERIC;
402     }
403
404 #ifdef ALSA_DEBUG
405     snd_output_stdio_attach( &p_sys->p_snd_stderr, stderr, 0 );
406 #endif
407
408     /* Open the device */
409     if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
410     {
411         if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
412                             SND_PCM_STREAM_PLAYBACK, 0 ) ) < 0 )
413         {
414             msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
415                              psz_iec_device, snd_strerror( i_snd_rc ) );
416             free( p_sys );
417             free( psz_device );
418             return VLC_EGENERIC;
419         }
420         i_buffer_size = ALSA_SPDIF_BUFFER_SIZE;
421         i_snd_pcm_format = SND_PCM_FORMAT_S16;
422         i_channels = 2;
423
424         p_aout->output.i_nb_samples = i_period_size = ALSA_SPDIF_PERIOD_SIZE;
425         p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
426         p_aout->output.output.i_frame_length = A52_FRAME_NB;
427
428         aout_VolumeNoneInit( p_aout );
429     }
430     else
431     {
432         msg_Dbg( p_aout, "opening ALSA device `%s'", psz_device );
433
434         if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
435                             SND_PCM_STREAM_PLAYBACK, 0 ) ) < 0 )
436         {
437             msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
438                              psz_device, snd_strerror( i_snd_rc ) );
439             free( p_sys );
440             free( psz_device );
441             return VLC_EGENERIC;
442         }
443         i_buffer_size = ALSA_DEFAULT_BUFFER_SIZE;
444         i_channels = aout_FormatNbChannels( &p_aout->output.output );
445
446         p_aout->output.i_nb_samples = i_period_size = ALSA_DEFAULT_PERIOD_SIZE;
447
448         aout_VolumeSoftInit( p_aout );
449     }
450
451     /* Free psz_device so that all the remaining data is stack-allocated */
452     free( psz_device );
453
454     p_aout->output.pf_play = Play;
455
456     snd_pcm_hw_params_alloca(&p_hw);
457     snd_pcm_sw_params_alloca(&p_sw);
458
459     /* Get Initial hardware parameters */
460     if ( ( i_snd_rc = snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) ) < 0 )
461     {
462         msg_Err( p_aout, "unable to retrieve initial hardware parameters (%s)",
463                          snd_strerror( i_snd_rc ) );
464         goto error;
465     }
466
467     /* Set format. */
468     if ( ( i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
469                                                     i_snd_pcm_format ) ) < 0 )
470     {
471         msg_Err( p_aout, "unable to set stream sample size and word order (%s)",
472                          snd_strerror( i_snd_rc ) );
473         goto error;
474     }
475
476     if ( ( i_snd_rc = snd_pcm_hw_params_set_access( p_sys->p_snd_pcm, p_hw,
477                                     SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 )
478     {
479         msg_Err( p_aout, "unable to set interleaved stream format (%s)",
480                          snd_strerror( i_snd_rc ) );
481         goto error;
482     }
483
484     /* Set channels. */
485     if ( ( i_snd_rc = snd_pcm_hw_params_set_channels( p_sys->p_snd_pcm, p_hw,
486                                                       i_channels ) ) < 0 )
487     {
488         msg_Err( p_aout, "unable to set number of output channels (%s)",
489                          snd_strerror( i_snd_rc ) );
490         goto error;
491     }
492
493     /* Set rate. */
494 #ifdef HAVE_ALSA_NEW_API
495     if ( ( i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw,
496                                 &p_aout->output.output.i_rate, NULL ) ) < 0 )
497 #else
498     if ( ( i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw,
499                                 p_aout->output.output.i_rate, NULL ) ) < 0 )
500 #endif
501     {
502         msg_Warn( p_aout, "The rate %d Hz is not supported by your hardware. "
503                   "Using %d Hz instead.\n", p_aout->output.output.i_rate,
504                   i_snd_rc );
505         p_aout->output.output.i_rate = i_snd_rc;
506     }
507
508     /* Set buffer size. */
509 #ifdef HAVE_ALSA_NEW_API
510     if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm,
511                                     p_hw, &i_buffer_size ) ) < 0 )
512 #else
513     if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm,
514                                     p_hw, i_buffer_size ) ) < 0 )
515 #endif
516     {
517         msg_Err( p_aout, "unable to set buffer size (%s)",
518                          snd_strerror( i_snd_rc ) );
519         goto error;
520     }
521
522     /* Set period size. */
523 #ifdef HAVE_ALSA_NEW_API
524     if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm,
525                                     p_hw, &i_period_size, NULL ) ) < 0 )
526 #else
527     if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm,
528                                     p_hw, i_period_size, NULL ) ) < 0 )
529 #endif
530     {
531         msg_Err( p_aout, "unable to set period size (%s)",
532                          snd_strerror( i_snd_rc ) );
533         goto error;
534     }
535     p_aout->output.i_nb_samples = i_period_size;
536
537     /* Commit hardware parameters. */
538     if ( ( i_snd_rc = snd_pcm_hw_params( p_sys->p_snd_pcm, p_hw ) ) < 0 )
539     {
540         msg_Err( p_aout, "unable to commit hardware configuration (%s)",
541                          snd_strerror( i_snd_rc ) );
542         goto error;
543     }
544
545 #ifdef HAVE_ALSA_NEW_API
546     if( ( i_snd_rc = snd_pcm_hw_params_get_period_time( p_hw,
547                                     &p_sys->i_period_time, NULL ) ) < 0 )
548 #else
549     if( ( p_sys->i_period_time =
550                   snd_pcm_hw_params_get_period_time( p_hw, NULL ) ) < 0 )
551 #endif
552     {
553         msg_Err( p_aout, "unable to get period time (%s)",
554                          snd_strerror( i_snd_rc ) );
555         goto error;
556     }
557
558     /* Get Initial software parameters */
559     snd_pcm_sw_params_current( p_sys->p_snd_pcm, p_sw );
560
561     i_snd_rc = snd_pcm_sw_params_set_sleep_min( p_sys->p_snd_pcm, p_sw, 0 );
562
563     i_snd_rc = snd_pcm_sw_params_set_avail_min( p_sys->p_snd_pcm, p_sw,
564                                                 p_aout->output.i_nb_samples );
565
566     /* Commit software parameters. */
567     if ( snd_pcm_sw_params( p_sys->p_snd_pcm, p_sw ) < 0 )
568     {
569         msg_Err( p_aout, "unable to set software configuration" );
570         goto error;
571     }
572
573 #ifdef ALSA_DEBUG
574     snd_output_printf( p_sys->p_snd_stderr, "\nALSA hardware setup:\n\n" );
575     snd_pcm_dump_hw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
576     snd_output_printf( p_sys->p_snd_stderr, "\nALSA software setup:\n\n" );
577     snd_pcm_dump_sw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
578     snd_output_printf( p_sys->p_snd_stderr, "\n" );
579 #endif
580
581     /* Create ALSA thread and wait for its readiness. */
582     if( vlc_thread_create( p_aout, "aout", ALSAThread,
583                            VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) )
584     {
585         msg_Err( p_aout, "cannot create ALSA thread (%s)", strerror(errno) );
586         goto error;
587     }
588
589     return 0;
590
591 error:
592     snd_pcm_close( p_sys->p_snd_pcm );
593 #ifdef ALSA_DEBUG
594     snd_output_close( p_sys->p_snd_stderr );
595 #endif
596     free( p_sys );
597     return VLC_EGENERIC;
598 }
599
600 /*****************************************************************************
601  * Play: nothing to do
602  *****************************************************************************/
603 static void Play( aout_instance_t *p_aout )
604 {
605     if( !p_aout->output.p_sys->b_playing )
606     {
607         p_aout->output.p_sys->b_playing = 1;
608
609         /* get the playing date of the first aout buffer */
610         p_aout->output.p_sys->start_date =
611             aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
612
613         /* wake up the audio output thread */
614         vlc_mutex_lock( &p_aout->output.p_sys->lock );
615         vlc_cond_signal( &p_aout->output.p_sys->wait );
616         vlc_mutex_unlock( &p_aout->output.p_sys->lock );
617     }
618 }
619
620 /*****************************************************************************
621  * Close: close the ALSA device
622  *****************************************************************************/
623 static void Close( vlc_object_t *p_this )
624 {
625     aout_instance_t *p_aout = (aout_instance_t *)p_this;
626     struct aout_sys_t * p_sys = p_aout->output.p_sys;
627     int i_snd_rc;
628
629     /* make sure the audio output thread is waken up */
630     vlc_mutex_lock( &p_aout->output.p_sys->lock );
631     vlc_cond_signal( &p_aout->output.p_sys->wait );
632     vlc_mutex_unlock( &p_aout->output.p_sys->lock );
633
634     p_aout->b_die = VLC_TRUE;
635     vlc_thread_join( p_aout );
636     p_aout->b_die = VLC_FALSE;
637
638     i_snd_rc = snd_pcm_close( p_sys->p_snd_pcm );
639
640     if( i_snd_rc > 0 )
641     {
642         msg_Err( p_aout, "failed closing ALSA device (%s)",
643                          snd_strerror( i_snd_rc ) );
644     }
645
646 #ifdef ALSA_DEBUG
647     snd_output_close( p_sys->p_snd_stderr );
648 #endif
649
650     free( p_sys->p_status );
651     free( p_sys );
652 }
653
654 /*****************************************************************************
655  * ALSAThread: asynchronous thread used to DMA the data to the device
656  *****************************************************************************/
657 static int ALSAThread( aout_instance_t * p_aout )
658 {
659     /* Wait for the exact time to start playing (avoids resampling) */
660     vlc_mutex_lock( &p_aout->output.p_sys->lock );
661     if( !p_aout->output.p_sys->start_date )
662         vlc_cond_wait( &p_aout->output.p_sys->wait,
663                        &p_aout->output.p_sys->lock );
664     vlc_mutex_unlock( &p_aout->output.p_sys->lock );
665
666     mwait( p_aout->output.p_sys->start_date - AOUT_PTS_TOLERANCE / 4 );
667
668     while ( !p_aout->b_die )
669     {
670         ALSAFill( p_aout );
671     }
672
673     return 0;
674 }
675
676 /*****************************************************************************
677  * ALSAFill: function used to fill the ALSA buffer as much as possible
678  *****************************************************************************/
679 static void ALSAFill( aout_instance_t * p_aout )
680 {
681     struct aout_sys_t * p_sys = p_aout->output.p_sys;
682
683     aout_buffer_t * p_buffer;
684     snd_pcm_status_t * p_status = p_sys->p_status;
685     snd_timestamp_t ts_next;
686     int i_snd_rc;
687     mtime_t next_date;
688
689     /* Fill in the buffer until space or audio output buffer shortage */
690     {
691         /* Get the status */
692         i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status );
693         if( i_snd_rc < 0 )
694         {
695             msg_Err( p_aout, "unable to get the device's status (%s)",
696                              snd_strerror( i_snd_rc ) );
697
698             msleep( p_sys->i_period_time >> 1 );
699             return;
700         }
701
702         /* Handle buffer underruns and reget the status */
703         if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN )
704         {
705             /* Prepare the device */
706             i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm );
707
708             if( i_snd_rc == 0 )
709             {
710                 msg_Warn( p_aout, "recovered from buffer underrun" );
711
712                 /* Reget the status */
713                 i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status );
714                 if( i_snd_rc < 0 )
715                 {
716                     msg_Err( p_aout, "unable to get the device's status after "
717                              "recovery (%s)", snd_strerror( i_snd_rc ) );
718
719                     msleep( p_sys->i_period_time >> 1 );
720                     return;
721                 }
722             }
723             else
724             {
725                 msg_Err( p_aout, "unable to recover from buffer underrun" );
726
727                 msleep( p_sys->i_period_time >> 1 );
728                 return;
729             }
730
731             /* Underrun, try to recover as quickly as possible */
732             next_date = mdate();
733         }
734         else
735         {
736             /* Here the device should be either in the RUNNING state.
737              * p_status is valid. */
738
739             snd_pcm_status_get_tstamp( p_status, &ts_next );
740             next_date = (mtime_t)ts_next.tv_sec * 1000000 + ts_next.tv_usec;
741             next_date += (mtime_t)snd_pcm_status_get_delay(p_status)
742                 * 1000000 / p_aout->output.output.i_rate;
743         }
744
745         p_buffer = aout_OutputNextBuffer( p_aout, next_date,
746                         (p_aout->output.output.i_format ==
747                          VLC_FOURCC('s','p','d','i')) );
748
749         /* Audio output buffer shortage -> stop the fill process and wait */
750         if( p_buffer == NULL )
751         {
752             msleep( p_sys->i_period_time >> 1 );
753             return;
754         }
755
756         i_snd_rc = snd_pcm_writei( p_sys->p_snd_pcm, p_buffer->p_buffer,
757                                    p_buffer->i_nb_samples );
758
759         if( i_snd_rc < 0 )
760         {
761             msg_Err( p_aout, "write failed (%s)",
762                              snd_strerror( i_snd_rc ) );
763         }
764
765         aout_BufferFree( p_buffer );
766     }
767 }