Skip to content

Commit

Permalink
May 2022 samples
Browse files Browse the repository at this point in the history
* WASAPIRendering: Associating a Windows Audio Session API (WASAPI) stream with a window
* FakeMenu: Demonstrate new menu theme parts and states
* AcousticEchoCancellation: Setting a reference stream for acoustic echo cancellation
* EventTracingEnumerateProviders: Enumerating providers with optional filters
* CloudMirror: Providing status UI, also addresses #194, #221, #222
  • Loading branch information
Windows classic samples committed May 12, 2022
1 parent 67a8cdd commit 7cbd99a
Show file tree
Hide file tree
Showing 69 changed files with 3,666 additions and 672 deletions.
52 changes: 52 additions & 0 deletions Samples/AcousticEchoCancellation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
page_type: sample
languages:
- cpp
products:
- windows-api-win32
name: Acoustic echo cancellation
urlFragment: AcousticEchoCancellation
description: Set the reference endpoint id for acoustic echo cancellation.
extendedZipContent:
- path: LICENSE
target: LICENSE
---

# Acoustic Echo Cancellation Sample

This sample demonstrates the use of the IAcousticEchoCancellationControl::SetEchoCancellationRenderEndpoint method.

The Acoustic Echo Cancellation APIs provide applications using communications streams the ability to customize
the reference stream of choice to use for echo cancellation. By default, the system uses the default render
device as the reference stream.

The sample demonstrates how an application can check to see if the audio stream provides a way to configure
echo cancellation. If it does, the interface is used to configure the reference endpoint to be used for echo
cancellation on a communication stream by using the reference stream from a specific render endpoint.

To use this sample, select a render endpoint from the list (by number),
and the sample use that render endpoint as the reference stream for echo cancellation.

This sample requires Windows 11 build 22540 or later.

Sample Language Implementations
===============================
C++

To build the sample using the command prompt:
=============================================

1. Open the Command Prompt window and navigate to the directory.
2. Type msbuild [Solution Filename]

To build the sample using Visual Studio (preferred method):
================================================================

1. Open Windows Explorer and navigate to the directory.
2. Double-click the icon for the .sln (solution) file to open the file in Visual Studio.
3. In the Build menu, select Build Solution. The application will be built in the default
\Debug or \Release directory

To run the sample:
=================
Type AcousticEchoCancellation.exe at the command line.
173 changes: 173 additions & 0 deletions Samples/AcousticEchoCancellation/cpp/AECCapture.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#include <windows.h>
#include <wchar.h>
#include <AudioSessionTypes.h>

#include "AECCapture.h"

#if !defined(NTDDI_WIN10_NI) || (NTDDI_VERSION < NTDDI_WIN10_NI)
#error This sample requires SDK version 22540 or higher.
#endif

#define REFTIMES_PER_SEC 10000000

HRESULT CAECCapture::IsAcousticEchoCancellationEffectPresent(bool* result)
{
*result = false;

// IAudioEffectsManager requires build 22000 or higher.
wil::com_ptr_nothrow<IAudioEffectsManager> audioEffectsManager;
HRESULT hr = _audioClient->GetService(IID_PPV_ARGS(&audioEffectsManager));
if (hr == E_NOINTERFACE)
{
// Audio effects manager is not supported, so clearly not present.
return S_OK;
}

wil::unique_cotaskmem_array_ptr<AUDIO_EFFECT> effects;
UINT32 numEffects;
RETURN_IF_FAILED(audioEffectsManager->GetAudioEffects(&effects, &numEffects));

for (UINT32 i = 0; i < numEffects; i++)
{
// Check for acoustic echo cancellation Audio Processing Object (APO)
if (effects[i].id == AUDIO_EFFECT_TYPE_ACOUSTIC_ECHO_CANCELLATION)
{
*result = true;
break;
}
}
return S_OK;
}

HRESULT CAECCapture::InitializeCaptureClient()
{
wil::com_ptr_nothrow<IMMDeviceEnumerator> enumerator;
RETURN_IF_FAILED(::CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&enumerator)));

wil::com_ptr_nothrow<IMMDevice> device;
RETURN_IF_FAILED(enumerator->GetDefaultAudioEndpoint(eCapture, eCommunications, &device));

RETURN_IF_FAILED(device->Activate(__uuidof(IAudioClient2), CLSCTX_INPROC_SERVER, NULL, (void**)&_audioClient));

// Set the category as communications.
AudioClientProperties clientProperties = {};
clientProperties.cbSize = sizeof(AudioClientProperties);
clientProperties.eCategory = AudioCategory_Communications;
RETURN_IF_FAILED(_audioClient->SetClientProperties(&clientProperties));

wil::unique_cotaskmem_ptr<WAVEFORMATEX> wfxCapture;
RETURN_IF_FAILED(_audioClient->GetMixFormat(wil::out_param(wfxCapture)));

REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
RETURN_IF_FAILED(_audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
hnsRequestedDuration,
0,
wfxCapture.get(),
NULL));

bool aecEffectPresent;
RETURN_IF_FAILED(IsAcousticEchoCancellationEffectPresent(&aecEffectPresent));

if (!aecEffectPresent)
{
wprintf(L"Warning: Capture stream is not echo cancelled.\n");

// An APO vendor can add code here to insert an in-app
// acoustic echo cancellation APO before starting the capture stream.
}

wil::unique_cotaskmem_string deviceId;
RETURN_IF_FAILED(device->GetId(&deviceId));
wprintf(L"Created communications stream on capture endpoint %ls\n", deviceId.get());

RETURN_IF_FAILED(_audioClient->GetService(IID_PPV_ARGS(&_captureClient)));

return S_OK;
}

HRESULT CAECCapture::RecordCommunicationsStream()
{
DWORD mmcssTaskIndex = 0;
HANDLE mmcssTaskHandle = AvSetMmThreadCharacteristics(L"Audio", &mmcssTaskIndex);
RETURN_HR_IF(HRESULT_FROM_WIN32(GetLastError()), mmcssTaskHandle == 0);

auto avRevertMmThreadCharacteristicsOnExit = wil::scope_exit([&]()
{
AvRevertMmThreadCharacteristics(mmcssTaskHandle);
});

wil::unique_event_nothrow bufferComplete;
RETURN_IF_FAILED(bufferComplete.create());
RETURN_IF_FAILED(_audioClient->SetEventHandle(bufferComplete.get()));

RETURN_IF_FAILED(_audioClient->Start());

wprintf(L"Started communications capture stream.\n");

HANDLE events[] = { _terminationEvent.get(), bufferComplete.get() };

while (WAIT_OBJECT_0 != WaitForMultipleObjects(ARRAYSIZE(events), events, FALSE, INFINITE))
{
UINT32 packetLength = 0;
while (SUCCEEDED(_captureClient->GetNextPacketSize(&packetLength)) && packetLength > 0)
{
PBYTE buffer;
UINT32 numFramesRead;
DWORD flags = 0;
RETURN_IF_FAILED(_captureClient->GetBuffer(&buffer, &numFramesRead, &flags, nullptr, nullptr));

// At this point, the app can send the buffer to the capture pipeline.
// This program just discards the buffer without processing it.

RETURN_IF_FAILED(_captureClient->ReleaseBuffer(numFramesRead));
}
}

RETURN_IF_FAILED(_audioClient->Stop());

return S_OK;
}

HRESULT CAECCapture::StartCapture() try
{
RETURN_IF_FAILED(_terminationEvent.create());

RETURN_IF_FAILED(InitializeCaptureClient());

_captureThread = std::thread(
[this]()
{
RecordCommunicationsStream();
});

return S_OK;
} CATCH_RETURN()

HRESULT CAECCapture::StopCapture()
{
_terminationEvent.SetEvent();
_captureThread.join();
return S_OK;
}

HRESULT CAECCapture::SetEchoCancellationRenderEndpoint(PCWSTR aecReferenceEndpointId)
{
wil::com_ptr_nothrow<IAcousticEchoCancellationControl> aecControl;
HRESULT hr = _audioClient->GetService(IID_PPV_ARGS(&aecControl));

if (hr == E_NOINTERFACE)
{
// For this app, we ignore any failure to to control acoustic echo cancellation.
// (Treat as best effort.)
wprintf(L"Warning: Acoustic echo cancellation control is not available.\n");
return S_OK;
}

RETURN_IF_FAILED_MSG(hr, "_audioClient->GetService(IID_PPV_ARGS(&aecControl))");

// Call SetEchoCancellationRenderEndpoint to change the endpoint of the auxiliary input stream.
RETURN_IF_FAILED(aecControl->SetEchoCancellationRenderEndpoint(aecReferenceEndpointId));

return S_OK;
}
34 changes: 34 additions & 0 deletions Samples/AcousticEchoCancellation/cpp/AECCapture.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#include <AudioClient.h>
#include <mmdeviceapi.h>
#include <initguid.h>
#include <guiddef.h>
#include <mfapi.h>

#include <wrl\implements.h>
#include <wil\com.h>
#include <wil\result.h>

#include <thread>

class CAECCapture :
public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::ClassicCom >, Microsoft::WRL::FtmBase >
{
public:
HRESULT StartCapture();
HRESULT StopCapture();

HRESULT SetEchoCancellationRenderEndpoint(PCWSTR aecReferenceEndpointId);

private:
HRESULT InitializeCaptureClient();
HRESULT IsAcousticEchoCancellationEffectPresent(bool* result);
HRESULT RecordCommunicationsStream();

std::thread _captureThread;
wil::com_ptr_nothrow<IAudioCaptureClient> _captureClient;
wil::com_ptr_nothrow<IAudioClient2> _audioClient;

wil::unique_event_nothrow _terminationEvent;
};
104 changes: 104 additions & 0 deletions Samples/AcousticEchoCancellation/cpp/AcousticEchoCancellation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include <Windows.h>
#include <propkeydef.h>
#include <Functiondiscoverykeys_devpkey.h>
#include "AECCapture.h"

HRESULT GetRenderEndpointId(PWSTR* endpointId)
{
*endpointId = nullptr;

wil::com_ptr_nothrow<IMMDeviceEnumerator> deviceEnumerator;
RETURN_IF_FAILED(CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC, __uuidof(IMMDeviceEnumerator), (void**)&deviceEnumerator));

wil::com_ptr_nothrow<IMMDeviceCollection> spDeviceCollection;
RETURN_IF_FAILED(deviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &spDeviceCollection));

UINT deviceCount;
RETURN_IF_FAILED(spDeviceCollection->GetCount(&deviceCount));

wprintf(L"0: system default\n");

for (UINT i = 0; i < deviceCount; i++)
{
// Get the device from the collection.
wil::com_ptr_nothrow<IMMDevice> device;
RETURN_IF_FAILED(spDeviceCollection->Item(i, &device));

// Get the device friendly name.
wil::com_ptr_nothrow<IPropertyStore> properties;
RETURN_IF_FAILED(device->OpenPropertyStore(STGM_READ, &properties));
wil::unique_prop_variant variant;
RETURN_IF_FAILED(properties->GetValue(PKEY_Device_FriendlyName, &variant));

wprintf(L"%d: %ls\n", i + 1, variant.pwszVal);
}

wprintf(L"Choose a device to use as the acoustic echo cancellation render endpoint: ");
fflush(stdout);

UINT index;
if (wscanf_s(L"%u", &index) != 1)
{
return HRESULT_FROM_WIN32(ERROR_CANCELLED);
}
if (index == 0)
{
// nullptr means "use the system default"
*endpointId = nullptr;
return S_OK;
}

// Convert from 1-based index to 0-based index.
index = index - 1;

if (index > deviceCount)
{
wprintf(L"Invalid choice.\n");
return HRESULT_FROM_WIN32(ERROR_CANCELLED);
}

// Get the chosen device from the collection.
wil::com_ptr_nothrow<IMMDevice> device;
RETURN_IF_FAILED(spDeviceCollection->Item(index, &device));

// Get and return the endpoint ID for that device.
RETURN_IF_FAILED(device->GetId(endpointId));

return S_OK;
}

int wmain(int argc, wchar_t* argv[])
{
// Print diagnostic messages to the console for developer convenience.
wil::SetResultLoggingCallback([](wil::FailureInfo const& failure) noexcept
{
wchar_t message[1024];
wil::GetFailureLogString(message, ARRAYSIZE(message), failure);
wprintf(L"Diagnostic message: %ls\n", message);
});


RETURN_IF_FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED));
wil::unique_couninitialize_call uninitialize;

wil::unique_cotaskmem_string endpointId;
RETURN_IF_FAILED(GetRenderEndpointId(&endpointId));

CAECCapture aecCapture;
RETURN_IF_FAILED(aecCapture.StartCapture());

// Make sure we Stop capture even if an error occurs.
auto stop = wil::scope_exit([&]
{
aecCapture.StopCapture();
});

RETURN_IF_FAILED(aecCapture.SetEchoCancellationRenderEndpoint(endpointId.get()));

// Capture for 10 seconds.
wprintf(L"Capturing for 10 seconds...\n");
Sleep(10000);
wprintf(L"Finished.\n");

return 0;
}
31 changes: 31 additions & 0 deletions Samples/AcousticEchoCancellation/cpp/AcousticEchoCancellation.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30204.135
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AcousticEchoCancellation", "AcousticEchoCancellation.vcxproj", "{6E745655-513E-4713-B3AB-D6D3F62D7734}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Debug|x64.ActiveCfg = Debug|x64
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Debug|x64.Build.0 = Debug|x64
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Debug|x86.ActiveCfg = Debug|Win32
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Debug|x86.Build.0 = Debug|Win32
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Release|x64.ActiveCfg = Release|x64
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Release|x64.Build.0 = Release|x64
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Release|x86.ActiveCfg = Release|Win32
{6E745655-513E-4713-B3AB-D6D3F62D7734}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {64406148-8C03-42D2-8A2A-C14A03CFF8E3}
EndGlobalSection
EndGlobal
Loading

0 comments on commit 7cbd99a

Please sign in to comment.