1 /*
2  * madplay - MPEG audio decoder and player
3  * Copyright (C) 2000-2004 Robert Leslie
4  * ALSA audio output module (C) 2002 Hod McWuff
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * $Id: audio_alsa.c,v 1.6 2004/02/23 21:35:23 rob Exp $
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #	include "config.h"
25 #endif
26 
27 #include "global.h"
28 
29 #include <errno.h>
30 
31 #define ALSA_PCM_OLD_HW_PARAMS_API
32 #define ALSA_PCM_OLD_SW_PARAMS_API
33 #include <alsa/asoundlib.h>
34 
35 #include <mad.h>
36 
37 #include "audio.h"
38 
39 char *buf	= NULL;
40 int paused	= 0;
41 
42 int rate	= -1;
43 int channels	= -1;
44 int bitdepth	= -1;
45 int sample_size	= -1;
46 
47 int buffer_time		= 500000;
48 int period_time		= 100000;
49 char *defaultdev	= "plughw:0,0";
50 
51 snd_pcm_hw_params_t *alsa_hwparams;
52 snd_pcm_sw_params_t *alsa_swparams;
53 
54 snd_pcm_sframes_t buffer_size;
55 snd_pcm_sframes_t period_size;
56 
57 snd_pcm_format_t  alsa_format = -1;
58 snd_pcm_access_t  alsa_access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
59 
60 snd_pcm_t *alsa_handle		= NULL;
61 
62 static audio_pcmfunc_t *audio_pcm;
63 
64 static
set_hwparams(snd_pcm_t * handle,snd_pcm_hw_params_t * params,snd_pcm_access_t access)65 int set_hwparams(snd_pcm_t *handle,
66 		 snd_pcm_hw_params_t *params,
67 		 snd_pcm_access_t access)
68 {
69 	int err, dir;
70 
71 	/* choose all parameters */
72 	err = snd_pcm_hw_params_any(handle,params);
73 	if (err < 0) {
74 		printf("Access type not available for playback: %s\n", snd_strerror(err));
75 		return err;
76 	}
77 	/* set the sample format */
78 	err = snd_pcm_hw_params_set_format(handle, params, alsa_format);
79 	if (err < 0) {
80 		printf("Sample format not available for playback: %s\n", snd_strerror(err));
81 		return err;
82 	}
83 	/* set the count of channels */
84 	err = snd_pcm_hw_params_set_channels(handle, params, channels);
85 	if (err < 0) {
86 		printf("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
87 		return err;
88 	}
89 	/* set the stream rate */
90 	err = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0);
91 	if (err < 0) {
92 		printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
93 		return err;
94 	}
95 	if (err != rate) {
96 		printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
97 		return -EINVAL;
98 	}
99 	/* set buffer time */
100 	err = snd_pcm_hw_params_set_buffer_time_near(handle, params, buffer_time, &dir);
101 	if (err < 0) {
102 		printf("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
103 		return err;
104 	}
105 	buffer_size = snd_pcm_hw_params_get_buffer_size(params);
106 	/* set period time */
107 	err = snd_pcm_hw_params_set_period_time_near(handle, params, period_time, &dir);
108 	if (err < 0) {
109 		printf("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
110 		return err;
111 	}
112 	period_size = snd_pcm_hw_params_get_period_size(params, &dir);
113 	/* write the parameters to device */
114 	err = snd_pcm_hw_params(handle, params);
115 	if (err < 0) {
116 		printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
117 		return err;
118 	}
119 	return 0;
120 }
121 
122 static
set_swparams(snd_pcm_t * handle,snd_pcm_sw_params_t * params)123 int set_swparams(snd_pcm_t *handle,
124 		 snd_pcm_sw_params_t *params)
125 {
126 	int err;
127 
128         /* get current swparams */
129         err = snd_pcm_sw_params_current(handle, params);
130         if (err < 0) {
131                 printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
132                 return err;
133         }
134         /* start transfer when the buffer is full */
135         err = snd_pcm_sw_params_set_start_threshold(handle, params, buffer_size);
136         if (err < 0) {
137                 printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
138                 return err;
139 										        }
140         /* allow transfer when at least period_size samples can be processed */
141         err = snd_pcm_sw_params_set_avail_min(handle, params, period_size);
142         if (err < 0) {
143                 printf("Unable to set avail min for playback: %s\n", snd_strerror(err));
144                 return err;
145 												        }
146         /* align all transfers to 1 samples */
147         err = snd_pcm_sw_params_set_xfer_align(handle, params, 1);
148         if (err < 0) {
149                 printf("Unable to set transfer align for playback: %s\n", snd_strerror(err));
150                 return err;
151         }
152         /* write the parameters to device */
153         err = snd_pcm_sw_params(handle, params);
154         if (err < 0) {
155                 printf("Unable to set sw params for playback: %s\n", snd_strerror(err));
156                 return err;
157         }
158         return 0;
159 }
160 
161 
162 static
init(struct audio_init * init)163 int init(struct audio_init *init)
164 {
165 	int err;
166 
167 	if (init->path)
168 		err = snd_pcm_open(&alsa_handle, init->path, SND_PCM_STREAM_PLAYBACK, 0);
169 	else
170 		err = snd_pcm_open(&alsa_handle, defaultdev, SND_PCM_STREAM_PLAYBACK, 0);
171 
172 	if (err < 0) {
173 		audio_error=snd_strerror(err);
174 		return -1;
175 	}
176 
177 	return 0;
178 }
179 
180 static
config(struct audio_config * config)181 int config(struct audio_config *config)
182 {
183 	int err;
184 
185 	snd_pcm_hw_params_alloca(&alsa_hwparams);
186 	snd_pcm_sw_params_alloca(&alsa_swparams);
187 
188 	bitdepth	= config->precision;
189 	channels	= config->channels;
190 	rate		= config->speed;
191 
192 	if ( bitdepth == 0 )
193 		config->precision = bitdepth = 32;
194 
195 	switch (bitdepth)
196 	{
197 		case 8:
198 			alsa_format = SND_PCM_FORMAT_U8;
199 			audio_pcm   = audio_pcm_u8;
200 			break;
201 		case 16:
202 			alsa_format = SND_PCM_FORMAT_S16;
203 #if __BYTE_ORDER == __LITTLE_ENDIAN
204 			audio_pcm = audio_pcm_s16le;
205 #else
206 			audio_pcm = audio_pcm_s16be;
207 #endif
208 			break;
209 		case 24:
210 			config->precision = bitdepth = 32;
211 		case 32:
212 			alsa_format = SND_PCM_FORMAT_S32;
213 #if __BYTE_ORDER == __LITTLE_ENDIAN
214 			audio_pcm = audio_pcm_s32le;
215 #else
216 			audio_pcm = audio_pcm_s32be;
217 #endif
218 			break;
219 		default:
220 			audio_error="bitdepth not one of [8,16,24,32]";
221 			return -1;
222 	}
223 
224 	sample_size	= bitdepth * channels / 8;
225 
226 	err = set_hwparams(alsa_handle, alsa_hwparams, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
227 	if (err < 0) {
228 		audio_error=snd_strerror(err);
229 		return -1;
230 	}
231 
232 	err = set_swparams(alsa_handle, alsa_swparams);
233 	if (err < 0) {
234 		audio_error=snd_strerror(err);
235 		return -1;
236 	}
237 
238 	err = snd_pcm_prepare(alsa_handle);
239 	if (err < 0) {
240 		audio_error=snd_strerror(err);
241 		return -1;
242 	}
243 
244 	buf = malloc(buffer_size);
245 	if (buf == NULL) {
246 		audio_error="unable to allocate output buffer table";
247 		return -1;
248 	}
249 
250 	return 0;
251 
252 }
253 
254 static
xrun_recovery(snd_pcm_t * handle,int err)255 int xrun_recovery(snd_pcm_t *handle, int err)
256 {
257 	if (err == -EPIPE) {		/* underrun */
258 		err = snd_pcm_prepare(handle);
259 		if (err < 0) {
260 			audio_error=snd_strerror(err);
261 			return -1;
262 		}
263 	} else if (err == -ESTRPIPE) {
264 		while ((err = snd_pcm_resume(handle)) == -EAGAIN)
265 			sleep(1);	/* wait until suspend flag is gone */
266 		if (err < 0) {
267 			err = snd_pcm_prepare(handle);
268 			if (err < 0) {
269 				audio_error = snd_strerror(err);
270 				return -1;
271 			}
272 		}
273 		return 0;
274 	}
275 	return err;
276 }
277 
278 static
play(struct audio_play * play)279 int play(struct audio_play *play)
280 {
281 	int err, len;
282 	char *ptr;
283 
284 	ptr = buf;
285 	len = play->nsamples;
286 
287 	audio_pcm(ptr, len, play->samples[0], play->samples[1],
288 			play->mode, play->stats);
289 
290 	while (len > 0) {
291 
292 		err = snd_pcm_mmap_writei(alsa_handle, ptr, len);
293 
294 		if (err == -EAGAIN)
295 			continue;
296 
297 		if (err < 0) {
298 			if (xrun_recovery(alsa_handle, err) < 0) {
299 				audio_error = snd_strerror(err);
300 				return -1;
301 
302 			}
303 			break;
304 		}
305 
306 		len -= err;
307 		ptr += err * sample_size;
308 
309 	}
310 
311 	return 0;
312 
313 }
314 
315 static
stop(struct audio_stop * stop)316 int stop(struct audio_stop *stop)
317 {
318 	int err;
319 
320 	err = snd_pcm_drop(alsa_handle);
321 	if (err < 0) {
322 		audio_error = snd_strerror(err);
323 		return -1;
324 	}
325 
326 	err = snd_pcm_prepare(alsa_handle);
327 	if (err < 0) {
328 		audio_error = snd_strerror(err);
329 		return -1;
330 	}
331 
332 	return 0;
333 
334 }
335 
336 static
finish(struct audio_finish * finish)337 int finish(struct audio_finish *finish)
338 {
339 	int err;
340 
341 	err = snd_pcm_close(alsa_handle);
342 	if (err < 0) {
343 		audio_error = snd_strerror(err);
344 		return -1;
345 	}
346 
347 	return 0;
348 
349 }
350 
audio_alsa(union audio_control * control)351 int audio_alsa(union audio_control *control)
352 {
353   audio_error = 0;
354 
355   switch (control->command) {
356   case AUDIO_COMMAND_INIT:
357     return init(&control->init);
358 
359   case AUDIO_COMMAND_CONFIG:
360     return config(&control->config);
361 
362   case AUDIO_COMMAND_PLAY:
363     return play(&control->play);
364 
365   case AUDIO_COMMAND_STOP:
366     return stop(&control->stop);
367 
368   case AUDIO_COMMAND_FINISH:
369     return finish(&control->finish);
370   }
371 
372   return 0;
373 }
374