1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "media/audio/alsa/alsa_util.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "media/audio/alsa/alsa_wrapper.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace alsa_util {
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static snd_pcm_t* OpenDevice(media::AlsaWrapper* wrapper,
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const char* device_name,
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             snd_pcm_stream_t type,
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             int channels,
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             int sample_rate,
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             snd_pcm_format_t pcm_format,
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             int latency_us) {
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  snd_pcm_t* handle = NULL;
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int error = wrapper->PcmOpen(&handle, device_name, type, SND_PCM_NONBLOCK);
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (error < 0) {
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "PcmOpen: " << device_name << ","
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << wrapper->StrError(error);
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  error = wrapper->PcmSetParams(handle, pcm_format,
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                SND_PCM_ACCESS_RW_INTERLEAVED, channels,
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                sample_rate, 1, latency_us);
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (error < 0) {
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "PcmSetParams: " << device_name << ", "
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << wrapper->StrError(error) << " - Format: " << pcm_format
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << " Channels: " << channels << " Latency: " << latency_us;
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (alsa_util::CloseDevice(wrapper, handle) < 0) {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(ajwong): Retry on certain errors?
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(WARNING) << "Unable to close audio device. Leaking handle.";
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return handle;
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static std::string DeviceNameToControlName(const std::string& device_name) {
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char kMixerPrefix[] = "hw";
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string control_name;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t pos1 = device_name.find(':');
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (pos1 == std::string::npos) {
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    control_name = device_name;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Examples:
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // deviceName: "front:CARD=Intel,DEV=0", controlName: "hw:CARD=Intel".
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // deviceName: "default:CARD=Intel", controlName: "CARD=Intel".
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t pos2 = device_name.find(',');
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    control_name = (pos2 == std::string::npos) ?
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        device_name.substr(pos1) :
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        kMixerPrefix + device_name.substr(pos1, pos2 - pos1);
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return control_name;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)snd_pcm_format_t BitsToFormat(int bits_per_sample) {
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (bits_per_sample) {
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 8:
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return SND_PCM_FORMAT_U8;
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 16:
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return SND_PCM_FORMAT_S16;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 24:
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return SND_PCM_FORMAT_S24;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 32:
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return SND_PCM_FORMAT_S32;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return SND_PCM_FORMAT_UNKNOWN;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int CloseDevice(media::AlsaWrapper* wrapper, snd_pcm_t* handle) {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string device_name = wrapper->PcmName(handle);
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int error = wrapper->PcmClose(handle);
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (error < 0) {
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "PcmClose: " << device_name << ", "
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << wrapper->StrError(error);
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return error;
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)snd_pcm_t* OpenCaptureDevice(media::AlsaWrapper* wrapper,
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const char* device_name,
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             int channels,
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             int sample_rate,
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             snd_pcm_format_t pcm_format,
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             int latency_us) {
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OpenDevice(wrapper, device_name, SND_PCM_STREAM_CAPTURE, channels,
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    sample_rate, pcm_format, latency_us);
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)snd_pcm_t* OpenPlaybackDevice(media::AlsaWrapper* wrapper,
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              const char* device_name,
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              int channels,
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              int sample_rate,
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              snd_pcm_format_t pcm_format,
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              int latency_us) {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OpenDevice(wrapper, device_name, SND_PCM_STREAM_PLAYBACK, channels,
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    sample_rate, pcm_format, latency_us);
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)snd_mixer_t* OpenMixer(media::AlsaWrapper* wrapper,
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       const std::string& device_name) {
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  snd_mixer_t* mixer = NULL;
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int error = wrapper->MixerOpen(&mixer, 0);
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (error < 0) {
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "MixerOpen: " << device_name << ", "
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << wrapper->StrError(error);
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string control_name = DeviceNameToControlName(device_name);
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  error = wrapper->MixerAttach(mixer, control_name.c_str());
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (error < 0) {
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "MixerAttach, " << control_name << ", "
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << wrapper->StrError(error);
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    alsa_util::CloseMixer(wrapper, mixer, device_name);
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  error = wrapper->MixerElementRegister(mixer, NULL, NULL);
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (error < 0) {
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "MixerElementRegister: " << control_name << ", "
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << wrapper->StrError(error);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    alsa_util::CloseMixer(wrapper, mixer, device_name);
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return mixer;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CloseMixer(media::AlsaWrapper* wrapper, snd_mixer_t* mixer,
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                const std::string& device_name) {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!mixer)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wrapper->MixerFree(mixer);
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int error = 0;
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!device_name.empty()) {
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string control_name = DeviceNameToControlName(device_name);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    error = wrapper->MixerDetach(mixer, control_name.c_str());
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (error < 0) {
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(WARNING) << "MixerDetach: " << control_name << ", "
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   << wrapper->StrError(error);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  error = wrapper->MixerClose(mixer);
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (error < 0) {
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "MixerClose: " << wrapper->StrError(error);
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)snd_mixer_elem_t* LoadCaptureMixerElement(media::AlsaWrapper* wrapper,
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          snd_mixer_t* mixer) {
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!mixer)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int error = wrapper->MixerLoad(mixer);
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (error < 0) {
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "MixerLoad: " << wrapper->StrError(error);
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  snd_mixer_elem_t* elem = NULL;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  snd_mixer_elem_t* mic_elem = NULL;
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char kCaptureElemName[] = "Capture";
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char kMicElemName[] = "Mic";
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (elem = wrapper->MixerFirstElem(mixer);
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       elem;
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       elem = wrapper->MixerNextElem(elem)) {
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (wrapper->MixerSelemIsActive(elem)) {
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const char* elem_name = wrapper->MixerSelemName(elem);
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (strcmp(elem_name, kCaptureElemName) == 0)
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return elem;
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else if (strcmp(elem_name, kMicElemName) == 0)
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mic_elem = elem;
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Did not find any Capture handle, use the Mic handle.
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return mic_elem;
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace alsa_util
201