Home | History | Annotate | Download | only in cras
      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 
     17 namespace media {
     18 
     19 CrasInputStream::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 
     34 CrasInputStream::~CrasInputStream() {
     35   DCHECK(!client_);
     36 }
     37 
     38 bool 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 
     88 void 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 
    105 void 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 
    168 void 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.
    183 int 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.
    195 int 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 
    204 void 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 
    236 void CrasInputStream::NotifyStreamError(int err) {
    237   if (callback_)
    238     callback_->OnError(this);
    239 }
    240 
    241 double 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 
    250 void 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 
    265 double 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 
    273 double CrasInputStream::GetVolumeRatioFromDecibels(double dB) const {
    274   return pow(10, dB / 20.0);
    275 }
    276 
    277 double CrasInputStream::GetDecibelsFromVolumeRatio(double volume_ratio) const {
    278   return 20 * log10(volume_ratio);
    279 }
    280 
    281 }  // namespace media
    282