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