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