1 // Copyright 2013 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/alsa/alsa_util.h"
6
7 #include <string>
8
9 #include "base/logging.h"
10 #include "media/audio/alsa/alsa_wrapper.h"
11
12 namespace alsa_util {
13
OpenDevice(media::AlsaWrapper * wrapper,const char * device_name,snd_pcm_stream_t type,int channels,int sample_rate,snd_pcm_format_t pcm_format,int latency_us)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
DeviceNameToControlName(const std::string & device_name)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
BitsToFormat(int bits_per_sample)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
CloseDevice(media::AlsaWrapper * wrapper,snd_pcm_t * handle)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
OpenCaptureDevice(media::AlsaWrapper * wrapper,const char * device_name,int channels,int sample_rate,snd_pcm_format_t pcm_format,int latency_us)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
OpenPlaybackDevice(media::AlsaWrapper * wrapper,const char * device_name,int channels,int sample_rate,snd_pcm_format_t pcm_format,int latency_us)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
OpenMixer(media::AlsaWrapper * wrapper,const std::string & device_name)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
CloseMixer(media::AlsaWrapper * wrapper,snd_mixer_t * mixer,const std::string & device_name)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
LoadCaptureMixerElement(media::AlsaWrapper * wrapper,snd_mixer_t * mixer)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