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 /*****************************************************************************
26 *****************************************************************************/
30 #include <errno.h> /* ENOMEM */
31 #include <fcntl.h> /* open(), O_WRONLY */
32 #include <sys/ioctl.h> /* ioctl() */
33 #include <string.h> /* strerror() */
34 #include <unistd.h> /* write(), close() */
35 #include <stdio.h> /* "intf_msg.h" */
36 #include <stdlib.h> /* calloc(), malloc(), free() */
38 #include <sys/asoundlib.h>
39 #include <linux/asound.h>
42 #include "common.h" /* boolean_t, byte_t */
47 #include "audio_output.h" /* aout_thread_t */
49 #include "intf_msg.h" /* intf_DbgMsg(), intf_ErrMsg() */
55 typedef struct alsa_device_s
60 typedef struct alsa_card_s
65 /* here we store plugin dependant informations */
67 typedef struct aout_sys_s
69 snd_pcm_t * p_alsa_handle;
70 alsa_device_t * p_alsa_device;
71 alsa_card_t * p_alsa_card;
72 snd_pcm_channel_params_t s_alsa_channel_params;
73 snd_pcm_format_t s_alsa_format;
78 /*****************************************************************************
79 * aout_AlsaOpen : creates a handle and opens an alsa device
80 *****************************************************************************/
82 int aout_AlsaOpen( aout_thread_t *p_aout )
87 /* Allocate structures */
88 p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
89 if( p_aout->p_sys == NULL )
91 intf_ErrMsg("error: %s\n", strerror(ENOMEM) );
95 p_aout->p_sys->p_alsa_device = malloc( sizeof( alsa_device_t) );
96 p_aout->p_sys->p_alsa_card = malloc( sizeof( alsa_device_t ) );
97 if( ( p_aout->p_sys->p_alsa_device == NULL ) ||
98 ( p_aout->p_sys->p_alsa_card == NULL ) )
100 intf_ErrMsg ( "error: %s\n", strerror(ENOMEM) );
105 p_aout->p_sys->p_alsa_device->i_num = 0;
106 p_aout->p_sys->p_alsa_card->i_num = 0;
107 /* FIXME : why not other format ? */
108 p_aout->i_format = AOUT_FMT_S16_LE;
109 /* FIXME : why always 2 channels ?*/
110 p_aout->i_channels = 2;
111 p_aout->l_rate = main_GetIntVariable( AOUT_RATE_VAR, AOUT_RATE_DEFAULT );
114 if ( ( i_open_returns = snd_pcm_open( &(p_aout->p_sys->p_alsa_handle),
115 p_aout->p_sys->p_alsa_card->i_num,
116 p_aout->p_sys->p_alsa_device->i_num,
117 SND_PCM_OPEN_PLAYBACK ) ) )
119 intf_ErrMsg ( "could not open alsa device; error code : %i\n",
126 intf_ErrMsg("Alsa device open \n\n");
130 /*****************************************************************************
131 * aout_AlsaSetFormat : sets the alsa output format
132 *****************************************************************************/
134 int aout_AlsaSetFormat ( aout_thread_t *p_aout )
137 int i_set_param_returns;
138 int i_prepare_playback_returns;
139 int i_playback_go_returns;
141 /* Fill with zeros */
142 memset(&p_aout->p_sys->s_alsa_channel_params,0,
143 sizeof(p_aout->p_sys->s_alsa_channel_params));
145 /* Fill the s_alsa_channel_params structure */
147 /* Tranfer mode and direction*/
148 p_aout->p_sys->s_alsa_channel_params.channel = SND_PCM_CHANNEL_PLAYBACK ;
149 p_aout->p_sys->s_alsa_channel_params.mode = SND_PCM_MODE_STREAM;
151 /* Format and rate */
152 p_aout->p_sys->s_alsa_channel_params.format.interleave = 1;
153 if ( p_aout->i_format == AOUT_FMT_S16_LE )
154 p_aout->p_sys->s_alsa_channel_params.format.format =
157 p_aout->p_sys->s_alsa_channel_params.format.format =
160 p_aout->p_sys->s_alsa_channel_params.format.rate = p_aout->l_rate;
161 p_aout->p_sys->s_alsa_channel_params.format.voices = p_aout->i_channels ;
163 /* When to start playing and when to stop */
164 p_aout->p_sys->s_alsa_channel_params.start_mode = SND_PCM_START_DATA;
165 p_aout->p_sys->s_alsa_channel_params.stop_mode = SND_PCM_STOP_STOP;
167 /* Buffer information . I have chosen the stream mode here
168 * instead of the block mode. I don't know whether i'm wrong
169 * but it seemed more logical */
170 p_aout->p_sys->s_alsa_channel_params.buf.stream.queue_size = 131072;
171 /* Fill with silence */
172 p_aout->p_sys->s_alsa_channel_params.buf.stream.fill = SND_PCM_FILL_NONE ;
173 p_aout->p_sys->s_alsa_channel_params.buf.stream.max_fill = 0 ;
175 /* Now we pass this to the driver */
176 i_set_param_returns = snd_pcm_channel_params (
177 p_aout->p_sys->p_alsa_handle,
178 &(p_aout->p_sys->s_alsa_channel_params) );
180 if ( i_set_param_returns )
182 intf_ErrMsg ( "ALSA_PLUGIN : Unable to set parameters; exit = %i\n",
183 i_set_param_returns );
184 intf_ErrMsg( "This means : %s\n\n",
185 snd_strerror( i_set_param_returns ) );
189 /* we shall now prepare the channel */
190 i_prepare_playback_returns =
191 snd_pcm_playback_prepare ( p_aout->p_sys->p_alsa_handle );
193 if ( i_prepare_playback_returns )
195 intf_ErrMsg ( "ALSA_PLUGIN : Unable to prepare channel : exit = %i\n",
196 i_prepare_playback_returns );
197 intf_ErrMsg( "This means : %s\n\n",
198 snd_strerror( i_set_param_returns ) );
204 i_playback_go_returns =
205 snd_pcm_playback_go ( p_aout->p_sys->p_alsa_handle );
206 if ( i_playback_go_returns )
208 intf_ErrMsg ( "ALSA_PLUGIN : Unable to prepare channel (bis) :
209 exit = %i\n", i_playback_go_returns );
210 intf_ErrMsg( "This means : %s\n\n",
211 snd_strerror( i_set_param_returns ) );
217 /*****************************************************************************
218 * aout_AlsaReset: resets the dsp
219 *****************************************************************************/
220 int aout_AlsaReset ( aout_thread_t *p_aout )
222 /* TODO : put something in here, such as close and open again
223 * or check status, drain, flush, .... */
227 /*****************************************************************************
228 * aout_AlsaSetChannels: sets mono, stereo and other modes
229 *****************************************************************************/
230 int aout_AlsaSetChannels ( aout_thread_t *p_aout )
232 /* TODO : normally, nothing
233 * everything should be done in the AlsaSetFormat, as far a I understand
234 * the alsa documentation
239 /*****************************************************************************
240 * aout_AlsaSetRate: sets the audio output rate
241 *****************************************************************************
242 * As in the previous function, the rate is supposed to be set in the
243 * AlsaSetFormat function
244 *****************************************************************************/
245 int aout_AlsaSetRate ( aout_thread_t *p_aout )
250 /*****************************************************************************
251 * aout_AlsaGetBufInfo: buffer status query
252 *****************************************************************************/
253 long aout_AlsaGetBufInfo ( aout_thread_t *p_aout, long l_buffer_limit )
255 snd_pcm_channel_status_t alsa_channel_status;
256 int i_alsa_get_status_returns;
258 memset (&alsa_channel_status, 0, sizeof(alsa_channel_status));
259 i_alsa_get_status_returns = snd_pcm_channel_status (
260 p_aout->p_sys->p_alsa_handle, &alsa_channel_status );
262 if ( i_alsa_get_status_returns )
264 intf_ErrMsg ( "Error getting alsa buffer info; exit=%i\n",
265 i_alsa_get_status_returns );
266 intf_ErrMsg ( "This means : %s \n\n",
267 snd_strerror ( i_alsa_get_status_returns ) );
271 switch (alsa_channel_status.status)
273 case SND_PCM_STATUS_NOTREADY : intf_ErrMsg("Status NOT READY \n \n");
275 case SND_PCM_STATUS_UNDERRUN : {
278 "Status UNDERRUN ... draining \n \n");
280 snd_pcm_playback_prepare(
281 p_aout->p_sys->p_alsa_handle );
282 if ( i_drain_returns )
285 "Error : could not flush : %i\n",
289 snd_strerror(i_drain_returns));
294 return ( alsa_channel_status.count );
297 /*****************************************************************************
298 * aout_AlsaPlaySamples
299 *****************************************************************************/
300 void aout_AlsaPlaySamples ( aout_thread_t *p_aout, byte_t *buffer, int i_size )
304 i_write_returns = (int) snd_pcm_write (
305 p_aout->p_sys->p_alsa_handle, (void *)buffer, (size_t) i_size );
307 if ( i_write_returns <= 0 )
309 intf_ErrMsg ( "Error writing blocks; exit=%i\n", i_write_returns );
310 intf_ErrMsg ( "This means : %s\n", snd_strerror( i_write_returns ) );
314 /*****************************************************************************
315 * aout_AlsaClose : close the Alsa device
316 *****************************************************************************/
317 void aout_AlsaClose ( aout_thread_t *p_aout )
321 i_close_returns = snd_pcm_close ( p_aout->p_sys->p_alsa_handle );
323 if ( i_close_returns )
325 intf_ErrMsg( "Error closing alsa device; exit=%i\n",i_close_returns );
326 intf_ErrMsg( "This means : %s\n\n",snd_strerror( i_close_returns ) );