Index: linux/audio_output_alsa.cc |
diff --git a/linux/audio_output_alsa.cc b/linux/audio_output_alsa.cc |
index dc763ba46024157c64509916161204e043e12c5a..fdf4c6e811c63c7b0c6aea49fa17b7f7662fe98b 100644 |
--- a/linux/audio_output_alsa.cc |
+++ b/linux/audio_output_alsa.cc |
@@ -13,6 +13,57 @@ |
#include "log.h" |
#include "threading.h" |
+namespace { |
+ |
+bool isDeviceReady() { |
+ snd_ctl_card_info_t *card_info; |
+ snd_ctl_card_info_alloca(&card_info); |
+ |
+ snd_pcm_info_t *pcm_info; |
+ snd_pcm_info_alloca(&pcm_info); |
+ |
+ int valid_playback_devices = 0; |
+ int card_index = -1; |
+ |
+ while(snd_card_next(&card_index) == 0 && card_index >= 0) { |
+ char card_name[20]; |
+ snprintf(card_name, sizeof(card_name), "hw:%d", card_index); |
+ LOG(INFO) << "Checking ALSA sound card " << card_name; |
+ |
+ snd_ctl_t *ctl; |
+ if(snd_ctl_open(&ctl, card_name, 0) < 0) |
+ continue; |
+ |
+ snd_ctl_card_info(ctl, card_info); |
+ |
+ int dev_index = -1; |
+ while (snd_ctl_pcm_next_device(ctl, &dev_index) == 0 && dev_index >= 0) { |
+ char device_name[30]; |
+ snprintf(device_name, sizeof(device_name), |
+ "hw:%d,%d", card_index, dev_index); |
+ LOG(INFO) << "Checking ALSA sound device " << device_name; |
+ |
+ /* Obtain info about this particular device */ |
+ snd_pcm_info_set_device(pcm_info, dev_index); |
+ snd_pcm_info_set_subdevice(pcm_info, 0); |
+ snd_pcm_info_set_stream(pcm_info, SND_PCM_STREAM_PLAYBACK); |
+ if (snd_ctl_pcm_info(ctl, pcm_info) >= 0) { |
+ LOG(INFO) << " Valid playback device: " << device_name; |
+ valid_playback_devices++; |
+ } |
+ } |
+ snd_ctl_close(ctl); |
+ } |
+ |
+ LOG(INFO) << "Total valid playback devices: " << valid_playback_devices; |
+ if (valid_playback_devices == 0) { |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+} |
+ |
namespace speech_synthesis { |
class LinuxAlsaAudioOutput : public AudioOutput, public Runnable { |
@@ -32,93 +83,44 @@ class LinuxAlsaAudioOutput : public AudioOutput, public Runnable { |
return true; |
} |
+ if (!isDeviceReady()) { |
+ return false; |
+ } |
+ |
int err; |
if ((err = snd_pcm_open(&pcm_out_handle_, |
- "plughw:0,0", |
+ "default", |
SND_PCM_STREAM_PLAYBACK, |
0)) < 0) { |
LOG(INFO) << "Can't open wave output: " << snd_strerror(err) << "\n"; |
return false; |
} |
- snd_pcm_hw_params_t *hw_params; |
- if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) { |
- LOG(INFO) << "Can't alloc sound hardware struct " << |
- snd_strerror(err) << "\n"; |
- return false; |
- } |
- |
- if ((err = snd_pcm_hw_params_any(pcm_out_handle_, hw_params)) < 0) { |
- LOG(INFO) << "Can't init sound hardware struct: " << |
- snd_strerror(err) << "\n"; |
- return false; |
- } |
- |
- if ((err = snd_pcm_hw_params_set_access( |
- pcm_out_handle_, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { |
- LOG(INFO) << "Can't set access: " << snd_strerror(err) << "\n"; |
- return false; |
- } |
- |
- if ((err = snd_pcm_hw_params_set_format(pcm_out_handle_, |
- hw_params, |
- SND_PCM_FORMAT_S16_LE)) < 0) { |
- LOG(INFO) << "Can't set 16-bit: " << snd_strerror(err) << "\n"; |
- return false; |
- } |
- |
- if ((err = snd_pcm_hw_params_set_rate(pcm_out_handle_, |
- hw_params, |
- 44100, |
- 0)) < 0) { |
- LOG(INFO) << "Can't set rate to 44100: " << snd_strerror(err) << "\n"; |
- return false; |
- } |
- |
- if ((err = snd_pcm_hw_params_set_channels(pcm_out_handle_, |
- hw_params, |
- 1)) < 0) { |
- LOG(INFO) << "Can't set channels to 1: " << snd_strerror(err) << "\n"; |
- return false; |
- } |
- |
- int dir = 0; |
- snd_pcm_uframes_t desired_period = 512; |
- if ((err = snd_pcm_hw_params_set_period_size_near( |
- pcm_out_handle_, hw_params, &desired_period, &dir)) < 0) { |
- LOG(INFO) << "Can't set period size: " << snd_strerror(err) << "\n"; |
+ sample_rate_ = 44100; |
+ channel_count_ = 1; |
+ int soft_resample = 1; |
+ unsigned int latency_us = 50000; |
+ if ((err = snd_pcm_set_params(pcm_out_handle_, |
+ SND_PCM_FORMAT_S16_LE, |
+ SND_PCM_ACCESS_RW_INTERLEAVED, |
+ channel_count_, |
+ sample_rate_, |
+ soft_resample, |
+ latency_us)) < 0) { |
+ LOG(INFO) << "Can't set pcm parameters " << snd_strerror(err); |
return false; |
} |
- snd_pcm_uframes_t period_size; |
- snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir); |
- chunk_size_ = static_cast<int>(period_size); |
- |
- snd_pcm_uframes_t desired_buffer_size = period_size * 4; |
- if ((err = snd_pcm_hw_params_set_buffer_size( |
- pcm_out_handle_, hw_params, desired_buffer_size)) < 0) { |
- LOG(INFO) << "Can't set buffer size: " << snd_strerror(err) << "\n"; |
- return false; |
- } |
- |
- snd_pcm_uframes_t buffer_size; |
- snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size); |
- total_buffer_size_ = static_cast<int>(buffer_size); |
- |
- unsigned int rate; |
- snd_pcm_hw_params_get_rate(hw_params, &rate, &dir); |
- sample_rate_ = rate; |
- |
- unsigned int channels; |
- snd_pcm_hw_params_get_channels(hw_params, &channels); |
- channel_count_ = channels; |
- |
- if ((err = snd_pcm_hw_params(pcm_out_handle_, hw_params)) < 0) { |
- LOG(INFO) << "Can't set hardware params: " << snd_strerror(err) << "\n"; |
+ snd_pcm_uframes_t buffer_size = 0; |
+ snd_pcm_uframes_t period_size = 0; |
+ if ((err = snd_pcm_get_params(pcm_out_handle_, &buffer_size, &period_size)) |
+ < 0) { |
+ LOG(INFO) << "Can't get pcm parameters: " << snd_strerror(err); |
return false; |
} |
- snd_pcm_hw_params_free(hw_params); |
+ chunk_size_ = period_size; |
+ total_buffer_size_ = buffer_size; |
if ((err = snd_pcm_prepare(pcm_out_handle_)) < 0) { |
LOG(INFO) << "Can't prepare: " << snd_strerror(err) << "\n"; |
@@ -185,7 +187,11 @@ class LinuxAlsaAudioOutput : public AudioOutput, public Runnable { |
static_cast<snd_pcm_uframes_t>(chunk_size)); |
if (err < 0) { |
LOG(INFO) << "Write error: " << err << snd_strerror(err) << "\n"; |
- return; |
+ do { |
+ sleep(1); |
+ LOG(INFO) << "Attempting to recover audio"; |
+ } while (keep_running_ && |
+ 0 != snd_pcm_recover(pcm_out_handle_, err, 0)); |
} |
} |