diff --git a/audio/sysvad/EndpointsCommon/mintopo.cpp b/audio/sysvad/EndpointsCommon/mintopo.cpp index 28ddf4199..4f2bf4fab 100644 --- a/audio/sysvad/EndpointsCommon/mintopo.cpp +++ b/audio/sysvad/EndpointsCommon/mintopo.cpp @@ -570,7 +570,7 @@ Return Value: // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY // pDesc->JackCapabilities = JackCapabilities; - + ntStatus = STATUS_SUCCESS; } } diff --git a/audio/sysvad/EndpointsCommon/mintopo.h b/audio/sysvad/EndpointsCommon/mintopo.h index 4aa3d2de4..266fc81d5 100644 --- a/audio/sysvad/EndpointsCommon/mintopo.h +++ b/audio/sysvad/EndpointsCommon/mintopo.h @@ -73,17 +73,17 @@ class CMiniportTopology : NTSTATUS PropertyHandlerJackDescription ( - _In_ PPCPROPERTY_REQUEST PropertyRequest, - _In_ ULONG cJackDescriptions, - _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION *JackDescriptions ); NTSTATUS PropertyHandlerJackDescription2 ( - _In_ PPCPROPERTY_REQUEST PropertyRequest, - _In_ ULONG cJackDescriptions, - _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions, - _In_ DWORD JackCapabilities + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION *JackDescriptions, + _In_ DWORD JackCapabilities ); #if defined(SYSVAD_BTH_BYPASS) || defined(SYSVAD_USB_SIDEBAND) diff --git a/audio/sysvad/EndpointsCommon/minwavertstream.cpp b/audio/sysvad/EndpointsCommon/minwavertstream.cpp index 93ecdcf33..499d5b38e 100644 --- a/audio/sysvad/EndpointsCommon/minwavertstream.cpp +++ b/audio/sysvad/EndpointsCommon/minwavertstream.cpp @@ -968,12 +968,12 @@ NTSTATUS CMiniportWaveRTStream::GetReadPacket // Return next packet number to be read *PacketNumber = availablePacketNumber; - // Compute and return timestamp corresponding to start of the available packet. In a real hardware + // Compute and return timestamp corresponding to the end of the available packet. In a real hardware // driver, the timestamp would be computed in a driver and hardware specific manner. In this sample // driver, it is extrapolated from the sample driver's internal simulated position correlation // [m_ullLinearPosition @ m_ullDmaTimeStamp] and the sample's internal 64-bit packet counter, subtracting // 1 from the packet counter to compute the time at the start of that last completed packet. - ULONGLONG linearPositionOfAvailablePacket = (packetCounter - 1) * (m_ulDmaBufferSize / m_ulNotificationsPerBuffer); + ULONGLONG linearPositionOfAvailablePacket = packetCounter * (m_ulDmaBufferSize / m_ulNotificationsPerBuffer); // Need to divide by (1000 * 10000 because m_ulDmaMovementRate is average bytes per sec ULONGLONG carryForwardBytes = (hnsElapsedTimeCarryForward * m_ulDmaMovementRate) / 10000000; ULONGLONG deltaLinearPosition = ullLinearPosition + carryForwardBytes - linearPositionOfAvailablePacket; diff --git a/audio/sysvad/EndpointsCommon/speakertopo.cpp b/audio/sysvad/EndpointsCommon/speakertopo.cpp index cca4dac94..fee3d5428 100644 --- a/audio/sysvad/EndpointsCommon/speakertopo.cpp +++ b/audio/sysvad/EndpointsCommon/speakertopo.cpp @@ -22,7 +22,6 @@ Module Name: #pragma code_seg("PAGE") - //============================================================================= NTSTATUS PropertyHandler_SpeakerTopoFilter diff --git a/audio/sysvad/EndpointsCommon/speakertoptable.h b/audio/sysvad/EndpointsCommon/speakertoptable.h index 50f58c7b8..eb12173a0 100644 --- a/audio/sysvad/EndpointsCommon/speakertoptable.h +++ b/audio/sysvad/EndpointsCommon/speakertoptable.h @@ -85,7 +85,7 @@ PCPIN_DESCRIPTOR SpeakerTopoMiniportPins[] = //============================================================================= static -KSJACK_DESCRIPTION SpeakerJackDescSpeakers = +KSJACK_DESCRIPTION SpeakerJackDescBridge = { KSAUDIO_SPEAKER_STEREO, 0xB3C98C, // Color spec for green @@ -101,7 +101,7 @@ static PKSJACK_DESCRIPTION SpeakerJackDescriptions[] = { NULL, - &SpeakerJackDescSpeakers + &SpeakerJackDescBridge }; //============================================================================= @@ -120,17 +120,20 @@ PCPROPERTY_ITEM PropertiesSpeakerTopoFilter[] = { &KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION, - KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + KSPROPERTY_TYPE_GET | + KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_SpeakerTopoFilter }, { &KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2, - KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + KSPROPERTY_TYPE_GET | + KSPROPERTY_TYPE_BASICSUPPORT, PropertyHandler_SpeakerTopoFilter } }; + DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerTopoFilter, PropertiesSpeakerTopoFilter); //============================================================================= @@ -152,4 +155,4 @@ PCFILTER_DESCRIPTOR SpeakerTopoMiniportFilterDescriptor = }; #endif // _SYSVAD_SPEAKERTOPTABLE_H_ - + diff --git a/audio/sysvad/Package/package.VcxProj b/audio/sysvad/Package/package.VcxProj index cef0bc336..0b47036c5 100644 --- a/audio/sysvad/Package/package.VcxProj +++ b/audio/sysvad/Package/package.VcxProj @@ -1,4 +1,4 @@ - + @@ -109,4 +109,4 @@ - \ No newline at end of file + diff --git a/audio/sysvad/TabletAudioSample/ComponentizedApoSample.inx b/audio/sysvad/TabletAudioSample/ComponentizedApoSample.inx index a6bfb4aff..be9428fba 100644 Binary files a/audio/sysvad/TabletAudioSample/ComponentizedApoSample.inx and b/audio/sysvad/TabletAudioSample/ComponentizedApoSample.inx differ diff --git a/audio/sysvad/TabletAudioSample/ComponentizedAudioSample.inx b/audio/sysvad/TabletAudioSample/ComponentizedAudioSample.inx index a5e6e73f5..ccaa73273 100644 Binary files a/audio/sysvad/TabletAudioSample/ComponentizedAudioSample.inx and b/audio/sysvad/TabletAudioSample/ComponentizedAudioSample.inx differ diff --git a/audio/sysvad/TabletAudioSample/ComponentizedAudioSampleExtension.inx b/audio/sysvad/TabletAudioSample/ComponentizedAudioSampleExtension.inx index ce3ed3355..1c89827ad 100644 Binary files a/audio/sysvad/TabletAudioSample/ComponentizedAudioSampleExtension.inx and b/audio/sysvad/TabletAudioSample/ComponentizedAudioSampleExtension.inx differ diff --git a/audio/sysvad/TabletAudioSample/tabletaudiosample.inx b/audio/sysvad/TabletAudioSample/tabletaudiosample.inx index ba96faeed..58ee4d880 100644 Binary files a/audio/sysvad/TabletAudioSample/tabletaudiosample.inx and b/audio/sysvad/TabletAudioSample/tabletaudiosample.inx differ diff --git a/audio/sysvad/sysvad.sln b/audio/sysvad/sysvad.sln index 1935f7493..6a58b6df1 100644 --- a/audio/sysvad/sysvad.sln +++ b/audio/sysvad/sysvad.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27703.2042 diff --git a/audio/tests/KSPosTst/KsPosTestTaef.cpp b/audio/tests/KSPosTst/KsPosTestTaef.cpp new file mode 100644 index 000000000..b92f9e4f4 --- /dev/null +++ b/audio/tests/KSPosTst/KsPosTestTaef.cpp @@ -0,0 +1,215 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// File Name: +// +// KsPosTestTaef.cpp +// +// Abstract: +// +// Implementation file for KsPosTestTaef +// +// ------------------------------------------------------------------------------- +#include "PreComp.h" +#include "KsPosTestTaef.h" +#include "tests.h" + +using namespace WEX::Common; +using namespace WEX::Logging; +using namespace WEX::TestExecution; + +#define RUN_TEST_CASE(testfn) \ +{ \ + SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures); \ + testfn(); \ +} + +IBasicLog * g_pBasicLog = NULL; +WDMAudio::KsPosTest* g_pKsPosTst = NULL; + +TIMER_MECHANISM g_Timer = +{ + tpQPC, "QueryPerformanceCounter" +}; + +void LogWaveFormat(WAVEFORMATEX* pwfx) +{ + WAVEFORMATEXTENSIBLE* wfex = (WAVEFORMATEXTENSIBLE*)pwfx; + + switch (pwfx->wFormatTag) + { + case WAVE_FORMAT_PCM: + LOG(g_pBasicLog, L" wFormatTag = WAVE_FORMAT_PCM"); + break; + case WAVE_FORMAT_IEEE_FLOAT: + LOG(g_pBasicLog, L" wFormatTag = WAVE_FORMAT_IEEE_FLOAT"); + break; + case WAVE_FORMAT_EXTENSIBLE: + LOG(g_pBasicLog, L" wFormatTag = WAVE_FORMAT_EXTENSIBLE"); + if (wfex->SubFormat.Data1 == WAVE_FORMAT_PCM) + LOG(g_pBasicLog, L" SubFormat = WAVE_FORMAT_PCM"); + if (wfex->SubFormat.Data1 == WAVE_FORMAT_IEEE_FLOAT) + LOG(g_pBasicLog, L" SubFormat = WAVE_FORMAT_IEEE_FLOAT"); + break; + default: + LOG(g_pBasicLog, L" wFormatTag = UNKNOWN!"); + } + LOG(g_pBasicLog, L" nChannel = %d", pwfx->nChannels); + LOG(g_pBasicLog, L" nSamplesPerSec = %d", pwfx->nSamplesPerSec); + LOG(g_pBasicLog, L" nAvgBytesPerSe = %d", pwfx->nAvgBytesPerSec); + LOG(g_pBasicLog, L" nBlockAlign = %d", pwfx->nBlockAlign); + LOG(g_pBasicLog, L" wBitsPerSample = %d", pwfx->wBitsPerSample); + + + if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + LOG(g_pBasicLog, L" wValidBitsPerSample = %d", wfex->Samples.wValidBitsPerSample); + LOG(g_pBasicLog, L" dwChannelMask = %hd", wfex->dwChannelMask); + } +} + +// ------------------------------------------------------------------------------- +namespace WDMAudio +{ + BEGIN_MODULE() + MODULE_PROPERTY(L"Feature", L"PortCls HCK Tests") + MODULE_PROPERTY(L"TestResourceDependent", L"true") + END_MODULE() + + // Create WEXBasicLog, Coinitialize Etc + MODULE_SETUP(WDMAudioSetup) + + // Release stuff acquired in WDMAudioSetup + MODULE_CLEANUP(WDMAudioCleanup) +}; + +bool WDMAudio::WDMAudioSetup() +{ + if (NULL == g_pBasicLog) + { + if (!(VERIFY_SUCCEEDED(CreateWexBasicLog(&g_pBasicLog)))) + { + return false; + } + } + + return true; +} + +bool WDMAudio::WDMAudioCleanup() +{ + if (g_pBasicLog) + { + g_pBasicLog->Release(); + } + + return true; +} + +// ------------------------------------------------------------------------------- +bool WDMAudio::KsPosTest::KsPosTestSetup() +{ + g_pKsPosTst = this; + m_pTimer = &g_Timer; + // The histograms can be displayed by using the command line parameter checked below + // If the parameter is not specified, the default is to not log anything + bool param = true; + WEX::TestExecution::RuntimeParameters::TryGetValue(L"logHistograms", param); + m_fLogHistograms = param; + return true; +} + +bool WDMAudio::KsPosTest::KsPosTestCleanUp() +{ + g_pKsPosTst = NULL; + + return true; +} + +// ------------------------------------------------------------------------------- +bool WDMAudio::KsPosTest::TestCaseSetup() +{ + CComPtr spResource; + CComQIPtr spHalfContainer; + + SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures); + + Log::Comment(L""); + Log::Comment(L"------------------------------------------------------"); + Log::Comment(L"Running test case setup"); + + size_t count = Resources::Count(); + if (!VERIFY_ARE_EQUAL(count, (size_t)1)) { return false; } + if (!VERIFY_SUCCEEDED(Resources::Item(0, &spResource))) { return false; } + + // 1. Assign m_pHalf + spHalfContainer = spResource; + if (!VERIFY_IS_NOT_NULL(spHalfContainer)) { return false; } + if (!VERIFY_SUCCEEDED(spHalfContainer->GetHalfApp(&m_pHalf))) { return false; } + + // 2. Check if this is a loopback pin. If so, assign the host pin's HalfApp + if (m_pHalf->m_ConnectorType == eLoopbackConnector) + { + VERIFY_SUCCEEDED(m_pHalf->GetHostHalfApp(&m_pHostHalf)); + } + else + { + m_pHostHalf = NULL; + } + + Log::Comment(L"The test will run on the following format:"); + if (m_pHostHalf != NULL) + { + LogWaveFormat(m_pHostHalf->m_pCurrentFormat.get()); + } + else + { + LogWaveFormat(m_pHalf->m_pCurrentFormat.get()); + } + + return true; +} + +bool WDMAudio::KsPosTest::TestCaseCleanUp() +{ + // Stop the timer here, in case any latency test fails + NtSetTimerResolution(0, FALSE, &actualTimerResolution); + + if (!CompareWaveFormat(m_pHalf->m_pCurrentFormat.get(), m_pHalf->m_pPreferredFormat.get())) { + CloneWaveFormat(m_pHalf->m_pPreferredFormat.get(), wil::out_param(m_pHalf->m_pCurrentFormat)); + m_pHalf->m_u32CurrentDefaultPeriodicityInFrames = m_pHalf->m_u32DefaultPeriodicityInFrames; + m_pHalf->m_u32CurrentFundamentalPeriodicityInFrames = m_pHalf->m_u32FundamentalPeriodicityInFrames; + m_pHalf->m_u32CurrentMinPeriodicityInFrames = m_pHalf->m_u32MinPeriodicityInFrames; + m_pHalf->m_u32CurrentMaxPeriodicityInFrames = m_pHalf->m_u32MaxPeriodicityInFrames; + } + + if (m_pHostHalf != NULL) { + if (!VERIFY_SUCCEEDED(m_pHostHalf->CleanupStream())) { return false; } + if (!VERIFY_SUCCEEDED(m_pHostHalf->ReleaseEndpoint())) { return false; } + if (!VERIFY_SUCCEEDED(m_pHostHalf->ReleaseSineToneDataBuffer())) { return false; } + delete m_pHostHalf; + m_pHostHalf = NULL; + } + + if (m_pHalf != NULL) { + if (!VERIFY_SUCCEEDED(m_pHalf->CleanupStream())) { return false; } + if (!VERIFY_SUCCEEDED(m_pHalf->ReleaseEndpoint())) { return false; } + if (!VERIFY_SUCCEEDED(m_pHalf->ReleaseSineToneDataBuffer())) { return false; } + + m_pHalf = NULL; + } + + return true; +} + +// ------------------------------------------------------------------------------- +void WDMAudio::KsPosTest::TAEF_DriftAndJitter() +{ + RUN_TEST_CASE(Test_DriftAndJitter_Default); +} + +void WDMAudio::KsPosTest::TAEF_Latency() +{ + RUN_TEST_CASE(Test_Latency_Default); +} diff --git a/audio/tests/KSPosTst/PreComp.h b/audio/tests/KSPosTst/PreComp.h new file mode 100644 index 000000000..5fdfc9db6 --- /dev/null +++ b/audio/tests/KSPosTst/PreComp.h @@ -0,0 +1,108 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// File Name: +// +// PreComp.h +// +// Abstract: +// +// Precompiled common headers +// +// ------------------------------------------------------------------------------- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// Number of milliseconds that the test will be delayed to wait for the stream to stabilize +#define DELAY_FOR_STABILIZE 100 + +// Number of milliseconds in a second +#define MILLISECONDS_PER_SECOND 1000 + +// Number of HNS units in one second +#define HNSTIME_UNITS_PER_SECOND 10000000 + +// Number of HNS units in one millisecond +#define HNSTIME_UNITS_PER_MILLISECOND 10000 + +typedef double (*TIMEPROC) (void); +double tpQPC(void); + +typedef struct _tag_tm +{ + TIMEPROC Proc; + LPCSTR strRep; +} TIMER_MECHANISM, * PTIMER_MECHANISM; + +// ---------------------------------------------------------- +// Timer functions +extern "C" __kernel_entry NTSYSCALLAPI NTSTATUS NTAPI NtSetTimerResolution( + _In_ ULONG DesiredTime, _In_ BOOLEAN SetResolution, _Out_ PULONG ActualTime); + +// 1 ms timer resolution +static ULONG timerResolution = (1 * 10000); +static ULONG actualTimerResolution; + +// ------------------------------------------------------------------------------ +// structs used to hold results of perf tests +#define DATA_POINTS 100 + +typedef struct +{ + double testTime; // Time the sample was taken, in milliseconds, relative to the start of the sample collection + double positionTime; // Time of the reported position, in milliseconds, relative to the start of the sample collection + double position; // Position, in seconds, relative to the start of the sample collection +} POSITION_SET, * PPOSITION_SET; + +typedef struct +{ + PPOSITION_SET argPosSets; + ULONG cPosSets; + ULONG ctLatencyMeas; + ULONG csBufferMeas; + double bytesPerSecond; // measured (in bytes/sec, NOT samples/sec) + double dOffset; // measured +} PERF_RESULTS, * PPERF_RESULTS; + +typedef struct +{ + WAVEFORMATEXTENSIBLE wfxFormat; + PERF_RESULTS perfResults; // +} FORMAT_ENTRY, * PFORMAT_ENTRY; diff --git a/audio/tests/KSPosTst/TestResourceBuild.cpp b/audio/tests/KSPosTst/TestResourceBuild.cpp new file mode 100644 index 000000000..93959089c --- /dev/null +++ b/audio/tests/KSPosTst/TestResourceBuild.cpp @@ -0,0 +1,522 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// File Name: +// +// TestResourceBuild.cpp +// +// Abstract: +// +// TAEF BuildResourceList implementation +// +// ------------------------------------------------------------------------------ +#include "PreComp.h" +#include +#include + +using namespace WEX::Common; +using namespace WEX::Logging; +using namespace WEX::TestExecution; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// CreateTestResource +// +// Create the test resource with MMDevice, device id, device name, data flow, connector type, connector id, mode, +// list of formats +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT CreateTestResource +( + ResourceList& resourceList, + DeviceDescriptor descriptor +) +{ + HRESULT hr = S_OK; + + CComHeapPtr spHalfApp; + wil::com_ptr_nothrow spTestResource; + GUID ResourceGUID; + CComBSTR szResourceName; + + // Create HalfApp + spHalfApp.Attach(new CHalfApp(descriptor)); + if (!VERIFY_IS_NOT_NULL(spHalfApp)) { + hr = E_OUTOFMEMORY; + return hr; + } + + // Create PinTestResource + if (!VERIFY_SUCCEEDED(hr = CoCreateGuid(&ResourceGUID))) { + return hr; + } + if (!VERIFY_SUCCEEDED(hr = CPinTestResource::CreateInstance( + spHalfApp, ResourceGUID, &spTestResource))) { + return hr; + } + + // Add to resource list + spHalfApp.Detach(); + if (!VERIFY_SUCCEEDED(hr = resourceList.Add(spTestResource.get()))) { + return hr; + } + if (!VERIFY_SUCCEEDED(hr = spTestResource->GetValue(CComBSTR(TestResourceProperty::c_szName), &szResourceName))) { + return hr; + } + Log::Comment(String().Format(L"Test Resource (%s) added", (PCWSTR)szResourceName)); + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// GetProcessingModesForConnector +// +// For host/keyword detector pin, processing mode info is cached in property store and can be directly read. For offload +// pin, query for ks processing mode property. For loopback pin, it doesn't support any processing modes, return GUID_NULL. +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT GetProcessingModesForConnector +( + IMMDevice* pDevice, + UINT uConnectorId, + EndpointConnectorType eConnectorType, + ULONG *pCount, + AUDIO_SIGNALPROCESSINGMODE **ppModes +) +{ + HRESULT hr = S_OK; + + *pCount = 0; + *ppModes = NULL; + + if (eConnectorType == eHostProcessConnector || eConnectorType == eKeywordDetectorConnector) { + if (!VERIFY_SUCCEEDED(hr = GetCachedProcessingModes(pDevice, eConnectorType, pCount, ppModes))) { + return hr; + } + } + else if (eConnectorType == eOffloadConnector) { + if (!VERIFY_SUCCEEDED(hr = GetProcessingModes(pDevice, uConnectorId, pCount, ppModes))) { + return hr; + } + } + else if (eConnectorType == eLoopbackConnector) { + // Loopback pin doesn't support any processing modes, so put 1 GUID_NULL + CComHeapPtr spModes; + + if (!spModes.Allocate(1)) + return E_OUTOFMEMORY; + + spModes[0] = GUID_NULL; + + *pCount = 1; + *ppModes = spModes.Detach(); + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// GetDefaultFormatForConnector +// +// Read audio engine device format from property store as default format. +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT GetDefaultFormatForConnector +( + IMMDevice* pDevice, + EndpointConnectorType eConnectorType, + WAVEFORMATEX **ppDefaultFormat +) +{ + HRESULT hr = S_OK; + + *ppDefaultFormat = NULL; + if (!VERIFY_SUCCEEDED(hr = GetCachedDefaultFormat(pDevice, eConnectorType, ppDefaultFormat))){ + return hr; + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// GetSupportedFormatsForConnector +// +// For host/keyword detector pin, supported formats info is cached in property store and can be directly read. For offload +// pin, provide with a predefined list of formats and check whether format is supported. For loopback pin, it matches the format +// of host pin so put 0 format record. +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT GetSupportedFormatRecordsForConnector +( + IMMDevice* pDevice, + UINT uConnectorId, + EndpointConnectorType eConnectorType, + AUDIO_SIGNALPROCESSINGMODE mode, + STACKWISE_DATAFLOW dataFlow, + ULONG *pCount, + FORMAT_RECORD **ppFormatRecords +) +{ + HRESULT hr = S_OK; + + *pCount = 0; + *ppFormatRecords = NULL; + + if (eConnectorType == eHostProcessConnector || eConnectorType == eKeywordDetectorConnector) { + if (!VERIFY_SUCCEEDED(hr = GetCachedSupportedFormatRecords(pDevice, eConnectorType, mode, pCount, ppFormatRecords))) { + return hr; + } + } + else if (eConnectorType == eOffloadConnector) { + if (!VERIFY_SUCCEEDED(hr = GetSupportedFormatRecords(pDevice, uConnectorId, eConnectorType, mode, dataFlow, pCount, ppFormatRecords))) { + return hr; + } + } + else if (eConnectorType == eLoopbackConnector) { + *pCount = 0; + *ppFormatRecords = NULL; + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// GetPreferredFormatForConnector +// +// If KSPROPERTY_PIN_PROPOSEDATAFORMAT2 is supported, use the proposed format for mode as preferred format for mode. +// Otherwise, use default format as preferred format. +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT GetPreferredFormatForConnector +( + IMMDevice* pDevice, + UINT uConnectorId, + EndpointConnectorType eConnectorType, + AUDIO_SIGNALPROCESSINGMODE mode, + WAVEFORMATEX **ppPreferredFormat +) +{ + HRESULT hr = S_OK; + wil::unique_cotaskmem_ptr pDefaultFormat; + wil::unique_cotaskmem_ptr pProposedFormat; + + *ppPreferredFormat = NULL; + + + // Get default format for connector + if (!VERIFY_SUCCEEDED(hr = GetDefaultFormatForConnector(pDevice, eConnectorType, wil::out_param(pDefaultFormat)))) { + return hr; + } + + // Return the processing mode specific format proposed by the driver + hr = GetProposedFormatForProcessingMode(pDevice, uConnectorId, mode, wil::out_param(pProposedFormat)); + if (hr == S_OK) { + CloneWaveFormat(pProposedFormat.get(), ppPreferredFormat); + } + else { + CloneWaveFormat(pDefaultFormat.get(), ppPreferredFormat); + hr = S_OK; + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// GetPreferredFormatPeriodicityCharacteristicsForConnector +// +// Get periodicity characteristics for format. For host/keyword detector pin, periodicity info is cached along with format +// in property store and can be searched from supported format list. For offload pin, it should be calculate from +// DiscoverPeriodicityCharacteristicsForFormat. +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT GetPreferredFormatPeriodicityCharacteristicsForConnector +( + IMMDevice* pDevice, + EndpointConnectorType eConnectorType, + AUDIO_SIGNALPROCESSINGMODE mode, + STACKWISE_DATAFLOW dataFlow, + WAVEFORMATEX *pPreferredFormat, + ULONG cFormatRecords, + PFORMAT_RECORD pFormatRecords, + UINT32 *pDefaultPeriodicityInFrames, + UINT32 *pFundamentalPeriodicityInFrames, + UINT32 *pMinPeriodicityInFrames, + UINT32 *pMaxPeriodicityInFrames, + UINT32 *pMaxPeriodicityInFramesExtended +) +{ + HRESULT hr = S_OK; + bool bFormatInList = false; + + for (ULONG i = 0; i < cFormatRecords; i++) { + if (CompareWaveFormat((WAVEFORMATEX *)&pFormatRecords[i].wfxEx, pPreferredFormat)) { + bFormatInList = true; + *pDefaultPeriodicityInFrames = pFormatRecords[i].defaultPeriodInFrames; + *pFundamentalPeriodicityInFrames = pFormatRecords[i].fundamentalPeriodInFrames; + *pMinPeriodicityInFrames = pFormatRecords[i].minPeriodInFrames; + *pMaxPeriodicityInFrames = pFormatRecords[i].maxPeriodInFrames; + *pMaxPeriodicityInFramesExtended = pFormatRecords[i].maxPeriodInFramesExtended; + break; + } + } + + if (eConnectorType == eHostProcessConnector || eConnectorType == eKeywordDetectorConnector) { + if (!VERIFY_IS_TRUE(bFormatInList)) { + hr = E_NOTFOUND; + return hr; + } + } + else if (eConnectorType == eOffloadConnector || eConnectorType == eLoopbackConnector) { + if (!VERIFY_SUCCEEDED(hr = DiscoverPeriodicityCharacteristicsForFormat(pDevice, eConnectorType, mode, pPreferredFormat, dataFlow, pDefaultPeriodicityInFrames, pFundamentalPeriodicityInFrames, pMinPeriodicityInFrames, pMaxPeriodicityInFrames, pMaxPeriodicityInFramesExtended))) { + return hr; + } + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// AddTestResourceForConnector +// +// For each connector, read connector id, get all processing modes, identify default and preferred format for each mode +// and also enumerate a list of supported formats for each mode. Store all the infos in test resource. +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT AddTestResourceForConnector +( + ResourceList& resourceList, + LPWSTR deviceId, + LPWSTR deviceName, + IMMDevice* pDevice, + EndpointConnectorType eConnectorType, + STACKWISE_DATAFLOW dataFlow +) +{ + HRESULT hr = S_OK; + bool bHasConnector = false; + UINT uConnectorId; + ULONG cModes = 0; + CComHeapPtr spModes; + AUDIO_SIGNALPROCESSINGMODE mode; + ULONG cFormatRecords = 0; + CComHeapPtr spFormatRecords; + wil::unique_cotaskmem_ptr pPreferredFormat; + UINT32 u32DefaultPeriodicityInFrames; + UINT32 u32FundamentalPeriodicityInFrames; + UINT32 u32MinPeriodicityInFrames; + UINT32 u32MaxPeriodicityInFrames; + UINT32 u32MaxPeriodicityInFramesExtended; + bool isAVStream = false; + bool isBluetooth = false; + bool isSideband = false; + + // Get connector id + if (!VERIFY_SUCCEEDED(hr = GetConnectorId(pDevice, eConnectorType, &bHasConnector, &uConnectorId))) { + return hr; + } + if (!bHasConnector) { + return hr; + } + + Log::Comment(String().Format(L"Adding Test Resource for pin [%u]:", (uConnectorId & PARTID_MASK))); + + + // Get all signal processing modes for connector + if (!VERIFY_SUCCEEDED(hr = GetProcessingModesForConnector(pDevice, uConnectorId, eConnectorType, &cModes, &spModes))) { + return hr; + } + + // Loop through each mode, get preferred format and list of formats and create test resource for each mode + for (ULONG i = 0; i < cModes; i++) { + + mode = spModes[i]; + + if (!VERIFY_SUCCEEDED(hr = GetSupportedFormatRecordsForConnector(pDevice, uConnectorId, eConnectorType, mode, dataFlow, &cFormatRecords, &spFormatRecords))) { + return hr; + } + + if (!VERIFY_SUCCEEDED(hr = GetPreferredFormatForConnector(pDevice, uConnectorId, eConnectorType, mode, wil::out_param(pPreferredFormat)))) { + return hr; + } + + if (!VERIFY_SUCCEEDED(hr = GetPreferredFormatPeriodicityCharacteristicsForConnector(pDevice, eConnectorType, mode, dataFlow, pPreferredFormat.get(), cFormatRecords, spFormatRecords, &u32DefaultPeriodicityInFrames, &u32FundamentalPeriodicityInFrames, &u32MinPeriodicityInFrames, &u32MaxPeriodicityInFrames, &u32MaxPeriodicityInFramesExtended))) { + return hr; + } + + // Check if audio endpoint is AVStream + if (!VERIFY_SUCCEEDED(hr = IsAVStream(pDevice, &isAVStream))) { + return hr; + } + + // Check if audio endpoint is Bluetooth + if (!VERIFY_SUCCEEDED(hr = IsBluetooth(pDevice, &isBluetooth))) { + return hr; + } + + // Check if audio endpoint is side band + if (!VERIFY_SUCCEEDED(hr = IsSideband(pDevice, &isSideband))) { + return hr; + } + + DeviceDescriptor descriptor = { 0 }; + descriptor.pDevice = pDevice; + descriptor.pwstrAudioEndpointId = deviceId; + descriptor.pwstrAudioEndpointFriendlyName = deviceName; + descriptor.dataFlow = dataFlow; + descriptor.eConnectorType = eConnectorType; + descriptor.uConnectorId = uConnectorId; + descriptor.mode = mode; + descriptor.cModes = cModes; + descriptor.pModes = spModes; + descriptor.cFormatRecords = cFormatRecords; + descriptor.pFormatRecords = spFormatRecords; + descriptor.pPreferredFormat = pPreferredFormat.get(); + descriptor.u32DefaultPeriodicityInFrames = u32DefaultPeriodicityInFrames; + descriptor.u32FundamentalPeriodicityInFrames = u32FundamentalPeriodicityInFrames; + descriptor.u32MinPeriodicityInFrames = u32MinPeriodicityInFrames; + descriptor.u32MaxPeriodicityInFrames = u32MaxPeriodicityInFrames; + descriptor.bIsAVStream = isAVStream; + descriptor.bIsBluetooth = isBluetooth; + descriptor.bIsSideband = isSideband; + + if (!VERIFY_SUCCEEDED(hr = CreateTestResource(resourceList, descriptor))) { + return hr; + } + + spFormatRecords.Free(); + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// AddTestResourcesForDevice +// +// Identify the existence of all pin types and add test resources for each pin +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT AddTestResourcesForDevice +( + ResourceList& resourceList, + LPWSTR deviceId, + LPWSTR deviceName, + STACKWISE_DATAFLOW dataFlow +) +{ + HRESULT hr = S_OK; + wil::com_ptr_nothrow spEnumerator; + wil::com_ptr_nothrow spDevice; + + Log::Comment(String().Format(L"Adding Test Resource for Device [%s]:", deviceName)); + + // Read the default device format from the property store + if (!VERIFY_SUCCEEDED(hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&spEnumerator))) { + return hr; + } + if (!VERIFY_SUCCEEDED(hr = spEnumerator->GetDevice(deviceId, &spDevice))) { + return hr; + } + + // Identify existence of host pin. Add test resources for host pin. + if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eHostProcessConnector, dataFlow))) { + return hr; + } + + // Identify existence of offload pin. Add test resources for offload pin. + if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eOffloadConnector, dataFlow))) { + return hr; + } + + // Identify existence of loopback pin. Add test resources for loopback pin. + if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eLoopbackConnector, capture ))) { + return hr; + } + + /* + // Identify existence of keyword detector pin. Add test resources for keyword detector pin. + if (!VERIFY_SUCCEEDED(hr = AddTestResourceForConnector(resourceList, deviceId, deviceName, spDevice.get(), eKeywordDetectorConnector, dataFlow))) { + return hr; + } + */ + + return hr; +} + +HRESULT __cdecl BuildResourceList(ResourceList& resourceList) +{ + HRESULT hr = S_OK; + wil::com_ptr_nothrow spEnumerator; + wil::com_ptr_nothrow spRenderEndpoints; + wil::com_ptr_nothrow spCaptureEndpoints; + wil::com_ptr_nothrow spEndpoint; + UINT cRenderDevices = 0; + UINT cCaptureDevices = 0; + UINT i = 0; + wil::unique_cotaskmem_string id; + wil::unique_cotaskmem_string friendlyName; + + SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures); + DisableVerifyExceptions disable; + + VERIFY_SUCCEEDED(::CoInitializeEx(NULL, COINIT_MULTITHREADED)); + + Log::Comment(L"In BuildResourceList"); + + // Create IMMDevice Enumerator + VERIFY_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&spEnumerator)); + + // Enumerate all render endpoints + VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &spRenderEndpoints)); + VERIFY_SUCCEEDED(spRenderEndpoints->GetCount(&cRenderDevices)); + + // Enumerate all capture endpoints + VERIFY_SUCCEEDED(spEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &spCaptureEndpoints)); + VERIFY_SUCCEEDED(spCaptureEndpoints->GetCount(&cCaptureDevices)); + + if (!VERIFY_IS_TRUE(cRenderDevices | cCaptureDevices)) + { + hr = E_FAIL; + Log::Comment(L"No device was found!"); + goto Exit; + } + + Log::Comment(String().Format(L"Found %d viable rendering device!", cRenderDevices)); + // Add test resources for render endpoints + if (cRenderDevices) { + for (i = 0; i < cRenderDevices; i++) { + VERIFY_SUCCEEDED(spRenderEndpoints->Item(i, &spEndpoint)); + VERIFY_SUCCEEDED(spEndpoint->GetId(&id)); + VERIFY_SUCCEEDED(GetEndpointFriendlyName(spEndpoint.get(), &friendlyName)); + + Log::Comment(String().Format(L"\\\\ Device: %s (%s)", friendlyName.get(), id.get())); + VERIFY_SUCCEEDED(AddTestResourcesForDevice(resourceList, id.get(), friendlyName.get(), render)); + } + } + + Log::Comment(String().Format(L"Found %d viable capture device!", cCaptureDevices)); + // Add test resources for capture endpoints + if (cCaptureDevices) { + for (i = 0; i < cCaptureDevices; i++) { + VERIFY_SUCCEEDED(spCaptureEndpoints->Item(i, &spEndpoint)); + VERIFY_SUCCEEDED(spEndpoint->GetId(&id)); + VERIFY_SUCCEEDED(GetEndpointFriendlyName(spEndpoint.get(), &friendlyName)); + + Log::Comment(String().Format(L"\\\\ Device: %s (%s)", friendlyName.get(), id.get())); + VERIFY_SUCCEEDED(AddTestResourcesForDevice(resourceList, id.get(), friendlyName.get(), capture)); + } + } + + Log::Comment(L"Resource enumeration complete."); + Log::Comment(String().Format(L"Enumerated %u resources", resourceList.Count())); + +Exit: + return hr; +} diff --git a/audio/tests/KSPosTst/locallimits.h b/audio/tests/KSPosTst/locallimits.h new file mode 100644 index 000000000..60336eb50 --- /dev/null +++ b/audio/tests/KSPosTst/locallimits.h @@ -0,0 +1,25 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// Module Name: +// +// locallimits.h +// +// Abstract: +// +// defines the tolerances for WHQL failures for this test app +// +// ------------------------------------------------------------------------------- + +// max latency for rendering or capturing is 20 ms +#define LATENCY_THRESHOLD 20. + +// Real time sampling rates must be within 1% of the theoretical rate, +// per the format, as required by the PC98/PC99 HW Design Guide. +#define SAMPLERATE_THRESHOLD 1.0 + +// audio devices are required to be SAMPLEACCURATE (PC9x HW Design Guide).. +// but for some reason we are making them ms accurate?? +#define JITTER_STD_THRESHOLD 1.0 // 1 ms max allowed for std-deviation +#define JITTER_MAX_THRESHOLD 4.0 // 4 ms max allowed for max deviation diff --git a/audio/tests/KSPosTst/pintest.cpp b/audio/tests/KSPosTst/pintest.cpp new file mode 100644 index 000000000..57e35b07c --- /dev/null +++ b/audio/tests/KSPosTst/pintest.cpp @@ -0,0 +1,518 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// Module Name: +// +// pintest.cpp +// +// Abstract: +// +// Implementation file for test cases +// +// ------------------------------------------------------------------------------- + +#include "PreComp.h" +#include "KsPosTestTaef.h" +#include "tests.h" + +#include +#include + +#define DEFAULT_PERIODICITY 0 +#define DEFAULT_LATENCY_COEFFICIENT 0 +#define xGetTime g_pKsPosTst->m_pTimer->Proc + +// ------------------------------------ +// Test_DriftAndJitter helper functions +// ------------------------------------ + +HRESULT CollectSampleData +( + CHalfApp* pHalfApp, + PFORMAT_ENTRY pFormatEntry, + HNSTIME requestedPeriodicity +) +{ + DWORD periodicityInMs = (DWORD)ceil(HNSTIME_TO_MILLISECONDS_DOUBLE(requestedPeriodicity)); + + if (!pFormatEntry) + { + ERR(g_pBasicLog, "FAIL: Current Format Entry is NULL."); + return E_INVALIDARG; + } + + PPERF_RESULTS pResults = &pFormatEntry->perfResults; + PPOSITION_SET pPosSet = pResults->argPosSets; + double tStart = 0, tPre = 0, tPost = 0; + UINT i = 0; + UINT64 u64Position; + UINT64 positionHNS; + UINT64 u64Frequency; + double testStartHns; + LARGE_INTEGER liNow; + LARGE_INTEGER liFrequency; + QueryPerformanceFrequency(&liFrequency); + + VERIFY_SUCCEEDED(pHalfApp->InitializeEndpoint()); + VERIFY_SUCCEEDED(pHalfApp->CreateSineToneDataBuffer(pHalfApp->m_pCurrentFormat.get())); + VERIFY_SUCCEEDED(pHalfApp->InitializeStream(requestedPeriodicity, DEFAULT_LATENCY_COEFFICIENT)); + + // run the adapter sink pin + VERIFY_SUCCEEDED(pHalfApp->StartStream()); + + // Get a HNS reference from the begining of the test so the HNS from system and driver times won't be so large + QueryPerformanceCounter(&liNow); + testStartHns = (double)liNow.QuadPart * HNSTIME_UNITS_PER_SECOND / (double)liFrequency.QuadPart; + + tStart = xGetTime(); + Sleep(DELAY_FOR_STABILIZE); + // Get the stream frequency in order to determine the time offset + VERIFY_SUCCEEDED(pHalfApp->m_pAudioClock->GetFrequency(&u64Frequency)); + + // Start collecting the data + for (i = 0; i < DATA_POINTS;) + { + Sleep(periodicityInMs); + + tPre = xGetTime(); + VERIFY_SUCCEEDED(pHalfApp->GetPosition(&u64Position, &positionHNS)); + QueryPerformanceCounter(&liNow); + tPost = xGetTime(); + + if (u64Position > 0) + { + // only count this result if the GetPosition and GetTime happened relatively atomically + if ((tPost - tPre) < 1.) + { + double testHNS = (double)liNow.QuadPart * HNSTIME_UNITS_PER_SECOND / (double)liFrequency.QuadPart; + // In both sampling rate and jitter calculation, the time used is in milliseconds, so we can convert it right here + pPosSet[i].testTime = HNSTIME_TO_MILLISECONDS_DOUBLE(testHNS - testStartHns); + pPosSet[i].positionTime = HNSTIME_TO_MILLISECONDS_DOUBLE(positionHNS - testStartHns); + // We store the values as seconds because it is easier to convert to bytes per second, on sampling rate calculation, and milliseconds, on jitter calculation. + pPosSet[i].position = (double)u64Position / (double)u64Frequency; + + // Ignore repeated positions (that means they have the same QPC and should have the same position value) + if ((i > 0) && (pPosSet[i - 1].positionTime == pPosSet[i].positionTime)) + { + // If the position is the same, decrement the count so that sample gets discarded by overwriting it + i--; + WARN(g_pBasicLog, "The same position was reported at %f ms: pos = %f seconds", pPosSet[i].testTime, pPosSet[i].position); + } + i++; + } + else + { + LOG(g_pBasicLog, "GetPosition function too slow"); + } + } + // If a non-zero position has not been reported during one second, break the loop and check if there is enough data + else if ((tPost - tStart) > MILLISECONDS_PER_SECOND) + { + break; + } + + pResults->cPosSets = i; + } + + VERIFY_SUCCEEDED(pHalfApp->StopStream()); + + // Did we get enough data? + VERIFY_IS_TRUE(pResults->cPosSets >= 30); + + return S_OK; +} + +// -------------------------------------------------------------------------------------------------- +// CalculateSampleRate +// -------------------------------------------------------------------------------------------------- +HRESULT CalculateSampleRate +( + PFORMAT_ENTRY pFormatEntry +) +{ + WAVEFORMATEX* pwfx = &pFormatEntry->wfxFormat.Format; + PPERF_RESULTS pResults = &pFormatEntry->perfResults; + + double dError, dErrorPercent; + + // Pair the positions with test HNS time to find the slope + // Alocate memory for this type-specific array + wil::unique_cotaskmem_ptr pointArray((PDPOINT)CoTaskMemAlloc(DATA_POINTS * sizeof(DPOINT))); + PDPOINT pArray = pointArray.get(); + // Get a pointer to the sample set + PPOSITION_SET posSet = pResults->argPosSets; + for (int i = 0; i < DATA_POINTS; i++) + { + pArray[i].x = posSet[i].positionTime; + pArray[i].y = posSet[i].position * pwfx->nAvgBytesPerSec; // Convert to bytes + } + + ULONG cArray = pResults->cPosSets; // number of points + + // calculate the slope and intercept (bytes per second and latency) + pResults->bytesPerSecond = SLOPE(pArray, cArray); + pResults->dOffset = INTERCEPT(pArray, cArray, pResults->bytesPerSecond); + + // Convert from bytes/ms to bytes/sec + pResults->bytesPerSecond *= MILLISECONDS_PER_SECOND; + + // The offset is used in the jitter calculation and it is used in milliseconds, so we can save it in those units + pResults->dOffset = pResults->dOffset / pResults->bytesPerSecond * MILLISECONDS_PER_SECOND; + + // calculate errors + dError = pResults->bytesPerSecond - (double)pwfx->nAvgBytesPerSec; + dErrorPercent = (dError / (double)pwfx->nAvgBytesPerSec) * 100.0; + + if (g_pKsPosTst->m_fLogHistograms) + { + LOG(g_pBasicLog, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + LOG(g_pBasicLog, "SampleRate calculated from histogram (method of least squares)\n"); + + LOG(g_pBasicLog, "Collected %d data points", pResults->cPosSets); + LOG(g_pBasicLog, "Specified sample rate = %6.3f bytes/sec, %6.3f samples/sec", (double)(pwfx->nBlockAlign * pwfx->nSamplesPerSec), (double)pwfx->nSamplesPerSec); + LOG(g_pBasicLog, "Calculated sample rate = %6.3f bytes/sec, %6.3f samples/sec", pResults->bytesPerSecond, pResults->bytesPerSecond / (double)pwfx->nBlockAlign); + LOG(g_pBasicLog, "Error = %.3f %%", dErrorPercent); + LOG(g_pBasicLog, "Calculated time offset = %f ms", pResults->dOffset); + } + + // evaluate the results + VERIFY_IS_TRUE(abs(dErrorPercent) <= SAMPLERATE_THRESHOLD); + + return S_OK; +} + +// -------------------------------------------------------------------------------------------------- +// CalculateJitter +// -------------------------------------------------------------------------------------------------- +HRESULT CalculateJitter +( + PFORMAT_ENTRY pFormatEntry +) +{ + PPERF_RESULTS pResults = &pFormatEntry->perfResults; + + double dError = 0.0; + double dErrorAve = 0.0; + double dErrorMax = 0.0; + double dStdDeviation = 0.0; + double reportedPosition; + double timeDifference; + ULONG i; + PPOSITION_SET pPosSet = pResults->argPosSets; + + if (g_pKsPosTst->m_fLogHistograms) + { + LOG(g_pBasicLog, "Reported QPC Compensated Expected"); + LOG(g_pBasicLog, "position adjust position position Error"); + LOG(g_pBasicLog, "(ms) (ms) (ms) (ms) (ms)"); + LOG(g_pBasicLog, "========== ========== ========== ========== =========="); + } + + for (i = 0; i < pResults->cPosSets; i++) + { + // Convert the time offset from seconds to milliseconds + reportedPosition = pPosSet[i].position * MILLISECONDS_PER_SECOND; // milliseconds + + // First, we compensate for the latency of the samples (they were taken 100 ms after stream start) + pPosSet[i].position = reportedPosition - pResults->dOffset; // The offset is already in ms + + // Then, before comparing the expected and actual position, adjust the given sample to get the position corresponding to the test time. + timeDifference = pPosSet[i].testTime - pPosSet[i].positionTime; // milliseconds + // If the difference is positive, the time of the position is behind of what is expected and that position gets increased accordingly. + // If the difference is negative, the time of the position is ahead of what is expected and that position gets decreased accordingly. + // In the rare case when the QPC and time are the same, the difference is zero because we are on the spot and that is the position that should be compared. + pPosSet[i].position += timeDifference; // milliseconds + + dError = pPosSet[i].testTime - pPosSet[i].position; + dErrorAve += dError; + + if (g_pKsPosTst->m_fLogHistograms) + { + LOG(g_pBasicLog, "%10.2f, %10.2f, %10.2f, %10.2f, %10.2f", reportedPosition, timeDifference, pPosSet[i].position, pPosSet[i].testTime, dError); + } + + if (dError < 0.) + dError = -dError; + if (dError > dErrorMax) + dErrorMax = dError; + } + + dErrorAve /= (double)pResults->cPosSets; + + VERIFY_IS_TRUE((ULONG)dErrorAve < 2); // if this is not true then, there's something wrong with our compensation... + + for (i = 0; i < pResults->cPosSets; i++) + { + dError = pPosSet[i].testTime - pPosSet[i].position; + dStdDeviation += ((dError - dErrorAve) * (dError - dErrorAve)); + } + + dStdDeviation /= (double)pResults->cPosSets; + dStdDeviation = sqrt(dStdDeviation); + + if (g_pKsPosTst->m_fLogHistograms) + { + LOG(g_pBasicLog, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + LOG(g_pBasicLog, "Jitter calculated from histogram (with (samplerate drift and latency) compensation)\n"); + LOG(g_pBasicLog, "Max deviation = %6.3f ms", dErrorMax); + LOG(g_pBasicLog, "Standard deviation = %6.3f ms", dStdDeviation); + } + + // evaluate the results ~~~~~~~~~~~~~ + VERIFY_IS_TRUE(abs(dErrorMax) < JITTER_MAX_THRESHOLD); + VERIFY_IS_TRUE(abs(dStdDeviation) < JITTER_STD_THRESHOLD); + + return S_OK; +} + +// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ +void Test_DriftAndJitter_Main +( + HNSTIME requestedPeriodicity +) +{ + CHalfApp* pHalfApp = g_pKsPosTst->m_pHalf; + CHalfApp* pHostHalfApp = g_pKsPosTst->m_pHostHalf; + BOOL isLoopbackPin = pHalfApp->m_ConnectorType == eLoopbackConnector; + + // Test will ignore Bluetooth devices until proper thresholds are in place. + if (pHalfApp->m_bIsBluetooth) + { + SKIP(g_pBasicLog, "Test is not supported for Bluetooth pins"); + return; + } + + // If this is a loobpack pin, start the host pin's stream + // This can be done before collecting the samples, since the host pin will keep playing until stopped + if (isLoopbackPin) + { + // Match loopback stream format with host stream format + CloneWaveFormat(pHostHalfApp->m_pCurrentFormat.get(), wil::out_param(pHalfApp->m_pCurrentFormat)); + pHalfApp->m_u32CurrentDefaultPeriodicityInFrames = pHostHalfApp->m_u32CurrentDefaultPeriodicityInFrames; + pHalfApp->m_u32CurrentFundamentalPeriodicityInFrames = pHostHalfApp->m_u32CurrentFundamentalPeriodicityInFrames; + pHalfApp->m_u32CurrentMinPeriodicityInFrames = pHostHalfApp->m_u32CurrentMinPeriodicityInFrames; + pHalfApp->m_u32CurrentMaxPeriodicityInFrames = pHostHalfApp->m_u32CurrentMaxPeriodicityInFrames; + + // Initialize and start host stream + VERIFY_SUCCEEDED(pHostHalfApp->InitializeEndpoint()); + VERIFY_SUCCEEDED(pHostHalfApp->CreateSineToneDataBuffer(pHostHalfApp->m_pCurrentFormat.get())); + VERIFY_SUCCEEDED(pHostHalfApp->InitializeStream(requestedPeriodicity, DEFAULT_LATENCY_COEFFICIENT)); + VERIFY_SUCCEEDED(pHostHalfApp->StartStream()); + } + + FORMAT_ENTRY results; + memset(&results, 0, sizeof(FORMAT_ENTRY)); + results.wfxFormat.Format = *pHalfApp->m_pCurrentFormat.get(); + // Alocate memory for the sample data + wil::unique_cotaskmem_ptr pPositionSet((PPOSITION_SET)CoTaskMemAlloc(DATA_POINTS * sizeof(POSITION_SET))); + results.perfResults.argPosSets = pPositionSet.get(); + + // collect the data ~~~~~~~~~~~~~~~~~ + VERIFY_SUCCEEDED(CollectSampleData(pHalfApp, &results, requestedPeriodicity)); + + // Stop and cleanup the host stream since we are done collecting samples + if (isLoopbackPin) + { + VERIFY_SUCCEEDED(pHostHalfApp->StopStream()); + VERIFY_SUCCEEDED(pHostHalfApp->CleanupStream()); + VERIFY_SUCCEEDED(pHostHalfApp->ReleaseEndpoint()); + VERIFY_SUCCEEDED(pHostHalfApp->ReleaseSineToneDataBuffer()); + } + + // calculate drift ~~~~~~~~~~~~~~~~~~ + VERIFY_SUCCEEDED(CalculateSampleRate(&results)); + + // calculate Jitter ~~~~~~~~~~~~~~~~~ + VERIFY_SUCCEEDED(CalculateJitter(&results)); +} + +// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ +void Test_DriftAndJitter_Default(void) +{ + HNSTIME requestedTime = FRAMES_TO_HNSTIME_DOUBLE(g_pKsPosTst->m_pHalf->m_u32CurrentDefaultPeriodicityInFrames, g_pKsPosTst->m_pHalf->m_pCurrentFormat->nSamplesPerSec); + if (g_pKsPosTst->m_fLogHistograms) + { + LOG(g_pBasicLog, "Periodicity: %d frames, %f ms", g_pKsPosTst->m_pHalf->m_u32CurrentDefaultPeriodicityInFrames, HNSTIME_TO_MILLISECONDS_DOUBLE(requestedTime)); + } + Test_DriftAndJitter_Main(requestedTime); +} + +/* + Perform a cycle of creating a pin, starting a stream, stoping and destroying. + + This is done with the idea of putting the driver and audio hardware in an active state + before doing time sensitive tests. +*/ +void ActivateDriver(CHalfApp* pHalfApp) +{ + VERIFY_SUCCEEDED(pHalfApp->InitializeEndpoint()); + VERIFY_SUCCEEDED(pHalfApp->CreateSineToneDataBuffer(pHalfApp->m_pCurrentFormat.get())); + VERIFY_SUCCEEDED(pHalfApp->InitializeStream(DEFAULT_PERIODICITY, DEFAULT_LATENCY_COEFFICIENT)); + VERIFY_SUCCEEDED(pHalfApp->StartStream()); + Sleep(100); + VERIFY_SUCCEEDED(pHalfApp->StopStream()); + VERIFY_SUCCEEDED(pHalfApp->CleanupStream()); + VERIFY_SUCCEEDED(pHalfApp->ReleaseEndpoint()); + VERIFY_SUCCEEDED(pHalfApp->ReleaseSineToneDataBuffer()); +} + +// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ +void Test_Latency_Main (HNSTIME requestedPeriodicity) +{ + CHalfApp* pHalfApp = g_pKsPosTst->m_pHalf; + CHalfApp* pHostHalfApp = g_pKsPosTst->m_pHostHalf; + NTSTATUS lTimerResStatus; + BOOL isLoopbackPin = pHalfApp->m_ConnectorType == eLoopbackConnector; + + // Test will ignore Bluetooth devices until proper thresholds are in place. + if (pHalfApp->m_bIsBluetooth) + { + SKIP(g_pBasicLog, "Test is not supported for Bluetooth pins"); + return; + } + + lTimerResStatus = NtSetTimerResolution(timerResolution, TRUE, &actualTimerResolution); + + if (!NT_SUCCESS(lTimerResStatus)) + { + SKIP(g_pBasicLog, "Fail to set timer precision. Skip the test."); + return; + } + + // If it is a loopback pin, get the host pin and start the stream + if (isLoopbackPin) + { + // Match loopback stream format with host stream format + CloneWaveFormat(pHostHalfApp->m_pCurrentFormat.get(), wil::out_param(pHalfApp->m_pCurrentFormat)); + pHalfApp->m_u32CurrentDefaultPeriodicityInFrames = pHostHalfApp->m_u32CurrentDefaultPeriodicityInFrames; + pHalfApp->m_u32CurrentFundamentalPeriodicityInFrames = pHostHalfApp->m_u32CurrentFundamentalPeriodicityInFrames; + pHalfApp->m_u32CurrentMinPeriodicityInFrames = pHostHalfApp->m_u32CurrentMinPeriodicityInFrames; + pHalfApp->m_u32CurrentMaxPeriodicityInFrames = pHostHalfApp->m_u32CurrentMaxPeriodicityInFrames; + + // Initialize and start host stream + VERIFY_SUCCEEDED(pHostHalfApp->InitializeEndpoint()); + VERIFY_SUCCEEDED(pHostHalfApp->CreateSineToneDataBuffer(pHostHalfApp->m_pCurrentFormat.get())); + VERIFY_SUCCEEDED(pHostHalfApp->InitializeStream(requestedPeriodicity, DEFAULT_LATENCY_COEFFICIENT)); + VERIFY_SUCCEEDED(pHostHalfApp->StartStream()); + } + + // Perform a stream cycle to make sure the driver is on a good state + ActivateDriver(pHalfApp); + + // Initialize the stream + VERIFY_SUCCEEDED(pHalfApp->InitializeEndpoint()); + VERIFY_SUCCEEDED(pHalfApp->CreateSineToneDataBuffer(pHalfApp->m_pCurrentFormat.get())); + VERIFY_SUCCEEDED(pHalfApp->InitializeStream(requestedPeriodicity, DEFAULT_LATENCY_COEFFICIENT)); + + UINT64 u64Position = 0; + UINT64 driverHns = 0; + + double tPosPre = 0.0; + double tPosPost = 0.0; + + LARGE_INTEGER liFrequency = { 0 }; + LARGE_INTEGER timeStamp = { 0 }; + QueryPerformanceFrequency(&liFrequency); + + // mark the time + double tRunPre = xGetTime(); + // Start the stream + VERIFY_SUCCEEDED(pHalfApp->StartStream()); + // mark the time + double tRunPost = xGetTime(); + + LOG(g_pBasicLog, "Starting the stream took %.03f ms", (tRunPost - tRunPre)); + + // loop until we get a non-zero position + while (u64Position == 0) + { + Sleep(1); + // timer before position call + tPosPre = xGetTime(); + VERIFY_SUCCEEDED(pHalfApp->GetPosition(&u64Position, &driverHns)); + QueryPerformanceCounter(&timeStamp); + // timer after position call + tPosPost = xGetTime(); + if (g_pKsPosTst->m_fLogHistograms) + { + LOG(g_pBasicLog, "Testing first non-zero position reported: %u samples, %u HNS at %.03f ms", u64Position, driverHns, tPosPost - tRunPre); + } + + // Fail if the cursor does not move within 500 ms + VERIFY_IS_TRUE((tPosPost - tRunPost) < 500.0); + } + + // Before stopping, get the frequency of the stream + UINT64 u64Frequency; + VERIFY_SUCCEEDED(pHalfApp->m_pAudioClock->GetFrequency(&u64Frequency)); + + // the uncertainty in time is the max possible value minus the min possible value divided by 2 (for +/- purposes) + double tMax = tPosPost - tRunPre; + double tMin = tPosPre - tRunPost; + double tAve = (tMax + tMin) / 2.0; + double tUncertainty = (tMax - tMin) / 2.0; + + // Convert the stream position to an offset of milliseconds + double tOffset = MILLISECONDS_PER_SECOND * (double)u64Position / (double)u64Frequency; + + // Calculate the difference in time of the position time versus the test time + UINT64 testHns = 10000000 * timeStamp.QuadPart / liFrequency.QuadPart; + INT64 timeDiff = testHns - driverHns; + + // Compensate the positon to take this difference into account + tOffset += (double)timeDiff / HNSTIME_UNITS_PER_MILLISECOND; + + if (g_pKsPosTst->m_fLogHistograms) + { + LOG(g_pBasicLog, "Times (HNS): %u, %u", testHns, driverHns); + LOG(g_pBasicLog, "Time difference: %d HNS (%f ms)", timeDiff, (double)timeDiff / HNSTIME_UNITS_PER_MILLISECOND); + LOG(g_pBasicLog, "First non-zero position = %u at %g ms", u64Position, tAve); + LOG(g_pBasicLog, "Time offset / max time: %f ms / %f ms", tOffset, tMax); + LOG(g_pBasicLog, "Frequency: %lu", u64Frequency); + } + + double tLatency; + tLatency = tAve - tOffset; + if (tLatency < 0) + { + // This means that the position is between the average and max times, which should be OK (as long as it is not above the max) + tLatency *= -1; + LOG(g_pBasicLog, "The position is greater than the average time"); + } + LOG(g_pBasicLog, "Adjusted latency = %.03f ms (+/- %.3f ms)", tLatency, tUncertainty); + + // if the reported position is outside the max time for this call (see tUncertainty calculation above), then fail + // temp fix until we know how far can be the data in front of the + // real time because of buffering + VERIFY_IS_FALSE(tOffset > tMax); + + // Verify that the latency is within the threshold + VERIFY_IS_TRUE(tLatency <= LATENCY_THRESHOLD); // ms + + // Stop the endpoint + VERIFY_SUCCEEDED(pHalfApp->StopStream()); + // If this was a loopback pin, stop the host stream also + if (isLoopbackPin) + { + VERIFY_SUCCEEDED(pHostHalfApp->StopStream()); + VERIFY_SUCCEEDED(pHostHalfApp->CleanupStream()); + VERIFY_SUCCEEDED(pHostHalfApp->ReleaseEndpoint()); + VERIFY_SUCCEEDED(pHostHalfApp->ReleaseSineToneDataBuffer()); + } + + // Turn off the higher resolution timer + NtSetTimerResolution(0, FALSE, &actualTimerResolution); +} + +// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ +void Test_Latency_Default(void) +{ + HNSTIME requestedTime = FRAMES_TO_HNSTIME_DOUBLE(g_pKsPosTst->m_pHalf->m_u32CurrentDefaultPeriodicityInFrames, g_pKsPosTst->m_pHalf->m_pCurrentFormat->nSamplesPerSec); + LOG(g_pBasicLog, "Periodicity: %d frames, %f ms", g_pKsPosTst->m_pHalf->m_u32CurrentDefaultPeriodicityInFrames, HNSTIME_TO_MILLISECONDS_DOUBLE(requestedTime)); + Test_Latency_Main(requestedTime); +} diff --git a/audio/tests/KSPosTst/tests.h b/audio/tests/KSPosTst/tests.h new file mode 100644 index 000000000..ec0a68cc3 --- /dev/null +++ b/audio/tests/KSPosTst/tests.h @@ -0,0 +1,18 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// Module Name: +// +// tests.h +// +// Abstract: +// +// Header for testcases +// +// ------------------------------------------------------------------------------- +#pragma once + +void Test_DriftAndJitter_Default(void); + +void Test_Latency_Default(void); diff --git a/audio/tests/KSPosTst/timetest.cpp b/audio/tests/KSPosTst/timetest.cpp new file mode 100644 index 000000000..d8026716e --- /dev/null +++ b/audio/tests/KSPosTst/timetest.cpp @@ -0,0 +1,50 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// Module Name: +// +// timetest.cpp +// +// Abstract: +// +// Functions to precisely measure time. +// +// ------------------------------------------------------------------------------- + +#include "PreComp.h" +#include + +// ------------------------------------------------------------------------------ +// returns millisecs +double tpQPC (void) +{ + static LARGE_INTEGER liFrequency = { 0 }; + static LARGE_INTEGER liStart = { 0 }; + static bool bInitialized = false; + + if (!bInitialized) + { + if (!QueryPerformanceFrequency(&liFrequency)) + { + throw ("QueryPerformanceFrequency failed"); + } + + if (!QueryPerformanceCounter(&liStart)) + { + throw ("QueryPerformanceCounter failed"); + } + + bInitialized = true; + } + + LARGE_INTEGER liNow = { 0 }; + + if (!QueryPerformanceCounter(&liNow)) + { + throw ("QueryPerformanceCounter failed"); + } + + // milliseconds since test start + return 1000.0 * (liNow.QuadPart - liStart.QuadPart) / liFrequency.QuadPart; +} diff --git a/audio/tests/wavetest/HalfApp.cpp b/audio/tests/PinResourceHelpers/HalfApp.cpp similarity index 88% rename from audio/tests/wavetest/HalfApp.cpp rename to audio/tests/PinResourceHelpers/HalfApp.cpp index 14a15c080..83c9c8f31 100644 --- a/audio/tests/wavetest/HalfApp.cpp +++ b/audio/tests/PinResourceHelpers/HalfApp.cpp @@ -12,15 +12,16 @@ // // ------------------------------------------------------------------------------- -#include "PreComp.h" -#include "WaveTestTaef.h" -#include "HalfApp.h" -#include "TestResourceHelper.h" -#include "PropertyHelper.h" -#include -#include -#include -#include + + + + + + + + + + HRESULT CHalfApp::InitializeEndpoint @@ -114,6 +115,9 @@ CHalfApp::InitializeEndpoint if (!VERIFY_SUCCEEDED(hr = m_pAudioDeviceEndpoint.query_to(&m_pAudioEndpointRT))) { return hr; } + if (!VERIFY_SUCCEEDED(hr = m_pAudioDeviceEndpoint.query_to(&m_pAudioClock))) { + return hr; + } return hr; } @@ -134,6 +138,7 @@ CHalfApp::ReleaseEndpoint m_pAudioEndpointControl.reset(); m_pAudioEndpoint.reset(); m_pAudioEndpointRT.reset(); + m_pAudioClock.reset(); return hr; } @@ -380,6 +385,7 @@ CHalfApp::GetSecondHalfApp UINT32 u32MinPeriodicityInFrames; UINT32 u32MaxPeriodicityInFrames; UINT32 u32MaxPeriodicityInFramesExtended; + DeviceDescriptor descriptor = { 0 }; if (!VERIFY_SUCCEEDED(hr = GetSupportedFormatRecordsForConnector(m_pDevice.get(), m_uConnectorId, m_ConnectorType, secondMode, m_DataFlow, &cFormatRecords, &spFormatRecords))) { return hr; @@ -393,7 +399,26 @@ CHalfApp::GetSecondHalfApp return hr; } - *ppSecondHalfApp = new CHalfApp(m_pDevice.get(), m_pwstrDeviceId.get(), m_pwstrDeviceFriendlyName.get(), m_DataFlow, m_ConnectorType, m_uConnectorId, secondMode, m_cModes, m_pModes, cFormatRecords, spFormatRecords, pPreferredFormat.get(), u32DefaultPeriodicityInFrames, u32FundamentalPeriodicityInFrames, u32MinPeriodicityInFrames, u32MaxPeriodicityInFrames); + descriptor.pDevice = m_pDevice.get(); + descriptor.pwstrAudioEndpointId = m_pwstrDeviceId.get(); + descriptor.pwstrAudioEndpointFriendlyName = m_pwstrDeviceFriendlyName.get(); + descriptor.dataFlow = m_DataFlow; + descriptor.eConnectorType = m_ConnectorType; + descriptor.uConnectorId = m_uConnectorId; + descriptor.mode = secondMode; + descriptor.cModes = m_cModes; + descriptor.pModes = m_pModes; + descriptor.cFormatRecords = cFormatRecords; + descriptor.pFormatRecords = spFormatRecords; + descriptor.pPreferredFormat = pPreferredFormat.get(); + descriptor.u32DefaultPeriodicityInFrames = u32DefaultPeriodicityInFrames; + descriptor.u32FundamentalPeriodicityInFrames = u32FundamentalPeriodicityInFrames; + descriptor.u32MinPeriodicityInFrames = u32MinPeriodicityInFrames; + descriptor.u32MaxPeriodicityInFrames = u32MaxPeriodicityInFrames; + descriptor.bIsAVStream = m_bIsAVStream; + descriptor.bIsBluetooth = m_bIsBluetooth; + descriptor.bIsSideband = m_bIsSideband; + *ppSecondHalfApp = new CHalfApp(descriptor); if (*ppSecondHalfApp == NULL) { hr = E_OUTOFMEMORY; return hr; @@ -432,6 +457,7 @@ CHalfApp::GetHostHalfApp UINT32 u32MinPeriodicityInFrames; UINT32 u32MaxPeriodicityInFrames; UINT32 u32MaxPeriodicityInFramesExtended; + DeviceDescriptor descriptor = { 0 }; // It is only used when testing on loopback pin. When preparing loopback pin, we also need prepare the host pin. VERIFY_IS_TRUE(m_ConnectorType == eLoopbackConnector); @@ -474,7 +500,26 @@ CHalfApp::GetHostHalfApp return hr; } - *ppHostHalfApp = new CHalfApp(m_pDevice.get(), m_pwstrDeviceId.get(), m_pwstrDeviceFriendlyName.get(), render, eHostProcessConnector, uConnectorId, mode, cModes, spModes, cFormatRecords, spFormatRecords, pPreferredFormat.get(), u32DefaultPeriodicityInFrames, u32FundamentalPeriodicityInFrames, u32MinPeriodicityInFrames, u32MaxPeriodicityInFrames); + descriptor.pDevice = m_pDevice.get(); + descriptor.pwstrAudioEndpointId = m_pwstrDeviceId.get(); + descriptor.pwstrAudioEndpointFriendlyName = m_pwstrDeviceFriendlyName.get(); + descriptor.dataFlow = render; + descriptor.eConnectorType = eHostProcessConnector; + descriptor.uConnectorId = uConnectorId; + descriptor.mode = mode; + descriptor.cModes = cModes; + descriptor.pModes = spModes; + descriptor.cFormatRecords = cFormatRecords; + descriptor.pFormatRecords = spFormatRecords; + descriptor.pPreferredFormat = pPreferredFormat.get(); + descriptor.u32DefaultPeriodicityInFrames = u32DefaultPeriodicityInFrames; + descriptor.u32FundamentalPeriodicityInFrames = u32FundamentalPeriodicityInFrames; + descriptor.u32MinPeriodicityInFrames = u32MinPeriodicityInFrames; + descriptor.u32MaxPeriodicityInFrames = u32MaxPeriodicityInFrames; + descriptor.bIsAVStream = m_bIsAVStream; + descriptor.bIsBluetooth = m_bIsBluetooth; + descriptor.bIsSideband = m_bIsSideband; + *ppHostHalfApp = new CHalfApp(descriptor); if (*ppHostHalfApp == NULL) { hr = E_OUTOFMEMORY; return hr; @@ -987,13 +1032,9 @@ inline void CHalfApp::CancelTimer(HANDLE timer) HRESULT CHalfApp::GetPosition(UINT64* pu64Position, UINT64* pu64hnsQPCPosition) { HRESULT hr = S_OK; - wil::com_ptr_nothrow spAudioClock; auto lock = m_CritSec.lock(); - if (!VERIFY_SUCCEEDED(m_pAudioDeviceEndpoint.query_to(&spAudioClock))) { - return hr; - } - if (!VERIFY_SUCCEEDED(spAudioClock->GetPosition(pu64Position, pu64hnsQPCPosition))) { + if (!VERIFY_SUCCEEDED(m_pAudioClock->GetPosition(pu64Position, pu64hnsQPCPosition))) { return hr; } diff --git a/audio/tests/PinResourceHelpers/PreComp.h b/audio/tests/PinResourceHelpers/PreComp.h new file mode 100644 index 000000000..85fc4f833 --- /dev/null +++ b/audio/tests/PinResourceHelpers/PreComp.h @@ -0,0 +1,143 @@ +// ------------------------------------------------------------------------------ +// +// Copyright (C) Microsoft. All rights reserved. +// +// File Name: +// +// PreComp.h +// +// Abstract: +// +// Precompiled common headers +// +// ------------------------------------------------------------------------------- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// Define custom test resource properties +namespace WEX { + namespace TestExecution { + namespace TestResourceProperty + { + static const wchar_t c_szMode[] = L"Mode"; + static const wchar_t c_szPin[] = L"Pin"; + } + } +} + +// ---------------------------------------------------------- +// Parameters for test sine tone +#define TEST_AMPLITUDE 1.0f +#define TEST_FREQUENCY 200.0f + +// ---------------------------------------------------------- +// Predefined format list for offload streaming +#define COUNT_FORMATS 4 + +static WAVEFORMATEXTENSIBLE ListofFormats[COUNT_FORMATS] = +{ + // 1 + { + // Format + { + WAVE_FORMAT_EXTENSIBLE, // wFormatTag + 2, // nChannels + 44100, // nSamplesPerSec + 176400, // nAvgBytesPerSec + 4, // nBlockAlign + 16, // wBitsPerSample + 22 // cbSize + }, + // Samples + { + 16 // wValidBitsPerSample + }, + KSAUDIO_SPEAKER_STEREO, // dwChannelMask + KSDATAFORMAT_SUBTYPE_PCM // SubFormat + }, + // 2 + { + // Format + { + WAVE_FORMAT_EXTENSIBLE, // wFormatTag + 2, // nChannels + 48000, // nSamplesPerSec + 192000, // nAvgBytesPerSec + 4, // nBlockAlign + 16, // wBitsPerSample + 22 // cbSize + }, + // Samples + { + 16 // wValidBitsPerSample + }, + KSAUDIO_SPEAKER_STEREO, // dwChannelMask + KSDATAFORMAT_SUBTYPE_PCM // SubFormat + }, + // 3 + { + // Format + { + WAVE_FORMAT_EXTENSIBLE, // wFormatTag + 2, // nChannels + 88200, // nSamplesPerSec + 352800, // nAvgBytesPerSec + 4, // nBlockAlign + 16, // wBitsPerSample + 22 // cbSize + }, + // Samples + { + 16 // wValidBitsPerSample + }, + KSAUDIO_SPEAKER_STEREO, // dwChannelMask + KSDATAFORMAT_SUBTYPE_PCM // SubFormat + }, + // 4 + { + // Format + { + WAVE_FORMAT_EXTENSIBLE, // wFormatTag + 2, // nChannels + 96000, // nSamplesPerSec + 384000, // nAvgBytesPerSec + 4, // nBlockAlign + 16, // wBitsPerSample + 22 // cbSize + }, + // Samples + { + 16 // wValidBitsPerSample + }, + KSAUDIO_SPEAKER_STEREO, // dwChannelMask + KSDATAFORMAT_SUBTYPE_PCM // SubFormat + }, +}; diff --git a/audio/tests/wavetest/PropertyHelper.cpp b/audio/tests/PinResourceHelpers/PropertyHelper.cpp similarity index 80% rename from audio/tests/wavetest/PropertyHelper.cpp rename to audio/tests/PinResourceHelpers/PropertyHelper.cpp index fb60d3cc9..6e85e53ee 100644 --- a/audio/tests/wavetest/PropertyHelper.cpp +++ b/audio/tests/PinResourceHelpers/PropertyHelper.cpp @@ -4,7 +4,7 @@ // // Module Name: // -// EndpointPropertyHelpers.cpp +// PropertyHelper.cpp // // Abstract: // @@ -14,7 +14,8 @@ #include "PreComp.h" #include #include -#include "PropertyHelper.h" +#include +#include /////////////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -722,6 +723,8 @@ HRESULT IsFormatSupported return hr; } + CoTaskMemFree(pKsRequestedFormat); + return hr; } @@ -1093,3 +1096,168 @@ HRESULT GetAvailiablePinInstanceCount return hr; } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// IsAVStream +// +// Check if audio device is AVStream +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT IsAVStream +( + IMMDevice* pDevice, + bool* pIsAVStream +) +{ + HRESULT hr = S_OK; + wil::com_ptr_nothrow spDevnodeDevice; + wil::com_ptr_nothrow spPnpProperties; + wil::unique_prop_variant varDeviceService; + + *pIsAVStream = false; + + // Find the associated PnP device + if (!VERIFY_SUCCEEDED(hr = GetPnpDevnodeFromMMDevice(pDevice, &spDevnodeDevice))) { + return hr; + } + + // Open pnp device property store + if (!VERIFY_SUCCEEDED(hr = spDevnodeDevice->OpenPropertyStore(STGM_READ, &spPnpProperties))) { + return hr; + } + + // Read the DEVPKEY_Device_Service + if (!VERIFY_SUCCEEDED(hr = spPnpProperties->GetValue((REFPROPERTYKEY)DEVPKEY_Device_Service, &varDeviceService))) { + return hr; + } + + if (!VERIFY_IS_TRUE(varDeviceService.vt == VT_LPWSTR && varDeviceService.pwszVal != nullptr)) { + hr = E_FAIL; + return hr; + } + + if (0 == _wcsicmp(varDeviceService.pwszVal, L"usbaudio") || 0 == _wcsicmp(varDeviceService.pwszVal, L"BthHFAud") || 0 == _wcsicmp(varDeviceService.pwszVal, L"BthA2dp")) { + *pIsAVStream = true; + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// IsBluetooth +// +// Check if audio device is Bluetooth +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT IsBluetooth +( + IMMDevice* pDevice, + bool* pIsBluetooth +) +{ + HRESULT hr = S_OK; + wil::com_ptr_nothrow spPropertyStore; + wil::unique_prop_variant varIsBluetooth; + + *pIsBluetooth = false; + + // Open pnp device property store + if (!VERIFY_SUCCEEDED(hr = pDevice->OpenPropertyStore(STGM_READ, &spPropertyStore))) { + return hr; + } + + // Read the PKEY_Endpoint_IsBluetooth + hr = spPropertyStore->GetValue((REFPROPERTYKEY)PKEY_Endpoint_IsBluetooth, &varIsBluetooth); + if (hr != S_OK) { + return hr; + } + + if (varIsBluetooth.vt == VT_BOOL) + { + *pIsBluetooth = varIsBluetooth.boolVal == VARIANT_TRUE ? true : false; + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// IsSideband +// +// Check if audio device is side band +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT IsSideband +( + IMMDevice* pDevice, + bool* pIsSideband +) +{ + HRESULT hr = S_OK; + wil::com_ptr_nothrow spPropertyStore; + wil::unique_prop_variant varIsSideband; + + *pIsSideband = false; + + // Open pnp device property store + if (!VERIFY_SUCCEEDED(hr = pDevice->OpenPropertyStore(STGM_READ, &spPropertyStore))) { + return hr; + } + + // Read the PKEY_Endpoint_IsBluetooth + hr = spPropertyStore->GetValue((REFPROPERTYKEY)PKEY_Endpoint_IsSideband, &varIsSideband); + if (hr != S_OK) { + return hr; + } + + if (varIsSideband.vt == VT_BOOL) + { + *pIsSideband = varIsSideband.boolVal == VARIANT_TRUE ? true : false; + } + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// GetPnpDevnodeFromMMDeivce +// +// Get pnp devnode from mm device +// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +HRESULT GetPnpDevnodeFromMMDevice +( + IMMDevice* pDevice, + IMMDevice** pDevnodeDevice +) +{ + HRESULT hr = S_OK; + wil::com_ptr_nothrow spEnumerator; + wil::com_ptr_nothrow spProps; + wil::com_ptr_nothrow spDevnodeAsDevice; + wil::com_ptr_nothrow spDevnodeProps; + wil::unique_prop_variant varDevnode; + + // Create an enumerator + if (!VERIFY_SUCCEEDED(hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void **)&spEnumerator))) { + return hr; + } + + // Read the PKEY_Endpoint_Devnode + if (!VERIFY_SUCCEEDED(hr = pDevice->OpenPropertyStore(STGM_READ, &spProps))) { + return hr; + } + if (!VERIFY_SUCCEEDED(hr = spProps->GetValue(PKEY_Endpoint_Devnode, &varDevnode))) { + return hr; + } + + // Get the devnode as an IMMDevice + if (!VERIFY_SUCCEEDED(hr = spEnumerator->GetDevice(varDevnode.pwszVal, &spDevnodeAsDevice))) { + return hr; + } + *pDevnodeDevice = spDevnodeAsDevice.detach(); + + return hr; +} diff --git a/audio/tests/wavetest/TestResource.cpp b/audio/tests/PinResourceHelpers/TestResource.cpp similarity index 99% rename from audio/tests/wavetest/TestResource.cpp rename to audio/tests/PinResourceHelpers/TestResource.cpp index 67f3ca79f..f3762b848 100644 --- a/audio/tests/wavetest/TestResource.cpp +++ b/audio/tests/PinResourceHelpers/TestResource.cpp @@ -13,8 +13,8 @@ // ------------------------------------------------------------------------------- #include "PreComp.h" -#include "HalfApp.h" -#include "TestResource.h" +#include +#include using namespace WEX::Logging; using namespace WEX::TestExecution; @@ -328,4 +328,4 @@ LPWSTR CPinTestResource::PinName(EndpointConnectorType eConnectorType) default: return L"UNKNOWN"; } -} \ No newline at end of file +} diff --git a/audio/tests/wavetest/TestResourceHelper.h b/audio/tests/PinResourceHelpers/TestResourceHelper.h similarity index 98% rename from audio/tests/wavetest/TestResourceHelper.h rename to audio/tests/PinResourceHelpers/TestResourceHelper.h index 24bae634a..08fafa23d 100644 --- a/audio/tests/wavetest/TestResourceHelper.h +++ b/audio/tests/PinResourceHelpers/TestResourceHelper.h @@ -13,6 +13,8 @@ // ------------------------------------------------------------------------------- #pragma once +#include + // ---------------------------------------------------------------------- HRESULT GetProcessingModesForConnector ( @@ -68,4 +70,4 @@ HRESULT GetSupportedFormatRecordsForConnector STACKWISE_DATAFLOW dataFlow, ULONG* pCount, FORMAT_RECORD** ppFormatRecords -); \ No newline at end of file +); diff --git a/audio/tests/wavetest/HalfApp.h b/audio/tests/wavetest/HalfApp.h deleted file mode 100644 index 1bf14de91..000000000 --- a/audio/tests/wavetest/HalfApp.h +++ /dev/null @@ -1,195 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Copyright (C) Microsoft. All rights reserved. -// -// File Name: -// -// HalfApp.h -// -// Abstract: -// -// CHalfApp declarations -// -// ------------------------------------------------------------------------------- -#pragma once - -#define BUF_LEN_IN_MS 1000 // 1 second - -class CHalfApp -{ -public: - // Basic pin infos - wil::com_ptr_nothrow m_pDevice; - wil::unique_cotaskmem_string m_pwstrDeviceId; - wil::unique_cotaskmem_string m_pwstrDeviceFriendlyName; - STACKWISE_DATAFLOW m_DataFlow; - EndpointConnectorType m_ConnectorType; - UINT m_uConnectorId; - GUID m_Mode; - ULONG m_cModes; - GUID* m_pModes; // All processing modes - - // Formats - wil::unique_cotaskmem_ptr m_pPreferredFormat; - UINT32 m_u32DefaultPeriodicityInFrames; - UINT32 m_u32FundamentalPeriodicityInFrames; - UINT32 m_u32MinPeriodicityInFrames; - UINT32 m_u32MaxPeriodicityInFrames; - - PFORMAT_RECORD m_pFormatRecords; - ULONG m_cFormatRecords; - - wil::unique_cotaskmem_ptr m_pCurrentFormat; - UINT32 m_u32CurrentDefaultPeriodicityInFrames; - UINT32 m_u32CurrentFundamentalPeriodicityInFrames; - UINT32 m_u32CurrentMinPeriodicityInFrames; - UINT32 m_u32CurrentMaxPeriodicityInFrames; - - // Audio endpoint interfaces - wil::com_ptr_nothrow m_pAudioDeviceEndpoint; - wil::com_ptr_nothrow m_pAudioEndpoint; - wil::com_ptr_nothrow m_pAudioEndpointControl; - wil::com_ptr_nothrow m_pAudioEndpointRT; - - // Data buffer - wil::unique_hlocal_ptr m_pbSineToneDataBuffer; - DWORD m_dwSineToneDataBufferSize; - DWORD m_dwSineToneDataBufferPosition; - - // Stream - HNSTIME m_hnsPeriod = 0; - FLOAT32 m_f32EndpointFrameRate = 0.F; - HNSTIME m_hnsEndpointLatency = 0; - bool m_bIsEventCapable = false; - bool m_bStreamInitialized = false; - volatile bool m_bStreamThreadTerminate = false; - bool m_bYieldActive = false; - bool m_bIsBackupTimerRequired = true; - - wil::unique_handle m_hStreamThread; - - LPTHREAD_START_ROUTINE m_pStreamRoutine = nullptr; - wil::unique_event_nothrow m_hProcessThreadStartedEvent; - wil::unique_event_nothrow m_hTerminate; - wil::unique_event_nothrow m_hEndpointBufferCompleteEvent; - wil::unique_handle m_hTimer; - - wil::critical_section m_CritSec; - - typedef enum - { - ET_DO_NOTHING = 0x00, - ET_TERMINATE = 0x01, - ET_OBJECT_BUFFER_COMPLETE = 0x02, - ET_CLIENT_RELEASE_BUFFER = 0x04, - ET_TIMER = 0x08, - ET_PAUSE_PUMP = 0x10, - ET_RESUME_PUMP = 0x20, - } TEventType; - - CHalfApp( - _In_ IMMDevice* pDevice, - _In_ LPWSTR pwstrAudioEndpointId, - _In_ LPWSTR pwstrAudioEndpointFriendlyName, - _In_ STACKWISE_DATAFLOW dataFlow, - _In_ EndpointConnectorType eConnectorType, - _In_ UINT uConnectorId, - _In_ GUID mode, - _In_ ULONG cModes, - _In_ GUID* pModes, - _In_ ULONG cFormatRecords, - _In_ PFORMAT_RECORD pFormatRecords, - _In_ PWAVEFORMATEX pPreferredFormat, - _In_ UINT32 u32DefaultPeriodicityInFrames, - _In_ UINT32 u32FundamentalPeriodicityInFrames, - _In_ UINT32 u32MinPeriodicityInFrames, - _In_ UINT32 u32MaxPeriodicityInFrames - ) - : m_DataFlow(dataFlow) - , m_ConnectorType(eConnectorType) - , m_uConnectorId(uConnectorId) - , m_cModes(cModes) - , m_cFormatRecords(cFormatRecords) - , m_u32DefaultPeriodicityInFrames(u32DefaultPeriodicityInFrames) - , m_u32FundamentalPeriodicityInFrames(u32FundamentalPeriodicityInFrames) - , m_u32MinPeriodicityInFrames(u32MinPeriodicityInFrames) - , m_u32MaxPeriodicityInFrames(u32MaxPeriodicityInFrames) - , m_u32CurrentDefaultPeriodicityInFrames(u32DefaultPeriodicityInFrames) - , m_u32CurrentFundamentalPeriodicityInFrames(u32FundamentalPeriodicityInFrames) - , m_u32CurrentMinPeriodicityInFrames(u32MinPeriodicityInFrames) - , m_u32CurrentMaxPeriodicityInFrames(u32MaxPeriodicityInFrames) - { - m_pDevice = pDevice; - m_pAudioDeviceEndpoint = nullptr; - m_pAudioEndpointControl = nullptr; - - m_pwstrDeviceId = wil::make_cotaskmem_string_nothrow(pwstrAudioEndpointId, wcslen(pwstrAudioEndpointId)); - m_pwstrDeviceFriendlyName = wil::make_cotaskmem_string_nothrow(pwstrAudioEndpointFriendlyName, wcslen(pwstrAudioEndpointFriendlyName)); - m_Mode = mode; - - m_pModes = new GUID[cModes]; - memcpy(m_pModes, pModes, sizeof(GUID)*cModes); - m_pFormatRecords = new FORMAT_RECORD[cFormatRecords]; - memcpy(m_pFormatRecords, pFormatRecords, sizeof(FORMAT_RECORD)*cFormatRecords); - - CloneWaveFormat(pPreferredFormat, wil::out_param(m_pPreferredFormat)); - CloneWaveFormat(pPreferredFormat, wil::out_param(m_pCurrentFormat)); // Use preferred format as current format - } - - ~CHalfApp(void) - { - if (m_pModes) - delete[] m_pModes; - if (m_pFormatRecords) - delete[] m_pFormatRecords; - } - - // Audio endpoint interfaces - HRESULT InitializeEndpoint(); - HRESULT ReleaseEndpoint(); - HRESULT StartEndpoint(); - HRESULT StopEndpoint(); - HRESULT ResetEndpoint(); - - // Sine tone data buffer - HRESULT CreateSineToneDataBuffer(WAVEFORMATEX* pWfx); - HRESULT ReleaseSineToneDataBuffer(); - - - - - // Stream - HRESULT InitializeAndSetBuffer(HNSTIME hnsPeriod, UINT32 u32LatencyCoefficient); - HRESULT InitializeStream(HNSTIME requestedPeriodicity, UINT32 u32LatencyCoefficient); - HRESULT CleanupStream(); - HRESULT StartStream(); - HRESULT StopStream(); - static DWORD CALLBACK RenderStreamRoutine(PVOID lpParameter); - static DWORD CALLBACK CaptureStreamRoutine(PVOID lpParameter); - void SignalAndWaitForThread(); - - - - HRESULT SetTimer(HANDLE timer, HNSTIME timeDuration, bool fireImmediatley); - void CancelTimer(HANDLE timer); - HRESULT GetPosition(UINT64* pu64Position, UINT64* pu64hnsQPCPosition); - - // Multiple pin instances - HRESULT GetCurrentAvailiablePinInstanceCount(UINT32* pAvailablePinInstanceCount); - HRESULT GetSecondHalfApp(AUDIO_SIGNALPROCESSINGMODE secondMode, CHalfApp** ppSecondHalfApp); - - // Only for loopback testcases - HRESULT GetHostHalfApp(CHalfApp** ppHostHalfApp); -}; - -// {571BF784-A9D6-420A-BCA0-C579A8710236} -DEFINE_GUID(IID_IHalfAppContainer, - 0x571bf784, 0xa9d6, 0x420a, 0xbc, 0xa0, 0xc5, 0x79, 0xa8, 071, 0x02, 0x36); - -DECLARE_INTERFACE_IID_( -IHalfAppContainer, -IUnknown, -"571BF784-A9D6-420A-BCA0-C579A8710236") -{ - STDMETHOD(GetHalfApp)(THIS_ CHalfApp ** ppHalfApp) PURE; -}; diff --git a/audio/tests/wavetest/PreComp.h b/audio/tests/wavetest/PreComp.h index 2044d6b12..76210090e 100644 --- a/audio/tests/wavetest/PreComp.h +++ b/audio/tests/wavetest/PreComp.h @@ -24,13 +24,11 @@ #include #include #include -#include #include #include #include -#include #include #include #include @@ -47,108 +45,3 @@ #define WARN(log, ...) BasicLogPrintf( log, XWARN, 1, __VA_ARGS__ ) #define BLOCK(log, ...) BasicLogPrintf( log, XBLOCK, 1, __VA_ARGS__ ) #define ERR(log, ...) BasicLogPrintf( log, XFAIL, 1, __VA_ARGS__ ) - -// ------------------------------------------------------------------------------ -// Data flow -enum STACKWISE_DATAFLOW { render, capture }; - -// ------------------------------------------------------------------------------ -// Structs used to hold results of format records -typedef struct -{ - WAVEFORMATEXTENSIBLE wfxEx; - UINT32 fundamentalPeriodInFrames; - UINT32 defaultPeriodInFrames; - UINT32 minPeriodInFrames; - UINT32 maxPeriodInFrames; - UINT32 maxPeriodInFramesExtended; -} FORMAT_RECORD, *PFORMAT_RECORD; - -// ---------------------------------------------------------- -// Parameters for test sine tone -#define TEST_AMPLITUDE 1.0f -#define TEST_FREQUENCY 200.0f - -// ---------------------------------------------------------- -// Predefined format list for offload streaming -#define COUNT_FORMATS 4 - -static WAVEFORMATEXTENSIBLE ListofFormats[COUNT_FORMATS] = -{ - // 1 - { - // Format - { - WAVE_FORMAT_EXTENSIBLE, // wFormatTag - 2, // nChannels - 44100, // nSamplesPerSec - 176400, // nAvgBytesPerSec - 4, // nBlockAlign - 16, // wBitsPerSample - 22 // cbSize - }, - // Samples - { - 16 // wValidBitsPerSample - }, - KSAUDIO_SPEAKER_STEREO, // dwChannelMask - KSDATAFORMAT_SUBTYPE_PCM // SubFormat - }, - // 2 - { - // Format - { - WAVE_FORMAT_EXTENSIBLE, // wFormatTag - 2, // nChannels - 48000, // nSamplesPerSec - 192000, // nAvgBytesPerSec - 4, // nBlockAlign - 16, // wBitsPerSample - 22 // cbSize - }, - // Samples - { - 16 // wValidBitsPerSample - }, - KSAUDIO_SPEAKER_STEREO, // dwChannelMask - KSDATAFORMAT_SUBTYPE_PCM // SubFormat - }, - // 3 - { - // Format - { - WAVE_FORMAT_EXTENSIBLE, // wFormatTag - 2, // nChannels - 88200, // nSamplesPerSec - 352800, // nAvgBytesPerSec - 4, // nBlockAlign - 16, // wBitsPerSample - 22 // cbSize - }, - // Samples - { - 16 // wValidBitsPerSample - }, - KSAUDIO_SPEAKER_STEREO, // dwChannelMask - KSDATAFORMAT_SUBTYPE_PCM // SubFormat - }, - // 4 - { - // Format - { - WAVE_FORMAT_EXTENSIBLE, // wFormatTag - 2, // nChannels - 96000, // nSamplesPerSec - 384000, // nAvgBytesPerSec - 4, // nBlockAlign - 16, // wBitsPerSample - 22 // cbSize - }, - // Samples - { - 16 // wValidBitsPerSample - }, - KSAUDIO_SPEAKER_STEREO, // dwChannelMask - KSDATAFORMAT_SUBTYPE_PCM // SubFormat - }, -}; \ No newline at end of file diff --git a/audio/tests/wavetest/PropertyHelper.h b/audio/tests/wavetest/PropertyHelper.h deleted file mode 100644 index 9f01a9a79..000000000 --- a/audio/tests/wavetest/PropertyHelper.h +++ /dev/null @@ -1,190 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Copyright (C) Microsoft. All rights reserved. -// -// Module Name: -// -// PropertyHelpers.h -// -// Abstract: -// -// Header for common helpers/defines of property -// -// ------------------------------------------------------------------------------- -#pragma once - -#define kSystemDefaultPeriod 100000 // 10 milliseconds -#define kSystemMinPeriodForNonRTCapableEndpoints 20000 // 2 milliseconds - - - - - - - - - - - - - - - - - - - - - - - - - - - -// ---------------------------------------------------------------------- -HRESULT GetConnectorId -( - IMMDevice* pDevice, - EndpointConnectorType eConnectorType, - bool* hasConnector, - UINT* pConnectorId -); - -// ---------------------------------------------------------------------- -HRESULT GetEndpointFriendlyName -( - IMMDevice* pDevice, - LPWSTR* ppwszEndpointName -); - -// ---------------------------------------------------------------------- -HRESULT GetAudioFilterAsDevice -( - IMMDevice* pDevice, - IMMDevice** ppAudioFilterAsDevice -); - -// ---------------------------------------------------------------------- -HRESULT GetCachedProcessingModes -( - IMMDevice* pDevice, - EndpointConnectorType eConnectorType, - ULONG *pCount, - AUDIO_SIGNALPROCESSINGMODE **ppModes -); - - - - - - - - - - - - -// ---------------------------------------------------------------------- -HRESULT GetProcessingModes -( - IMMDevice* pDevice, - UINT pinId, - ULONG *pCount, - AUDIO_SIGNALPROCESSINGMODE **ppModes -); - -// ---------------------------------------------------------------------- -HRESULT GetCachedSupportedFormatRecords -( - IMMDevice* pDevice, - EndpointConnectorType eConnectorType, - AUDIO_SIGNALPROCESSINGMODE mode, - ULONG *pCount, - FORMAT_RECORD **ppFormatRecords -); - - - - - - - - - - - - - -// ---------------------------------------------------------------------- -HRESULT GetSupportedFormatRecords -( - IMMDevice* pDevice, - UINT pinId, - EndpointConnectorType eConnectorType, - AUDIO_SIGNALPROCESSINGMODE mode, - STACKWISE_DATAFLOW dataFlow, - ULONG *pCount, - FORMAT_RECORD **ppFormatRecords -); - -// ---------------------------------------------------------------------- -HRESULT IsFormatSupported -( - IMMDevice* pDevice, - UINT pinId, - WAVEFORMATEX *pWfx, - BOOL* pbSupported -); - -// ---------------------------------------------------------------------- -HRESULT DiscoverPeriodicityCharacteristicsForFormat -( - IMMDevice* pDevice, - EndpointConnectorType eConnectorType, - AUDIO_SIGNALPROCESSINGMODE mode, - WAVEFORMATEX *pWfx, - STACKWISE_DATAFLOW dataFlow, - UINT32 *pDefaultPeriodicityInFrames, - UINT32 *pFundamentalPeriodicityInFrames, - UINT32 *pMinPeriodicityInFrames, - UINT32 *pMaxPeriodicityInFrames, - UINT32 *pMaxPeriodicityInFramesExtended -); - -// ---------------------------------------------------------------------- -HRESULT CheckConnectorSupportForPeriodicity -( - IMMDevice* pDevice, - EndpointConnectorType eConnectorType, - AUDIO_SIGNALPROCESSINGMODE mode, - WAVEFORMATEX *pWfx, - STACKWISE_DATAFLOW dataFlow, - HNSTIME RequestedPeriodicity, - UINT32 *pActualPeriodicityInFrames -); - -// ---------------------------------------------------------------------- -HRESULT GetCachedDefaultFormat -( - IMMDevice* pDevice, - EndpointConnectorType eConnectorType, - WAVEFORMATEX** ppDefaultFormat -); - -// ---------------------------------------------------------------------- -HRESULT GetProposedFormatForProcessingMode -( - IMMDevice* pDevice, - UINT pinId, - AUDIO_SIGNALPROCESSINGMODE mode, - WAVEFORMATEX **ppProposedFormat -); - -// ---------------------------------------------------------------------- -HRESULT GetAvailiablePinInstanceCount -( - IMMDevice* pDevice, - UINT pinId, - UINT32* pAvailablePinInstanceCount -); diff --git a/audio/tests/wavetest/TestResource.h b/audio/tests/wavetest/TestResource.h deleted file mode 100644 index 49dee5a56..000000000 --- a/audio/tests/wavetest/TestResource.h +++ /dev/null @@ -1,66 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// Copyright (C) Microsoft. All rights reserved. -// -// File Name: -// -// TestResource.h -// -// Abstract: -// -// TAEF Test Resource -// -// ------------------------------------------------------------------------------- -#include "WaveTestTaef.h" - -class CPinTestResource : - public WEX::TestExecution::ITestResource, - public IHalfAppContainer -{ -public: - //IUnknown - STDMETHODIMP_(ULONG) AddRef(); - STDMETHODIMP_(ULONG) Release(); - STDMETHODIMP QueryInterface( - __in REFIID riid, - __deref_out VOID **ppvObject - ); - // ITestResouce - STDMETHODIMP GetGuid(GUID* pGuid); - STDMETHODIMP SetGuid(GUID guid); - STDMETHODIMP GetValue(BSTR name, BSTR* pValue); - STDMETHODIMP SetValue(BSTR name, BSTR value); - - //IHalfAppContainer - STDMETHODIMP GetHalfApp(CHalfApp ** ppHalfApp); - - static HRESULT STDMETHODCALLTYPE CreateInstance( - CHalfApp * pHalf, - REFGUID guid, - WEX::TestExecution::ITestResource ** ppOut - ); - -private: - - CPinTestResource(); - ~CPinTestResource(); - - HRESULT STDMETHODCALLTYPE Initialize( - CHalfApp * pHalf, - REFGUID guid - ); - - LPWSTR ModeName(REFGUID guidMode); - LPWSTR PinName(EndpointConnectorType eConnectorType); - - CComBSTR m_szType; - CComBSTR m_szName; - CComBSTR m_szId; - CComBSTR m_szMode; - CComBSTR m_szPin; - - CAutoPtr m_spHalfApp; - - ULONG m_cRef; - GUID m_guid; -}; \ No newline at end of file diff --git a/audio/tests/wavetest/TestResourceBuild.cpp b/audio/tests/wavetest/TestResourceBuild.cpp index 51cce52dd..5ca2ff0d2 100644 --- a/audio/tests/wavetest/TestResourceBuild.cpp +++ b/audio/tests/wavetest/TestResourceBuild.cpp @@ -12,8 +12,8 @@ // // ------------------------------------------------------------------------------ #include "PreComp.h" -#include "TestResource.h" -#include "PropertyHelper.h" +#include +#include using namespace WEX::Common; using namespace WEX::Logging; @@ -30,32 +30,18 @@ using namespace WEX::TestExecution; HRESULT CreateTestResource ( ResourceList& resourceList, - LPWSTR deviceId, - LPWSTR deviceName, - IMMDevice* pDevice, - STACKWISE_DATAFLOW dataFlow, - EndpointConnectorType eConnectorType, - UINT uConnectorId, - AUDIO_SIGNALPROCESSINGMODE mode, - ULONG cModes, - AUDIO_SIGNALPROCESSINGMODE* pModes, - ULONG cFormatRecords, - PFORMAT_RECORD pFormatRecords, - PWAVEFORMATEX pPreferredFormat, - UINT32 defaultPeriodicityInFrames, - UINT32 fundamentalPeriodicityInFrames, - UINT32 minPeriodicityInFrames, - UINT32 maxPeriodicityInFrames + DeviceDescriptor descriptor ) { HRESULT hr = S_OK; + CComHeapPtr spHalfApp; wil::com_ptr_nothrow spTestResource; GUID ResourceGUID; CComBSTR szResourceName; // Create HalfApp - spHalfApp.Attach(new CHalfApp(pDevice, deviceId, deviceName, dataFlow, eConnectorType, uConnectorId, mode, cModes, pModes, cFormatRecords, pFormatRecords, pPreferredFormat, defaultPeriodicityInFrames, fundamentalPeriodicityInFrames, minPeriodicityInFrames, maxPeriodicityInFrames)); + spHalfApp.Attach(new CHalfApp(descriptor)); if (!VERIFY_IS_NOT_NULL(spHalfApp)) { hr = E_OUTOFMEMORY; return hr; @@ -327,6 +313,7 @@ HRESULT AddTestResourceForConnector UINT32 u32MinPeriodicityInFrames; UINT32 u32MaxPeriodicityInFrames; UINT32 u32MaxPeriodicityInFramesExtended; + bool isAVStream = false; // Get connector id if (!VERIFY_SUCCEEDED(hr = GetConnectorId(pDevice, eConnectorType, &bHasConnector, &uConnectorId))) { @@ -338,6 +325,7 @@ HRESULT AddTestResourceForConnector Log::Comment(String().Format(L"Adding Test Resource for pin [%u]:", (uConnectorId & PARTID_MASK))); + // Get all signal processing modes for connector if (!VERIFY_SUCCEEDED(hr = GetProcessingModesForConnector(pDevice, uConnectorId, eConnectorType, &cModes, &spModes))) { return hr; @@ -345,6 +333,7 @@ HRESULT AddTestResourceForConnector // Loop through each mode, get preferred format and list of formats and create test resource for each mode for (ULONG i = 0; i < cModes; i++) { + mode = spModes[i]; if (!VERIFY_SUCCEEDED(hr = GetSupportedFormatRecordsForConnector(pDevice, uConnectorId, eConnectorType, mode, dataFlow, &cFormatRecords, &spFormatRecords))) { @@ -358,8 +347,32 @@ HRESULT AddTestResourceForConnector if (!VERIFY_SUCCEEDED(hr = GetPreferredFormatPeriodicityCharacteristicsForConnector(pDevice, eConnectorType, mode, dataFlow, pPreferredFormat.get(), cFormatRecords, spFormatRecords, &u32DefaultPeriodicityInFrames, &u32FundamentalPeriodicityInFrames, &u32MinPeriodicityInFrames, &u32MaxPeriodicityInFrames, &u32MaxPeriodicityInFramesExtended))) { return hr; } - - if (!VERIFY_SUCCEEDED(hr = CreateTestResource(resourceList, deviceId, deviceName, pDevice, dataFlow, eConnectorType, uConnectorId, mode, cModes, spModes, cFormatRecords, spFormatRecords, pPreferredFormat.get(), u32DefaultPeriodicityInFrames, u32FundamentalPeriodicityInFrames, u32MinPeriodicityInFrames, u32MaxPeriodicityInFrames))) { + + // Check if audio endpoint is AVStream + if (!VERIFY_SUCCEEDED(hr = IsAVStream(pDevice, &isAVStream))) { + return hr; + } + + DeviceDescriptor descriptor = { 0 }; + descriptor.pDevice = pDevice; + descriptor.pwstrAudioEndpointId = deviceId; + descriptor.pwstrAudioEndpointFriendlyName = deviceName; + descriptor.dataFlow = dataFlow; + descriptor.eConnectorType = eConnectorType; + descriptor.uConnectorId = uConnectorId; + descriptor.mode = mode; + descriptor.cModes = cModes; + descriptor.pModes = spModes; + descriptor.cFormatRecords = cFormatRecords; + descriptor.pFormatRecords = spFormatRecords; + descriptor.pPreferredFormat = pPreferredFormat.get(); + descriptor.u32DefaultPeriodicityInFrames = u32DefaultPeriodicityInFrames; + descriptor.u32FundamentalPeriodicityInFrames = u32FundamentalPeriodicityInFrames; + descriptor.u32MinPeriodicityInFrames = u32MinPeriodicityInFrames; + descriptor.u32MaxPeriodicityInFrames = u32MaxPeriodicityInFrames; + descriptor.bIsAVStream = isAVStream; + + if (!VERIFY_SUCCEEDED(hr = CreateTestResource(resourceList, descriptor))) { return hr; } } diff --git a/audio/tests/wavetest/WaveTestTaef.cpp b/audio/tests/wavetest/WaveTestTaef.cpp index dacf44ab9..be5d47f31 100644 --- a/audio/tests/wavetest/WaveTestTaef.cpp +++ b/audio/tests/wavetest/WaveTestTaef.cpp @@ -164,7 +164,7 @@ void WDMAudio::WaveTest::TAEF_StreamMultipleModes() RUN_TEST_CASE(Test_StreamMultipleModes) } -void WDMAudio::WaveTest::TAEF_VerifyPinSupportsPullMode() +void WDMAudio::WaveTest::TAEF_VerifyPinWaveRTConformance() { - RUN_TEST_CASE(Test_VerifyPinSupportsPullMode) -} \ No newline at end of file + RUN_TEST_CASE(Test_VerifyPinWaveRTConformance) +} diff --git a/audio/tests/wavetest/WaveTestTaef.h b/audio/tests/wavetest/WaveTestTaef.h index a8d38305a..8c87a7f85 100644 --- a/audio/tests/wavetest/WaveTestTaef.h +++ b/audio/tests/wavetest/WaveTestTaef.h @@ -13,7 +13,7 @@ // ------------------------------------------------------------------------------- #pragma once -#include "HalfApp.h" +#include extern IBasicLog* g_pBasicLog; @@ -41,6 +41,7 @@ namespace WDMAudio TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Server x64") TEST_CLASS_PROPERTY(L"Kits.MinRelease", L"19H1") TEST_CLASS_PROPERTY(L"Kits.CorePackageComposition", L"Full") + TEST_CLASS_PROPERTY(L"Kits.CorePackageComposition", L"OnecoreUAP") TEST_CLASS_PROPERTY(L"Kits.IsInProc", L"True") TEST_CLASS_PROPERTY(L"Kits.Parameter", L"DeviceID") TEST_CLASS_PROPERTY(L"Kits.Parameter.DeviceID.Description", L"Device id of device under test") @@ -109,7 +110,7 @@ namespace WDMAudio TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*RAW' OR @Id = '*DEFAULT'") END_TEST_METHOD() - BEGIN_TEST_METHOD(TAEF_VerifyPinSupportsPullMode) + BEGIN_TEST_METHOD(TAEF_VerifyPinWaveRTConformance) TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional") TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification") TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature") diff --git a/audio/tests/wavetest/comptest.cpp b/audio/tests/wavetest/comptest.cpp index ef41972cb..db823156d 100644 --- a/audio/tests/wavetest/comptest.cpp +++ b/audio/tests/wavetest/comptest.cpp @@ -17,8 +17,8 @@ #include "tests.h" // ------------------------------------------------------------------------------ -// Test_VerifyPinSupportsPullMode: Test the requirement that WaveRT drivers must support pull mode audio streaming technology. -void Test_VerifyPinSupportsPullMode(void) +// Test_VerifyPinSupportsPullMode: Test the requirement that new drivers must be WaveRT and WaveRT drivers must support event driven audio streaming technology. +void Test_VerifyPinWaveRTConformance(void) { CHalfApp* pHalfApp = g_pWaveTest->m_pHalf; BOOL bIsRTCapable = false; @@ -28,10 +28,17 @@ void Test_VerifyPinSupportsPullMode(void) VERIFY_SUCCEEDED(pHalfApp->m_pAudioDeviceEndpoint->GetRTCaps(&bIsRTCapable)); - // Only require pull mode for WaveRT drivers + // Check for our new requirement - new drivers must be WaveRT. Here we skip the test on AVStream (UAC1/HFP/A2dp) + // For other old drivers that are not WaveRT, may issue Errata to cover the failure. + if (!pHalfApp->m_bIsAVStream) + { + VERIFY_IS_TRUE(bIsRTCapable); + } + + // Only require event driven for WaveRT drivers if (bIsRTCapable) { VERIFY_SUCCEEDED(pHalfApp->m_pAudioDeviceEndpoint->GetEventDrivenCapable(&bIsEventCapable)); VERIFY_IS_TRUE(bIsEventCapable); } -} \ No newline at end of file +} diff --git a/audio/tests/wavetest/tests.h b/audio/tests/wavetest/tests.h index 38a6aaa17..e3ae06913 100644 --- a/audio/tests/wavetest/tests.h +++ b/audio/tests/wavetest/tests.h @@ -24,4 +24,4 @@ void Test_LoopbackStreamDifferentFormat(void); void Test_StreamMultipleModes(void); // Compliance test -void Test_VerifyPinSupportsPullMode(void); \ No newline at end of file +void Test_VerifyPinWaveRTConformance(void); \ No newline at end of file