]> git.sesse.net Git - vlc/blob - plugins/alsa/aout_alsa.c
* Ported Glide and MGA plugins to the new module API. MGA never worked,
[vlc] / plugins / alsa / aout_alsa.c
1 /*****************************************************************************
2  * aout_alsa.c : Alsa functions library
3  *****************************************************************************
4  * Copyright (C) 2000 VideoLAN
5  *
6  * Authors:
7  *  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 #include <linux/asound.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     alsa_device_t       s_alsa_device;
70     alsa_card_t         s_alsa_card;
71     snd_pcm_channel_params_t s_alsa_channel_params;
72     snd_pcm_format_t    s_alsa_format;
73 } aout_sys_t;
74
75 /*****************************************************************************
76  * Local prototypes
77  *****************************************************************************/
78 static int     aout_Probe       ( probedata_t *p_data );
79 static int     aout_Open        ( aout_thread_t *p_aout );
80 static int     aout_SetFormat   ( aout_thread_t *p_aout );
81 static long    aout_GetBufInfo  ( aout_thread_t *p_aout, long l_buffer_info );
82 static void    aout_Play        ( aout_thread_t *p_aout,
83                                           byte_t *buffer, int i_size );
84 static void    aout_Close       ( aout_thread_t *p_aout );
85
86 /*****************************************************************************
87  * Functions exported as capabilities. They are declared as static so that
88  * we don't pollute the namespace too much.
89  *****************************************************************************/
90 void _M( aout_getfunctions )( function_list_t * p_function_list )
91 {
92     p_function_list->pf_probe = aout_Probe;
93     p_function_list->functions.aout.pf_open = aout_Open;
94     p_function_list->functions.aout.pf_setformat = aout_SetFormat;
95     p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
96     p_function_list->functions.aout.pf_play = aout_Play;
97     p_function_list->functions.aout.pf_close = aout_Close;
98 }
99
100 /*****************************************************************************
101  * aout_Probe: probes the audio device and return a score
102  *****************************************************************************
103  * This function tries to open the dps and returns a score to the plugin
104  * manager so that it can make its choice.
105  *****************************************************************************/
106 static int aout_Probe( probedata_t *p_data )
107 {
108     int i_open_return,i_close_return;
109     aout_sys_t local_sys;
110     /* This is the same as the beginning of the aout_Open */
111     
112     /* Initialize  */
113     local_sys.s_alsa_device.i_num = 0;
114     local_sys.s_alsa_card.i_num = 0;
115
116     /* Open device */
117     i_open_return = snd_pcm_open( &(local_sys.p_alsa_handle),
118                      local_sys.s_alsa_card.i_num,
119                      local_sys.s_alsa_device.i_num,
120                      SND_PCM_OPEN_PLAYBACK );
121     if( i_open_return )
122     {
123         /* Score is zero */
124         return ( 0 );
125     }
126
127     /* Close it */
128     i_close_return = snd_pcm_close ( local_sys.p_alsa_handle );
129     
130     if( i_close_return )
131     {
132         intf_ErrMsg( "Error closing alsa device in aout_probe; exit=%i",
133                      i_close_return );
134         intf_ErrMsg( "This means : %s",snd_strerror( i_close_return ) );
135         return( 0 );
136     }
137     
138     if( TestMethod( AOUT_METHOD_VAR, "alsa" ) )
139     {
140         return( 999 );
141     }
142
143     /* And return score */
144     return( 50 );
145 }    
146
147 /*****************************************************************************
148  * aout_Open : creates a handle and opens an alsa device
149  *****************************************************************************
150  * This function opens an alsa device, through the alsa API
151  *****************************************************************************/
152 static int aout_Open( aout_thread_t *p_aout )
153 {
154
155     int i_open_returns;
156     
157     /* Allocate structures */
158     p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
159     if( p_aout->p_sys == NULL )
160     {
161         intf_ErrMsg( "Alsa Plugin : Could not allocate memory" );
162         intf_ErrMsg( "error: %s", strerror(ENOMEM) );
163         return( 1 );
164     }
165
166     /* Initialize  */
167     p_aout->p_sys->s_alsa_device.i_num = 0;
168     p_aout->p_sys->s_alsa_card.i_num = 0;
169     /* FIXME : why not other format ? */
170     p_aout->i_format = AOUT_FMT_S16_LE;   
171     /* FIXME : why always 2 channels ?*/
172     p_aout->i_channels = 2;
173     p_aout->l_rate = main_GetIntVariable( AOUT_RATE_VAR, AOUT_RATE_DEFAULT );
174     
175     /* Open device */
176     if( ( i_open_returns = snd_pcm_open( &(p_aout->p_sys->p_alsa_handle),
177                 p_aout->p_sys->s_alsa_card.i_num,
178                 p_aout->p_sys->s_alsa_device.i_num,
179                 SND_PCM_OPEN_PLAYBACK ) ) )
180     {
181         intf_ErrMsg( "Could not open alsa device; exit = %i",
182                       i_open_returns );
183         intf_ErrMsg( "This means : %s", snd_strerror(i_open_returns) );
184         return( -1 );
185     }
186
187     intf_DbgMsg( "Alsa plugin : Alsa device successfully opened" );
188     return( 0 );
189 }
190
191
192 /*****************************************************************************
193  * aout_SetFormat : sets the alsa output format
194  *****************************************************************************
195  * This function prepares the device, sets the rate, format, the mode
196  * ("play as soon as you have data"), and buffer information.
197  *****************************************************************************/
198 static int aout_SetFormat( aout_thread_t *p_aout )
199 {
200     
201     int i_set_param_returns;
202     int i_prepare_playback_returns;
203     int i_playback_go_returns;
204
205     /* Fill with zeros */
206     memset( &p_aout->p_sys->s_alsa_channel_params,0,
207             sizeof( p_aout->p_sys->s_alsa_channel_params ) );
208     
209     /* Fill the s_alsa_channel_params structure */
210
211     /* Tranfer mode and direction*/    
212     p_aout->p_sys->s_alsa_channel_params.channel = SND_PCM_CHANNEL_PLAYBACK ;
213     p_aout->p_sys->s_alsa_channel_params.mode = SND_PCM_MODE_STREAM;
214     
215     /* Format and rate */
216     p_aout->p_sys->s_alsa_channel_params.format.interleave = 1;
217     if( p_aout->i_format == AOUT_FMT_S16_LE )
218         p_aout->p_sys->s_alsa_channel_params.format.format = 
219             SND_PCM_SFMT_S16_LE;
220     else
221         p_aout->p_sys->s_alsa_channel_params.format.format = 
222             SND_PCM_SFMT_S16_BE;
223     p_aout->p_sys->s_alsa_channel_params.format.rate = p_aout->l_rate;
224     p_aout->p_sys->s_alsa_channel_params.format.voices = p_aout->i_channels ;
225     
226     /* When to start playing and when to stop */
227     p_aout->p_sys->s_alsa_channel_params.start_mode = SND_PCM_START_DATA;
228     p_aout->p_sys->s_alsa_channel_params.stop_mode = SND_PCM_STOP_STOP;
229
230     /* Buffer information . I have chosen the stream mode here
231      * instead of the block mode. I don't know whether i'm wrong 
232      * but it seemed more logical */
233     /* TODO : find the best value to put here. Probably depending
234      * on many parameters */
235     p_aout->p_sys->s_alsa_channel_params.buf.stream.queue_size = 131072; 
236     
237     p_aout->p_sys->s_alsa_channel_params.buf.stream.fill = SND_PCM_FILL_NONE ;
238     p_aout->p_sys->s_alsa_channel_params.buf.stream.max_fill = 0 ; 
239   
240     /* Now we pass this to the driver */
241     i_set_param_returns = snd_pcm_channel_params( 
242             p_aout->p_sys->p_alsa_handle, 
243             &(p_aout->p_sys->s_alsa_channel_params) );
244     
245     if( i_set_param_returns )
246     {
247         intf_ErrMsg( "ALSA_PLUGIN : Unable to set parameters; exit = %i",
248                      i_set_param_returns );
249         intf_ErrMsg( "This means : %s",
250                      snd_strerror( i_set_param_returns ) );
251         return( -1 );
252     }
253
254     /* we shall now prepare the channel */
255     i_prepare_playback_returns = 
256         snd_pcm_playback_prepare( p_aout->p_sys->p_alsa_handle );
257
258     if( i_prepare_playback_returns )
259     {
260         intf_ErrMsg( "ALSA_PLUGIN : Unable to prepare channel : exit = %i",
261                       i_prepare_playback_returns );
262         intf_ErrMsg( "This means : %s",
263                       snd_strerror( i_set_param_returns ) );
264
265         return( -1 );
266     }
267     
268    /* then we may go */
269    i_playback_go_returns =
270        snd_pcm_playback_go( p_aout->p_sys->p_alsa_handle );
271     if( i_playback_go_returns )
272     {
273         intf_ErrMsg( "ALSA_PLUGIN : Unable to prepare channel (bis) : 
274                 exit  = %i", i_playback_go_returns );
275         intf_ErrMsg( "This means : %s",
276                 snd_strerror( i_set_param_returns ) );
277         return( -1 );
278     }
279     return( 0 );
280 }
281
282 /*****************************************************************************
283  * aout_BufInfo: buffer status query
284  *****************************************************************************
285  * This function returns the number of used byte in the queue.
286  * It also deals with errors : indeed if the device comes to run out
287  * of data to play, it switches to the "underrun" status. It has to
288  * be flushed and re-prepared
289  *****************************************************************************/
290 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
291 {
292     snd_pcm_channel_status_t alsa_channel_status;
293     int i_alsa_get_status_returns;
294     
295     memset(&alsa_channel_status, 0, sizeof( alsa_channel_status ) );
296    
297     i_alsa_get_status_returns = snd_pcm_channel_status( 
298             p_aout->p_sys->p_alsa_handle, &alsa_channel_status );
299
300     if( i_alsa_get_status_returns )
301     {
302         intf_ErrMsg ( "Error getting alsa buffer info; exit=%i",
303                       i_alsa_get_status_returns );
304         intf_ErrMsg ( "This means : %s",
305                       snd_strerror ( i_alsa_get_status_returns ) );
306         return ( -1 );
307     }
308
309     switch( alsa_channel_status.status )
310     {
311     case SND_PCM_STATUS_NOTREADY : intf_ErrMsg("Status NOT READY");
312                                    break;
313     case SND_PCM_STATUS_UNDERRUN : {
314
315          int i_prepare_returns;
316          intf_ErrMsg( "Status UNDERRUN ... reseting queue ");
317          i_prepare_returns = snd_pcm_playback_prepare( 
318                              p_aout->p_sys->p_alsa_handle );
319          if ( i_prepare_returns )
320          {
321              intf_ErrMsg( "Error : could not flush : %i", i_prepare_returns );
322              intf_ErrMsg( "This means : %s", snd_strerror(i_prepare_returns) );
323          }
324          break;
325                                    }
326     } 
327     return(  alsa_channel_status.count );
328 }
329
330 /*****************************************************************************
331  * aout_Play : plays a sample
332  *****************************************************************************
333  * Plays a sample using the snd_pcm_write function from the alsa API
334  *****************************************************************************/
335 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
336 {
337     int i_write_returns;
338
339     i_write_returns = (int) snd_pcm_write (
340             p_aout->p_sys->p_alsa_handle, (void *) buffer, (size_t) i_size );
341
342     if( i_write_returns <= 0 )
343     {
344         intf_ErrMsg ( "Error writing blocks; exit=%i", i_write_returns );
345         intf_ErrMsg ( "This means : %s", snd_strerror( i_write_returns ) );
346     }
347 }
348
349 /*****************************************************************************
350  * aout_Close : close the Alsa device
351  *****************************************************************************/
352 static void aout_Close( aout_thread_t *p_aout )
353 {
354     int i_close_returns;
355
356     i_close_returns = snd_pcm_close( p_aout->p_sys->p_alsa_handle );
357
358     if( i_close_returns )
359     {
360         intf_ErrMsg( "Error closing alsa device; exit=%i",i_close_returns );
361         intf_ErrMsg( "This means : %s",snd_strerror( i_close_returns ) );
362     }
363     free( p_aout->p_sys );
364     
365     intf_DbgMsg( "Alsa plugin : Alsa device closed");
366 }
367