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