commit bbd1402d8bfd350185dcc967306c4c72094c2c21 from: Omar Polo date: Thu Mar 23 17:36:53 2023 UTC alsa: fix snd_pcm_writei usage snd_pcm_writei works in terms of audio frames, not buffer (bytes) length like snd_write does, so account for that. To do so, prepare a `bpf' (bytes per frame) multiplier in the configure stage and use it to convert back and forward between the buffer length and the number of frames. While here, also use snd_pcm_avail_update() to know how many frames may be written. commit - 34674849fb1efe5167c457f0dc98ee7fadcc8886 commit + bbd1402d8bfd350185dcc967306c4c72094c2c21 blob - 59128b71c8a1192389220edbb0178e4a416c51b9 blob + fe6c8acb9722c865545c5a6e0950f4ae2b0905b5 --- audio_alsa.c +++ audio_alsa.c @@ -24,6 +24,7 @@ #include "log.h" static snd_pcm_t *pcm; +static size_t bpf; int audio_open(void (*onmove_cb)(void *, int)) @@ -49,18 +50,24 @@ audio_setup(unsigned int bits, unsigned int rate, unsi int err; snd_pcm_format_t fmt; - if (bits == 8) + if (bits == 8) { fmt = SND_PCM_FORMAT_S8; - else if (bits == 16) + bpf = 1; + } else if (bits == 16) { fmt = SND_PCM_FORMAT_S16; - else if (bits == 24) + bpf = 2; + } else if (bits == 24) { fmt = SND_PCM_FORMAT_S24; - else if (bits == 32) + bpf = 4; + } else if (bits == 32) { fmt = SND_PCM_FORMAT_S32; - else { + bpf = 4; + } else { log_warnx("can't handle %d bits", bits); return -1; } + + bpf *= channels; err = snd_pcm_set_params(pcm, fmt, SND_PCM_ACCESS_RW_INTERLEAVED, channels, rate, 1, 500000 /* 0.5s */); @@ -109,14 +116,33 @@ audio_revents(struct pollfd *pfds) size_t audio_write(const void *buf, size_t len) { - snd_pcm_sframes_t ret; + snd_pcm_sframes_t avail, ret; + /* + * snd_pcm_writei works in terms of FRAMES, not BYTES! + */ + len /= bpf; + + avail = snd_pcm_avail_update(pcm); + if (avail < 0) { + if (avail == -EPIPE) { + log_warnx("alsa xrun occurred"); + return 0; + } + log_warnx("snd_pcm_avail_update failure: %s", + snd_strerror(avail)); + return 0; + } + + if (len > avail) + len = avail; + ret = snd_pcm_writei(pcm, buf, len); if (ret < 0) { log_warnx("snd_pcm_writei failed: %s", snd_strerror(ret)); return 0; } - return ret; + return ret * bpf; } int