cras_input.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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/cras/cras_input.h"
6
7#include <math.h>
8
9#include "base/basictypes.h"
10#include "base/bind.h"
11#include "base/logging.h"
12#include "base/time/time.h"
13#include "media/audio/audio_manager.h"
14#include "media/audio/cras/audio_manager_cras.h"
15#include "media/audio/linux/alsa_util.h"
16
17namespace media {
18
19CrasInputStream::CrasInputStream(const AudioParameters& params,
20                                 AudioManagerCras* manager,
21                                 const std::string& device_id)
22    : audio_manager_(manager),
23      bytes_per_frame_(0),
24      callback_(NULL),
25      client_(NULL),
26      params_(params),
27      started_(false),
28      stream_id_(0),
29      stream_direction_(device_id == AudioManagerCras::kLoopbackDeviceId
30                            ? CRAS_STREAM_POST_MIX_PRE_DSP
31                            : CRAS_STREAM_INPUT) {
32  DCHECK(audio_manager_);
33}
34
35CrasInputStream::~CrasInputStream() {
36  DCHECK(!client_);
37}
38
39bool CrasInputStream::Open() {
40  if (client_) {
41    NOTREACHED() << "CrasInputStream already open";
42    return false;  // Already open.
43  }
44
45  // Sanity check input values.
46  if (params_.sample_rate() <= 0) {
47    DLOG(WARNING) << "Unsupported audio frequency.";
48    return false;
49  }
50
51  if (AudioParameters::AUDIO_PCM_LINEAR != params_.format() &&
52      AudioParameters::AUDIO_PCM_LOW_LATENCY != params_.format()) {
53    DLOG(WARNING) << "Unsupported audio format.";
54    return false;
55  }
56
57  snd_pcm_format_t pcm_format =
58      alsa_util::BitsToFormat(params_.bits_per_sample());
59  if (pcm_format == SND_PCM_FORMAT_UNKNOWN) {
60    DLOG(WARNING) << "Unsupported bits/sample: " << params_.bits_per_sample();
61    return false;
62  }
63
64  // Create the client and connect to the CRAS server.
65  if (cras_client_create(&client_) < 0) {
66    DLOG(WARNING) << "Couldn't create CRAS client.\n";
67    client_ = NULL;
68    return false;
69  }
70
71  if (cras_client_connect(client_)) {
72    DLOG(WARNING) << "Couldn't connect CRAS client.\n";
73    cras_client_destroy(client_);
74    client_ = NULL;
75    return false;
76  }
77
78  // Then start running the client.
79  if (cras_client_run_thread(client_)) {
80    DLOG(WARNING) << "Couldn't run CRAS client.\n";
81    cras_client_destroy(client_);
82    client_ = NULL;
83    return false;
84  }
85
86  return true;
87}
88
89void CrasInputStream::Close() {
90  if (client_) {
91    cras_client_stop(client_);
92    cras_client_destroy(client_);
93    client_ = NULL;
94  }
95
96  if (callback_) {
97    callback_->OnClose(this);
98    callback_ = NULL;
99  }
100
101  // Signal to the manager that we're closed and can be removed.
102  // Should be last call in the method as it deletes "this".
103  audio_manager_->ReleaseInputStream(this);
104}
105
106void CrasInputStream::Start(AudioInputCallback* callback) {
107  DCHECK(client_);
108  DCHECK(callback);
109
110  // If already playing, stop before re-starting.
111  if (started_)
112    return;
113
114  StartAgc();
115
116  callback_ = callback;
117
118  // Prepare |audio_format| and |stream_params| for the stream we
119  // will create.
120  cras_audio_format* audio_format = cras_audio_format_create(
121      alsa_util::BitsToFormat(params_.bits_per_sample()),
122      params_.sample_rate(),
123      params_.channels());
124  if (!audio_format) {
125    DLOG(WARNING) << "Error setting up audio parameters.";
126    callback_->OnError(this);
127    callback_ = NULL;
128    return;
129  }
130
131  unsigned int frames_per_packet = params_.frames_per_buffer();
132  cras_stream_params* stream_params = cras_client_stream_params_create(
133      stream_direction_,
134      frames_per_packet,  // Total latency.
135      frames_per_packet,  // Call back when this many ready.
136      frames_per_packet,  // Minimum Callback level ignored for capture streams.
137      CRAS_STREAM_TYPE_DEFAULT,
138      0,  // Unused flags.
139      this,
140      CrasInputStream::SamplesReady,
141      CrasInputStream::StreamError,
142      audio_format);
143  if (!stream_params) {
144    DLOG(WARNING) << "Error setting up stream parameters.";
145    callback_->OnError(this);
146    callback_ = NULL;
147    cras_audio_format_destroy(audio_format);
148    return;
149  }
150
151  // Before starting the stream, save the number of bytes in a frame for use in
152  // the callback.
153  bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format);
154
155  // Adding the stream will start the audio callbacks.
156  if (cras_client_add_stream(client_, &stream_id_, stream_params)) {
157    DLOG(WARNING) << "Failed to add the stream.";
158    callback_->OnError(this);
159    callback_ = NULL;
160  }
161
162  // Done with config params.
163  cras_audio_format_destroy(audio_format);
164  cras_client_stream_params_destroy(stream_params);
165
166  started_ = true;
167}
168
169void CrasInputStream::Stop() {
170  DCHECK(client_);
171
172  if (!callback_ || !started_)
173    return;
174
175  StopAgc();
176
177  // Removing the stream from the client stops audio.
178  cras_client_rm_stream(client_, stream_id_);
179
180  started_ = false;
181}
182
183// Static callback asking for samples.  Run on high priority thread.
184int CrasInputStream::SamplesReady(cras_client* client,
185                                  cras_stream_id_t stream_id,
186                                  uint8* samples,
187                                  size_t frames,
188                                  const timespec* sample_ts,
189                                  void* arg) {
190  CrasInputStream* me = static_cast<CrasInputStream*>(arg);
191  me->ReadAudio(frames, samples, sample_ts);
192  return frames;
193}
194
195// Static callback for stream errors.
196int CrasInputStream::StreamError(cras_client* client,
197                                 cras_stream_id_t stream_id,
198                                 int err,
199                                 void* arg) {
200  CrasInputStream* me = static_cast<CrasInputStream*>(arg);
201  me->NotifyStreamError(err);
202  return 0;
203}
204
205void CrasInputStream::ReadAudio(size_t frames,
206                                uint8* buffer,
207                                const timespec* sample_ts) {
208  DCHECK(callback_);
209
210  timespec latency_ts = {0, 0};
211
212  // Determine latency and pass that on to the sink.  sample_ts is the wall time
213  // indicating when the first sample in the buffer was captured.  Convert that
214  // to latency in bytes.
215  cras_client_calc_capture_latency(sample_ts, &latency_ts);
216  double latency_usec =
217      latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond +
218      latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond;
219  double frames_latency =
220      latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond;
221  unsigned int bytes_latency =
222      static_cast<unsigned int>(frames_latency * bytes_per_frame_);
223
224  // Update the AGC volume level once every second. Note that, |volume| is
225  // also updated each time SetVolume() is called through IPC by the
226  // render-side AGC.
227  double normalized_volume = 0.0;
228  GetAgcVolume(&normalized_volume);
229
230  callback_->OnData(this,
231                    buffer,
232                    frames * bytes_per_frame_,
233                    bytes_latency,
234                    normalized_volume);
235}
236
237void CrasInputStream::NotifyStreamError(int err) {
238  if (callback_)
239    callback_->OnError(this);
240}
241
242double CrasInputStream::GetMaxVolume() {
243  DCHECK(client_);
244
245  // Capture gain is returned as dB * 100 (150 => 1.5dBFS).  Convert the dB
246  // value to a ratio before returning.
247  double dB = cras_client_get_system_max_capture_gain(client_) / 100.0;
248  return GetVolumeRatioFromDecibels(dB);
249}
250
251void CrasInputStream::SetVolume(double volume) {
252  DCHECK(client_);
253
254  // Convert from the passed volume ratio, to dB * 100.
255  double dB = GetDecibelsFromVolumeRatio(volume);
256  cras_client_set_system_capture_gain(client_, static_cast<long>(dB * 100.0));
257
258  // Update the AGC volume level based on the last setting above. Note that,
259  // the volume-level resolution is not infinite and it is therefore not
260  // possible to assume that the volume provided as input parameter can be
261  // used directly. Instead, a new query to the audio hardware is required.
262  // This method does nothing if AGC is disabled.
263  UpdateAgcVolume();
264}
265
266double CrasInputStream::GetVolume() {
267  if (!client_)
268    return 0.0;
269
270  long dB = cras_client_get_system_capture_gain(client_) / 100.0;
271  return GetVolumeRatioFromDecibels(dB);
272}
273
274double CrasInputStream::GetVolumeRatioFromDecibels(double dB) const {
275  return pow(10, dB / 20.0);
276}
277
278double CrasInputStream::GetDecibelsFromVolumeRatio(double volume_ratio) const {
279  return 20 * log10(volume_ratio);
280}
281
282}  // namespace media
283