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