]> git.sesse.net Git - vlc/blob - plugins/alsa/aout_alsa.c
5bcd03e95255b9102351471d128a18dde2c459a0
[vlc] / plugins / alsa / aout_alsa.c
1 /*****************************************************************************
2  * aout_alsa.c : Alsa functions library
3  *****************************************************************************
4  * Copyright (C) 2000 VideoLAN
5  * $Id: aout_alsa.c,v 1.15 2001/05/11 01:03:14 henri 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 #define MODULE_NAME alsa
26 #include "modules_inner.h"
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31
32 #include "defs.h"
33
34 #include <errno.h>                                                 /* ENOMEM */
35 #include <string.h>                                            /* strerror() */
36 #include <stdio.h>                                           /* "intf_msg.h" */
37 #include <stdlib.h>                            /* calloc(), malloc(), free() */
38
39 #include <sys/asoundlib.h>
40
41 #include "config.h"
42 #include "common.h"                                     /* boolean_t, byte_t */
43 #include "threads.h"
44 #include "mtime.h"
45 #include "tests.h"
46
47 #include "audio_output.h"                                   /* aout_thread_t */
48
49 #include "intf_msg.h"                        /* intf_DbgMsg(), intf_ErrMsg() */
50 #include "main.h"
51
52 #include "modules.h"
53
54 typedef struct alsa_device_s
55 {
56     int i_num;
57 } alsa_device_t;
58
59 typedef struct alsa_card_s
60 {
61     int i_num;
62 } alsa_card_t;
63
64 /* here we store plugin dependant informations */
65
66 typedef struct aout_sys_s
67 {
68     snd_pcm_t   * p_alsa_handle;
69     unsigned long buffer_time;
70     unsigned long period_time;
71     unsigned long chunk_size;
72     unsigned long buffer_size;
73     unsigned long rate;
74     unsigned int  bytes_per_sample;
75     unsigned int  samples_per_frame;
76     unsigned int  bytes_per_frame;
77 } aout_sys_t;
78
79
80 /*****************************************************************************
81  * aout_Probe: probes the audio device and return a score
82  *****************************************************************************
83  * This function tries to open the dps and returns a score to the plugin
84  * manager so that it can make its choice.
85  *****************************************************************************/
86 static int aout_Probe( probedata_t *p_data )
87 {
88     int i_open_return, i_close_return;
89     aout_sys_t local_sys;
90     
91     /* Open device */
92     i_open_return = snd_pcm_open( &(local_sys.p_alsa_handle), "plug:0,0", 
93                                   SND_PCM_STREAM_PLAYBACK, 0 );
94     if( i_open_return )
95     {
96         intf_ErrMsg( "Aout_alsa: error opening alsa device in aout_probe(%d)"
97                      " : %s", i_open_return, snd_strerror( i_open_return ) );
98         intf_WarnMsg( 1, "Aout_alsa : module scored 0" );
99         return ( 0 );
100     }
101
102     /* Close it */
103     i_close_return = snd_pcm_close( local_sys.p_alsa_handle );
104     
105     if( i_close_return )
106     {
107         intf_ErrMsg( "Aout_alsa: error closing alsa device in aout_probe(%d)"
108                      " : %s", i_close_return, snd_strerror( i_close_return ) );
109         intf_WarnMsg( 1, "Aout_alsa : module scored 0" );
110         return( 0 );
111     }
112     
113     if( TestMethod( AOUT_METHOD_VAR, "alsa" ) )
114     {
115         intf_WarnMsg( 1, "Aout_alsa : module scored 999" );
116         return( 999 );
117     }
118
119     /* And return score */
120     return( 100 );
121 }    
122
123 /*****************************************************************************
124  * aout_Open : creates a handle and opens an alsa device
125  *****************************************************************************
126  * This function opens an alsa device, through the alsa API
127  *****************************************************************************/
128 static int aout_Open( aout_thread_t *p_aout )
129 {
130
131     int i_open_returns;
132     
133     /* Allocate structures */
134     p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
135     if( p_aout->p_sys == NULL )
136     {
137         intf_ErrMsg( "Aout_alsa : Could not allocate memory : %s", 
138                      strerror(ENOMEM) );
139         return( 1 );
140     }
141
142     p_aout->i_format   = AOUT_FORMAT_DEFAULT;
143     p_aout->i_channels = 1 + main_GetIntVariable( AOUT_STEREO_VAR,
144                                                   AOUT_STEREO_DEFAULT );
145     p_aout->l_rate     = main_GetIntVariable( AOUT_RATE_VAR,
146                                               AOUT_RATE_DEFAULT );
147
148     /* Open device */
149     if( ( i_open_returns = snd_pcm_open(&(p_aout->p_sys->p_alsa_handle), 
150                                         "plug:0,0", 
151                                         SND_PCM_STREAM_PLAYBACK, 0) ) )
152     {
153         intf_ErrMsg( "Could not open alsa device; exit = %i",
154                       i_open_returns );
155         intf_ErrMsg( "This means : %s", snd_strerror(i_open_returns) );
156         return( -1 );
157     }
158
159     intf_DbgMsg( "Aout_alsa : Alsa device successfully opened" );
160     return( 0 );
161 }
162
163
164 /*****************************************************************************
165  * aout_SetFormat : sets the alsa output format
166  *****************************************************************************
167  * This function prepares the device, sets the rate, format, the mode
168  * ( "play as soon as you have data" ), and buffer information.
169  *****************************************************************************/
170 static int aout_SetFormat( aout_thread_t *p_aout )
171 {
172     
173     int i_rv;
174     int i_format;
175     
176     snd_pcm_hw_params_t *p_hw;
177     snd_pcm_sw_params_t *p_sw;
178     
179     snd_pcm_hw_params_alloca(&p_hw);
180     snd_pcm_sw_params_alloca(&p_sw);
181
182     switch (p_aout->i_format) {
183         case AOUT_FMT_S16_LE:
184             i_format = SND_PCM_FORMAT_S16_LE;
185             p_aout->p_sys->bytes_per_sample = 2;
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         intf_ErrMsg( "Aout_alsa: 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         intf_ErrMsg( "Aout_alsa: 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         intf_ErrMsg( "Aout_alsa: Unable to set stream sample size and word" 
218                      " order." );
219         return( -1 );
220     }
221
222     i_rv = snd_pcm_hw_params_set_channels( p_aout->p_sys->p_alsa_handle, p_hw, 
223                                            p_aout->i_channels );
224     if( i_rv < 0 )
225     {
226         intf_ErrMsg( "Aout_alsa: Unable to set number of output channels." );
227         return( -1 );
228     }
229     
230     i_rv = snd_pcm_hw_params_set_rate_near( p_aout->p_sys->p_alsa_handle, p_hw, 
231                                             p_aout->l_rate, 0 );
232     if( i_rv < 0 )
233     {
234         intf_ErrMsg( "Aout_alsa: Unable to set sample rate." );
235         return( -1 );
236     }
237     else 
238         p_aout->p_sys->rate = i_rv;    
239         
240     i_rv = snd_pcm_hw_params_set_buffer_time_near( p_aout->p_sys->p_alsa_handle,
241                                                    p_hw, AOUT_BUFFER_DURATION, 
242                                                    0 );
243     if( i_rv < 0 )
244     {
245         intf_ErrMsg( "Aout_alsa: Unable to set buffer time." );
246         return( -1 );
247     }
248     else
249         p_aout->p_sys->buffer_time = i_rv;
250     
251     i_rv = snd_pcm_hw_params_set_period_time_near( p_aout->p_sys->p_alsa_handle,
252          p_hw, p_aout->p_sys->buffer_time / p_aout->p_sys->bytes_per_frame, 0 );
253     if( i_rv < 0 )
254     {
255         intf_ErrMsg( "Aout_alsa: Unable to set period time." );
256         return( -1 );
257     }
258     else
259         p_aout->p_sys->period_time = i_rv;
260     
261     i_rv = snd_pcm_hw_params(p_aout->p_sys->p_alsa_handle, p_hw);
262     if (i_rv < 0)
263     {
264         intf_ErrMsg( "Aout_alsa: Unable to set hardware configuration." );
265         return( -1 );
266     }
267     
268     p_aout->p_sys->chunk_size = snd_pcm_hw_params_get_period_size( p_hw, 0 );
269         p_aout->p_sys->buffer_size = snd_pcm_hw_params_get_buffer_size( p_hw );
270
271         snd_pcm_sw_params_current( p_aout->p_sys->p_alsa_handle, p_sw );
272         i_rv = snd_pcm_sw_params_set_sleep_min( p_aout->p_sys->p_alsa_handle, p_sw, 
273                                             0 );
274
275         i_rv = snd_pcm_sw_params_set_avail_min( p_aout->p_sys->p_alsa_handle, p_sw,
276                                             p_aout->p_sys->chunk_size );
277
278     /* Worked with the CVS version but not with 0.9beta3
279         i_rv = snd_pcm_sw_params_set_start_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_set_stop_threshold( p_aout->p_sys->p_alsa_handle, 
283                                              p_sw, p_aout->p_sys->buffer_size);
284     */
285         i_rv = snd_pcm_sw_params( p_aout->p_sys->p_alsa_handle, p_sw );
286     if( i_rv < 0 )
287     {
288                 intf_ErrMsg( "Aout_alsa: Unable to set software configuration." );
289         return( -1 );
290         }
291
292
293     return( 0 );
294 }
295
296 /*****************************************************************************
297  * static void aout_HandleXrun : reprepare the output
298  *****************************************************************************
299  * When buffer gets empty, the driver goes in "Xrun" state, where it needs
300  * to be reprepared before playing again
301  *****************************************************************************/
302 static void aout_HandleXrun(aout_thread_t *p_aout)
303 {
304     int i_rv;
305
306     intf_ErrMsg( "Aout_alsa: resetting output after buffer underrun." );
307     
308     i_rv = snd_pcm_reset( p_aout->p_sys->p_alsa_handle );
309     i_rv = snd_pcm_prepare( p_aout->p_sys->p_alsa_handle );
310     if( i_rv < 0 )
311         intf_ErrMsg( "Aout_alsa: Unable to recover from buffer underrun: %s", 
312                      snd_strerror( i_rv ) );
313 }
314
315
316 /*****************************************************************************
317  * aout_BufInfo: buffer status query
318  *****************************************************************************
319  * This function returns the number of used byte in the queue.
320  * It also deals with errors : indeed if the device comes to run out
321  * of data to play, it switches to the "underrun" status. It has to
322  * be flushed and re-prepared
323  *****************************************************************************/
324 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
325 {
326     snd_pcm_status_t *p_status;
327     int i_alsa_get_status_returns;
328     
329     snd_pcm_status_alloca( &p_status );
330
331     i_alsa_get_status_returns = snd_pcm_status( p_aout->p_sys->p_alsa_handle, 
332                                                 p_status );
333
334     if( i_alsa_get_status_returns )
335     {
336         intf_ErrMsg ( "Aout_alsa: Error getting alsa buffer info (%d) : %s",
337                       i_alsa_get_status_returns,
338                       snd_strerror ( i_alsa_get_status_returns ) );
339         return ( -1 );
340     }
341
342     switch( snd_pcm_status_get_state( p_status ) )
343     {
344         case SND_PCM_STATE_XRUN :
345             aout_HandleXrun( p_aout );
346             break;
347
348         case SND_PCM_STATE_OPEN:
349         case SND_PCM_STATE_PREPARED:
350         case SND_PCM_STATE_RUNNING:
351             break;
352             
353         default:
354             intf_ErrMsg( "Aout_alsa: Encountered unhandled condition %i!", 
355                          snd_pcm_status_get_state( p_status ) );
356             break;
357     } 
358
359     return( snd_pcm_status_get_avail(p_status) * 
360             p_aout->p_sys->bytes_per_frame );
361 }
362
363 /*****************************************************************************
364  * aout_Play : plays a sample
365  *****************************************************************************
366  * Plays a sample using the snd_pcm_writei function from the alsa API
367  *****************************************************************************/
368 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
369 {
370     snd_pcm_uframes_t tot_frames;
371     snd_pcm_uframes_t frames_left;
372     snd_pcm_uframes_t rv;
373     
374     tot_frames = i_size / p_aout->p_sys->bytes_per_frame;
375     frames_left = tot_frames;
376
377     while( frames_left > 0 )
378     {        
379         rv = snd_pcm_writei( p_aout->p_sys->p_alsa_handle, buffer + 
380                              (tot_frames - frames_left) * 
381                              p_aout->p_sys->bytes_per_frame, frames_left );
382
383         if( (signed int) rv < 0 )
384         {
385             intf_ErrMsg( "Aout_alsa: error writing to output: %s", 
386                          snd_strerror( rv ) );
387             return;
388         }
389
390         frames_left -= rv;
391     }
392 }
393
394 /*****************************************************************************
395  * aout_Close : close the Alsa device
396  *****************************************************************************/
397 static void aout_Close( aout_thread_t *p_aout )
398 {
399     int i_close_returns;
400
401     i_close_returns = snd_pcm_close( p_aout->p_sys->p_alsa_handle );
402
403     if( i_close_returns )
404     {
405         intf_ErrMsg( "Aout_alsa: error closing alsa device (%d): %s",
406                      i_close_returns, snd_strerror( i_close_returns ) );
407     }
408
409     free( p_aout->p_sys );
410     
411     intf_DbgMsg( "Aout_alsa : Alsa device closed" );
412 }
413
414 /*****************************************************************************
415  * Functions exported as capabilities. They are declared as static so that
416  * we don't pollute the namespace too much.
417  *****************************************************************************/
418 void _M( aout_getfunctions )( function_list_t * p_function_list )
419 {
420     p_function_list->pf_probe = aout_Probe;
421     p_function_list->functions.aout.pf_open = aout_Open;
422     p_function_list->functions.aout.pf_setformat = aout_SetFormat;
423     p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
424     p_function_list->functions.aout.pf_play = aout_Play;
425     p_function_list->functions.aout.pf_close = aout_Close;
426 }