Mercurial > mplayer.hg
comparison libao2/ao_alsa9.c @ 6633:769246a4eb41
cVS: ---------------------------------------------------------------------
added get/set_sound controls
stripped down initializing
changed play() again
outsourced xrun-handling
added some cases to get_space() as xrun-handling
added nonblock-mode
added mmap-mode
added subopts for mmap and noblock called mmap and noblock
could be accessed as -ao alsa9:noblock:mmap
author | joyping |
---|---|
date | Wed, 03 Jul 2002 21:43:28 +0000 |
parents | 1595ca898d3b |
children | df3bf0f971d1 |
comparison
equal
deleted
inserted
replaced
6632:1c2aa650de79 | 6633:769246a4eb41 |
---|---|
10 */ | 10 */ |
11 | 11 |
12 #include <errno.h> | 12 #include <errno.h> |
13 #include <sys/time.h> | 13 #include <sys/time.h> |
14 #include <stdlib.h> | 14 #include <stdlib.h> |
15 //#include <unistd.h> | 15 #include <math.h> |
16 //#include <string.h> | 16 #include <string.h> |
17 | 17 |
18 #include "../config.h" | 18 #include "../config.h" |
19 | 19 |
20 #if HAVE_SYS_ASOUNDLIB_H | 20 #if HAVE_SYS_ASOUNDLIB_H |
21 #include <sys/asoundlib.h> | 21 #include <sys/asoundlib.h> |
46 static snd_pcm_format_t alsa_format; | 46 static snd_pcm_format_t alsa_format; |
47 static snd_pcm_hw_params_t *alsa_hwparams; | 47 static snd_pcm_hw_params_t *alsa_hwparams; |
48 static snd_pcm_sw_params_t *alsa_swparams; | 48 static snd_pcm_sw_params_t *alsa_swparams; |
49 static char *alsa_device; | 49 static char *alsa_device; |
50 | 50 |
51 static int alsa_fragsize = OUTBURST; /* possible 4096, original 8192, OUTBURST is set statically to 512 in config.h but now its not used cause chunksize is allocated dynamically. */ | 51 /* possible 4096, original 8192, OUTBURST is set statically to 512 in config.h */ |
52 static int alsa_fragsize = 4096; //OUTBURST | |
52 static int alsa_fragcount = 8; | 53 static int alsa_fragcount = 8; |
53 | 54 |
54 static int chunk_size = -1; | 55 static int chunk_size = -1; |
55 static int buffer_size = 0; | |
56 static int start_delay = 0; | |
57 static int stop_delay = 0; | |
58 static size_t bits_per_sample, bits_per_frame; | 56 static size_t bits_per_sample, bits_per_frame; |
59 static size_t chunk_bytes; | 57 static size_t chunk_bytes; |
60 | 58 |
59 int ao_mmap = 0; | |
60 int ao_noblock = 0; | |
61 | |
62 static int open_mode; | |
63 static int set_block_mode; | |
64 | |
61 #define ALSA_DEVICE_SIZE 48 | 65 #define ALSA_DEVICE_SIZE 48 |
62 | 66 |
63 #define BUFFERTIME /* last undef */ | 67 #undef BUFFERTIME |
64 #undef SET_PERIOD /* only function now is to set chunksize staticaly, last defined */ | 68 #define SET_CHUNKSIZE |
65 #define SW_PARAMS /* last undef */ | |
66 | |
67 | 69 |
68 snd_pcm_t * | 70 snd_pcm_t * |
71 | |
69 spdif_init(int acard, int adevice) | 72 spdif_init(int acard, int adevice) |
70 { | 73 { |
71 //char *pcm_name = "hw:0,2"; /* first card second device */ | 74 //char *pcm_name = "hw:0,2"; /* first card second device */ |
72 char pcm_name[255]; | 75 char pcm_name[255]; |
73 static snd_aes_iec958_t spdif; | 76 static snd_aes_iec958_t spdif; |
147 err = snd_pcm_hw_params_any(handler, params); | 150 err = snd_pcm_hw_params_any(handler, params); |
148 if (err < 0) { | 151 if (err < 0) { |
149 fprintf(stderr, "Broken configuration for this PCM: no configurations available"); | 152 fprintf(stderr, "Broken configuration for this PCM: no configurations available"); |
150 return NULL; | 153 return NULL; |
151 } | 154 } |
152 err = snd_pcm_hw_params_set_access(handler, params, | 155 |
153 SND_PCM_ACCESS_RW_INTERLEAVED); | 156 err = snd_pcm_hw_params_set_access(handler, params,SND_PCM_ACCESS_RW_INTERLEAVED); |
154 if (err < 0) { | 157 if (err < 0) { |
155 fprintf(stderr, "Access tyep not available"); | 158 fprintf(stderr, "Access tyep not available"); |
156 return NULL; | 159 return NULL; |
157 } | 160 } |
158 err = snd_pcm_hw_params_set_format(handler, params, format); | 161 err = snd_pcm_hw_params_set_format(handler, params, format); |
184 | 187 |
185 | 188 |
186 /* to set/get/query special features/parameters */ | 189 /* to set/get/query special features/parameters */ |
187 static int control(int cmd, int arg) | 190 static int control(int cmd, int arg) |
188 { | 191 { |
189 return(CONTROL_UNKNOWN); | 192 switch(cmd) { |
193 case AOCONTROL_QUERY_FORMAT: | |
194 return CONTROL_TRUE; | |
195 case AOCONTROL_GET_VOLUME: | |
196 case AOCONTROL_SET_VOLUME: | |
197 { | |
198 ao_control_vol_t *vol = (ao_control_vol_t *)arg; | |
199 | |
200 int err; | |
201 snd_mixer_t *handle; | |
202 snd_mixer_elem_t *elem; | |
203 snd_mixer_selem_id_t *sid; | |
204 | |
205 const char *mix_name = "PCM"; | |
206 char *card = "default"; | |
207 | |
208 long pmin, pmax; | |
209 long get_vol, set_vol; | |
210 float calc_vol, diff, f_multi; | |
211 | |
212 if(ao_data.format == AFMT_AC3) | |
213 return CONTROL_TRUE; | |
214 | |
215 //allocate simple id | |
216 snd_mixer_selem_id_alloca(&sid); | |
217 | |
218 //sets simple-mixer index and name | |
219 snd_mixer_selem_id_set_index(sid, 0); | |
220 snd_mixer_selem_id_set_name(sid, mix_name); | |
221 | |
222 if ((err = snd_mixer_open(&handle, 0)) < 0) { | |
223 printf("alsa-control: mixer open error: %s\n", snd_strerror(err)); | |
224 return CONTROL_ERROR; | |
225 } | |
226 | |
227 if ((err = snd_mixer_attach(handle, card)) < 0) { | |
228 printf("alsa-control: mixer attach %s error: %s", card, snd_strerror(err)); | |
229 snd_mixer_close(handle); | |
230 return CONTROL_ERROR; | |
231 } | |
232 | |
233 if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { | |
234 printf("alsa-control: mixer register error: %s", snd_strerror(err)); | |
235 snd_mixer_close(handle); | |
236 return CONTROL_ERROR; | |
237 } | |
238 err = snd_mixer_load(handle); | |
239 if (err < 0) { | |
240 printf("alsa-control: mixer load error: %s", snd_strerror(err)); | |
241 snd_mixer_close(handle); | |
242 return CONTROL_ERROR; | |
243 } | |
244 | |
245 elem = snd_mixer_find_selem(handle, sid); | |
246 if (!elem) { | |
247 printf("alsa-control: unable to find simple control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid)); | |
248 snd_mixer_close(handle); | |
249 return CONTROL_ERROR; | |
250 } | |
251 | |
252 snd_mixer_selem_get_playback_volume_range(elem,&pmin,&pmax); | |
253 f_multi = (100 / (float)pmax); | |
254 | |
255 if (cmd == AOCONTROL_SET_VOLUME) { | |
256 | |
257 diff = (vol->left+vol->right) / 2; | |
258 set_vol = rint(diff / f_multi); | |
259 | |
260 if (set_vol < 0) | |
261 set_vol = 0; | |
262 else if (set_vol > pmax) | |
263 set_vol = pmax; | |
264 | |
265 //setting channels | |
266 if ((err = snd_mixer_selem_set_playback_volume(elem, 0, set_vol)) < 0) { | |
267 printf("alsa-control: error setting left channel, %s",snd_strerror(err)); | |
268 return CONTROL_ERROR; | |
269 } | |
270 if ((err = snd_mixer_selem_set_playback_volume(elem, 1, set_vol)) < 0) { | |
271 printf("alsa-control: error setting right channel, %s",snd_strerror(err)); | |
272 return CONTROL_ERROR; | |
273 } | |
274 | |
275 //printf("diff=%f, set_vol=%i, pmax=%i, mult=%f\n", diff, set_vol, pmax, f_multi); | |
276 } | |
277 else { | |
278 snd_mixer_selem_get_playback_volume(elem, 0, &get_vol); | |
279 calc_vol = get_vol; | |
280 calc_vol = rintf(calc_vol * f_multi); | |
281 | |
282 vol->left = vol->right = (int)calc_vol; | |
283 | |
284 //printf("get_vol = %i, calc=%i\n",get_vol, calc_vol); | |
285 } | |
286 snd_mixer_close(handle); | |
287 return CONTROL_OK; | |
288 } | |
289 } | |
290 return(CONTROL_UNKNOWN); | |
190 } | 291 } |
191 | 292 |
192 | 293 |
193 /* | 294 /* |
194 open & setup audio device | 295 open & setup audio device |
196 */ | 297 */ |
197 static int init(int rate_hz, int channels, int format, int flags) | 298 static int init(int rate_hz, int channels, int format, int flags) |
198 { | 299 { |
199 int err; | 300 int err; |
200 int cards = -1; | 301 int cards = -1; |
302 int period_val; | |
201 snd_pcm_info_t *alsa_info; | 303 snd_pcm_info_t *alsa_info; |
202 | 304 char *str_block_mode; |
203 size_t xfer_align; //new | 305 |
204 snd_pcm_uframes_t start_threshold, stop_threshold; //new | |
205 | |
206 printf("alsa-init: testing and bugreports are welcome.\n"); | 306 printf("alsa-init: testing and bugreports are welcome.\n"); |
207 printf("alsa-init: requested format: %d Hz, %d channels, %s\n", rate_hz, | 307 printf("alsa-init: requested format: %d Hz, %d channels, %s\n", rate_hz, |
208 channels, audio_out_format_name(format)); | 308 channels, audio_out_format_name(format)); |
209 | 309 |
210 alsa_handler = NULL; | 310 alsa_handler = NULL; |
221 ao_data.samplerate = rate_hz; | 321 ao_data.samplerate = rate_hz; |
222 ao_data.bps = channels; /* really this is bytes per frame so bad varname */ | 322 ao_data.bps = channels; /* really this is bytes per frame so bad varname */ |
223 ao_data.format = format; | 323 ao_data.format = format; |
224 ao_data.channels = channels; | 324 ao_data.channels = channels; |
225 ao_data.outburst = OUTBURST; | 325 ao_data.outburst = OUTBURST; |
226 ao_data.buffersize = 16384; | 326 ao_data.buffersize = MAX_OUTBURST; // was 16384 |
227 | 327 |
228 switch (format) | 328 switch (format) |
229 { | 329 { |
230 case AFMT_S8: | 330 case AFMT_S8: |
231 alsa_format = SND_PCM_FORMAT_S8; | 331 alsa_format = SND_PCM_FORMAT_S8; |
265 case -1: | 365 case -1: |
266 printf("alsa-init: invalid format (%s) requested - output disabled\n", | 366 printf("alsa-init: invalid format (%s) requested - output disabled\n", |
267 audio_out_format_name(format)); | 367 audio_out_format_name(format)); |
268 return(0); | 368 return(0); |
269 default: | 369 default: |
270 break; | 370 break; |
271 } | 371 } |
272 | 372 |
273 if (ao_subdevice != NULL) // ?? makes no sense | 373 if (ao_subdevice) { |
274 alsa_device = ao_subdevice; | 374 //start parsing ao_subdevice, ugly and not thread safe! |
375 //maybe there's a better way? | |
376 int i2 = 1; | |
377 int i3 = 0; | |
378 char *sub_str; | |
379 | |
380 char *token_str[3]; | |
381 char* test_str = strdup(ao_subdevice); | |
382 | |
383 //printf("subd=%s, test=%s\n", ao_subdevice,test_str); | |
384 | |
385 if ((strcspn(ao_subdevice, ":")) > 0) { | |
386 | |
387 sub_str = strtok(test_str, ":"); | |
388 *(token_str) = sub_str; | |
389 | |
390 while (((sub_str = strtok(NULL, ":")) != NULL) && (i2 <= 3)) { | |
391 *(token_str+i2) = sub_str; | |
392 //printf("token %i: %s\n", i2, *(token_str+i2)); | |
393 i2 += 1; | |
394 } | |
395 | |
396 for (i3=0; i3 <= i2-1; i3++) { | |
397 //printf("test %i, %s\n", i3, *(token_str+i3)); | |
398 if (strcmp(*(token_str + i3), "mmap") == 0) { | |
399 ao_mmap = 1; | |
400 } | |
401 else if (strcmp(*(token_str+i3), "noblock") == 0) { | |
402 ao_noblock = 1; | |
403 } | |
404 else if (strcmp(*(token_str+i3), "hw") == 0) { | |
405 alsa_device = *(token_str+i3); | |
406 } | |
407 else if (!alsa_device || !ao_mmap || !ao_noblock) { | |
408 alsa_device = *(token_str+i3); | |
409 } | |
410 } | |
411 } | |
412 } //end parsing ao_subdevice | |
275 | 413 |
276 if (alsa_device == NULL) | 414 if (alsa_device == NULL) |
277 { | 415 { |
278 int tmp_device, tmp_subdevice, err; | 416 int tmp_device, tmp_subdevice, err; |
279 | 417 |
317 | 455 |
318 if (format == AFMT_AC3) { | 456 if (format == AFMT_AC3) { |
319 // Try to initialize the SPDIF interface | 457 // Try to initialize the SPDIF interface |
320 alsa_handler = spdif_init(0, 2); | 458 alsa_handler = spdif_init(0, 2); |
321 } | 459 } |
322 | 460 |
461 //setting modes for block or nonblock-mode | |
462 if (ao_noblock) { | |
463 open_mode = SND_PCM_NONBLOCK; | |
464 set_block_mode = 1; | |
465 str_block_mode = "nonblock-mode"; | |
466 } | |
467 else { | |
468 open_mode = 0; | |
469 set_block_mode = 0; | |
470 str_block_mode = "block-mode"; | |
471 } | |
472 | |
473 | |
474 //modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC | |
323 if (!alsa_handler) { | 475 if (!alsa_handler) { |
324 if ((err = snd_pcm_open(&alsa_handler,alsa_device,SND_PCM_STREAM_PLAYBACK,0)) < 0) | 476 if ((err = snd_pcm_open(&alsa_handler, alsa_device, SND_PCM_STREAM_PLAYBACK, open_mode)) < 0) |
325 { | 477 { |
326 printf("alsa-init: playback open error: %s\n", snd_strerror(err)); | 478 if (ao_noblock) { |
327 return(0); | 479 printf("alsa-init: open in nonblock-mode failed, trying to open in block-mode\n"); |
480 if ((err = snd_pcm_open(&alsa_handler, alsa_device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { | |
481 printf("alsa-init: playback open error: %s\n", snd_strerror(err)); | |
482 return(0); | |
483 } else { | |
484 set_block_mode = 0; | |
485 str_block_mode = "block-mode"; | |
328 } | 486 } |
329 } | 487 } else { |
488 printf("alsa-init: playback open error: %s\n", snd_strerror(err)); | |
489 return(0); | |
490 } | |
491 } | |
492 } | |
493 | |
494 if ((err = snd_pcm_nonblock(alsa_handler, set_block_mode)) < 0) { | |
495 printf("alsa-init: error set block-mode %s\n", snd_strerror(err)); | |
496 } | |
497 else if (verbose) { | |
498 printf("alsa-init: pcm opend in %s\n", str_block_mode); | |
499 } | |
330 | 500 |
331 snd_pcm_hw_params_alloca(&alsa_hwparams); | 501 snd_pcm_hw_params_alloca(&alsa_hwparams); |
332 snd_pcm_sw_params_alloca(&alsa_swparams); | 502 snd_pcm_sw_params_alloca(&alsa_swparams); |
333 | 503 |
334 // setting hw-parameters | 504 // setting hw-parameters |
337 printf("alsa-init: unable to get initial parameters: %s\n", | 507 printf("alsa-init: unable to get initial parameters: %s\n", |
338 snd_strerror(err)); | 508 snd_strerror(err)); |
339 return(0); | 509 return(0); |
340 } | 510 } |
341 | 511 |
342 if ((err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams, | 512 if (ao_mmap) { |
343 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) | 513 snd_pcm_access_mask_t *mask = alloca(snd_pcm_access_mask_sizeof()); |
344 { | 514 snd_pcm_access_mask_none(mask); |
345 printf("alsa-init: unable to set access type: %s\n", | 515 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED); |
346 snd_strerror(err)); | 516 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); |
347 return(0); | 517 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX); |
518 err = snd_pcm_hw_params_set_access_mask(alsa_handler, alsa_hwparams, mask); | |
519 printf("alsa-init: mmap set\n"); | |
520 } else { | |
521 err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams,SND_PCM_ACCESS_RW_INTERLEAVED); | |
522 printf("alsa-init: interleave set\n"); | |
523 } | |
524 if (err < 0) { | |
525 printf("alsa-init: unable to set access type: %s\n", snd_strerror(err)); | |
526 return (0); | |
348 } | 527 } |
349 | 528 |
350 if ((err = snd_pcm_hw_params_set_format(alsa_handler, alsa_hwparams, | 529 if ((err = snd_pcm_hw_params_set_format(alsa_handler, alsa_hwparams, |
351 alsa_format)) < 0) | 530 alsa_format)) < 0) |
352 { | 531 { |
365 | 544 |
366 if ((err = snd_pcm_hw_params_set_rate_near(alsa_handler, alsa_hwparams, ao_data.samplerate, 0)) < 0) | 545 if ((err = snd_pcm_hw_params_set_rate_near(alsa_handler, alsa_hwparams, ao_data.samplerate, 0)) < 0) |
367 { | 546 { |
368 printf("alsa-init: unable to set samplerate-2: %s\n", | 547 printf("alsa-init: unable to set samplerate-2: %s\n", |
369 snd_strerror(err)); | 548 snd_strerror(err)); |
370 //snd_pcm_hw_params_dump(alsa_hwparams, errlog); jp | |
371 return(0); | 549 return(0); |
372 } | 550 } |
373 | |
374 | |
375 #ifdef SET_PERIOD | |
376 { | |
377 if ((err = snd_pcm_hw_params_set_period_size(alsa_handler, alsa_hwparams, alsa_fragsize, 0)) < 0) | |
378 { | |
379 printf("alsa-init: unable to set periodsize: %s\n", snd_strerror(err)); | |
380 return(0); | |
381 } | |
382 } | |
383 #endif | |
384 | 551 |
385 #ifdef BUFFERTIME | 552 #ifdef BUFFERTIME |
386 { | 553 { |
387 int alsa_buffer_time = 500000; /* original 60 */ | 554 int alsa_buffer_time = 500000; /* original 60 */ |
388 | 555 |
404 if (verbose) | 571 if (verbose) |
405 printf("alsa-init: buffer_time: %d, period_time :%d\n",alsa_buffer_time, err); | 572 printf("alsa-init: buffer_time: %d, period_time :%d\n",alsa_buffer_time, err); |
406 } | 573 } |
407 #endif | 574 #endif |
408 | 575 |
409 /* get chunk-size */ | 576 #ifdef SET_CHUNKSIZE |
410 if ((err = snd_pcm_hw_params_get_period_size(alsa_hwparams, 0)) < 0) | 577 { |
578 //set chunksize | |
579 chunk_size = alsa_fragsize / 4; | |
580 | |
581 if ((err = snd_pcm_hw_params_set_period_size(alsa_handler, alsa_hwparams, chunk_size, 0)) < 0) | |
411 { | 582 { |
412 printf("alsa-init: unable to get chunk-size in hw-params: %s\n", snd_strerror(err)); | 583 printf("alsa-init: unable to set periodsize: %s\n", snd_strerror(err)); |
413 return(0); | 584 return(0); |
414 } else | 585 } |
415 { | 586 else if (verbose) { |
416 chunk_size = err; | 587 printf("alsa-init: chunksize set to %i\n", chunk_size); |
417 if (verbose) {printf("alsa-init: got chunksize %i\n", chunk_size);} | 588 } |
418 } | 589 |
419 | 590 //set period_count |
420 /* get buffer size */ | 591 if ((period_val = snd_pcm_hw_params_get_periods_max(alsa_hwparams, 0)) < alsa_fragcount) { |
421 if ((err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams)) < 0) | 592 alsa_fragcount = period_val; |
422 { | 593 } |
423 printf("alsa-init: unable to get buffersize in hw-params: %s\n", snd_strerror(err)); | 594 |
424 return(0); | 595 if (verbose) |
425 } else | 596 printf("alsa-init: current val=%i, fragcount=%i\n", period_val, alsa_fragcount); |
426 { | 597 |
427 ao_data.buffersize = err; | 598 if ((err = snd_pcm_hw_params_set_periods(alsa_handler, alsa_hwparams, alsa_fragcount, 0)) < 0) { |
428 if (verbose) {printf("alsa-init: got buffersize %i\n", ao_data.buffersize);} | 599 printf("alsa-init: unable to set periods: %s\n", snd_strerror(err)); |
429 } | 600 } |
430 | 601 } |
431 if (MAX_OUTBURST > ao_data.buffersize) { //warning if MAX_OUTBURST is bigger than buffersize | 602 #endif |
432 printf("alsa-init: WARNING! MAX_OUTBURST exceeds your available buffersize.\nalsa-init: MAX_OUTBURST=%i, buffersize=%i\n",MAX_OUTBURST,ao_data.buffersize);} | |
433 | |
434 if (chunk_size == ao_data.buffersize) | |
435 { | |
436 printf("alsa-init: Can't use period equal to buffer size (%u == %lu)", chunk_size, (long)buffer_size); | |
437 return(0); | |
438 } | |
439 | 603 |
440 /* finally install hardware parameters */ | 604 /* finally install hardware parameters */ |
441 if ((err = snd_pcm_hw_params(alsa_handler, alsa_hwparams)) < 0) | 605 if ((err = snd_pcm_hw_params(alsa_handler, alsa_hwparams)) < 0) |
442 { | 606 { |
443 printf("alsa-init: unable to set hw-parameters: %s\n", | 607 printf("alsa-init: unable to set hw-parameters: %s\n", |
444 snd_strerror(err)); | 608 snd_strerror(err)); |
445 return(0); | 609 return(0); |
446 } | 610 } |
447 // end setting hw-params | 611 // end setting hw-params |
448 | 612 |
449 #ifdef SW_PARAMS | 613 |
450 { | 614 // gets buffersize for control |
451 size_t n; | 615 if ((err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams)) < 0) |
452 xfer_align = snd_pcm_sw_params_get_xfer_align(alsa_swparams); | 616 { |
453 if (xfer_align == 0) | 617 printf("alsa-init: unable to get buffersize: %s\n", snd_strerror(err)); |
454 xfer_align = 4; | 618 return(0); |
455 n = (ao_data.buffersize / xfer_align) * xfer_align; | 619 } |
456 | 620 else { |
457 if (start_delay <= 0) { | 621 ao_data.buffersize = err; |
458 start_threshold = n + (double) ao_data.samplerate * start_delay / 1000000; | 622 if (verbose) |
459 } else { | 623 printf("alsa-init: got buffersize=%i\n", ao_data.buffersize); |
460 start_threshold = (double) ao_data.samplerate * start_delay / 1000000; | 624 } |
461 } | 625 |
462 if (start_threshold < 1) | 626 // setting sw-params (only avail-min) if noblocking mode was choosed |
463 start_threshold = 1; | 627 if (ao_noblock) |
464 if (start_threshold > n) | 628 { |
465 start_threshold = n; | |
466 | |
467 if (stop_delay <= 0) { | |
468 stop_threshold = ao_data.buffersize + (double) ao_data.samplerate * stop_delay / 1000000; | |
469 } else { | |
470 stop_threshold = (double) ao_data.samplerate * stop_delay / 1000000; | |
471 } | |
472 | |
473 if (verbose) { | |
474 printf("alsa-init: start_threshold=%lu, stop_threshold=%lu\n",start_threshold,stop_threshold); | |
475 printf("alsa-init: n=%i\n", n); | |
476 } | |
477 | 629 |
478 if ((err = snd_pcm_sw_params_current(alsa_handler, alsa_swparams)) < 0) | 630 if ((err = snd_pcm_sw_params_current(alsa_handler, alsa_swparams)) < 0) |
479 { | 631 { |
480 printf("alsa-init: unable to get parameters: %s\n",snd_strerror(err)); | 632 printf("alsa-init: unable to get parameters: %s\n",snd_strerror(err)); |
481 return(0); | 633 return(0); |
482 } | 634 } |
483 | 635 |
484 //set min available frames to consider pcm ready (4) | 636 //set min available frames to consider pcm ready (4) |
485 if ((err = snd_pcm_sw_params_set_avail_min(alsa_handler, alsa_swparams, 4)) < 0) | 637 //increased for nonblock-mode should be set dynamically later |
638 if ((err = snd_pcm_sw_params_set_avail_min(alsa_handler, alsa_swparams, chunk_size)) < 0) | |
486 { | 639 { |
487 printf("alsa-init: unable to set avail_min %s\n",snd_strerror(err)); | 640 printf("alsa-init: unable to set avail_min %s\n",snd_strerror(err)); |
488 return(0); | |
489 } | |
490 | |
491 if ((err = snd_pcm_sw_params_set_start_threshold(alsa_handler, alsa_swparams, start_threshold)) < 0) | |
492 { | |
493 printf("alsa-init: unable to set start_threshold %s\n",snd_strerror(err)); | |
494 return(0); | |
495 } | |
496 | |
497 if ((err = snd_pcm_sw_params_set_stop_threshold(alsa_handler, alsa_swparams, stop_threshold)) < 0) | |
498 { | |
499 printf("alsa-init: unable to set stop_threshold %s\n",snd_strerror(err)); | |
500 return(0); | |
501 } | |
502 | |
503 //transfers stream aligned to 4 in nonblocking-mode it would be 1 | |
504 if ((err = snd_pcm_sw_params_set_xfer_align(alsa_handler, alsa_swparams, 4)) < 0) | |
505 { | |
506 printf("alsa-init: unable to set xfer_align: %s\n",snd_strerror(err)); | |
507 return(0); | 641 return(0); |
508 } | 642 } |
509 | 643 |
510 if ((err = snd_pcm_sw_params(alsa_handler, alsa_swparams)) < 0) | 644 if ((err = snd_pcm_sw_params(alsa_handler, alsa_swparams)) < 0) |
511 { | 645 { |
517 bits_per_frame = bits_per_sample * channels; | 651 bits_per_frame = bits_per_sample * channels; |
518 chunk_bytes = chunk_size * bits_per_frame / 8; | 652 chunk_bytes = chunk_size * bits_per_frame / 8; |
519 | 653 |
520 if (verbose) { | 654 if (verbose) { |
521 printf("alsa-init: bits per sample (bps)=%i, bits per frame (bpf)=%i, chunk_bytes=%i\n",bits_per_sample,bits_per_frame,chunk_bytes);} | 655 printf("alsa-init: bits per sample (bps)=%i, bits per frame (bpf)=%i, chunk_bytes=%i\n",bits_per_sample,bits_per_frame,chunk_bytes);} |
522 } | 656 |
523 | 657 }//end swparams |
524 #endif //end swparams | |
525 | 658 |
526 if ((err = snd_pcm_prepare(alsa_handler)) < 0) | 659 if ((err = snd_pcm_prepare(alsa_handler)) < 0) |
527 { | 660 { |
528 printf("alsa-init: pcm prepare error: %s\n", snd_strerror(err)); | 661 printf("alsa-init: pcm prepare error: %s\n", snd_strerror(err)); |
529 return(0); | 662 return(0); |
537 | 670 |
538 | 671 |
539 /* close audio device */ | 672 /* close audio device */ |
540 static void uninit() | 673 static void uninit() |
541 { | 674 { |
675 | |
676 if (alsa_handler) { | |
542 int err; | 677 int err; |
543 | 678 |
544 //if (alsa_device != NULL) | 679 if (!ao_noblock) { |
545 //free(alsa_device); | 680 if ((err = snd_pcm_drain(alsa_handler)) < 0) |
546 | 681 { |
547 //snd_pcm_hw_params_free(alsa_hwparams); | 682 printf("alsa-uninit: pcm drain error: %s\n", snd_strerror(err)); |
548 | 683 return; |
549 if ((err = snd_pcm_drain(alsa_handler)) < 0) | 684 } |
550 { | |
551 printf("alsa-uninit: pcm drain error: %s\n", snd_strerror(err)); | |
552 return; | |
553 } | 685 } |
554 | 686 |
555 if ((err = snd_pcm_close(alsa_handler)) < 0) | 687 if ((err = snd_pcm_close(alsa_handler)) < 0) |
556 { | 688 { |
557 printf("alsa-uninit: pcm close error: %s\n", snd_strerror(err)); | 689 printf("alsa-uninit: pcm close error: %s\n", snd_strerror(err)); |
558 return; | 690 return; |
559 } | 691 } |
560 else { | 692 else { |
561 alsa_handler = NULL; | 693 alsa_handler = NULL; |
562 alsa_device = NULL; | 694 alsa_device = NULL; |
563 printf("alsa-uninit: pcm closed\n"); | 695 printf("alsa-uninit: pcm closed\n"); |
564 } | 696 } |
697 } | |
698 else { | |
699 printf("alsa-uninit: no handler defined!\n"); | |
700 } | |
565 } | 701 } |
566 | 702 |
567 static void audio_pause() | 703 static void audio_pause() |
568 { | 704 { |
569 int err; | 705 int err; |
570 | 706 |
571 if ((err = snd_pcm_drain(alsa_handler)) < 0) | 707 if (!ao_noblock) { |
572 { | 708 //drain causes error in nonblock-mode! |
573 printf("alsa-pause: pcm drain error: %s\n", snd_strerror(err)); | 709 if ((err = snd_pcm_drain(alsa_handler)) < 0) |
574 return; | 710 { |
575 } | 711 printf("alsa-pause: pcm drain error: %s\n", snd_strerror(err)); |
576 | 712 return; |
577 #ifdef reset | 713 } |
578 if ((err = snd_pcm_reset(alsa_handler)) < 0) | 714 } |
579 { | 715 else { |
580 printf("alsa-pause: pcm reset error: %s\n", snd_strerror(err)); | 716 if (verbose) |
581 return; | 717 printf("alsa-pause: paused nonblock\n"); |
582 } | 718 |
583 #endif | 719 return; |
720 } | |
584 } | 721 } |
585 | 722 |
586 static void audio_resume() | 723 static void audio_resume() |
587 { | 724 { |
588 int err; | 725 int err; |
597 /* stop playing and empty buffers (for seeking/pause) */ | 734 /* stop playing and empty buffers (for seeking/pause) */ |
598 static void reset() | 735 static void reset() |
599 { | 736 { |
600 int err; | 737 int err; |
601 | 738 |
602 if ((err = snd_pcm_drain(alsa_handler)) < 0) | 739 if (!ao_noblock) { |
603 { | 740 //drain causes error in nonblock-mode! |
604 printf("alsa-reset: pcm drain error: %s\n", snd_strerror(err)); | 741 if ((err = snd_pcm_drain(alsa_handler)) < 0) |
605 return; | 742 { |
606 } | 743 printf("alsa-pause: pcm drain error: %s\n", snd_strerror(err)); |
607 | 744 return; |
608 if ((err = snd_pcm_prepare(alsa_handler)) < 0) | 745 } |
609 { | 746 |
610 printf("alsa-reset: pcm prepare error: %s\n", snd_strerror(err)); | 747 if ((err = snd_pcm_prepare(alsa_handler)) < 0) |
611 return; | 748 { |
612 } | 749 printf("alsa-reset: pcm prepare error: %s\n", snd_strerror(err)); |
613 | 750 return; |
751 } | |
752 } else { | |
753 if (verbose) | |
754 printf("alsa-reset: reset nonblocked"); | |
755 return; | |
756 } | |
614 } | 757 } |
615 | 758 |
616 #ifndef timersub | 759 #ifndef timersub |
617 #define timersub(a, b, result) \ | 760 #define timersub(a, b, result) \ |
618 do { \ | 761 do { \ |
623 (result)->tv_usec += 1000000; \ | 766 (result)->tv_usec += 1000000; \ |
624 } \ | 767 } \ |
625 } while (0) | 768 } while (0) |
626 #endif | 769 #endif |
627 | 770 |
771 /* I/O error handler */ | |
772 static int xrun(u_char *str_mode) | |
773 { | |
774 int err; | |
775 snd_pcm_status_t *status; | |
776 | |
777 snd_pcm_status_alloca(&status); | |
778 | |
779 if ((err = snd_pcm_status(alsa_handler, status))<0) { | |
780 printf("status error: %s", snd_strerror(err)); | |
781 return(0); | |
782 } | |
783 | |
784 if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) { | |
785 struct timeval now, diff, tstamp; | |
786 gettimeofday(&now, 0); | |
787 snd_pcm_status_get_trigger_tstamp(status, &tstamp); | |
788 timersub(&now, &tstamp, &diff); | |
789 printf("alsa-%s: xrun of at least %.3f msecs. resetting stream\n", | |
790 str_mode, | |
791 diff.tv_sec * 1000 + diff.tv_usec / 1000.0); | |
792 } | |
793 | |
794 if ((err = snd_pcm_prepare(alsa_handler))<0) { | |
795 printf("xrun: prepare error: %s", snd_strerror(err)); | |
796 return(0); | |
797 } | |
798 | |
799 return(1); /* ok, data should be accepted again */ | |
800 } | |
801 | |
628 /* | 802 /* |
629 plays 'len' bytes of 'data' | 803 plays 'len' bytes of 'data' |
630 returns: number of bytes played | 804 returns: number of bytes played |
631 modified last at 26.06.02 by jp | 805 modified last at 29.06.02 by jp |
806 thanxs for marius <marius@rospot.com> for giving us the light ;) | |
632 */ | 807 */ |
633 | 808 |
634 static int play(void* data, int len, int flags) | 809 static int play(void* data, int len, int flags) |
635 { | 810 { |
636 | 811 |
637 snd_pcm_status_t *status; | 812 //ao_data.bps is always 4 cause its set to channels * 2 by alsa_format?? |
638 | 813 int num_frames = len / ao_data.bps; |
639 int num_frames=len/ao_data.bps; | |
640 signed short *output_samples=data; | 814 signed short *output_samples=data; |
641 snd_pcm_sframes_t res = 0; | 815 snd_pcm_sframes_t res = 0; |
816 | |
817 //printf("alsa-play: frames=%i, len=%i",num_frames,len); | |
642 | 818 |
643 if (!alsa_handler) { | 819 if (!alsa_handler) { |
644 printf("alsa-play: device configuration error"); | 820 printf("alsa-play: device configuration error"); |
645 return 0; | 821 return 0; |
646 } | 822 } |
647 | 823 |
648 do { | 824 while (num_frames > 0) { |
649 if (res == -EPIPE) { /* underrun */ | 825 |
650 snd_pcm_status_alloca(&status); | 826 if (ao_mmap) { |
651 if ((res = snd_pcm_status(alsa_handler, status))<0) { | 827 res = snd_pcm_mmap_writei(alsa_handler, (void *)output_samples, num_frames); |
652 printf("alsa-play: buffer underrun. can't determine length"); | |
653 } else { | 828 } else { |
654 if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) { | 829 res = snd_pcm_writei(alsa_handler, (void *)output_samples, num_frames); |
655 struct timeval now, diff, tstamp; | 830 } |
656 gettimeofday(&now, 0); | 831 |
657 snd_pcm_status_get_trigger_tstamp(status, &tstamp); | 832 if (res == -EAGAIN) { |
658 timersub(&now, &tstamp, &diff); | 833 snd_pcm_wait(alsa_handler, 1000); |
659 printf("alsa-play: xrun of at least %.3f msecs. resetting stream", | 834 } |
660 diff.tv_sec * 1000 + diff.tv_usec / 1000.0); | 835 else if (res == -EPIPE) { /* underrun */ |
661 } else | 836 if (xrun("play") <= 0) { |
662 printf("alsa-play: xrun. can't determine length"); | 837 printf("alsa-play: xrun reset error"); |
663 } | 838 return(0); |
664 res = snd_pcm_prepare(alsa_handler); | 839 } |
665 } | 840 } |
666 else if (res == -ESTRPIPE) { /* suspend */ | 841 else if (res == -ESTRPIPE) { /* suspend */ |
667 printf("alsa-play: pcm in suspend mode. trying to resume"); | 842 printf("alsa-play: pcm in suspend mode. trying to resume\n"); |
668 while ((res = snd_pcm_resume(alsa_handler)) == -EAGAIN) | 843 while ((res = snd_pcm_resume(alsa_handler)) == -EAGAIN) |
669 sleep(1); | 844 sleep(1); |
670 if (res < 0) | 845 } |
671 res = snd_pcm_prepare(alsa_handler); | 846 else if (res < 0) { |
672 } | 847 printf("alsa-play: unknown status, trying to reset soundcard\n"); |
673 | 848 if ((res = snd_pcm_prepare(alsa_handler)) < 0) { |
674 if (res >= 0) | 849 printf("alsa-play: snd prepare error"); |
675 res = snd_pcm_writei(alsa_handler, (void *)output_samples, num_frames); | 850 return(0); |
676 | 851 break; |
677 if (res > 0) { | 852 } |
678 output_samples += ao_data.channels * res; | 853 } |
679 num_frames -= res; | 854 |
680 } | 855 if (res > 0) { |
681 | 856 output_samples += ao_data.channels * res; |
682 } while (res == -EPIPE || num_frames > 0); | 857 num_frames -= res; |
858 } | |
859 | |
860 } //end while | |
683 | 861 |
684 if (res < 0) { | 862 if (res < 0) { |
685 printf("alsa-play: write error %s", snd_strerror(res)); | 863 printf("alsa-play: write error %s", snd_strerror(res)); |
686 return 0; | 864 return 0; |
687 } | 865 } |
688 return res < 0 ? (int)res : len; | 866 return res < 0 ? (int)res : len; |
689 } | 867 } |
690 | 868 |
691 | |
692 /* how many byes are free in the buffer */ | 869 /* how many byes are free in the buffer */ |
693 static int get_space() | 870 static int get_space() |
694 { | 871 { |
695 snd_pcm_status_t *status; | 872 snd_pcm_status_t *status; |
696 int ret; | 873 int ret; |
874 char *str_status; | |
875 | |
876 //snd_pcm_sframes_t avail_frames = 0; | |
697 | 877 |
698 if ((ret = snd_pcm_status_malloc(&status)) < 0) | 878 if ((ret = snd_pcm_status_malloc(&status)) < 0) |
699 { | 879 { |
700 printf("alsa-space: memory allocation error: %s\n", snd_strerror(ret)); | 880 printf("alsa-space: memory allocation error: %s\n", snd_strerror(ret)); |
701 return(0); | 881 return(0); |
707 return(0); | 887 return(0); |
708 } | 888 } |
709 | 889 |
710 switch(snd_pcm_status_get_state(status)) | 890 switch(snd_pcm_status_get_state(status)) |
711 { | 891 { |
712 case SND_PCM_STATE_OPEN: | 892 case SND_PCM_STATE_OPEN: |
713 case SND_PCM_STATE_PREPARED: | 893 str_status = "open"; |
714 case SND_PCM_STATE_RUNNING: | 894 case SND_PCM_STATE_PREPARED: |
715 ret = snd_pcm_status_get_avail(status) * ao_data.bps; | 895 if (str_status != "open") |
716 break; | 896 str_status = "prepared"; |
717 default: | 897 case SND_PCM_STATE_RUNNING: |
718 ret = 0; | 898 ret = snd_pcm_status_get_avail(status) * ao_data.bps; |
719 } | 899 //avail_frames = snd_pcm_avail_update(alsa_handler) * ao_data.bps; |
720 | 900 if (str_status != "open" && str_status != "prepared") |
901 str_status = "running"; | |
902 break; | |
903 case SND_PCM_STATE_PAUSED: | |
904 if (verbose) printf("alsa-space: paused"); | |
905 str_status = "paused"; | |
906 ret = 0; | |
907 break; | |
908 case SND_PCM_STATE_XRUN: | |
909 xrun("space"); | |
910 str_status = "xrun"; | |
911 ret = 0; | |
912 break; | |
913 default: | |
914 str_status = "undefined"; | |
915 ret = 0; | |
916 } | |
917 | |
918 if (verbose && str_status != "running") | |
919 printf("alsa-space: free space = %i, status=%i, %s --\n", ret, status, str_status); | |
721 snd_pcm_status_free(status); | 920 snd_pcm_status_free(status); |
722 | 921 |
723 if (ret < 0) | 922 if (ret < 0) { |
923 printf("negative value!!\n"); | |
724 ret = 0; | 924 ret = 0; |
725 //printf("alsa-space: free space = %i",ret); | 925 } |
926 | |
726 return(ret); | 927 return(ret); |
727 } | 928 } |
728 | 929 |
729 /* delay in seconds between first and last sample in buffer */ | 930 /* delay in seconds between first and last sample in buffer */ |
730 static float get_delay() | 931 static float get_delay() |
731 { | 932 { |
933 | |
934 if (alsa_handler) { | |
935 | |
732 snd_pcm_status_t *status; | 936 snd_pcm_status_t *status; |
733 float ret; | 937 float ret; |
734 | 938 |
735 if ((ret = snd_pcm_status_malloc(&status)) < 0) | 939 if ((ret = snd_pcm_status_malloc(&status)) < 0) |
736 { | 940 { |
758 snd_pcm_status_free(status); | 962 snd_pcm_status_free(status); |
759 | 963 |
760 if (ret < 0) | 964 if (ret < 0) |
761 ret = 0; | 965 ret = 0; |
762 return(ret); | 966 return(ret); |
967 | |
968 } else { | |
969 return(0); | |
970 } | |
763 } | 971 } |