/* Simple C interface for XAudio2 Author: Hans-Kristian Arntzen License: Public Domain */ #include "xaudio.h" #include "xaudio-c.h" #include #include #include "../../msvc/msvc_compat.h" #define MAX_BUFFERS 16 #define MAX_BUFFERS_MASK (MAX_BUFFERS - 1) #undef min #undef max #define min(a, b) ((a) < (b) ? (a) : (b)) #define max(a, b) ((a) > (b) ? (a) : (b)) struct xaudio2 { const IXAudio2VoiceCallbackVtbl *lpVtbl; uint8_t *buf; IXAudio2 *pXAudio2; IXAudio2MasteringVoice *pMasterVoice; IXAudio2SourceVoice *pSourceVoice; HANDLE hEvent; volatile long buffers; unsigned bufsize; unsigned bufptr; unsigned write_buffer; }; static void WINAPI voice_on_buffer_end(void *handle_, void *data) { (void)data; xaudio2_t *handle = (xaudio2_t*)handle_; InterlockedDecrement(&handle->buffers); SetEvent(handle->hEvent); } static void WINAPI dummy_voidp(void *handle, void *data) { (void)handle; (void)data; } static void WINAPI dummy_nil(void *handle) { (void)handle; } static void WINAPI dummy_uint32(void *handle, UINT32 dummy) { (void)handle; (void)dummy; } static void WINAPI dummy_voidp_hresult(void *handle, void *data, HRESULT dummy) { (void)handle; (void)data; (void)dummy; } const struct IXAudio2VoiceCallbackVtbl voice_vtable = { dummy_uint32, dummy_nil, dummy_nil, dummy_voidp, voice_on_buffer_end, dummy_voidp, dummy_voidp_hresult, }; void xaudio2_enumerate_devices(xaudio2_t *xa) { UINT32 dev_count; IXAudio2_GetDeviceCount(xa->pXAudio2, &dev_count); fprintf(stderr, "XAudio2 devices:\n"); for (unsigned i = 0; i < dev_count; i++) { XAUDIO2_DEVICE_DETAILS dev_detail; IXAudio2_GetDeviceDetails(xa->pXAudio2, i, &dev_detail); fwprintf(stderr, L"\t%u: %s\n", i, dev_detail.DisplayName); } } xaudio2_t *xaudio2_new(unsigned samplerate, unsigned channels, size_t size, unsigned device) { xaudio2_t *handle = (xaudio2_t*)calloc(1, sizeof(*handle)); if (!handle) return NULL; handle->lpVtbl = &voice_vtable; CoInitializeEx(0, COINIT_MULTITHREADED); WAVEFORMATEX wfx = {0}; if (FAILED(XAudio2Create(&handle->pXAudio2))) goto error; if (FAILED(IXAudio2_CreateMasteringVoice(handle->pXAudio2, &handle->pMasterVoice, channels, samplerate, 0, device, NULL))) goto error; wfx.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; wfx.nChannels = channels; wfx.nSamplesPerSec = samplerate; wfx.nBlockAlign = channels * sizeof(float); wfx.wBitsPerSample = sizeof(float) * 8; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; wfx.cbSize = 0; if (FAILED(IXAudio2_CreateSourceVoice(handle->pXAudio2, &handle->pSourceVoice, &wfx, XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO, (IXAudio2VoiceCallback*)handle, 0, 0))) goto error; handle->hEvent = CreateEvent(0, FALSE, FALSE, 0); if (!handle->hEvent) goto error; handle->bufsize = size / MAX_BUFFERS; handle->buf = (uint8_t*)calloc(1, handle->bufsize * MAX_BUFFERS); if (!handle->buf) goto error; if (FAILED(IXAudio2SourceVoice_Start(handle->pSourceVoice, 0, XAUDIO2_COMMIT_NOW))) goto error; return handle; error: xaudio2_free(handle); return NULL; } size_t xaudio2_write_avail(xaudio2_t *handle) { return handle->bufsize * (MAX_BUFFERS - handle->buffers - 1); } size_t xaudio2_write(xaudio2_t *handle, const void *buf, size_t bytes_) { unsigned bytes = bytes_; const uint8_t *buffer = (const uint8_t*)buf; while (bytes) { unsigned need = min(bytes, handle->bufsize - handle->bufptr); memcpy(handle->buf + handle->write_buffer * handle->bufsize + handle->bufptr, buffer, need); handle->bufptr += need; buffer += need; bytes -= need; if (handle->bufptr == handle->bufsize) { while (handle->buffers == MAX_BUFFERS - 1) WaitForSingleObject(handle->hEvent, INFINITE); XAUDIO2_BUFFER xa2buffer = {0}; xa2buffer.AudioBytes = handle->bufsize; xa2buffer.pAudioData = handle->buf + handle->write_buffer * handle->bufsize; if (FAILED(IXAudio2SourceVoice_SubmitSourceBuffer(handle->pSourceVoice, &xa2buffer, NULL))) return 0; InterlockedIncrement(&handle->buffers); handle->bufptr = 0; handle->write_buffer = (handle->write_buffer + 1) & MAX_BUFFERS_MASK; } } return bytes_; } void xaudio2_free(xaudio2_t *handle) { if (handle) { if (handle->pSourceVoice) { IXAudio2SourceVoice_Stop(handle->pSourceVoice, 0, XAUDIO2_COMMIT_NOW); IXAudio2SourceVoice_DestroyVoice(handle->pSourceVoice); } if (handle->pMasterVoice) IXAudio2MasteringVoice_DestroyVoice(handle->pMasterVoice); if (handle->pXAudio2) IXAudio2_Release(handle->pXAudio2); if (handle->hEvent) CloseHandle(handle->hEvent); free(handle->buf); free(handle); } }