]> git.sesse.net Git - vlc/blob - modules/audio_output/alsa.c
ALSA audio output v3.01 is out !
[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.2 2002/08/14 10:50:12 bozo 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 #include <alsa/asoundlib.h>
42
43 /*****************************************************************************
44  * aout_sys_t: ALSA audio output method descriptor
45  *****************************************************************************
46  * This structure is part of the audio output thread descriptor.
47  * It describes the ALSA specific properties of an audio device.
48  *****************************************************************************/
49 struct aout_sys_t
50 {
51     snd_pcm_t         * p_snd_pcm;
52     snd_pcm_sframes_t   i_buffer_size;
53     int                 i_period_time;
54
55     volatile vlc_bool_t b_initialized;
56
57 #ifdef DEBUG
58     snd_output_t      * p_snd_stderr;
59 #endif
60 };
61
62 /* These values are in frames.
63  * To convert them to a numer of bytes you have to multiply them by the
64  * number of channel(s) (eg. 2 for stereo) and the size of a sample (eg.
65  * 2 for s16). */
66 #define ALSA_DEFAULT_PERIOD_SIZE        2048
67 #define ALSA_DEFAULT_BUFFER_SIZE        ( ALSA_DEFAULT_PERIOD_SIZE << 4 )
68 #define ALSA_SPDIF_PERIOD_SIZE          1536
69 #define ALSA_SPDIF_BUFFER_SIZE          ( ALSA_SPDIF_PERIOD_SIZE << 4 )
70
71 /*****************************************************************************
72  * Local prototypes
73  *****************************************************************************/
74 static int  Open         ( vlc_object_t * );
75 static void Close        ( vlc_object_t * );
76
77 static int  SetFormat    ( aout_instance_t * );
78 static void Play         ( aout_instance_t *,
79                            aout_buffer_t * );
80
81 static int  ALSAThread   ( aout_instance_t * );
82 static void ALSAFill     ( aout_instance_t * );
83
84 /*****************************************************************************
85  * Module descriptor
86  *****************************************************************************/
87 vlc_module_begin();
88     add_category_hint( N_("Audio"), NULL );
89     add_string( "alsa-device", NULL, NULL, N_("Name"), NULL );
90     set_description( _("ALSA audio module") );
91     set_capability( "audio output", 50 );
92     set_callbacks( Open, Close );
93 vlc_module_end();
94
95 /*****************************************************************************
96  * Open: create a handle and open an alsa device
97  *****************************************************************************
98  * This function opens an alsa device, through the alsa API
99  *****************************************************************************/
100 static int Open( vlc_object_t *p_this )
101 {
102     aout_instance_t * p_aout = (aout_instance_t *)p_this;
103     struct aout_sys_t * p_sys;
104     char * psz_device;
105
106     /* Allows user to choose which ALSA device to use */
107     char  psz_alsadev[128];
108     char * psz_userdev;
109     int   i_snd_rc;
110
111     /* Allocate structures */
112     p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
113     if( p_sys == NULL )
114     {
115         msg_Err( p_aout, "out of memory" );
116         return -1;
117     }
118
119 #ifdef DEBUG
120     snd_output_stdio_attach( &p_sys->p_snd_stderr, stderr, 0 );
121 #endif
122
123     /* Read in ALSA device preferences from configuration */
124     psz_userdev = config_GetPsz( p_aout, "alsa-device" );
125
126     if( psz_userdev )
127     {
128         psz_device = psz_userdev;
129     }
130     else
131     {
132         /* Use the internal logic to decide on the device name */
133         if ( p_aout->output.output.i_format == AOUT_FMT_SPDIF )
134         {
135             unsigned char s[4];
136             s[0] = IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO;
137             s[1] = IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER;
138             s[2] = 0;
139             s[3] = IEC958_AES3_CON_FS_48000;
140             sprintf( psz_alsadev,
141                      "iec958:AES0=0x%x,AES1=0x%x,AES2=0x%x,AES3=0x%x",
142                      s[0], s[1], s[2], s[3] );
143             psz_device = psz_alsadev;
144             p_sys->i_buffer_size = ALSA_SPDIF_BUFFER_SIZE;
145             p_aout->output.i_nb_samples = ALSA_SPDIF_PERIOD_SIZE;
146         }
147         else
148         {
149             psz_device = "default";
150             p_sys->i_buffer_size = ALSA_DEFAULT_BUFFER_SIZE;
151             p_aout->output.i_nb_samples = ALSA_DEFAULT_PERIOD_SIZE;
152         }
153     }
154
155     /* Open device */
156     i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
157                              SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
158     if( i_snd_rc < 0 )
159     {
160         msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
161                          psz_device, snd_strerror(i_snd_rc) );
162         if( psz_userdev )
163             free( psz_userdev );
164         free( p_sys );
165         p_sys->p_snd_pcm = NULL;
166         return -1;
167     }
168
169     if( psz_userdev )
170     {
171         free( psz_userdev );
172     }
173
174     /* Create ALSA thread and wait for its readiness. */
175     p_sys->b_initialized = VLC_FALSE;
176     if( vlc_thread_create( p_aout, "aout", ALSAThread, VLC_FALSE ) )
177     {
178         msg_Err( p_aout, "cannot create ALSA thread (%s)", strerror(errno) );
179         if( psz_userdev )
180             free( psz_userdev );
181         free( p_sys );
182         return -1;
183     }
184
185     p_aout->output.pf_setformat = SetFormat;
186     p_aout->output.pf_play = Play;
187
188     return 0;
189 }
190
191 /*****************************************************************************
192  * SetFormat : sets the alsa output format
193  *****************************************************************************
194  * This function prepares the device, sets the rate, format, the mode
195  * ( "play as soon as you have data" ), and buffer information.
196  *****************************************************************************/
197 static int SetFormat( aout_instance_t * p_aout )
198 {
199     struct aout_sys_t * p_sys = p_aout->output.p_sys;
200
201     int i_snd_rc;
202     int i_format;
203
204     snd_pcm_hw_params_t *p_hw;
205     snd_pcm_sw_params_t *p_sw;
206
207     snd_pcm_hw_params_alloca(&p_hw);
208     snd_pcm_sw_params_alloca(&p_sw);
209
210     switch (p_aout->output.output.i_format)
211     {
212         case AOUT_FMT_MU_LAW:    i_format = SND_PCM_FORMAT_MU_LAW; break;
213         case AOUT_FMT_A_LAW:     i_format = SND_PCM_FORMAT_A_LAW; break;
214         case AOUT_FMT_IMA_ADPCM: i_format = SND_PCM_FORMAT_IMA_ADPCM; break;
215         case AOUT_FMT_U8:        i_format = SND_PCM_FORMAT_U8; break;
216         case AOUT_FMT_S16_LE:    i_format = SND_PCM_FORMAT_S16_LE; break;
217         case AOUT_FMT_S16_BE:    i_format = SND_PCM_FORMAT_S16_BE; break;
218         case AOUT_FMT_S8:        i_format = SND_PCM_FORMAT_S8; break;
219         case AOUT_FMT_U16_LE:    i_format = SND_PCM_FORMAT_U16_LE; break;
220         case AOUT_FMT_U16_BE:    i_format = SND_PCM_FORMAT_U16_BE; break;
221         case AOUT_FMT_FLOAT32:   i_format = SND_PCM_FORMAT_FLOAT; break;
222         case AOUT_FMT_FIXED32:
223         default:
224             msg_Err( p_aout, "audio output format 0x%x not supported",
225                      p_aout->output.output.i_format );
226             return -1;
227             break;
228     }
229
230     i_snd_rc = snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw );
231     if( i_snd_rc < 0 )
232     {
233         msg_Err( p_aout, "unable to retrieve initial hardware parameters" );
234         return -1;
235     }
236
237     i_snd_rc = snd_pcm_hw_params_set_access( p_sys->p_snd_pcm, p_hw,
238                                              SND_PCM_ACCESS_RW_INTERLEAVED );
239     if( i_snd_rc < 0 )
240     {
241         msg_Err( p_aout, "unable to set interleaved stream format" );
242         return -1;
243     }
244
245     i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw, i_format );
246     if( i_snd_rc < 0 )
247     {
248         msg_Err( p_aout, "unable to set stream sample size and word order" );
249         return -1;
250     }
251
252     i_snd_rc = snd_pcm_hw_params_set_channels( p_sys->p_snd_pcm, p_hw,
253                     p_aout->output.output.i_channels );
254     if( i_snd_rc < 0 )
255     {
256         msg_Err( p_aout, "unable to set number of output channels" );
257         return -1;
258     }
259
260     i_snd_rc = snd_pcm_hw_params_set_rate( p_sys->p_snd_pcm, p_hw,
261                                            p_aout->output.output.i_rate, 0 );
262     if( i_snd_rc < 0 )
263     {
264         msg_Err( p_aout, "unable to set sample rate" );
265         return -1;
266     }
267
268     i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm, p_hw,
269                                                        p_sys->i_buffer_size );
270     if( i_snd_rc < 0 )
271     {
272         msg_Err( p_aout, "unable to set buffer time" );
273         return -1;
274     }
275     p_sys->i_buffer_size = i_snd_rc;
276
277     i_snd_rc = snd_pcm_hw_params_set_period_size_near(
278                     p_sys->p_snd_pcm, p_hw, p_aout->output.i_nb_samples, 0 );
279     if( i_snd_rc < 0 )
280     {
281         msg_Err( p_aout, "unable to set period size" );
282         return -1;
283     }
284     p_aout->output.i_nb_samples = i_snd_rc;
285     p_sys->i_period_time = snd_pcm_hw_params_get_period_time( p_hw, 0 );
286
287     i_snd_rc = snd_pcm_hw_params( p_sys->p_snd_pcm, p_hw );
288     if (i_snd_rc < 0)
289     {
290         msg_Err( p_aout, "unable to set hardware configuration" );
291         return -1;
292     }
293
294     snd_pcm_sw_params_current( p_sys->p_snd_pcm, p_sw );
295     i_snd_rc = snd_pcm_sw_params_set_sleep_min( p_sys->p_snd_pcm, p_sw, 0 );
296
297     i_snd_rc = snd_pcm_sw_params_set_avail_min( p_sys->p_snd_pcm, p_sw,
298                                                 p_aout->output.i_nb_samples );
299
300     i_snd_rc = snd_pcm_sw_params( p_sys->p_snd_pcm, p_sw );
301     if( i_snd_rc < 0 )
302     {
303         msg_Err( p_aout, "unable to set software configuration" );
304         return -1;
305     }
306
307 #ifdef DEBUG
308     snd_output_printf( p_sys->p_snd_stderr, "\nALSA hardware setup:\n\n" );
309     snd_pcm_dump_hw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
310     snd_output_printf( p_sys->p_snd_stderr, "\nALSA software setup:\n\n" );
311     snd_pcm_dump_sw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
312     snd_output_printf( p_sys->p_snd_stderr, "\n" );
313 #endif
314
315     p_sys->b_initialized = VLC_TRUE;
316
317     return 0;
318 }
319
320 /*****************************************************************************
321  * Play: queue a buffer for playing by ALSAThread
322  *****************************************************************************/
323 static void Play( aout_instance_t *p_aout, aout_buffer_t * p_buffer )
324 {
325     aout_FifoPush( p_aout, &p_aout->output.fifo, p_buffer );
326 }
327
328 /*****************************************************************************
329  * Close: close the Alsa device
330  *****************************************************************************/
331 static void Close( vlc_object_t *p_this )
332 {
333     aout_instance_t *p_aout = (aout_instance_t *)p_this;
334     struct aout_sys_t * p_sys = p_aout->output.p_sys;
335     int i_snd_rc;
336
337     p_aout->b_die = 1;
338     vlc_thread_join( p_aout );
339
340     if( p_sys->p_snd_pcm )
341     {
342         i_snd_rc = snd_pcm_close( p_sys->p_snd_pcm );
343
344         if( i_snd_rc > 0 )
345         {
346             msg_Err( p_aout, "failed closing ALSA device (%s)",
347                              snd_strerror( i_snd_rc ) );
348         }
349     }
350
351     free( p_sys );
352 }
353
354 /*****************************************************************************
355  * ALSAThread: asynchronous thread used to DMA the data to the device
356  *****************************************************************************/
357 static int ALSAThread( aout_instance_t * p_aout )
358 {
359     struct aout_sys_t * p_sys = p_aout->output.p_sys;
360
361     while ( !p_aout->b_die )
362     {
363         if( !p_sys->b_initialized )
364         {
365             msleep( THREAD_SLEEP );
366             continue;
367         }
368
369         ALSAFill( p_aout );
370
371         msleep( p_sys->i_period_time );
372     }
373
374     return 0;
375 }
376
377 /*****************************************************************************
378  * ALSAFill: function used to fill the ALSA buffer as much as possible
379  *****************************************************************************/
380 static void ALSAFill( aout_instance_t * p_aout )
381 {
382     struct aout_sys_t * p_sys = p_aout->output.p_sys;
383
384     aout_buffer_t * p_buffer;
385     mtime_t next_date = 0;
386     snd_pcm_status_t * p_status;
387     snd_timestamp_t ts_next;
388     int i_snd_rc, i_size;
389     byte_t * p_bytes;
390     snd_pcm_uframes_t i_avail;
391
392     snd_pcm_status_alloca( &p_status );
393
394     i_snd_rc = snd_pcm_wait( p_sys->p_snd_pcm, THREAD_SLEEP );
395     if( i_snd_rc < 0 )
396     {
397         msg_Err( p_aout, "alsa device not ready !!! (%s)",
398                          snd_strerror( i_snd_rc ) );
399         return;
400     }
401
402     while( VLC_TRUE )
403     {
404         i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status );
405         if( i_snd_rc < 0 )
406         {
407             msg_Err( p_aout, "unable to get the device's status (%s)",
408                              snd_strerror( i_snd_rc ) );
409             return;
410         }
411
412         if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN )
413         {
414             i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm );
415             if( i_snd_rc == 0 )
416             {
417                 msg_Err( p_aout, "recovered from buffer underrun" );
418                 next_date = mdate();
419                 i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status );
420                 if( i_snd_rc < 0 )
421                 {
422                     msg_Err( p_aout,
423                         "unable to get the device's status after recovery (%s)",
424                         snd_strerror( i_snd_rc ) );
425                     return;
426                 }
427             }
428             else
429             {
430                 msg_Err( p_aout, "unable to recover from buffer underrun" );
431                 return;
432             }
433         }
434
435         i_avail = snd_pcm_status_get_avail( p_status );
436         if( i_avail >= p_aout->output.i_nb_samples )
437         {
438             snd_pcm_status_get_tstamp( p_status, &ts_next );
439             next_date = (mtime_t)ts_next.tv_sec * 1000000 + ts_next.tv_usec;
440
441             p_buffer = aout_OutputNextBuffer( p_aout, next_date, 0 );
442
443             if( p_buffer == NULL )
444                 return;
445
446             p_bytes = p_buffer->p_buffer;
447
448             i_snd_rc = snd_pcm_writei( p_sys->p_snd_pcm, p_bytes,
449                                        p_buffer->i_nb_samples );
450
451             if( i_snd_rc < 0 )
452             {
453                 msg_Err( p_aout, "write failed (%s)",
454                                  snd_strerror( i_snd_rc ) );
455             }
456             else
457             {
458                 aout_BufferFree( p_buffer );
459             }
460         }
461     }
462 }
463