From 8929b332cb473a13fc1f5406d9b1a48d509dce99 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 26 Jul 2020 13:13:01 +0200 Subject: Sound: Recover broken audio connection by closing and reopening the device --- src/libmobile/sender.c | 2 +- src/libsound/sound_alsa.c | 124 +++++++++++++++++++++++++++------------------- 2 files changed, 75 insertions(+), 51 deletions(-) diff --git a/src/libmobile/sender.c b/src/libmobile/sender.c index 4f0cd62..369d90e 100644 --- a/src/libmobile/sender.c +++ b/src/libmobile/sender.c @@ -347,7 +347,7 @@ void process_sender_audio(sender_t *sender, int *quit, int latspl) #endif count = sender->audio_get_tosend(sender->audio, latspl); if (count < 0) { - PDEBUG(DSENDER, DEBUG_ERROR, "Failed to get number of samples in buffer (rc = %d)!\n", count); + PDEBUG_CHAN(DSENDER, DEBUG_ERROR, "Failed to get number of samples in buffer (rc = %d)!\n", count); if (count == -EPIPE) { if (cant_recover) { cant_recover: diff --git a/src/libsound/sound_alsa.c b/src/libsound/sound_alsa.c index 21199c9..6cb0cc2 100644 --- a/src/libsound/sound_alsa.c +++ b/src/libsound/sound_alsa.c @@ -28,6 +28,9 @@ typedef struct sound { snd_pcm_t *phandle, *chandle; int pchannels, cchannels; + int channels; /* required number of channels */ + int samplerate; /* required sample rate */ + char *audiodev; /* required device */ double spl_deviation; /* how much deviation is one sample step */ double paging_phaseshift; /* phase to shift every sample */ double paging_phase; /* current phase */ @@ -112,9 +115,48 @@ error: return rc; } -static int sound_prepare(sound_t *sound) +static int dev_open(sound_t *sound) { - int rc; + int rc, rc_rec, rc_play; + + rc_play = snd_pcm_open(&sound->phandle, sound->audiodev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + rc_rec = snd_pcm_open(&sound->chandle, sound->audiodev, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); + if (rc_play < 0 && rc_rec < 0) { + PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s'! (%s)\n", sound->audiodev, snd_strerror(rc_play)); + PDEBUG(DSOUND, DEBUG_ERROR, "Run 'aplay -l' to get a list of available cards and devices.\n"); + PDEBUG(DSOUND, DEBUG_ERROR, "Then use 'hw::' for audio device.\n"); + return rc_play; + } + if (rc_play < 0) { + PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s' for playback! (%s) Please select a device that supports both direction audio.\n", sound->audiodev, snd_strerror(rc_play)); + return rc_play; + } + if (rc_rec < 0) { + PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s' for capture! (%s) Please select a device that supports both direction audio.\n", sound->audiodev, snd_strerror(rc_rec)); + return rc_rec; + } + + rc = set_hw_params(sound->phandle, sound->samplerate, &sound->pchannels); + if (rc < 0) { + PDEBUG(DSOUND, DEBUG_ERROR, "Failed to set playback hw params\n"); + return rc; + } + if (sound->pchannels < sound->channels) { + PDEBUG(DSOUND, DEBUG_ERROR, "Sound card only supports %d channel for playback.\n", sound->pchannels); + return rc; + } + PDEBUG(DSOUND, DEBUG_DEBUG, "Playback with %d channels.\n", sound->pchannels); + + rc = set_hw_params(sound->chandle, sound->samplerate, &sound->cchannels); + if (rc < 0) { + PDEBUG(DSOUND, DEBUG_ERROR, "Failed to set capture hw params\n"); + return rc; + } + if (sound->cchannels < sound->channels) { + PDEBUG(DSOUND, DEBUG_ERROR, "Sound card only supports %d channel for capture.\n", sound->cchannels); + return -EIO; + } + PDEBUG(DSOUND, DEBUG_DEBUG, "Capture with %d channels.\n", sound->cchannels); rc = snd_pcm_prepare(sound->phandle); if (rc < 0) { @@ -131,10 +173,18 @@ static int sound_prepare(sound_t *sound) return 0; } +static void dev_close(sound_t *sound) +{ + if (sound->phandle != NULL) + snd_pcm_close(sound->phandle); + if (sound->chandle != NULL) + snd_pcm_close(sound->chandle); +} + void *sound_open(const char *audiodev, double __attribute__((unused)) *tx_frequency, double __attribute__((unused)) *rx_frequency, int __attribute__((unused)) *am, int channels, double __attribute__((unused)) paging_frequency, int samplerate, int __attribute((unused)) latspl, double max_deviation, double __attribute__((unused)) max_modulation, double __attribute__((unused)) modulation_index) { sound_t *sound; - int rc, rc_rec, rc_play; + int rc; if (channels < 1 || channels > 2) { PDEBUG(DSOUND, DEBUG_ERROR, "Cannot use more than two channels with the same sound card!\n"); @@ -147,49 +197,13 @@ void *sound_open(const char *audiodev, double __attribute__((unused)) *tx_freque return NULL; } + sound->audiodev = strdup(audiodev); + sound->channels = channels; + sound->samplerate = samplerate; sound->spl_deviation = max_deviation / 32767.0; sound->paging_phaseshift = 1.0 / ((double)samplerate / 1000.0); - rc_play = snd_pcm_open(&sound->phandle, audiodev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); - rc_rec = snd_pcm_open(&sound->chandle, audiodev, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); - if (rc_play < 0 && rc_rec < 0) { - PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s'! (%s)\n", audiodev, snd_strerror(rc_play)); - PDEBUG(DSOUND, DEBUG_ERROR, "Run 'aplay -l' to get a list of available cards and devices.\n"); - PDEBUG(DSOUND, DEBUG_ERROR, "Then use 'hw::' for audio device.\n"); - goto error; - } - if (rc_play < 0) { - PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s' for playback! (%s) Please select a device that supports both direction audio.\n", audiodev, snd_strerror(rc_play)); - goto error; - } - if (rc_rec < 0) { - PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s' for capture! (%s) Please select a device that supports both direction audio.\n", audiodev, snd_strerror(rc_rec)); - goto error; - } - - rc = set_hw_params(sound->phandle, samplerate, &sound->pchannels); - if (rc < 0) { - PDEBUG(DSOUND, DEBUG_ERROR, "Failed to set playback hw params\n"); - goto error; - } - if (sound->pchannels < channels) { - PDEBUG(DSOUND, DEBUG_ERROR, "Sound card only supports %d channel for playback.\n", sound->pchannels); - goto error; - } - PDEBUG(DSOUND, DEBUG_DEBUG, "Playback with %d channels.\n", sound->pchannels); - - rc = set_hw_params(sound->chandle, samplerate, &sound->cchannels); - if (rc < 0) { - PDEBUG(DSOUND, DEBUG_ERROR, "Failed to set capture hw params\n"); - goto error; - } - if (sound->cchannels < channels) { - PDEBUG(DSOUND, DEBUG_ERROR, "Sound card only supports %d channel for capture.\n", sound->cchannels); - goto error; - } - PDEBUG(DSOUND, DEBUG_DEBUG, "Capture with %d channels.\n", sound->cchannels); - - rc = sound_prepare(sound); + rc = dev_open(sound); if (rc < 0) goto error; @@ -228,10 +242,8 @@ void sound_close(void *inst) { sound_t *sound = (sound_t *)inst; - if (sound->phandle != NULL) - snd_pcm_close(sound->phandle); - if (sound->chandle != NULL) - snd_pcm_close(sound->chandle); + dev_close(sound); + free(sound->audiodev); free(sound); } @@ -344,8 +356,12 @@ int sound_write(void *inst, sample_t **samples, uint8_t __attribute__((unused)) if (rc < 0) { PDEBUG(DSOUND, DEBUG_ERROR, "failed to write audio to interface (%s)\n", snd_strerror(rc)); if (rc == -EPIPE) { - sound_prepare(sound); + dev_close(sound); + rc = dev_open(sound); + if (rc < 0) + return rc; sound_start(sound); + return -EPIPE; /* indicate what happened */ } return rc; } @@ -390,8 +406,12 @@ int sound_read(void *inst, sample_t **samples, int num, int channels, double *rf PDEBUG(DSOUND, DEBUG_ERROR, "failed to read audio from interface (%s)\n", snd_strerror(rc)); /* recover read */ if (rc == -EPIPE) { - sound_prepare(sound); + dev_close(sound); + rc = dev_open(sound); + if (rc < 0) + return rc; sound_start(sound); + return -EPIPE; /* indicate what happened */ } return rc; } @@ -464,8 +484,12 @@ int sound_get_tosend(void *inst, int latspl) else PDEBUG(DSOUND, DEBUG_ERROR, "failed to get delay from interface (%s)\n", snd_strerror(rc)); if (rc == -EPIPE) { - sound_prepare(sound); + dev_close(sound); + rc = dev_open(sound); + if (rc < 0) + return rc; sound_start(sound); + return -EPIPE; /* indicate what happened */ } return rc; } -- cgit v1.2.3