alsa: More efficient audio thread iteration.

Now we sleep the thread in WaitDevice until ALSA reawakens it because it
needs more data, and we feed it exactly as much as it can take at that
point.

Like the recent PulseAudio changes, this is both more efficient, reliable,
and consistent.
main
Ryan C. Gordon 2023-09-30 11:50:53 -04:00
parent 47cba08259
commit 64fee85c69
No known key found for this signature in database
GPG Key ID: FA148B892AB48044
1 changed files with 22 additions and 13 deletions

View File

@ -333,33 +333,34 @@ static void no_swizzle(SDL_AudioDevice *device, void *buffer, Uint32 bufferlen)
/* This function waits until it is possible to write a full sound buffer */ /* This function waits until it is possible to write a full sound buffer */
static void ALSA_WaitDevice(SDL_AudioDevice *device) static void ALSA_WaitDevice(SDL_AudioDevice *device)
{ {
const snd_pcm_sframes_t needed = (snd_pcm_sframes_t)device->sample_frames; const int fulldelay = (int) ((((Uint64) device->sample_frames) * 1000) / device->spec.freq);
while (!SDL_AtomicGet(&device->shutdown)) { const int delay = SDL_max(fulldelay, 10);
const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(device->hidden->pcm_handle);
if (rc < 0 && rc != -EAGAIN) {
int status = rc;
status = ALSA_snd_pcm_recover(device->hidden->pcm_handle, status, 0); while (!SDL_AtomicGet(&device->shutdown)) {
const int rc = ALSA_snd_pcm_wait(device->hidden->pcm_handle, delay);
if (rc < 0 && (rc != -EAGAIN)) {
const int status = ALSA_snd_pcm_recover(device->hidden->pcm_handle, rc, 0);
if (status < 0) { if (status < 0) {
/* Hmm, not much we can do - abort */ /* Hmm, not much we can do - abort */
fprintf(stderr, "ALSA snd_pcm_avail failed (unrecoverable): %s\n", SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA snd_pcm_avail failed (unrecoverable): %s\n",
ALSA_snd_strerror(rc)); ALSA_snd_strerror(rc));
SDL_AudioDeviceDisconnected(device); SDL_AudioDeviceDisconnected(device);
} }
return; return;
} else if (rc < needed) {
const Uint32 delay = ((needed - (SDL_max(rc, 0))) * 1000) / device->spec.freq;
SDL_Delay(SDL_max(delay, 10));
} else {
break; /* ready to go! */
} }
if (rc > 0) {
break; // ready to go!
}
// Timed out! Make sure we aren't shutting down and then wait again.
} }
} }
static int ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) static int ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{ {
SDL_assert(buffer == device->hidden->mixbuf); SDL_assert(buffer == device->hidden->mixbuf);
Uint8 *sample_buf = device->hidden->mixbuf; Uint8 *sample_buf = (Uint8 *) buffer; // !!! FIXME: deal with this without casting away constness
const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec); const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec);
snd_pcm_uframes_t frames_left = (snd_pcm_uframes_t) (buflen / frame_size); snd_pcm_uframes_t frames_left = (snd_pcm_uframes_t) (buflen / frame_size);
@ -368,6 +369,7 @@ static int ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buf
while ((frames_left > 0) && !SDL_AtomicGet(&device->shutdown)) { while ((frames_left > 0) && !SDL_AtomicGet(&device->shutdown)) {
int status = ALSA_snd_pcm_writei(device->hidden->pcm_handle, int status = ALSA_snd_pcm_writei(device->hidden->pcm_handle,
sample_buf, frames_left); sample_buf, frames_left);
//SDL_Log("ALSA PLAYDEVICE: WROTE %d of %d bytes", (status >= 0) ? ((int) (status * frame_size)) : status, (int) (frames_left * frame_size));
if (status < 0) { if (status < 0) {
if (status == -EAGAIN) { if (status == -EAGAIN) {
/* Apparently snd_pcm_recover() doesn't handle this case - /* Apparently snd_pcm_recover() doesn't handle this case -
@ -399,6 +401,13 @@ static int ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buf
static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
{ {
const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(device->hidden->pcm_handle);
const int requested_frames = SDL_min(device->sample_frames, rc);
SDL_assert(requested_frames > 0);
const int requested_bytes = requested_frames * SDL_AUDIO_FRAMESIZE(device->spec);
SDL_assert(requested_bytes <= *buffer_size);
//SDL_Log("ALSA GETDEVICEBUF: NEED %d BYTES", requested_bytes);
*buffer_size = requested_bytes;
return device->hidden->mixbuf; return device->hidden->mixbuf;
} }