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 }
CAESinkALSA::CAESinkALSA() : m_pcm(NULL) { /* ensure that ALSA has been initialized */ if (!snd_config) snd_config_update(); }
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(); }
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 }
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; }
/*---------------------------------------------------------------------------- ** 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; }
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); }
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; } } } }
/* 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; }
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)); } }
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); } }
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); } }