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