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/cras/cras_input.h"
9 #include "base/basictypes.h"
10 #include "base/logging.h"
11 #include "base/time/time.h"
12 #include "media/audio/audio_manager.h"
13 #include "media/audio/cras/audio_manager_cras.h"
17 CrasInputStream::CrasInputStream(const AudioParameters
& params
,
18 AudioManagerCras
* manager
,
19 const std::string
& device_id
)
20 : audio_manager_(manager
),
27 stream_direction_(CRAS_STREAM_INPUT
),
28 pin_device_(NO_DEVICE
),
29 is_loopback_(device_id
== AudioManagerBase::kLoopbackInputDeviceId
) {
30 DCHECK(audio_manager_
);
31 audio_bus_
= AudioBus::Create(params_
);
34 CrasInputStream::~CrasInputStream() {
38 bool CrasInputStream::Open() {
40 NOTREACHED() << "CrasInputStream already open";
41 return false; // Already open.
44 // Sanity check input values.
45 if (params_
.sample_rate() <= 0) {
46 DLOG(WARNING
) << "Unsupported audio frequency.";
50 if (AudioParameters::AUDIO_PCM_LINEAR
!= params_
.format() &&
51 AudioParameters::AUDIO_PCM_LOW_LATENCY
!= params_
.format()) {
52 DLOG(WARNING
) << "Unsupported audio format.";
56 snd_pcm_format_t pcm_format
=
57 AudioManagerCras::BitsToFormat(params_
.bits_per_sample());
58 if (pcm_format
== SND_PCM_FORMAT_UNKNOWN
) {
59 DLOG(WARNING
) << "Unsupported bits/sample: " << params_
.bits_per_sample();
63 // Create the client and connect to the CRAS server.
64 if (cras_client_create(&client_
) < 0) {
65 DLOG(WARNING
) << "Couldn't create CRAS client.\n";
70 if (cras_client_connect(client_
)) {
71 DLOG(WARNING
) << "Couldn't connect CRAS client.\n";
72 cras_client_destroy(client_
);
77 // Then start running the client.
78 if (cras_client_run_thread(client_
)) {
79 DLOG(WARNING
) << "Couldn't run CRAS client.\n";
80 cras_client_destroy(client_
);
86 if (cras_client_connected_wait(client_
) < 0) {
87 DLOG(WARNING
) << "Couldn't synchronize data.";
88 // TODO(chinyue): Add a DestroyClientOnError method to de-duplicate the
90 cras_client_destroy(client_
);
94 pin_device_
= cras_client_get_first_dev_type_idx(client_
,
95 CRAS_NODE_TYPE_POST_MIX_PRE_DSP
, CRAS_STREAM_INPUT
);
96 if (pin_device_
< 0) {
97 DLOG(WARNING
) << "Couldn't find CRAS loopback device.";
98 cras_client_destroy(client_
);
107 void CrasInputStream::Close() {
111 cras_client_stop(client_
);
112 cras_client_destroy(client_
);
116 // Signal to the manager that we're closed and can be removed.
117 // Should be last call in the method as it deletes "this".
118 audio_manager_
->ReleaseInputStream(this);
121 void CrasInputStream::Start(AudioInputCallback
* callback
) {
125 // Channel map to CRAS_CHANNEL, values in the same order of
126 // corresponding source in Chromium defined Channels.
127 static const int kChannelMap
[] = {
140 static_assert(arraysize(kChannelMap
) == CHANNELS_MAX
+ 1,
141 "kChannelMap array size should match");
143 // If already playing, stop before re-starting.
149 callback_
= callback
;
151 // Prepare |audio_format| and |stream_params| for the stream we
153 cras_audio_format
* audio_format
= cras_audio_format_create(
154 AudioManagerCras::BitsToFormat(params_
.bits_per_sample()),
155 params_
.sample_rate(),
158 DLOG(WARNING
) << "Error setting up audio parameters.";
159 callback_
->OnError(this);
164 // Initialize channel layout to all -1 to indicate that none of
165 // the channels is set in the layout.
166 int8 layout
[CRAS_CH_MAX
];
167 for (size_t i
= 0; i
< arraysize(layout
); ++i
)
170 // Converts to CRAS defined channels. ChannelOrder will return -1
171 // for channels that are not present in params_.channel_layout().
172 for (size_t i
= 0; i
< arraysize(kChannelMap
); ++i
) {
173 layout
[kChannelMap
[i
]] = ChannelOrder(params_
.channel_layout(),
174 static_cast<Channels
>(i
));
176 if (cras_audio_format_set_channel_layout(audio_format
, layout
) != 0) {
177 DLOG(WARNING
) << "Error setting channel layout.";
178 callback
->OnError(this);
183 if (params_
.effects() & AudioParameters::PlatformEffectsMask::HOTWORD
)
184 flags
= HOTWORD_STREAM
;
186 unsigned int frames_per_packet
= params_
.frames_per_buffer();
187 cras_stream_params
* stream_params
= cras_client_stream_params_create(
189 frames_per_packet
, // Total latency.
190 frames_per_packet
, // Call back when this many ready.
191 frames_per_packet
, // Minimum Callback level ignored for capture streams.
192 CRAS_STREAM_TYPE_DEFAULT
,
195 CrasInputStream::SamplesReady
,
196 CrasInputStream::StreamError
,
198 if (!stream_params
) {
199 DLOG(WARNING
) << "Error setting up stream parameters.";
200 callback_
->OnError(this);
202 cras_audio_format_destroy(audio_format
);
206 // Before starting the stream, save the number of bytes in a frame for use in
208 bytes_per_frame_
= cras_client_format_bytes_per_frame(audio_format
);
210 // Adding the stream will start the audio callbacks.
211 if (cras_client_add_pinned_stream(client_
, pin_device_
, &stream_id_
,
213 DLOG(WARNING
) << "Failed to add the stream.";
214 callback_
->OnError(this);
218 // Done with config params.
219 cras_audio_format_destroy(audio_format
);
220 cras_client_stream_params_destroy(stream_params
);
225 void CrasInputStream::Stop() {
228 if (!callback_
|| !started_
)
233 // Removing the stream from the client stops audio.
234 cras_client_rm_stream(client_
, stream_id_
);
240 // Static callback asking for samples. Run on high priority thread.
241 int CrasInputStream::SamplesReady(cras_client
* client
,
242 cras_stream_id_t stream_id
,
245 const timespec
* sample_ts
,
247 CrasInputStream
* me
= static_cast<CrasInputStream
*>(arg
);
248 me
->ReadAudio(frames
, samples
, sample_ts
);
252 // Static callback for stream errors.
253 int CrasInputStream::StreamError(cras_client
* client
,
254 cras_stream_id_t stream_id
,
257 CrasInputStream
* me
= static_cast<CrasInputStream
*>(arg
);
258 me
->NotifyStreamError(err
);
262 void CrasInputStream::ReadAudio(size_t frames
,
264 const timespec
* sample_ts
) {
267 timespec latency_ts
= {0, 0};
269 // Determine latency and pass that on to the sink. sample_ts is the wall time
270 // indicating when the first sample in the buffer was captured. Convert that
271 // to latency in bytes.
272 cras_client_calc_capture_latency(sample_ts
, &latency_ts
);
273 double latency_usec
=
274 latency_ts
.tv_sec
* base::Time::kMicrosecondsPerSecond
+
275 latency_ts
.tv_nsec
/ base::Time::kNanosecondsPerMicrosecond
;
276 double frames_latency
=
277 latency_usec
* params_
.sample_rate() / base::Time::kMicrosecondsPerSecond
;
278 unsigned int bytes_latency
=
279 static_cast<unsigned int>(frames_latency
* bytes_per_frame_
);
281 // Update the AGC volume level once every second. Note that, |volume| is
282 // also updated each time SetVolume() is called through IPC by the
284 double normalized_volume
= 0.0;
285 GetAgcVolume(&normalized_volume
);
287 audio_bus_
->FromInterleaved(
288 buffer
, audio_bus_
->frames(), params_
.bits_per_sample() / 8);
289 callback_
->OnData(this, audio_bus_
.get(), bytes_latency
, normalized_volume
);
292 void CrasInputStream::NotifyStreamError(int err
) {
294 callback_
->OnError(this);
297 double CrasInputStream::GetMaxVolume() {
300 // Capture gain is returned as dB * 100 (150 => 1.5dBFS). Convert the dB
301 // value to a ratio before returning.
302 double dB
= cras_client_get_system_max_capture_gain(client_
) / 100.0;
303 return GetVolumeRatioFromDecibels(dB
);
306 void CrasInputStream::SetVolume(double volume
) {
309 // Convert from the passed volume ratio, to dB * 100.
310 double dB
= GetDecibelsFromVolumeRatio(volume
);
311 cras_client_set_system_capture_gain(client_
, static_cast<long>(dB
* 100.0));
313 // Update the AGC volume level based on the last setting above. Note that,
314 // the volume-level resolution is not infinite and it is therefore not
315 // possible to assume that the volume provided as input parameter can be
316 // used directly. Instead, a new query to the audio hardware is required.
317 // This method does nothing if AGC is disabled.
321 double CrasInputStream::GetVolume() {
325 long dB
= cras_client_get_system_capture_gain(client_
) / 100.0;
326 return GetVolumeRatioFromDecibels(dB
);
329 bool CrasInputStream::IsMuted() {
333 double CrasInputStream::GetVolumeRatioFromDecibels(double dB
) const {
334 return pow(10, dB
/ 20.0);
337 double CrasInputStream::GetDecibelsFromVolumeRatio(double volume_ratio
) const {
338 return 20 * log10(volume_ratio
);