]> git.sesse.net Git - vlc/blob - plugins/alsa/aout_alsa.c
* Fixed a few warnings with gcc 3.0.
[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.14 2001/05/06 04:32:02 sam Exp $
6  *
7  * Authors: Henri Fallon <henri@videolan.org>
8  * 
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 #define MODULE_NAME alsa
25 #include "modules_inner.h"
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30
31 #include "defs.h"
32
33 #include <errno.h>                                                 /* ENOMEM */
34 #include <string.h>                                            /* strerror() */
35 #include <stdio.h>                                           /* "intf_msg.h" */
36 #include <stdlib.h>                            /* calloc(), malloc(), free() */
37
38 #include <sys/asoundlib.h>
39
40 #include "config.h"
41 #include "common.h"                                     /* boolean_t, byte_t */
42 #include "threads.h"
43 #include "mtime.h"
44 #include "tests.h"
45
46 #include "audio_output.h"                                   /* aout_thread_t */
47
48 #include "intf_msg.h"                        /* intf_DbgMsg(), intf_ErrMsg() */
49 #include "main.h"
50
51 #include "modules.h"
52
53 typedef struct alsa_device_s
54 {
55     int i_num;
56 } alsa_device_t;
57
58 typedef struct alsa_card_s
59 {
60     int i_num;
61 } alsa_card_t;
62
63 /* here we store plugin dependant informations */
64
65 typedef struct aout_sys_s
66 {
67     snd_pcm_t         * p_alsa_handle;
68     alsa_device_t       s_alsa_device;
69     alsa_card_t         s_alsa_card;
70     snd_pcm_channel_params_t s_alsa_channel_params;
71     snd_pcm_format_t    s_alsa_format;
72 } aout_sys_t;
73
74 /*****************************************************************************
75  * Local prototypes
76  *****************************************************************************/
77 static int     aout_Probe       ( probedata_t *p_data );
78 static int     aout_Open        ( aout_thread_t *p_aout );
79 static int     aout_SetFormat   ( aout_thread_t *p_aout );
80 static long    aout_GetBufInfo  ( aout_thread_t *p_aout, long l_buffer_info );
81 static void    aout_Play        ( aout_thread_t *p_aout,
82                                           byte_t *buffer, int i_size );
83 static void    aout_Close       ( aout_thread_t *p_aout );
84
85 /*****************************************************************************
86  * Functions exported as capabilities. They are declared as static so that
87  * we don't pollute the namespace too much.
88  *****************************************************************************/
89 void _M( aout_getfunctions )( function_list_t * p_function_list )
90 {
91     p_function_list->pf_probe = aout_Probe;
92     p_function_list->functions.aout.pf_open = aout_Open;
93     p_function_list->functions.aout.pf_setformat = aout_SetFormat;
94     p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
95     p_function_list->functions.aout.pf_play = aout_Play;
96     p_function_list->functions.aout.pf_close = aout_Close;
97 }
98
99 /*****************************************************************************
100  * aout_Probe: probes the audio device and return a score
101  *****************************************************************************
102  * This function tries to open the dps and returns a score to the plugin
103  * manager so that it can make its choice.
104  *****************************************************************************/
105 static int aout_Probe( probedata_t *p_data )
106 {
107     int i_open_return,i_close_return;
108     aout_sys_t local_sys;
109     /* This is the same as the beginning of the aout_Open */
110     
111     /* Initialize  */
112     local_sys.s_alsa_device.i_num = 0;
113     local_sys.s_alsa_card.i_num = 0;
114
115     /* Open device */
116     i_open_return = snd_pcm_open( &(local_sys.p_alsa_handle),
117                      local_sys.s_alsa_card.i_num,
118                      local_sys.s_alsa_device.i_num,
119                      SND_PCM_OPEN_PLAYBACK );
120     if( i_open_return )
121     {
122         /* Score is zero */
123         return ( 0 );
124     }
125
126     /* Close it */
127     i_close_return = snd_pcm_close ( local_sys.p_alsa_handle );
128     
129     if( i_close_return )
130     {
131         intf_ErrMsg( "Error closing alsa device in aout_probe; exit=%i",
132                      i_close_return );
133         intf_ErrMsg( "This means : %s",snd_strerror( i_close_return ) );
134         return( 0 );
135     }
136     
137     if( TestMethod( AOUT_METHOD_VAR, "alsa" ) )
138     {
139         return( 999 );
140     }
141
142     /* And return score */
143     return( 50 );
144 }    
145
146 /*****************************************************************************
147  * aout_Open : creates a handle and opens an alsa device
148  *****************************************************************************
149  * This function opens an alsa device, through the alsa API
150  *****************************************************************************/
151 static int aout_Open( aout_thread_t *p_aout )
152 {
153
154     int i_open_returns;
155     
156     /* Allocate structures */
157     p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
158     if( p_aout->p_sys == NULL )
159     {
160         intf_ErrMsg( "Alsa Plugin : Could not allocate memory" );
161         intf_ErrMsg( "error: %s", strerror(ENOMEM) );
162         return( 1 );
163     }
164
165     /* Initialize  */
166     p_aout->p_sys->s_alsa_device.i_num = 0;
167     p_aout->p_sys->s_alsa_card.i_num = 0;
168     /* FIXME : why not other format ? */
169     p_aout->i_format = AOUT_FMT_S16_LE;   
170     /* FIXME : why always 2 channels ?*/
171     p_aout->i_channels = 2;
172     p_aout->l_rate = main_GetIntVariable( AOUT_RATE_VAR, AOUT_RATE_DEFAULT );
173     
174     /* Open device */
175     if( ( i_open_returns = snd_pcm_open( &(p_aout->p_sys->p_alsa_handle),
176                 p_aout->p_sys->s_alsa_card.i_num,
177                 p_aout->p_sys->s_alsa_device.i_num,
178                 SND_PCM_OPEN_PLAYBACK ) ) )
179     {
180         intf_ErrMsg( "Could not open alsa device; exit = %i",
181                       i_open_returns );
182         intf_ErrMsg( "This means : %s", snd_strerror(i_open_returns) );
183         return( -1 );
184     }
185
186     intf_DbgMsg( "Alsa plugin : Alsa device successfully opened" );
187     return( 0 );
188 }
189
190
191 /*****************************************************************************
192  * aout_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 aout_SetFormat( aout_thread_t *p_aout )
198 {
199     
200     int i_set_param_returns;
201     int i_prepare_playback_returns;
202     int i_playback_go_returns;
203
204     /* Fill with zeros */
205     memset( &p_aout->p_sys->s_alsa_channel_params,0,
206             sizeof( p_aout->p_sys->s_alsa_channel_params ) );
207     
208     /* Fill the s_alsa_channel_params structure */
209
210     /* Tranfer mode and direction*/    
211     p_aout->p_sys->s_alsa_channel_params.channel = SND_PCM_CHANNEL_PLAYBACK ;
212     p_aout->p_sys->s_alsa_channel_params.mode = SND_PCM_MODE_STREAM;
213     
214     /* Format and rate */
215     p_aout->p_sys->s_alsa_channel_params.format.interleave = 1;
216     if( p_aout->i_format == AOUT_FMT_S16_LE )
217         p_aout->p_sys->s_alsa_channel_params.format.format = 
218             SND_PCM_SFMT_S16_LE;
219     else
220         p_aout->p_sys->s_alsa_channel_params.format.format = 
221             SND_PCM_SFMT_S16_BE;
222     p_aout->p_sys->s_alsa_channel_params.format.rate = p_aout->l_rate;
223     p_aout->p_sys->s_alsa_channel_params.format.voices = p_aout->i_channels ;
224     
225     /* When to start playing and when to stop */
226     p_aout->p_sys->s_alsa_channel_params.start_mode = SND_PCM_START_DATA;
227     p_aout->p_sys->s_alsa_channel_params.stop_mode = SND_PCM_STOP_STOP;
228
229     /* Buffer information . I have chosen the stream mode here
230      * instead of the block mode. I don't know whether i'm wrong 
231      * but it seemed more logical */
232     /* TODO : find the best value to put here. Probably depending
233      * on many parameters */
234     p_aout->p_sys->s_alsa_channel_params.buf.stream.queue_size = 131072; 
235     
236     p_aout->p_sys->s_alsa_channel_params.buf.stream.fill = SND_PCM_FILL_NONE ;
237     p_aout->p_sys->s_alsa_channel_params.buf.stream.max_fill = 0 ; 
238   
239     /* Now we pass this to the driver */
240     i_set_param_returns = snd_pcm_channel_params( 
241             p_aout->p_sys->p_alsa_handle, 
242             &(p_aout->p_sys->s_alsa_channel_params) );
243     
244     if( i_set_param_returns )
245     {
246         intf_ErrMsg( "ALSA_PLUGIN : Unable to set parameters; exit = %i",
247                      i_set_param_returns );
248         intf_ErrMsg( "This means : %s",
249                      snd_strerror( i_set_param_returns ) );
250         return( -1 );
251     }
252
253     /* we shall now prepare the channel */
254     i_prepare_playback_returns = 
255         snd_pcm_playback_prepare( p_aout->p_sys->p_alsa_handle );
256
257     if( i_prepare_playback_returns )
258     {
259         intf_ErrMsg( "ALSA_PLUGIN : Unable to prepare channel : exit = %i",
260                       i_prepare_playback_returns );
261         intf_ErrMsg( "This means : %s",
262                       snd_strerror( i_set_param_returns ) );
263
264         return( -1 );
265     }
266     
267    /* then we may go */
268    i_playback_go_returns =
269        snd_pcm_playback_go( p_aout->p_sys->p_alsa_handle );
270     if( i_playback_go_returns )
271     {
272         intf_ErrMsg( "ALSA_PLUGIN : Unable to prepare channel (bis) : "
273                 "exit  = %i", i_playback_go_returns );
274         intf_ErrMsg( "This means : %s",
275                 snd_strerror( i_set_param_returns ) );
276         return( -1 );
277     }
278     return( 0 );
279 }
280
281 /*****************************************************************************
282  * aout_BufInfo: buffer status query
283  *****************************************************************************
284  * This function returns the number of used byte in the queue.
285  * It also deals with errors : indeed if the device comes to run out
286  * of data to play, it switches to the "underrun" status. It has to
287  * be flushed and re-prepared
288  *****************************************************************************/
289 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
290 {
291     snd_pcm_channel_status_t alsa_channel_status;
292     int i_alsa_get_status_returns;
293     
294     memset(&alsa_channel_status, 0, sizeof( alsa_channel_status ) );
295    
296     i_alsa_get_status_returns = snd_pcm_channel_status( 
297             p_aout->p_sys->p_alsa_handle, &alsa_channel_status );
298
299     if( i_alsa_get_status_returns )
300     {
301         intf_ErrMsg ( "Error getting alsa buffer info; exit=%i",
302                       i_alsa_get_status_returns );
303         intf_ErrMsg ( "This means : %s",
304                       snd_strerror ( i_alsa_get_status_returns ) );
305         return ( -1 );
306     }
307
308     switch( alsa_channel_status.status )
309     {
310     case SND_PCM_STATUS_NOTREADY : intf_ErrMsg("Status NOT READY");
311                                    break;
312     case SND_PCM_STATUS_UNDERRUN : {
313
314          int i_prepare_returns;
315          intf_ErrMsg( "Status UNDERRUN ... reseting queue ");
316          i_prepare_returns = snd_pcm_playback_prepare( 
317                              p_aout->p_sys->p_alsa_handle );
318          if ( i_prepare_returns )
319          {
320              intf_ErrMsg( "Error : could not flush : %i", i_prepare_returns );
321              intf_ErrMsg( "This means : %s", snd_strerror(i_prepare_returns) );
322          }
323          break;
324                                    }
325     } 
326     return(  alsa_channel_status.count );
327 }
328
329 /*****************************************************************************
330  * aout_Play : plays a sample
331  *****************************************************************************
332  * Plays a sample using the snd_pcm_write function from the alsa API
333  *****************************************************************************/
334 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
335 {
336     int i_write_returns;
337
338     i_write_returns = (int) snd_pcm_write (
339             p_aout->p_sys->p_alsa_handle, (void *) buffer, (size_t) i_size );
340
341     if( i_write_returns <= 0 )
342     {
343         intf_ErrMsg ( "Error writing blocks; exit=%i", i_write_returns );
344         intf_ErrMsg ( "This means : %s", snd_strerror( i_write_returns ) );
345     }
346 }
347
348 /*****************************************************************************
349  * aout_Close : close the Alsa device
350  *****************************************************************************/
351 static void aout_Close( aout_thread_t *p_aout )
352 {
353     int i_close_returns;
354
355     i_close_returns = snd_pcm_close( p_aout->p_sys->p_alsa_handle );
356
357     if( i_close_returns )
358     {
359         intf_ErrMsg( "Error closing alsa device; exit=%i",i_close_returns );
360         intf_ErrMsg( "This means : %s",snd_strerror( i_close_returns ) );
361     }
362     free( p_aout->p_sys );
363     
364     intf_DbgMsg( "Alsa plugin : Alsa device closed");
365 }
366