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