Home | History | Annotate | Download | only in linux
      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/linux/alsa_util.h"
      6 
      7 #include <string>
      8 
      9 #include "base/logging.h"
     10 #include "media/audio/linux/alsa_wrapper.h"
     11 
     12 namespace alsa_util {
     13 
     14 static snd_pcm_t* OpenDevice(media::AlsaWrapper* wrapper,
     15                              const char* device_name,
     16                              snd_pcm_stream_t type,
     17                              int channels,
     18                              int sample_rate,
     19                              snd_pcm_format_t pcm_format,
     20                              int latency_us) {
     21   snd_pcm_t* handle = NULL;
     22   int error = wrapper->PcmOpen(&handle, device_name, type, SND_PCM_NONBLOCK);
     23   if (error < 0) {
     24     LOG(WARNING) << "PcmOpen: " << device_name << ","
     25                  << wrapper->StrError(error);
     26     return NULL;
     27   }
     28 
     29   error = wrapper->PcmSetParams(handle, pcm_format,
     30                                 SND_PCM_ACCESS_RW_INTERLEAVED, channels,
     31                                 sample_rate, 1, latency_us);
     32   if (error < 0) {
     33     LOG(WARNING) << "PcmSetParams: " << device_name << ", "
     34                  << wrapper->StrError(error) << " - Format: " << pcm_format
     35                  << " Channels: " << channels << " Latency: " << latency_us;
     36     if (alsa_util::CloseDevice(wrapper, handle) < 0) {
     37       // TODO(ajwong): Retry on certain errors?
     38       LOG(WARNING) << "Unable to close audio device. Leaking handle.";
     39     }
     40     return NULL;
     41   }
     42 
     43   return handle;
     44 }
     45 
     46 static std::string DeviceNameToControlName(const std::string& device_name) {
     47   const char kMixerPrefix[] = "hw";
     48   std::string control_name;
     49   size_t pos1 = device_name.find(':');
     50   if (pos1 == std::string::npos) {
     51     control_name = device_name;
     52   } else {
     53     // Examples:
     54     // deviceName: "front:CARD=Intel,DEV=0", controlName: "hw:CARD=Intel".
     55     // deviceName: "default:CARD=Intel", controlName: "CARD=Intel".
     56     size_t pos2 = device_name.find(',');
     57     control_name = (pos2 == std::string::npos) ?
     58         device_name.substr(pos1) :
     59         kMixerPrefix + device_name.substr(pos1, pos2 - pos1);
     60   }
     61 
     62   return control_name;
     63 }
     64 
     65 snd_pcm_format_t BitsToFormat(int bits_per_sample) {
     66   switch (bits_per_sample) {
     67     case 8:
     68       return SND_PCM_FORMAT_U8;
     69 
     70     case 16:
     71       return SND_PCM_FORMAT_S16;
     72 
     73     case 24:
     74       return SND_PCM_FORMAT_S24;
     75 
     76     case 32:
     77       return SND_PCM_FORMAT_S32;
     78 
     79     default:
     80       return SND_PCM_FORMAT_UNKNOWN;
     81   }
     82 }
     83 
     84 int CloseDevice(media::AlsaWrapper* wrapper, snd_pcm_t* handle) {
     85   std::string device_name = wrapper->PcmName(handle);
     86   int error = wrapper->PcmClose(handle);
     87   if (error < 0) {
     88     LOG(ERROR) << "PcmClose: " << device_name << ", "
     89                << wrapper->StrError(error);
     90   }
     91 
     92   return error;
     93 }
     94 
     95 snd_pcm_t* OpenCaptureDevice(media::AlsaWrapper* wrapper,
     96                              const char* device_name,
     97                              int channels,
     98                              int sample_rate,
     99                              snd_pcm_format_t pcm_format,
    100                              int latency_us) {
    101   return OpenDevice(wrapper, device_name, SND_PCM_STREAM_CAPTURE, channels,
    102                     sample_rate, pcm_format, latency_us);
    103 }
    104 
    105 snd_pcm_t* OpenPlaybackDevice(media::AlsaWrapper* wrapper,
    106                               const char* device_name,
    107                               int channels,
    108                               int sample_rate,
    109                               snd_pcm_format_t pcm_format,
    110                               int latency_us) {
    111   return OpenDevice(wrapper, device_name, SND_PCM_STREAM_PLAYBACK, channels,
    112                     sample_rate, pcm_format, latency_us);
    113 }
    114 
    115 snd_mixer_t* OpenMixer(media::AlsaWrapper* wrapper,
    116                        const std::string& device_name) {
    117   snd_mixer_t* mixer = NULL;
    118 
    119   int error = wrapper->MixerOpen(&mixer, 0);
    120   if (error < 0) {
    121     LOG(ERROR) << "MixerOpen: " << device_name << ", "
    122                << wrapper->StrError(error);
    123     return NULL;
    124   }
    125 
    126   std::string control_name = DeviceNameToControlName(device_name);
    127   error = wrapper->MixerAttach(mixer, control_name.c_str());
    128   if (error < 0) {
    129     LOG(ERROR) << "MixerAttach, " << control_name << ", "
    130                << wrapper->StrError(error);
    131     alsa_util::CloseMixer(wrapper, mixer, device_name);
    132     return NULL;
    133   }
    134 
    135   error = wrapper->MixerElementRegister(mixer, NULL, NULL);
    136   if (error < 0) {
    137     LOG(ERROR) << "MixerElementRegister: " << control_name << ", "
    138                << wrapper->StrError(error);
    139     alsa_util::CloseMixer(wrapper, mixer, device_name);
    140     return NULL;
    141   }
    142 
    143   return mixer;
    144 }
    145 
    146 void CloseMixer(media::AlsaWrapper* wrapper, snd_mixer_t* mixer,
    147                 const std::string& device_name) {
    148   if (!mixer)
    149     return;
    150 
    151   wrapper->MixerFree(mixer);
    152 
    153   int error = 0;
    154   if (!device_name.empty()) {
    155     std::string control_name = DeviceNameToControlName(device_name);
    156     error = wrapper->MixerDetach(mixer, control_name.c_str());
    157     if (error < 0) {
    158       LOG(WARNING) << "MixerDetach: " << control_name << ", "
    159                    << wrapper->StrError(error);
    160     }
    161   }
    162 
    163   error = wrapper->MixerClose(mixer);
    164   if (error < 0) {
    165     LOG(WARNING) << "MixerClose: " << wrapper->StrError(error);
    166   }
    167 }
    168 
    169 snd_mixer_elem_t* LoadCaptureMixerElement(media::AlsaWrapper* wrapper,
    170                                           snd_mixer_t* mixer) {
    171   if (!mixer)
    172     return NULL;
    173 
    174   int error = wrapper->MixerLoad(mixer);
    175   if (error < 0) {
    176     LOG(ERROR) << "MixerLoad: " << wrapper->StrError(error);
    177     return NULL;
    178   }
    179 
    180   snd_mixer_elem_t* elem = NULL;
    181   snd_mixer_elem_t* mic_elem = NULL;
    182   const char kCaptureElemName[] = "Capture";
    183   const char kMicElemName[] = "Mic";
    184   for (elem = wrapper->MixerFirstElem(mixer);
    185        elem;
    186        elem = wrapper->MixerNextElem(elem)) {
    187     if (wrapper->MixerSelemIsActive(elem)) {
    188       const char* elem_name = wrapper->MixerSelemName(elem);
    189       if (strcmp(elem_name, kCaptureElemName) == 0)
    190         return elem;
    191       else if (strcmp(elem_name, kMicElemName) == 0)
    192         mic_elem = elem;
    193     }
    194   }
    195 
    196   // Did not find any Capture handle, use the Mic handle.
    197   return mic_elem;
    198 }
    199 
    200 }  // namespace alsa_util
    201