From 8c1ef3051df08f314807578eb828ccf16da3947a Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 27 Jan 2011 01:46:00 +0100 Subject: [PATCH] Add XAudio support. Need a separate xaudio-c.dll which actually implements the interface. --- Makefile.win32 | 8 ++- audio/xaudio-c.cpp | 160 +++++++++++++++++++++++++++++++++++++++++++++ audio/xaudio-c.h | 38 +++++++++++ audio/xaudio.c | 158 ++++++++++++++++++++++++++++++++++++++++++++ config.def.h | 3 + driver.c | 27 +------- driver.h | 1 + settings.c | 3 + 8 files changed, 373 insertions(+), 25 deletions(-) create mode 100644 audio/xaudio-c.cpp create mode 100644 audio/xaudio-c.h create mode 100644 audio/xaudio.c diff --git a/Makefile.win32 b/Makefile.win32 index d8d0c9dd31..a2bf77eec2 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -10,6 +10,7 @@ HAVE_SRC = 1 HAVE_SDL = 1 HAVE_XML = 1 HAVE_FREETYPE = 1 +HAVE_XAUDIO = 1 libsnes ?= -lsnes LIBS = @@ -31,6 +32,11 @@ ifeq ($(HAVE_SDL), 1) DEFINES += $(SDL_CFLAGS) -DHAVE_SDL endif +ifeq ($(HAVE_XAUDIO), 1) + OBJ += audio/xaudio.o + DEFINES += -DHAVE_XAUDIO +endif + ifeq ($(HAVE_XML), 1) OBJ += gfx/shader_glsl.o DEFINES += $(XML_CFLAGS) -DHAVE_XML @@ -84,7 +90,7 @@ clean: rm -f tools/*.o dist: all - zip -r ssnes-win32-0.2.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll libsamplerate-0.dll freetype6.dll $(JTARGET) + zip -r ssnes-win32-0.2.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll libsamplerate-0.dll freetype6.dll xaudio-c.dll $(JTARGET) libs: wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-libs.zip --no-check-certificate diff --git a/audio/xaudio-c.cpp b/audio/xaudio-c.cpp new file mode 100644 index 0000000000..b1ba3e1ec3 --- /dev/null +++ b/audio/xaudio-c.cpp @@ -0,0 +1,160 @@ +/* + Simple C interface for XAudio2 + Author: Hans-Kristian Arntzen + License: Public Domain +*/ + +// This will currently only compile with MSVC2k10. +// Should be compilable with some MinGW trickery as well. + +#include "xaudio-c.h" + +struct xaudio2 : public IXAudio2VoiceCallback +{ + xaudio2(unsigned samplerate, unsigned channels, unsigned bits, unsigned size) : + buf(nullptr), pXAudio2(nullptr), pMasterVoice(nullptr), pSourceVoice(nullptr), + buffers(0), bufptr(0) + { + CoInitializeEx(nullptr, COINIT_MULTITHREADED); + HRESULT hr; + if (FAILED(hr = XAudio2Create(&pXAudio2, 0))) + throw -1; + if (FAILED(hr = pXAudio2->CreateMasteringVoice( + &pMasterVoice, channels, samplerate, 0, 0, nullptr))) + throw -1; + WAVEFORMATEX wfx; + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = channels; + wfx.nSamplesPerSec = samplerate; + wfx.nBlockAlign = channels * bits / 8; + wfx.wBitsPerSample = bits; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + wfx.cbSize = 0; + if (FAILED(hr = pXAudio2->CreateSourceVoice( + &pSourceVoice, &wfx, XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO, this, nullptr, nullptr))) + throw -1; + + pSourceVoice->Start(); + + hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (!hEvent) + throw -1; + + max_buffers = 8; + bufsize = size / max_buffers; + buf = new uint8_t[bufsize * max_buffers]; + write_buffer = 0; + } + + ~xaudio2() + { + if (pSourceVoice) + { + pSourceVoice->Stop(); + pSourceVoice->DestroyVoice(); + } + + if (pMasterVoice) + pMasterVoice->DestroyVoice(); + + if (pXAudio2) + pXAudio2->Release(); + + if (hEvent) + CloseHandle(hEvent); + + if (buf) + delete[] buf; + } + + size_t write_avail() const + { + return bufsize * (max_buffers - buffers - 1); + } + + size_t write(const void *buffer, size_t bytes) + { + size_t written = 0; + while (written < bytes) + { + size_t need = min(bytes - written, bufsize - bufptr); + memcpy((uint8_t*)buf + write_buffer * bufsize + bufptr, (const uint8_t*)buffer + written, need); + written += need; + bufptr += need; + + if (bufptr == bufsize) + { + while (buffers == max_buffers - 1) + WaitForSingleObject(hEvent, INFINITE); + + XAUDIO2_BUFFER xa2buffer = {0}; + xa2buffer.AudioBytes = bufsize; + xa2buffer.pAudioData = buf + write_buffer * bufsize; + xa2buffer.pContext = nullptr; + InterlockedIncrement(&buffers); + pSourceVoice->SubmitSourceBuffer(&xa2buffer); + bufptr = 0; + write_buffer = (write_buffer + 1) % max_buffers; + } + } + return bytes; + } + + STDMETHOD_(void, OnBufferStart) (void *) {} + STDMETHOD_(void, OnBufferEnd) (void *) + { + InterlockedDecrement(&buffers); + SetEvent(hEvent); + } + STDMETHOD_(void, OnLoopEnd) (void *) {} + STDMETHOD_(void, OnStreamEnd) () {} + STDMETHOD_(void, OnVoiceError) (void *, HRESULT) {} + STDMETHOD_(void, OnVoiceProcessingPassEnd) () + { + SetEvent(hEvent); + } + STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {} + + uint8_t *buf; + IXAudio2 *pXAudio2; + IXAudio2MasteringVoice *pMasterVoice; + IXAudio2SourceVoice *pSourceVoice; + HANDLE hEvent; + + volatile long buffers; + unsigned max_buffers; + unsigned bufsize; + unsigned bufptr; + unsigned write_buffer; +}; + +XAUDIOC_API xaudio2_t *xaudio2_new(unsigned samplerate, unsigned channels, unsigned bits, size_t size) +{ + xaudio2 *handle = nullptr; + try { + handle = new xaudio2(samplerate, channels, bits, size); + } catch(...) { delete handle; return nullptr; } + + return handle; +} + +XAUDIOC_API size_t xaudio2_write_avail(xaudio2_t *handle) +{ + try { + return handle->write_avail(); + } catch(...) { return 0; } +} + +XAUDIOC_API size_t xaudio2_write(xaudio2_t *handle, const void *buf, size_t bytes) +{ + try { + return handle->write(buf, bytes); + } catch(...) { return 0; } +} + +XAUDIOC_API void xaudio2_free(xaudio2_t *handle) +{ + delete handle; +} + + diff --git a/audio/xaudio-c.h b/audio/xaudio-c.h new file mode 100644 index 0000000000..a100c991fe --- /dev/null +++ b/audio/xaudio-c.h @@ -0,0 +1,38 @@ +/* + Simple C interface for XAudio2 + Author: Hans-Kristian Arntzen + License: Public Domain +*/ + +#ifndef XAUDIO_C_H +#define XAUDIO_C_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef XAUDIOC_EXPORTS +#define XAUDIOC_API __declspec(dllexport) +#else +#define XAUDIOC_API __declspec(dllimport) +#endif + +typedef struct xaudio2 xaudio2_t; + +XAUDIOC_API xaudio2_t* xaudio2_new(unsigned samplerate, unsigned channels, unsigned bits, size_t bufsize); +XAUDIOC_API size_t xaudio2_write_avail(xaudio2_t *handle); +XAUDIOC_API size_t xaudio2_write(xaudio2_t *handle, const void *data, size_t bytes); +XAUDIOC_API void xaudio2_free(xaudio2_t *handle); + +typedef xaudio2_t* (*xaudio2_new_t)(unsigned, unsigned, unsigned, size_t); +typedef size_t (*xaudio2_write_avail_t)(xaudio2_t*); +typedef size_t (*xaudio2_write_t)(xaudio2_t*, const void*, size_t); +typedef void (*xaudio2_free_t)(xaudio2_t*); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/audio/xaudio.c b/audio/xaudio.c new file mode 100644 index 0000000000..e705441b64 --- /dev/null +++ b/audio/xaudio.c @@ -0,0 +1,158 @@ +/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010-2011 - Hans-Kristian Arntzen + * + * Some code herein may be based on code found in BSNES. + * + * SSNES is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with SSNES. + * If not, see . + */ + + +#include "driver.h" +#include +#include "xaudio-c.h" + +#define WIN32_LEAN_AND_MEAN +#include + +typedef struct +{ + xaudio2_t *xa; + bool nonblock; +} xa_t; + +static xaudio2_new_t pxanew = NULL; +static xaudio2_write_t pxawrite = NULL; +static xaudio2_write_avail_t pxawrite_avail = NULL; +static xaudio2_free_t pxafree = NULL; +static HMODULE lib = NULL; + +#define SYM(X) ((void*)GetProcAddress(lib, "xaudio2_" #X)) + +static void deinit_lib(void) +{ + FreeModule(lib); + lib = NULL; +} + +static bool init_lib(void) +{ + if (lib) + return true; + + lib = LoadLibrary("xaudio-c.dll"); + if (!lib) + return false; + + pxanew = SYM(new); + pxawrite = SYM(write); + pxawrite_avail = SYM(write_avail); + pxafree = SYM(free); + + if (!pxanew || !pxawrite || !pxawrite_avail || !pxafree) + { + deinit_lib(); + return false; + } + return true; +} + +// Interesting hack from http://www-graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 +static inline uint32_t next_pow2(uint32_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +static void* __xa_init(const char* device, int rate, int latency) +{ + if (!init_lib()) + return NULL; + + xa_t *xa = calloc(1, sizeof(xa_t)); + if (xa == NULL) + return NULL; + + size_t bufsize = latency * 4 * rate / 1000; + bufsize = next_pow2(bufsize); + + xa->xa = pxanew(rate, 2, 16, bufsize); + if (!xa->xa) + { + free(xa); + return NULL; + } + return xa; +} + +static ssize_t __xa_write(void* data, const void* buf, size_t size) +{ + xa_t *xa = data; + if (xa->nonblock) + { + size_t avail = pxawrite_avail(xa->xa); + if (avail < size) + size = avail; + } + return pxawrite(xa->xa, buf, size); +} + +static bool __xa_stop(void *data) +{ + (void)data; + return true; +} + +static void __xa_set_nonblock_state(void *data, bool state) +{ + xa_t *xa = data; + xa->nonblock = state; +} + +static bool __xa_start(void *data) +{ + (void)data; + return true; +} + +static void __xa_free(void *data) +{ + xa_t *xa = data; + if (xa && xa->xa) + { + if (pxafree) + pxafree(xa->xa); + free(xa); + } + deinit_lib(); +} + +const audio_driver_t audio_xa = { + .init = __xa_init, + .write = __xa_write, + .stop = __xa_stop, + .start = __xa_start, + .set_nonblock_state = __xa_set_nonblock_state, + .free = __xa_free, + .ident = "xaudio" +}; + + + + + + diff --git a/config.def.h b/config.def.h index d0aa845668..2b51545d50 100644 --- a/config.def.h +++ b/config.def.h @@ -53,6 +53,7 @@ #define AUDIO_AL 5 #define AUDIO_JACK 6 #define AUDIO_SDL 8 +#define AUDIO_XAUDIO 9 //////////////////////// #define INPUT_SDL 7 //////////////////////// @@ -69,6 +70,8 @@ #define AUDIO_DEFAULT_DRIVER AUDIO_RSOUND #elif HAVE_ROAR #define AUDIO_DEFAULT_DRIVER AUDIO_ROAR +#elif HAVE_XAUDIO +#define AUDIO_DEFAULT_DRIVER AUDIO_XAUDIO #elif HAVE_AL #define AUDIO_DEFAULT_DRIVER AUDIO_AL #elif HAVE_SDL diff --git a/driver.c b/driver.c index db651631ae..4c621e1942 100644 --- a/driver.c +++ b/driver.c @@ -49,6 +49,9 @@ static const audio_driver_t *audio_drivers[] = { #ifdef HAVE_SDL &audio_sdl, #endif +#ifdef HAVE_XAUDIO + &audio_xa, +#endif }; static const video_driver_t *video_drivers[] = { @@ -268,27 +271,3 @@ void uninit_video_input(void) driver_t driver; -#if 0 -driver_t driver = { -#if VIDEO_DRIVER == VIDEO_GL - .video = &video_gl, -#else -#error "Define a valid video driver in config.h" -#endif - -#if AUDIO_DRIVER == AUDIO_ALSA - .audio = &audio_alsa, -#elif AUDIO_DRIVER == AUDIO_RSOUND - .audio = &audio_rsound, -#elif AUDIO_DRIVER == AUDIO_OSS - .audio = &audio_oss, -#elif AUDIO_DRIVER == AUDIO_ROAR - .audio = &audio_roar, -#elif AUDIO_DRIVER == AUDIO_AL - .audio = &audio_openal, -#else -#error "Define a valid audio driver in config.h" -#endif -}; -#endif - diff --git a/driver.h b/driver.h index dd92eb8a52..c5792dd26e 100644 --- a/driver.h +++ b/driver.h @@ -138,6 +138,7 @@ extern const audio_driver_t audio_roar; extern const audio_driver_t audio_openal; extern const audio_driver_t audio_jack; extern const audio_driver_t audio_sdl; +extern const audio_driver_t audio_xa; extern const video_driver_t video_gl; extern const input_driver_t input_sdl; //////////////////////////////////////////////// diff --git a/settings.c b/settings.c index 2ccdba3fbf..4ddaabca52 100644 --- a/settings.c +++ b/settings.c @@ -69,6 +69,9 @@ static void set_defaults(void) case AUDIO_SDL: def_audio = "sdl"; break; + case AUDIO_XAUDIO: + def_audio = "xaudio"; + break; default: break; }