213 lines
8.1 KiB
C
213 lines
8.1 KiB
C
|
|
||
|
static const GUID IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4c32, 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2};
|
||
|
static const GUID IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, 0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2};
|
||
|
static const GUID CLSID_MMDeviceEnumerator = {0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E};
|
||
|
static const GUID IID_IMMDeviceEnumerator = {0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6};
|
||
|
static const GUID PcmSubformatGuid = {STATIC_KSDATAFORMAT_SUBTYPE_PCM};
|
||
|
|
||
|
#ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
|
||
|
#define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
|
||
|
#endif
|
||
|
|
||
|
#ifndef AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
|
||
|
#define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
|
||
|
#endif
|
||
|
|
||
|
#define SOUND_LATENCY_FPS 10
|
||
|
#define REFTIMES_PER_SEC 10000000
|
||
|
|
||
|
#define CO_CREATE_INSTANCE(name) HRESULT name(REFCLSID rclsid, LPUNKNOWN *pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv)
|
||
|
typedef CO_CREATE_INSTANCE(CoCreateInstance_);
|
||
|
CO_CREATE_INSTANCE(CoCreateInstanceStub)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
global CoCreateInstance_ *CoCreateInstanceProc = CoCreateInstanceStub;
|
||
|
|
||
|
#define CO_INITIALIZE_EX(name) HRESULT name(LPVOID pvReserved, DWORD dwCoInit)
|
||
|
typedef CO_INITIALIZE_EX(CoInitializeEx_);
|
||
|
CO_INITIALIZE_EX(CoInitializeExStub)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
global CoInitializeEx_ *CoInitializeExProc = CoInitializeExStub;
|
||
|
|
||
|
typedef struct W32_SoundOutput W32_SoundOutput;
|
||
|
struct W32_SoundOutput
|
||
|
{
|
||
|
b32 initialized;
|
||
|
|
||
|
IMMDeviceEnumerator *device_enum;
|
||
|
IMMDevice *device;
|
||
|
IAudioClient *audio_client;
|
||
|
IAudioRenderClient *audio_render_client;
|
||
|
REFERENCE_TIME sound_buffer_duration;
|
||
|
u32 buffer_frame_count;
|
||
|
u32 channels;
|
||
|
u32 samples_per_second;
|
||
|
u32 latency_frame_count;
|
||
|
};
|
||
|
|
||
|
internal void
|
||
|
W32_LoadWASAPI(void)
|
||
|
{
|
||
|
HMODULE wasapi_lib = LoadLibraryA("ole32.dll");
|
||
|
if(wasapi_lib)
|
||
|
{
|
||
|
CoCreateInstanceProc = (CoCreateInstance_ *)GetProcAddress(wasapi_lib, "CoCreateInstance");
|
||
|
CoInitializeExProc = (CoInitializeEx_ *)GetProcAddress(wasapi_lib, "CoInitializeEx");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CoCreateInstanceProc = CoCreateInstanceStub;
|
||
|
CoInitializeExProc = CoInitializeExStub;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
W32_InitWASAPI(W32_SoundOutput *output)
|
||
|
{
|
||
|
CoInitializeExProc(0, COINIT_SPEED_OVER_MEMORY);
|
||
|
|
||
|
REFERENCE_TIME requested_sound_duration = REFTIMES_PER_SEC * 2;
|
||
|
|
||
|
HRESULT result;
|
||
|
|
||
|
result = CoCreateInstanceProc(&CLSID_MMDeviceEnumerator,
|
||
|
0,
|
||
|
CLSCTX_ALL,
|
||
|
&IID_IMMDeviceEnumerator,
|
||
|
(LPVOID *)(&output->device_enum));
|
||
|
if(result == S_OK)
|
||
|
{
|
||
|
|
||
|
output->device_enum->lpVtbl->GetDefaultAudioEndpoint(output->device_enum,
|
||
|
eRender,
|
||
|
eConsole,
|
||
|
&output->device);
|
||
|
if(result == S_OK)
|
||
|
{
|
||
|
|
||
|
result = output->device->lpVtbl->Activate(output->device,
|
||
|
&IID_IAudioClient,
|
||
|
CLSCTX_ALL,
|
||
|
0,
|
||
|
(void **)&output->audio_client);
|
||
|
if(result == S_OK)
|
||
|
{
|
||
|
WAVEFORMATEX *wave_format = 0;
|
||
|
|
||
|
output->audio_client->lpVtbl->GetMixFormat(output->audio_client, &wave_format);
|
||
|
|
||
|
output->samples_per_second = 44100;//wave_format->nSamplesPerSec;
|
||
|
WORD bits_per_sample = sizeof(i16)*8;
|
||
|
WORD block_align = (output->channels * bits_per_sample) / 8;
|
||
|
DWORD average_bytes_per_second = block_align * output->samples_per_second;
|
||
|
WORD cb_size = 0;
|
||
|
|
||
|
WAVEFORMATEX new_wave_format = {
|
||
|
WAVE_FORMAT_PCM,
|
||
|
(WORD)output->channels,
|
||
|
output->samples_per_second,
|
||
|
average_bytes_per_second,
|
||
|
block_align,
|
||
|
bits_per_sample,
|
||
|
cb_size,
|
||
|
};
|
||
|
|
||
|
result = output->audio_client->lpVtbl->Initialize(output->audio_client,
|
||
|
AUDCLNT_SHAREMODE_SHARED,
|
||
|
AUDCLNT_STREAMFLAGS_RATEADJUST | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY,
|
||
|
requested_sound_duration,
|
||
|
0,
|
||
|
&new_wave_format,
|
||
|
0);
|
||
|
|
||
|
output->latency_frame_count = output->samples_per_second / SOUND_LATENCY_FPS;
|
||
|
|
||
|
if(result == S_OK)
|
||
|
{
|
||
|
|
||
|
result = output->audio_client->lpVtbl->GetService(output->audio_client,
|
||
|
&IID_IAudioRenderClient,
|
||
|
(void **)&output->audio_render_client);
|
||
|
|
||
|
if(result == S_OK)
|
||
|
{
|
||
|
// NOTE(Ryan): Audio initialization was successful
|
||
|
|
||
|
output->audio_client->lpVtbl->GetBufferSize(output->audio_client, &output->buffer_frame_count);
|
||
|
|
||
|
output->sound_buffer_duration = (REFERENCE_TIME)((f64)REFTIMES_PER_SEC *
|
||
|
output->buffer_frame_count / output->samples_per_second);
|
||
|
|
||
|
output->audio_client->lpVtbl->Start(output->audio_client);
|
||
|
|
||
|
output->initialized = 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
W32_OutputError("WASAPI Error", "Request for audio render service failed.");
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
W32_OutputError("WASAPI Error",
|
||
|
"Audio client initialization failed.");
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
W32_OutputError("WASAPI Error", "Could not activate audio device.");
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
W32_OutputError("WASAPI Error", "Default audio endpoint was not found.");
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
W32_OutputError("WASAPI Error", "Device enumerator retrieval failed.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
W32_CleanUpWASAPI(W32_SoundOutput *output)
|
||
|
{
|
||
|
if(output->initialized)
|
||
|
{
|
||
|
output->audio_client->lpVtbl->Stop(output->audio_client);
|
||
|
|
||
|
output->device_enum->lpVtbl->Release(output->device_enum);
|
||
|
output->device->lpVtbl->Release(output->device);
|
||
|
output->audio_client->lpVtbl->Release(output->audio_client);
|
||
|
output->audio_render_client->lpVtbl->Release(output->audio_render_client);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
W32_FillSoundBuffer(u32 samples_to_write, f32 *samples, W32_SoundOutput *output)
|
||
|
{
|
||
|
if(samples_to_write)
|
||
|
{
|
||
|
BYTE *data = 0;
|
||
|
DWORD flags = 0;
|
||
|
|
||
|
output->audio_render_client->lpVtbl->GetBuffer(output->audio_render_client, samples_to_write, &data);
|
||
|
if(data)
|
||
|
{
|
||
|
i16 *destination = (i16 *)data;
|
||
|
f32 *source = samples;
|
||
|
for(UINT32 i = 0; i < samples_to_write; ++i)
|
||
|
{
|
||
|
i16 left = (i16)(*source++ * 3000);
|
||
|
i16 right = (i16)(*source++ * 3000);
|
||
|
*destination++ = left;
|
||
|
*destination++ = right;
|
||
|
}
|
||
|
}
|
||
|
output->audio_render_client->lpVtbl->ReleaseBuffer(output->audio_render_client, samples_to_write, flags);
|
||
|
}
|
||
|
}
|