1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "media/audio/linux/alsa_util.h" 6 7 #include <string> 8 9 #include "base/logging.h" 10 #include "media/audio/linux/alsa_wrapper.h" 11 12 namespace alsa_util { 13 14 static snd_pcm_t* OpenDevice(media::AlsaWrapper* wrapper, 15 const char* device_name, 16 snd_pcm_stream_t type, 17 int channels, 18 int sample_rate, 19 snd_pcm_format_t pcm_format, 20 int latency_us) { 21 snd_pcm_t* handle = NULL; 22 int error = wrapper->PcmOpen(&handle, device_name, type, SND_PCM_NONBLOCK); 23 if (error < 0) { 24 LOG(WARNING) << "PcmOpen: " << device_name << "," 25 << wrapper->StrError(error); 26 return NULL; 27 } 28 29 error = wrapper->PcmSetParams(handle, pcm_format, 30 SND_PCM_ACCESS_RW_INTERLEAVED, channels, 31 sample_rate, 1, latency_us); 32 if (error < 0) { 33 LOG(WARNING) << "PcmSetParams: " << device_name << ", " 34 << wrapper->StrError(error) << " - Format: " << pcm_format 35 << " Channels: " << channels << " Latency: " << latency_us; 36 if (alsa_util::CloseDevice(wrapper, handle) < 0) { 37 // TODO(ajwong): Retry on certain errors? 38 LOG(WARNING) << "Unable to close audio device. Leaking handle."; 39 } 40 return NULL; 41 } 42 43 return handle; 44 } 45 46 static std::string DeviceNameToControlName(const std::string& device_name) { 47 const char kMixerPrefix[] = "hw"; 48 std::string control_name; 49 size_t pos1 = device_name.find(':'); 50 if (pos1 == std::string::npos) { 51 control_name = device_name; 52 } else { 53 // Examples: 54 // deviceName: "front:CARD=Intel,DEV=0", controlName: "hw:CARD=Intel". 55 // deviceName: "default:CARD=Intel", controlName: "CARD=Intel". 56 size_t pos2 = device_name.find(','); 57 control_name = (pos2 == std::string::npos) ? 58 device_name.substr(pos1) : 59 kMixerPrefix + device_name.substr(pos1, pos2 - pos1); 60 } 61 62 return control_name; 63 } 64 65 snd_pcm_format_t BitsToFormat(int bits_per_sample) { 66 switch (bits_per_sample) { 67 case 8: 68 return SND_PCM_FORMAT_U8; 69 70 case 16: 71 return SND_PCM_FORMAT_S16; 72 73 case 24: 74 return SND_PCM_FORMAT_S24; 75 76 case 32: 77 return SND_PCM_FORMAT_S32; 78 79 default: 80 return SND_PCM_FORMAT_UNKNOWN; 81 } 82 } 83 84 int CloseDevice(media::AlsaWrapper* wrapper, snd_pcm_t* handle) { 85 std::string device_name = wrapper->PcmName(handle); 86 int error = wrapper->PcmClose(handle); 87 if (error < 0) { 88 LOG(ERROR) << "PcmClose: " << device_name << ", " 89 << wrapper->StrError(error); 90 } 91 92 return error; 93 } 94 95 snd_pcm_t* OpenCaptureDevice(media::AlsaWrapper* wrapper, 96 const char* device_name, 97 int channels, 98 int sample_rate, 99 snd_pcm_format_t pcm_format, 100 int latency_us) { 101 return OpenDevice(wrapper, device_name, SND_PCM_STREAM_CAPTURE, channels, 102 sample_rate, pcm_format, latency_us); 103 } 104 105 snd_pcm_t* OpenPlaybackDevice(media::AlsaWrapper* wrapper, 106 const char* device_name, 107 int channels, 108 int sample_rate, 109 snd_pcm_format_t pcm_format, 110 int latency_us) { 111 return OpenDevice(wrapper, device_name, SND_PCM_STREAM_PLAYBACK, channels, 112 sample_rate, pcm_format, latency_us); 113 } 114 115 snd_mixer_t* OpenMixer(media::AlsaWrapper* wrapper, 116 const std::string& device_name) { 117 snd_mixer_t* mixer = NULL; 118 119 int error = wrapper->MixerOpen(&mixer, 0); 120 if (error < 0) { 121 LOG(ERROR) << "MixerOpen: " << device_name << ", " 122 << wrapper->StrError(error); 123 return NULL; 124 } 125 126 std::string control_name = DeviceNameToControlName(device_name); 127 error = wrapper->MixerAttach(mixer, control_name.c_str()); 128 if (error < 0) { 129 LOG(ERROR) << "MixerAttach, " << control_name << ", " 130 << wrapper->StrError(error); 131 alsa_util::CloseMixer(wrapper, mixer, device_name); 132 return NULL; 133 } 134 135 error = wrapper->MixerElementRegister(mixer, NULL, NULL); 136 if (error < 0) { 137 LOG(ERROR) << "MixerElementRegister: " << control_name << ", " 138 << wrapper->StrError(error); 139 alsa_util::CloseMixer(wrapper, mixer, device_name); 140 return NULL; 141 } 142 143 return mixer; 144 } 145 146 void CloseMixer(media::AlsaWrapper* wrapper, snd_mixer_t* mixer, 147 const std::string& device_name) { 148 if (!mixer) 149 return; 150 151 wrapper->MixerFree(mixer); 152 153 int error = 0; 154 if (!device_name.empty()) { 155 std::string control_name = DeviceNameToControlName(device_name); 156 error = wrapper->MixerDetach(mixer, control_name.c_str()); 157 if (error < 0) { 158 LOG(WARNING) << "MixerDetach: " << control_name << ", " 159 << wrapper->StrError(error); 160 } 161 } 162 163 error = wrapper->MixerClose(mixer); 164 if (error < 0) { 165 LOG(WARNING) << "MixerClose: " << wrapper->StrError(error); 166 } 167 } 168 169 snd_mixer_elem_t* LoadCaptureMixerElement(media::AlsaWrapper* wrapper, 170 snd_mixer_t* mixer) { 171 if (!mixer) 172 return NULL; 173 174 int error = wrapper->MixerLoad(mixer); 175 if (error < 0) { 176 LOG(ERROR) << "MixerLoad: " << wrapper->StrError(error); 177 return NULL; 178 } 179 180 snd_mixer_elem_t* elem = NULL; 181 snd_mixer_elem_t* mic_elem = NULL; 182 const char kCaptureElemName[] = "Capture"; 183 const char kMicElemName[] = "Mic"; 184 for (elem = wrapper->MixerFirstElem(mixer); 185 elem; 186 elem = wrapper->MixerNextElem(elem)) { 187 if (wrapper->MixerSelemIsActive(elem)) { 188 const char* elem_name = wrapper->MixerSelemName(elem); 189 if (strcmp(elem_name, kCaptureElemName) == 0) 190 return elem; 191 else if (strcmp(elem_name, kMicElemName) == 0) 192 mic_elem = elem; 193 } 194 } 195 196 // Did not find any Capture handle, use the Mic handle. 197 return mic_elem; 198 } 199 200 } // namespace alsa_util 201