Skip to content

Instantly share code, notes, and snippets.

@fukuroder
Last active March 9, 2024 02:14
Show Gist options
  • Save fukuroder/e280cb9a2e2f06c83d12804fd2480f31 to your computer and use it in GitHub Desktop.
Save fukuroder/e280cb9a2e2f06c83d12804fd2480f31 to your computer and use it in GitHub Desktop.
WASAPIでcaptureしてそのままrenderするサンプル
/*
* wasapi_capture_and_render.cpp
*
* Created by fukuroda (https://github.com/fukuroder)
*/
// STL
#include <iostream>
#include <vector>
// windows API
#include <windows.h>
#include <audioclient.h>
#include <mmdeviceapi.h>
#include <avrt.h>
#pragma comment(lib, "avrt.lib")
static bool Playing = true;
std::vector<SHORT> Process(const std::vector<SHORT>& Input)
{
// do something
return Input;
}
BOOL WINAPI HandlerRoutine(DWORD dwCtrlType)
{
if (dwCtrlType == CTRL_C_EVENT) {
Playing = false;
return TRUE;
}
else {
return FALSE;
}
}
int main()
{
//
IMMDeviceEnumerator* pDeviceEnumerator = nullptr;
//
IMMDevice* pRenderDevice = nullptr;
//
IMMDevice* pCaptureDevice = nullptr;
//
IAudioClient* pRenderClient = nullptr;
//
IAudioClient* pCaptureClient = nullptr;
//
IAudioRenderClient* pRenderService = nullptr;
//
IAudioCaptureClient* pCaptureService = nullptr;
//
HANDLE hRenderEvent = nullptr;
//
HANDLE hCaptureEvent = nullptr;
//
HANDLE hTask = nullptr;
SetConsoleCtrlHandler(HandlerRoutine, TRUE);
try
{
// COM result
HRESULT hr = S_OK;
hr = CoInitialize(nullptr);
if (FAILED(hr)) {
throw std::runtime_error("CoInitialize() error");
}
hr = CoCreateInstance(
__uuidof(MMDeviceEnumerator),
nullptr,
CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator),
(void**)&pDeviceEnumerator);
if (FAILED(hr)) {
throw std::runtime_error("CoCreateInstance() error");
}
hr = pDeviceEnumerator->GetDefaultAudioEndpoint(
eRender,
eConsole,
&pRenderDevice);
if (FAILED(hr)) {
throw std::runtime_error("pDeviceEnumerator->GetDefaultAudioEndpoint() error");
}
std::cout << "pDeviceEnumerator->GetDefaultAudioEndpoint() OK" << std::endl;
hr = pDeviceEnumerator->GetDefaultAudioEndpoint(
eCapture,
eConsole,
&pCaptureDevice);
if (FAILED(hr)) {
throw std::runtime_error("pDeviceEnumerator->GetDefaultAudioEndpoint() error");
}
std::cout << "pDeviceEnumerator->GetDefaultAudioEndpoint() OK" << std::endl;
hr = pRenderDevice->Activate(
__uuidof(IAudioClient),
CLSCTX_ALL,
nullptr,
(void**)&pRenderClient);
if (FAILED(hr)) {
throw std::runtime_error("pRenderDevice->Activate() error");
}
std::cout << "pRenderDevice->Activate() OK" << std::endl;
hr = pCaptureDevice->Activate(
__uuidof(IAudioClient),
CLSCTX_ALL,
nullptr,
(void**)&pCaptureClient);
if (FAILED(hr)) {
throw std::runtime_error("pCaptureDevice->Activate() error");
}
std::cout << "pCaptureDevice->Activate() OK" << std::endl;
REFERENCE_TIME DefaultDevicePeriod = 0;
REFERENCE_TIME MinimumDevicePeriod = 0;
hr = pRenderClient->GetDevicePeriod(&DefaultDevicePeriod, &MinimumDevicePeriod);
if (FAILED(hr)) {
throw std::runtime_error("pRenderClient->GetDevicePeriod() error");
}
std::cout << "pRenderClient->GetDevicePeriod() OK" << std::endl;
std::cout << "default render device period=" << DefaultDevicePeriod * 100 << "[nano seconds]" << std::endl;
std::cout << "minimum render device period=" << MinimumDevicePeriod * 100 << "[nano seconds]" << std::endl;
REFERENCE_TIME DefaultDevicePeriod_ = 0;
REFERENCE_TIME MinimumDevicePeriod_ = 0;
hr = pCaptureClient->GetDevicePeriod(&DefaultDevicePeriod_, &MinimumDevicePeriod_);
if (FAILED(hr)) {
throw std::runtime_error("pCaptureClient->GetDevicePeriod() error");
}
std::cout << "pCaptureClient->GetDevicePeriod() OK" << std::endl;
std::cout << "default capture device period=" << DefaultDevicePeriod_ * 100 << "[nano seconds]" << std::endl;
std::cout << "minimum capture device period=" << MinimumDevicePeriod_ * 100 << "[nano seconds]" << std::endl;
WAVEFORMATEX WaveFormat = {};
WaveFormat.wFormatTag = WAVE_FORMAT_PCM;
WaveFormat.nChannels = 2;
WaveFormat.nSamplesPerSec = 44100;
WaveFormat.wBitsPerSample = 16;
WaveFormat.nAvgBytesPerSec = WaveFormat.nSamplesPerSec * WaveFormat.nChannels * WaveFormat.wBitsPerSample / 8;
WaveFormat.nBlockAlign = WaveFormat.nChannels * WaveFormat.wBitsPerSample / 8;
hr = pRenderClient->Initialize(
AUDCLNT_SHAREMODE_EXCLUSIVE, // exclusive mode
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
DefaultDevicePeriod,
DefaultDevicePeriod,
&WaveFormat,
nullptr);
if (FAILED(hr)) {
throw std::runtime_error("pRenderClient->Initialize() error");
}
std::cout << "pRenderClient->Initialize() OK" << std::endl;
hr = pCaptureClient->Initialize(
AUDCLNT_SHAREMODE_EXCLUSIVE, // exclusive mode
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
DefaultDevicePeriod,
DefaultDevicePeriod,
&WaveFormat,
nullptr);
if (FAILED(hr)) {
throw std::runtime_error("pCaptureClient->Initialize() error");
}
std::cout << "pCaptureClient->Initialize() OK" << std::endl;
// event
hRenderEvent = CreateEvent(nullptr, false, false, nullptr);
if (hRenderEvent == nullptr) {
throw std::runtime_error("CreateEvent() error");
}
hCaptureEvent = CreateEvent(nullptr, false, false, nullptr);
if (hCaptureEvent == nullptr) {
throw std::runtime_error("CreateEvent() error");
}
hr = pRenderClient->SetEventHandle(hRenderEvent);
if (FAILED(hr)) {
throw std::runtime_error("pRenderClient->SetEventHandle() error");
}
std::cout << "pRenderClient->SetEventHandle() OK" << std::endl;
hr = pCaptureClient->SetEventHandle(hCaptureEvent);
if (FAILED(hr)) {
throw std::runtime_error("pCaptureClient->SetEventHandle() error");
}
std::cout << "pCaptureClient->SetEventHandle() OK" << std::endl;
UINT32 NumRenderBufferFrames = 0;
hr = pRenderClient->GetBufferSize(&NumRenderBufferFrames);
if (FAILED(hr)) {
throw std::runtime_error("pRenderClient->GetBufferSize() error");
}
std::cout << "pRenderClient->GetBufferSize() OK" << std::endl;
std::cout << "render buffer frame size=" << NumRenderBufferFrames << "[frames]" << std::endl;
UINT32 NumCaptureBufferFrames = 0;
hr = pCaptureClient->GetBufferSize(&NumCaptureBufferFrames);
if (FAILED(hr)) {
throw std::runtime_error("pCaptureClient->GetBufferSize() error");
}
std::cout << "pCaptureClient->GetBufferSize() OK" << std::endl;
std::cout << "capture buffer frame size=" << NumCaptureBufferFrames << "[frames]" << std::endl;
hr = pRenderClient->GetService(
__uuidof(IAudioRenderClient),
(void**)&pRenderService);
if (FAILED(hr)) {
throw std::runtime_error("pRenderClient->GetService() error");
}
std::cout << "pRenderClient->GetService() OK" << std::endl;
hr = pCaptureClient->GetService(
__uuidof(IAudioCaptureClient),
(void**)&pCaptureService);
if (FAILED(hr)) {
throw std::runtime_error("pCaptureClient->GetService() error");
}
std::cout << "pCaptureClient->GetService() OK" << std::endl;
// exclusive mode
DWORD taskIndex = 0;
hTask = AvSetMmThreadCharacteristics(TEXT("Pro Audio"), &taskIndex);
if (hTask == nullptr) {
throw std::runtime_error("AvSetMmThreadCharacteristics error");
}
std::cout << "exclusive mode OK" << std::endl;
hr = pRenderClient->Start();
if (FAILED(hr)) {
throw std::runtime_error("pRenderClient->Start() error");
}
std::cout << "pRenderClient->Start() OK" << std::endl;
hr = pCaptureClient->Start();
if (FAILED(hr)) {
throw std::runtime_error("pCaptureClient->Start() error");
}
std::cout << "pCaptureClient->Start() OK" << std::endl;
while (Playing)
{
WaitForSingleObject(hCaptureEvent, INFINITE);
SHORT* pCaptureData = nullptr;
UINT32 NumFramesToRead = 0;
DWORD flags = 0;
hr = pCaptureService->GetBuffer((BYTE**)&pCaptureData, &NumFramesToRead, &flags, nullptr, nullptr);
if (FAILED(hr)) {
throw std::runtime_error("pCaptureService->GetBuffer() error");
}
std::vector<SHORT> Input(pCaptureData, &pCaptureData[NumFramesToRead * 2]);
hr = pCaptureService->ReleaseBuffer(NumFramesToRead);
if (FAILED(hr)) {
throw std::runtime_error("pCaptureService->ReleaseBuffer() error");
}
/////////////////////////////////////////////////
std::vector<SHORT> Output = Process(Input);
/////////////////////////////////////////////////
SHORT* pRenderData = nullptr;
hr = pRenderService->GetBuffer(NumFramesToRead, (BYTE**)&pRenderData);
if (FAILED(hr)) {
throw std::runtime_error("pRenderService->GetBuffer() error");
}
memcpy(pRenderData, Output.data(), Output.size() * sizeof(SHORT));
hr = pRenderService->ReleaseBuffer(NumFramesToRead, 0);
if (FAILED(hr)) {
throw std::runtime_error("pRenderService->ReleaseBuffer() error");
}
WaitForSingleObject(hRenderEvent, INFINITE);
}
do
{
// wait for buffer to be empty
UINT32 NumPaddingFrames = 0;
hr = pRenderClient->GetCurrentPadding(&NumPaddingFrames);
if (FAILED(hr)) {
throw std::runtime_error("pRenderClient->GetCurrentPadding() error");
}
std::cout << "pRenderClient->GetCurrentPadding() OK" << std::endl;
if (NumPaddingFrames == 0)
{
std::cout << "current buffer padding=0[frames]" << std::endl;
break;
}
WaitForSingleObject(hRenderEvent, INFINITE);
} while (true);
hr = pRenderClient->Stop();
if (FAILED(hr)) {
throw std::runtime_error("pRenderClient->Stop() error");
}
std::cout << "pRenderClient->Stop() OK" << std::endl;
hr = pCaptureClient->Stop();
if (FAILED(hr)) {
throw std::runtime_error("pCaptureClient->Stop() error");
}
std::cout << "pCaptureClient->Stop() OK" << std::endl;
}
catch (std::exception & ex)
{
std::cout << "error:" << ex.what() << std::endl;
}
if (pDeviceEnumerator) {
pDeviceEnumerator->Release();
pDeviceEnumerator = nullptr;
}
if (pRenderDevice) {
pRenderDevice->Release();
pRenderDevice = nullptr;
}
if (pCaptureDevice) {
pCaptureDevice->Release();
pCaptureDevice = nullptr;
}
if (pRenderClient) {
pRenderClient->Release();
pRenderClient = nullptr;
}
if (pCaptureClient) {
pCaptureClient->Release();
pCaptureClient = nullptr;
}
if (pRenderService) {
pRenderService->Release();
pRenderService = nullptr;
}
if (pCaptureService) {
pCaptureService->Release();
pCaptureService = nullptr;
}
if (hRenderEvent) {
CloseHandle(hRenderEvent);
hRenderEvent = nullptr;
}
if (hCaptureEvent) {
CloseHandle(hCaptureEvent);
hCaptureEvent = nullptr;
}
if (hTask) {
AvRevertMmThreadCharacteristics(hTask);
hTask = nullptr;
}
CoUninitialize();
return 0;
}
@Abhishek0815
Copy link

Abhishek0815 commented Mar 27, 2022

Hi,for exclusive mode this code isn't working.
error:pRenderClient->Initialize() error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment