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.
5 #include "media/audio/alsa/alsa_util.h"
8 #include "base/logging.h"
9 #include "media/audio/alsa/alsa_wrapper.h"
13 static snd_pcm_t
* OpenDevice(media::AlsaWrapper
* wrapper
,
14 const char* device_name
,
15 snd_pcm_stream_t type
,
18 snd_pcm_format_t pcm_format
,
20 snd_pcm_t
* handle
= NULL
;
21 int error
= wrapper
->PcmOpen(&handle
, device_name
, type
, SND_PCM_NONBLOCK
);
23 LOG(WARNING
) << "PcmOpen: " << device_name
<< ","
24 << wrapper
->StrError(error
);
28 error
= wrapper
->PcmSetParams(handle
, pcm_format
,
29 SND_PCM_ACCESS_RW_INTERLEAVED
, channels
,
30 sample_rate
, 1, latency_us
);
32 LOG(WARNING
) << "PcmSetParams: " << device_name
<< ", "
33 << wrapper
->StrError(error
) << " - Format: " << pcm_format
34 << " Channels: " << channels
<< " Latency: " << latency_us
;
35 if (alsa_util::CloseDevice(wrapper
, handle
) < 0) {
36 // TODO(ajwong): Retry on certain errors?
37 LOG(WARNING
) << "Unable to close audio device. Leaking handle.";
45 static std::string
DeviceNameToControlName(const std::string
& device_name
) {
46 const char kMixerPrefix
[] = "hw";
47 std::string control_name
;
48 size_t pos1
= device_name
.find(':');
49 if (pos1
== std::string::npos
) {
50 control_name
= device_name
;
53 // deviceName: "front:CARD=Intel,DEV=0", controlName: "hw:CARD=Intel".
54 // deviceName: "default:CARD=Intel", controlName: "CARD=Intel".
55 size_t pos2
= device_name
.find(',');
56 control_name
= (pos2
== std::string::npos
) ?
57 device_name
.substr(pos1
) :
58 kMixerPrefix
+ device_name
.substr(pos1
, pos2
- pos1
);
64 snd_pcm_format_t
BitsToFormat(int bits_per_sample
) {
65 switch (bits_per_sample
) {
67 return SND_PCM_FORMAT_U8
;
70 return SND_PCM_FORMAT_S16
;
73 return SND_PCM_FORMAT_S24
;
76 return SND_PCM_FORMAT_S32
;
79 return SND_PCM_FORMAT_UNKNOWN
;
83 int CloseDevice(media::AlsaWrapper
* wrapper
, snd_pcm_t
* handle
) {
84 std::string device_name
= wrapper
->PcmName(handle
);
85 int error
= wrapper
->PcmClose(handle
);
87 LOG(ERROR
) << "PcmClose: " << device_name
<< ", "
88 << wrapper
->StrError(error
);
94 snd_pcm_t
* OpenCaptureDevice(media::AlsaWrapper
* wrapper
,
95 const char* device_name
,
98 snd_pcm_format_t pcm_format
,
100 return OpenDevice(wrapper
, device_name
, SND_PCM_STREAM_CAPTURE
, channels
,
101 sample_rate
, pcm_format
, latency_us
);
104 snd_pcm_t
* OpenPlaybackDevice(media::AlsaWrapper
* wrapper
,
105 const char* device_name
,
108 snd_pcm_format_t pcm_format
,
110 return OpenDevice(wrapper
, device_name
, SND_PCM_STREAM_PLAYBACK
, channels
,
111 sample_rate
, pcm_format
, latency_us
);
114 snd_mixer_t
* OpenMixer(media::AlsaWrapper
* wrapper
,
115 const std::string
& device_name
) {
116 snd_mixer_t
* mixer
= NULL
;
118 int error
= wrapper
->MixerOpen(&mixer
, 0);
120 LOG(ERROR
) << "MixerOpen: " << device_name
<< ", "
121 << wrapper
->StrError(error
);
125 std::string control_name
= DeviceNameToControlName(device_name
);
126 error
= wrapper
->MixerAttach(mixer
, control_name
.c_str());
128 LOG(ERROR
) << "MixerAttach, " << control_name
<< ", "
129 << wrapper
->StrError(error
);
130 alsa_util::CloseMixer(wrapper
, mixer
, device_name
);
134 error
= wrapper
->MixerElementRegister(mixer
, NULL
, NULL
);
136 LOG(ERROR
) << "MixerElementRegister: " << control_name
<< ", "
137 << wrapper
->StrError(error
);
138 alsa_util::CloseMixer(wrapper
, mixer
, device_name
);
145 void CloseMixer(media::AlsaWrapper
* wrapper
, snd_mixer_t
* mixer
,
146 const std::string
& device_name
) {
150 wrapper
->MixerFree(mixer
);
153 if (!device_name
.empty()) {
154 std::string control_name
= DeviceNameToControlName(device_name
);
155 error
= wrapper
->MixerDetach(mixer
, control_name
.c_str());
157 LOG(WARNING
) << "MixerDetach: " << control_name
<< ", "
158 << wrapper
->StrError(error
);
162 error
= wrapper
->MixerClose(mixer
);
164 LOG(WARNING
) << "MixerClose: " << wrapper
->StrError(error
);
168 snd_mixer_elem_t
* LoadCaptureMixerElement(media::AlsaWrapper
* wrapper
,
169 snd_mixer_t
* mixer
) {
173 int error
= wrapper
->MixerLoad(mixer
);
175 LOG(ERROR
) << "MixerLoad: " << wrapper
->StrError(error
);
179 snd_mixer_elem_t
* elem
= NULL
;
180 snd_mixer_elem_t
* mic_elem
= NULL
;
181 const char kCaptureElemName
[] = "Capture";
182 const char kMicElemName
[] = "Mic";
183 for (elem
= wrapper
->MixerFirstElem(mixer
);
185 elem
= wrapper
->MixerNextElem(elem
)) {
186 if (wrapper
->MixerSelemIsActive(elem
)) {
187 const char* elem_name
= wrapper
->MixerSelemName(elem
);
188 if (strcmp(elem_name
, kCaptureElemName
) == 0)
190 else if (strcmp(elem_name
, kMicElemName
) == 0)
195 // Did not find any Capture handle, use the Mic handle.
199 } // namespace alsa_util