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.
5 #include "media/audio/linux/alsa_util.h"
9 #include "base/logging.h"
10 #include "media/audio/linux/alsa_wrapper.h"
14 static snd_pcm_t
* OpenDevice(media::AlsaWrapper
* wrapper
,
15 const char* device_name
,
16 snd_pcm_stream_t type
,
19 snd_pcm_format_t pcm_format
,
21 snd_pcm_t
* handle
= NULL
;
22 int error
= wrapper
->PcmOpen(&handle
, device_name
, type
, SND_PCM_NONBLOCK
);
24 LOG(WARNING
) << "PcmOpen: " << device_name
<< ","
25 << wrapper
->StrError(error
);
29 error
= wrapper
->PcmSetParams(handle
, pcm_format
,
30 SND_PCM_ACCESS_RW_INTERLEAVED
, channels
,
31 sample_rate
, 1, latency_us
);
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.";
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
;
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
);
65 snd_pcm_format_t
BitsToFormat(int bits_per_sample
) {
66 switch (bits_per_sample
) {
68 return SND_PCM_FORMAT_U8
;
71 return SND_PCM_FORMAT_S16
;
74 return SND_PCM_FORMAT_S24
;
77 return SND_PCM_FORMAT_S32
;
80 return SND_PCM_FORMAT_UNKNOWN
;
84 int CloseDevice(media::AlsaWrapper
* wrapper
, snd_pcm_t
* handle
) {
85 std::string device_name
= wrapper
->PcmName(handle
);
86 int error
= wrapper
->PcmClose(handle
);
88 LOG(ERROR
) << "PcmClose: " << device_name
<< ", "
89 << wrapper
->StrError(error
);
95 snd_pcm_t
* OpenCaptureDevice(media::AlsaWrapper
* wrapper
,
96 const char* device_name
,
99 snd_pcm_format_t pcm_format
,
101 return OpenDevice(wrapper
, device_name
, SND_PCM_STREAM_CAPTURE
, channels
,
102 sample_rate
, pcm_format
, latency_us
);
105 snd_pcm_t
* OpenPlaybackDevice(media::AlsaWrapper
* wrapper
,
106 const char* device_name
,
109 snd_pcm_format_t pcm_format
,
111 return OpenDevice(wrapper
, device_name
, SND_PCM_STREAM_PLAYBACK
, channels
,
112 sample_rate
, pcm_format
, latency_us
);
115 snd_mixer_t
* OpenMixer(media::AlsaWrapper
* wrapper
,
116 const std::string
& device_name
) {
117 snd_mixer_t
* mixer
= NULL
;
119 int error
= wrapper
->MixerOpen(&mixer
, 0);
121 LOG(ERROR
) << "MixerOpen: " << device_name
<< ", "
122 << wrapper
->StrError(error
);
126 std::string control_name
= DeviceNameToControlName(device_name
);
127 error
= wrapper
->MixerAttach(mixer
, control_name
.c_str());
129 LOG(ERROR
) << "MixerAttach, " << control_name
<< ", "
130 << wrapper
->StrError(error
);
131 alsa_util::CloseMixer(wrapper
, mixer
, device_name
);
135 error
= wrapper
->MixerElementRegister(mixer
, NULL
, NULL
);
137 LOG(ERROR
) << "MixerElementRegister: " << control_name
<< ", "
138 << wrapper
->StrError(error
);
139 alsa_util::CloseMixer(wrapper
, mixer
, device_name
);
146 void CloseMixer(media::AlsaWrapper
* wrapper
, snd_mixer_t
* mixer
,
147 const std::string
& device_name
) {
151 wrapper
->MixerFree(mixer
);
154 if (!device_name
.empty()) {
155 std::string control_name
= DeviceNameToControlName(device_name
);
156 error
= wrapper
->MixerDetach(mixer
, control_name
.c_str());
158 LOG(WARNING
) << "MixerDetach: " << control_name
<< ", "
159 << wrapper
->StrError(error
);
163 error
= wrapper
->MixerClose(mixer
);
165 LOG(WARNING
) << "MixerClose: " << wrapper
->StrError(error
);
169 snd_mixer_elem_t
* LoadCaptureMixerElement(media::AlsaWrapper
* wrapper
,
170 snd_mixer_t
* mixer
) {
174 int error
= wrapper
->MixerLoad(mixer
);
176 LOG(ERROR
) << "MixerLoad: " << wrapper
->StrError(error
);
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
);
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)
191 else if (strcmp(elem_name
, kMicElemName
) == 0)
196 // Did not find any Capture handle, use the Mic handle.
200 } // namespace alsa_util