]> git.sesse.net Git - vlc/blob - plugins/alsa/alsa.c
5d01c03e2abc1addae3433c1a1ca946fdfac17c1
[vlc] / plugins / alsa / alsa.c
1 /*****************************************************************************
2  * alsa.c : alsa plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000-2001 VideoLAN
5  * $Id: alsa.c,v 1.20 2002/07/20 18:01:42 sam 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  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <errno.h>                                                 /* ENOMEM */
30 #include <string.h>                                            /* strerror() */
31 #include <stdlib.h>                            /* calloc(), malloc(), free() */
32
33 #include <vlc/vlc.h>
34 #include <vlc/aout.h>
35
36 #include <alsa/asoundlib.h>
37
38 /*****************************************************************************
39  * Capabilities defined in the other files.
40  *****************************************************************************/
41 static void aout_getfunctions( function_list_t * p_function_list );
42 static int  aout_Open        ( aout_thread_t * );
43 static int  aout_SetFormat   ( aout_thread_t * );
44 static void aout_HandleXrun  ( aout_thread_t *);
45 static int  aout_GetBufInfo  ( aout_thread_t *, int i_buffer_limit );
46 static void aout_Play        ( aout_thread_t *, byte_t *buffer, int i_size );
47 static void aout_Close       ( aout_thread_t * );
48
49 /*****************************************************************************
50  * Build configuration tree.
51  *****************************************************************************/
52 MODULE_CONFIG_START
53     ADD_CATEGORY_HINT( N_("Device"), NULL )
54     ADD_STRING( "alsa-device", NULL, NULL, N_("Name"), NULL )
55 MODULE_CONFIG_STOP
56
57 MODULE_INIT_START
58     SET_DESCRIPTION( _("ALSA audio module") )
59     ADD_CAPABILITY( AOUT, 50 )
60 MODULE_INIT_STOP
61     
62 MODULE_ACTIVATE_START
63     aout_getfunctions( &p_module->p_functions->aout );
64 MODULE_ACTIVATE_STOP
65
66 MODULE_DEACTIVATE_START
67 MODULE_DEACTIVATE_STOP
68
69 /*****************************************************************************
70  * Preamble
71  *****************************************************************************/
72 typedef struct alsa_device_t
73 {
74     int i_num;
75 } alsa_device_t;
76
77 typedef struct alsa_card_t
78 {
79     int i_num;
80 } alsa_card_t;
81
82 /* here we store plugin dependant informations */
83
84 struct aout_sys_t
85 {
86     snd_pcm_t   * p_alsa_handle;
87     unsigned long buffer_time;
88     unsigned long period_time;
89     unsigned long chunk_size;
90     unsigned long buffer_size;
91     unsigned long rate;
92     unsigned int  bytes_per_sample;
93     unsigned int  samples_per_frame;
94     unsigned int  bytes_per_frame;
95 };
96
97 /*****************************************************************************
98  * Functions exported as capabilities. They are declared as static so that
99  * we don't pollute the namespace too much.
100  *****************************************************************************/
101 static void aout_getfunctions( function_list_t * p_function_list )
102 {
103     p_function_list->functions.aout.pf_open = aout_Open;
104     p_function_list->functions.aout.pf_setformat = aout_SetFormat;
105     p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
106     p_function_list->functions.aout.pf_play = aout_Play;
107     p_function_list->functions.aout.pf_close = aout_Close;
108 }
109
110 /*****************************************************************************
111  * aout_Open : creates a handle and opens an alsa device
112  *****************************************************************************
113  * This function opens an alsa device, through the alsa API
114  *****************************************************************************/
115 static int aout_Open( aout_thread_t *p_aout )
116 {
117     /* Allows user to choose which ALSA device to use */
118     char  psz_alsadev[128];
119     char *psz_device, *psz_userdev;
120     int   i_ret;
121
122     /* Allocate structures */
123     p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
124     if( p_aout->p_sys == NULL )
125     {
126         msg_Err( p_aout, "out of memory" );
127         return -1;
128     }
129
130     /* Read in ALSA device preferences from configuration */
131     psz_userdev = config_GetPsz( p_aout, "alsa-device" );
132
133     if( psz_userdev )
134     {
135         psz_device = psz_userdev;
136     }
137     else
138     {
139         /* Use the internal logic to decide on the device name */
140         if( p_aout->i_format != AOUT_FMT_AC3 )
141         {
142             psz_device = "default";
143         }
144         else
145         {
146             unsigned char s[4];
147             s[0] = IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO;
148             s[1] = IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER;
149             s[2] = 0;
150             s[3] = IEC958_AES3_CON_FS_48000;
151             sprintf( psz_alsadev,
152                      "iec958:AES0=0x%x,AES1=0x%x,AES2=0x%x,AES3=0x%x",
153                      s[0], s[1], s[2], s[3] );
154             psz_device = psz_alsadev;
155         }
156     }
157
158     /* Open device */
159     i_ret = snd_pcm_open( &(p_aout->p_sys->p_alsa_handle),
160                           psz_device, SND_PCM_STREAM_PLAYBACK, 0);
161     if( i_ret != 0 )
162     {
163         msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
164                          psz_device, snd_strerror(i_ret) );
165         if( psz_userdev )
166         {
167             free( psz_userdev );
168         }
169
170         return -1;
171     }
172
173     if( psz_userdev )
174     {
175         free( psz_userdev );
176     }
177
178     return 0;
179 }
180
181 /*****************************************************************************
182  * aout_SetFormat : sets the alsa output format
183  *****************************************************************************
184  * This function prepares the device, sets the rate, format, the mode
185  * ( "play as soon as you have data" ), and buffer information.
186  *****************************************************************************/
187 static int aout_SetFormat( aout_thread_t *p_aout )
188 {
189     int i_rv;
190     int i_format;
191
192     snd_pcm_hw_params_t *p_hw;
193     snd_pcm_sw_params_t *p_sw;
194
195     snd_pcm_hw_params_alloca(&p_hw);
196     snd_pcm_sw_params_alloca(&p_sw);
197
198     /* default value for snd_pcm_hw_params_set_buffer_time_near() */
199     p_aout->p_sys->buffer_time = AOUT_BUFFER_DURATION;
200
201     switch (p_aout->i_format)
202     {
203         case AOUT_FMT_S16_LE:
204             i_format = SND_PCM_FORMAT_S16_LE;
205             p_aout->p_sys->bytes_per_sample = 2;
206             break;
207
208         case AOUT_FMT_AC3:
209             i_format = SND_PCM_FORMAT_S16_LE;
210             p_aout->p_sys->bytes_per_sample = 2;
211             /* buffer_time must be 500000 to avoid a system crash */
212             p_aout->p_sys->buffer_time = 500000;
213             break;
214
215         default:
216             i_format = SND_PCM_FORMAT_S16_BE;
217             p_aout->p_sys->bytes_per_sample = 2;
218             break;
219     }
220
221     p_aout->p_sys->samples_per_frame = p_aout->i_channels;
222     p_aout->p_sys->bytes_per_frame = p_aout->p_sys->samples_per_frame *
223                                      p_aout->p_sys->bytes_per_sample;
224
225     i_rv = snd_pcm_hw_params_any( p_aout->p_sys->p_alsa_handle, p_hw );
226     if( i_rv < 0 )
227     {
228         msg_Err( p_aout, "unable to retrieve initial parameters" );
229         return -1;
230     }
231
232     i_rv = snd_pcm_hw_params_set_access( p_aout->p_sys->p_alsa_handle, p_hw,
233                                          SND_PCM_ACCESS_RW_INTERLEAVED );
234     if( i_rv < 0 )
235     {
236         msg_Err( p_aout, "unable to set interleaved stream format" );
237         return -1;
238     }
239
240     i_rv = snd_pcm_hw_params_set_format( p_aout->p_sys->p_alsa_handle,
241                                          p_hw, i_format );
242     if( i_rv < 0 )
243     {
244         msg_Err( p_aout, "unable to set stream sample size and word order" );
245         return -1;
246     }
247
248     i_rv = snd_pcm_hw_params_set_channels( p_aout->p_sys->p_alsa_handle, p_hw,
249                                            p_aout->i_channels );
250     if( i_rv < 0 )
251     {
252         msg_Err( p_aout, "unable to set number of output channels" );
253         return -1;
254     }
255
256     i_rv = snd_pcm_hw_params_set_rate_near( p_aout->p_sys->p_alsa_handle, p_hw,
257                                             p_aout->i_rate, 0 );
258     if( i_rv < 0 )
259     {
260         msg_Err( p_aout, "unable to set sample rate" );
261         return -1;
262     }
263     p_aout->p_sys->rate = i_rv;
264
265     i_rv = snd_pcm_hw_params_set_buffer_time_near( p_aout->p_sys->p_alsa_handle,
266                                                    p_hw,
267                                                    p_aout->p_sys->buffer_time,
268                                                    0 );
269     if( i_rv < 0 )
270     {
271         msg_Err( p_aout, "unable to set buffer time" );
272         return -1;
273     }
274     p_aout->p_sys->buffer_time = i_rv;
275
276     i_rv = snd_pcm_hw_params_set_period_time_near( p_aout->p_sys->p_alsa_handle,
277          p_hw, p_aout->p_sys->buffer_time / p_aout->p_sys->bytes_per_frame, 0 );
278     if( i_rv < 0 )
279     {
280         msg_Err( p_aout, "unable to set period time" );
281         return -1;
282     }
283     p_aout->p_sys->period_time = i_rv;
284
285     i_rv = snd_pcm_hw_params(p_aout->p_sys->p_alsa_handle, p_hw);
286     if (i_rv < 0)
287     {
288         msg_Err( p_aout, "unable to set hardware configuration" );
289         return -1;
290     }
291
292     p_aout->p_sys->chunk_size = snd_pcm_hw_params_get_period_size( p_hw, 0 );
293     p_aout->p_sys->buffer_size = snd_pcm_hw_params_get_buffer_size( p_hw );
294
295     snd_pcm_sw_params_current( p_aout->p_sys->p_alsa_handle, p_sw );
296     i_rv = snd_pcm_sw_params_set_sleep_min( p_aout->p_sys->p_alsa_handle, p_sw,
297                                             0 );
298
299     i_rv = snd_pcm_sw_params_set_avail_min( p_aout->p_sys->p_alsa_handle, p_sw,
300                                             p_aout->p_sys->chunk_size );
301
302     /* Worked with the CVS version but not with 0.9beta3
303     i_rv = snd_pcm_sw_params_set_start_threshold( p_aout->p_sys->p_alsa_handle,
304                                             p_sw, p_aout->p_sys->buffer_size );
305
306     i_rv = snd_pcm_sw_params_set_stop_threshold( p_aout->p_sys->p_alsa_handle,
307                                              p_sw, p_aout->p_sys->buffer_size);
308     */
309     i_rv = snd_pcm_sw_params( p_aout->p_sys->p_alsa_handle, p_sw );
310     if( i_rv < 0 )
311     {
312         msg_Err( p_aout, "unable to set software configuration" );
313         return -1;
314     }
315
316     return 0;
317 }
318
319 /*****************************************************************************
320  * aout_HandleXrun : reprepare the output
321  *****************************************************************************
322  * When buffer gets empty, the driver goes in "Xrun" state, where it needs
323  * to be reprepared before playing again
324  *****************************************************************************/
325 static void aout_HandleXrun(aout_thread_t *p_aout)
326 {
327     int i_rv;
328
329     msg_Err( p_aout, "resetting output after buffer underrun" );
330
331 //    i_rv = snd_pcm_reset( p_aout->p_sys->p_alsa_handle );
332     i_rv = snd_pcm_prepare( p_aout->p_sys->p_alsa_handle );
333     if( i_rv < 0 )
334     {
335         msg_Err( p_aout, "unable to recover from buffer underrun (%s)",
336                          snd_strerror( i_rv ) );
337     }
338 }
339
340
341 /*****************************************************************************
342  * aout_BufInfo: buffer status query
343  *****************************************************************************
344  * This function returns the number of used byte in the queue.
345  * It also deals with errors : indeed if the device comes to run out
346  * of data to play, it switches to the "underrun" status. It has to
347  * be flushed and re-prepared
348  *****************************************************************************/
349 static int aout_GetBufInfo( aout_thread_t *p_aout, int i_buffer_limit )
350 {
351     snd_pcm_status_t *p_status;
352     int i_alsa_get_status_returns;
353
354     snd_pcm_status_alloca( &p_status );
355
356     i_alsa_get_status_returns = snd_pcm_status( p_aout->p_sys->p_alsa_handle,
357                                                 p_status );
358
359     if( i_alsa_get_status_returns )
360     {
361         msg_Err( p_aout, "failed getting alsa buffer info (%s)",
362                          snd_strerror ( i_alsa_get_status_returns ) );
363         return ( -1 );
364     }
365
366     switch( snd_pcm_status_get_state( p_status ) )
367     {
368         case SND_PCM_STATE_XRUN :
369             aout_HandleXrun( p_aout );
370             break;
371
372         case SND_PCM_STATE_OPEN:
373         case SND_PCM_STATE_PREPARED:
374         case SND_PCM_STATE_RUNNING:
375             break;
376
377         default:
378             msg_Err( p_aout, "unhandled condition %i",
379                              snd_pcm_status_get_state( p_status ) );
380             break;
381     }
382
383     return snd_pcm_status_get_avail(p_status) * p_aout->p_sys->bytes_per_frame;
384 }
385
386 /*****************************************************************************
387  * aout_Play : plays a sample
388  *****************************************************************************
389  * Plays a sample using the snd_pcm_writei function from the alsa API
390  *****************************************************************************/
391 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
392 {
393     snd_pcm_uframes_t tot_frames;
394     snd_pcm_uframes_t frames_left;
395     snd_pcm_uframes_t rv;
396
397     tot_frames = i_size / p_aout->p_sys->bytes_per_frame;
398     frames_left = tot_frames;
399
400     while( frames_left > 0 )
401     {
402         rv = snd_pcm_writei( p_aout->p_sys->p_alsa_handle, buffer +
403                              (tot_frames - frames_left) *
404                              p_aout->p_sys->bytes_per_frame, frames_left );
405
406         if( (signed int) rv < 0 )
407         {
408             msg_Err( p_aout, "failed writing to output (%s)",
409                              snd_strerror( rv ) );
410             return;
411         }
412
413         frames_left -= rv;
414     }
415 }
416
417 /*****************************************************************************
418  * aout_Close : close the Alsa device
419  *****************************************************************************/
420 static void aout_Close( aout_thread_t *p_aout )
421 {
422     int i_close_returns;
423
424     i_close_returns = snd_pcm_close( p_aout->p_sys->p_alsa_handle );
425
426     if( i_close_returns )
427     {
428         msg_Err( p_aout, "failed closing ALSA device (%s)",
429                          snd_strerror( i_close_returns ) );
430     }
431
432     free( p_aout->p_sys );
433 }
434