-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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
Showing
69 changed files
with
3,666 additions
and
672 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
104
Samples/AcousticEchoCancellation/cpp/AcousticEchoCancellation.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
31
Samples/AcousticEchoCancellation/cpp/AcousticEchoCancellation.sln
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.