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/audio_manager.h"
     14 #include "media/audio/cras/audio_manager_cras.h"
     15 #include "media/audio/linux/alsa_util.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 == AudioManagerCras::kLoopbackDeviceId
     30                             ? CRAS_STREAM_POST_MIX_PRE_DSP
     31                             : CRAS_STREAM_INPUT) {
     32   DCHECK(audio_manager_);
     33 }
     34 
     35 CrasInputStream::~CrasInputStream() {
     36   DCHECK(!client_);
     37 }
     38 
     39 bool 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 
     89 void 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 
    106 void 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   LOG(ERROR) << "Input Start";
    118 
    119   // Prepare |audio_format| and |stream_params| for the stream we
    120   // will create.
    121   cras_audio_format* audio_format = cras_audio_format_create(
    122       alsa_util::BitsToFormat(params_.bits_per_sample()),
    123       params_.sample_rate(),
    124       params_.channels());
    125   if (!audio_format) {
    126     DLOG(WARNING) << "Error setting up audio parameters.";
    127     callback_->OnError(this);
    128     callback_ = NULL;
    129     return;
    130   }
    131 
    132   unsigned int frames_per_packet = params_.frames_per_buffer();
    133   cras_stream_params* stream_params = cras_client_stream_params_create(
    134       stream_direction_,
    135       frames_per_packet,  // Total latency.
    136       frames_per_packet,  // Call back when this many ready.
    137       frames_per_packet,  // Minimum Callback level ignored for capture streams.
    138       CRAS_STREAM_TYPE_DEFAULT,
    139       0,  // Unused flags.
    140       this,
    141       CrasInputStream::SamplesReady,
    142       CrasInputStream::StreamError,
    143       audio_format);
    144   if (!stream_params) {
    145     DLOG(WARNING) << "Error setting up stream parameters.";
    146     callback_->OnError(this);
    147     callback_ = NULL;
    148     cras_audio_format_destroy(audio_format);
    149     return;
    150   }
    151 
    152   // Before starting the stream, save the number of bytes in a frame for use in
    153   // the callback.
    154   bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format);
    155 
    156   // Adding the stream will start the audio callbacks.
    157   if (cras_client_add_stream(client_, &stream_id_, stream_params)) {
    158     DLOG(WARNING) << "Failed to add the stream.";
    159     callback_->OnError(this);
    160     callback_ = NULL;
    161   }
    162 
    163   // Done with config params.
    164   cras_audio_format_destroy(audio_format);
    165   cras_client_stream_params_destroy(stream_params);
    166 
    167   started_ = true;
    168 }
    169 
    170 void CrasInputStream::Stop() {
    171   DCHECK(client_);
    172 
    173   if (!callback_ || !started_)
    174     return;
    175 
    176   StopAgc();
    177 
    178   // Removing the stream from the client stops audio.
    179   cras_client_rm_stream(client_, stream_id_);
    180 
    181   started_ = false;
    182 }
    183 
    184 // Static callback asking for samples.  Run on high priority thread.
    185 int 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.
    197 int 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 
    206 void 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 
    238 void CrasInputStream::NotifyStreamError(int err) {
    239   if (callback_)
    240     callback_->OnError(this);
    241 }
    242 
    243 double 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 
    252 void 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 
    267 double 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 
    275 double CrasInputStream::GetVolumeRatioFromDecibels(double dB) const {
    276   return pow(10, dB / 20.0);
    277 }
    278 
    279 double CrasInputStream::GetDecibelsFromVolumeRatio(double volume_ratio) const {
    280   return 20 * log10(volume_ratio);
    281 }
    282 
    283 }  // namespace media
    284