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