comparison src/audio/alsa/SDL_alsa_audio.c @ 4357:a10dac5858fe SDL-1.2

Recommendation from Lennart Poettering: In ALSA_OpenAudio(): instead of setting period_size+n_periods OR buffer_size I'd recommend copying the hwparams stuff before you do this, then first try period_size+n_periods, and then apply it with snd_pcm_hw_params() and check if that works. If it didn't you should take the copy of hwparams and try setting buffer_size and apply that via snd_pcm_hw_params() and check if that worked. And if that failed too, then take the copy and don't apply neither period nor buffer settings and see if that works.
author Sam Lantinga <slouken@libsdl.org>
date Mon, 19 Oct 2009 02:23:21 +0000
parents 471dac4b41dd
children df306a61a61d
comparison
equal deleted inserted replaced
4356:ab2dfac9d5c1 4357:a10dac5858fe
63 static int (*SDL_NAME(snd_pcm_prepare))(snd_pcm_t *pcm); 63 static int (*SDL_NAME(snd_pcm_prepare))(snd_pcm_t *pcm);
64 static int (*SDL_NAME(snd_pcm_drain))(snd_pcm_t *pcm); 64 static int (*SDL_NAME(snd_pcm_drain))(snd_pcm_t *pcm);
65 static const char *(*SDL_NAME(snd_strerror))(int errnum); 65 static const char *(*SDL_NAME(snd_strerror))(int errnum);
66 static size_t (*SDL_NAME(snd_pcm_hw_params_sizeof))(void); 66 static size_t (*SDL_NAME(snd_pcm_hw_params_sizeof))(void);
67 static size_t (*SDL_NAME(snd_pcm_sw_params_sizeof))(void); 67 static size_t (*SDL_NAME(snd_pcm_sw_params_sizeof))(void);
68 static void (*SDL_NAME(snd_pcm_hw_params_copy))(snd_pcm_hw_params_t *dst, const snd_pcm_hw_params_t *src);
68 static int (*SDL_NAME(snd_pcm_hw_params_any))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); 69 static int (*SDL_NAME(snd_pcm_hw_params_any))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
69 static int (*SDL_NAME(snd_pcm_hw_params_set_access))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t access); 70 static int (*SDL_NAME(snd_pcm_hw_params_set_access))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t access);
70 static int (*SDL_NAME(snd_pcm_hw_params_set_format))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val); 71 static int (*SDL_NAME(snd_pcm_hw_params_set_format))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val);
71 static int (*SDL_NAME(snd_pcm_hw_params_set_channels))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); 72 static int (*SDL_NAME(snd_pcm_hw_params_set_channels))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val);
72 static int (*SDL_NAME(snd_pcm_hw_params_get_channels))(const snd_pcm_hw_params_t *params, unsigned int *val); 73 static int (*SDL_NAME(snd_pcm_hw_params_get_channels))(const snd_pcm_hw_params_t *params, unsigned int *val);
99 { "snd_pcm_prepare", (void**)(char*)&SDL_NAME(snd_pcm_prepare) }, 100 { "snd_pcm_prepare", (void**)(char*)&SDL_NAME(snd_pcm_prepare) },
100 { "snd_pcm_drain", (void**)(char*)&SDL_NAME(snd_pcm_drain) }, 101 { "snd_pcm_drain", (void**)(char*)&SDL_NAME(snd_pcm_drain) },
101 { "snd_strerror", (void**)(char*)&SDL_NAME(snd_strerror) }, 102 { "snd_strerror", (void**)(char*)&SDL_NAME(snd_strerror) },
102 { "snd_pcm_hw_params_sizeof", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_sizeof) }, 103 { "snd_pcm_hw_params_sizeof", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_sizeof) },
103 { "snd_pcm_sw_params_sizeof", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_sizeof) }, 104 { "snd_pcm_sw_params_sizeof", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_sizeof) },
105 { "snd_pcm_hw_params_copy", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_copy) },
104 { "snd_pcm_hw_params_any", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_any) }, 106 { "snd_pcm_hw_params_any", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_any) },
105 { "snd_pcm_hw_params_set_access", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_access) }, 107 { "snd_pcm_hw_params_set_access", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_access) },
106 { "snd_pcm_hw_params_set_format", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_format) }, 108 { "snd_pcm_hw_params_set_format", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_format) },
107 { "snd_pcm_hw_params_set_channels", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_channels) }, 109 { "snd_pcm_hw_params_set_channels", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_channels) },
108 { "snd_pcm_hw_params_get_channels", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_channels) }, 110 { "snd_pcm_hw_params_get_channels", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_channels) },
363 SDL_NAME(snd_pcm_close)(pcm_handle); 365 SDL_NAME(snd_pcm_close)(pcm_handle);
364 pcm_handle = NULL; 366 pcm_handle = NULL;
365 } 367 }
366 } 368 }
367 369
370 static int ALSA_finalize_hardware(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *hwparams, int override)
371 {
372 int status;
373 snd_pcm_uframes_t bufsize;
374
375 /* "set" the hardware with the desired parameters */
376 status = SDL_NAME(snd_pcm_hw_params)(pcm_handle, hwparams);
377 if ( status < 0 ) {
378 return(-1);
379 }
380
381 /* Get samples for the actual buffer size */
382 status = SDL_NAME(snd_pcm_hw_params_get_buffer_size)(hwparams, &bufsize);
383 if ( status < 0 ) {
384 return(-1);
385 }
386 if ( !override && bufsize != spec->samples * 2 ) {
387 return(-1);
388 }
389
390 /* FIXME: Is this safe to do? */
391 spec->samples = bufsize / 2;
392
393 /* This is useful for debugging */
394 if ( getenv("SDL_AUDIO_ALSA_DEBUG") ) {
395 snd_pcm_sframes_t persize = 0;
396 unsigned int periods = 0;
397
398 SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams, &persize, NULL);
399 SDL_NAME(snd_pcm_hw_params_get_periods)(hwparams, &periods, NULL);
400
401 fprintf(stderr, "ALSA: period size = %ld, periods = %u, buffer size = %lu\n", persize, periods, bufsize);
402 }
403 return(0);
404 }
405
406 static int ALSA_set_period_size(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *params)
407 {
408 const char *env;
409 int status;
410 int override = 0;
411 snd_pcm_hw_params_t *hwparams;
412 snd_pcm_uframes_t frames;
413 unsigned int periods;
414
415 /* Copy the hardware parameters for this setup */
416 snd_pcm_hw_params_alloca(&hwparams);
417 SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params);
418
419 env = getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE");
420 if ( env ) {
421 override = SDL_strol(env);
422 if ( override == 0 ) {
423 return(-1);
424 }
425 }
426
427 frames = spec->samples;
428 status = SDL_NAME(snd_pcm_hw_params_set_period_size_near)(pcm_handle, hwparams, &frames, NULL);
429 if ( status < 0 ) {
430 return(-1);
431 }
432
433 periods = 2;
434 status = SDL_NAME(snd_pcm_hw_params_set_periods_near)(pcm_handle, hwparams, &periods, NULL);
435 if ( status < 0 ) {
436 return(-1);
437 }
438
439 return ALSA_finalize_hardware(this, spec, hwparams, override);
440 }
441
442 static int ALSA_set_buffer_size(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *params)
443 {
444 const char *env;
445 int status;
446 int override = 0;
447 snd_pcm_hw_params_t *hwparams;
448 snd_pcm_uframes_t frames;
449
450 /* Copy the hardware parameters for this setup */
451 snd_pcm_hw_params_alloca(&hwparams);
452 SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params);
453
454 env = getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE");
455 if ( env ) {
456 override = SDL_strol(env);
457 if ( override == 0 ) {
458 return(-1);
459 }
460 }
461
462 frames = spec->samples * 2;
463 status = SDL_NAME(snd_pcm_hw_params_set_buffer_size_near)(pcm_handle, hwparams, &frames);
464 if ( status < 0 ) {
465 return(-1);
466 }
467
468 return ALSA_finalize_hardware(this, spec, hwparams, override);
469 }
470
368 static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec) 471 static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec)
369 { 472 {
370 int status; 473 int status;
371 snd_pcm_hw_params_t *hwparams; 474 snd_pcm_hw_params_t *hwparams;
372 snd_pcm_sw_params_t *swparams; 475 snd_pcm_sw_params_t *swparams;
373 snd_pcm_format_t format; 476 snd_pcm_format_t format;
374 snd_pcm_uframes_t frames;
375 unsigned int rate; 477 unsigned int rate;
376 unsigned int periods;
377 unsigned int channels; 478 unsigned int channels;
479 snd_pcm_uframes_t bufsize;
378 Uint16 test_format; 480 Uint16 test_format;
379 481
380 /* Open the audio device */ 482 /* Open the audio device */
381 /* Name of device should depend on # channels in spec */ 483 /* Name of device should depend on # channels in spec */
382 status = SDL_NAME(snd_pcm_open)(&pcm_handle, get_audio_device(spec->channels), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); 484 status = SDL_NAME(snd_pcm_open)(&pcm_handle, get_audio_device(spec->channels), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
467 return(-1); 569 return(-1);
468 } 570 }
469 spec->freq = rate; 571 spec->freq = rate;
470 572
471 /* Set the buffer size, in samples */ 573 /* Set the buffer size, in samples */
472 if (getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE")) { 574 if ( ALSA_set_period_size(this, spec, hwparams) < 0 &&
473 frames = spec->samples; 575 ALSA_set_buffer_size(this, spec, hwparams) < 0 ) {
474 status = SDL_NAME(snd_pcm_hw_params_set_period_size_near)(pcm_handle, hwparams, &frames, NULL); 576 /* Failed to set buffer size, try to just use the defaults */
475 if ( status < 0 ) { 577 if ( ALSA_finalize_hardware(this, spec, hwparams, 1) < 0 ) {
476 SDL_SetError("Couldn't set period size: %s", SDL_NAME(snd_strerror)(status)); 578 SDL_SetError("Couldn't set hardware audio parameters: %s", SDL_NAME(snd_strerror)(status));
477 ALSA_CloseAudio(this); 579 ALSA_CloseAudio(this);
478 return(-1); 580 return(-1);
479 } 581 }
480
481 spec->samples = frames;
482
483 periods = 2;
484 status = SDL_NAME(snd_pcm_hw_params_set_periods_near)(pcm_handle, hwparams, &periods, NULL);
485 if ( status < 0 ) {
486 SDL_SetError("Couldn't set period count: %s", SDL_NAME(snd_strerror)(status));
487 ALSA_CloseAudio(this);
488 return(-1);
489 }
490 } else {
491 frames = spec->samples * 2;
492 status = SDL_NAME(snd_pcm_hw_params_set_buffer_size_near)(pcm_handle, hwparams, &frames);
493 if ( status < 0 ) {
494 SDL_SetError("Couldn't set buffer size: %s", SDL_NAME(snd_strerror)(status));
495 ALSA_CloseAudio(this);
496 return(-1);
497 }
498 }
499
500 /* "set" the hardware with the desired parameters */
501 status = SDL_NAME(snd_pcm_hw_params)(pcm_handle, hwparams);
502 if ( status < 0 ) {
503 SDL_SetError("Couldn't set hardware audio parameters: %s", SDL_NAME(snd_strerror)(status));
504 ALSA_CloseAudio(this);
505 return(-1);
506 }
507
508 /* This is useful for debugging */
509 if (getenv("SDL_AUDIO_ALSA_DEBUG_PERIOD_SIZE")) {
510 snd_pcm_uframes_t bufsize;
511 snd_pcm_sframes_t persize;
512 unsigned int periods; int dir;
513
514 SDL_NAME(snd_pcm_hw_params_get_buffer_size)(hwparams, &bufsize);
515 SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams, &persize, &dir);
516 SDL_NAME(snd_pcm_hw_params_get_periods)(hwparams, &periods, &dir);
517
518 fprintf(stderr, "ALSA: period size = %ld, periods = %u, buffer size = %lu\n", persize, periods, bufsize);
519 } 582 }
520 583
521 /* Set the software parameters */ 584 /* Set the software parameters */
522 snd_pcm_sw_params_alloca(&swparams); 585 snd_pcm_sw_params_alloca(&swparams);
523 status = SDL_NAME(snd_pcm_sw_params_current)(pcm_handle, swparams); 586 status = SDL_NAME(snd_pcm_sw_params_current)(pcm_handle, swparams);