bool CWin32WASAPI::Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels *channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic, bool bAudioPassthrough) { CLog::Log(LOGDEBUG, __FUNCTION__": endpoint device %s", device.c_str()); //First check if the version of Windows we are running on even supports WASAPI. if (!g_sysinfo.IsVistaOrHigher()) { CLog::Log(LOGERROR, __FUNCTION__": WASAPI output requires Vista or higher."); return false; } //Only one exclusive stream may be initialized at one time. if(m_bIsAllocated) { CLog::Log(LOGERROR, __FUNCTION__": Cannot create more then one WASAPI stream at one time."); return false; } int layoutChannels = 0; if(!bAudioPassthrough) { //If no channel map is specified, use the default. if(!channelMap) channelMap = (PCMChannels *)wasapi_default_channel_layout[iChannels - 1]; PCMChannels *outLayout = m_remap.SetInputFormat(iChannels, channelMap, uiBitsPerSample / 8); for(PCMChannels *channel = outLayout; *channel != PCM_INVALID; channel++) ++layoutChannels; //Expand monural to stereo as most devices don't seem to like 1 channel PCM streams. //Stereo sources should be sent explicitly as two channels so that the external hardware //can apply ProLogic/5CH Stereo/etc processing on it. if(iChannels <= 2) { BuildChannelMapping(2, (PCMChannels *)wasapi_default_channel_layout[1]); layoutChannels = 2; m_remap.SetOutputFormat(2, m_SpeakerOrder, false); } else //Do the standard remapping. { BuildChannelMapping(layoutChannels, outLayout); m_remap.SetOutputFormat(layoutChannels, m_SpeakerOrder, false); } } m_bPlaying = false; m_bPause = false; m_bMuting = false; m_uiChannels = iChannels; m_uiBitsPerSample = uiBitsPerSample; m_bPassthrough = bAudioPassthrough; m_nCurrentVolume = g_settings.m_nVolumeLevel; m_pcmAmplifier.SetVolume(m_nCurrentVolume); WAVEFORMATEXTENSIBLE wfxex = {0}; //fill waveformatex ZeroMemory(&wfxex, sizeof(WAVEFORMATEXTENSIBLE)); wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); wfxex.Format.nChannels = layoutChannels; wfxex.Format.nSamplesPerSec = uiSamplesPerSec; if (bAudioPassthrough == true) { wfxex.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; wfxex.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF; wfxex.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF; wfxex.Format.wBitsPerSample = 16; wfxex.Format.nChannels = 2; } else { wfxex.dwChannelMask = m_uiSpeakerMask; wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfxex.Format.wBitsPerSample = uiBitsPerSample; } wfxex.Samples.wValidBitsPerSample = uiBitsPerSample == 32 ? 24 : uiBitsPerSample; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; m_uiAvgBytesPerSec = wfxex.Format.nAvgBytesPerSec; m_uiBytesPerFrame = wfxex.Format.nBlockAlign; m_uiBytesPerSrcFrame = bAudioPassthrough ? m_uiBytesPerFrame : iChannels * wfxex.Format.wBitsPerSample >> 3; IMMDeviceEnumerator* pEnumerator = NULL; IMMDeviceCollection* pEnumDevices = NULL; //Shut down Directsound. g_audioContext.SetActiveDevice(CAudioContext::NONE); HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); EXIT_ON_FAILURE(hr, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %i", hr) //Get our device. //First try to find the named device. UINT uiCount = 0; hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pEnumDevices); EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint enumeration failed.") hr = pEnumDevices->GetCount(&uiCount); EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.") for(UINT i = 0; i < uiCount; i++) { IPropertyStore *pProperty = NULL; PROPVARIANT varName; hr = pEnumDevices->Item(i, &m_pDevice); EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of WASAPI endpoint failed.") hr = m_pDevice->OpenPropertyStore(STGM_READ, &pProperty); EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of WASAPI endpoint properties failed.") hr = pProperty->GetValue(PKEY_Device_FriendlyName, &varName); if(FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint device name failed."); SAFE_RELEASE(pProperty); goto failed; } CStdStringW strRawDevName(varName.pwszVal); CStdString strDevName; g_charsetConverter.wToUTF8(strRawDevName, strDevName); if(device == strDevName) i = uiCount; else SAFE_RELEASE(m_pDevice); PropVariantClear(&varName); SAFE_RELEASE(pProperty); } SAFE_RELEASE(pEnumDevices); if(!m_pDevice) { CLog::Log(LOGDEBUG, __FUNCTION__": Could not locate the device named \"%s\" in the list of WASAPI endpoint devices. Trying the default device...", device.c_str()); hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &m_pDevice); EXIT_ON_FAILURE(hr, __FUNCTION__": Could not retrieve the default WASAPI audio endpoint.") }
bool CWin32DirectSound::Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, enum PCMChannels* channelMap, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, bool bIsMusic, EEncoded bAudioPassthrough) { m_uiDataChannels = iChannels; if(!bAudioPassthrough) { //If no channel map is specified, use the default. if(!channelMap) channelMap = (PCMChannels *)dsound_default_channel_layout[iChannels - 1]; PCMChannels *outLayout = m_remap.SetInputFormat(iChannels, channelMap, uiBitsPerSample / 8, uiSamplesPerSec); for(iChannels = 0; outLayout[iChannels] != PCM_INVALID;) ++iChannels; BuildChannelMapping(iChannels, outLayout); m_remap.SetOutputFormat(iChannels, m_SpeakerOrder, false); } bool bAudioOnAllSpeakers(false); g_audioContext.SetupSpeakerConfig(iChannels, bAudioOnAllSpeakers, bIsMusic); if(bAudioPassthrough) g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE_DIGITAL); else g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE); m_pDSound=g_audioContext.GetDirectSoundDevice(); m_bPause = false; m_bIsAllocated = false; m_pBuffer = NULL; m_uiChannels = iChannels; m_uiSamplesPerSec = uiSamplesPerSec; m_uiBitsPerSample = uiBitsPerSample; m_Passthrough = bAudioPassthrough; m_nCurrentVolume = g_settings.m_nVolumeLevel; m_drc = 0; WAVEFORMATEXTENSIBLE wfxex = {0}; //fill waveformatex ZeroMemory(&wfxex, sizeof(WAVEFORMATEXTENSIBLE)); wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); wfxex.Format.nChannels = iChannels; wfxex.Format.nSamplesPerSec = uiSamplesPerSec; if (bAudioPassthrough == true) { wfxex.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; wfxex.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF; wfxex.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF; wfxex.Format.wBitsPerSample = 16; wfxex.Format.nChannels = 2; } else { wfxex.dwChannelMask = m_uiSpeakerMask; if (iChannels > 2) wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; else wfxex.Format.wFormatTag = WAVE_FORMAT_PCM; wfxex.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM; wfxex.Format.wBitsPerSample = uiBitsPerSample; } wfxex.Samples.wValidBitsPerSample = wfxex.Format.wBitsPerSample; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; m_AvgBytesPerSec = wfxex.Format.nAvgBytesPerSec; m_uiBytesPerFrame = wfxex.Format.nBlockAlign; m_uiDataBytesPerFrame = (wfxex.Format.nBlockAlign / iChannels) * m_uiDataChannels; // unsure if these are the right values m_dwChunkSize = wfxex.Format.nBlockAlign * 3096; m_dwDataChunkSize = (m_dwChunkSize / iChannels) * m_uiDataChannels; m_dwBufferLen = m_dwChunkSize * 16; m_PreCacheSize = m_dwBufferLen - 2*m_dwChunkSize; CLog::Log(LOGDEBUG, __FUNCTION__": Packet Size = %d. Avg Bytes Per Second = %d.", m_dwChunkSize, m_AvgBytesPerSec); // fill in the secondary sound buffer descriptor DSBUFFERDESC dsbdesc; memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */ | DSBCAPS_GLOBALFOCUS /** Allows background playing */ | DSBCAPS_CTRLVOLUME; /** volume control enabled */ if (!g_sysinfo.IsVistaOrHigher()) dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE; /** Needed for 5.1 on emu101k, always fails on Vista, by design */ dsbdesc.dwBufferBytes = m_dwBufferLen; dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wfxex; // now create the stream buffer HRESULT res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL); if (res != DS_OK) { if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE) { SAFE_RELEASE(m_pBuffer); CLog::Log(LOGDEBUG, __FUNCTION__": Couldn't create secondary buffer (%s). Trying without LOCHARDWARE.", dserr2str(res)); // Try without DSBCAPS_LOCHARDWARE dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE; res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL); } if (res != DS_OK && dsbdesc.dwFlags & DSBCAPS_CTRLVOLUME) { SAFE_RELEASE(m_pBuffer); CLog::Log(LOGDEBUG, __FUNCTION__": Couldn't create secondary buffer (%s). Trying without CTRLVOLUME.", dserr2str(res)); // Try without DSBCAPS_CTRLVOLUME dsbdesc.dwFlags &= ~DSBCAPS_CTRLVOLUME; res = IDirectSound_CreateSoundBuffer(m_pDSound, &dsbdesc, &m_pBuffer, NULL); } if (res != DS_OK) { SAFE_RELEASE(m_pBuffer); CLog::Log(LOGERROR, __FUNCTION__": cannot create secondary buffer (%s)", dserr2str(res)); return false; } } CLog::Log(LOGDEBUG, __FUNCTION__": secondary buffer created"); m_pBuffer->Stop(); if (DSERR_CONTROLUNAVAIL == m_pBuffer->SetVolume(g_settings.m_nVolumeLevel)) CLog::Log(LOGINFO, __FUNCTION__": Volume control is unavailable in the current configuration"); m_bIsAllocated = true; m_BufferOffset = 0; m_CacheLen = 0; m_LastCacheCheck = XbmcThreads::SystemClockMillis(); return m_bIsAllocated; }