Hello!
After failing miserably to implement WASAPI on my own, I am trying to follow the example made by Martins for Day 19 (https://gist.github.com/mmozeiko/38c64bb65855d783645c#file-win32_handmade-cpp).
Currently I'm doing the buffer fill on a separate thread, writing directly the test sine wave (no secondary buffer yet).
The problem is that the sound doesn't start until ~1 second passes, even tho data is being written in the buffer since the beginning.
Any hint on what could be the problem?
#include "Mmdeviceapi.h" #include "audioclient.h" #include "avrt.h" global HANDLE refillEvent; global IAudioClient* audioClient; global IAudioRenderClient* renderClient; global WAVEFORMATEX waveformat; global u32 bufferSize; #define framesOfAudioLatency 1 #define gameUpdateHz 60 s32 latencySampleCount; s32 secondaryBufferSize; inline void audio_init() { waveformat.wFormatTag = WAVE_FORMAT_PCM; waveformat.nChannels = 2; waveformat.nSamplesPerSec = 48000; waveformat.wBitsPerSample = 16; waveformat.nBlockAlign = waveformat.nChannels * waveformat.wBitsPerSample / 8; waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec * waveformat.nBlockAlign; waveformat.cbSize = 0; latencySampleCount = framesOfAudioLatency * (waveformat.nSamplesPerSec / gameUpdateHz); secondaryBufferSize = waveformat.nSamplesPerSec; REFERENCE_TIME bufferDuration = 10000000ULL * secondaryBufferSize / waveformat.nSamplesPerSec; /////////////// HRESULT hr; hr = CoInitializeEx(NULL, COINIT_SPEED_OVER_MEMORY); assert(hr == S_OK); IMMDeviceEnumerator* pEnumerator; hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator); assert(hr == S_OK); IMMDevice* device; hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); assert(hr == S_OK); hr = device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&audioClient); assert(hr == S_OK); hr = audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, bufferDuration, 0, &waveformat, NULL); assert(hr == S_OK); hr = audioClient->GetService(__uuidof(IAudioRenderClient), (void**)&renderClient); assert(hr == S_OK); hr = audioClient->GetBufferSize(&bufferSize); assert(hr == S_OK); refillEvent = CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE); hr = audioClient->SetEventHandle(refillEvent); assert(hr == S_OK); BYTE* data = NULL; hr = renderClient->GetBuffer(bufferSize, &data); assert(hr == S_OK); hr = renderClient->ReleaseBuffer(bufferSize, AUDCLNT_BUFFERFLAGS_SILENT); assert(hr == S_OK); hr = audioClient->Start(); assert(hr == S_OK); } #define M_PI 3.1416 #define TAU (M_PI * 2) inline void sine_wave(s32 samplesToWrite, s16 *samples, s32 toneHz = 256) { static u32 runningSampleIndex = 0; s32 samplesPerSec = waveformat.nSamplesPerSec; s32 squareWavePeriod = samplesPerSec / toneHz; s32 halfSquareWavePeriod = (squareWavePeriod / 2); s16* at = samples; for(s32 i = 0; i < samplesToWrite; i++) { s16 sampleValue = ((runningSampleIndex / halfSquareWavePeriod) % 2) ? 1000 : -1000; *at++ = sampleValue; *at++ = sampleValue; runningSampleIndex++; } } inline void audio_job(void* jobData) { DWORD avrtTaskIndex; AvSetMmThreadCharacteristicsA("Pro Audio", &avrtTaskIndex); while(true) { DWORD res = WaitForSingleObject(refillEvent, INFINITE); if(res == WAIT_OBJECT_0) { s32 samplesToWrite = 0; u32 soundPaddingSize; audioClient->GetCurrentPadding(&soundPaddingSize); samplesToWrite = secondaryBufferSize - soundPaddingSize; if(samplesToWrite > latencySampleCount) { samplesToWrite = latencySampleCount; } if(samplesToWrite > 0) { BYTE* buffer = NULL; HRESULT hr = renderClient->GetBuffer(samplesToWrite, &buffer); assert(hr == S_OK); s16* dest = (s16*)buffer; sine_wave(samplesToWrite, dest); hr = renderClient->ReleaseBuffer(samplesToWrite, 0); assert(hr == S_OK); } } } }