Esempio n. 1
0
static void installAlsaPhononDeviceHandle()
{
#ifdef HAVE_LIBASOUND2
    // after recreating the global configuration we can go and install custom configuration
    snd_config_update_free_global();
    snd_config_update();
    Q_ASSERT(snd_config);

    // x-phonon: device
    QFile phononDefinition(":/phonon/phonondevice.alsa");
    phononDefinition.open(QIODevice::ReadOnly);
    const QByteArray &phononDefinitionData = phononDefinition.readAll();

    snd_input_t *sndInput = 0;
    if (0 == snd_input_buffer_open(&sndInput, phononDefinitionData.constData(), phononDefinitionData.size())) {
        Q_ASSERT(sndInput);
        snd_config_load(snd_config, sndInput);
        snd_input_close(sndInput);
    }

#if 0
    // phonon_softvol: device
    QFile softvolDefinition(":/phonon/softvol.alsa");
    softvolDefinition.open(QIODevice::ReadOnly);
    const QByteArray softvolDefinitionData = softvolDefinition.readAll();

    sndInput = 0;
    if (0 == snd_input_buffer_open(&sndInput, softvolDefinitionData.constData(), softvolDefinitionData.size())) {
        Q_ASSERT(sndInput);
        snd_config_load(snd_config, sndInput);
        snd_input_close(sndInput);
    }
#endif
#endif // HAVE_LIBASOUND2
}
Esempio n. 2
0
CAESinkALSA::CAESinkALSA() :
  m_pcm(NULL)
{
  /* ensure that ALSA has been initialized */
  if (!snd_config)
    snd_config_update();
}
Esempio n. 3
0
CAESinkALSA::CAESinkALSA() :
  m_bufferSize(0),
  m_formatSampleRateMul(0.0),
  m_passthrough(false),
  m_pcm(NULL),
  m_timeout(0)
{
  /* ensure that ALSA has been initialized */
  if (!snd_config)
    snd_config_update();
}
Esempio n. 4
0
bool CAESinkALSA::SoftResume()
{
    // reinit all the clibber
    bool ret = true; // all fine
    if(!m_pcm)
    {
      if (!snd_config)
        snd_config_update();

      ret = Initialize(m_initFormat, m_initDevice);
    }
   //we want that AE loves us again - reinit when initialize failed
   return ret; // force reinit if false
}
Esempio n. 5
0
bool CAESinkALSA::SoftResume()
{
    // reinit all the clibber
    if(!m_pcm)
    {
      if (!snd_config)
        snd_config_update();

    // Initialize what we had before again, SoftAE might keep it
    // but ignore ret value to give the chance to do reopening
    Initialize(m_initFormat, m_initDevice);
    }
   // make sure that OpenInternalSink is done again
   return false;
}
Esempio n. 6
0
/*----------------------------------------------------------------------------
**  ALSA_DefaultDevices
**
**      Jump through Alsa style hoops to (hopefully) properly determine
**  Alsa defaults for CTL Card #, as well as for PCM Card + Device #.
**  We'll also find out if the user has set any of the environment
**  variables that specify we're to use a specific card or device.
**
**  Parameters:
**      directhw        Whether to use a direct hardware device or not;
**                      essentially switches the pcm device name from
**                      one of 'default:X' or 'plughw:X' to "hw:X"
**      defctlcard      If !NULL, will hold the ctl card number given
**                      by the ALSA config as the default
**      defpcmcard      If !NULL, default pcm card #
**      defpcmdev       If !NULL, default pcm device #
**      fixedctlcard    If !NULL, and the user set the appropriate
**                          environment variable, we'll set to the
**                          card the user specified.
**      fixedpcmcard    If !NULL, and the user set the appropriate
**                          environment variable, we'll set to the
**                          card the user specified.
**      fixedpcmdev     If !NULL, and the user set the appropriate
**                          environment variable, we'll set to the
**                          device the user specified.
**
**  Returns:  0 on success, < 0 on failure
*/
static int ALSA_DefaultDevices(int directhw,
            long *defctlcard,
            long *defpcmcard, long *defpcmdev,
            int *fixedctlcard,
            int *fixedpcmcard, int *fixedpcmdev)
{
    snd_config_t   *configp;
    char pcmsearch[256];

    ALSA_RETURN_ONFAIL(snd_config_update());

    if (defctlcard)
        if (snd_config_search(snd_config, "defaults.ctl.card", &configp) >= 0)
            snd_config_get_integer(configp, defctlcard);

    if (defpcmcard)
        if (snd_config_search(snd_config, "defaults.pcm.card", &configp) >= 0)
            snd_config_get_integer(configp, defpcmcard);

    if (defpcmdev)
        if (snd_config_search(snd_config, "defaults.pcm.device", &configp) >= 0)
            snd_config_get_integer(configp, defpcmdev);


    if (fixedctlcard)
    {
        if (snd_config_search(snd_config, "*****@*****.**", &configp) >= 0)
            ALSA_CheckEnvironment(configp, fixedctlcard);
    }

    if (fixedpcmcard)
    {
        sprintf(pcmsearch, "*****@*****.**", directhw ? "hw" : "plughw");
        if (snd_config_search(snd_config, pcmsearch, &configp) >= 0)
            ALSA_CheckEnvironment(configp, fixedpcmcard);
    }

    if (fixedpcmdev)
    {
        sprintf(pcmsearch, "*****@*****.**", directhw ? "hw" : "plughw");
        if (snd_config_search(snd_config, pcmsearch, &configp) >= 0)
            ALSA_CheckEnvironment(configp, fixedpcmdev);
    }

    return 0;
}
Esempio n. 7
0
int main()
{
    size_t start, end;
    std::string desc;
    void ** hints, ** str_c;
    char * name_c, * desc_c;
    int index = 0;
    const char * ifaces[] = {
        "card", "pcm", "rawmidi", "timer", "seq", "hwdep", 0
    };

    snd_config_update();

    while (ifaces[index]) {
        std::cout << "---- Trying \"" << ifaces[index] << "\" ----\n\n";
        if (snd_device_name_hint(-1, ifaces[index], &hints) < 0) {
            std::cerr << "hint failed on \"" << ifaces[index] << "\"\n\n";
            ++index;
            continue;
        }

        str_c = hints;
        while (*str_c) {
            name_c = snd_device_name_get_hint(*str_c, "NAME");
            desc_c = snd_device_name_get_hint(*str_c, "DESC");

            std::cout << name_c << "\n";
            start = 0;
            desc = desc_c;
            do {
                end = desc.find("\n", start);
                std::cout << "   " << desc.substr(start, end - start) << "\n";
                start = end + 1;
            } while (end != std::string::npos);
            std::cout << "\n";
            free(name_c);
            free(desc_c);
            ++str_c;
        }
        snd_device_name_free_hint(hints);
        ++index;
    }
    return 0;
}
void ProjectOptions::InitDevInfo()
{
	snd_config_update();

	int ctlNum = -1;
	SoundDevInfo *inf = waveList.AddItem();
	inf->name = "default";
	inf->id = -1;
	inf->sub = -1;
	inf->type = 0;
	inf->info = NULL;

	while (snd_card_next(&ctlNum) == 0 && ctlNum >= 0 )
	{
#if SND_LIB_VERSION >= 0x1000E
		void **hints = 0;
		void **hh;
		if (snd_device_name_hint(ctlNum, "pcm", &hints) == 0)
		{
			hh = hints;
			while (*hh)
			{
				char *name = snd_device_name_get_hint(*hh, "NAME");
				char *desc = snd_device_name_get_hint(*hh, "DESC");
				for (char *cp = desc; *cp; cp++)
				{
					if (*cp < 0x20)
						*cp = ' ';
				}
				SoundDevInfo *inf = waveList.AddItem();
				inf->id = ctlNum;
				inf->sub = 0;
				inf->type = 0;
				inf->name = desc;
				inf->info = name;
				if (desc)
					free(desc);
				hh++;
			}
			snd_device_name_free_hint(hints);
		}
		if (snd_device_name_hint(ctlNum, "rawmidi", &hints) == 0)
		{
			hh = hints;
			while (*hh)
			{
				char *name = snd_device_name_get_hint(*hh, "NAME");
				char *desc = snd_device_name_get_hint(*hh, "DESC");
				for (char *cp = desc; *cp; cp++)
				{
					if (*cp < 0x20)
						*cp = ' ';
				}
				SoundDevInfo *inf = midiList.AddItem();
				inf->id = ctlNum;
				inf->sub = 0;
				inf->type = 0;
				inf->name = desc;
				inf->info = name;
				if (desc)
					free(desc);
				hh++;
			}
			snd_device_name_free_hint(hints);
		}
#endif
		/*
        snd_ctl_card_info_t *devInfo;
        snd_pcm_t *pcmHandle;
        snd_pcm_info_t *pcmInfo;
        snd_rawmidi_t *midiHandle;
        snd_rawmidi_info_t *midiInfo;

        char hw[128];

        snprintf(hw, sizeof(hw), "hw:%d", ctlNum);

        int devNum;
        snd_ctl_t *ctl;
        if (snd_ctl_open(&ctl, hw, 0) >= 0)
        {
        	devNum = -1;
			while (snd_ctl_pcm_next_device(ctl, &devNum) == 0 && devNum >= 0)
			{

				snprintf(hw, sizeof(hw), "hw:%d,%d", ctlNum, devNum);
				if (snd_pcm_open(&pcmHandle, hw, SND_PCM_STREAM_PLAYBACK, 0) >= 0)
				{
					snd_pcm_info_malloc(&pcmInfo);
					snd_pcm_info(pcmHandle, pcmInfo);
					SoundDevInfo *inf = waveList.AddItem();
					inf->id = ctlNum;
					inf->sub = devNum;
					inf->type = 0;
					inf->info = (void*)pcmInfo;
					inf->name = snd_pcm_info_get_name(pcmInfo);
					//inf->name += " - ";
					//inf->name += snd_pcm_info_get_subdevice_name(pcmInfo);
					snd_pcm_close(pcmHandle);
					pcmHandle = 0;
				}
			}
			devNum = -1;
			while (snd_ctl_rawmidi_next_device(ctl, &devNum) == 0 && devNum >= 0)
			{
				snprintf(hw, sizeof(hw), "hw:%d,%d", ctlNum, devNum);
				if (snd_rawmidi_open(&midiHandle, NULL, hw, 0) >= 0)
				{
					snd_rawmidi_info_malloc(&midiInfo);
					snd_rawmidi_info(midiHandle, midiInfo);
					SoundDevInfo *inf = midiList.AddItem();
					inf->id = ctlNum;
					inf->sub = devNum;
					inf->type = 1;
					inf->info = (void*)midiInfo;
					inf->name = snd_rawmidi_info_get_name(midiInfo);
					//inf->name += " - ";
					//inf->name += snd_rawmidi_info_get_subdevice_name(midiInfo);
					snd_rawmidi_close(midiHandle);
				}
			}
//			SoundDevInfo *inf = waveList.AddItem();
//			snd_ctl_card_info_alloca(&devInfo);
//	        snd_ctl_card_info(ctl, devInfo);
//			inf->name = snd_ctl_card_info_get_name(devInfo);
//			inf->info = reinterpret_cast<void*>(devInfo);
//			inf->id = (long)devNum;
			snd_ctl_close(ctl);
		}*/
	}
	// TODO:
	//snd_lib_error_set_handler(alsa_error_handler);
}
Esempio n. 9
0
void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
{
  /* ensure that ALSA has been initialized */
  snd_lib_error_set_handler(sndLibErrorHandler);
  if(!snd_config || force)
  {
    if(force)
      snd_config_update_free_global();

    snd_config_update();
  }

  snd_config_t *config;
  snd_config_copy(&config, snd_config);

  /* Always enumerate the default device.
   * Note: If "default" is a stereo device, EnumerateDevice()
   * will automatically add "@" instead to enable surroundXX mangling.
   * We don't want to do that if "default" can handle multichannel
   * itself (e.g. in case of a pulseaudio server). */
  EnumerateDevice(list, "default", "", config);

  void **hints;

  if (snd_device_name_hint(-1, "pcm", &hints) < 0)
  {
    CLog::Log(LOGINFO, "CAESinkALSA - Unable to get a list of devices");
    return;
  }

  std::string defaultDescription;

  for (void** hint = hints; *hint != NULL; ++hint)
  {
    char *io = snd_device_name_get_hint(*hint, "IOID");
    char *name = snd_device_name_get_hint(*hint, "NAME");
    char *desc = snd_device_name_get_hint(*hint, "DESC");
    if ((!io || strcmp(io, "Output") == 0) && name
        && strcmp(name, "null") != 0)
    {
      std::string baseName = std::string(name);
      baseName = baseName.substr(0, baseName.find(':'));

      if (strcmp(name, "default") == 0)
      {
        /* added already, but lets get the description if we have one */
        if (desc)
          defaultDescription = desc;
      }
      else if (baseName == "front")
      {
        /* Enumerate using the surroundXX mangling */
        /* do not enumerate basic "front", it is already handled
         * by the default "@" entry added in the very beginning */
        if (strcmp(name, "front") != 0)
          EnumerateDevice(list, std::string("@") + (name+5), desc ? desc : name, config);
      }

      /* Do not enumerate "default", it is already enumerated above. */

      /* Do not enumerate the sysdefault or surroundXX devices, those are
       * always accompanied with a "front" device and it is handled above
       * as "@". The below devices will be automatically used if available
       * for a "@" device. */

      /* Ubuntu has patched their alsa-lib so that "defaults.namehint.extended"
       * defaults to "on" instead of upstream "off", causing lots of unwanted
       * extra devices (many of which are not actually routed properly) to be
       * found by the enumeration process. Skip them as well ("hw", "dmix",
       * "plughw", "dsnoop"). */

      else if (baseName != "default"
            && baseName != "sysdefault"
            && baseName != "surround40"
            && baseName != "surround41"
            && baseName != "surround50"
            && baseName != "surround51"
            && baseName != "surround71"
            && baseName != "hw"
            && baseName != "dmix"
            && baseName != "plughw"
            && baseName != "dsnoop")
      {
        EnumerateDevice(list, name, desc ? desc : name, config);
      }
    }
    free(io);
    free(name);
    free(desc);
  }
  snd_device_name_free_hint(hints);

  /* set the displayname for default device */
  if (!list.empty() && list[0].m_deviceName == "default")
  {
    /* If we have one from a hint (DESC), use it */
    if (!defaultDescription.empty())
      list[0].m_displayName = defaultDescription;
    /* Otherwise use the discovered name or (unlikely) "Default" */
    else if (list[0].m_displayName.empty())
      list[0].m_displayName = "Default";
  }

  /* lets check uniqueness, we may need to append DEV or CARD to DisplayName */
  /* If even a single device of card/dev X clashes with Y, add suffixes to
   * all devices of both them, for clarity. */

  /* clashing card names, e.g. "NVidia", "NVidia_2" */
  std::set<std::string> cardsToAppend;

  /* clashing basename + cardname combinations, e.g. ("hdmi","Nvidia") */
  std::set<std::pair<std::string, std::string> > devsToAppend;

  for (AEDeviceInfoList::iterator it1 = list.begin(); it1 != list.end(); ++it1)
  {
    for (AEDeviceInfoList::iterator it2 = it1+1; it2 != list.end(); ++it2)
    {
      if (it1->m_displayName == it2->m_displayName
       && it1->m_displayNameExtra == it2->m_displayNameExtra)
      {
        /* something needs to be done */
        std::string cardString1 = GetParamFromName(it1->m_deviceName, "CARD");
        std::string cardString2 = GetParamFromName(it2->m_deviceName, "CARD");

        if (cardString1 != cardString2)
        {
          /* card name differs, add identifiers to all devices */
          cardsToAppend.insert(cardString1);
          cardsToAppend.insert(cardString2);
          continue;
        }

        std::string devString1 = GetParamFromName(it1->m_deviceName, "DEV");
        std::string devString2 = GetParamFromName(it2->m_deviceName, "DEV");

        if (devString1 != devString2)
        {
          /* device number differs, add identifiers to all such devices */
          devsToAppend.insert(std::make_pair(it1->m_deviceName.substr(0, it1->m_deviceName.find(':')), cardString1));
          devsToAppend.insert(std::make_pair(it2->m_deviceName.substr(0, it2->m_deviceName.find(':')), cardString2));
          continue;
        }

        /* if we got here, the configuration is really weird, just append the whole device string */
        it1->m_displayName += " (" + it1->m_deviceName + ")";
        it2->m_displayName += " (" + it2->m_deviceName + ")";
      }
    }
  }

  for (std::set<std::string>::iterator it = cardsToAppend.begin();
       it != cardsToAppend.end(); ++it)
  {
    for (AEDeviceInfoList::iterator itl = list.begin(); itl != list.end(); ++itl)
    {
      std::string cardString = GetParamFromName(itl->m_deviceName, "CARD");
      if (cardString == *it)
        /* "HDA NVidia (NVidia)", "HDA NVidia (NVidia_2)", ... */
        itl->m_displayName += " (" + cardString + ")";
    }
  }

  for (std::set<std::pair<std::string, std::string> >::iterator it = devsToAppend.begin();
       it != devsToAppend.end(); ++it)
  {
    for (AEDeviceInfoList::iterator itl = list.begin(); itl != list.end(); ++itl)
    {
      std::string baseName = itl->m_deviceName.substr(0, itl->m_deviceName.find(':'));
      std::string cardString = GetParamFromName(itl->m_deviceName, "CARD");
      if (baseName == it->first && cardString == it->second)
      {
        std::string devString = GetParamFromName(itl->m_deviceName, "DEV");
        /* "HDMI #0", "HDMI #1" ... */
        itl->m_displayNameExtra += " #" + devString;
      }
    }
  }
}
Esempio n. 10
0
/* Work around PulseAudio ALSA plugin bug where the PA server forces a
   higher than requested latency, but the plugin does not update its (and
   ALSA's) internal state to reflect that, leading to an immediate underrun
   situation. Inspired by WINE's make_handle_underrun_config.
   Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/053391.html
   From Mozilla https://github.com/kinetiknz/cubeb/blob/1aa0058d0729eb85505df104cd1ac072432c6d24/src/cubeb_alsa.c
*/
static snd_config_t *init_local_config_with_workaround(ao_device *device, char const *name){
  char pcm_node_name[80];
  int r;
  snd_config_t * lconf;
  snd_config_t * device_node;
  snd_config_t * type_node;
  snd_config_t * node;
  char const * type_string;

  lconf = NULL;
  snprintf(pcm_node_name,80,"pcm.%s",name);

  if (snd_config == NULL) snd_config_update();

  r = snd_config_copy(&lconf, snd_config);
  if(r<0){
    return NULL;
  }

  r = snd_config_search(lconf, pcm_node_name, &device_node);
  if (r != 0) {
    snd_config_delete(lconf);
    return NULL;
  }

  /* Fetch the PCM node's type, and bail out if it's not the PulseAudio plugin. */
  r = snd_config_search(device_node, "type", &type_node);
  if (r != 0) {
    snd_config_delete(lconf);
    return NULL;
  }

  r = snd_config_get_string(type_node, &type_string);
  if (r != 0) {
    snd_config_delete(lconf);
    return NULL;
  }

  if (strcmp(type_string, "pulse") != 0) {
    snd_config_delete(lconf);
    return NULL;
  }

  /* Don't clobber an explicit existing handle_underrun value, set it only
     if it doesn't already exist. */
  r = snd_config_search(device_node, "handle_underrun", &node);
  if (r != -ENOENT) {
    snd_config_delete(lconf);
    return NULL;
  }

  r = snd_config_imake_integer(&node, "handle_underrun", 0);
  if (r != 0) {
    snd_config_delete(lconf);
    return NULL;
  }

  r = snd_config_add(device_node, node);
  if (r != 0) {
    snd_config_delete(lconf);
    return NULL;
  }

  adebug("PulseAudio ALSA-emulation detected: disabling underrun detection\n");
  return lconf;
}
Esempio n. 11
0
std::vector<ALSAInput::Source> ALSAInput::GetSourceList() {
	std::vector<Source> list;

	/*
	This code is based on the ALSA device detection code used in PortAudio. I just ported it to C++ and improved it a bit.
	All credit goes to the PortAudio devs, they saved me a lot of time :).
	*/

	// these ALSA plugins are blacklisted because they are always defined but rarely useful
	// 'pulse' is also blacklisted because the native PulseAudio backend is more reliable
	std::vector<std::string> plugin_blacklist = {
		"cards", "default", "sysdefault", "hw", "plughw", "plug", "dmix", "dsnoop", "shm", "tee", "file", "null",
		"front", "rear", "center_lfe", "side", "surround40", "surround41", "surround50", "surround51", "surround71",
		"iec958", "spdif", "hdmi", "modem", "phoneline", "pulse",
	};
	std::sort(plugin_blacklist.begin(), plugin_blacklist.end());

	// the 'default' PCM must be first, so add it explicitly
	list.push_back(Source("default", "Default source"));

	Logger::LogInfo("[ALSAInput::GetSourceList] " + Logger::tr("Generating source list ..."));

	snd_ctl_card_info_t *alsa_card_info = NULL;
	snd_pcm_info_t *alsa_pcm_info = NULL;
	snd_ctl_t *alsa_ctl = NULL;

	try {

		// allocate card and PCM info structure
		if(snd_ctl_card_info_malloc(&alsa_card_info) < 0) {
			throw std::bad_alloc();
		}
		if(snd_pcm_info_malloc(&alsa_pcm_info) < 0) {
			throw std::bad_alloc();
		}

		// update the ALSA configuration
		snd_config_update_free_global();
		if(snd_config_update() < 0) {
			Logger::LogError("[ALSAInput::GetSourceList] " + Logger::tr("Error: Could not update ALSA configuration!"));
			throw ALSAException();
		}

		// find all PCM plugins (by parsing the config file)
		snd_config_t *alsa_config_pcms = NULL;
		if(snd_config_search(snd_config, "pcm", &alsa_config_pcms) == 0) {
			snd_config_iterator_t i, next;
			snd_config_for_each(i, next, alsa_config_pcms) {
				snd_config_t *alsa_config_pcm = snd_config_iterator_entry(i);

				// get the name
				const char *id = NULL;
				if(snd_config_get_id(alsa_config_pcm, &id) < 0 || id == NULL)
					continue;
				std::string plugin_name = id;

				// ignore the plugin if it is blacklisted
				if(std::binary_search(plugin_blacklist.begin(), plugin_blacklist.end(), plugin_name))
					continue;

				// try to get the description
				std::string plugin_description;
				snd_config_t *alsa_config_description = NULL;
				if(snd_config_search(alsa_config_pcm, "hint.description", &alsa_config_description) == 0) {
					const char *str = NULL;
					if(snd_config_get_string(alsa_config_description, &str) >= 0 && str != NULL) {
						plugin_description = str;
					}
				}

				// if there is no description, ignore it, because it's probably not meant to be used
				if(plugin_description.empty())
					continue;

				// if there is no description, use the type instead
				/*if(plugin_description.empty()) {
					snd_config_t *alsa_config_type = NULL;
					if(snd_config_search(alsa_config_pcm, "type", &alsa_config_type) >= 0) {
						const char *str = NULL;
						if(snd_config_get_string(alsa_config_type, &str) >= 0 && str != NULL) {
							plugin_description = std::string(str) + " plugin";
						}
					}
				}*/

				// add to list
				Logger::LogInfo("[ALSAInput::GetSourceList] " + Logger::tr("Found plugin: [%1] %2").arg(QString::fromStdString(plugin_name)).arg(QString::fromStdString(plugin_description)));
				list.push_back(Source(plugin_name, plugin_description));

			}
		}
Esempio n. 12
0
void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list)
{
  /* ensure that ALSA has been initialized */
  if(!snd_config)
    snd_config_update();

  snd_ctl_t *ctlhandle;
  snd_pcm_t *pcmhandle;

  snd_ctl_card_info_t *ctlinfo;
  snd_ctl_card_info_alloca(&ctlinfo);
  memset(ctlinfo, 0, snd_ctl_card_info_sizeof());

  snd_pcm_hw_params_t *hwparams;
  snd_pcm_hw_params_alloca(&hwparams);
  memset(hwparams, 0, snd_pcm_hw_params_sizeof());

  snd_pcm_info_t *pcminfo;
  snd_pcm_info_alloca(&pcminfo);
  memset(pcminfo, 0, snd_pcm_info_sizeof());

  /* get the sound config */
  snd_config_t *config;
  snd_config_copy(&config, snd_config);

  std::string strHwName;
  int n_cards = -1;
  while (snd_card_next(&n_cards) == 0 && n_cards != -1)
  {
    std::stringstream sstr;
    sstr << "hw:" << n_cards;
    std::string strHwName = sstr.str();

    if (snd_ctl_open_lconf(&ctlhandle, strHwName.c_str(), 0, config) != 0)
    {
      CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to open control for device %s", strHwName.c_str());
      continue;
    }

    if (snd_ctl_card_info(ctlhandle, ctlinfo) != 0)
    {
      CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to get card control info for device %s", strHwName.c_str());
      snd_ctl_close(ctlhandle);
      continue;
    }

    snd_hctl_t *hctl;
    if (snd_hctl_open_ctl(&hctl, ctlhandle) != 0)
      hctl = NULL;
    snd_hctl_load(hctl);

    int pcm_index    = 0;
    int iec958_index = 0;
    int hdmi_index   = 0;

    int dev = -1;
    while (snd_ctl_pcm_next_device(ctlhandle, &dev) == 0 && dev != -1)
    {
      snd_pcm_info_set_device   (pcminfo, dev);
      snd_pcm_info_set_subdevice(pcminfo, 0  );
      snd_pcm_info_set_stream   (pcminfo, SND_PCM_STREAM_PLAYBACK);

      if (snd_ctl_pcm_info(ctlhandle, pcminfo) < 0)
      {
        CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Skipping device %s,%d as it does not have PCM playback ability", strHwName.c_str(), dev);
        continue;
      }

      int dev_index;
      sstr.str(std::string());
      CAEDeviceInfo info;
      std::string devname = snd_pcm_info_get_name(pcminfo);

      bool maybeHDMI = false;

      /* detect HDMI */
      if (devname.find("HDMI") != std::string::npos)
      { 
        info.m_deviceType = AE_DEVTYPE_HDMI;
        dev_index = hdmi_index++;
        sstr << "hdmi";
      }
      else
      {
        /* detect IEC958 */

        /* some HDMI devices (intel) report Digital for HDMI also */
        if (devname.find("Digital") != std::string::npos)
          maybeHDMI = true;

        if (maybeHDMI || devname.find("IEC958" ) != std::string::npos)
        {
          info.m_deviceType = AE_DEVTYPE_IEC958;
          dev_index = iec958_index; /* dont increment, it might be HDMI */
          sstr << "iec958";
        }
        else
        {
          info.m_deviceType = AE_DEVTYPE_PCM;
          dev_index = pcm_index++;
          sstr << "hw";
        }
      }

      /* build the driver string to pass to ALSA */
      sstr << ":CARD=" << snd_ctl_card_info_get_id(ctlinfo) << ",DEV=" << dev_index;
      info.m_deviceName = sstr.str();

      /* get the friendly display name*/
      info.m_displayName      = snd_ctl_card_info_get_name(ctlinfo);
      info.m_displayNameExtra = devname;

      /* open the device for testing */
      int err = snd_pcm_open_lconf(&pcmhandle, info.m_deviceName.c_str(), SND_PCM_STREAM_PLAYBACK, 0, config);

      /* if open of possible IEC958 failed and it could be HDMI, try as HDMI */
      if (err < 0 && maybeHDMI)
      {
        /* check for HDMI if it failed */
        sstr.str(std::string());
        dev_index = hdmi_index;

        sstr << "hdmi";
        sstr << ":CARD=" << snd_ctl_card_info_get_id(ctlinfo) << ",DEV=" << dev_index;
        info.m_deviceName = sstr.str();
        err = snd_pcm_open_lconf(&pcmhandle, info.m_deviceName.c_str(), SND_PCM_STREAM_PLAYBACK, 0, config);

        /* if it was valid, increment the index and set the type */
        if (err >= 0)
        {
          ++hdmi_index;
          info.m_deviceType = AE_DEVTYPE_HDMI;
        }
      }

      /* if it's still IEC958, increment the index */
      if (info.m_deviceType == AE_DEVTYPE_IEC958)
        ++iec958_index;

      /* final error check */
      if (err < 0)
      {
        CLog::Log(LOGINFO, "CAESinkALSA::EnumerateDevicesEx - Unable to open %s for capability detection", strHwName.c_str());
        continue;
      }

      /* see if we can get ELD for this device */
      if (info.m_deviceType == AE_DEVTYPE_HDMI)
      {
        bool badHDMI = false;
        if (hctl && !GetELD(hctl, dev, info, badHDMI))
          CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to obtain ELD information for device %s, make sure you have ALSA >= 1.0.25", info.m_deviceName.c_str());

        if (badHDMI)
        {
          CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Skipping HDMI device %s as it has no ELD data", info.m_deviceName.c_str());
          continue;
        }
      }

      /* ensure we can get a playback configuration for the device */
      if (snd_pcm_hw_params_any(pcmhandle, hwparams) < 0)
      {
        CLog::Log(LOGINFO, "CAESinkALSA::EnumerateDevicesEx - No playback configurations available for device %s", info.m_deviceName.c_str());
        snd_pcm_close(pcmhandle);
        continue;
      }

      /* detect the available sample rates */
      for (unsigned int *rate = ALSASampleRateList; *rate != 0; ++rate)
        if (snd_pcm_hw_params_test_rate(pcmhandle, hwparams, *rate, 0) >= 0)
          info.m_sampleRates.push_back(*rate);

      /* detect the channels available */
      int channels = 0;
      for (int i = 1; i <= ALSA_MAX_CHANNELS; ++i)
        if (snd_pcm_hw_params_test_channels(pcmhandle, hwparams, i) >= 0)
          channels = i;

      CAEChannelInfo alsaChannels;
      for (int i = 0; i < channels; ++i)
      {
        if (!info.m_channels.HasChannel(ALSAChannelMap[i]))
          info.m_channels += ALSAChannelMap[i];
        alsaChannels += ALSAChannelMap[i];
      }

      /* remove the channels from m_channels that we cant use */
      info.m_channels.ResolveChannels(alsaChannels);

      /* detect the PCM sample formats that are available */
      for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1))
      {
        if (AE_IS_RAW(i) || i == AE_FMT_MAX)
          continue;
        snd_pcm_format_t fmt = AEFormatToALSAFormat(i);
        if (fmt == SND_PCM_FORMAT_UNKNOWN)
          continue;

        if (snd_pcm_hw_params_test_format(pcmhandle, hwparams, fmt) >= 0)
          info.m_dataFormats.push_back(i);
      }

      snd_pcm_close(pcmhandle);
      list.push_back(info);
    }

    /* snd_hctl_close also closes ctlhandle */
    if (hctl)
      snd_hctl_close(hctl);
    else
      snd_ctl_close(ctlhandle);
  }
}
Esempio n. 13
0
void AlsaBackend::UpdateDevicesList()
{
    Log("AlsaBackend::UpdateDeviceList\n");

    DeviceInfo info;
    void **hints, **n;
    char *name, *descr, *desc;
    unsigned devices = 0;

    InitDevicesList();

    info.SetDevice(devices++, "default", "Default device", "default");
    info.type = DeviceInfo::TYPE_PLUG;
    info.direction = 0;
    PcmPreProbe(info, OUTPUT);
    PcmPreProbe(info, INPUT);
    devicesList.push_back(info);

    // Start with safe alsa detection, list the devices from software config.

    snd_config_update();

    if (snd_device_name_hint(-1, "pcm", &hints) < 0)
        return;

    n = hints;

    while (*n != NULL) {
        name = snd_device_name_get_hint(*n, "NAME");
        descr = snd_device_name_get_hint(*n, "DESC");

        if (!descr)
            desc = (char*)"";
        else
        {
            desc = descr;
            for (int i = strlen(desc); i > 0; i--)
                if (desc[i-1] == '\n')
                    desc[i-1] = ' ';
        }

        if (IgnorePlugin(name))
        {
            Log("Ignoring ALSA device %s", name);
        }
        else
        {
            info.SetDevice(devices++, name, desc, name);
            info.type = DeviceInfo::TYPE_PLUG;
            info.probed = false;
            info.direction = 0;

            PcmPreProbe(info, OUTPUT);
            PcmPreProbe(info, INPUT);

            if (info.direction != 0)
                devicesList.push_back(info);
        }

        if (name != NULL)
            free(name);
        if (descr != NULL)
            free(descr);

        n++;
    }
    snd_device_name_free_hint(hints);

    // Continue with new detection, this is a more thorough test with probing device characteristics

    enum { IDLEN = 12 };
    char hwdev[IDLEN+1];
    int card, err, dev;
    snd_ctl_t*             handle = NULL;
    snd_ctl_card_info_t*   cardinfo;
    snd_pcm_info_t*        pcminfo;

    snd_ctl_card_info_alloca(&cardinfo);
    snd_pcm_info_alloca(&pcminfo);

    card = -1;
    while (snd_card_next(&card) == 0 && card >= 0)
    {
        snprintf(hwdev, IDLEN, "hw:%d", card);
        err = snd_ctl_open(&handle, hwdev, 0);

        if (sc_errcheck(err, "opening control interface", card, -1))
            continue;

        err = snd_ctl_card_info(handle, cardinfo);

        if (sc_errcheck(err, "obtaining card info", card, -1))
        {
            snd_ctl_close(handle);
            continue;
        }

        Log("Card %d, ID '%s', name '%s'", card, snd_ctl_card_info_get_id(cardinfo), snd_ctl_card_info_get_name(cardinfo));

        dev = -1;

        if (snd_ctl_pcm_next_device(handle, &dev) < 0)
        {
            snd_ctl_close(handle);
            continue;
        }

        while (dev >= 0)
        {
            if (!DevProbe(handle, pcminfo, card, dev, OUTPUT) && !DevProbe(handle, pcminfo, card, dev, INPUT))
            {
                if (snd_ctl_pcm_next_device(handle, &dev) < 0)
                    break;
            }

            snprintf(hwdev, IDLEN, "hw:%d,%d", card, dev);
            char strbuf[DEVICE_NAME_MAXLEN];
            snprintf(strbuf, DEVICE_NAME_MAXLEN, "%s, %s", snd_ctl_card_info_get_name(cardinfo), snd_pcm_info_get_name(pcminfo));
            info.SetDevice(devices++, hwdev, strbuf, hwdev);
            info.type = DeviceInfo::TYPE_HW;
            info.probed = false;
            info.direction = 0;

            PcmPreProbe(info, OUTPUT);
            PcmPreProbe(info, INPUT);

            Log("**********\n%s :: %s\n**********\n", info.guid, info.displayName);

            devicesList.push_back(info);

            if (snd_ctl_pcm_next_device(handle, &dev) < 0)
                break;
        }

        snd_ctl_close(handle);
    }

    // And complement with chewing a bit on user-defined entries, too.
    // from PortAudio
    /* Iterate over plugin devices */

    snd_config_t *topNode = NULL;
    assert(snd_config);

    if ((err = snd_config_search(snd_config, "pcm", &topNode)) >= 0)
    {
        snd_config_iterator_t i, next;

        snd_config_for_each(i, next, topNode)
        {
            const char *tpStr = "unknown", *idStr = NULL;
            int err = 0;

            snd_config_t *n = snd_config_iterator_entry(i), *tp = NULL;

            if ((err = snd_config_search(n, "type", &tp)) < 0)
            {
                if (-ENOENT != err)
                {
                    Log("plugin list error: %s", snd_strerror(err));
                }
            }
            else
            {
                snd_config_get_string(tp, &tpStr);
            }
            snd_config_get_id(n, &idStr);
            if (IgnorePlugin(idStr))
            {
                Log("Ignoring ALSA plugin device %s of type %s", idStr, tpStr);
                continue;
            }
            Log("Found plugin %s of type %s", idStr, tpStr);

            info.SetDevice(devices++, idStr, idStr, tpStr);
            info.probed = false;
            info.direction = 0;

            if (strncmp(tpStr, "bluetooth", 9)==0)
                info.type = DeviceInfo::TYPE_BLUETOOTH;
            else if(strncmp(tpStr, "null", 4) == 0)
            {
                info.type = DeviceInfo::TYPE_NULL;
                // Never need to probe the null device.
                info.probed = true;
            }
            else if(strncmp(tpStr, "unknown", 4) == 0)
                info.type = DeviceInfo::TYPE_UNKNOWN;
            else
            {
                info.type = DeviceInfo::TYPE_PLUG;
                // No need to preprobe bluetooth, null and unknown(?) types.
                PcmPreProbe(info, OUTPUT);
                PcmPreProbe(info, INPUT);
            }

            devicesList.push_back(info);
        }
    }