Mercurial > mplayer.hg
annotate libao2/ao_alsa9.c @ 1129:705fa4ac4fed
hacked to work seek/pause/resume/uninit
author | al3x |
---|---|
date | Thu, 14 Jun 2001 18:29:05 +0000 |
parents | d2dd25dd11bc |
children | a84610bb5476 |
rev | line source |
---|---|
1050 | 1 /* |
2 ao_alsa9 - ALSA-0.9.x output plugin for MPlayer | |
3 | |
4 (C) Alex Beregszaszi <alex@naxine.org> | |
5 */ | |
6 | |
7 #include <errno.h> | |
8 #include <sys/asoundlib.h> | |
9 | |
10 #include "../config.h" | |
11 | |
12 #include "audio_out.h" | |
13 #include "audio_out_internal.h" | |
1058 | 14 #include "afmt.h" |
1050 | 15 |
16 extern int verbose; | |
17 | |
18 static ao_info_t info = | |
19 { | |
20 "ALSA-0.9.x audio output", | |
21 "alsa9", | |
22 "Alex Beregszaszi <alex@naxine.org>", | |
23 "under developement" | |
24 }; | |
25 | |
26 LIBAO_EXTERN(alsa9) | |
27 | |
28 /* global variables: | |
29 ao_samplerate | |
30 ao_channels | |
31 ao_format | |
32 ao_bps | |
33 ao_outburst | |
34 ao_buffersize | |
35 */ | |
36 | |
37 static snd_pcm_t *alsa_handler; | |
38 static snd_pcm_format_t alsa_format; | |
39 static snd_pcm_hw_params_t *alsa_hwparams; | |
40 static snd_pcm_sw_params_t *alsa_swparams; | |
41 static char *alsa_device; | |
42 #define ALSA_DEVICE_SIZE 48 | |
43 | |
1128 | 44 static int alsa_fragsize = 8192; /* 4096 */ |
45 static int alsa_fragcount = 8; | |
46 | |
1050 | 47 /* to set/get/query special features/parameters */ |
48 static int control(int cmd, int arg) | |
49 { | |
50 switch(cmd) | |
51 { | |
52 case AOCONTROL_GET_DEVICE: | |
53 return(char *)alsa_device; /* egy kicsit brutalis, dehat :) */ | |
54 case AOCONTROL_SET_DEVICE: | |
55 strncpy(alsa_device, (char *)arg, ALSA_DEVICE_SIZE); | |
56 break; | |
57 } | |
58 return(CONTROL_UNKNOWN); | |
59 } | |
60 | |
1128 | 61 #undef start |
62 #define buffersize | |
63 #undef buffertime | |
64 #define set_period | |
65 #undef sw_params | |
66 #undef set_start_mode | |
67 | |
1050 | 68 /* |
69 open & setup audio device | |
70 return: 1=success 0=fail | |
71 */ | |
72 static int init(int rate_hz, int channels, int format, int flags) | |
73 { | |
74 int err; | |
75 int cards = -1; | |
76 snd_pcm_info_t *alsa_info; | |
77 | |
78 printf("alsa-init: requested format: %d Hz, %d channels, %s\n", rate_hz, | |
79 channels, audio_out_format_name(format)); | |
80 | |
81 alsa_handler = NULL; | |
82 | |
83 if (verbose) | |
84 printf("alsa-init: compiled for ALSA-%s (%d)\n", SND_LIB_VERSION_STR, | |
85 SND_LIB_VERSION); | |
86 | |
87 if ((err = snd_card_next(&cards)) < 0 || cards < 0) | |
88 { | |
89 printf("alsa-init: no soundcards found: %s\n", snd_strerror(err)); | |
90 return(0); | |
91 } | |
92 | |
1128 | 93 ao_samplerate = rate_hz; |
94 ao_bps = channels; /* really this is bytes per frame so bad varname */ | |
1050 | 95 ao_format = format; |
1128 | 96 ao_channels = channels; |
1050 | 97 ao_outburst = OUTBURST; |
98 ao_buffersize = 16384; | |
99 | |
100 switch (format) | |
101 { | |
102 case AFMT_S8: | |
103 alsa_format = SND_PCM_FORMAT_S8; | |
104 break; | |
105 case AFMT_U8: | |
106 alsa_format = SND_PCM_FORMAT_U8; | |
107 break; | |
108 case AFMT_U16_LE: | |
109 alsa_format = SND_PCM_FORMAT_U16_LE; | |
110 break; | |
111 case AFMT_U16_BE: | |
112 alsa_format = SND_PCM_FORMAT_U16_BE; | |
113 break; | |
114 case AFMT_S16_LE: | |
115 alsa_format = SND_PCM_FORMAT_S16_LE; | |
116 break; | |
117 case AFMT_S16_BE: | |
118 alsa_format = SND_PCM_FORMAT_S16_BE; | |
119 break; | |
120 default: | |
121 alsa_format = SND_PCM_FORMAT_MPEG; | |
122 break; | |
123 } | |
124 | |
125 switch(alsa_format) | |
126 { | |
127 case SND_PCM_FORMAT_S16_LE: | |
128 case SND_PCM_FORMAT_U16_LE: | |
129 ao_bps *= 2; | |
130 break; | |
131 case -1: | |
132 printf("alsa-init: invalid format (%s) requested - output disabled\n", | |
133 audio_out_format_name(format)); | |
134 return(0); | |
1115
a16b569f2702
-Wall style cleanups, TEST IT, it can be working by others
al3x
parents:
1058
diff
changeset
|
135 default: |
a16b569f2702
-Wall style cleanups, TEST IT, it can be working by others
al3x
parents:
1058
diff
changeset
|
136 break; |
1050 | 137 } |
1128 | 138 |
1050 | 139 if ((err = snd_pcm_info_malloc(&alsa_info)) < 0) |
140 { | |
141 printf("alsa-init: memory allocation error: %s\n", snd_strerror(err)); | |
142 return(0); | |
143 } | |
144 | |
145 if ((alsa_device = malloc(ALSA_DEVICE_SIZE)) == NULL) | |
146 { | |
147 printf("alsa-init: memory allocation error: %s\n", strerror(errno)); | |
148 return(0); | |
149 } | |
150 | |
151 snprintf(alsa_device, ALSA_DEVICE_SIZE, "hw:%d,%d", | |
152 snd_pcm_info_get_device(alsa_info), | |
153 snd_pcm_info_get_subdevice(alsa_info)); | |
154 | |
155 snd_pcm_info_free(alsa_info); | |
156 | |
157 printf("alsa-init: %d soundcard%s found, using: %s\n", cards+1, | |
158 (cards >= 0) ? "" : "s", alsa_device); | |
159 | |
160 if ((err = snd_pcm_open(&alsa_handler, alsa_device, SND_PCM_STREAM_PLAYBACK, | |
161 0)) < 0) | |
162 { | |
163 printf("alsa-init: playback open error: %s\n", snd_strerror(err)); | |
164 return(0); | |
165 } | |
166 | |
167 snd_pcm_hw_params_malloc(&alsa_hwparams); | |
168 snd_pcm_sw_params_malloc(&alsa_swparams); | |
169 | |
170 if ((err = snd_pcm_hw_params_any(alsa_handler, alsa_hwparams)) < 0) | |
171 { | |
172 printf("alsa-init: unable to get initial parameters: %s\n", | |
173 snd_strerror(err)); | |
174 return(0); | |
175 } | |
176 | |
177 if ((err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams, | |
178 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) | |
179 { | |
180 printf("alsa-init: unable to set access type: %s\n", | |
181 snd_strerror(err)); | |
182 return(0); | |
183 } | |
184 | |
185 if ((err = snd_pcm_hw_params_set_format(alsa_handler, alsa_hwparams, | |
186 alsa_format)) < 0) | |
187 { | |
188 printf("alsa-init: unable to set format: %s\n", | |
189 snd_strerror(err)); | |
190 return(0); | |
191 } | |
192 | |
193 if ((err = snd_pcm_hw_params_set_channels(alsa_handler, alsa_hwparams, | |
194 ao_channels)) < 0) | |
195 { | |
196 printf("alsa-init: unable to set channels: %s\n", | |
197 snd_strerror(err)); | |
198 return(0); | |
199 } | |
200 | |
1129 | 201 if ((err = snd_pcm_hw_params_set_rate(alsa_handler, alsa_hwparams, |
1050 | 202 ao_samplerate, 0)) < 0) |
203 { | |
1128 | 204 printf("alsa-init: unable to set samplerate: %s\n", |
1050 | 205 snd_strerror(err)); |
206 return(0); | |
207 } | |
208 | |
1128 | 209 #ifdef set_period |
1115
a16b569f2702
-Wall style cleanups, TEST IT, it can be working by others
al3x
parents:
1058
diff
changeset
|
210 { |
1128 | 211 if ((err = snd_pcm_hw_params_set_period_size(alsa_handler, alsa_hwparams, alsa_fragsize / 4, 0)) < 0) |
212 { | |
213 printf("alsa-init: unable to set periodsize: %s\n", | |
214 snd_strerror(err)); | |
215 return(0); | |
216 } | |
217 if ((err = snd_pcm_hw_params_set_periods(alsa_handler, alsa_hwparams, alsa_fragcount, 0)) < 0) | |
218 { | |
219 printf("alsa-init: unable to set periods: %s\n", | |
220 snd_strerror(err)); | |
221 return(0); | |
222 } | |
1115
a16b569f2702
-Wall style cleanups, TEST IT, it can be working by others
al3x
parents:
1058
diff
changeset
|
223 } |
1128 | 224 #endif |
1050 | 225 #ifdef buffersize |
226 if ((err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams)) < 0) | |
227 { | |
228 printf("alsa-init: unable to get buffer size: %s\n", | |
229 snd_strerror(err)); | |
230 return(0); | |
231 } else | |
1128 | 232 { |
233 printf("alsa-init: got buffersize %i\n", err); | |
1050 | 234 ao_buffersize = err; |
1128 | 235 } |
1050 | 236 #endif |
237 | |
238 #ifdef buffertime | |
239 { | |
240 int alsa_buffer_time = 60; | |
241 | |
242 if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa_handler, alsa_hwparams, | |
243 alsa_buffer_time, 0)) < 0) | |
244 { | |
245 printf("alsa-init: unable to set buffer time near: %s\n", | |
246 snd_strerror(err)); | |
247 return(0); | |
248 } else | |
249 alsa_buffer_time = err; | |
250 | |
251 if ((err = snd_pcm_hw_params_set_period_time_near(alsa_handler, alsa_hwparams, | |
252 alsa_buffer_time/ao_bps, 0)) < 0) | |
253 { | |
254 printf("alsa-init: unable to set period time: %s\n", | |
255 snd_strerror(err)); | |
256 return(0); | |
257 } | |
1128 | 258 printf("alsa-init: buffer_time: %d, period_time :%d\n", |
259 alsa_buffer_time, err); | |
1050 | 260 } |
261 #endif | |
262 | |
263 if ((err = snd_pcm_hw_params(alsa_handler, alsa_hwparams)) < 0) | |
264 { | |
265 printf("alsa-init: unable to set parameters: %s\n", | |
266 snd_strerror(err)); | |
267 return(0); | |
268 } | |
1128 | 269 |
270 #ifdef sw_params | |
1050 | 271 if ((err = snd_pcm_sw_params_current(alsa_handler, alsa_swparams)) < 0) |
272 { | |
273 printf("alsa-init: unable to get parameters: %s\n", | |
274 snd_strerror(err)); | |
275 return(0); | |
276 } | |
277 | |
1128 | 278 #ifdef set_start_mode |
1050 | 279 if ((err = snd_pcm_sw_params_set_start_mode(alsa_handler, alsa_swparams, |
280 SND_PCM_START_DATA)) < 0) | |
281 { | |
282 printf("alsa-init: unable to set start mode: %s\n", | |
283 snd_strerror(err)); | |
284 return(0); | |
285 } | |
1128 | 286 #endif |
1050 | 287 |
288 if ((err = snd_pcm_sw_params(alsa_handler, alsa_swparams)) < 0) | |
289 { | |
290 printf("alsa-init: unable to set parameters: %s\n", | |
291 snd_strerror(err)); | |
292 return(0); | |
293 } | |
294 | |
1128 | 295 // snd_pcm_sw_params_default(alsa_handler, alsa_swparams); |
296 #endif | |
1050 | 297 if ((err = snd_pcm_prepare(alsa_handler)) < 0) |
298 { | |
299 printf("alsa-init: pcm prepare error: %s\n", snd_strerror(err)); | |
300 return(0); | |
301 } | |
302 | |
1115
a16b569f2702
-Wall style cleanups, TEST IT, it can be working by others
al3x
parents:
1058
diff
changeset
|
303 #ifdef start |
1050 | 304 if ((err = snd_pcm_start(alsa_handler)) < 0) |
305 { | |
306 printf("alsa-init: pcm start error: %s\n", snd_strerror(err)); | |
307 if (err != -EPIPE) | |
308 return(0); | |
309 if ((err = snd_pcm_start(alsa_handler)) < 0) | |
310 { | |
311 printf("alsa-init: pcm start error: %s\n", snd_strerror(err)); | |
312 return(0); | |
313 } | |
314 } | |
1115
a16b569f2702
-Wall style cleanups, TEST IT, it can be working by others
al3x
parents:
1058
diff
changeset
|
315 #endif |
1128 | 316 printf("AUDIO: %d Hz/%d channels/%d bpf/%d bytes buffer/%s\n", |
317 ao_samplerate, ao_channels, ao_bps, ao_buffersize, | |
1050 | 318 snd_pcm_format_description(alsa_format)); |
319 return(1); | |
320 } | |
321 | |
322 /* close audio device */ | |
323 static void uninit() | |
324 { | |
325 int err; | |
326 | |
327 if (alsa_device != NULL) | |
328 free(alsa_device); | |
329 | |
330 snd_pcm_hw_params_free(alsa_hwparams); | |
331 snd_pcm_sw_params_free(alsa_swparams); | |
332 | |
333 if ((err = snd_pcm_drain(alsa_handler)) < 0) | |
334 { | |
335 printf("alsa-uninit: pcm drain error: %s\n", snd_strerror(err)); | |
336 return; | |
337 } | |
338 | |
1129 | 339 #ifdef start |
1050 | 340 if ((err = snd_pcm_reset(alsa_handler)) < 0) |
341 { | |
342 printf("alsa-uninit: pcm reset error: %s\n", snd_strerror(err)); | |
343 return; | |
344 } | |
1129 | 345 #endif |
1050 | 346 |
347 if ((err = snd_pcm_close(alsa_handler)) < 0) | |
348 { | |
349 printf("alsa-uninit: pcm close error: %s\n", snd_strerror(err)); | |
350 return; | |
351 } | |
352 } | |
353 | |
354 static void audio_pause() | |
355 { | |
356 int err; | |
357 | |
358 if ((err = snd_pcm_drain(alsa_handler)) < 0) | |
359 { | |
360 printf("alsa-pause: pcm drain error: %s\n", snd_strerror(err)); | |
361 return; | |
362 } | |
363 | |
1129 | 364 #ifdef reset |
1050 | 365 if ((err = snd_pcm_reset(alsa_handler)) < 0) |
366 { | |
367 printf("alsa-pause: pcm reset error: %s\n", snd_strerror(err)); | |
368 return; | |
369 } | |
1129 | 370 #endif |
1050 | 371 } |
372 | |
373 static void audio_resume() | |
374 { | |
375 int err; | |
376 | |
377 if ((err = snd_pcm_prepare(alsa_handler)) < 0) | |
378 { | |
379 printf("alsa-resume: pcm prepare error: %s\n", snd_strerror(err)); | |
380 return; | |
381 } | |
382 | |
1129 | 383 #ifdef start |
1050 | 384 if ((err = snd_pcm_start(alsa_handler)) < 0) |
385 { | |
386 printf("alsa-resume: pcm start error: %s\n", snd_strerror(err)); | |
387 return; | |
388 } | |
1129 | 389 #endif |
1050 | 390 } |
391 | |
392 /* stop playing and empty buffers (for seeking/pause) */ | |
393 static void reset() | |
394 { | |
395 int err; | |
396 | |
397 if ((err = snd_pcm_drain(alsa_handler)) < 0) | |
398 { | |
399 printf("alsa-reset: pcm drain error: %s\n", snd_strerror(err)); | |
400 return; | |
401 } | |
402 | |
1129 | 403 #ifdef start |
1050 | 404 if ((err = snd_pcm_reset(alsa_handler)) < 0) |
405 { | |
406 printf("alsa-reset: pcm reset error: %s\n", snd_strerror(err)); | |
407 return; | |
408 } | |
1129 | 409 #endif |
1050 | 410 |
411 if ((err = snd_pcm_prepare(alsa_handler)) < 0) | |
412 { | |
413 printf("alsa-reset: pcm prepare error: %s\n", snd_strerror(err)); | |
414 return; | |
415 } | |
416 | |
1129 | 417 #ifdef start |
1050 | 418 if ((err = snd_pcm_start(alsa_handler)) < 0) |
419 { | |
420 printf("alsa-reset: pcm start error: %s\n", snd_strerror(err)); | |
421 return; | |
422 } | |
1129 | 423 #endif |
1050 | 424 } |
425 | |
426 /* | |
427 plays 'len' bytes of 'data' | |
428 returns: number of bytes played | |
429 */ | |
430 static int play(void* data, int len, int flags) | |
431 { | |
1115
a16b569f2702
-Wall style cleanups, TEST IT, it can be working by others
al3x
parents:
1058
diff
changeset
|
432 int got_len; |
a16b569f2702
-Wall style cleanups, TEST IT, it can be working by others
al3x
parents:
1058
diff
changeset
|
433 |
1128 | 434 if ((got_len = snd_pcm_writei(alsa_handler, data, (len/ao_bps))) != (len/ao_bps)) |
1050 | 435 { |
1115
a16b569f2702
-Wall style cleanups, TEST IT, it can be working by others
al3x
parents:
1058
diff
changeset
|
436 if (got_len == -EPIPE) /* underrun? */ |
1050 | 437 { |
438 printf("alsa-play: alsa underrun, resetting stream\n"); | |
1128 | 439 if ((got_len = snd_pcm_prepare(alsa_handler)) < 0) |
1050 | 440 { |
1128 | 441 printf("alsa-play: playback prepare error: %s\n", snd_strerror(got_len)); |
1050 | 442 return(0); |
443 } | |
1128 | 444 if ((got_len = snd_pcm_writei(alsa_handler, data, (len/ao_bps))) != (len/ao_bps)) |
1050 | 445 { |
446 printf("alsa-play: write error after reset: %s - giving up\n", | |
1128 | 447 snd_strerror(got_len)); |
1050 | 448 return(0); |
449 } | |
450 return(len); /* 2nd write was ok */ | |
451 } | |
452 } | |
453 return(len); | |
454 } | |
455 | |
456 /* how many byes are free in the buffer */ | |
457 static int get_space() | |
458 { | |
459 snd_pcm_status_t *status; | |
460 int ret; | |
461 | |
462 if ((ret = snd_pcm_status_malloc(&status)) < 0) | |
463 { | |
464 printf("alsa-space: memory allocation error: %s\n", snd_strerror(ret)); | |
465 return(0); | |
466 } | |
467 | |
468 if ((ret = snd_pcm_status(alsa_handler, status)) < 0) | |
469 { | |
470 printf("alsa-space: cannot get pcm status: %s\n", snd_strerror(ret)); | |
471 return(0); | |
472 } | |
473 | |
474 switch(snd_pcm_status_get_state(status)) | |
475 { | |
476 case SND_PCM_STATE_OPEN: | |
477 case SND_PCM_STATE_PREPARED: | |
478 case SND_PCM_STATE_RUNNING: | |
479 ret = snd_pcm_status_get_avail(status) * ao_bps; | |
480 break; | |
481 default: | |
482 ret = 0; | |
483 } | |
484 | |
485 snd_pcm_status_free(status); | |
1129 | 486 |
487 if (ret < 0) | |
488 ret = 0; | |
1050 | 489 return(ret); |
490 } | |
491 | |
492 /* how many unplayed bytes are in the buffer */ | |
493 static int get_delay() | |
494 { | |
495 snd_pcm_status_t *status; | |
496 int ret; | |
497 | |
498 if ((ret = snd_pcm_status_malloc(&status)) < 0) | |
499 { | |
500 printf("alsa-delay: memory allocation error: %s\n", snd_strerror(ret)); | |
501 return(0); | |
502 } | |
503 | |
504 if ((ret = snd_pcm_status(alsa_handler, status)) < 0) | |
505 { | |
506 printf("alsa-delay: cannot get pcm status: %s\n", snd_strerror(ret)); | |
507 return(0); | |
508 } | |
509 | |
510 switch(snd_pcm_status_get_state(status)) | |
511 { | |
512 case SND_PCM_STATE_OPEN: | |
513 case SND_PCM_STATE_PREPARED: | |
514 case SND_PCM_STATE_RUNNING: | |
515 ret = snd_pcm_status_get_delay(status) * ao_bps; | |
516 break; | |
517 default: | |
518 ret = 0; | |
519 } | |
520 | |
521 snd_pcm_status_free(status); | |
1129 | 522 |
523 if (ret < 0) | |
524 ret = 0; | |
1050 | 525 return(ret); |
526 } |