cOMS/platform/win32/audio/XAudio2.h

216 lines
6.3 KiB
C

/**
* Jingga
*
* @copyright Jingga
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef TOS_SOUND_XAUDIO2_H
#define TOS_SOUND_XAUDIO2_H
#include <xaudio2.h>
#include <windows.h>
#include <objbase.h>
#include "../../../stdlib/Types.h"
#include "../../../audio/AudioSetting.h"
#include "../../../utils/MathUtils.h"
#include "../../../log/Log.h"
struct XAudio2Setting {
IXAudio2* audio_handle;
IXAudio2SourceVoice* source_voice;
IXAudio2MasteringVoice* mastering_voice;
XAUDIO2_BUFFER internal_buffer[2];
};
// BEGIN: Dynamically load XAudio2
typedef HRESULT WINAPI audio_create(IXAudio2**, UINT32, XAUDIO2_PROCESSOR);
HRESULT WINAPI XAudio2CreateStub(IXAudio2**, UINT32, XAUDIO2_PROCESSOR) {
return 0;
}
// END: Dynamically load XAudio2
void audio_load(HWND hwnd, AudioSetting* setting, XAudio2Setting* api_setting) {
CoInitialize(NULL);
HMODULE lib = LoadLibraryExA((LPCSTR) "xaudio2_9.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!lib) {
LOG(log_memory, "Xaudio2: Couldn't load xaudio2_9.dll\n", log_fp, true, true);
lib = LoadLibraryExA((LPCSTR) "xaudio2_8.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
}
if (!lib) {
LOG(log_memory, "Xaudio2: Couldn't load xaudio2_8.dll\n", log_fp, true, true);
return;
}
audio_create* XAudio2Create = (audio_create *) GetProcAddress(lib, "XAudio2Create");
if (!XAudio2Create || !SUCCEEDED(XAudio2Create(&api_setting->audio_handle, 0, XAUDIO2_DEFAULT_PROCESSOR))) {
LOG(log_memory, "Xaudio2: XAudio2Create failed\n", log_fp, true, true);
return;
}
HRESULT hr;
if (!SUCCEEDED(hr = api_setting->audio_handle->CreateMasteringVoice(
&api_setting->mastering_voice,
XAUDIO2_DEFAULT_CHANNELS,
setting->sample_rate,
0,
NULL))
) {
LOG(log_memory, "Xaudio2: CreateMasteringVoice failed\n", log_fp, true, true);
return;
}
WAVEFORMATEX wf = {};
wf.wFormatTag = WAVE_FORMAT_PCM;
wf.nChannels = 2;
wf.wBitsPerSample = (uint16) ((setting->sample_size * 8) / wf.nChannels); // = sample_size per channel
wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) / 8; // = sample_szie
wf.nSamplesPerSec = setting->sample_rate;
wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; // = buffer_size
wf.cbSize = 0;
if (!SUCCEEDED(api_setting->audio_handle->CreateSourceVoice(&api_setting->source_voice, &wf))) {
LOG(log_memory, "Xaudio2: CreateSourceVoice failed\n", log_fp, true, true);
return;
}
// @todo consider to remove mallocs/callocs
setting->buffer_size = setting->sample_rate * setting->sample_size;
setting->buffer = (int16 *) calloc(setting->sample_rate, setting->sample_size);
api_setting->internal_buffer[0].Flags = 0;
api_setting->internal_buffer[0].AudioBytes = setting->buffer_size;
api_setting->internal_buffer[0].pAudioData = (byte *) malloc(setting->buffer_size * sizeof(byte));
api_setting->internal_buffer[0].PlayBegin = 0;
api_setting->internal_buffer[0].PlayLength = 0;
api_setting->internal_buffer[0].LoopBegin = 0;
api_setting->internal_buffer[0].LoopLength = 0;
api_setting->internal_buffer[0].LoopCount = 0;
api_setting->internal_buffer[0].pContext = NULL;
api_setting->internal_buffer[1].Flags = 0;
api_setting->internal_buffer[1].AudioBytes = setting->buffer_size;
api_setting->internal_buffer[1].pAudioData = (byte *) malloc(setting->buffer_size * sizeof(byte));
api_setting->internal_buffer[1].PlayBegin = 0;
api_setting->internal_buffer[1].PlayLength = 0;
api_setting->internal_buffer[1].LoopBegin = 0;
api_setting->internal_buffer[1].LoopLength = 0;
api_setting->internal_buffer[1].LoopCount = 0;
api_setting->internal_buffer[1].pContext = NULL;
setting->sample_index = 0;
}
inline
void audio_play(AudioSetting* setting, XAudio2Setting* api_setting) {
if (!api_setting->source_voice) {
return;
}
api_setting->source_voice->Start(0, XAUDIO2_COMMIT_NOW);
setting->is_playing = true;
if (setting->sample_index > 1) {
setting->sample_index = 0;
}
}
inline
void audio_stop(AudioSetting* setting, XAudio2Setting* api_setting) {
if (!api_setting->source_voice) {
return;
}
api_setting->source_voice->Stop(0, XAUDIO2_COMMIT_NOW);
setting->is_playing = false;
}
inline
void audio_free(AudioSetting* setting, XAudio2Setting* api_setting)
{
if (api_setting->source_voice) {
api_setting->source_voice->DestroyVoice();
}
if (api_setting->mastering_voice) {
api_setting->mastering_voice->DestroyVoice();
}
if (api_setting->audio_handle) {
api_setting->audio_handle->Release();
}
if (api_setting->internal_buffer[0].pAudioData) {
free((void *) api_setting->internal_buffer[0].pAudioData);
}
if (api_setting->internal_buffer[1].pAudioData) {
free((void *) api_setting->internal_buffer[1].pAudioData);
}
if (setting->buffer) {
free((void *) setting->buffer);
}
}
/**
* Calculates the samples to generate for the buffer
*
* For XAudio2 we currently always fill the entire buffer size.
* For other audio APIs we maybe have to do something else
*/
inline
uint32 audio_buffer_fillable(const AudioSetting* setting, const XAudio2Setting* api_setting)
{
if (!api_setting->source_voice) {
return 0;
}
XAUDIO2_VOICE_STATE state;
api_setting->source_voice->GetState(&state);
if (state.BuffersQueued > 1) {
return 0;
}
return setting->buffer_size;
}
inline
void audio_play_buffer(AudioSetting* setting, XAudio2Setting* api_setting) {
if (!api_setting->source_voice || setting->sample_buffer_size == 0) {
return;
}
if (!setting->is_playing) {
audio_play(setting, api_setting);
}
uint32 idx = setting->sample_output % 2;
memcpy(
(void *) api_setting->internal_buffer[idx].pAudioData,
setting->buffer,
setting->sample_buffer_size
);
if (!SUCCEEDED(api_setting->source_voice->SubmitSourceBuffer(&api_setting->internal_buffer[idx]))) {
LOG(log_memory, "Xaudio2: SubmitSourceBuffer failed\n", log_fp, true, true);
return;
}
setting->sample_index += setting->sample_buffer_size / setting->sample_size;
setting->sample_buffer_size = 0;
}
#endif