summaryrefslogtreecommitdiff
path: root/audio/tests/wavetest
diff options
context:
space:
mode:
authorDaniel Rugerio <[email protected]>2019-06-28 16:08:54 -0700
committerAdonais Romero González <[email protected]>2019-06-28 16:08:54 -0700
commit8ffcdad009e4d410f92fabc4e322a76a0be3a69d (patch)
tree378b2d98a4210c61e2bc34f5b8959e87e4be5c6c /audio/tests/wavetest
parent75b2290f2a4ece68739d07135d352cdfec3a9e89 (diff)
Update audio samples (#387)
* Update SysVAD public sample. * Add WaveTest public sample.
Diffstat (limited to 'audio/tests/wavetest')
-rw-r--r--audio/tests/wavetest/HalfApp.cpp1001
-rw-r--r--audio/tests/wavetest/HalfApp.h195
-rw-r--r--audio/tests/wavetest/PreComp.h154
-rw-r--r--audio/tests/wavetest/PropertyHelper.cpp1095
-rw-r--r--audio/tests/wavetest/PropertyHelper.h190
-rw-r--r--audio/tests/wavetest/TestResource.cpp331
-rw-r--r--audio/tests/wavetest/TestResource.h66
-rw-r--r--audio/tests/wavetest/TestResourceBuild.cpp493
-rw-r--r--audio/tests/wavetest/TestResourceHelper.h71
-rw-r--r--audio/tests/wavetest/WaveTestTaef.cpp170
-rw-r--r--audio/tests/wavetest/WaveTestTaef.h124
-rw-r--r--audio/tests/wavetest/comptest.cpp37
-rw-r--r--audio/tests/wavetest/pintest.cpp492
-rw-r--r--audio/tests/wavetest/tests.h27
14 files changed, 4446 insertions, 0 deletions
diff --git a/audio/tests/wavetest/HalfApp.cpp b/audio/tests/wavetest/HalfApp.cpp
new file mode 100644
index 00000000..14a15c08
--- /dev/null
+++ b/audio/tests/wavetest/HalfApp.cpp
@@ -0,0 +1,1001 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+// Module Name:
+//
+// HalfApp.cpp
+//
+// Abstract:
+//
+// Implementation for CHalfApp class
+//
+// -------------------------------------------------------------------------------
+
+#include "PreComp.h"
+#include "WaveTestTaef.h"
+#include "HalfApp.h"
+#include "TestResourceHelper.h"
+#include "PropertyHelper.h"
+#include <dithering.h>
+#include <signal-source.h>
+#include <sine-signal-source.h>
+#include <dvmodule.h>
+
+HRESULT
+CHalfApp::InitializeEndpoint
+(
+ void
+)
+{
+ HRESULT hr = S_OK;
+
+
+
+ wil::unique_prop_variant varActivationParameter;
+
+
+
+ WAVEFORMATEX* pWfx = m_pCurrentFormat.get();
+
+ if (!VERIFY_IS_NOT_NULL(pWfx)) {
+ hr = E_NOTFOUND;
+ return hr;
+ }
+
+
+
+
+
+
+
+
+
+ // Allocate a structure for endpoint creation.
+ varActivationParameter.vt = VT_BLOB;
+
+ // Fill the endpoint creation structure...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ // Activate the endpoint
+ if (m_DataFlow == render) {
+ wil::com_ptr_nothrow<IAudioOutputEndpointRT> spAudioOutputEndpointRT;
+ if (!VERIFY_SUCCEEDED(hr = m_pDevice->Activate(
+ __uuidof(IAudioOutputEndpointRT),
+ CLSCTX_INPROC_SERVER,
+ &varActivationParameter,
+ reinterpret_cast<void**>(&spAudioOutputEndpointRT)))) {
+ return hr;
+ }
+ spAudioOutputEndpointRT.query_to(&m_pAudioDeviceEndpoint);
+ if (!VERIFY_IS_NOT_NULL(m_pAudioDeviceEndpoint)) {
+ hr = E_FAIL;
+ return hr;
+ }
+ }
+ else {
+ wil::com_ptr_nothrow<IAudioInputEndpointRT> spAudioInputEndpointRT;
+ if (!VERIFY_SUCCEEDED(hr = m_pDevice->Activate(
+ __uuidof(IAudioInputEndpointRT),
+ CLSCTX_INPROC_SERVER,
+ &varActivationParameter,
+ reinterpret_cast<void**>(&spAudioInputEndpointRT)))) {
+ return hr;
+ }
+ spAudioInputEndpointRT.query_to(&m_pAudioDeviceEndpoint);
+ if (!VERIFY_IS_NOT_NULL(m_pAudioDeviceEndpoint)) {
+ hr = E_FAIL;
+ return hr;
+ }
+ }
+
+ // Initialize other endpoint interfaces
+ if (!VERIFY_SUCCEEDED(hr = m_pAudioDeviceEndpoint.query_to(&m_pAudioEndpointControl))) {
+ return hr;
+ }
+ if (!VERIFY_SUCCEEDED(hr = m_pAudioDeviceEndpoint.query_to(&m_pAudioEndpoint))) {
+ return hr;
+ }
+ if (!VERIFY_SUCCEEDED(hr = m_pAudioDeviceEndpoint.query_to(&m_pAudioEndpointRT))) {
+ return hr;
+ }
+
+ return hr;
+}
+
+HRESULT
+CHalfApp::ReleaseEndpoint
+(
+ void
+)
+{
+ HRESULT hr = S_OK;
+
+ if (m_pAudioEndpointControl) {
+ m_pAudioEndpointControl->Reset();
+ }
+
+ m_pAudioDeviceEndpoint.reset();
+ m_pAudioEndpointControl.reset();
+ m_pAudioEndpoint.reset();
+ m_pAudioEndpointRT.reset();
+
+ return hr;
+}
+
+HRESULT
+CHalfApp::StartEndpoint
+(
+ void
+)
+{
+ HRESULT hr = S_OK;
+ auto lock = m_CritSec.lock();
+
+ if (!VERIFY_SUCCEEDED(hr = m_pAudioEndpointControl->Start())) {
+ return hr;
+ }
+
+ return hr;
+}
+
+HRESULT
+CHalfApp::StopEndpoint
+(
+ void
+)
+{
+ HRESULT hr = S_OK;
+ auto lock = m_CritSec.lock();
+
+ if (!VERIFY_SUCCEEDED(hr = m_pAudioEndpointControl->Stop())) {
+ return hr;
+ }
+
+ return hr;
+}
+
+HRESULT
+CHalfApp::ResetEndpoint
+(
+ void
+)
+{
+ HRESULT hr = S_OK;
+ auto lock = m_CritSec.lock();
+
+ if (!VERIFY_SUCCEEDED(hr = m_pAudioEndpointControl->Reset())) {
+ return hr;
+ }
+
+ return hr;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+//
+// Creates a pre-load data buffer used by the stream render and capture routines.
+//
+// If the current endpoint is a render one, the data buffer consists of a sine tone
+// wich gets loaded into a stream render routine.
+//
+// In the case of a capture endpoint, the pre-load data consists of silence.
+//
+////////////////////////////////////////////////////////////////////////////////////
+HRESULT
+CHalfApp::CreateSineToneDataBuffer
+(
+ WAVEFORMATEX* pWfx
+)
+{
+ HRESULT hr = S_OK;
+
+ if (!VERIFY_IS_NOT_NULL(pWfx)) {
+ hr = E_FAIL;
+ return hr;
+ }
+
+ m_dwSineToneDataBufferSize = (DWORD)(((ULONGLONG)BUF_LEN_IN_MS * pWfx->nSamplesPerSec / 1000) * pWfx->nBlockAlign);
+ m_pbSineToneDataBuffer.reset((BYTE*)LocalAlloc(LPTR, m_dwSineToneDataBufferSize));
+ if (!VERIFY_IS_NOT_NULL(m_pbSineToneDataBuffer)) {
+ hr = E_OUTOFMEMORY;
+ return hr;
+ }
+
+ if (m_DataFlow == render) // Sine tone for render
+ {
+ if (!VERIFY_SUCCEEDED(hr = FillBufferWithSineSignal(
+ g_pBasicLog, XFAIL,
+ pWfx,
+ TEST_AMPLITUDE, // amplitude
+ TEST_FREQUENCY, // frequency
+ 0.0, // initial phase,
+ 0.0, // dc
+ Method_NoDithering,
+ m_pbSineToneDataBuffer.get(),
+ m_dwSineToneDataBufferSize / pWfx->nBlockAlign,
+ m_dwSineToneDataBufferSize
+ ))) {
+ return hr;
+ }
+
+ m_dwSineToneDataBufferPosition = 0;
+ }
+ else // Silence for capture
+ {
+ memset(m_pbSineToneDataBuffer.get(), 0, m_dwSineToneDataBufferSize);
+ m_dwSineToneDataBufferPosition = 0;
+ }
+
+ return hr;
+}
+
+//////////////////////////////////
+//
+// Clears the current data buffer.
+//
+//////////////////////////////////
+HRESULT
+CHalfApp::ReleaseSineToneDataBuffer
+(
+ void
+)
+{
+ m_pbSineToneDataBuffer = nullptr;
+ m_dwSineToneDataBufferSize = 0;
+ m_dwSineToneDataBufferPosition = 0;
+
+ return S_OK;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+HRESULT
+CHalfApp::InitializeAndSetBuffer
+(
+ HNSTIME requestedPeriodicity,
+ UINT32 u32LatencyCoefficient
+)
+{
+ HRESULT hr = S_OK;
+ BOOL bIsEventCapable = false;
+
+ // If no requested periodicity is provided, use default.
+ if (requestedPeriodicity == 0) {
+ m_hnsPeriod = FRAMES_TO_HNSTIME_DOUBLE(m_u32CurrentDefaultPeriodicityInFrames, m_pCurrentFormat->nSamplesPerSec);
+ }
+ else {
+ m_hnsPeriod = requestedPeriodicity;
+ }
+
+ if (m_ConnectorType == eOffloadConnector) {
+ bIsEventCapable = true;
+ }
+ else {
+ if (!VERIFY_SUCCEEDED(hr = m_pAudioDeviceEndpoint->GetEventDrivenCapable(&bIsEventCapable))) {
+ return hr;
+ }
+ }
+
+ if (bIsEventCapable)
+ {
+ if (!VERIFY_SUCCEEDED(hr = m_pAudioEndpoint->SetStreamFlags(AUDCLNT_STREAMFLAGS_EVENTCALLBACK))) {
+ return hr;
+ }
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = m_pAudioDeviceEndpoint->SetBuffer(m_hnsPeriod, u32LatencyCoefficient))) {
+ return hr;
+ }
+
+ return hr;
+}
+
+HRESULT
+CHalfApp::GetCurrentAvailiablePinInstanceCount
+(
+ UINT32 *pAvailablePinInstanceCount
+)
+{
+ HRESULT hr = S_OK;
+
+ // Read the current pin instance counts
+ if (!VERIFY_SUCCEEDED(hr = GetAvailiablePinInstanceCount(m_pDevice.get(), m_uConnectorId, pAvailablePinInstanceCount))) {
+ return hr;
+ }
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+//
+// Gets a CHalfApp instance based on the current one, but with a different processing mode.
+//
+// The processing mode for the new instance is specified through the secondMode parameter.
+//
+///////////////////////////////////////////////////////////////////////////////////////////
+HRESULT
+CHalfApp::GetSecondHalfApp
+(
+ AUDIO_SIGNALPROCESSINGMODE secondMode,
+ CHalfApp** ppSecondHalfApp
+)
+{
+ HRESULT hr = S_OK;
+ ULONG cFormatRecords = 0;
+ CComHeapPtr<FORMAT_RECORD> spFormatRecords;
+ wil::unique_cotaskmem_ptr<WAVEFORMATEX> pPreferredFormat;
+ UINT32 u32DefaultPeriodicityInFrames;
+ UINT32 u32FundamentalPeriodicityInFrames;
+ UINT32 u32MinPeriodicityInFrames;
+ UINT32 u32MaxPeriodicityInFrames;
+ UINT32 u32MaxPeriodicityInFramesExtended;
+
+ if (!VERIFY_SUCCEEDED(hr = GetSupportedFormatRecordsForConnector(m_pDevice.get(), m_uConnectorId, m_ConnectorType, secondMode, m_DataFlow, &cFormatRecords, &spFormatRecords))) {
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = GetPreferredFormatForConnector(m_pDevice.get(), m_uConnectorId, m_ConnectorType, secondMode, wil::out_param(pPreferredFormat)))) {
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = GetPreferredFormatPeriodicityCharacteristicsForConnector(m_pDevice.get(), m_ConnectorType, secondMode, m_DataFlow, pPreferredFormat.get(), cFormatRecords, spFormatRecords, &u32DefaultPeriodicityInFrames, &u32FundamentalPeriodicityInFrames, &u32MinPeriodicityInFrames, &u32MaxPeriodicityInFrames, &u32MaxPeriodicityInFramesExtended))) {
+ 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);
+ if (*ppSecondHalfApp == NULL) {
+ hr = E_OUTOFMEMORY;
+ return hr;
+ }
+
+ return hr;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Gets a CHalfApp instance corresponding to the host pin of the current audio endpoint.
+//
+// This function is usually called on a test method that involves a loopback pin,
+// in order to be able to start and control a stream in the host pin and get loopback data.
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT
+CHalfApp::GetHostHalfApp
+(
+ CHalfApp** ppHostHalfApp
+)
+{
+ HRESULT hr = S_OK;
+ bool bHasConnector = false;
+ UINT uConnectorId;
+ ULONG cModes = 0;
+ CComHeapPtr<AUDIO_SIGNALPROCESSINGMODE> spModes;
+ bool bRawSupport = false;
+ bool bDefaultSupport = false;
+ AUDIO_SIGNALPROCESSINGMODE mode;
+ ULONG cFormatRecords = 0;
+ CComHeapPtr<FORMAT_RECORD> spFormatRecords;
+ wil::unique_cotaskmem_ptr<WAVEFORMATEX> pPreferredFormat;
+ UINT32 u32DefaultPeriodicityInFrames;
+ UINT32 u32FundamentalPeriodicityInFrames;
+ UINT32 u32MinPeriodicityInFrames;
+ UINT32 u32MaxPeriodicityInFrames;
+ UINT32 u32MaxPeriodicityInFramesExtended;
+
+ // 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);
+
+ // Get host connector id
+ if (!VERIFY_SUCCEEDED(hr = GetConnectorId(m_pDevice.get(), eHostProcessConnector, &bHasConnector, &uConnectorId))) {
+ return hr;
+ }
+ if (!bHasConnector) {
+ return hr;
+ }
+
+ // Get all signal processing modes for host connector
+ if (!VERIFY_SUCCEEDED(hr = GetProcessingModesForConnector(m_pDevice.get(), uConnectorId, eHostProcessConnector, &cModes, &spModes))) {
+ return hr;
+ }
+
+ // It is possible that host pin support multiple processing modes so we pick up one mode. If host pin support default, use default mode. If host pin support raw, use raw mode.
+ for (ULONG i = 0; i < cModes; i++) {
+ if (spModes[i] == AUDIO_SIGNALPROCESSINGMODE_RAW)
+ bRawSupport = true;
+ if (spModes[i] == AUDIO_SIGNALPROCESSINGMODE_DEFAULT)
+ bDefaultSupport = true;
+ }
+ if (!VERIFY_IS_TRUE(bRawSupport || bDefaultSupport)) {
+ hr = E_FAIL;
+ return hr;
+ }
+ mode = bDefaultSupport ? AUDIO_SIGNALPROCESSINGMODE_DEFAULT : AUDIO_SIGNALPROCESSINGMODE_RAW;
+
+ if (!VERIFY_SUCCEEDED(hr = GetSupportedFormatRecordsForConnector(m_pDevice.get(), uConnectorId, eHostProcessConnector, mode, render, &cFormatRecords, &spFormatRecords))) {
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = GetPreferredFormatForConnector(m_pDevice.get(), uConnectorId, eHostProcessConnector, mode, wil::out_param(pPreferredFormat)))) {
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = GetPreferredFormatPeriodicityCharacteristicsForConnector(m_pDevice.get(), eHostProcessConnector, mode, render, pPreferredFormat.get(), cFormatRecords, spFormatRecords, &u32DefaultPeriodicityInFrames, &u32FundamentalPeriodicityInFrames, &u32MinPeriodicityInFrames, &u32MaxPeriodicityInFrames, &u32MaxPeriodicityInFramesExtended))) {
+ 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);
+ if (*ppHostHalfApp == NULL) {
+ hr = E_OUTOFMEMORY;
+ return hr;
+ }
+
+ return hr;
+}
+
+
+HRESULT
+CHalfApp::InitializeStream
+(
+ HNSTIME requestedPeriodicity,
+ UINT32 u32LatencyCoefficient
+)
+{
+ HRESULT hr = S_OK;
+ BOOL bIsEventCapable;
+ wil::unique_cotaskmem_ptr<WAVEFORMATEX> pDeviceFormat;
+
+ if (!VERIFY_IS_TRUE(!m_bStreamInitialized)) {
+ hr = AEERR_ALREADY_INITIALIZED;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = InitializeAndSetBuffer(requestedPeriodicity, u32LatencyCoefficient))) {
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = m_pAudioEndpoint->GetFrameFormat(out_param(pDeviceFormat)))) {
+ return hr;
+ }
+ m_f32EndpointFrameRate = (FLOAT32)pDeviceFormat->nAvgBytesPerSec / (FLOAT32)pDeviceFormat->nBlockAlign;
+ if (!VERIFY_SUCCEEDED(hr = m_pAudioEndpoint->GetLatency(&m_hnsEndpointLatency))) {
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = m_hProcessThreadStartedEvent.create())) {
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = m_hTerminate.create(wil::EventOptions::ManualReset))) {
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = m_pAudioDeviceEndpoint->GetEventDrivenCapable(&bIsEventCapable))) {
+ return hr;
+ }
+
+ if (bIsEventCapable || m_ConnectorType == eOffloadConnector) {
+ m_bIsEventCapable = true;
+ }
+
+ if (m_bIsEventCapable)
+ {
+ if (!VERIFY_SUCCEEDED(hr = m_hEndpointBufferCompleteEvent.create())) {
+ return hr;
+ }
+ if (!VERIFY_SUCCEEDED(hr = m_pAudioEndpoint->SetEventHandle(m_hEndpointBufferCompleteEvent.get()))) {
+ return hr;
+ }
+ }
+
+ if (!m_bIsEventCapable) {
+ m_hTimer.reset(CreateWaitableTimerEx(NULL, NULL, 0, TIMER_ALL_ACCESS));
+ }
+
+ if (m_DataFlow == render) {
+ m_pStreamRoutine = RenderStreamRoutine;
+ }
+ else {
+ m_pStreamRoutine = CaptureStreamRoutine;
+ }
+
+ // To-do: Adjust for offload streaming
+
+ m_bStreamInitialized = true;
+
+ return hr;
+}
+
+HRESULT
+CHalfApp::CleanupStream
+(
+ void
+)
+{
+ HRESULT hr = S_OK;
+
+ // If the thread thread is still active, stop it
+ if (m_hStreamThread)
+ {
+ SignalAndWaitForThread();
+ }
+
+ m_hnsPeriod = 0;
+ m_f32EndpointFrameRate = 0.F;
+ m_hnsEndpointLatency = 0;
+
+ m_pStreamRoutine = nullptr;
+
+ m_hTerminate.reset();
+ m_hEndpointBufferCompleteEvent.reset();
+ m_hProcessThreadStartedEvent.reset();
+ m_hTimer.reset();
+
+ m_bIsEventCapable = false;
+ m_bStreamInitialized = false;
+ m_bStreamThreadTerminate = false;
+ m_bYieldActive = false;
+
+ return hr;
+}
+
+DWORD CALLBACK
+CHalfApp::RenderStreamRoutine
+(
+ PVOID lpParameter
+)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+DWORD CALLBACK
+CHalfApp::CaptureStreamRoutine
+(
+ PVOID lpParameter
+)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+HRESULT
+CHalfApp::StartStream
+(
+ void
+)
+{
+ HRESULT hr = S_OK;
+
+ if (!VERIFY_IS_TRUE(m_bStreamInitialized)) {
+ hr = AEERR_NOT_INITIALIZED;
+ return hr;
+ }
+ if (!VERIFY_IS_TRUE(!m_hStreamThread)) {
+ hr = AEERR_ALREADY_RUNNING;
+ return hr;
+ }
+
+ m_bStreamThreadTerminate = false;
+ m_hTerminate.ResetEvent();
+ m_hProcessThreadStartedEvent.ResetEvent();
+
+ DWORD dwThreadID;
+ m_hStreamThread.reset(CreateThread(NULL, 0, m_pStreamRoutine, this, 0, &dwThreadID));
+ if (!VERIFY_IS_NOT_NULL(m_hStreamThread)) {
+ DWORD dwError = GetLastError();
+ hr = HRESULT_FROM_WIN32(dwError);
+ return hr;
+ }
+ if (m_hStreamThread)
+ {
+ SetThreadPriority(m_hStreamThread.get(), THREAD_PRIORITY_HIGHEST);
+ }
+
+ // Wait for the processing thread to be started or to error out. To avoid the Start call from being blocked forever, give up waiting after 10 seconds, which is a duration quite unlikely to hit -- something must have gone wrong in the thread creation.
+ WaitForSingleObjectEx(m_hProcessThreadStartedEvent.get(), 10000, FALSE);
+
+ // Set timer
+ if (!m_bIsEventCapable) {
+ SetTimer(m_hTimer.get(), m_hnsPeriod, true);
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = StartEndpoint())) {
+ return hr;
+ }
+
+ return hr;
+}
+
+HRESULT
+CHalfApp::StopStream
+(
+ void
+)
+{
+ HRESULT hr = S_OK;
+
+ m_bStreamThreadTerminate = true;
+
+ if (!VERIFY_IS_TRUE(m_bStreamInitialized)) {
+ hr = AEERR_NOT_INITIALIZED;
+ return hr;
+ }
+ if (!VERIFY_IS_NOT_NULL(m_hStreamThread)) {
+ hr = AEERR_NOT_RUNNING;
+ return hr;
+ }
+
+ SignalAndWaitForThread();
+
+ //Cancel timer
+ if (!m_bIsEventCapable) {
+ CancelTimer(m_hTimer.get());
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = StopEndpoint())) {
+ return hr;
+ }
+
+ return hr;
+}
+
+void
+CHalfApp::SignalAndWaitForThread
+(
+ void
+)
+{
+ DWORD dwReturn;
+
+ m_bStreamThreadTerminate = true;
+ m_hTerminate.SetEvent();
+
+ dwReturn = WaitForSingleObjectEx(m_hStreamThread.get(), INFINITE, FALSE);
+
+ VERIFY_IS_TRUE(WAIT_OBJECT_0 == dwReturn);
+
+ m_hStreamThread.reset();
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+inline HRESULT CHalfApp::SetTimer(HANDLE timer, HNSTIME timeDuration, bool fireImmediatley)
+{
+ if (timer)
+ {
+ LARGE_INTEGER DueTime;
+
+ if (fireImmediatley)
+ {
+ DueTime.QuadPart = 0;
+ }
+ else
+ {
+ // Negate this to mean relative in call to SetWaitableTimer
+ DueTime.QuadPart = -timeDuration;
+ }
+
+ SetWaitableTimer(timer, &DueTime,
+ static_cast<UINT32>(MF_TO_MS(timeDuration)),
+ nullptr, nullptr, FALSE);
+ }
+ return S_OK;
+}
+
+inline void CHalfApp::CancelTimer(HANDLE timer)
+{
+ if (timer)
+ {
+ // Cancel the waitable timer
+ CancelWaitableTimer(timer);
+
+ // There is a race condition where the timer could have fired before it was canceled, which would leave the handle set to fire the next time events are waited upon. CancelWaitableTimer will not reset the state of the handle so
+ // wait for 0 ms for the timer to fire to clear it.
+ WaitForSingleObject(timer, 0);
+ }
+}
+
+HRESULT CHalfApp::GetPosition(UINT64* pu64Position, UINT64* pu64hnsQPCPosition)
+{
+ HRESULT hr = S_OK;
+ wil::com_ptr_nothrow<IAudioClock> spAudioClock;
+ auto lock = m_CritSec.lock();
+
+ if (!VERIFY_SUCCEEDED(m_pAudioDeviceEndpoint.query_to(&spAudioClock))) {
+ return hr;
+ }
+ if (!VERIFY_SUCCEEDED(spAudioClock->GetPosition(pu64Position, pu64hnsQPCPosition))) {
+ return hr;
+ }
+
+ return S_OK;
+}
diff --git a/audio/tests/wavetest/HalfApp.h b/audio/tests/wavetest/HalfApp.h
new file mode 100644
index 00000000..1bf14de9
--- /dev/null
+++ b/audio/tests/wavetest/HalfApp.h
@@ -0,0 +1,195 @@
+// ------------------------------------------------------------------------------
+//
+// 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<IMMDevice> 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<WAVEFORMATEX> 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<WAVEFORMATEX> m_pCurrentFormat;
+ UINT32 m_u32CurrentDefaultPeriodicityInFrames;
+ UINT32 m_u32CurrentFundamentalPeriodicityInFrames;
+ UINT32 m_u32CurrentMinPeriodicityInFrames;
+ UINT32 m_u32CurrentMaxPeriodicityInFrames;
+
+ // Audio endpoint interfaces
+ wil::com_ptr_nothrow<IAudioDeviceEndpoint> m_pAudioDeviceEndpoint;
+ wil::com_ptr_nothrow<IAudioEndpoint> m_pAudioEndpoint;
+ wil::com_ptr_nothrow<IAudioEndpointControl> m_pAudioEndpointControl;
+ wil::com_ptr_nothrow<IAudioEndpointRT> m_pAudioEndpointRT;
+
+ // Data buffer
+ wil::unique_hlocal_ptr<BYTE> 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
new file mode 100644
index 00000000..2044d6b1
--- /dev/null
+++ b/audio/tests/wavetest/PreComp.h
@@ -0,0 +1,154 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+// File Name:
+//
+// PreComp.h
+//
+// Abstract:
+//
+// Precompiled common headers
+//
+// -------------------------------------------------------------------------------
+
+#include <basiclog.h>
+#include <BasicLogHelper.h>
+#include <mmsystem.h>
+#include <atlbase.h>
+#include <atlcom.h>
+#include <initguid.h>
+
+#include <AudioEngineEndpoint.h>
+#include <AudioEngineEndpointP.h>
+#include <Audioclient.h>
+#include <mmdeviceapi.h>
+#include <mmdeviceapip.h>
+#include <devicetopology.h>
+#include <devicetopologyp.h>
+#include <propvarutil.h>
+
+#include <wil\com.h>
+
+#include <winioctl.h>
+#include <KsLib.h>
+#include <Waveutil.h>
+#include <aecommon.h>
+#include <ilog.h>
+#include <shlflags.h>
+#include <basicprintf.h>
+#include <wextestclass.h>
+#include <resourcelist.h>
+
+// ------------------------------------------------------------------------------
+// Log
+#define LOG(log, ...) BasicLogPrintf( log, XMSG, 1, __VA_ARGS__ )
+#define SKIP(log, ...) BasicLogPrintf( log, XSKIP, 1, __VA_ARGS__ )
+#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.cpp b/audio/tests/wavetest/PropertyHelper.cpp
new file mode 100644
index 00000000..fb60d3cc
--- /dev/null
+++ b/audio/tests/wavetest/PropertyHelper.cpp
@@ -0,0 +1,1095 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+// Module Name:
+//
+// EndpointPropertyHelpers.cpp
+//
+// Abstract:
+//
+// Implementation for common helpers of endpoint property
+//
+// -------------------------------------------------------------------------------
+#include "PreComp.h"
+#include <AVEndpointKeys.h>
+#include <Functiondiscoverykeys_devpkey.h>
+#include "PropertyHelper.h"
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetConnectorId
+//
+// Retrieve the connector id from the property store and checks if the connector exists
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetConnectorId
+(
+ IMMDevice* pDevice,
+ EndpointConnectorType eConnectorType,
+ bool* hasConnector,
+ UINT* pConnectorId
+)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetEndpointFriendlyName
+//
+// Retrieve the endpoint friendly name from the property store
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetEndpointFriendlyName
+(
+ IMMDevice* pDevice,
+ LPWSTR* ppwszEndpointName
+)
+{
+ HRESULT hr = S_OK;
+ wil::com_ptr_nothrow<IPropertyStore> spPropertyStore;
+ wil::unique_prop_variant var;
+ wil::unique_cotaskmem_string pwszName;
+
+ *ppwszEndpointName = NULL;
+
+ // Retrieve the endpoint friendly name from the propertey store
+ if (!VERIFY_SUCCEEDED(hr = pDevice->OpenPropertyStore(STGM_READ, &spPropertyStore))) {
+ return hr;
+ }
+ if (!VERIFY_SUCCEEDED(hr = spPropertyStore->GetValue(PKEY_Device_FriendlyName, &var))) {
+ return hr;
+ }
+ if (!VERIFY_IS_TRUE(var.vt == VT_LPWSTR)) {
+ hr = E_FAIL;
+ return hr;
+ }
+
+ pwszName = wil::make_cotaskmem_string_nothrow(var.pwszVal);
+ if (!VERIFY_IS_TRUE(pwszName.get() && wcslen(pwszName.get()) != 0)) {
+ hr = E_FAIL;
+ return hr;
+ }
+
+ *ppwszEndpointName = pwszName.release();
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetAudioFilterAsDevice
+//
+// Get the audio filter from MMDevice. This method is called to activate IKsControl, IKsFormatSupport or IKsGetProposedFormat.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetAudioFilterAsDevice
+(
+ IMMDevice* pDevice,
+ IMMDevice** ppAudioFilterAsDevice
+)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetCachedProcessingModes
+//
+// Read the processing mode characteristics from the property store and get all processing modes. This method applies to host and keyword detector pins only.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetCachedProcessingModes
+(
+ IMMDevice* pDevice,
+ EndpointConnectorType eConnectorType,
+ ULONG *pCount,
+ AUDIO_SIGNALPROCESSINGMODE **ppModes
+)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetProcessingModes
+//
+// Query for connector's processing modes property
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetProcessingModes
+(
+ IMMDevice* pDevice,
+ UINT pinId,
+ ULONG *pCount,
+ AUDIO_SIGNALPROCESSINGMODE **ppModes
+)
+{
+ HRESULT hr = S_OK;
+ wil::com_ptr_nothrow<IMMDevice> spAudioFilter;
+ wil::com_ptr_nothrow<IKsControl> spKsControl;
+ KSP_PIN pinProp;
+ ULONG cbNeeded = 0;
+ ULONG cbProperty;
+ PKSMULTIPLE_ITEM pProperty = NULL;
+ ULONG cbReturned = 0;
+ AUDIO_SIGNALPROCESSINGMODE* pModes = NULL;
+ CComHeapPtr<AUDIO_SIGNALPROCESSINGMODE> spModes;
+
+ *pCount = 0;
+ *ppModes = NULL;
+
+ // Get the audio filter
+ if (!VERIFY_SUCCEEDED(hr = GetAudioFilterAsDevice(pDevice, &spAudioFilter))) {
+ return hr;
+ }
+
+ // Activate IKsControl
+ if (!VERIFY_SUCCEEDED(hr = spAudioFilter->Activate(__uuidof(IKsControl), CLSCTX_ALL, NULL, (VOID**)&spKsControl))) {
+ return hr;
+ }
+
+ // Read the audio signal processing mode property
+ pinProp.Property.Set = KSPROPSETID_AudioSignalProcessing;
+ pinProp.Property.Id = KSPROPERTY_AUDIOSIGNALPROCESSING_MODES;
+ pinProp.Property.Flags = KSPROPERTY_TYPE_GET;
+ pinProp.PinId = pinId & PARTID_MASK;
+ pinProp.Reserved = 0;
+
+ // Determined the needed size
+ if (!VERIFY_SUCCEEDED(hr = spKsControl->KsProperty(&pinProp.Property, sizeof(KSP_PIN), NULL, 0, &cbNeeded))) {
+ return hr;
+ }
+ if (!VERIFY_IS_TRUE(cbNeeded > 0) || !VERIFY_IS_TRUE((cbNeeded - sizeof(KSMULTIPLE_ITEM)) % sizeof(GUID) == 0)) {
+ hr = E_NOTFOUND;
+ return hr;
+ }
+
+ cbProperty = cbNeeded;
+ pProperty = (PKSMULTIPLE_ITEM)CoTaskMemAlloc(cbNeeded);
+
+ if (!VERIFY_IS_NOT_NULL(pProperty)) {
+ hr = E_OUTOFMEMORY;
+ cbProperty = 0;
+ return hr;
+ }
+
+ // Query the processing mode property
+ if (!VERIFY_SUCCEEDED(hr = spKsControl->KsProperty(&pinProp.Property, sizeof(KSP_PIN), pProperty, cbNeeded, &cbReturned))) {
+ goto Exit;
+ }
+
+ if (!VERIFY_IS_NOT_NULL(pProperty)) {
+ hr = E_NOTFOUND;
+ goto Exit;
+ }
+
+ if (!VERIFY_IS_TRUE(cbProperty >= (sizeof(KSMULTIPLE_ITEM) + pProperty->Count * sizeof(GUID)))) {
+ hr = E_NOTFOUND;
+ goto Exit;
+ }
+
+ pModes = reinterpret_cast<GUID*>(pProperty + 1);
+
+ if (!VERIFY_IS_TRUE(spModes.Allocate(pProperty->Count)))
+ {
+ hr = E_OUTOFMEMORY;
+ goto Exit;
+ }
+
+ memcpy(spModes, pModes, sizeof(AUDIO_SIGNALPROCESSINGMODE)*(pProperty->Count));
+
+ *pCount = pProperty->Count;
+ *ppModes = spModes.Detach();
+
+Exit:
+ if (pProperty != NULL) {
+ CoTaskMemFree(pProperty);
+ }
+ cbProperty = 0;
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetCachedSupportedFormatRecords
+//
+// Read the processing mode characteristics from the property store and get all supported formats for processing mode. This method applies to host and keyword detector pins only.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetCachedSupportedFormatRecords
+(
+ IMMDevice* pDevice,
+ EndpointConnectorType eConnectorType,
+ AUDIO_SIGNALPROCESSINGMODE mode,
+ ULONG *pCount,
+ FORMAT_RECORD **ppFormatRecords
+)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetSupportedFormatRecords
+//
+// For pins do not have cached supported formats information, such as offload pin, we provide our predefined format list and check whether each is supported by the pin.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetSupportedFormatRecords
+(
+ IMMDevice* pDevice,
+ UINT pinId,
+ EndpointConnectorType eConnectorType,
+ AUDIO_SIGNALPROCESSINGMODE mode,
+ STACKWISE_DATAFLOW dataFlow,
+ ULONG *pCount,
+ FORMAT_RECORD **ppFormatRecords
+)
+{
+ HRESULT hr = S_OK;
+ FORMAT_RECORD *pFormatRecords;
+ UINT cFormatRecords = 0;
+ CComHeapPtr<FORMAT_RECORD> spFormatRecords;
+ BOOL isSupported = false;
+ WAVEFORMATEX *pWfx;
+ UINT32 u32DefaultPeriodicityInFrames = 0;
+ UINT32 u32FundamentalPeriodicityInFrames = 0;
+ UINT32 u32MinPeriodicityInFrames = 0;
+ UINT32 u32MaxPeriodicityInFrames = 0;
+ UINT32 u32MaxPeriodicityInFramesExtended = 0;
+
+ // Init return
+ *pCount = 0;
+ *ppFormatRecords = NULL;
+
+ pFormatRecords = new FORMAT_RECORD[COUNT_FORMATS];
+ if (!VERIFY_IS_NOT_NULL(pFormatRecords)) {
+ hr = E_OUTOFMEMORY;
+ return hr;
+ }
+ memset(pFormatRecords, 0, sizeof(FORMAT_RECORD)*COUNT_FORMATS);
+
+ for (ULONG i = 0; i < COUNT_FORMATS; i++) {
+ pWfx = (WAVEFORMATEX*)&ListofFormats[i];
+
+ // Check if format is supported
+ if (!VERIFY_SUCCEEDED(hr = IsFormatSupported(pDevice, pinId, pWfx, &isSupported))) {
+ return hr;
+ }
+
+ // If the format is supported, we discover the periodicity characteristics for the format and store them along with the format in our FORMAT_RECORD struct.
+ if (isSupported) {
+ if (!VERIFY_SUCCEEDED(hr = DiscoverPeriodicityCharacteristicsForFormat(pDevice, eConnectorType, mode, pWfx, dataFlow, &u32DefaultPeriodicityInFrames, &u32FundamentalPeriodicityInFrames, &u32MinPeriodicityInFrames, &u32MaxPeriodicityInFrames, &u32MaxPeriodicityInFramesExtended))) {
+ return hr;
+ }
+
+ pFormatRecords[cFormatRecords].defaultPeriodInFrames = u32DefaultPeriodicityInFrames;
+ pFormatRecords[cFormatRecords].fundamentalPeriodInFrames = u32FundamentalPeriodicityInFrames;
+ pFormatRecords[cFormatRecords].minPeriodInFrames = u32MinPeriodicityInFrames;
+ pFormatRecords[cFormatRecords].maxPeriodInFrames = u32MaxPeriodicityInFrames;
+ pFormatRecords[cFormatRecords].maxPeriodInFramesExtended = u32MaxPeriodicityInFramesExtended;
+ memcpy(&pFormatRecords[cFormatRecords].wfxEx, &ListofFormats[i], sizeof(WAVEFORMATEX) + ListofFormats[i].Format.cbSize);
+ cFormatRecords++;
+ }
+ }
+
+ if (!VERIFY_IS_TRUE(spFormatRecords.Allocate(cFormatRecords))) {
+ hr = E_OUTOFMEMORY;
+ return hr;
+ }
+
+ memcpy(spFormatRecords, pFormatRecords, sizeof(FORMAT_RECORD)*cFormatRecords);
+ delete[] pFormatRecords;
+
+ *pCount = cFormatRecords;
+ *ppFormatRecords = spFormatRecords.Detach();
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// IsFormatSupported
+//
+// Wrap up the IKsFormatSupport::IsFormatSupported method.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT IsFormatSupported
+(
+ IMMDevice* pDevice,
+ UINT pinId,
+ WAVEFORMATEX *pWfx,
+ BOOL* pbSupported
+)
+{
+ HRESULT hr = S_OK;
+ wil::com_ptr_nothrow<IMMDevice> spAudioFilter;
+ wil::com_ptr_nothrow<IDeviceTopology> spAudioFilterTopology;
+ wil::com_ptr_nothrow<IPart> spPart;
+ wil::com_ptr_nothrow<IKsFormatSupport> spFormatSupport;
+ PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pKsRequestedFormat = NULL;
+ PKSDATAFORMAT pKsFormat = NULL;
+ KSDATAFORMAT_WAVEFORMATEXTENSIBLE KsWfex = {};
+ DWORD cbKsFormat = 0;
+
+ // Init return
+ *pbSupported = FALSE;
+
+ // Get the audio filter
+ if (!VERIFY_SUCCEEDED(hr = GetAudioFilterAsDevice(pDevice, &spAudioFilter))) {
+ return hr;
+ }
+
+ // Activate an IDeviceTopology on the filter
+ if (!VERIFY_SUCCEEDED(hr = spAudioFilter->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spAudioFilterTopology))) {
+ return hr;
+ }
+
+ // Get the connector
+ if (!VERIFY_SUCCEEDED(hr = spAudioFilterTopology->GetPartById(pinId, &spPart))) {
+ return hr;
+ }
+
+ // Activate IKsFormatSupport interface
+ if (!VERIFY_SUCCEEDED(hr = spPart->Activate(CLSCTX_ALL, __uuidof(IKsFormatSupport), (void**)&spFormatSupport))) {
+ return hr;
+ }
+
+ // Convert to KSDATAFORMAT_WAVEFORMATEX
+ if (!VERIFY_SUCCEEDED(hr = CreateKSFormatFromWFXFormat(pWfx, (KSDATAFORMAT_WAVEFORMATEX**)&pKsRequestedFormat))) {
+ return hr;
+ }
+
+ if (pKsRequestedFormat->DataFormat.FormatSize < sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE))
+ {
+ memcpy(&KsWfex, pKsRequestedFormat, pKsRequestedFormat->DataFormat.FormatSize);
+ pKsFormat = reinterpret_cast<KSDATAFORMAT *>(&KsWfex);
+ cbKsFormat = sizeof(KsWfex);
+ }
+ else
+ {
+ pKsFormat = reinterpret_cast<KSDATAFORMAT *>(pKsRequestedFormat);
+ cbKsFormat = pKsRequestedFormat->DataFormat.FormatSize;
+ }
+
+ // Expect that pbFormat is really a KSDATAFORMAT
+ if (!VERIFY_IS_TRUE(cbKsFormat >= sizeof(KSDATAFORMAT))) {
+ hr = E_INVALIDARG;
+ return hr;
+ }
+
+ // validate return pointers (this is an externally callable method)
+ if (!VERIFY_IS_TRUE(pKsFormat)) {
+ hr = E_POINTER;
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = spFormatSupport->IsFormatSupported(pKsFormat, cbKsFormat, pbSupported))) {
+ return hr;
+ }
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// DiscoverPeriodicityCharacteristicsForFormat
+//
+// Discover the periodicity characteristics for given format.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+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 hr = S_OK;
+ HNSTIME hnsDefaultPeriod = kSystemDefaultPeriod;
+ UINT32 ActualPeriodicityInFrames;
+
+ // Verify format is not null
+ if (!VERIFY_IS_NOT_NULL(pWfx)) {
+ hr = E_INVALIDARG;
+ return hr;
+ }
+
+ *pDefaultPeriodicityInFrames =
+ *pFundamentalPeriodicityInFrames =
+ *pMinPeriodicityInFrames =
+ *pMaxPeriodicityInFrames = HNSTIME_TO_FRAMES_DOUBLE(hnsDefaultPeriod, pWfx->nSamplesPerSec);
+
+ // Check to see whether the endpoint supports the default periodicity and return the actual periodicity
+ if (!VERIFY_SUCCEEDED(hr = CheckConnectorSupportForPeriodicity(pDevice, eConnectorType, mode, pWfx, dataFlow, hnsDefaultPeriod, &ActualPeriodicityInFrames))) {
+ return hr;
+ }
+
+ // Use what we get as the default, max, min and fundamental periodicity
+ *pDefaultPeriodicityInFrames =
+ *pFundamentalPeriodicityInFrames =
+ *pMinPeriodicityInFrames =
+ *pMaxPeriodicityInFrames = ActualPeriodicityInFrames;
+
+ // Max periodicity is capped to the default periodicity for all clients except extended periodicity clients
+ *pMaxPeriodicityInFramesExtended = *pMaxPeriodicityInFrames;
+ if (*pMaxPeriodicityInFrames > *pDefaultPeriodicityInFrames)
+ {
+ *pMaxPeriodicityInFrames = *pDefaultPeriodicityInFrames;
+ }
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// CheckConnectorSupportForPeriodicity
+//
+// Check to see if the connector supports the specified periodicity and return the actual periodicity.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT CheckConnectorSupportForPeriodicity
+(
+ IMMDevice* pDevice,
+ EndpointConnectorType eConnectorType,
+ AUDIO_SIGNALPROCESSINGMODE mode,
+ WAVEFORMATEX *pWfx,
+ STACKWISE_DATAFLOW dataFlow,
+ HNSTIME RequestedPeriodicity,
+ UINT32 *pActualPeriodicityInFrames
+)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetCachedDefaultFormat
+//
+// Read the default device format from the property store. Use audio engine device format for all other pin types. For Keyword Detector pin, it has a different default device format.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetCachedDefaultFormat
+(
+ IMMDevice* pDevice,
+ EndpointConnectorType eConnectorType,
+ WAVEFORMATEX** ppDefaultFormat
+)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetProposedFormatForProcessingMode
+//
+// Wrap up the IKsGetProposedFormat::GetProposedFormatForMode method.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetProposedFormatForProcessingMode
+(
+ IMMDevice* pDevice,
+ UINT pinId,
+ AUDIO_SIGNALPROCESSINGMODE mode,
+ WAVEFORMATEX **ppProposedFormat
+)
+{
+ HRESULT hr = S_OK;
+ wil::com_ptr_nothrow<IMMDevice> spAudioFilter;
+ wil::com_ptr_nothrow<IDeviceTopology> spAudioFilterTopology;
+ wil::com_ptr_nothrow<IPart> spPart;
+
+
+
+ PKSDATAFORMAT pKsProposedFormat = NULL;
+ WAVEFORMATEXTENSIBLE* pWfxEx = NULL;
+
+ // Init return
+ *ppProposedFormat = NULL;
+
+ if (!VERIFY_SUCCEEDED(hr = GetAudioFilterAsDevice(pDevice, &spAudioFilter))) {
+ return hr;
+ }
+
+ // Activate an IDeviceTopology on the filter
+ if (!VERIFY_SUCCEEDED(hr = spAudioFilter->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spAudioFilterTopology))) {
+ return hr;
+ }
+
+ // Get the connector
+ if (!VERIFY_SUCCEEDED(hr = spAudioFilterTopology->GetPartById(pinId, &spPart))) {
+ return hr;
+ }
+
+ // Retrieve the proposed format for a specific processing mode and store it in pKsProposedFormat...
+
+
+
+
+
+
+
+
+
+
+
+
+ // Allocate memory for ppOffloadDefaultFormat and copy pKsDataDeviceFormat to ppOffloadDefaultFormat
+ pWfxEx = (WAVEFORMATEXTENSIBLE *)CoTaskMemAlloc(sizeof(WAVEFORMATEX) + ((PKSDATAFORMAT_WAVEFORMATEX)pKsProposedFormat)->WaveFormatEx.cbSize);
+ if (!VERIFY_IS_NOT_NULL(pWfxEx)) {
+ CoTaskMemFree(pKsProposedFormat);
+ hr = E_OUTOFMEMORY;
+ return hr;
+ }
+ memcpy(pWfxEx, &(((PKSDATAFORMAT_WAVEFORMATEX)pKsProposedFormat)->WaveFormatEx), sizeof(WAVEFORMATEX) + ((PKSDATAFORMAT_WAVEFORMATEX)pKsProposedFormat)->WaveFormatEx.cbSize);
+ *ppProposedFormat = (WAVEFORMATEX*)pWfxEx;
+
+ return hr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// GetCurrentAvailiablePinInstanceCount
+//
+// Query for the connector's pin instance count property.
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+HRESULT GetAvailiablePinInstanceCount
+(
+ IMMDevice* pDevice,
+ UINT pinId,
+ UINT32* pAvailablePinInstanceCount
+)
+{
+ HRESULT hr = S_OK;
+ wil::com_ptr_nothrow<IMMDevice> spAudioFilter;
+ wil::com_ptr_nothrow<IKsControl> spKsControl;
+ KSP_PIN pinProp;
+ KSPIN_CINSTANCES pinInstances;
+ ULONG cbReturned = 0;
+
+ *pAvailablePinInstanceCount = 0;
+
+ if (!VERIFY_SUCCEEDED(hr = GetAudioFilterAsDevice(pDevice, &spAudioFilter))) {
+ return hr;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = spAudioFilter->Activate(__uuidof(IKsControl), CLSCTX_ALL, NULL, (VOID**)&spKsControl))) {
+ return hr;
+ }
+
+ // Read the pin instance count
+ pinProp.Property.Set = KSPROPSETID_Pin;
+ pinProp.Property.Id = KSPROPERTY_PIN_CINSTANCES;
+ pinProp.Property.Flags = KSPROPERTY_TYPE_GET;
+ pinProp.PinId = pinId & PARTID_MASK;
+ pinProp.Reserved = 0;
+
+ if (!VERIFY_SUCCEEDED(hr = spKsControl->KsProperty(&pinProp.Property, sizeof(KSP_PIN), &pinInstances, sizeof(KSPIN_CINSTANCES), &cbReturned))) {
+ return hr;
+ }
+
+ *pAvailablePinInstanceCount = pinInstances.PossibleCount - pinInstances.CurrentCount;
+
+ return hr;
+}
diff --git a/audio/tests/wavetest/PropertyHelper.h b/audio/tests/wavetest/PropertyHelper.h
new file mode 100644
index 00000000..9f01a9a7
--- /dev/null
+++ b/audio/tests/wavetest/PropertyHelper.h
@@ -0,0 +1,190 @@
+// ------------------------------------------------------------------------------
+//
+// 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.cpp b/audio/tests/wavetest/TestResource.cpp
new file mode 100644
index 00000000..67f3ca79
--- /dev/null
+++ b/audio/tests/wavetest/TestResource.cpp
@@ -0,0 +1,331 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+// File Name:
+//
+// TestResource.cpp
+//
+// Abstract:
+//
+// TAEF Test Resource implementation
+//
+// -------------------------------------------------------------------------------
+
+#include "PreComp.h"
+#include "HalfApp.h"
+#include "TestResource.h"
+
+using namespace WEX::Logging;
+using namespace WEX::TestExecution;
+
+CPinTestResource::CPinTestResource() :
+ m_cRef(1)
+{}
+
+CPinTestResource::~CPinTestResource()
+{}
+
+HRESULT STDMETHODCALLTYPE
+CPinTestResource::CreateInstance
+(
+ CHalfApp * pHalf,
+ REFGUID guid,
+ ITestResource ** ppOut
+)
+{
+ HRESULT hr = S_OK;
+ CPinTestResource * pTestResource;
+ SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures);
+
+ if (!VERIFY_IS_NOT_NULL(pHalf))
+ {
+ return E_INVALIDARG;
+ }
+
+ if (!VERIFY_IS_NOT_NULL(ppOut))
+ {
+ return E_POINTER;
+ }
+
+ pTestResource = new CPinTestResource();
+ if (!VERIFY_IS_NOT_NULL(pTestResource))
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = pTestResource->Initialize(
+ pHalf, guid))) {
+ pTestResource->Release();
+ return hr;
+ }
+
+ *ppOut = pTestResource;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE
+CPinTestResource::Initialize
+(
+ CHalfApp * pHalf,
+ REFGUID guid
+)
+{
+ HRESULT hr = S_OK;
+
+ WCHAR szStr[MAX_PATH];
+ LPWSTR szMode;
+ LPWSTR szPin;
+
+ SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures);
+
+ m_guid = guid;
+ m_spHalfApp.Attach(pHalf);
+
+ // Dataflow
+ if (m_spHalfApp->m_DataFlow == capture)
+ {
+ m_szType.Attach(W2BSTR(L"Capture"));
+ if (!VERIFY_IS_NOT_NULL(m_szType)) { return E_OUTOFMEMORY; }
+ }
+ else
+ {
+ m_szType.Attach(W2BSTR(L"Render"));
+ if (!VERIFY_IS_NOT_NULL(m_szType)) { return E_OUTOFMEMORY; }
+ }
+
+ szMode = ModeName(m_spHalfApp->m_Mode);
+ szPin = PinName(m_spHalfApp->m_ConnectorType);
+
+ // Id: Combine device Id, pin type and mode
+ m_szId.Attach(W2BSTR(m_spHalfApp->m_pwstrDeviceId.get()));
+ if (!VERIFY_SUCCEEDED(hr = StringCchPrintfW(
+ szStr, MAX_PATH, L"#%s#%s",
+ szPin,
+ szMode))) {
+ return hr;
+ }
+ if (!VERIFY_SUCCEEDED(hr = m_szId.Append(szStr))) {
+ return hr;
+ }
+ if (!VERIFY_IS_NOT_NULL(m_szId)) { return E_OUTOFMEMORY; }
+
+ // Name: Combine device friendly name, pin type, and mode
+ m_szName.Attach(W2BSTR(m_spHalfApp->m_pwstrDeviceFriendlyName.get()));
+ if (!VERIFY_SUCCEEDED(hr = StringCchPrintfW(
+ szStr, MAX_PATH, L" Pin %s Mode %s",
+ szPin,
+ szMode))) {
+ return hr;
+ }
+ if (!VERIFY_SUCCEEDED(hr = m_szName.Append(szStr))) {
+ return hr;
+ }
+ if (!VERIFY_IS_NOT_NULL(m_szName)) { return E_OUTOFMEMORY; }
+
+ // Mode
+ m_szMode.Attach(W2BSTR(szMode));
+ if (!VERIFY_IS_NOT_NULL(m_szMode)) { return E_OUTOFMEMORY; }
+
+ // Pin
+ m_szPin.Attach(W2BSTR(szPin));
+ if (!VERIFY_IS_NOT_NULL(m_szPin)) { return E_OUTOFMEMORY; }
+
+ return hr;
+}
+
+STDMETHODIMP_(ULONG)
+CPinTestResource::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+}
+
+STDMETHODIMP_(ULONG)
+CPinTestResource::Release()
+{
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (0 == cRef)
+ {
+ delete this;
+ }
+
+ return cRef;
+}
+
+STDMETHODIMP
+CPinTestResource::QueryInterface(
+ __in REFIID riid,
+ __deref_out void ** ppvObject
+)
+{
+ HRESULT hr = S_OK;
+
+ //IF_FALSE_EXIT_HR( (NULL != ppvObject), E_POINTER);
+
+ if (IsEqualIID(riid, __uuidof(IUnknown)))
+ {
+ *ppvObject = static_cast<void *>(this);
+ }
+ else if (IsEqualIID(riid, __uuidof(ITestResource)))
+ {
+ *ppvObject = static_cast<ITestResource *>(this);
+ }
+ else if (IsEqualIID(riid, __uuidof(IHalfAppContainer)))
+ {
+ *ppvObject = static_cast<IHalfAppContainer *>(this);
+ }
+ else
+ {
+ *ppvObject = NULL;
+ hr = E_NOINTERFACE;
+ goto Exit;
+ }
+
+ AddRef();
+
+Exit:
+ return hr;
+}
+
+STDMETHODIMP
+CPinTestResource::GetGuid(GUID* pGuid) {
+ // don't want to see VERIFY spew
+ SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures);
+
+ if (!VERIFY_IS_NOT_NULL(pGuid)) { return E_POINTER; }
+
+ *pGuid = m_guid;
+ return S_OK;
+}
+
+STDMETHODIMP
+CPinTestResource::SetGuid(GUID guid) {
+ m_guid = guid;
+ return S_OK;
+}
+
+STDMETHODIMP
+CPinTestResource::GetValue(BSTR name, BSTR* pValue) {
+ // don't want to see VERIFY spew
+ SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures);
+
+ if (!VERIFY_IS_NOT_NULL(name)) { return E_POINTER; }
+ if (!VERIFY_IS_NOT_NULL(pValue)) { return E_POINTER; }
+
+ if (0 == wcscmp(name, TestResourceProperty::c_szName)) {
+ if (!VERIFY_IS_NOT_NULL(*pValue = SysAllocString(m_szName))) { return E_OUTOFMEMORY; }
+ return S_OK;
+ }
+
+ if (0 == wcscmp(name, TestResourceProperty::c_szId)) {
+ if (!VERIFY_IS_NOT_NULL(*pValue = SysAllocString(m_szId))) { return E_OUTOFMEMORY; }
+ return S_OK;
+ }
+
+ if (0 == wcscmp(name, TestResourceProperty::c_szType)) {
+ if (!VERIFY_IS_NOT_NULL(*pValue = SysAllocString(m_szType))) { return E_OUTOFMEMORY; }
+ return S_OK;
+ }
+
+ if (0 == wcscmp(name, TestResourceProperty::c_szMode)) {
+ if (!VERIFY_IS_NOT_NULL(*pValue = SysAllocString(m_szMode))) { return E_OUTOFMEMORY; }
+ return S_OK;
+ }
+
+ if (0 == wcscmp(name, TestResourceProperty::c_szPin)) {
+ if (!VERIFY_IS_NOT_NULL(*pValue = SysAllocString(m_szPin))) { return E_OUTOFMEMORY; }
+ return S_OK;
+ }
+
+ Log::Error(L"CDevice::GetValue name not found");
+ return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+}
+
+STDMETHODIMP
+CPinTestResource::SetValue(BSTR name, BSTR value) {
+ // don't want to see VERIFY spew
+ SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures);
+
+ if (!VERIFY_IS_NOT_NULL(name)) { return E_POINTER; }
+ if (!VERIFY_IS_NOT_NULL(value)) { return E_POINTER; }
+
+ if (0 == wcscmp(name, TestResourceProperty::c_szName)) {
+ return m_szName.AssignBSTR(value);
+ }
+
+ if (0 == wcscmp(name, TestResourceProperty::c_szId)) {
+ return m_szId.AssignBSTR(value);
+ }
+
+ if (0 == wcscmp(name, TestResourceProperty::c_szType)) {
+ return m_szType.AssignBSTR(value);
+ }
+
+ if (0 == wcscmp(name, TestResourceProperty::c_szMode)) {
+ return m_szMode.AssignBSTR(value);
+ }
+
+ if (0 == wcscmp(name, TestResourceProperty::c_szPin)) {
+ return m_szPin.AssignBSTR(value);
+ }
+
+ Log::Error(L"CDevice::SetValue name not found");
+
+ return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+}
+
+STDMETHODIMP
+CPinTestResource::GetHalfApp(CHalfApp ** ppHalfApp)
+{
+ SetVerifyOutput verifySettings(VerifyOutputSettings::LogOnlyFailures);
+
+ if (!VERIFY_IS_NOT_NULL(ppHalfApp)) { return E_POINTER; }
+
+ *ppHalfApp = m_spHalfApp;
+
+ return S_OK;
+}
+
+static const struct
+{
+ GUID mode;
+ LPWSTR name;
+}knownModes[] =
+{
+ { GUID_NULL, L"NO_MODE" },
+ { AUDIO_SIGNALPROCESSINGMODE_RAW, L"RAW" },
+ { AUDIO_SIGNALPROCESSINGMODE_DEFAULT, L"DEFAULT" },
+ { AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, L"COMMUNICATIONS" },
+ { AUDIO_SIGNALPROCESSINGMODE_SPEECH, L"SPEECH" },
+ { AUDIO_SIGNALPROCESSINGMODE_MEDIA, L"MEDIA" },
+ { AUDIO_SIGNALPROCESSINGMODE_MOVIE, L"MOVIE" },
+ { AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION, L"NOTIFICATION" },
+};
+
+
+LPWSTR CPinTestResource::ModeName(REFGUID guidMode)
+{
+ for (auto m : knownModes)
+ {
+ if (m.mode == guidMode) { return m.name; }
+ }
+
+ return L"UNKNOWN";
+}
+
+LPWSTR CPinTestResource::PinName(EndpointConnectorType eConnectorType)
+{
+ switch (eConnectorType)
+ {
+ case eHostProcessConnector:
+ return L"HOST";
+ case eOffloadConnector:
+ return L"OFFLOAD";
+ case eLoopbackConnector:
+ return L"LOOPBACK";
+ case eKeywordDetectorConnector:
+ return L"KEYWORD";
+ default:
+ return L"UNKNOWN";
+ }
+} \ No newline at end of file
diff --git a/audio/tests/wavetest/TestResource.h b/audio/tests/wavetest/TestResource.h
new file mode 100644
index 00000000..49dee5a5
--- /dev/null
+++ b/audio/tests/wavetest/TestResource.h
@@ -0,0 +1,66 @@
+// ------------------------------------------------------------------------------
+//
+// 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<CHalfApp> 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
new file mode 100644
index 00000000..51cce52d
--- /dev/null
+++ b/audio/tests/wavetest/TestResourceBuild.cpp
@@ -0,0 +1,493 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+// File Name:
+//
+// TestResourceBuild.cpp
+//
+// Abstract:
+//
+// TAEF BuildResourceList implementation
+//
+// ------------------------------------------------------------------------------
+#include "PreComp.h"
+#include "TestResource.h"
+#include "PropertyHelper.h"
+
+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,
+ 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
+)
+{
+ HRESULT hr = S_OK;
+ CComHeapPtr<CHalfApp> spHalfApp;
+ wil::com_ptr_nothrow<ITestResource> 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));
+ 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<AUDIO_SIGNALPROCESSINGMODE> 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<WAVEFORMATEX> pDefaultFormat;
+ wil::unique_cotaskmem_ptr<WAVEFORMATEX> 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<AUDIO_SIGNALPROCESSINGMODE> spModes;
+ AUDIO_SIGNALPROCESSINGMODE mode;
+ ULONG cFormatRecords = 0;
+ CComHeapPtr<FORMAT_RECORD> spFormatRecords;
+ wil::unique_cotaskmem_ptr<WAVEFORMATEX> pPreferredFormat;
+ UINT32 u32DefaultPeriodicityInFrames;
+ UINT32 u32FundamentalPeriodicityInFrames;
+ UINT32 u32MinPeriodicityInFrames;
+ UINT32 u32MaxPeriodicityInFrames;
+ UINT32 u32MaxPeriodicityInFramesExtended;
+
+ // 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;
+ }
+
+ if (!VERIFY_SUCCEEDED(hr = CreateTestResource(resourceList, deviceId, deviceName, pDevice, dataFlow, eConnectorType, uConnectorId, mode, cModes, spModes, cFormatRecords, spFormatRecords, pPreferredFormat.get(), u32DefaultPeriodicityInFrames, u32FundamentalPeriodicityInFrames, u32MinPeriodicityInFrames, u32MaxPeriodicityInFrames))) {
+ return hr;
+ }
+ }
+
+ 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<IMMDeviceEnumerator> spEnumerator;
+ wil::com_ptr_nothrow<IMMDevice> 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<IMMDeviceEnumerator> spEnumerator;
+ wil::com_ptr_nothrow<IMMDeviceCollection> spRenderEndpoints;
+ wil::com_ptr_nothrow<IMMDeviceCollection> spCaptureEndpoints;
+ wil::com_ptr_nothrow<IMMDevice> 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/wavetest/TestResourceHelper.h b/audio/tests/wavetest/TestResourceHelper.h
new file mode 100644
index 00000000..24bae634
--- /dev/null
+++ b/audio/tests/wavetest/TestResourceHelper.h
@@ -0,0 +1,71 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+// Module Name:
+//
+// TestResourceHelpers.h
+//
+// Abstract:
+//
+// Header for BuildResourceList helpers.
+//
+// -------------------------------------------------------------------------------
+#pragma once
+
+// ----------------------------------------------------------------------
+HRESULT GetProcessingModesForConnector
+(
+ IMMDevice* pDevice,
+ UINT uConnectorId,
+ EndpointConnectorType eConnectorType,
+ ULONG* pCount,
+ AUDIO_SIGNALPROCESSINGMODE** ppModes
+);
+
+// ----------------------------------------------------------------------
+HRESULT GetDefaultFormatForConnector
+(
+ IMMDevice* pDevice,
+ EndpointConnectorType eConnectorType,
+ WAVEFORMATEX** ppDefaultFormat
+);
+
+// ----------------------------------------------------------------------
+HRESULT GetPreferredFormatForConnector
+(
+ IMMDevice* pDevice,
+ UINT uConnectorId,
+ EndpointConnectorType eConnectorType,
+ AUDIO_SIGNALPROCESSINGMODE mode,
+ WAVEFORMATEX** ppPreferredFormat
+);
+
+// ----------------------------------------------------------------------
+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 GetSupportedFormatRecordsForConnector
+(
+ IMMDevice* pDevice,
+ UINT uConnectorId,
+ EndpointConnectorType eConnectorType,
+ AUDIO_SIGNALPROCESSINGMODE mode,
+ STACKWISE_DATAFLOW dataFlow,
+ ULONG* pCount,
+ FORMAT_RECORD** ppFormatRecords
+); \ No newline at end of file
diff --git a/audio/tests/wavetest/WaveTestTaef.cpp b/audio/tests/wavetest/WaveTestTaef.cpp
new file mode 100644
index 00000000..dacf44ab
--- /dev/null
+++ b/audio/tests/wavetest/WaveTestTaef.cpp
@@ -0,0 +1,170 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+// File Name:
+//
+// WaveTestTaef.cpp
+//
+// Abstract:
+//
+// Implementation file for WaveTestTaef
+//
+// -------------------------------------------------------------------------------
+#include "PreComp.h"
+#include "WaveTestTaef.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::WaveTest * g_pWaveTest = NULL;
+
+// -------------------------------------------------------------------------------
+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::WaveTest::WaveTestSetup()
+{
+ g_pWaveTest = this;
+
+ return true;
+}
+
+bool WDMAudio::WaveTest::WaveTestCleanUp()
+{
+ g_pWaveTest = NULL;
+
+ return true;
+}
+
+// -------------------------------------------------------------------------------
+bool WDMAudio::WaveTest::TestCaseSetup()
+{
+ CComPtr<ITestResource> spResource;
+ CComQIPtr<IHalfAppContainer> 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; }
+
+ return true;
+}
+
+bool WDMAudio::WaveTest::TestCaseCleanUp()
+{
+ 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_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::WaveTest::TAEF_StreamStateControl()
+{
+ RUN_TEST_CASE(Test_StreamStateControl)
+}
+
+void WDMAudio::WaveTest::TAEF_LoopbackStreamStateControl()
+{
+ RUN_TEST_CASE(Test_LoopbackStreamStateControl)
+}
+
+void WDMAudio::WaveTest::TAEF_StreamMinBufferSize()
+{
+ RUN_TEST_CASE(Test_StreamMinBufferSize)
+}
+
+void WDMAudio::WaveTest::TAEF_StreamMaxBufferSize()
+{
+ RUN_TEST_CASE(Test_StreamMaxBufferSize)
+}
+
+void WDMAudio::WaveTest::TAEF_StreamDifferentFormat()
+{
+ RUN_TEST_CASE(Test_StreamDifferentFormat)
+
+}
+
+void WDMAudio::WaveTest::TAEF_LoopbackStreamDifferentFormat()
+{
+ RUN_TEST_CASE(Test_LoopbackStreamDifferentFormat)
+
+}
+
+void WDMAudio::WaveTest::TAEF_StreamMultipleModes()
+{
+ RUN_TEST_CASE(Test_StreamMultipleModes)
+}
+
+void WDMAudio::WaveTest::TAEF_VerifyPinSupportsPullMode()
+{
+ RUN_TEST_CASE(Test_VerifyPinSupportsPullMode)
+} \ No newline at end of file
diff --git a/audio/tests/wavetest/WaveTestTaef.h b/audio/tests/wavetest/WaveTestTaef.h
new file mode 100644
index 00000000..a8d38305
--- /dev/null
+++ b/audio/tests/wavetest/WaveTestTaef.h
@@ -0,0 +1,124 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+// File Name:
+//
+// WaveTestTaef.h
+//
+// Abstract:
+//
+// Include file for WaveTestTaef
+//
+// -------------------------------------------------------------------------------
+#pragma once
+
+#include "HalfApp.h"
+
+extern IBasicLog* g_pBasicLog;
+
+// 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";
+ }
+ }
+}
+
+namespace WDMAudio
+{
+
+ class WaveTest
+ {
+ BEGIN_TEST_CLASS(WaveTest)
+ TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client x86")
+ TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client x64")
+ TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client ARM")
+ TEST_CLASS_PROPERTY(L"Kits.SupportedOS", L"Windows v10.0 Client ARM64")
+ 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.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")
+ TEST_CLASS_PROPERTY(L"Kits.PublishingOrganization", L"Microsoft Corporation")
+ TEST_CLASS_PROPERTY(L"Kits.TestType", L"Development")
+ TEST_CLASS_PROPERTY(L"Kits.DevelopmentPhase", L"Development and Integration")
+ TEST_CLASS_PROPERTY(L"Subfeature", L"Wave Driver Tests")
+ TEST_CLASS_PROPERTY(L"ThreadingModel", L"MTA")
+ TEST_CLASS_PROPERTY(L"ArtifactUnderTest", L"onecore\\external\\ddk\\inc\\drmk.h")
+ TEST_CLASS_PROPERTY(L"ArtifactUnderTest", L"onecoreuap\\external\\ddk\\lib\\$(build.arch)\\drmk.lib")
+ TEST_CLASS_PROPERTY(L"ArtifactUnderTest", L"onecoreuap\\internal\\drivers\\inc\\private\\wdm\\audio\\inf\\wdmaudio.inf")
+ END_TEST_CLASS()
+
+ TEST_CLASS_SETUP(WaveTestSetup)
+ TEST_CLASS_CLEANUP(WaveTestCleanUp)
+
+ TEST_METHOD_SETUP(TestCaseSetup)
+ TEST_METHOD_CLEANUP(TestCaseCleanUp)
+
+ BEGIN_TEST_METHOD(TAEF_StreamStateControl)
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional")
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification")
+ TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature")
+ TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*HOST*' OR @Id = '*OFFLOAD*'")
+ END_TEST_METHOD()
+
+ BEGIN_TEST_METHOD(TAEF_LoopbackStreamStateControl)
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional")
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification")
+ TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature")
+ TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*LOOPBACK*'")
+ END_TEST_METHOD()
+
+ BEGIN_TEST_METHOD(TAEF_StreamMinBufferSize)
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional")
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification")
+ TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature")
+ TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*HOST*' OR @Id = '*OFFLOAD*'")
+ END_TEST_METHOD()
+
+ BEGIN_TEST_METHOD(TAEF_StreamMaxBufferSize)
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional")
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification")
+ TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature")
+ TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*HOST*' OR @Id = '*OFFLOAD*'")
+ END_TEST_METHOD()
+
+ BEGIN_TEST_METHOD(TAEF_StreamDifferentFormat)
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional")
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification")
+ TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature")
+ TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*HOST*' OR @Id = '*OFFLOAD*'")
+ END_TEST_METHOD()
+
+ BEGIN_TEST_METHOD(TAEF_LoopbackStreamDifferentFormat)
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional")
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification")
+ TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature")
+ TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*LOOPBACK*'")
+ END_TEST_METHOD()
+
+ BEGIN_TEST_METHOD(TAEF_StreamMultipleModes)
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional")
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification")
+ TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature")
+ TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Id = '*RAW' OR @Id = '*DEFAULT'")
+ END_TEST_METHOD()
+
+ BEGIN_TEST_METHOD(TAEF_VerifyPinSupportsPullMode)
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Functional")
+ TEST_METHOD_PROPERTY(L"HCKCategory", L"Certification")
+ TEST_METHOD_PROPERTY(L"TestClassification:Scope", L"Feature")
+ TEST_METHOD_PROPERTY(L"ResourceSelection", L"@Type = 'Render' OR @Type = 'Capture'")
+ END_TEST_METHOD()
+
+ public:
+ CHalfApp* m_pHalf;
+ };
+}
+
+extern WDMAudio::WaveTest* g_pWaveTest;
diff --git a/audio/tests/wavetest/comptest.cpp b/audio/tests/wavetest/comptest.cpp
new file mode 100644
index 00000000..ef41972c
--- /dev/null
+++ b/audio/tests/wavetest/comptest.cpp
@@ -0,0 +1,37 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+// Module Name:
+//
+// comptest.cpp
+//
+// Abstract:
+//
+// Implementation file for test cases
+//
+// -------------------------------------------------------------------------------
+
+#include "PreComp.h"
+#include "WaveTestTaef.h"
+#include "tests.h"
+
+// ------------------------------------------------------------------------------
+// Test_VerifyPinSupportsPullMode: Test the requirement that WaveRT drivers must support pull mode audio streaming technology.
+void Test_VerifyPinSupportsPullMode(void)
+{
+ CHalfApp* pHalfApp = g_pWaveTest->m_pHalf;
+ BOOL bIsRTCapable = false;
+ BOOL bIsEventCapable = false;
+
+ VERIFY_SUCCEEDED(pHalfApp->InitializeEndpoint());
+
+ VERIFY_SUCCEEDED(pHalfApp->m_pAudioDeviceEndpoint->GetRTCaps(&bIsRTCapable));
+
+ // Only require pull mode 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/pintest.cpp b/audio/tests/wavetest/pintest.cpp
new file mode 100644
index 00000000..a8b356c9
--- /dev/null
+++ b/audio/tests/wavetest/pintest.cpp
@@ -0,0 +1,492 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+// Module Name:
+//
+// pintest.cpp
+//
+// Abstract:
+//
+// Implementation file for test cases
+//
+// -------------------------------------------------------------------------------
+
+#include "PreComp.h"
+#include "WaveTestTaef.h"
+#include "tests.h"
+
+#define DEFAULT_PERIODICITY 0
+#define DEFAULT_LATENCY_COEFFICIENT 0
+
+static const struct
+{
+ GUID mode;
+ LPWSTR name;
+}knownModes[] =
+{
+ { GUID_NULL, L"NO_MODE" },
+{ AUDIO_SIGNALPROCESSINGMODE_RAW, L"RAW" },
+{ AUDIO_SIGNALPROCESSINGMODE_DEFAULT, L"DEFAULT" },
+{ AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, L"COMMUNICATIONS" },
+{ AUDIO_SIGNALPROCESSINGMODE_SPEECH, L"SPEECH" },
+{ AUDIO_SIGNALPROCESSINGMODE_MEDIA, L"MEDIA" },
+{ AUDIO_SIGNALPROCESSINGMODE_MOVIE, L"MOVIE" },
+{ AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION, L"NOTIFICATION" },
+};
+
+
+LPWSTR ModeName(REFGUID guidMode)
+{
+ for (auto m : knownModes)
+ {
+ if (m.mode == guidMode) { return m.name; }
+ }
+
+ return L"UNKNOWN";
+}
+
+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);
+ }
+}
+
+// ------------------------------------------------------------------------------
+// Test_StreamStateControl: Test the stream state contorl of an endpoint. In RUN state pin position should be moving,
+// in PAUSE state pin position should be same all time, in STOP state pin position should be zero.
+void Test_StreamStateControl(void)
+{
+ CHalfApp* pHalfApp = g_pWaveTest->m_pHalf;
+ UINT32 u32Index = 0;
+ UINT64 u64PreviousPosition;
+ UINT64 u64Position;
+ UINT64 u64CorrelatedSystemTime;
+
+ // Log wave format
+ LOG(g_pBasicLog, L"Test case will be run on the following format:");
+ LogWaveFormat(pHalfApp->m_pCurrentFormat.get());
+
+ VERIFY_SUCCEEDED(pHalfApp->InitializeEndpoint());
+ VERIFY_SUCCEEDED(pHalfApp->CreateSineToneDataBuffer(pHalfApp->m_pCurrentFormat.get()));
+ VERIFY_SUCCEEDED(pHalfApp->InitializeStream(DEFAULT_PERIODICITY, DEFAULT_LATENCY_COEFFICIENT));
+
+ // Stream start state
+ VERIFY_SUCCEEDED(pHalfApp->StartStream());
+ Sleep(100);
+ VERIFY_SUCCEEDED(pHalfApp->GetPosition(&u64PreviousPosition, &u64CorrelatedSystemTime));
+ for (u32Index = 0; u32Index < 10; u32Index++) {
+ Sleep(50);
+ VERIFY_SUCCEEDED(pHalfApp->GetPosition(&u64Position, &u64CorrelatedSystemTime));
+ VERIFY_IS_TRUE(u64Position != u64PreviousPosition);
+ u64PreviousPosition = u64Position;
+ }
+
+ // Stream stop state
+ // IAudioEndpointControl::Stop set pin to PAUSE state
+ VERIFY_SUCCEEDED(pHalfApp->StopEndpoint());
+ Sleep(100);
+ VERIFY_SUCCEEDED(pHalfApp->GetPosition(&u64PreviousPosition, &u64CorrelatedSystemTime));
+ for (u32Index = 0; u32Index < 10; u32Index++) {
+ Sleep(50);
+ VERIFY_SUCCEEDED(pHalfApp->GetPosition(&u64Position, &u64CorrelatedSystemTime));
+ VERIFY_IS_TRUE(u64Position == u64PreviousPosition);
+ u64PreviousPosition = u64Position;
+ }
+
+ // Stream reset state
+ // IAudioEndpointControl::Reset set pin to STOP state
+ VERIFY_SUCCEEDED(pHalfApp->ResetEndpoint());
+ Sleep(100);
+ for (u32Index = 0; u32Index < 10; u32Index++) {
+ Sleep(50);
+ VERIFY_SUCCEEDED(pHalfApp->GetPosition(&u64Position, &u64CorrelatedSystemTime));
+ VERIFY_IS_TRUE(u64Position == 0);
+ }
+
+ VERIFY_SUCCEEDED(pHalfApp->StartEndpoint());
+ Sleep(100);
+
+ VERIFY_SUCCEEDED(pHalfApp->StopStream());
+}
+
+// ------------------------------------------------------------------------------
+// Test_StreamMinBufferSize: Test the stream with minimum processing time (buffer size).
+void Test_StreamMinBufferSize(void)
+{
+ CHalfApp* pHalfApp = g_pWaveTest->m_pHalf;
+
+ // Set buffer with mininum periodicity
+ Test_StreamBufferSize(FRAMES_TO_HNSTIME_DOUBLE(pHalfApp->m_u32CurrentMinPeriodicityInFrames, pHalfApp->m_pCurrentFormat->nSamplesPerSec));
+}
+
+// ------------------------------------------------------------------------------
+// Test_StreamMaxBufferSize: Test the stream with maximum processing time (buffer size).
+void Test_StreamMaxBufferSize(void)
+{
+ CHalfApp* pHalfApp = g_pWaveTest->m_pHalf;
+
+ // Set buffer with maximum periodicity
+ Test_StreamBufferSize(FRAMES_TO_HNSTIME_DOUBLE(pHalfApp->m_u32CurrentMaxPeriodicityInFrames, pHalfApp->m_pCurrentFormat->nSamplesPerSec));
+}
+
+// ------------------------------------------------------------------------------
+// Test_StreamBufferSize: Test the stream with requested processing time (buffer size).
+void Test_StreamBufferSize(HNSTIME requestedPeriodicity)
+{
+ CHalfApp* pHalfApp = g_pWaveTest->m_pHalf;
+ UINT32 u32Index = 0;
+ UINT64 u64PreviousPosition;
+ UINT64 u64Position;
+ UINT64 u64CorrelatedSystemTime;
+
+ // Log wave format
+ LOG(g_pBasicLog, L"Test case will be run on the following format:");
+ LogWaveFormat(pHalfApp->m_pCurrentFormat.get());
+
+ VERIFY_SUCCEEDED(pHalfApp->InitializeEndpoint());
+ VERIFY_SUCCEEDED(pHalfApp->CreateSineToneDataBuffer(pHalfApp->m_pCurrentFormat.get()));
+ VERIFY_SUCCEEDED(pHalfApp->InitializeStream(requestedPeriodicity, DEFAULT_LATENCY_COEFFICIENT));
+ VERIFY_SUCCEEDED(pHalfApp->StartStream());
+
+ Sleep(100);
+
+ // Check pin position
+ VERIFY_SUCCEEDED(pHalfApp->GetPosition(&u64PreviousPosition, &u64CorrelatedSystemTime));
+ for (u32Index = 0; u32Index < 10; u32Index++) {
+ Sleep(50);
+ VERIFY_SUCCEEDED(pHalfApp->GetPosition(&u64Position, &u64CorrelatedSystemTime));
+ VERIFY_IS_TRUE(u64Position != u64PreviousPosition);
+ u64PreviousPosition = u64Position;
+ }
+
+ VERIFY_SUCCEEDED(pHalfApp->StopStream());
+}
+
+// ------------------------------------------------------------------------------
+// Test_StreamDifferentFormat: Test the stream with all supported formats.
+void Test_StreamDifferentFormat(void)
+{
+ CHalfApp* pHalfApp = g_pWaveTest->m_pHalf;
+ UINT32 u32FormatIndex = 0;
+ UINT32 u32Index = 0;
+ UINT64 u64PreviousPosition;
+ UINT64 u64Position;
+ UINT64 u64CorrelatedSystemTime;
+
+ // Loop through supported formats and stream with different format
+ for (;u32FormatIndex < pHalfApp->m_cFormatRecords; u32FormatIndex++) {
+
+ // Change the current format
+ CloneWaveFormat((WAVEFORMATEX *)&pHalfApp->m_pFormatRecords[u32FormatIndex].wfxEx, wil::out_param(pHalfApp->m_pCurrentFormat));
+ pHalfApp->m_u32CurrentDefaultPeriodicityInFrames = pHalfApp->m_pFormatRecords[u32FormatIndex].defaultPeriodInFrames;
+ pHalfApp->m_u32CurrentFundamentalPeriodicityInFrames = pHalfApp->m_pFormatRecords[u32FormatIndex].fundamentalPeriodInFrames;
+ pHalfApp->m_u32CurrentMinPeriodicityInFrames = pHalfApp->m_pFormatRecords[u32FormatIndex].minPeriodInFrames;
+ pHalfApp->m_u32CurrentMaxPeriodicityInFrames = pHalfApp->m_pFormatRecords[u32FormatIndex].maxPeriodInFrames;
+
+ // Log the current format
+ LOG(g_pBasicLog, L"Current format:");
+ LogWaveFormat(pHalfApp->m_pCurrentFormat.get());
+
+ // Initialize endpoints and start stream with current format
+ 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);
+
+ // Check pin position
+ VERIFY_SUCCEEDED(pHalfApp->GetPosition(&u64PreviousPosition, &u64CorrelatedSystemTime));
+ for (u32Index = 0; u32Index < 10; u32Index++) {
+ Sleep(50);
+ VERIFY_SUCCEEDED(pHalfApp->GetPosition(&u64Position, &u64CorrelatedSystemTime));
+ VERIFY_IS_TRUE(u64Position != u64PreviousPosition);
+ u64PreviousPosition = u64Position;
+ }
+
+ // Make sure to stop stearm and clean up stream, endpoints and data buffer before change to next format
+ VERIFY_SUCCEEDED(pHalfApp->StopStream());
+ VERIFY_SUCCEEDED(pHalfApp->CleanupStream());
+ VERIFY_SUCCEEDED(pHalfApp->ReleaseEndpoint());
+ VERIFY_SUCCEEDED(pHalfApp->ReleaseSineToneDataBuffer());
+ }
+}
+
+// ------------------------------------------------------------------------------
+// Test_StreamMultipleModes: Create 2 pin instances in different mode (if supported) and test streaming.
+void Test_StreamMultipleModes(void)
+{
+ CHalfApp* pMainHalfApp = g_pWaveTest->m_pHalf;
+ UINT32 u32PinInstanceCount = 0;
+ AUDIO_SIGNALPROCESSINGMODE secondMode;
+ CHalfApp* pSecondHalfApp;
+ UINT32 u32Index = 0;
+ UINT64 u64PreviousPosition1;
+ UINT64 u64PreviousPosition2;
+ UINT64 u64Position1;
+ UINT64 u64Position2;
+ UINT64 u64CorrelatedSystemTime1;
+ UINT64 u64CorrelatedSystemTime2;
+
+ // Check processing mode count
+ if (pMainHalfApp->m_cModes < 2) {
+ SKIP(g_pBasicLog, L"SKIP: Current pin does not support multiple modes. Skipping multiple modes test. ");
+ return;
+ }
+ // Check pin instance count, if less that 2, then multiple pin streaming is not supported
+ VERIFY_SUCCEEDED(pMainHalfApp->GetCurrentAvailiablePinInstanceCount(&u32PinInstanceCount));
+ if (u32PinInstanceCount < 2) {
+ SKIP(g_pBasicLog, L"SKIP: Current availiable pin instance count is less than 2. Skipping multiple modes test. ");
+ return;
+ }
+
+ // Loop through each mode, create second half app, and start stream
+ for (ULONG i = 0; i < pMainHalfApp->m_cModes; i++) {
+ secondMode = pMainHalfApp->m_pModes[i];
+ if (IsEqualGUID(pMainHalfApp->m_Mode, secondMode)) {
+ continue;
+ }
+
+ VERIFY_SUCCEEDED(pMainHalfApp->GetSecondHalfApp(secondMode, &pSecondHalfApp));
+
+ // Log both modes and formats
+ LOG(g_pBasicLog, L"First stream is in %s mode", ModeName(pMainHalfApp->m_Mode));
+ LOG(g_pBasicLog, L"First stream will be run on the following format:");
+ LogWaveFormat(pMainHalfApp->m_pCurrentFormat.get());
+ LOG(g_pBasicLog, L"Second stream is in %s mode", ModeName(pSecondHalfApp->m_Mode));
+ LOG(g_pBasicLog, L"Second stream will be run on the following format:");
+ LogWaveFormat(pSecondHalfApp->m_pCurrentFormat.get());
+
+ // Initialize and start first stream
+ VERIFY_SUCCEEDED(pMainHalfApp->InitializeEndpoint());
+ VERIFY_SUCCEEDED(pMainHalfApp->CreateSineToneDataBuffer(pMainHalfApp->m_pCurrentFormat.get()));
+ VERIFY_SUCCEEDED(pMainHalfApp->InitializeStream(DEFAULT_PERIODICITY, DEFAULT_LATENCY_COEFFICIENT));
+ VERIFY_SUCCEEDED(pMainHalfApp->StartStream());
+
+ Sleep(100);
+
+ // Initialize and start second stream
+ VERIFY_SUCCEEDED(pSecondHalfApp->InitializeEndpoint());
+ VERIFY_SUCCEEDED(pSecondHalfApp->CreateSineToneDataBuffer(pSecondHalfApp->m_pCurrentFormat.get()));
+ VERIFY_SUCCEEDED(pSecondHalfApp->InitializeStream(DEFAULT_PERIODICITY, DEFAULT_LATENCY_COEFFICIENT));
+ VERIFY_SUCCEEDED(pSecondHalfApp->StartStream());
+
+ Sleep(100);
+
+ // Check position for two streams
+ VERIFY_SUCCEEDED(pMainHalfApp->GetPosition(&u64PreviousPosition1, &u64CorrelatedSystemTime1));
+ VERIFY_SUCCEEDED(pSecondHalfApp->GetPosition(&u64PreviousPosition2, &u64CorrelatedSystemTime2));
+ for (u32Index = 0; u32Index < 10; u32Index++) {
+ Sleep(50);
+ VERIFY_SUCCEEDED(pMainHalfApp->GetPosition(&u64Position1, &u64CorrelatedSystemTime1));
+ VERIFY_SUCCEEDED(pSecondHalfApp->GetPosition(&u64Position2, &u64CorrelatedSystemTime2));
+ VERIFY_IS_TRUE(u64Position1 != u64PreviousPosition1);
+ VERIFY_IS_TRUE(u64Position2 != u64PreviousPosition2);
+ u64PreviousPosition1 = u64Position1;
+ u64PreviousPosition2 = u64Position2;
+ }
+
+ // Stop and cleanup first stream
+ VERIFY_SUCCEEDED(pMainHalfApp->StopStream());
+ VERIFY_SUCCEEDED(pMainHalfApp->CleanupStream());
+ VERIFY_SUCCEEDED(pMainHalfApp->ReleaseEndpoint());
+ VERIFY_SUCCEEDED(pMainHalfApp->ReleaseSineToneDataBuffer());
+
+ // Stop and cleanup second stream
+ VERIFY_SUCCEEDED(pSecondHalfApp->StopStream());
+ VERIFY_SUCCEEDED(pSecondHalfApp->CleanupStream());
+ VERIFY_SUCCEEDED(pSecondHalfApp->ReleaseEndpoint());
+ VERIFY_SUCCEEDED(pSecondHalfApp->ReleaseSineToneDataBuffer());
+ delete pSecondHalfApp;
+ }
+}
+
+// ------------------------------------------------------------------------------
+// Test_LoopbackStreamStateControl: Test the stream state contorl of an loopback endpoint. In RUN state pin position should
+// be moving, in PAUSE state pin position should be same all time, in STOP state pin position should be zero.
+void Test_LoopbackStreamStateControl(void)
+{
+ CHalfApp* pLoopbackHalfApp = g_pWaveTest->m_pHalf;
+ CHalfApp* pHostHalfApp;
+ UINT32 u32Index = 0;
+ UINT64 u64PreviousPosition;
+ UINT64 u64Position;
+ UINT64 u64CorrelatedSystemTime;
+
+ // Get the corresponding host half app.
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->GetHostHalfApp(&pHostHalfApp));
+
+ // Match loopback stream format with host stream format
+ CloneWaveFormat(pHostHalfApp->m_pCurrentFormat.get(), wil::out_param(pLoopbackHalfApp->m_pCurrentFormat));
+ pLoopbackHalfApp->m_u32CurrentDefaultPeriodicityInFrames = pHostHalfApp->m_u32CurrentDefaultPeriodicityInFrames;
+ pLoopbackHalfApp->m_u32CurrentFundamentalPeriodicityInFrames = pHostHalfApp->m_u32CurrentFundamentalPeriodicityInFrames;
+ pLoopbackHalfApp->m_u32CurrentMinPeriodicityInFrames = pHostHalfApp->m_u32CurrentMinPeriodicityInFrames;
+ pLoopbackHalfApp->m_u32CurrentMaxPeriodicityInFrames = pHostHalfApp->m_u32CurrentMaxPeriodicityInFrames;
+
+ // Log wave format
+ LOG(g_pBasicLog, L"Test case will be run on the following format:");
+ LogWaveFormat(pLoopbackHalfApp->m_pCurrentFormat.get());
+
+ // Initialize and start host stream
+ VERIFY_SUCCEEDED(pHostHalfApp->InitializeEndpoint());
+ VERIFY_SUCCEEDED(pHostHalfApp->CreateSineToneDataBuffer(pHostHalfApp->m_pCurrentFormat.get()));
+ VERIFY_SUCCEEDED(pHostHalfApp->InitializeStream(DEFAULT_PERIODICITY, DEFAULT_LATENCY_COEFFICIENT));
+ VERIFY_SUCCEEDED(pHostHalfApp->StartStream());
+
+ // Initialize and start loopback stream
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->InitializeEndpoint());
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->CreateSineToneDataBuffer(pLoopbackHalfApp->m_pCurrentFormat.get()));
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->InitializeStream(DEFAULT_PERIODICITY, DEFAULT_LATENCY_COEFFICIENT));
+
+ // Stream start state
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->StartStream());
+ Sleep(100);
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->GetPosition(&u64PreviousPosition, &u64CorrelatedSystemTime));
+ for (u32Index = 0; u32Index < 10; u32Index++) {
+ Sleep(50);
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->GetPosition(&u64Position, &u64CorrelatedSystemTime));
+ VERIFY_IS_TRUE(u64Position != u64PreviousPosition);
+ u64PreviousPosition = u64Position;
+ }
+
+ // Stream stop state
+ // IAudioEndpointControl::Stop set pin to PAUSE state
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->StopEndpoint());
+ Sleep(100);
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->GetPosition(&u64PreviousPosition, &u64CorrelatedSystemTime));
+ for (u32Index = 0; u32Index < 10; u32Index++) {
+ Sleep(50);
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->GetPosition(&u64Position, &u64CorrelatedSystemTime));
+ VERIFY_IS_TRUE(u64Position == u64PreviousPosition);
+ u64PreviousPosition = u64Position;
+ }
+
+ // Stream reset state
+ // IAudioEndpointControl::Reset set pin to STOP state
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->ResetEndpoint());
+ Sleep(100);
+ for (u32Index = 0; u32Index < 10; u32Index++) {
+ Sleep(50);
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->GetPosition(&u64Position, &u64CorrelatedSystemTime));
+ VERIFY_IS_TRUE(u64Position == 0);
+ }
+
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->StartEndpoint());
+ Sleep(100);
+
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->StopStream());
+
+ // Stop and cleanup host stream
+ VERIFY_SUCCEEDED(pHostHalfApp->StopStream());
+ VERIFY_SUCCEEDED(pHostHalfApp->CleanupStream());
+ VERIFY_SUCCEEDED(pHostHalfApp->ReleaseEndpoint());
+ VERIFY_SUCCEEDED(pHostHalfApp->ReleaseSineToneDataBuffer());
+
+ delete pHostHalfApp;
+}
+
+// ------------------------------------------------------------------------------
+// Test_LoopbackStreamDifferentFormat: Test the loopback stream with all formats supported by host pin.
+void Test_LoopbackStreamDifferentFormat(void)
+{
+ CHalfApp* pLoopbackHalfApp = g_pWaveTest->m_pHalf;
+ CHalfApp* pHostHalfApp;
+ UINT32 u32FormatIndex = 0;
+ UINT32 u32Index = 0;
+ UINT64 u64PreviousPosition;
+ UINT64 u64Position;
+ UINT64 u64CorrelatedSystemTime;
+
+ // Get the corresponding host half app.
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->GetHostHalfApp(&pHostHalfApp));
+
+ // Loop through supported formats and stream with different format
+ for (;u32FormatIndex < pHostHalfApp->m_cFormatRecords; u32FormatIndex++) {
+
+ // Change the current format
+ CloneWaveFormat((WAVEFORMATEX *)&pHostHalfApp->m_pFormatRecords[u32FormatIndex].wfxEx, wil::out_param(pHostHalfApp->m_pCurrentFormat));
+ pHostHalfApp->m_u32CurrentDefaultPeriodicityInFrames = pHostHalfApp->m_pFormatRecords[u32FormatIndex].defaultPeriodInFrames;
+ pHostHalfApp->m_u32CurrentFundamentalPeriodicityInFrames = pHostHalfApp->m_pFormatRecords[u32FormatIndex].fundamentalPeriodInFrames;
+ pHostHalfApp->m_u32CurrentMinPeriodicityInFrames = pHostHalfApp->m_pFormatRecords[u32FormatIndex].minPeriodInFrames;
+ pHostHalfApp->m_u32CurrentMaxPeriodicityInFrames = pHostHalfApp->m_pFormatRecords[u32FormatIndex].maxPeriodInFrames;
+
+ // Match loopback stream format with host stream format
+ CloneWaveFormat(pHostHalfApp->m_pCurrentFormat.get(), wil::out_param(pLoopbackHalfApp->m_pCurrentFormat));
+ pLoopbackHalfApp->m_u32CurrentDefaultPeriodicityInFrames = pHostHalfApp->m_u32CurrentDefaultPeriodicityInFrames;
+ pLoopbackHalfApp->m_u32CurrentFundamentalPeriodicityInFrames = pHostHalfApp->m_u32CurrentFundamentalPeriodicityInFrames;
+ pLoopbackHalfApp->m_u32CurrentMinPeriodicityInFrames = pHostHalfApp->m_u32CurrentMinPeriodicityInFrames;
+ pLoopbackHalfApp->m_u32CurrentMaxPeriodicityInFrames = pHostHalfApp->m_u32CurrentMaxPeriodicityInFrames;
+
+ // Log the current format
+ LOG(g_pBasicLog, L"Current format:");
+ LogWaveFormat(pLoopbackHalfApp->m_pCurrentFormat.get());
+
+ // Initialize and start host stream
+ VERIFY_SUCCEEDED(pHostHalfApp->InitializeEndpoint());
+ VERIFY_SUCCEEDED(pHostHalfApp->CreateSineToneDataBuffer(pHostHalfApp->m_pCurrentFormat.get()));
+ VERIFY_SUCCEEDED(pHostHalfApp->InitializeStream(DEFAULT_PERIODICITY, DEFAULT_LATENCY_COEFFICIENT));
+ VERIFY_SUCCEEDED(pHostHalfApp->StartStream());
+
+ // Initialize and start loopback stream
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->InitializeEndpoint());
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->CreateSineToneDataBuffer(pLoopbackHalfApp->m_pCurrentFormat.get()));
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->InitializeStream(DEFAULT_PERIODICITY, DEFAULT_LATENCY_COEFFICIENT));
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->StartStream());
+
+ Sleep(100);
+
+ // Check pin position
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->GetPosition(&u64PreviousPosition, &u64CorrelatedSystemTime));
+ for (u32Index = 0; u32Index < 10; u32Index++) {
+ Sleep(50);
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->GetPosition(&u64Position, &u64CorrelatedSystemTime));
+ VERIFY_IS_TRUE(u64Position != u64PreviousPosition);
+ u64PreviousPosition = u64Position;
+ }
+
+ // Make sure to stop and clean up loopback stream, endpoints and data buffer before change to next format
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->StopStream());
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->CleanupStream());
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->ReleaseEndpoint());
+ VERIFY_SUCCEEDED(pLoopbackHalfApp->ReleaseSineToneDataBuffer());
+
+ // Stop and cleanup host stream
+ VERIFY_SUCCEEDED(pHostHalfApp->StopStream());
+ VERIFY_SUCCEEDED(pHostHalfApp->CleanupStream());
+ VERIFY_SUCCEEDED(pHostHalfApp->ReleaseEndpoint());
+ VERIFY_SUCCEEDED(pHostHalfApp->ReleaseSineToneDataBuffer());
+ }
+
+ delete pHostHalfApp;
+} \ No newline at end of file
diff --git a/audio/tests/wavetest/tests.h b/audio/tests/wavetest/tests.h
new file mode 100644
index 00000000..38a6aaa1
--- /dev/null
+++ b/audio/tests/wavetest/tests.h
@@ -0,0 +1,27 @@
+// ------------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft. All rights reserved.
+//
+// Module Name:
+//
+// tests.h
+//
+// Abstract:
+//
+// Header for testcases
+//
+// -------------------------------------------------------------------------------
+#pragma once
+
+// Stream test
+void Test_StreamStateControl(void);
+void Test_LoopbackStreamStateControl(void);
+void Test_StreamMinBufferSize(void);
+void Test_StreamMaxBufferSize(void);
+void Test_StreamBufferSize(HNSTIME requestedPeriodicity);
+void Test_StreamDifferentFormat(void);
+void Test_LoopbackStreamDifferentFormat(void);
+void Test_StreamMultipleModes(void);
+
+// Compliance test
+void Test_VerifyPinSupportsPullMode(void); \ No newline at end of file