Skip to content

Instantly share code, notes, and snippets.

@fukuroder
Last active July 8, 2021 06:38
Show Gist options
  • Save fukuroder/7823555 to your computer and use it in GitHub Desktop.
Save fukuroder/7823555 to your computer and use it in GitHub Desktop.
wave file player (WASAPI shared mode)
/*
* wave_play_wasapi_shared.cpp
*
* Created by fukuroda (https://github.com/fukuroder)
*/
// windows API
#include <windows.h>
#include <audioclient.h>
#include <mmdeviceapi.h>
// libsndfile -> http://www.mega-nerd.com/libsndfile/
#include "sndfile.h"
#pragma comment(lib, "libsndfile-1.lib")
// STL
#include <iostream>
int main()
{
SNDFILE* snd_file = nullptr;
IMMDevice* pDevice = nullptr;
IMMDeviceEnumerator* pDeviceEnumerator = nullptr;
IAudioClient* pAudioClient = nullptr;
IAudioRenderClient* pAudioRenderClient = nullptr;
HANDLE hEvent = nullptr;
try
{
// open sound file (wav/ogg/flac...)
SF_INFO sf_info = {};
snd_file = sf_open("test.wav", SFM_READ, &sf_info);
if (snd_file == nullptr) throw std::runtime_error("sf_open error");
if (sf_info.frames == 0 ||
sf_info.channels != 2 ||
sf_info.samplerate != 44100) throw std::runtime_error("44.1kHz/stereo only");
// 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,
&pDevice);
if (FAILED(hr)) throw std::runtime_error("IMMDeviceEnumerator.GetDefaultAudioEndpoint error");
std::cout << "IMMDeviceEnumerator.GetDefaultAudioEndpoint()->OK" << std::endl;
hr = pDevice->Activate(
__uuidof(IAudioClient),
CLSCTX_ALL,
nullptr,
(void**)&pAudioClient);
if (FAILED(hr)) throw std::runtime_error("IMMDevice.Activate error");
std::cout << "IMMDevice.Activate()->OK" << std::endl;
REFERENCE_TIME MinimumDevicePeriod = 0;
hr = pAudioClient->GetDevicePeriod(nullptr, &MinimumDevicePeriod);
if (FAILED(hr)) throw std::runtime_error("IAudioClient.GetDevicePeriod error");
std::cout << "minimum device period=" << MinimumDevicePeriod *100 << "[nano seconds]" << std::endl;
WAVEFORMATEX wave_format = {};
wave_format.wFormatTag = WAVE_FORMAT_PCM;
wave_format.nChannels = 2;
wave_format.nSamplesPerSec = 44100;
wave_format.nAvgBytesPerSec = 44100 * 2 * 16 / 8;
wave_format.nBlockAlign = 2 * 16 / 8;
wave_format.wBitsPerSample = 16;
hr = pAudioClient->Initialize(
AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
MinimumDevicePeriod,
0,
&wave_format,
nullptr);
if (FAILED(hr)) throw std::runtime_error("IAudioClient.Initialize error");
std::cout << "IAudioClient.Initialize()->OK" << std::endl;
// event
hEvent = CreateEvent(nullptr, false, false, nullptr);
if (FAILED(hr)) throw std::runtime_error("CreateEvent error");
hr = pAudioClient->SetEventHandle(hEvent);
if (FAILED(hr)) throw std::runtime_error("IAudioClient.SetEventHandle error");
UINT32 NumBufferFrames = 0;
hr = pAudioClient->GetBufferSize(&NumBufferFrames);
if (FAILED(hr)) throw std::runtime_error("IAudioClient.GetBufferSize error");
std::cout << "buffer frame size=" << NumBufferFrames << "[frames]" << std::endl;
hr = pAudioClient->GetService(
__uuidof(IAudioRenderClient),
(void**)&pAudioRenderClient);
if (FAILED(hr)) throw std::runtime_error("IAudioClient.GetService error");
BYTE* pData = nullptr;
hr = pAudioRenderClient->GetBuffer(NumBufferFrames, &pData);
if (FAILED(hr)) throw std::runtime_error("IAudioRenderClient.GetBuffer error");
sf_count_t read_count = sf_readf_short(snd_file, (short*)pData, NumBufferFrames);
hr = pAudioRenderClient->ReleaseBuffer((UINT32)read_count, 0);
if (FAILED(hr)) throw std::runtime_error("IAudioRenderClient.ReleaseBuffer error");
hr = pAudioClient->Start();
if (FAILED(hr)) throw std::runtime_error("IAudioClient.Start error");
std::cout << "IAudioClient.Start()->OK" << std::endl;
bool playing = (read_count == NumBufferFrames);
while (playing)
{
WaitForSingleObject(hEvent, INFINITE);
UINT32 NumPaddingFrames = 0;
hr = pAudioClient->GetCurrentPadding(&NumPaddingFrames);
if (FAILED(hr)) throw std::runtime_error("IAudioClient.GetCurrentPadding error");
UINT32 numAvailableFrames = NumBufferFrames - NumPaddingFrames;
if (numAvailableFrames == 0) continue;
hr = pAudioRenderClient->GetBuffer(numAvailableFrames, &pData);
if (FAILED(hr)) throw std::runtime_error("IAudioRenderClient.GetBuffer error");
read_count = sf_readf_short(snd_file, (short*)pData, numAvailableFrames);
hr = pAudioRenderClient->ReleaseBuffer((UINT32)read_count, 0);
if (FAILED(hr)) throw std::runtime_error("IAudioRenderClient.ReleaseBuffer error");
playing = (read_count == numAvailableFrames);
}
do
{
// wait for buffer to be empty
WaitForSingleObject(hEvent, INFINITE);
UINT32 NumPaddingFrames = 0;
hr = pAudioClient->GetCurrentPadding(&NumPaddingFrames);
if (FAILED(hr)) throw std::runtime_error("IAudioClient.GetCurrentPadding error");
if (NumPaddingFrames == 0)
{
std::cout << "current buffer padding=0[frames]" << std::endl;
break;
}
} while (true);
hr = pAudioClient->Stop();
if (FAILED(hr)) throw std::runtime_error("IAudioClient.Stop error");
std::cout << "IAudioClient.Stop()->OK" << std::endl;
}
catch (std::exception & ex)
{
std::cout << "error:" << ex.what() << std::endl;
}
if (hEvent) CloseHandle(hEvent);
if (pDeviceEnumerator) pDeviceEnumerator->Release();
if (pDevice) pDevice->Release();
if (pAudioClient) pAudioClient->Release();
if (pAudioRenderClient) pAudioRenderClient->Release();
CoUninitialize();
sf_close(snd_file);
return 0;
}
@foufikand
Copy link

Thank you for this sample can i have your contacts, i want to talk to you about WASAPI as a beginner, please!
Have a good day

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