Created
June 10, 2016 18:03
-
-
Save anonymous/6359a8273ab33bcf24bd705ea779a09f to your computer and use it in GitHub Desktop.
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
#include "sound.h" | |
#include <Audioclient.h> | |
#include <audiopolicy.h> | |
#include <mmdeviceapi.h> | |
#include <ksmedia.h> | |
#include <stdio.h> | |
#include <cmath> | |
#include <limits> | |
#include <Avrt.h> | |
// REFERENCE_TIME time units per second and per millisecond | |
#define REFTIMES_PER_SEC 10000000 | |
#define REFTIMES_PER_MILLISEC 10000 | |
#define IF_ERROR_EXIT(hr) do {\ | |
if (FAILED(hr)){\ | |
printf("sound.cpp:%d: WASAPI code error %lX\n", __LINE__, hr);\ | |
goto exit_err;\ | |
}\ | |
} while(0) | |
#define SAFE_RELEASE(punk) \ | |
if ((punk) != NULL) \ | |
{ (punk)->Release(); (punk) = NULL; } | |
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); | |
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); | |
const IID IID_IAudioClient = __uuidof(IAudioClient); | |
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); | |
#define TWO_PI (3.14159265359*2) | |
static inline float sin01(float alpha) { | |
printf("alpha: %f\n", alpha); | |
return 0.5*sin(alpha) + 0.5; | |
} | |
static inline float sin_minmax_Hz(float min, float max, float freq_Hz, float t) { | |
return (max - min) / 2.0 * sin01(t * freq_Hz * TWO_PI); | |
} | |
HRESULT PlayAudioStream() { | |
HRESULT hr; | |
REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC; | |
IMMDeviceEnumerator *pEnumerator = NULL; | |
IMMDevice *pDevice = NULL; | |
IAudioClient *pAudioClient = NULL; | |
IAudioRenderClient *pRenderClient = NULL; | |
UINT32 bufferFrameCount = 0; | |
WAVEFORMATEX *pwfx = NULL; | |
BYTE *pData = NULL; | |
DWORD flags = 0; | |
HANDLE hEvent = NULL; | |
HANDLE hTask = NULL; | |
WAVEFORMATEX wave_format = {}; | |
hr = CoCreateInstance( | |
CLSID_MMDeviceEnumerator, NULL, | |
CLSCTX_ALL, IID_IMMDeviceEnumerator, | |
(void**)&pEnumerator); | |
IF_ERROR_EXIT(hr); | |
hr = pEnumerator->GetDefaultAudioEndpoint( | |
eRender, eConsole, &pDevice); | |
IF_ERROR_EXIT(hr); | |
hr = pDevice->Activate( | |
IID_IAudioClient, CLSCTX_ALL, | |
NULL, (void**)&pAudioClient); | |
IF_ERROR_EXIT(hr); | |
REFERENCE_TIME DefaultDevicePeriod = 0, MinimumDevicePeriod = 0; | |
hr = pAudioClient->GetDevicePeriod(&DefaultDevicePeriod, &MinimumDevicePeriod); | |
IF_ERROR_EXIT(hr); | |
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->IsFormatSupported( | |
AUDCLNT_SHAREMODE_EXCLUSIVE, | |
&wave_format, | |
NULL // can't suggest a "closest match" in exclusive mode | |
); | |
if (AUDCLNT_E_UNSUPPORTED_FORMAT == hr) { | |
printf("WASAPI: default audio device does not support the requested WAVEFORMATEX (44100/2ch/16bit, aka CD)\n"); | |
pAudioClient->Release(); | |
return hr; | |
} | |
IF_ERROR_EXIT(hr); | |
hr = pAudioClient->Initialize( | |
AUDCLNT_SHAREMODE_EXCLUSIVE, | |
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, | |
MinimumDevicePeriod, | |
MinimumDevicePeriod, | |
&wave_format, | |
NULL); | |
IF_ERROR_EXIT(hr); | |
// Get the actual size of the allocated buffer. | |
hr = pAudioClient->GetBufferSize(&bufferFrameCount); | |
INT32 FrameSize_bytes = bufferFrameCount * wave_format.nChannels * wave_format.wBitsPerSample / 8; | |
IF_ERROR_EXIT(hr); | |
hr = pAudioClient->GetService( | |
IID_IAudioRenderClient, | |
(void**)&pRenderClient); | |
IF_ERROR_EXIT(hr); | |
hEvent = CreateEvent(nullptr, false, false, nullptr); | |
if (hEvent == INVALID_HANDLE_VALUE) { printf("CreateEvent failed\n"); return -1; } | |
hr = pAudioClient->SetEventHandle(hEvent); | |
IF_ERROR_EXIT(hr); | |
const size_t num_samples = FrameSize_bytes / sizeof(unsigned short); | |
unsigned short *samples = new unsigned short[num_samples]; | |
float min = (float)(std::numeric_limits<unsigned short>::min)(); | |
float max = (float)(std::numeric_limits<unsigned short>::max)(); | |
float halfmax = max / 2.0; | |
float dt = 1.0 / (float)wave_format.nSamplesPerSec; | |
float freq = (float)wave_format.nSamplesPerSec / (float)bufferFrameCount; | |
for (int i = 0; i < num_samples/2; ++i) { | |
float t = (float)i * dt; | |
samples[2*i] = sin_minmax_Hz(min, max, freq, t); // L channel | |
samples[2*i + 1] = sin_minmax_Hz(min, max, freq, t); // R channel | |
} | |
hr = pRenderClient->GetBuffer(bufferFrameCount, &pData); | |
IF_ERROR_EXIT(hr); | |
memcpy(pData, samples, FrameSize_bytes); | |
hr = pRenderClient->ReleaseBuffer(bufferFrameCount, flags); | |
IF_ERROR_EXIT(hr); | |
printf("bufferFrameCount: %d, FrameSize_bytes: %d\n", bufferFrameCount, FrameSize_bytes); | |
// increase thread priority for optimal av performance | |
DWORD taskIndex = 0; | |
hTask = AvSetMmThreadCharacteristics(TEXT("Pro Audio"), &taskIndex); | |
if (hTask == NULL) { | |
hr = E_FAIL; | |
IF_ERROR_EXIT(hr); | |
} | |
hr = pAudioClient->Start(); // Start playing. | |
IF_ERROR_EXIT(hr); | |
while (true) { | |
WaitForSingleObject(hEvent, INFINITE); | |
hr = pRenderClient->GetBuffer(bufferFrameCount, &pData); | |
IF_ERROR_EXIT(hr); | |
memcpy(pData, samples, FrameSize_bytes); | |
hr = pRenderClient->ReleaseBuffer(bufferFrameCount, 0); | |
IF_ERROR_EXIT(hr); | |
} | |
hr = pAudioClient->Stop(); // Stop playing. | |
IF_ERROR_EXIT(hr); | |
exit_err: | |
SAFE_RELEASE(pEnumerator) | |
SAFE_RELEASE(pDevice) | |
SAFE_RELEASE(pAudioClient) | |
SAFE_RELEASE(pRenderClient) | |
if (hEvent != NULL) { | |
CloseHandle(hEvent); | |
} | |
if (hTask != NULL) { | |
AvRevertMmThreadCharacteristics(hTask); | |
} | |
return hr; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment