Fix transmit failures when using virtual devices

Direwolf does not send a continuous stream to the audio device.
It merely sends samples when it has a transmission to accomplish.
This seems to work okay on hardware devices, but is causing an
issue when working against a virtual device like plug or dmix
because the vitual device seems to get into a weird state while
direwolf is not sending samples.  This causes it to error out
with -EBADFD on every transmission after the first.

Direwolf tries to recover from this error by running
snd_pcm_recover() on the output device.  Inspecting the code of
this function revealed that it was a noop on any errors other
than -EPIPE or -ESTRPIPE, so it had no hope of fixing any issues
other than a buffer underrun or a suspended device.

This patch breaks things out so that we only run snd_pcm_recover()
on -EPIPE or -ESTRPIPE, and for any other error we go ahead and
just try to prepare the device again.  This appears to work to
make transmissions subsequent to the first work correctly on
virtual devices.  We handle -EBADFD differently merely so that
we don't print the error message for a quasi-expected condition.

This problem could also possibly be solved by unprotecting the
snd_pcm_prepare() on line 1174 of audio.c and just preparing the
device whether it apparently needs it or not, but I'm wary of the
unintended consequences of doing so in cases where the code is
currently working.  Much more testing would be necessary for that
solution.  This solution should only touch cases that are not working
currently regardless.
This commit is contained in:
Annaliese McDermond 2018-12-02 03:19:48 +00:00 committed by wb2osz
parent b915842c9f
commit 91d5b90e2a
1 changed files with 15 additions and 1 deletions

16
audio.c
View File

@ -1204,6 +1204,17 @@ int audio_flush (int a)
snd_pcm_recover (adev[a].audio_out_handle, k, 1);
}
else if (k == -ESTRPIPE) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Driver suspended, recovering\n");
snd_pcm_recover(adev[a].audio_out_handle, k, 1);
}
else if (k == -EBADFD) {
k = snd_pcm_prepare (adev[a].audio_out_handle);
if(k < 0) {
dw_printf ("Error preparing after bad state: %s\n", snd_strerror(k));
}
}
else if (k < 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Audio write error: %s\n", snd_strerror(k));
@ -1211,7 +1222,10 @@ int audio_flush (int a)
/* Some other error condition. */
/* Try again. What do we have to lose? */
snd_pcm_recover (adev[a].audio_out_handle, k, 1);
k = snd_pcm_prepare (adev[a].audio_out_handle);
if(k < 0) {
dw_printf ("Error preparing after error: %s\n", snd_strerror(k));
}
}
else if (k != adev[a].outbuf_len / adev[a].bytes_per_frame) {
text_color_set(DW_COLOR_ERROR);