HRESULT CWASAPIRenderFilter::IsFormatSupported(const WAVEFORMATEXTENSIBLE* pwfx, WAVEFORMATEXTENSIBLE** pwfxAccepted) { WAVEFORMATEXTENSIBLE* pwfxCM = NULL; WAVEFORMATEX* tmpPwfx = NULL; HRESULT hr = m_pAudioClient->IsFormatSupported(m_pSettings->m_WASAPIShareMode, (WAVEFORMATEX*)pwfx, (WAVEFORMATEX**)&pwfxCM); if (hr != S_OK) { CopyWaveFormatEx((WAVEFORMATEXTENSIBLE**)&tmpPwfx, pwfx); tmpPwfx->cbSize = 0; tmpPwfx->wFormatTag = WAVE_FORMAT_PCM; hr = m_pAudioClient->IsFormatSupported(m_pSettings->m_WASAPIShareMode, (WAVEFORMATEX*)tmpPwfx, (WAVEFORMATEX**)&pwfxCM); if (hr != S_OK) { Log("CWASAPIRenderFilter::NegotiateFormat WASAPI client refused the format: (0x%08x)", hr); LogWaveFormat(pwfxCM, "Closest match would be" ); SAFE_DELETE_WAVEFORMATEX(tmpPwfx); CoTaskMemFree(pwfxCM); return VFW_E_TYPE_NOT_ACCEPTED; } ToWaveFormatExtensible(pwfxAccepted, tmpPwfx); (*pwfxAccepted)->Format.cbSize = 0; (*pwfxAccepted)->Format.wFormatTag = WAVE_FORMAT_PCM; SAFE_DELETE_WAVEFORMATEX(tmpPwfx); } else CopyWaveFormatEx(pwfxAccepted, pwfx); return hr; }
HRESULT CWASAPIRenderFilter::IsFormatSupported(const WAVEFORMATEXTENSIBLE* pwfx, WAVEFORMATEXTENSIBLE** pwfxAccepted) { WAVEFORMATEXTENSIBLE* pwfxCM = NULL; WAVEFORMATEX* tmpPwfx = NULL; AUDCLNT_SHAREMODE shareMode = m_pSettings->GetWASAPIMode(); HRESULT hr = m_pAudioClient->IsFormatSupported(shareMode, (WAVEFORMATEX*)pwfx, (WAVEFORMATEX**)&pwfxCM); if (hr != S_OK) { if (hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED) { Log("CWASAPIRenderFilter::NegotiateFormat WASAPI client will only accept shared mode sessions, check audio device's configuration."); return hr; } if (CBaseAudioSink::CanBitstream(pwfx)) // Misrepresenting the media type for bit streamable formats will make negotiation succeed, but playback will not return hr; CopyWaveFormatEx((WAVEFORMATEXTENSIBLE**)&tmpPwfx, pwfx); tmpPwfx->cbSize = 0; tmpPwfx->wFormatTag = WAVE_FORMAT_PCM; hr = m_pAudioClient->IsFormatSupported(shareMode, (WAVEFORMATEX*)tmpPwfx, (WAVEFORMATEX**)&pwfxCM); if (hr != S_OK) { Log("CWASAPIRenderFilter::NegotiateFormat WASAPI client refused the format: (0x%08x)", hr); LogWaveFormat(pwfxCM, "Closest match would be" ); SAFE_DELETE_WAVEFORMATEX(tmpPwfx); CoTaskMemFree(pwfxCM); return VFW_E_TYPE_NOT_ACCEPTED; } ToWaveFormatExtensible(pwfxAccepted, tmpPwfx); (*pwfxAccepted)->Format.cbSize = 0; (*pwfxAccepted)->Format.wFormatTag = WAVE_FORMAT_PCM; SAFE_DELETE_WAVEFORMATEX(tmpPwfx); } else CopyWaveFormatEx(pwfxAccepted, pwfx); return hr; }
HRESULT CChannelMixer::NegotiateFormat(const WAVEFORMATEXTENSIBLE* pwfx, int nApplyChangesDepth, ChannelOrder* pChOrder) { if (!pwfx) return VFW_E_TYPE_NOT_ACCEPTED; if (FormatsEqual(pwfx, m_pInputFormat)) { *pChOrder = m_chOrder; return S_OK; } if (!m_pNextSink) return VFW_E_TYPE_NOT_ACCEPTED; bool bApplyChanges = (nApplyChangesDepth != 0); if (nApplyChangesDepth != INFINITE && nApplyChangesDepth > 0) nApplyChangesDepth--; if (pwfx->SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) return VFW_E_TYPE_NOT_ACCEPTED; HRESULT hr = S_OK; bool expandToStereo = pwfx->Format.nChannels == 1 && m_pSettings->m_bExpandMonoToStereo; if (!m_pSettings->m_bForceChannelMixing && !expandToStereo) { // try the format directly hr = m_pNextSink->NegotiateFormat(pwfx, nApplyChangesDepth, pChOrder); if (SUCCEEDED(hr)) { if (bApplyChanges) { SetInputFormat(pwfx); SetOutputFormat(pwfx); m_bPassThrough = false; hr = SetupConversion(*pChOrder); } m_chOrder = *pChOrder; return hr; } } WAVEFORMATEXTENSIBLE* pOutWfx; CopyWaveFormatEx(&pOutWfx, pwfx); if (!expandToStereo || m_pSettings->m_bForceChannelMixing) { pOutWfx->dwChannelMask = m_pSettings->m_lSpeakerConfig; pOutWfx->Format.nChannels = m_pSettings->m_lSpeakerCount; } else // Expand mono to stereo { pOutWfx->dwChannelMask = KSAUDIO_SPEAKER_STEREO; pOutWfx->Format.nChannels = 2; } pOutWfx->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; pOutWfx->Format.nBlockAlign = pOutWfx->Format.wBitsPerSample / 8 * pOutWfx->Format.nChannels; pOutWfx->Format.nAvgBytesPerSec = pOutWfx->Format.nBlockAlign * pOutWfx->Format.nSamplesPerSec; hr = m_pNextSink->NegotiateFormat(pOutWfx, nApplyChangesDepth, pChOrder); m_chOrder = *pChOrder; if (FAILED(hr)) { SAFE_DELETE_WAVEFORMATEX(pOutWfx); return hr; } if (bApplyChanges) { LogWaveFormat(pwfx, "MIX - applying "); m_bPassThrough = false; SetInputFormat(pwfx); SetOutputFormat(pOutWfx, true); hr = SetupConversion(*pChOrder); } else { LogWaveFormat(pwfx, "MIX - "); SAFE_DELETE_WAVEFORMATEX(pOutWfx); } return hr; }
HRESULT CSampleRateConverter::NegotiateFormat(const WAVEFORMATEXTENSIBLE* pwfx, int nApplyChangesDepth, ChannelOrder* pChOrder) { if (!pwfx) return VFW_E_TYPE_NOT_ACCEPTED; if (FormatsEqual(pwfx, m_pInputFormat)) { *pChOrder = m_chOrder; return S_OK; } if (!m_pNextSink) return VFW_E_TYPE_NOT_ACCEPTED; bool bApplyChanges = (nApplyChangesDepth != 0); if (nApplyChangesDepth != INFINITE && nApplyChangesDepth > 0) nApplyChangesDepth--; // Try passthrough HRESULT hr = m_pNextSink->NegotiateFormat(pwfx, nApplyChangesDepth, pChOrder); if (SUCCEEDED(hr)) { if (bApplyChanges) { m_bPassThrough = true; SetInputFormat(pwfx); SetOutputFormat(pwfx); } m_chOrder = *pChOrder; return hr; } if (pwfx->SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) return VFW_E_TYPE_NOT_ACCEPTED; WAVEFORMATEXTENSIBLE* pOutWfx; CopyWaveFormatEx(&pOutWfx, pwfx); pOutWfx->Format.nSamplesPerSec = 0; hr = VFW_E_TYPE_NOT_ACCEPTED; const unsigned int sampleRateCount = sizeof(gAllowedSampleRates) / sizeof(int); unsigned int startPoint = 0; // TODO test duplicate sample rates first // Search for the input sample rate in sample rate array bool foundSampleRate = false; for (unsigned int i = 0; i < sampleRateCount && !foundSampleRate; i++) { if (gAllowedSampleRates[i] == pwfx->Format.nSamplesPerSec) { startPoint = ++i; // select closest sample rate in ascending order foundSampleRate = true; } } if (!foundSampleRate) Log("CSampleRateConverter::NegotiateFormat - sample rate (%d) not found in the source array", pwfx->Format.nSamplesPerSec); unsigned int sampleRatesTested = 0; for (int i = startPoint; FAILED(hr) && pOutWfx->Format.nSamplesPerSec == 0 && sampleRatesTested < sampleRateCount; i++) { if (pOutWfx->Format.nSamplesPerSec == pwfx->Format.nSamplesPerSec) { sampleRatesTested++; continue; // skip if same as source } pOutWfx->Format.nSamplesPerSec = gAllowedSampleRates[i]; pOutWfx->Format.nAvgBytesPerSec = gAllowedSampleRates[i] * pOutWfx->Format.nBlockAlign; hr = m_pNextSink->NegotiateFormat(pOutWfx, nApplyChangesDepth, pChOrder); sampleRatesTested++; if (FAILED(hr)) pOutWfx->Format.nSamplesPerSec = 0; // Search from the lower end if (i == sampleRateCount - 1) i = 0; } if (FAILED(hr)) { SAFE_DELETE_WAVEFORMATEX(pOutWfx); return hr; } if (bApplyChanges) { LogWaveFormat(pwfx, "SRC - applying "); m_bPassThrough = false; SetInputFormat(pwfx); SetOutputFormat(pOutWfx, true); hr = SetupConversion(); // TODO: do something meaningfull if SetupConversion fails //if (FAILED(hr)) } else { LogWaveFormat(pwfx, "SRC - "); SAFE_DELETE_WAVEFORMATEX(pOutWfx); } m_chOrder = *pChOrder; return S_OK; }
// Format negotiation HRESULT CWASAPIRenderFilter::NegotiateFormat(const WAVEFORMATEXTENSIBLE* pwfx, int nApplyChangesDepth, ChannelOrder* pChOrder) { if (!pwfx) return VFW_E_TYPE_NOT_ACCEPTED; if (FormatsEqual(pwfx, m_pInputFormat)) { *pChOrder = m_chOrder; return S_OK; } bool bApplyChanges = nApplyChangesDepth != 0; bool bitDepthForced = (m_pSettings->m_nForceBitDepth != 0 && m_pSettings->m_nForceBitDepth != pwfx->Format.wBitsPerSample); bool sampleRateForced = (m_pSettings->m_nForceSamplingRate != 0 && m_pSettings->m_nForceSamplingRate != pwfx->Format.nSamplesPerSec); if ((bitDepthForced || sampleRateForced) && pwfx->SubFormat == KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL || pwfx->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT && bitDepthForced) return VFW_E_TYPE_NOT_ACCEPTED; if (((bitDepthForced && m_pSettings->m_nForceBitDepth != pwfx->Format.wBitsPerSample) || (sampleRateForced && m_pSettings->m_nForceSamplingRate != pwfx->Format.nSamplesPerSec))) return VFW_E_TYPE_NOT_ACCEPTED; CAutoLock lock(&m_csResources); HRESULT hr = CreateAudioClient(); if (FAILED(hr)) { Log("CWASAPIRenderFilter::NegotiateFormat Error, audio client not initialized: (0x%08x)", hr); return VFW_E_CANNOT_CONNECT; } WAVEFORMATEXTENSIBLE* pwfxAccepted = NULL; hr = IsFormatSupported(pwfx, &pwfxAccepted); if (FAILED(hr)) { SAFE_DELETE_WAVEFORMATEX(pwfxAccepted); return hr; } if (bApplyChanges) { LogWaveFormat(pwfx, "REN - applying "); // Stop and discard audio client StopAudioClient(); SAFE_RELEASE(m_pRenderClient); SAFE_RELEASE(m_pAudioClock); SAFE_RELEASE(m_pAudioClient); // We must use incoming format so the WAVEFORMATEXTENSIBLE to WAVEFORMATEXT difference // that some audio drivers require is not causing an infonite loop of format changes SetInputFormat(pwfx); // Reinitialize audio client hr = CreateAudioClient(true); } else LogWaveFormat(pwfx, "Input format "); m_chOrder = *pChOrder = DS_ORDER; SAFE_DELETE_WAVEFORMATEX(pwfxAccepted); return hr; }
HRESULT CWASAPIRenderFilter::InitAudioClient() { Log("WASAPIRenderFilter::InitAudioClient"); HRESULT hr = S_OK; if (m_pSettings->m_hnsPeriod == 0 || m_pSettings->m_hnsPeriod == 1) { REFERENCE_TIME defaultPeriod(0); REFERENCE_TIME minimumPeriod(0); hr = m_pAudioClient->GetDevicePeriod(&defaultPeriod, &minimumPeriod); if (SUCCEEDED(hr)) { if (m_pSettings->m_hnsPeriod == 0) m_pSettings->m_hnsPeriod = defaultPeriod; else m_pSettings->m_hnsPeriod = minimumPeriod; Log("WASAPIRenderFilter::InitAudioClient using device period from driver %I64u ms", m_pSettings->m_hnsPeriod / 10000); } else { Log("WASAPIRenderFilter::InitAudioClient failed to get device period from driver (0x%08x) - using 50 ms", hr); m_pSettings->m_hnsPeriod = 500000; //50 ms is the best according to James @Slysoft } } WAVEFORMATEXTENSIBLE* pwfxAccepted = NULL; hr = IsFormatSupported(m_pInputFormat, &pwfxAccepted); if (FAILED(hr)) { SAFE_DELETE_WAVEFORMATEX(pwfxAccepted); return hr; } GetBufferSize((WAVEFORMATEX*)pwfxAccepted, &m_pSettings->m_hnsPeriod); if (SUCCEEDED(hr)) hr = m_pAudioClient->Initialize(m_pSettings->m_WASAPIShareMode, m_dwStreamFlags, m_pSettings->m_hnsPeriod, m_pSettings->m_hnsPeriod, (WAVEFORMATEX*)pwfxAccepted, NULL); if (FAILED(hr) && hr != AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { Log("WASAPIRenderFilter::InitAudioClient Initialize failed (0x%08x)", hr); SAFE_DELETE_WAVEFORMATEX(pwfxAccepted); return hr; } if (hr == S_OK) { SAFE_RELEASE(m_pAudioClock); hr = m_pAudioClient->GetService(__uuidof(IAudioClock), (void**)&m_pAudioClock); if (SUCCEEDED(hr)) m_pAudioClock->GetFrequency(&m_nHWfreq); else Log("WASAPIRenderFilter::IAudioClock not found!"); } if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { // if the buffer size was not aligned, need to do the alignment dance Log("WASAPIRenderFilter::InitAudioClient Buffer size not aligned. Realigning"); // get the buffer size, which will be aligned hr = m_pAudioClient->GetBufferSize(&m_nFramesInBuffer); // throw away this IAudioClient SAFE_RELEASE(m_pAudioClient); // calculate the new aligned periodicity m_pSettings->m_hnsPeriod = // hns = (REFERENCE_TIME)( 10000.0 * // (hns / ms) * 1000 * // (ms / s) * m_nFramesInBuffer / // frames / m_pInputFormat->Format.nSamplesPerSec // (frames / s) + 0.5 // rounding ); if (SUCCEEDED(hr)) hr = CreateAudioClient(); Log("WASAPIRenderFilter::InitAudioClient Trying again with periodicity of %I64u hundred-nanoseconds, or %u frames", m_pSettings->m_hnsPeriod, m_nFramesInBuffer); if (SUCCEEDED (hr)) hr = m_pAudioClient->Initialize(m_pSettings->m_WASAPIShareMode, m_dwStreamFlags, m_pSettings->m_hnsPeriod, m_pSettings->m_hnsPeriod, (WAVEFORMATEX*)pwfxAccepted, NULL); if (FAILED(hr)) { Log("WASAPIRenderFilter::InitAudioClient Failed to reinitialize the audio client"); SAFE_DELETE_WAVEFORMATEX(pwfxAccepted); return hr; } else { SAFE_RELEASE(m_pAudioClock); hr = m_pAudioClient->GetService(__uuidof(IAudioClock), (void**)&m_pAudioClock); if (FAILED(hr)) Log("WASAPIRenderFilter::IAudioClock not found!"); else m_pAudioClock->GetFrequency(&m_nHWfreq); } } // if (AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED == hr) // get the buffer size, which is aligned if (SUCCEEDED(hr)) hr = m_pAudioClient->GetBufferSize(&m_nFramesInBuffer); // calculate the new period if (SUCCEEDED (hr)) hr = m_pAudioClient->GetService(__uuidof(IAudioRenderClient), (void**)(&m_pRenderClient)); if (FAILED(hr)) Log("WASAPIRenderFilter::InitAudioClient service initialization failed (0x%08x)", hr); else Log("WASAPIRenderer::InitAudioClient service initialization success"); if (m_pSettings->m_bWASAPIUseEventMode) { hr = m_pAudioClient->SetEventHandle(m_hDataEvent); if (FAILED(hr)) { Log("WASAPIRenderFilter::InitAudioClient SetEventHandle failed (0x%08x)", hr); SAFE_DELETE_WAVEFORMATEX(pwfxAccepted); return hr; } } REFERENCE_TIME latency(0); m_pAudioClient->GetStreamLatency(&latency); Log("WASAPIRenderFilter::InitAudioClient device reported latency %I64u ms - buffer based latency %I64u ms", latency / 10000, Latency() / 10000); // Dynamic format change requires restart for the audio client if (m_state != StateStopped) StartAudioClient(); m_bDeviceInitialized = true; SAFE_DELETE_WAVEFORMATEX(pwfxAccepted); return hr; }