| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 // | 4 // |
| 5 // Implementation of audio output using the Linux ALSA interface. | 5 // Implementation of audio output using the Linux ALSA interface. |
| 6 | 6 |
| 7 #include <fcntl.h> | 7 #include <fcntl.h> |
| 8 #include <stdio.h> | 8 #include <stdio.h> |
| 9 | 9 |
| 10 #include <alsa/asoundlib.h> | 10 #include <alsa/asoundlib.h> |
| 11 | 11 |
| 12 #include "audio_output.h" | 12 #include "audio_output.h" |
| 13 #include "log.h" | 13 #include "log.h" |
| 14 #include "threading.h" | 14 #include "threading.h" |
| 15 | 15 |
| 16 namespace { |
| 17 |
| 18 bool isDeviceReady() { |
| 19 snd_ctl_card_info_t *card_info; |
| 20 snd_ctl_card_info_alloca(&card_info); |
| 21 |
| 22 snd_pcm_info_t *pcm_info; |
| 23 snd_pcm_info_alloca(&pcm_info); |
| 24 |
| 25 int valid_playback_devices = 0; |
| 26 int card_index = -1; |
| 27 |
| 28 while(snd_card_next(&card_index) == 0 && card_index >= 0) { |
| 29 char card_name[20]; |
| 30 snprintf(card_name, sizeof(card_name), "hw:%d", card_index); |
| 31 LOG(INFO) << "Checking ALSA sound card " << card_name; |
| 32 |
| 33 snd_ctl_t *ctl; |
| 34 if(snd_ctl_open(&ctl, card_name, 0) < 0) |
| 35 continue; |
| 36 |
| 37 snd_ctl_card_info(ctl, card_info); |
| 38 |
| 39 int dev_index = -1; |
| 40 while (snd_ctl_pcm_next_device(ctl, &dev_index) == 0 && dev_index >= 0) { |
| 41 char device_name[30]; |
| 42 snprintf(device_name, sizeof(device_name), |
| 43 "hw:%d,%d", card_index, dev_index); |
| 44 LOG(INFO) << "Checking ALSA sound device " << device_name; |
| 45 |
| 46 /* Obtain info about this particular device */ |
| 47 snd_pcm_info_set_device(pcm_info, dev_index); |
| 48 snd_pcm_info_set_subdevice(pcm_info, 0); |
| 49 snd_pcm_info_set_stream(pcm_info, SND_PCM_STREAM_PLAYBACK); |
| 50 if (snd_ctl_pcm_info(ctl, pcm_info) >= 0) { |
| 51 LOG(INFO) << " Valid playback device: " << device_name; |
| 52 valid_playback_devices++; |
| 53 } |
| 54 } |
| 55 snd_ctl_close(ctl); |
| 56 } |
| 57 |
| 58 LOG(INFO) << "Total valid playback devices: " << valid_playback_devices; |
| 59 if (valid_playback_devices == 0) { |
| 60 return false; |
| 61 } |
| 62 return true; |
| 63 } |
| 64 |
| 65 } |
| 66 |
| 16 namespace speech_synthesis { | 67 namespace speech_synthesis { |
| 17 | 68 |
| 18 class LinuxAlsaAudioOutput : public AudioOutput, public Runnable { | 69 class LinuxAlsaAudioOutput : public AudioOutput, public Runnable { |
| 19 public: | 70 public: |
| 20 explicit LinuxAlsaAudioOutput(Threading* threading) | 71 explicit LinuxAlsaAudioOutput(Threading* threading) |
| 21 : threading_(threading), | 72 : threading_(threading), |
| 22 provider_(NULL) { | 73 provider_(NULL) { |
| 23 } | 74 } |
| 24 | 75 |
| 25 bool Init(AudioProvider *provider) { | 76 bool Init(AudioProvider *provider) { |
| 26 if (!provider) { | 77 if (!provider) { |
| 27 LOG(ERROR) << "An AudioProvider is required.\n"; | 78 LOG(ERROR) << "An AudioProvider is required.\n"; |
| 28 return false; | 79 return false; |
| 29 } | 80 } |
| 30 | 81 |
| 31 if (provider_) { | 82 if (provider_) { |
| 32 return true; | 83 return true; |
| 33 } | 84 } |
| 34 | 85 |
| 86 if (!isDeviceReady()) { |
| 87 return false; |
| 88 } |
| 89 |
| 35 int err; | 90 int err; |
| 36 if ((err = snd_pcm_open(&pcm_out_handle_, | 91 if ((err = snd_pcm_open(&pcm_out_handle_, |
| 37 "plughw:0,0", | 92 "default", |
| 38 SND_PCM_STREAM_PLAYBACK, | 93 SND_PCM_STREAM_PLAYBACK, |
| 39 0)) < 0) { | 94 0)) < 0) { |
| 40 LOG(INFO) << "Can't open wave output: " << snd_strerror(err) << "\n"; | 95 LOG(INFO) << "Can't open wave output: " << snd_strerror(err) << "\n"; |
| 41 return false; | 96 return false; |
| 42 } | 97 } |
| 43 | 98 |
| 44 snd_pcm_hw_params_t *hw_params; | 99 sample_rate_ = 44100; |
| 45 if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) { | 100 channel_count_ = 1; |
| 46 LOG(INFO) << "Can't alloc sound hardware struct " << | 101 int soft_resample = 1; |
| 47 snd_strerror(err) << "\n"; | 102 unsigned int latency_us = 50000; |
| 103 if ((err = snd_pcm_set_params(pcm_out_handle_, |
| 104 SND_PCM_FORMAT_S16_LE, |
| 105 SND_PCM_ACCESS_RW_INTERLEAVED, |
| 106 channel_count_, |
| 107 sample_rate_, |
| 108 soft_resample, |
| 109 latency_us)) < 0) { |
| 110 LOG(INFO) << "Can't set pcm parameters " << snd_strerror(err); |
| 48 return false; | 111 return false; |
| 49 } | 112 } |
| 50 | 113 |
| 51 if ((err = snd_pcm_hw_params_any(pcm_out_handle_, hw_params)) < 0) { | 114 snd_pcm_uframes_t buffer_size = 0; |
| 52 LOG(INFO) << "Can't init sound hardware struct: " << | 115 snd_pcm_uframes_t period_size = 0; |
| 53 snd_strerror(err) << "\n"; | 116 if ((err = snd_pcm_get_params(pcm_out_handle_, &buffer_size, &period_size)) |
| 117 < 0) { |
| 118 LOG(INFO) << "Can't get pcm parameters: " << snd_strerror(err); |
| 54 return false; | 119 return false; |
| 55 } | 120 } |
| 56 | 121 |
| 57 if ((err = snd_pcm_hw_params_set_access( | 122 chunk_size_ = period_size; |
| 58 pcm_out_handle_, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { | 123 total_buffer_size_ = buffer_size; |
| 59 LOG(INFO) << "Can't set access: " << snd_strerror(err) << "\n"; | |
| 60 return false; | |
| 61 } | |
| 62 | |
| 63 if ((err = snd_pcm_hw_params_set_format(pcm_out_handle_, | |
| 64 hw_params, | |
| 65 SND_PCM_FORMAT_S16_LE)) < 0) { | |
| 66 LOG(INFO) << "Can't set 16-bit: " << snd_strerror(err) << "\n"; | |
| 67 return false; | |
| 68 } | |
| 69 | |
| 70 if ((err = snd_pcm_hw_params_set_rate(pcm_out_handle_, | |
| 71 hw_params, | |
| 72 44100, | |
| 73 0)) < 0) { | |
| 74 LOG(INFO) << "Can't set rate to 44100: " << snd_strerror(err) << "\n"; | |
| 75 return false; | |
| 76 } | |
| 77 | |
| 78 if ((err = snd_pcm_hw_params_set_channels(pcm_out_handle_, | |
| 79 hw_params, | |
| 80 1)) < 0) { | |
| 81 LOG(INFO) << "Can't set channels to 1: " << snd_strerror(err) << "\n"; | |
| 82 return false; | |
| 83 } | |
| 84 | |
| 85 int dir = 0; | |
| 86 snd_pcm_uframes_t desired_period = 512; | |
| 87 if ((err = snd_pcm_hw_params_set_period_size_near( | |
| 88 pcm_out_handle_, hw_params, &desired_period, &dir)) < 0) { | |
| 89 LOG(INFO) << "Can't set period size: " << snd_strerror(err) << "\n"; | |
| 90 return false; | |
| 91 } | |
| 92 | |
| 93 snd_pcm_uframes_t period_size; | |
| 94 snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir); | |
| 95 chunk_size_ = static_cast<int>(period_size); | |
| 96 | |
| 97 snd_pcm_uframes_t desired_buffer_size = period_size * 4; | |
| 98 if ((err = snd_pcm_hw_params_set_buffer_size( | |
| 99 pcm_out_handle_, hw_params, desired_buffer_size)) < 0) { | |
| 100 LOG(INFO) << "Can't set buffer size: " << snd_strerror(err) << "\n"; | |
| 101 return false; | |
| 102 } | |
| 103 | |
| 104 snd_pcm_uframes_t buffer_size; | |
| 105 snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size); | |
| 106 total_buffer_size_ = static_cast<int>(buffer_size); | |
| 107 | |
| 108 unsigned int rate; | |
| 109 snd_pcm_hw_params_get_rate(hw_params, &rate, &dir); | |
| 110 sample_rate_ = rate; | |
| 111 | |
| 112 unsigned int channels; | |
| 113 snd_pcm_hw_params_get_channels(hw_params, &channels); | |
| 114 channel_count_ = channels; | |
| 115 | |
| 116 if ((err = snd_pcm_hw_params(pcm_out_handle_, hw_params)) < 0) { | |
| 117 LOG(INFO) << "Can't set hardware params: " << snd_strerror(err) << "\n"; | |
| 118 return false; | |
| 119 } | |
| 120 | |
| 121 snd_pcm_hw_params_free(hw_params); | |
| 122 | 124 |
| 123 if ((err = snd_pcm_prepare(pcm_out_handle_)) < 0) { | 125 if ((err = snd_pcm_prepare(pcm_out_handle_)) < 0) { |
| 124 LOG(INFO) << "Can't prepare: " << snd_strerror(err) << "\n"; | 126 LOG(INFO) << "Can't prepare: " << snd_strerror(err) << "\n"; |
| 125 return false; | 127 return false; |
| 126 } | 128 } |
| 127 | 129 |
| 128 keep_running_ = true; | 130 keep_running_ = true; |
| 129 provider_ = provider; | 131 provider_ = provider; |
| 130 return true; | 132 return true; |
| 131 } | 133 } |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 178 if (!provider_->FillAudioBuffer(chunk, chunk_size)) { | 180 if (!provider_->FillAudioBuffer(chunk, chunk_size)) { |
| 179 LOG(INFO) << "Could not fill audio buffer\n"; | 181 LOG(INFO) << "Could not fill audio buffer\n"; |
| 180 break; | 182 break; |
| 181 } | 183 } |
| 182 | 184 |
| 183 int err = snd_pcm_writei(pcm_out_handle_, | 185 int err = snd_pcm_writei(pcm_out_handle_, |
| 184 static_cast<void *>(chunk), | 186 static_cast<void *>(chunk), |
| 185 static_cast<snd_pcm_uframes_t>(chunk_size)); | 187 static_cast<snd_pcm_uframes_t>(chunk_size)); |
| 186 if (err < 0) { | 188 if (err < 0) { |
| 187 LOG(INFO) << "Write error: " << err << snd_strerror(err) << "\n"; | 189 LOG(INFO) << "Write error: " << err << snd_strerror(err) << "\n"; |
| 188 return; | 190 do { |
| 191 sleep(1); |
| 192 LOG(INFO) << "Attempting to recover audio"; |
| 193 } while (keep_running_ && |
| 194 0 != snd_pcm_recover(pcm_out_handle_, err, 0)); |
| 189 } | 195 } |
| 190 } | 196 } |
| 191 | 197 |
| 192 delete[] chunk; | 198 delete[] chunk; |
| 193 } | 199 } |
| 194 | 200 |
| 195 private: | 201 private: |
| 196 volatile bool keep_running_; | 202 volatile bool keep_running_; |
| 197 Threading* threading_; | 203 Threading* threading_; |
| 198 AudioProvider* provider_; | 204 AudioProvider* provider_; |
| 199 Thread* thread_; | 205 Thread* thread_; |
| 200 snd_pcm_t *pcm_out_handle_; | 206 snd_pcm_t *pcm_out_handle_; |
| 201 int sample_rate_; | 207 int sample_rate_; |
| 202 int channel_count_; | 208 int channel_count_; |
| 203 int chunk_size_; | 209 int chunk_size_; |
| 204 int total_buffer_size_; | 210 int total_buffer_size_; |
| 205 }; | 211 }; |
| 206 | 212 |
| 207 AudioOutput* AudioOutput::Create(Threading* threading) { | 213 AudioOutput* AudioOutput::Create(Threading* threading) { |
| 208 return new LinuxAlsaAudioOutput(threading); | 214 return new LinuxAlsaAudioOutput(threading); |
| 209 } | 215 } |
| 210 | 216 |
| 211 } // namespace speech_synthesis | 217 } // namespace speech_synthesis |
| 212 | 218 |
| OLD | NEW |