static HRESULT WINAPI IDsCaptureDriverImpl_CreateCaptureBuffer(PIDSCDRIVER iface, LPWAVEFORMATEX pwfx, DWORD dwFlags, DWORD dwCardAddress, LPDWORD pdwcbBufferSize, LPBYTE *ppbBuffer, LPVOID *ppvObj) { IDsCaptureDriverImpl *This = (IDsCaptureDriverImpl *)iface; IDsCaptureDriverBufferImpl** ippdsdb = (IDsCaptureDriverBufferImpl**)ppvObj; HRESULT err; TRACE("(%p,%p,%x,%x)\n",iface,pwfx,dwFlags,dwCardAddress); if (This->capture_buffer) return DSERR_ALLOCATED; This->capture_buffer = *ippdsdb = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDsCaptureDriverBufferImpl)); if (*ippdsdb == NULL) return DSERR_OUTOFMEMORY; (*ippdsdb)->hw_params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_hw_params_sizeof()); (*ippdsdb)->sw_params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof()); (*ippdsdb)->presented_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *pdwcbBufferSize); if (!(*ippdsdb)->hw_params || !(*ippdsdb)->sw_params || !(*ippdsdb)->presented_buffer) { HeapFree(GetProcessHeap(), 0, (*ippdsdb)->sw_params); HeapFree(GetProcessHeap(), 0, (*ippdsdb)->hw_params); HeapFree(GetProcessHeap(), 0, (*ippdsdb)->presented_buffer); return DSERR_OUTOFMEMORY; } (*ippdsdb)->lpVtbl = &dsdbvt; (*ippdsdb)->ref = 1; (*ippdsdb)->drv = This; (*ippdsdb)->mmap_buflen_bytes = *pdwcbBufferSize; InitializeCriticalSection(&(*ippdsdb)->pcm_crst); (*ippdsdb)->pcm_crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ALSA_DSCAPTURE.pcm_crst"); /* SetFormat initialises pcm */ err = IDsDriverBuffer_SetFormat((IDsDriverBuffer*)*ppvObj, pwfx); if (FAILED(err)) { WARN("Error occurred: %08x\n", err); goto err; } *ppbBuffer = (*ippdsdb)->presented_buffer; /* buffer is ready to go */ TRACE("buffer created at %p\n", *ippdsdb); return err; err: HeapFree(GetProcessHeap(), 0, (*ippdsdb)->presented_buffer); HeapFree(GetProcessHeap(), 0, (*ippdsdb)->sw_params); HeapFree(GetProcessHeap(), 0, (*ippdsdb)->hw_params); HeapFree(GetProcessHeap(), 0, *ippdsdb); *ippdsdb = NULL; return err; }
bool test_open(const char *device, unsigned rates[]) { int err; snd_pcm_t *pcm; snd_pcm_hw_params_t *hw_params; hw_params = (snd_pcm_hw_params_t *) alloca(snd_pcm_hw_params_sizeof()); memset(hw_params, 0, snd_pcm_hw_params_sizeof()); // open device if ((err = snd_pcm_open(&pcm, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { LOG_ERROR("playback open error: %s", snd_strerror(err)); return false; } // get max params if ((err = snd_pcm_hw_params_any(pcm, hw_params)) < 0) { LOG_ERROR("hwparam init error: %s", snd_strerror(err)); return false; } // find supported sample rates to enable client side resampling of non supported rates unsigned i, ind; unsigned ref[] TEST_RATES; for (i = 0, ind = 0; ref[i]; ++i) { if (snd_pcm_hw_params_test_rate(pcm, hw_params, ref[i], 0) == 0) { rates[ind++] = ref[i]; } } if ((err = snd_pcm_close(pcm)) < 0) { LOG_ERROR("snd_pcm_close error: %s", snd_strerror(err)); return false; } return true; }
static HRESULT WINAPI IDsCaptureDriverImpl_Open(PIDSCDRIVER iface) { HRESULT hr = S_OK; IDsCaptureDriverImpl *This = (IDsCaptureDriverImpl *)iface; int err=0; snd_pcm_t *pcm = NULL; snd_pcm_hw_params_t *hw_params; /* While this is not really needed, it is a good idea to do this, * to see if sound can be initialized */ hw_params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_hw_params_sizeof()); if (!hw_params) { hr = DSERR_OUTOFMEMORY; WARN("--> %08x\n", hr); return hr; } err = snd_pcm_open(&pcm, WInDev[This->wDevID].pcmname, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); if (err < 0) goto err; err = snd_pcm_hw_params_any(pcm, hw_params); if (err < 0) goto err; err = snd_pcm_hw_params_set_access (pcm, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED); if (err < 0) { err = snd_pcm_hw_params_set_access (pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) goto err; } TRACE("Success\n"); snd_pcm_close(pcm); HeapFree(GetProcessHeap(), 0, hw_params); return hr; err: hr = DSERR_GENERIC; WARN("Failed to open device: %s\n", snd_strerror(err)); if (pcm) snd_pcm_close(pcm); HeapFree(GetProcessHeap(), 0, hw_params); WARN("--> %08x\n", hr); return hr; }
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); 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"; } 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); }
bool CAESinkALSA::InitializeHW(AEAudioFormat &format) { snd_pcm_hw_params_t *hw_params; snd_pcm_hw_params_alloca(&hw_params); memset(hw_params, 0, snd_pcm_hw_params_sizeof()); snd_pcm_hw_params_any(m_pcm, hw_params); snd_pcm_hw_params_set_access(m_pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); unsigned int sampleRate = format.m_sampleRate; unsigned int channelCount = format.m_channelLayout.Count(); snd_pcm_hw_params_set_rate_near (m_pcm, hw_params, &sampleRate, NULL); snd_pcm_hw_params_set_channels_near(m_pcm, hw_params, &channelCount); /* ensure we opened X channels or more */ if (format.m_channelLayout.Count() > channelCount) { CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Unable to open the required number of channels"); } /* update the channelLayout to what we managed to open */ format.m_channelLayout.Reset(); for (unsigned int i = 0; i < channelCount; ++i) format.m_channelLayout += ALSAChannelMap[i]; snd_pcm_format_t fmt = AEFormatToALSAFormat(format.m_dataFormat); if (fmt == SND_PCM_FORMAT_UNKNOWN) { /* if we dont support the requested format, fallback to float */ format.m_dataFormat = AE_FMT_FLOAT; fmt = SND_PCM_FORMAT_FLOAT; } /* try the data format */ if (snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0) { /* if the chosen format is not supported, try each one in decending order */ CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Your hardware does not support %s, trying other formats", CAEUtil::DataFormatToStr(format.m_dataFormat)); 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; if (m_passthrough && i != AE_FMT_S16BE && i != AE_FMT_S16LE) continue; fmt = AEFormatToALSAFormat(i); if (fmt == SND_PCM_FORMAT_UNKNOWN || snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0) { fmt = SND_PCM_FORMAT_UNKNOWN; continue; } int fmtBits = CAEUtil::DataFormatToBits(i); int bits = snd_pcm_hw_params_get_sbits(hw_params); if (bits != fmtBits) { /* if we opened in 32bit and only have 24bits, pack into 24 */ if (fmtBits == 32 && bits == 24) i = AE_FMT_S24NE4; else continue; } /* record that the format fell back to X */ format.m_dataFormat = i; CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Using data format %s", CAEUtil::DataFormatToStr(format.m_dataFormat)); break; } /* if we failed to find a valid output format */ if (fmt == SND_PCM_FORMAT_UNKNOWN) { CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Unable to find a suitable output format"); return false; } } snd_pcm_uframes_t periodSize, bufferSize; snd_pcm_hw_params_get_buffer_size_max(hw_params, &bufferSize); snd_pcm_hw_params_get_period_size_max(hw_params, &periodSize, NULL); /* We want to make sure, that we have max 200 ms Buffer with a periodSize of approx 50 ms. Choosing a higher bufferSize will cause problems with menu sounds. Buffer will be increased after those are fixed. */ periodSize = std::min(periodSize, (snd_pcm_uframes_t) sampleRate / 20); bufferSize = std::min(bufferSize, (snd_pcm_uframes_t) sampleRate / 5); /* According to upstream we should set buffer size first - so make sure it is always at least 4x period size to not get underruns (some systems seem to have issues with only 2 periods) */ periodSize = std::min(periodSize, bufferSize / 4); CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Request: periodSize %lu, bufferSize %lu", periodSize, bufferSize); snd_pcm_hw_params_t *hw_params_copy; snd_pcm_hw_params_alloca(&hw_params_copy); snd_pcm_hw_params_copy(hw_params_copy, hw_params); // copy what we have and is already working // Make sure to not initialize too large to not cause underruns snd_pcm_uframes_t periodSizeMax = bufferSize / 3; if(snd_pcm_hw_params_set_period_size_max(m_pcm, hw_params_copy, &periodSizeMax, NULL) != 0) { snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Request: Failed to limit periodSize to %lu", periodSizeMax); } // first trying bufferSize, PeriodSize // for more info see here: // http://mailman.alsa-project.org/pipermail/alsa-devel/2009-September/021069.html // the last three tries are done as within pulseaudio // backup periodSize and bufferSize first. Restore them after every failed try snd_pcm_uframes_t periodSizeTemp, bufferSizeTemp; periodSizeTemp = periodSize; bufferSizeTemp = bufferSize; if (snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize) != 0 || snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL) != 0 || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { bufferSize = bufferSizeTemp; periodSize = periodSizeTemp; // retry with PeriodSize, bufferSize snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy if (snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL) != 0 || snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize) != 0 || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { // try only periodSize periodSize = periodSizeTemp; snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restore working copy if(snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL) != 0 || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { // try only BufferSize bufferSize = bufferSizeTemp; snd_pcm_hw_params_copy(hw_params_copy, hw_params); // restory working copy if (snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize) != 0 || snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { // set default that Alsa would choose CLog::Log(LOGWARNING, "CAESinkAlsa::IntializeHW - Using default alsa values - set failed"); if (snd_pcm_hw_params(m_pcm, hw_params) != 0) { CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Could not init a valid sink"); return false; } } } // reread values when alsa default was kept snd_pcm_get_params(m_pcm, &bufferSize, &periodSize); } } CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Got: periodSize %lu, bufferSize %lu", periodSize, bufferSize); /* set the format parameters */ format.m_sampleRate = sampleRate; format.m_frames = periodSize; format.m_frameSamples = periodSize * format.m_channelLayout.Count(); format.m_frameSize = snd_pcm_frames_to_bytes(m_pcm, 1); m_bufferSize = (unsigned int)bufferSize; m_timeout = std::ceil((double)(bufferSize * 1000) / (double)sampleRate); CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Setting timeout to %d ms", m_timeout); return true; }
static snd_pcm_t *alsa_open(char *dev, int rate, int channels) { snd_pcm_hw_params_t *hwp; snd_pcm_sw_params_t *swp; snd_pcm_t *h; int r; int dir; snd_pcm_uframes_t period_size_min; snd_pcm_uframes_t period_size_max; snd_pcm_uframes_t buffer_size_min; snd_pcm_uframes_t buffer_size_max; snd_pcm_uframes_t period_size; snd_pcm_uframes_t buffer_size; if ((r = snd_pcm_open(&h, dev, SND_PCM_STREAM_PLAYBACK, 0) < 0)) return NULL; hwp = alloca(snd_pcm_hw_params_sizeof()); memset(hwp, 0, snd_pcm_hw_params_sizeof()); snd_pcm_hw_params_any(h, hwp); snd_pcm_hw_params_set_access(h, hwp, SND_PCM_ACCESS_RW_INTERLEAVED); snd_pcm_hw_params_set_format(h, hwp, SND_PCM_FORMAT_S16_LE); snd_pcm_hw_params_set_rate(h, hwp, rate, 0); snd_pcm_hw_params_set_channels(h, hwp, channels); /* Configurue period */ dir = 0; snd_pcm_hw_params_get_period_size_min(hwp, &period_size_min, &dir); dir = 0; snd_pcm_hw_params_get_period_size_max(hwp, &period_size_max, &dir); period_size = 1024; dir = 0; r = snd_pcm_hw_params_set_period_size_near(h, hwp, &period_size, &dir); if (r < 0) { fprintf(stderr, "audio: Unable to set period size %lu (%s)\n", period_size, snd_strerror(r)); snd_pcm_close(h); return NULL; } dir = 0; r = snd_pcm_hw_params_get_period_size(hwp, &period_size, &dir); if (r < 0) { fprintf(stderr, "audio: Unable to get period size (%s)\n", snd_strerror(r)); snd_pcm_close(h); return NULL; } /* Configurue buffer size */ snd_pcm_hw_params_get_buffer_size_min(hwp, &buffer_size_min); snd_pcm_hw_params_get_buffer_size_max(hwp, &buffer_size_max); buffer_size = period_size * 4; dir = 0; r = snd_pcm_hw_params_set_buffer_size_near(h, hwp, &buffer_size); if (r < 0) { fprintf(stderr, "audio: Unable to set buffer size %lu (%s)\n", buffer_size, snd_strerror(r)); snd_pcm_close(h); return NULL; } r = snd_pcm_hw_params_get_buffer_size(hwp, &buffer_size); if (r < 0) { fprintf(stderr, "audio: Unable to get buffer size (%s)\n", snd_strerror(r)); snd_pcm_close(h); return NULL; } /* write the hw params */ r = snd_pcm_hw_params(h, hwp); if (r < 0) { fprintf(stderr, "audio: Unable to configure hardware parameters (%s)\n", snd_strerror(r)); snd_pcm_close(h); return NULL; } /* * Software parameters */ swp = alloca(snd_pcm_sw_params_sizeof()); memset(hwp, 0, snd_pcm_sw_params_sizeof()); snd_pcm_sw_params_current(h, swp); r = snd_pcm_sw_params_set_avail_min(h, swp, period_size); if (r < 0) { fprintf(stderr, "audio: Unable to configure wakeup threshold (%s)\n", snd_strerror(r)); snd_pcm_close(h); return NULL; } snd_pcm_sw_params_set_start_threshold(h, swp, 0); if (r < 0) { fprintf(stderr, "audio: Unable to configure start threshold (%s)\n", snd_strerror(r)); snd_pcm_close(h); return NULL; } r = snd_pcm_sw_params(h, swp); if (r < 0) { fprintf(stderr, "audio: Cannot set soft parameters (%s)\n", snd_strerror(r)); snd_pcm_close(h); return NULL; } r = snd_pcm_prepare(h); if (r < 0) { fprintf(stderr, "audio: Cannot prepare audio for playback (%s)\n", snd_strerror(r)); snd_pcm_close(h); return NULL; } return h; }
bool CAESinkALSA::InitializeHW(AEAudioFormat &format) { snd_pcm_hw_params_t *hw_params; snd_pcm_hw_params_alloca(&hw_params); memset(hw_params, 0, snd_pcm_hw_params_sizeof()); snd_pcm_hw_params_any(m_pcm, hw_params); snd_pcm_hw_params_set_access(m_pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); unsigned int sampleRate = format.m_sampleRate; unsigned int channelCount = format.m_channelLayout.Count(); snd_pcm_hw_params_set_rate_near (m_pcm, hw_params, &sampleRate, NULL); snd_pcm_hw_params_set_channels_near(m_pcm, hw_params, &channelCount); /* ensure we opened X channels or more */ if (format.m_channelLayout.Count() > channelCount) { CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Unable to open the required number of channels"); return false; } /* update the channelLayout to what we managed to open */ format.m_channelLayout.Reset(); for (unsigned int i = 0; i < channelCount; ++i) format.m_channelLayout += ALSAChannelMap[i]; snd_pcm_format_t fmt = AEFormatToALSAFormat(format.m_dataFormat); if (fmt == SND_PCM_FORMAT_UNKNOWN) { /* if we dont support the requested format, fallback to float */ format.m_dataFormat = AE_FMT_FLOAT; fmt = SND_PCM_FORMAT_FLOAT; } /* try the data format */ if (snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0) { /* if the chosen format is not supported, try each one in decending order */ CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Your hardware does not support %s, trying other formats", CAEUtil::DataFormatToStr(format.m_dataFormat)); 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; if (m_passthrough && i != AE_FMT_S16BE && i != AE_FMT_S16LE) continue; fmt = AEFormatToALSAFormat(i); if (fmt == SND_PCM_FORMAT_UNKNOWN || snd_pcm_hw_params_set_format(m_pcm, hw_params, fmt) < 0) { fmt = SND_PCM_FORMAT_UNKNOWN; continue; } int fmtBits = CAEUtil::DataFormatToBits(i); int bits = snd_pcm_hw_params_get_sbits(hw_params); if (bits != fmtBits) { /* if we opened in 32bit and only have 24bits, pack into 24 */ if (fmtBits == 32 && bits == 24) i = AE_FMT_S24NE4; else continue; } /* record that the format fell back to X */ format.m_dataFormat = i; CLog::Log(LOGINFO, "CAESinkALSA::InitializeHW - Using data format %s", CAEUtil::DataFormatToStr(format.m_dataFormat)); break; } /* if we failed to find a valid output format */ if (fmt == SND_PCM_FORMAT_UNKNOWN) { CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Unable to find a suitable output format"); return false; } } unsigned int periods; snd_pcm_uframes_t periodSize, bufferSize; snd_pcm_hw_params_get_buffer_size_max(hw_params, &bufferSize); bufferSize = std::min(bufferSize, (snd_pcm_uframes_t)8192); periodSize = bufferSize / ALSA_PERIODS; periods = ALSA_PERIODS; CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Request: periodSize %lu, periods %u, bufferSize %lu", periodSize, periods, bufferSize); /* work on a copy of the hw params */ snd_pcm_hw_params_t *hw_params_copy; snd_pcm_hw_params_alloca(&hw_params_copy); /* try to set the buffer size then the period size */ snd_pcm_hw_params_copy(hw_params_copy, hw_params); snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize); snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL); snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL); if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { /* try to set the period size then the buffer size */ snd_pcm_hw_params_copy(hw_params_copy, hw_params); snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL); snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize); snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL); if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { /* try to just set the buffer size */ snd_pcm_hw_params_copy(hw_params_copy, hw_params); snd_pcm_hw_params_set_buffer_size_near(m_pcm, hw_params_copy, &bufferSize); snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL); if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { /* try to just set the period size */ snd_pcm_hw_params_copy(hw_params_copy, hw_params); snd_pcm_hw_params_set_period_size_near(m_pcm, hw_params_copy, &periodSize, NULL); snd_pcm_hw_params_set_periods_near (m_pcm, hw_params_copy, &periods , NULL); if (snd_pcm_hw_params(m_pcm, hw_params_copy) != 0) { CLog::Log(LOGERROR, "CAESinkALSA::InitializeHW - Failed to set the parameters"); return false; } } } } snd_pcm_hw_params_get_period_size(hw_params_copy, &periodSize, NULL); snd_pcm_hw_params_get_buffer_size(hw_params_copy, &bufferSize); CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Got: periodSize %lu, periods %u, bufferSize %lu", periodSize, periods, bufferSize); /* set the format parameters */ format.m_sampleRate = sampleRate; format.m_frames = periodSize; format.m_frameSamples = periodSize * format.m_channelLayout.Count(); format.m_frameSize = snd_pcm_frames_to_bytes(m_pcm, 1); m_bufferSize = (unsigned int)bufferSize; m_timeout = std::ceil((double)(bufferSize * 1000) / (double)sampleRate); CLog::Log(LOGDEBUG, "CAESinkALSA::InitializeHW - Setting timeout to %d ms", m_timeout); return true; }
/************************************************************************** * widOpen [internal] */ static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags) { WINE_WAVEDEV* wwi; snd_pcm_hw_params_t * hw_params; snd_pcm_sw_params_t * sw_params; snd_pcm_access_t access; snd_pcm_format_t format; unsigned int rate; unsigned int buffer_time = 500000; unsigned int period_time = 10000; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; int flags; snd_pcm_t * pcm; int err; int dir; DWORD ret; /* JPW TODO - review this code */ TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags); if (lpDesc == NULL) { WARN("Invalid Parameter !\n"); return MMSYSERR_INVALPARAM; } if (wDevID >= ALSA_WidNumDevs) { TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs); 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; } wwi = &WInDev[wDevID]; if (wwi->pcm != NULL) { WARN("already allocated\n"); return MMSYSERR_ALLOCATED; } wwi->pcm = 0; flags = SND_PCM_NONBLOCK; if ( (err=snd_pcm_open(&pcm, wwi->pcmname, SND_PCM_STREAM_CAPTURE, flags)) < 0 ) { ERR("Error open: %s\n", snd_strerror(err)); return MMSYSERR_NOTENABLED; } wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK); wwi->waveDesc = *lpDesc; ALSA_copyFormat(lpDesc->lpFormat, &wwi->format); if (wwi->format.Format.wBitsPerSample == 0) { WARN("Resetting zeroed wBitsPerSample\n"); wwi->format.Format.wBitsPerSample = 8 * (wwi->format.Format.nAvgBytesPerSec / wwi->format.Format.nSamplesPerSec) / wwi->format.Format.nChannels; } hw_params = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_hw_params_sizeof() ); sw_params = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof() ); snd_pcm_hw_params_any(pcm, hw_params); #define EXIT_ON_ERROR(f,e,txt) do \ { \ int err; \ if ( (err = (f) ) < 0) \ { \ WARN(txt ": %s\n", snd_strerror(err)); \ ret = (e); \ goto error; \ } \ } while(0) 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"); wwi->read = snd_pcm_readi; } else wwi->read = snd_pcm_mmap_readi; EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm, hw_params, wwi->format.Format.nChannels), WAVERR_BADFORMAT, "unable to set required channels"); if ((wwi->format.Format.wFormatTag == WAVE_FORMAT_PCM) || ((wwi->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) && IsEqualGUID(&wwi->format.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) { format = (wwi->format.Format.wBitsPerSample == 8) ? SND_PCM_FORMAT_U8 : (wwi->format.Format.wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE : (wwi->format.Format.wBitsPerSample == 24) ? SND_PCM_FORMAT_S24_3LE : (wwi->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_S32_LE : -1; } else if ((wwi->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) && IsEqualGUID(&wwi->format.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)){ format = (wwi->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_FLOAT_LE : -1; } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_MULAW) { FIXME("unimplemented format: WAVE_FORMAT_MULAW\n"); ret = WAVERR_BADFORMAT; goto error; } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_ALAW) { FIXME("unimplemented format: WAVE_FORMAT_ALAW\n"); ret = WAVERR_BADFORMAT; goto error; } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_ADPCM) { FIXME("unimplemented format: WAVE_FORMAT_ADPCM\n"); ret = WAVERR_BADFORMAT; goto error; } else { ERR("invalid format: %0x04x\n", wwi->format.Format.wFormatTag); ret = WAVERR_BADFORMAT; goto error; } EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm, hw_params, format), WAVERR_BADFORMAT, "unable to set required format"); rate = wwi->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", wwi->format.Format.nSamplesPerSec, snd_strerror(rate)); ret = WAVERR_BADFORMAT; goto error; } if (!ALSA_NearMatch(rate, wwi->format.Format.nSamplesPerSec)) { WARN("Rate doesn't match (requested %d Hz, got %d Hz)\n", wwi->format.Format.nSamplesPerSec, rate); ret = WAVERR_BADFORMAT; goto error; } 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"); dir=0; 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... */ if ( wwi->hw_params ) snd_pcm_hw_params_free(wwi->hw_params); snd_pcm_hw_params_malloc(&(wwi->hw_params)); snd_pcm_hw_params_copy(wwi->hw_params, hw_params); wwi->dwBufferSize = snd_pcm_frames_to_bytes(pcm, buffer_size); wwi->lpQueuePtr = wwi->lpPlayPtr = wwi->lpLoopPtr = NULL; wwi->pcm = pcm; ALSA_InitRingMessage(&wwi->msgRing); wwi->dwPeriodSize = snd_pcm_frames_to_bytes(pcm, period_size); TRACE("dwPeriodSize=%u\n", wwi->dwPeriodSize); TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%u, nSamplesPerSec=%u, nChannels=%u nBlockAlign=%u!\n", wwi->format.Format.wBitsPerSample, wwi->format.Format.nAvgBytesPerSec, wwi->format.Format.nSamplesPerSec, wwi->format.Format.nChannels, wwi->format.Format.nBlockAlign); wwi->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL); wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD_PTR)wDevID, 0, &(wwi->dwThreadID)); if (wwi->hThread) SetThreadPriority(wwi->hThread, THREAD_PRIORITY_TIME_CRITICAL); WaitForSingleObject(wwi->hStartUpEvent, INFINITE); CloseHandle(wwi->hStartUpEvent); wwi->hStartUpEvent = INVALID_HANDLE_VALUE; HeapFree( GetProcessHeap(), 0, hw_params ); HeapFree( GetProcessHeap(), 0, sw_params ); return widNotifyClient(wwi, WIM_OPEN, 0L, 0L); error: snd_pcm_close(pcm); HeapFree( GetProcessHeap(), 0, hw_params ); HeapFree( GetProcessHeap(), 0, sw_params ); return ret; }
static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface, LPWAVEFORMATEX pwfx, DWORD dwFlags, DWORD dwCardAddress, LPDWORD pdwcbBufferSize, LPBYTE *ppbBuffer, LPVOID *ppvObj) { IDsDriverImpl *This = (IDsDriverImpl *)iface; IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj; HRESULT err; TRACE("(%p,%p,%x,%x)\n",iface,pwfx,dwFlags,dwCardAddress); /* we only support primary buffers... for now */ if (!(dwFlags & DSBCAPS_PRIMARYBUFFER)) return DSERR_UNSUPPORTED; if (This->primary) return DSERR_ALLOCATED; *ippdsdb = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDsDriverBufferImpl)); if (*ippdsdb == NULL) return DSERR_OUTOFMEMORY; (*ippdsdb)->hw_params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_hw_params_sizeof()); (*ippdsdb)->sw_params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof()); if (!(*ippdsdb)->hw_params || !(*ippdsdb)->sw_params) { HeapFree(GetProcessHeap(), 0, (*ippdsdb)->sw_params); HeapFree(GetProcessHeap(), 0, (*ippdsdb)->hw_params); return DSERR_OUTOFMEMORY; } (*ippdsdb)->lpVtbl = &dsdbvt; (*ippdsdb)->ref = 1; (*ippdsdb)->drv = This; InitializeCriticalSection(&(*ippdsdb)->pcm_crst); (*ippdsdb)->pcm_crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ALSA_DSOUTPUT.pcm_crst"); /* SetFormat has to re-initialize pcm here anyway */ err = SetFormat(*ippdsdb, pwfx); if (FAILED(err)) { WARN("Error occurred: %08x\n", err); goto err; } if (dwFlags & DSBCAPS_PRIMARYBUFFER) This->primary = *ippdsdb; *pdwcbBufferSize = (*ippdsdb)->mmap_buflen_bytes; *ppbBuffer = (*ippdsdb)->mmap_buffer; /* buffer is ready to go */ TRACE("buffer created at %p\n", *ippdsdb); return err; err: HeapFree(GetProcessHeap(), 0, (*ippdsdb)->sw_params); HeapFree(GetProcessHeap(), 0, (*ippdsdb)->hw_params); HeapFree(GetProcessHeap(), 0, *ippdsdb); *ippdsdb = NULL; return err; }
/*---------------------------------------------------------------------------- ** ALSA_TestDeviceForWine ** ** Test to see if a given device is sufficient for Wine. */ static int ALSA_TestDeviceForWine(int card, int device, snd_pcm_stream_t streamtype) { snd_pcm_t *pcm = NULL; char pcmname[256]; int retcode; snd_pcm_hw_params_t *hwparams; const char *reason = NULL; unsigned int rrate; hwparams = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_hw_params_sizeof() ); /* Note that the plug: device masks out a lot of info, we want to avoid that */ sprintf(pcmname, "hw:%d,%d", card, device); retcode = snd_pcm_open(&pcm, pcmname, streamtype, SND_PCM_NONBLOCK); if (retcode < 0) { /* Note that a busy device isn't automatically disqualified */ if (retcode == (-1 * EBUSY)) retcode = 0; goto exit; } retcode = snd_pcm_hw_params_any(pcm, hwparams); if (retcode < 0) { reason = "Could not retrieve hw_params"; goto exit; } /* set the count of channels */ retcode = snd_pcm_hw_params_set_channels(pcm, hwparams, 2); if (retcode < 0) { retcode = snd_pcm_hw_params_set_channels(pcm, hwparams, 1); /* If we can't open stereo, try mono; this is vital for snd_usb_audio microphones */ } if (retcode < 0) { reason = "Could not set channels"; goto exit; } rrate = 44100; retcode = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rrate, 0); if (retcode < 0) { reason = "Could not set rate"; goto exit; } if (rrate == 0) { reason = "Rate came back as 0"; goto exit; } /* write the parameters to device */ retcode = snd_pcm_hw_params(pcm, hwparams); if (retcode < 0) { reason = "Could not set hwparams"; goto exit; } retcode = 0; exit: if (pcm) snd_pcm_close(pcm); HeapFree( GetProcessHeap(), 0, hwparams ); if (retcode != 0 && retcode != (-1 * ENOENT)) TRACE("Discarding card %d/device %d: %s [%d(%s)]\n", card, device, reason, retcode, snd_strerror(retcode)); return retcode; }
/*---------------------------------------------------------------------------- ** 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; }
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); } }
int main() { printf ("package Sound.Constants is\n"); printf (" Playback_Stream : constant := %u;\n", (unsigned int)SND_PCM_STREAM_PLAYBACK); printf (" Capture_Stream : constant := %u;\n", (unsigned int)SND_PCM_STREAM_CAPTURE); printf ("\n"); printf (" State_Open : constant := %u;\n", (unsigned int)SND_PCM_STATE_OPEN); printf (" State_Setup : constant := %u;\n", (unsigned int)SND_PCM_STATE_SETUP); printf (" State_Prepared : constant := %u;\n", (unsigned int)SND_PCM_STATE_PREPARED); printf (" State_Running : constant := %u;\n", (unsigned int)SND_PCM_STATE_RUNNING); printf (" State_XRun : constant := %u;\n", (unsigned int)SND_PCM_STATE_XRUN); printf (" State_Draining : constant := %u;\n", (unsigned int)SND_PCM_STATE_DRAINING); printf (" State_Paused : constant := %u;\n", (unsigned int)SND_PCM_STATE_PAUSED); printf (" State_Suspended : constant := %u;\n", (unsigned int)SND_PCM_STATE_SUSPENDED); printf (" State_Disconnected : constant := %u;\n", (unsigned int)SND_PCM_STATE_DISCONNECTED); printf ("\n"); printf (" Sound_Stream_Non_Blocking : constant := %u;\n", (unsigned int)SND_PCM_NONBLOCK); printf (" Sound_Stream_Asynchronous : constant := %u;\n", (unsigned int)SND_PCM_ASYNC); printf ("\n"); printf (" Format_Unknown : constant := %d;\n", (int)SND_PCM_FORMAT_UNKNOWN); printf (" Format_Signed_8_Bit : constant := %d;\n", (int)SND_PCM_FORMAT_S8); printf (" Format_Unsigned_8_Bit : constant := %d;\n", (int)SND_PCM_FORMAT_U8); printf (" Format_Signed_16_Bit_Little_Endian : constant := %d;\n", (int)SND_PCM_FORMAT_S16_LE); printf (" Format_Signed_16_Bit_Big_Endian : constant := %d;\n", (int)SND_PCM_FORMAT_S16_BE); printf (" Format_Unsigned_16_Bit_Little_Endian : constant := %d;\n", (int)SND_PCM_FORMAT_U16_LE); printf (" Format_Unsigned_16_Bit_Big_Endian : constant := %d;\n", (int)SND_PCM_FORMAT_U16_BE); printf (" Format_Signed_24_Bit_Little_Endian : constant := %d;\n", (int)SND_PCM_FORMAT_S24_LE); printf (" Format_Signed_24_Bit_Big_Endian : constant := %d;\n", (int)SND_PCM_FORMAT_S24_BE); printf (" Format_Unsigned_24_Bit_Little_Endian : constant := %d;\n", (int)SND_PCM_FORMAT_U24_LE); printf (" Format_Unsigned_24_Bit_Big_Endian : constant := %d;\n", (int)SND_PCM_FORMAT_U24_BE); printf (" Format_Signed_32_Bit_Little_Endian : constant := %d;\n", (int)SND_PCM_FORMAT_S32_LE); printf (" Format_Signed_32_Bit_Big_Endian : constant := %d;\n", (int)SND_PCM_FORMAT_S32_BE); printf (" Format_Unsigned_32_Bit_Little_Endian : constant := %d;\n", (int)SND_PCM_FORMAT_U32_LE); printf (" Format_Unsigned_32_Bit_Big_Endian : constant := %d;\n", (int)SND_PCM_FORMAT_U32_BE); printf (" Format_FLOAT_LE : constant := %d;\n", (int)SND_PCM_FORMAT_FLOAT_LE); printf (" Format_FLOAT_BE : constant := %d;\n", (int)SND_PCM_FORMAT_FLOAT_BE); printf (" Format_FLOAT64_LE : constant := %d;\n", (int)SND_PCM_FORMAT_FLOAT64_LE); printf (" Format_FLOAT64_BE : constant := %d;\n", (int)SND_PCM_FORMAT_FLOAT64_BE); printf (" Format_IEC958_SUBFRAME_LE : constant := %d;\n", (int)SND_PCM_FORMAT_IEC958_SUBFRAME_LE); printf (" Format_IEC958_SUBFRAME_BE : constant := %d;\n", (int)SND_PCM_FORMAT_IEC958_SUBFRAME_BE); printf (" Format_MU_LAW : constant := %d;\n", (int)SND_PCM_FORMAT_MU_LAW); printf (" Format_A_LAW : constant := %d;\n", (int)SND_PCM_FORMAT_A_LAW); printf (" Format_IMA_ADPCM : constant := %d;\n", (int)SND_PCM_FORMAT_IMA_ADPCM); printf (" Format_MPEG : constant := %d;\n", (int)SND_PCM_FORMAT_MPEG); printf (" Format_GSM : constant := %d;\n", (int)SND_PCM_FORMAT_GSM); printf (" Format_SPECIAL : constant := %d;\n", (int)SND_PCM_FORMAT_SPECIAL); printf (" Format_S24_3LE : constant := %d;\n", (int)SND_PCM_FORMAT_S24_3LE); printf (" Format_S24_3BE : constant := %d;\n", (int)SND_PCM_FORMAT_S24_3BE); printf (" Format_U24_3LE : constant := %d;\n", (int)SND_PCM_FORMAT_U24_3LE); printf (" Format_U24_3BE : constant := %d;\n", (int)SND_PCM_FORMAT_U24_3BE); printf (" Format_S20_3LE : constant := %d;\n", (int)SND_PCM_FORMAT_S20_3LE); printf (" Format_S20_3BE : constant := %d;\n", (int)SND_PCM_FORMAT_S20_3BE); printf (" Format_U20_3LE : constant := %d;\n", (int)SND_PCM_FORMAT_U20_3LE); printf (" Format_U20_3BE : constant := %d;\n", (int)SND_PCM_FORMAT_U20_3BE); printf (" Format_S18_3LE : constant := %d;\n", (int)SND_PCM_FORMAT_S18_3LE); printf (" Format_S18_3BE : constant := %d;\n", (int)SND_PCM_FORMAT_S18_3BE); printf (" Format_U18_3LE : constant := %d;\n", (int)SND_PCM_FORMAT_U18_3LE); printf (" Format_U18_3BE : constant := %d;\n", (int)SND_PCM_FORMAT_U18_3BE); printf (" Format_Last : constant := %d;\n", (int)SND_PCM_FORMAT_LAST); printf (" Format_Signed_16_Bit : constant := %d;\n", (int)SND_PCM_FORMAT_S16); printf (" Format_Unsigned_16_Bit : constant := %d;\n", (int)SND_PCM_FORMAT_U16); printf ("\n"); printf (" Access_Memory_Mapped_Interleaved : constant := %u;\n", (unsigned int)SND_PCM_ACCESS_MMAP_INTERLEAVED); printf (" Access_Memory_Mapped_Noninterleaved : constant := %u;\n", (unsigned int)SND_PCM_ACCESS_MMAP_NONINTERLEAVED); printf (" Access_Memory_Mapped_Complex : constant := %u;\n", (unsigned int)SND_PCM_ACCESS_MMAP_COMPLEX); printf (" Access_Read_Write_Interleaved : constant := %u;\n", (unsigned int)SND_PCM_ACCESS_RW_INTERLEAVED); printf (" Access_Read_Write_Noninterleaved : constant := %u;\n", (unsigned int)SND_PCM_ACCESS_RW_NONINTERLEAVED); printf ("\n"); printf (" Error_Bad_File_Descriptor : constant := %u;\n", (unsigned int)EBADFD); printf (" Error_Pipe : constant := %u;\n", (unsigned int)EPIPE); printf (" Error_Stream_Pipe : constant := %u;\n", (unsigned int)ESTRPIPE); printf (" Error_Interrupted_System_Call : constant := %u;\n", (unsigned int)EINTR); printf (" Error_Again : constant := %u;\n", (unsigned int)EAGAIN); printf (" Error_Unsupported_Feature : constant := %u;\n", (unsigned int)ENOSYS); printf ("\n"); printf (" hw_params_Size : constant := %u;\n", (unsigned int)(8 * (snd_pcm_hw_params_sizeof()))); printf ("end Sound.Constants;\n"); return 0; };
static int alsa_open(const char *device, unsigned sample_rate, unsigned alsa_buffer, unsigned alsa_period) { int err; snd_pcm_hw_params_t *hw_params; snd_pcm_hw_params_alloca(&hw_params); // close if already open if (pcmp) alsa_close(); // reset params alsa.rate = 0; alsa.period_size = 0; strcpy(alsa.device, device); if (strlen(device) > MAX_DEVICE_LEN - 4 - 1) { LOG_ERROR("device name too long: %s", device); return -1; } LOG_INFO("opening device at: %u", sample_rate); bool retry; do { // open device if ((err = snd_pcm_open(&pcmp, alsa.device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { LOG_ERROR("playback open error: %s", snd_strerror(err)); return err; } // init params memset(hw_params, 0, snd_pcm_hw_params_sizeof()); if ((err = snd_pcm_hw_params_any(pcmp, hw_params)) < 0) { LOG_ERROR("hwparam init error: %s", snd_strerror(err)); return err; } // open hw: devices without resampling, if sample rate fails try plughw: with resampling bool hw = !strncmp(alsa.device, "hw:", 3); retry = false; if ((err = snd_pcm_hw_params_set_rate_resample(pcmp, hw_params, !hw)) < 0) { LOG_ERROR("resampling setup failed: %s", snd_strerror(err)); return err; } if ((err = snd_pcm_hw_params_set_rate(pcmp, hw_params, sample_rate, 0)) < 0) { if (hw) { strcpy(alsa.device + 4, device); memcpy(alsa.device, "plug", 4); LOG_INFO("reopening device %s in plug mode as %s for resampling", device, alsa.device); snd_pcm_close(pcmp); retry = true; } } } while (retry); // set access if (!alsa.mmap || snd_pcm_hw_params_set_access(pcmp, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) { if ((err = snd_pcm_hw_params_set_access(pcmp, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { LOG_ERROR("access type not available: %s", snd_strerror(err)); return err; } alsa.mmap = false; } // set the sample format snd_pcm_format_t *fmt = alsa.format ? &alsa.format : (snd_pcm_format_t *)fmts; do { if (snd_pcm_hw_params_set_format(pcmp, hw_params, *fmt) >= 0) { LOG_INFO("opened device %s using format: %s sample rate: %u mmap: %u", alsa.device, snd_pcm_format_name(*fmt), sample_rate, alsa.mmap); alsa.format = *fmt; break; } if (alsa.format) { LOG_ERROR("unable to open audio device requested format: %s", snd_pcm_format_name(alsa.format)); return -1; } ++fmt; if (*fmt == SND_PCM_FORMAT_UNKNOWN) { LOG_ERROR("unable to open audio device with any supported format"); return -1; } } while (*fmt != SND_PCM_FORMAT_UNKNOWN); // set the output format to be used by _scale_and_pack switch(alsa.format) { case SND_PCM_FORMAT_S32_LE: output.format = S32_LE; break; case SND_PCM_FORMAT_S24_LE: output.format = S24_LE; break; case SND_PCM_FORMAT_S24_3LE: output.format = S24_3LE; break; case SND_PCM_FORMAT_S16_LE: output.format = S16_LE; break; default: break; } // set channels if ((err = snd_pcm_hw_params_set_channels (pcmp, hw_params, 2)) < 0) { LOG_ERROR("channel count not available: %s", snd_strerror(err)); return err; } // set period size - value of < 50 treated as period count, otherwise size in bytes if (alsa_period < 50) { unsigned count = alsa_period; if ((err = snd_pcm_hw_params_set_periods_near(pcmp, hw_params, &count, 0)) < 0) { LOG_ERROR("unable to set period count %s", snd_strerror(err)); return err; } } else { snd_pcm_uframes_t size = alsa_period; int dir = 0; if ((err = snd_pcm_hw_params_set_period_size_near(pcmp, hw_params, &size, &dir)) < 0) { LOG_ERROR("unable to set period size %s", snd_strerror(err)); return err; } } // set buffer size - value of < 500 treated as buffer time in ms, otherwise size in bytes if (alsa_buffer < 500) { unsigned time = alsa_buffer * 1000; int dir = 0; if ((err = snd_pcm_hw_params_set_buffer_time_near(pcmp, hw_params, &time, &dir)) < 0) { LOG_ERROR("unable to set buffer time %s", snd_strerror(err)); return err; } } else { snd_pcm_uframes_t size = alsa_buffer; if ((err = snd_pcm_hw_params_set_buffer_size_near(pcmp, hw_params, &size)) < 0) { LOG_ERROR("unable to set buffer size %s", snd_strerror(err)); return err; } } // get period_size if ((err = snd_pcm_hw_params_get_period_size(hw_params, &alsa.period_size, 0)) < 0) { LOG_ERROR("unable to get period size: %s", snd_strerror(err)); return err; } // get buffer_size if ((err = snd_pcm_hw_params_get_buffer_size(hw_params, &alsa.buffer_size)) < 0) { LOG_ERROR("unable to get buffer size: %s", snd_strerror(err)); return err; } LOG_INFO("buffer: %u period: %u -> buffer size: %u period size: %u", alsa_buffer, alsa_period, alsa.buffer_size, alsa.period_size); // ensure we have two buffer sizes of samples before starting output output.start_frames = alsa.buffer_size * 2; // create an intermediate buffer for non mmap case for all but NATIVE_FORMAT // this is used to pack samples into the output format before calling writei if (!alsa.mmap && !alsa.write_buf && alsa.format != NATIVE_FORMAT) { alsa.write_buf = malloc(alsa.buffer_size * BYTES_PER_FRAME); if (!alsa.write_buf) { LOG_ERROR("unable to malloc write_buf"); return -1; } } // set params if ((err = snd_pcm_hw_params(pcmp, hw_params)) < 0) { LOG_ERROR("unable to set hw params: %s", snd_strerror(err)); return err; } // dump info if (loglevel == lSDEBUG) { static snd_output_t *debug_output; snd_output_stdio_attach(&debug_output, stderr, 0); snd_pcm_dump(pcmp, debug_output); } // this indicates we have opened the device ok alsa.rate = sample_rate; return 0; }