cpal icon indicating copy to clipboard operation
cpal copied to clipboard

ALSA PCM.try_recover: error handling

Open lautarodragan opened this issue 5 months ago • 1 comments

When passing silent=false to PCM.try_recover, it'll write to stderr a detail of the error.

In this case, it's always underrun occurred or overrun occurred. It logs this even when successfully recovering. This is the relevant code in ALSA's GitHub mirror, as of now:

        if (err == -EPIPE) {
                const char *s;
                if (snd_pcm_stream(pcm) == SND_PCM_STREAM_PLAYBACK)
                        s = "underrun";
                else
                        s = "overrun";
                if (!silent)
                        SNDERR("%s occurred", s);
                err = snd_pcm_prepare(pcm);
                if (err < 0) {
                        SNDERR("cannot recovery from %s, prepare failed: %s", s, snd_strerror(err));
                        return err;
                }
                return 0;
        }

By setting it to silent, we lose this detail (whether it was an under- or over-run). But letting it write directly to stderr is messing up my ratatui app :( and I don't think there's a standard, cross-platform, safe way to prevent anything from writing to std(out|err) in-process.

Failures in try_handle will now call error_callback, but these noisy underrun occurred always recovered successfully, in my case.

I guess we could also offer some sort of #[cfg(feature = "alsa-silent-try_recover")] or adding a run-time arg to the function chain all the way up to build_output_stream and then Rodio's try_from_device, or add it as an option in struct StreamConfig. Personally, I'd prefer avoiding the added code complexity of the run-time option, but I could see some value in the build-time option, if properly documented.

Also, calling the error callback will wind up doing a eprintln that wasn't happening previously (in rodio, at least), so we're trading one stderr output for another. That's something to manage in Rodio, but still worth mentioning.


For context, I can consistently trigger these by calling Rodio's sink.try_seek too quickly (as fast as crossterm's event::poll will trigger it, while I hold down a key I'm mapping to seek in my app).

Silencing this error gets rid of the visual noise/glitches, but I still need to manage the sound glitches somehow. But that's probably on me for not debouncing calls to seek and/or using OutputStream::try_default() and not setting anything manually in SupportedStreamConfig (buffer_size, I guess).

lautarodragan avatar Sep 06 '24 04:09 lautarodragan