1 /*****************************************************************************
2 * aout_alsa.c : Alsa functions library
3 *****************************************************************************
4 * Copyright (C) 2000 VideoLAN
7 * Henri Fallon <henri@videolan.org>
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.
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.
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 *****************************************************************************/
24 #define MODULE_NAME alsa
26 /*****************************************************************************
28 *****************************************************************************/
32 #include <errno.h> /* ENOMEM */
33 #include <string.h> /* strerror() */
34 #include <stdio.h> /* "intf_msg.h" */
35 #include <stdlib.h> /* calloc(), malloc(), free() */
37 #include <sys/asoundlib.h>
38 #include <linux/asound.h>
41 #include "common.h" /* boolean_t, byte_t */
46 #include "audio_output.h" /* aout_thread_t */
48 #include "intf_msg.h" /* intf_DbgMsg(), intf_ErrMsg() */
52 #include "modules_inner.h"
56 typedef struct alsa_device_s
61 typedef struct alsa_card_s
66 /* here we store plugin dependant informations */
68 typedef struct aout_sys_s
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;
77 /*****************************************************************************
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 );
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 )
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;
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 )
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 */
117 local_sys.s_alsa_device.i_num = 0;
118 local_sys.s_alsa_card.i_num = 0;
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 );
132 i_close_return = snd_pcm_close ( local_sys.p_alsa_handle );
136 intf_ErrMsg( "Error closing alsa device in aout_probe; exit=%i",
138 intf_ErrMsg( "This means : %s",snd_strerror( i_close_return ) );
142 if( TestMethod( AOUT_METHOD_VAR, "alsa" ) )
147 /* And return score */
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 )
161 /* Allocate structures */
162 p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
163 if( p_aout->p_sys == NULL )
165 intf_ErrMsg( "Alsa Plugin : Could not allocate memory" );
166 intf_ErrMsg( "error: %s", strerror(ENOMEM) );
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 );
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 ) ) )
185 intf_ErrMsg( "Could not open alsa device; exit = %i",
187 intf_ErrMsg( "This means : %s", snd_strerror(i_open_returns) );
191 intf_DbgMsg( "Alsa plugin : Alsa device successfully opened" );
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 )
205 int i_set_param_returns;
206 int i_prepare_playback_returns;
207 int i_playback_go_returns;
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 ) );
213 /* Fill the s_alsa_channel_params structure */
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;
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 =
225 p_aout->p_sys->s_alsa_channel_params.format.format =
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 ;
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;
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;
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 ;
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) );
249 if( i_set_param_returns )
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 ) );
258 /* we shall now prepare the channel */
259 i_prepare_playback_returns =
260 snd_pcm_playback_prepare( p_aout->p_sys->p_alsa_handle );
262 if( i_prepare_playback_returns )
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 ) );
273 i_playback_go_returns =
274 snd_pcm_playback_go( p_aout->p_sys->p_alsa_handle );
275 if( i_playback_go_returns )
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 ) );
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 )
296 snd_pcm_channel_status_t alsa_channel_status;
297 int i_alsa_get_status_returns;
299 memset(&alsa_channel_status, 0, sizeof( alsa_channel_status ) );
301 i_alsa_get_status_returns = snd_pcm_channel_status(
302 p_aout->p_sys->p_alsa_handle, &alsa_channel_status );
304 if( i_alsa_get_status_returns )
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 ) );
313 switch( alsa_channel_status.status )
315 case SND_PCM_STATUS_NOTREADY : intf_ErrMsg("Status NOT READY");
317 case SND_PCM_STATUS_UNDERRUN : {
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 )
325 intf_ErrMsg( "Error : could not flush : %i", i_prepare_returns );
326 intf_ErrMsg( "This means : %s", snd_strerror(i_prepare_returns) );
331 return( alsa_channel_status.count );
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 )
343 i_write_returns = (int) snd_pcm_write (
344 p_aout->p_sys->p_alsa_handle, (void *) buffer, (size_t) i_size );
346 if( i_write_returns <= 0 )
348 intf_ErrMsg ( "Error writing blocks; exit=%i", i_write_returns );
349 intf_ErrMsg ( "This means : %s", snd_strerror( i_write_returns ) );
353 /*****************************************************************************
354 * aout_Close : close the Alsa device
355 *****************************************************************************/
356 static void aout_Close( aout_thread_t *p_aout )
360 i_close_returns = snd_pcm_close( p_aout->p_sys->p_alsa_handle );
362 if( i_close_returns )
364 intf_ErrMsg( "Error closing alsa device; exit=%i",i_close_returns );
365 intf_ErrMsg( "This means : %s",snd_strerror( i_close_returns ) );
367 free( p_aout->p_sys );
369 intf_DbgMsg( "Alsa plugin : Alsa device closed");