/*---------------------------------------------------------------------------- ** ALSA_ComputeCaps ** ** Given an ALSA PCM, figure out our HW CAPS structure info. ** ctl can be null, pcm is required, as is all output parms. ** */ static int ALSA_ComputeCaps(snd_ctl_t *ctl, snd_pcm_t *pcm, WORD *channels, DWORD *flags, DWORD *formats, DWORD *supports) { snd_pcm_hw_params_t *hw_params; snd_pcm_format_mask_t *fmask; snd_pcm_access_mask_t *acmask; unsigned int ratemin = 0; unsigned int ratemax = 0; unsigned int chmin = 0; unsigned int chmax = 0; int rc, dir = 0; hw_params = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_hw_params_sizeof() ); fmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof() ); acmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_access_mask_sizeof() ); if ((rc = snd_pcm_hw_params_any(pcm, hw_params)) < 0) goto done; snd_pcm_hw_params_get_format_mask(hw_params, fmask); if ((rc = snd_pcm_hw_params_get_access_mask(hw_params, acmask)) < 0) goto done; if ((rc = snd_pcm_hw_params_get_rate_min(hw_params, &ratemin, &dir)) < 0) goto done; if ((rc = snd_pcm_hw_params_get_rate_max(hw_params, &ratemax, &dir)) < 0) goto done; if ((rc = snd_pcm_hw_params_get_channels_min(hw_params, &chmin)) < 0) goto done; if ((rc = snd_pcm_hw_params_get_channels_max(hw_params, &chmax)) < 0) goto done; #define X(r,v) \ if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \ { \ if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \ { \ if (chmin <= 1 && 1 <= chmax) \ *formats |= WAVE_FORMAT_##v##M08; \ if (chmin <= 2 && 2 <= chmax) \ *formats |= WAVE_FORMAT_##v##S08; \ } \ if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \ { \ if (chmin <= 1 && 1 <= chmax) \ *formats |= WAVE_FORMAT_##v##M16; \ if (chmin <= 2 && 2 <= chmax) \ *formats |= WAVE_FORMAT_##v##S16; \ } \ } X(11025,1); X(22050,2); X(44100,4); X(48000,48); X(96000,96); #undef X if (chmin > 1) FIXME("Device has a minimum of %d channels\n", chmin); *channels = chmax; /* FIXME: is sample accurate always true ? ** Can we do WAVECAPS_PITCH, WAVECAPS_SYNC, or WAVECAPS_PLAYBACKRATE? */ *supports |= WAVECAPS_SAMPLEACCURATE; *supports |= WAVECAPS_DIRECTSOUND; /* check for volume control support */ if (ctl) { if (snd_ctl_name(ctl)) { snd_hctl_t *hctl; if (snd_hctl_open(&hctl, snd_ctl_name(ctl), 0) >= 0) { snd_hctl_load(hctl); if (!ALSA_CheckSetVolume( hctl, NULL, NULL, NULL, NULL, NULL, NULL, NULL )) { *supports |= WAVECAPS_VOLUME; if (chmin <= 2 && 2 <= chmax) *supports |= WAVECAPS_LRVOLUME; } snd_hctl_free(hctl); snd_hctl_close(hctl); } } } *flags = DSCAPS_CERTIFIED | DSCAPS_CONTINUOUSRATE; *flags |= DSCAPS_SECONDARYMONO | DSCAPS_SECONDARYSTEREO; *flags |= DSCAPS_SECONDARY8BIT | DSCAPS_SECONDARY16BIT; if (*formats & (WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_48M08 | WAVE_FORMAT_96M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_4M16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_96M16) ) *flags |= DSCAPS_PRIMARYMONO; if (*formats & (WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_96S08 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96S16) ) *flags |= DSCAPS_PRIMARYSTEREO; if (*formats & (WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_48M08 | WAVE_FORMAT_96M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_96S08) ) *flags |= DSCAPS_PRIMARY8BIT; if (*formats & (WAVE_FORMAT_1M16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_4M16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96S16) ) *flags |= DSCAPS_PRIMARY16BIT; rc = 0; done: if (rc < 0) ERR("failed: %s(%d)\n", snd_strerror(rc), rc); HeapFree( GetProcessHeap(), 0, hw_params ); HeapFree( GetProcessHeap(), 0, fmask ); HeapFree( GetProcessHeap(), 0, acmask ); return rc; }
/* Init the ALSA library: - Open the configured device, if none, use default device. - Search for the routing control - Search for the ringing and incall routes values */ static int utils_alsa_init() { if(utils_alsa_hctl) return 0; debug("utils_alsa_init: start\n"); int i; for(i=0;i<UTILS_AUDIO_ROUTE_COUNT;i++)utils_alsa_routes[i]=-1; char *alsa_control=utils_conf_get_string(UTILS_CONF_GROUP_ACTION_AUDIO,UTILS_CONF_ATTR_ACTION_AUDIO_ALSA_ROUTE_CONTROL_NAME); char *alsa_route_play=utils_conf_get_string(UTILS_CONF_GROUP_ACTION_AUDIO,UTILS_CONF_ATTR_ACTION_AUDIO_ALSA_ROUTE_CONTROL_RINGING); char *alsa_route_incall=utils_conf_get_string(UTILS_CONF_GROUP_ACTION_AUDIO,UTILS_CONF_ATTR_ACTION_AUDIO_ALSA_ROUTE_CONTROL_INCALL); char *alsa_route_speaker=utils_conf_get_string(UTILS_CONF_GROUP_ACTION_AUDIO,UTILS_CONF_ATTR_ACTION_AUDIO_ALSA_ROUTE_CONTROL_SPEAKER); char *alsa_route_handset=utils_conf_get_string(UTILS_CONF_GROUP_ACTION_AUDIO,UTILS_CONF_ATTR_ACTION_AUDIO_ALSA_ROUTE_CONTROL_HANDSET); if(!alsa_control) return 0; char *alsa_device=utils_conf_get_string(UTILS_CONF_GROUP_ACTION_AUDIO,UTILS_CONF_ATTR_ACTION_AUDIO_ALSA_ROUTE_DEVICE); int ret=0; if(alsa_device) ret = snd_hctl_open(&utils_alsa_hctl, alsa_device, 0); else ret = snd_hctl_open(&utils_alsa_hctl, "default", 0); if(ret){ error("utils_alsa_init: Alsa control device open failed: %s\n", snd_strerror(ret)); goto done; } ret = snd_hctl_load(utils_alsa_hctl); if(ret){ error("utils_alsa_init: Alsa control device load failed\n"); goto done; } snd_hctl_elem_t *elem=snd_hctl_first_elem(utils_alsa_hctl); snd_ctl_elem_info_t *info; snd_ctl_elem_info_alloca(&info); while(elem){ const char *name=snd_hctl_elem_get_name(elem); ret=snd_hctl_elem_info(elem, info); if(ret){ error("utils_alsa_init: snd_hctl_elem_info for ctrl %s error %s\n", name, snd_strerror(ret)); continue; } if(!strcmp(name, alsa_control)){ debug("utils_alsa_init: Found element %s\n", name); // Find the values for(i=0; i<snd_ctl_elem_info_get_items(info); i++){ snd_ctl_elem_info_set_item(info, i); snd_hctl_elem_info(elem, info); const char *s=snd_ctl_elem_info_get_item_name(info); debug("utils_alsa_init: check control value %s:%s\n", name, s); if(alsa_route_play && !strcmp(alsa_route_play, s)){ debug("utils_alsa_init: utils_alsa_route_play=%ud\n", i); utils_alsa_route_play=i; } if(alsa_route_incall && !strcmp(alsa_route_incall, s)){ debug("utils_alsa_init: utils_alsa_route_incall=%ud\n", i); utils_alsa_route_incall=i; } if(alsa_route_speaker && !strcmp(alsa_route_speaker, s)){ debug("utils_alsa_init: alsa_route_speaker=%ud\n", i); utils_alsa_routes[UTILS_AUDIO_ROUTE_SPEAKER]=i; } if(alsa_route_handset && !strcmp(alsa_route_handset, s)){ debug("utils_alsa_init: alsa_route_handset=%ud\n", i); utils_alsa_routes[UTILS_AUDIO_ROUTE_HANDSET]=i; } } snd_ctl_elem_id_malloc(&utils_alsa_route_elem_id); snd_ctl_elem_info_get_id(info, utils_alsa_route_elem_id); utils_alsa_route_elem=elem; } elem=snd_hctl_elem_next(elem); } if(utils_alsa_route_play==-1) utils_alsa_route_play=utils_alsa_routes[UTILS_AUDIO_ROUTE_SPEAKER]; if(utils_alsa_route_incall==-1) utils_alsa_route_incall=utils_alsa_routes[UTILS_AUDIO_ROUTE_HANDSET]; debug("utils_alsa_init: ok\n"); done: g_free(alsa_device); return ret; }
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 CAESinkALSA::EnumerateDevice(AEDeviceInfoList &list, const std::string &device, const std::string &description, snd_config_t *config) { snd_pcm_t *pcmhandle = NULL; if (!OpenPCMDevice(device, "", ALSA_MAX_CHANNELS, &pcmhandle, config)) return; snd_pcm_info_t *pcminfo; snd_pcm_info_alloca(&pcminfo); memset(pcminfo, 0, snd_pcm_info_sizeof()); int err = snd_pcm_info(pcmhandle, pcminfo); if (err < 0) { CLog::Log(LOGINFO, "CAESinkALSA - Unable to get pcm_info for \"%s\"", device.c_str()); snd_pcm_close(pcmhandle); } int cardNr = snd_pcm_info_get_card(pcminfo); CAEDeviceInfo info; info.m_deviceName = device; info.m_deviceType = AEDeviceTypeFromName(device); if (cardNr >= 0) { /* "HDA NVidia", "HDA Intel", "HDA ATI HDMI", "SB Live! 24-bit External", ... */ char *cardName; if (snd_card_get_name(cardNr, &cardName) == 0) info.m_displayName = cardName; if (info.m_deviceType == AE_DEVTYPE_HDMI && info.m_displayName.size() > 5 && info.m_displayName.substr(info.m_displayName.size()-5) == " HDMI") { /* We already know this is HDMI, strip it */ info.m_displayName.erase(info.m_displayName.size()-5); } /* "CONEXANT Analog", "USB Audio", "HDMI 0", "ALC889 Digital" ... */ std::string pcminfoName = snd_pcm_info_get_name(pcminfo); /* * Filter "USB Audio", in those cases snd_card_get_name() is more * meaningful already */ if (pcminfoName != "USB Audio") info.m_displayNameExtra = pcminfoName; if (info.m_deviceType == AE_DEVTYPE_HDMI) { /* replace, this was likely "HDMI 0" */ info.m_displayNameExtra = "HDMI"; int dev = snd_pcm_info_get_device(pcminfo); if (dev >= 0) { /* lets see if we can get ELD info */ snd_ctl_t *ctlhandle; std::stringstream sstr; sstr << "hw:" << cardNr; std::string strHwName = sstr.str(); if (snd_ctl_open_lconf(&ctlhandle, strHwName.c_str(), 0, config) == 0) { snd_hctl_t *hctl; if (snd_hctl_open_ctl(&hctl, ctlhandle) == 0) { snd_hctl_load(hctl); bool badHDMI = false; if (!GetELD(hctl, dev, info, badHDMI)) CLog::Log(LOGDEBUG, "CAESinkALSA - Unable to obtain ELD information for device \"%s\" (not supported by device, or kernel older than 3.2)", device.c_str()); /* snd_hctl_close also closes ctlhandle */ snd_hctl_close(hctl); // regarding data formats we don't trust ELD // push all passthrough formats to the list AEDataFormatList::iterator it; for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1)) { if (!AE_IS_RAW(i)) continue; it = find(info.m_dataFormats.begin(), info.m_dataFormats.end(), i); if (it == info.m_dataFormats.end()) info.m_dataFormats.push_back(i); } if (badHDMI) { /* * Warn about disconnected devices, but keep them enabled * Detection can go wrong on Intel, Nvidia and on all * AMD (fglrx) hardware, so it is not safe to close those * handles */ CLog::Log(LOGDEBUG, "CAESinkALSA - HDMI device \"%s\" may be unconnected (no ELD data)", device.c_str()); } } else { snd_ctl_close(ctlhandle); } } } } else if (info.m_deviceType == AE_DEVTYPE_IEC958) { /* append instead of replace, pcminfoName is useful for S/PDIF */ if (!info.m_displayNameExtra.empty()) info.m_displayNameExtra += ' '; info.m_displayNameExtra += "S/PDIF"; info.m_dataFormats.push_back(AE_FMT_AC3); info.m_dataFormats.push_back(AE_FMT_DTS); } else if (info.m_displayNameExtra.empty()) { /* for USB audio, it gets a bit confusing as there is * - "SB Live! 24-bit External" * - "SB Live! 24-bit External, S/PDIF" * so add "Analog" qualifier to the first one */ info.m_displayNameExtra = "Analog"; } /* "default" is a device that will be used for all inputs, while * "@" will be mangled to front/default/surroundXX as necessary */ if (device == "@" || device == "default") { /* Make it "Default (whatever)" */ info.m_displayName = "Default (" + info.m_displayName + (info.m_displayNameExtra.empty() ? "" : " " + info.m_displayNameExtra + ")"); info.m_displayNameExtra = ""; } } else { /* virtual devices: "default", "pulse", ... */ /* description can be e.g. "PulseAudio Sound Server" - for hw devices it is * normally uninteresting, like "HDMI Audio Output" or "Default Audio Device", * so we only use it for virtual devices that have no better display name */ info.m_displayName = description; } snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_alloca(&hwparams); memset(hwparams, 0, snd_pcm_hw_params_sizeof()); /* ensure we can get a playback configuration for the device */ if (snd_pcm_hw_params_any(pcmhandle, hwparams) < 0) { CLog::Log(LOGINFO, "CAESinkALSA - No playback configurations available for device \"%s\"", device.c_str()); snd_pcm_close(pcmhandle); return; } /* 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 = ALSA_MAX_CHANNELS; i >= 1; --i) { /* Reopen the device if needed on the special "surroundXX" cases */ if (info.m_deviceType == AE_DEVTYPE_PCM && (i == 8 || i == 6 || i == 4)) OpenPCMDevice(device, "", i, &pcmhandle, config); if (snd_pcm_hw_params_test_channels(pcmhandle, hwparams, i) >= 0) { channels = i; break; } } if (device == "default" && channels == 2) { /* This looks like the ALSA standard default stereo dmix device, we * probably want to use "@" instead to get surroundXX. */ snd_pcm_close(pcmhandle); EnumerateDevice(list, "@", description, config); return; } 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); }
int main() { int cardNumber = -1; int errNum; char *controlType; char deviceID[16]; // ------------------------------------------------------------------------ // ALSA control elements. // ------------------------------------------------------------------------ snd_ctl_t *ctl; // Simple control handle. snd_ctl_elem_id_t *id; // Simple control element id. snd_ctl_elem_value_t *control; // Simple control element value. snd_ctl_elem_type_t type; // Simple control element type. snd_ctl_elem_info_t *info; // Simple control element info container. snd_ctl_card_info_t *card; // Simple control card info container. snd_hctl_t *hctl; // High level control handle; snd_hctl_elem_t *elem; // High level control element handle. // ------------------------------------------------------------------------ // Initialise ALSA card and device types. // ------------------------------------------------------------------------ snd_ctl_card_info_alloca( &card ); snd_ctl_elem_value_alloca( &control ); snd_ctl_elem_id_alloca( &id ); snd_ctl_elem_info_alloca( &info ); // ------------------------------------------------------------------------ // Start card section. // ------------------------------------------------------------------------ // For each card. while (1) { // Find next card number. If < 0 then returns 1st card. errNum = snd_card_next( &cardNumber ); if (( errNum < 0 ) || ( cardNumber < 0 )) break; // Concatenate strings to get card's control interface. sprintf( deviceID, "hw:%i", cardNumber ); // Try to open card. if ( snd_ctl_open( &ctl, deviceID, 0 ) < 0 ) { printf( "Error opening card.\n" ); continue; } // Fill control card info element. if ( snd_ctl_card_info( ctl, card ) < 0 ) { printf( "Error getting card info.\n" ); continue; } // -------------------------------------------------------------------- // Print header block. // -------------------------------------------------------------------- printf( "\t+-----------------------------" ); printf( "-----------------------------+\n" ); printf( "\t| Card: %d - %-46s |", cardNumber, snd_ctl_card_info_get_name( card )); printf( "\n" ); printf( "\t+--------+------------" ); printf( "+------------------------------------+\n" ); printf( "\t| Device | Type " ); printf( "| Name |\n" ); printf( "\t+--------+------------" ); printf( "+------------------------------------+\n" ); // -------------------------------------------------------------------- // Start control section. // -------------------------------------------------------------------- // Open an empty high level control. if ( snd_hctl_open( &hctl, deviceID, 0 ) < 0 ) printf( "Error opening high level control.\n" ); // Load high level control element. if ( snd_hctl_load( hctl ) < 0 ) printf( "Error loading high level control.\n" ); // -------------------------------------------------------------------- // For each control element. // -------------------------------------------------------------------- for ( elem = snd_hctl_first_elem( hctl ); elem; elem = snd_hctl_elem_next( elem )) { // Get ID of high level control element. snd_hctl_elem_get_id( elem, id ); // ---------------------------------------------------------------- // Determine control type. // ---------------------------------------------------------------- if ( snd_hctl_elem_info( elem, info ) < 0 ) printf( "Can't get control information.\n" ); type = snd_ctl_elem_info_get_type( info ); switch ( type ) { case SND_CTL_ELEM_TYPE_NONE: controlType = "None"; break; case SND_CTL_ELEM_TYPE_BOOLEAN: controlType = "Boolean"; break; case SND_CTL_ELEM_TYPE_INTEGER: controlType = "Integer"; break; case SND_CTL_ELEM_TYPE_INTEGER64: controlType = "Integer64"; break; case SND_CTL_ELEM_TYPE_ENUMERATED: controlType = "Enumerated"; break; case SND_CTL_ELEM_TYPE_BYTES: controlType = "Bytes"; break; case SND_CTL_ELEM_TYPE_IEC958: controlType = "IEC958"; break; default: controlType = "Not Found"; break; } printf( "\t| %-6i | %-10s | %-34s |\n", snd_hctl_elem_get_numid( elem ), controlType, snd_hctl_elem_get_name( elem )); } printf( "\t+--------+------------" ); printf( "+------------------------------------+\n\n" ); // -------------------------------------------------------------------- // Tidy up. // -------------------------------------------------------------------- snd_hctl_close( hctl ); snd_ctl_close( ctl ); } return 0; }
/************************************************************************** * wodOpen [internal] */ static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags) { WINE_WAVEDEV* wwo; snd_pcm_t * pcm = NULL; snd_hctl_t * hctl = NULL; snd_pcm_hw_params_t * hw_params = NULL; snd_pcm_sw_params_t * sw_params; snd_pcm_access_t access; snd_pcm_format_t format = -1; unsigned int rate; unsigned int buffer_time = 120000; unsigned int period_time = 22000; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; int flags; int err=0; int dir=0; DWORD retcode = 0; TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags); if (lpDesc == NULL) { WARN("Invalid Parameter !\n"); return MMSYSERR_INVALPARAM; } if (wDevID >= ALSA_WodNumDevs) { TRACE("Asked for device %d, but only %d known!\n", wDevID, ALSA_WodNumDevs); return MMSYSERR_BADDEVICEID; } /* only PCM format is supported so far... */ if (!ALSA_supportedFormat(lpDesc->lpFormat)) { WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n", lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, lpDesc->lpFormat->nSamplesPerSec); return WAVERR_BADFORMAT; } if (dwFlags & WAVE_FORMAT_QUERY) { TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n", lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels, lpDesc->lpFormat->nSamplesPerSec); return MMSYSERR_NOERROR; } wwo = &WOutDev[wDevID]; if (wwo->pcm != NULL) { WARN("%d already allocated\n", wDevID); return MMSYSERR_ALLOCATED; } if (dwFlags & WAVE_DIRECTSOUND) FIXME("Why are we called with DirectSound flag? It doesn't use MMSYSTEM any more\n"); /* not supported, ignore it */ dwFlags &= ~WAVE_DIRECTSOUND; flags = SND_PCM_NONBLOCK; if ( (err = snd_pcm_open(&pcm, wwo->pcmname, SND_PCM_STREAM_PLAYBACK, flags)) < 0) { ERR("Error open: %s\n", snd_strerror(err)); return MMSYSERR_NOTENABLED; } if (wwo->ctlname) { err = snd_hctl_open(&hctl, wwo->ctlname, 0); if (err >= 0) { snd_hctl_load(hctl); } else { WARN("Could not open hctl for [%s]: %s\n", wwo->ctlname, snd_strerror(err)); hctl = NULL; } } wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK); wwo->waveDesc = *lpDesc; ALSA_copyFormat(lpDesc->lpFormat, &wwo->format); TRACE("Requested this format: %dx%dx%d %s\n", wwo->format.Format.nSamplesPerSec, wwo->format.Format.wBitsPerSample, wwo->format.Format.nChannels, ALSA_getFormat(wwo->format.Format.wFormatTag)); if (wwo->format.Format.wBitsPerSample == 0) { WARN("Resetting zeroed wBitsPerSample\n"); wwo->format.Format.wBitsPerSample = 8 * (wwo->format.Format.nAvgBytesPerSec / wwo->format.Format.nSamplesPerSec) / wwo->format.Format.nChannels; } #define EXIT_ON_ERROR(f,e,txt) do \ { \ int err; \ if ( (err = (f) ) < 0) \ { \ WARN(txt ": %s\n", snd_strerror(err)); \ retcode=e; \ goto errexit; \ } \ } while(0) sw_params = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof() ); snd_pcm_hw_params_malloc(&hw_params); if (! hw_params) { retcode = MMSYSERR_NOMEM; goto errexit; } snd_pcm_hw_params_any(pcm, hw_params); access = SND_PCM_ACCESS_MMAP_INTERLEAVED; if ( ( err = snd_pcm_hw_params_set_access(pcm, hw_params, access ) ) < 0) { WARN("mmap not available. switching to standard write.\n"); access = SND_PCM_ACCESS_RW_INTERLEAVED; EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm, hw_params, access ), MMSYSERR_INVALPARAM, "unable to set access for playback"); wwo->write = snd_pcm_writei; } else wwo->write = snd_pcm_mmap_writei; if ((err = snd_pcm_hw_params_set_channels(pcm, hw_params, wwo->format.Format.nChannels)) < 0) { WARN("unable to set required channels: %d\n", wwo->format.Format.nChannels); EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm, hw_params, wwo->format.Format.nChannels ), WAVERR_BADFORMAT, "unable to set required channels" ); } if ((wwo->format.Format.wFormatTag == WAVE_FORMAT_PCM) || ((wwo->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) && IsEqualGUID(&wwo->format.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) { format = (wwo->format.Format.wBitsPerSample == 8) ? SND_PCM_FORMAT_U8 : (wwo->format.Format.wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE : (wwo->format.Format.wBitsPerSample == 24) ? SND_PCM_FORMAT_S24_3LE : (wwo->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_S32_LE : -1; } else if ((wwo->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) && IsEqualGUID(&wwo->format.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)){ format = (wwo->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_FLOAT_LE : -1; } else if (wwo->format.Format.wFormatTag == WAVE_FORMAT_MULAW) { FIXME("unimplemented format: WAVE_FORMAT_MULAW\n"); retcode = WAVERR_BADFORMAT; goto errexit; } else if (wwo->format.Format.wFormatTag == WAVE_FORMAT_ALAW) { FIXME("unimplemented format: WAVE_FORMAT_ALAW\n"); retcode = WAVERR_BADFORMAT; goto errexit; } else if (wwo->format.Format.wFormatTag == WAVE_FORMAT_ADPCM) { FIXME("unimplemented format: WAVE_FORMAT_ADPCM\n"); retcode = WAVERR_BADFORMAT; goto errexit; } else { ERR("invalid format: %0x04x\n", wwo->format.Format.wFormatTag); retcode = WAVERR_BADFORMAT; goto errexit; } if ((err = snd_pcm_hw_params_set_format(pcm, hw_params, format)) < 0) { WARN("unable to set required format: %s\n", snd_pcm_format_name(format)); EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm, hw_params, format), WAVERR_BADFORMAT, "unable to set required format" ); } rate = wwo->format.Format.nSamplesPerSec; dir=0; err = snd_pcm_hw_params_set_rate_near(pcm, hw_params, &rate, &dir); if (err < 0) { WARN("Rate %d Hz not available for playback: %s\n", wwo->format.Format.nSamplesPerSec, snd_strerror(rate)); retcode = WAVERR_BADFORMAT; goto errexit; } if (!ALSA_NearMatch(rate, wwo->format.Format.nSamplesPerSec)) { WARN("Rate doesn't match (requested %d Hz, got %d Hz)\n", wwo->format.Format.nSamplesPerSec, rate); retcode = WAVERR_BADFORMAT; goto errexit; } TRACE("Got this format: %dx%dx%d %s\n", wwo->format.Format.nSamplesPerSec, wwo->format.Format.wBitsPerSample, wwo->format.Format.nChannels, ALSA_getFormat(wwo->format.Format.wFormatTag)); dir=0; EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_time, &dir), MMSYSERR_INVALPARAM, "unable to set buffer time"); dir=0; EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm, hw_params, &period_time, &dir), MMSYSERR_INVALPARAM, "unable to set period time"); EXIT_ON_ERROR( snd_pcm_hw_params(pcm, hw_params), MMSYSERR_INVALPARAM, "unable to set hw params for playback"); err = snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir); err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size); snd_pcm_sw_params_current(pcm, sw_params); EXIT_ON_ERROR( snd_pcm_sw_params_set_start_threshold(pcm, sw_params, 1), MMSYSERR_ERROR, "unable to set start threshold"); EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence size"); EXIT_ON_ERROR( snd_pcm_sw_params_set_avail_min(pcm, sw_params, period_size), MMSYSERR_ERROR, "unable to set avail min"); EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_threshold(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence threshold"); EXIT_ON_ERROR( snd_pcm_sw_params(pcm, sw_params), MMSYSERR_ERROR, "unable to set sw params for playback"); #undef EXIT_ON_ERROR snd_pcm_prepare(pcm); if (TRACE_ON(wave)) ALSA_TraceParameters(hw_params, sw_params, FALSE); /* now, we can save all required data for later use... */ wwo->dwBufferSize = snd_pcm_frames_to_bytes(pcm, buffer_size); wwo->lpQueuePtr = wwo->lpPlayPtr = wwo->lpLoopPtr = NULL; wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0; wwo->dwPartialOffset = 0; ALSA_InitRingMessage(&wwo->msgRing); wwo->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL); wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD_PTR)wDevID, 0, &(wwo->dwThreadID)); if (wwo->hThread) SetThreadPriority(wwo->hThread, THREAD_PRIORITY_TIME_CRITICAL); else { ERR("Thread creation for the wodPlayer failed!\n"); CloseHandle(wwo->hStartUpEvent); retcode = MMSYSERR_NOMEM; goto errexit; } WaitForSingleObject(wwo->hStartUpEvent, INFINITE); CloseHandle(wwo->hStartUpEvent); wwo->hStartUpEvent = INVALID_HANDLE_VALUE; TRACE("handle=%p\n", pcm); TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%u, nSamplesPerSec=%u, nChannels=%u nBlockAlign=%u!\n", wwo->format.Format.wBitsPerSample, wwo->format.Format.nAvgBytesPerSec, wwo->format.Format.nSamplesPerSec, wwo->format.Format.nChannels, wwo->format.Format.nBlockAlign); HeapFree( GetProcessHeap(), 0, sw_params ); wwo->pcm = pcm; wwo->hctl = hctl; if ( wwo->hw_params ) snd_pcm_hw_params_free(wwo->hw_params); wwo->hw_params = hw_params; return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L); errexit: if (pcm) snd_pcm_close(pcm); if (hctl) { snd_hctl_free(hctl); snd_hctl_close(hctl); } if ( hw_params ) snd_pcm_hw_params_free(hw_params); HeapFree( GetProcessHeap(), 0, sw_params ); if (wwo->msgRing.ring_buffer_size > 0) ALSA_DestroyRingMessage(&wwo->msgRing); return retcode; }
int phoneui_utils_sound_init_finish(GKeyFile *keyfile,char* suffix) { int err, f; char *device_name; char *alsa; static GSourceFuncs funcs = { _sourcefunc_prepare, _sourcefunc_check, _sourcefunc_dispatch, 0, 0, 0 }; alsa = malloc(strlen(suffix) + strlen("alsa") + 1); if (alsa) { strcpy(alsa, "alsa"); strcat(alsa, suffix); }else{ alsa = strdup("alsa"); g_warning("Malloc failure in %s at line %d",__func__,__LINE__); } sound_state = SOUND_STATE_IDLE; sound_state_type = SOUND_STATE_TYPE_DEFAULT; device_name = g_key_file_get_string(keyfile, alsa, "hardware_control_name", NULL); if (!device_name) { g_message("No hw control found, using default"); device_name = strdup("hw:0"); } if (hctl) { snd_hctl_close(hctl); } err = snd_hctl_open(&hctl, device_name, 0); if (err) { g_warning("Cannot open alsa:hardware_control_name '%s': %s", device_name, snd_strerror(err)); g_key_file_free(keyfile); return err; } err = snd_hctl_load(hctl); if (err) { g_warning("Cannot load alsa:hardware_control_name '%s': %s", device_name, snd_strerror(err)); } free(device_name); /*FIXME: add idle bt */ _phoneui_utils_sound_init_set_control(keyfile, "idle", suffix, SOUND_STATE_IDLE, SOUND_STATE_TYPE_HANDSET); _phoneui_utils_sound_init_set_control(keyfile, "bluetooth", suffix, SOUND_STATE_CALL, SOUND_STATE_TYPE_BLUETOOTH); _phoneui_utils_sound_init_set_control(keyfile, "handset", suffix, SOUND_STATE_CALL, SOUND_STATE_TYPE_HANDSET); _phoneui_utils_sound_init_set_control(keyfile, "headset", suffix, SOUND_STATE_CALL, SOUND_STATE_TYPE_HEADSET); _phoneui_utils_sound_init_set_control(keyfile, "speaker", suffix, SOUND_STATE_SPEAKER, SOUND_STATE_TYPE_HANDSET); snd_hctl_nonblock(hctl, 1); poll_fd_count = snd_hctl_poll_descriptors_count(hctl); poll_fds = malloc(sizeof(struct pollfd) * poll_fd_count); snd_hctl_poll_descriptors(hctl, poll_fds, poll_fd_count); source_alsa_poll = g_source_new(&funcs, sizeof(GSource)); for (f = 0; f < poll_fd_count; f++) { g_source_add_poll(source_alsa_poll, (GPollFD *)&poll_fds[f]); } g_source_attach(source_alsa_poll, NULL); /*Register for HEADPHONE insertion */ phoneui_info_register_input_events(_input_events_cb, NULL); fso_audio = (FreeSmartphoneDeviceAudio *)_fso (FREE_SMARTPHONE_DEVICE_TYPE_AUDIO_PROXY, FSO_FRAMEWORK_DEVICE_ServiceDBusName, FSO_FRAMEWORK_DEVICE_AudioServicePath, FSO_FRAMEWORK_DEVICE_AudioServiceFace); g_key_file_free(keyfile); return err; }