2 Simple DirectMedia Layer
3 Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
21 #include "SDL_config.h"
23 #if SDL_AUDIO_DRIVER_XAUDIO2
25 #include "../../core/windows/SDL_windows.h"
26 #include "SDL_audio.h"
27 #include "../SDL_audio_c.h"
28 #include "../SDL_sysaudio.h"
29 #include "SDL_assert.h"
31 #include <dxsdkver.h> /* XAudio2 exists as of the March 2008 DirectX SDK */
32 #if (!defined(_DXSDK_BUILD_MAJOR) || (_DXSDK_BUILD_MAJOR < 1284))
33 # warning Your DirectX SDK is too old. Disabling XAudio2 support.
35 # define SDL_XAUDIO2_HAS_SDK 1
38 #ifdef SDL_XAUDIO2_HAS_SDK
43 /* Hidden "this" pointer for the audio functions */
44 #define _THIS SDL_AudioDevice *this
46 struct SDL_PrivateAudioData
49 IXAudio2SourceVoice *source;
50 IXAudio2MasteringVoice *mastering;
58 static __inline__ char *
59 utf16_to_utf8(const WCHAR *S)
61 /* !!! FIXME: this should be UTF-16, not UCS-2! */
62 return SDL_iconv_string("UTF-8", "UCS-2", (char *)(S),
63 (SDL_wcslen(S)+1)*sizeof(WCHAR));
67 XAUDIO2_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
69 IXAudio2 *ixa2 = NULL;
75 SDL_SetError("XAudio2: capture devices unsupported.");
77 } else if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) {
78 SDL_SetError("XAudio2: XAudio2Create() failed.");
80 } else if (IXAudio2_GetDeviceCount(ixa2, &devcount) != S_OK) {
81 SDL_SetError("XAudio2: IXAudio2::GetDeviceCount() failed.");
82 IXAudio2_Release(ixa2);
86 for (i = 0; i < devcount; i++) {
87 XAUDIO2_DEVICE_DETAILS details;
88 if (IXAudio2_GetDeviceDetails(ixa2, i, &details) == S_OK) {
89 char *str = utf16_to_utf8(details.DisplayName);
92 SDL_free(str); /* addfn() made a copy of the string. */
97 IXAudio2_Release(ixa2);
100 static void STDMETHODCALLTYPE
101 VoiceCBOnBufferEnd(THIS_ void *data)
103 /* Just signal the SDL audio thread and get out of XAudio2's way. */
104 SDL_AudioDevice *this = (SDL_AudioDevice *) data;
105 ReleaseSemaphore(this->hidden->semaphore, 1, NULL);
108 static void STDMETHODCALLTYPE
109 VoiceCBOnVoiceError(THIS_ void *data, HRESULT Error)
111 /* !!! FIXME: attempt to recover, or mark device disconnected. */
112 SDL_assert(0 && "write me!");
115 /* no-op callbacks... */
116 static void STDMETHODCALLTYPE VoiceCBOnStreamEnd(THIS) {}
117 static void STDMETHODCALLTYPE VoiceCBOnVoiceProcessPassStart(THIS_ UINT32 b) {}
118 static void STDMETHODCALLTYPE VoiceCBOnVoiceProcessPassEnd(THIS) {}
119 static void STDMETHODCALLTYPE VoiceCBOnBufferStart(THIS_ void *data) {}
120 static void STDMETHODCALLTYPE VoiceCBOnLoopEnd(THIS_ void *data) {}
124 XAUDIO2_GetDeviceBuf(_THIS)
126 return this->hidden->nextbuf;
130 XAUDIO2_PlayDevice(_THIS)
132 XAUDIO2_BUFFER buffer;
133 Uint8 *mixbuf = this->hidden->mixbuf;
134 Uint8 *nextbuf = this->hidden->nextbuf;
135 const int mixlen = this->hidden->mixlen;
136 IXAudio2SourceVoice *source = this->hidden->source;
137 HRESULT result = S_OK;
139 if (!this->enabled) { /* shutting down? */
143 /* Submit the next filled buffer */
145 buffer.AudioBytes = mixlen;
146 buffer.pAudioData = nextbuf;
147 buffer.pContext = this;
149 if (nextbuf == mixbuf) {
154 this->hidden->nextbuf = nextbuf;
156 result = IXAudio2SourceVoice_SubmitSourceBuffer(source, &buffer, NULL);
157 if (result == XAUDIO2_E_DEVICE_INVALIDATED) {
158 /* !!! FIXME: possibly disconnected or temporary lost. Recover? */
161 if (result != S_OK) { /* uhoh, panic! */
162 IXAudio2SourceVoice_FlushSourceBuffers(source);
168 XAUDIO2_WaitDevice(_THIS)
171 WaitForSingleObject(this->hidden->semaphore, INFINITE);
176 XAUDIO2_WaitDone(_THIS)
178 IXAudio2SourceVoice *source = this->hidden->source;
179 XAUDIO2_VOICE_STATE state;
180 SDL_assert(!this->enabled); /* flag that stops playing. */
181 IXAudio2SourceVoice_Discontinuity(source);
182 IXAudio2SourceVoice_GetState(source, &state);
183 while (state.BuffersQueued > 0) {
184 WaitForSingleObject(this->hidden->semaphore, INFINITE);
185 IXAudio2SourceVoice_GetState(source, &state);
191 XAUDIO2_CloseDevice(_THIS)
193 if (this->hidden != NULL) {
194 IXAudio2 *ixa2 = this->hidden->ixa2;
195 IXAudio2SourceVoice *source = this->hidden->source;
196 IXAudio2MasteringVoice *mastering = this->hidden->mastering;
198 if (source != NULL) {
199 IXAudio2SourceVoice_Stop(source, 0, XAUDIO2_COMMIT_NOW);
200 IXAudio2SourceVoice_FlushSourceBuffers(source);
201 IXAudio2SourceVoice_DestroyVoice(source);
204 IXAudio2_StopEngine(ixa2);
206 if (mastering != NULL) {
207 IXAudio2MasteringVoice_DestroyVoice(mastering);
210 IXAudio2_Release(ixa2);
212 if (this->hidden->mixbuf != NULL) {
213 SDL_free(this->hidden->mixbuf);
215 if (this->hidden->semaphore != NULL) {
216 CloseHandle(this->hidden->semaphore);
219 SDL_free(this->hidden);
225 XAUDIO2_OpenDevice(_THIS, const char *devname, int iscapture)
227 HRESULT result = S_OK;
228 WAVEFORMATEX waveformat;
229 int valid_format = 0;
230 SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
231 IXAudio2 *ixa2 = NULL;
232 IXAudio2SourceVoice *source = NULL;
233 UINT32 devId = 0; /* 0 == system default device. */
235 static IXAudio2VoiceCallbackVtbl callbacks_vtable = {
236 VoiceCBOnVoiceProcessPassStart,
237 VoiceCBOnVoiceProcessPassEnd,
239 VoiceCBOnBufferStart,
245 static IXAudio2VoiceCallback callbacks = { &callbacks_vtable };
248 SDL_SetError("XAudio2: capture devices unsupported.");
250 } else if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) {
251 SDL_SetError("XAudio2: XAudio2Create() failed.");
255 if (devname != NULL) {
259 if (IXAudio2_GetDeviceCount(ixa2, &devcount) != S_OK) {
260 IXAudio2_Release(ixa2);
261 SDL_SetError("XAudio2: IXAudio2_GetDeviceCount() failed.");
264 for (i = 0; i < devcount; i++) {
265 XAUDIO2_DEVICE_DETAILS details;
266 if (IXAudio2_GetDeviceDetails(ixa2, i, &details) == S_OK) {
267 char *str = utf16_to_utf8(details.DisplayName);
269 const int match = (SDL_strcmp(str, devname) == 0);
280 IXAudio2_Release(ixa2);
281 SDL_SetError("XAudio2: Requested device not found.");
286 /* Initialize all variables that we clean on shutdown */
287 this->hidden = (struct SDL_PrivateAudioData *)
288 SDL_malloc((sizeof *this->hidden));
289 if (this->hidden == NULL) {
290 IXAudio2_Release(ixa2);
294 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
296 this->hidden->ixa2 = ixa2;
297 this->hidden->semaphore = CreateSemaphore(NULL, 1, 2, NULL);
298 if (this->hidden->semaphore == NULL) {
299 XAUDIO2_CloseDevice(this);
300 SDL_SetError("XAudio2: CreateSemaphore() failed!");
304 while ((!valid_format) && (test_format)) {
305 switch (test_format) {
310 this->spec.format = test_format;
314 test_format = SDL_NextAudioFormat();
318 XAUDIO2_CloseDevice(this);
319 SDL_SetError("XAudio2: Unsupported audio format");
323 /* Update the fragment size as size in bytes */
324 SDL_CalculateAudioSpec(&this->spec);
326 /* We feed a Source, it feeds the Mastering, which feeds the device. */
327 this->hidden->mixlen = this->spec.size;
328 this->hidden->mixbuf = (Uint8 *) SDL_malloc(2 * this->hidden->mixlen);
329 if (this->hidden->mixbuf == NULL) {
330 XAUDIO2_CloseDevice(this);
334 this->hidden->nextbuf = this->hidden->mixbuf;
335 SDL_memset(this->hidden->mixbuf, '\0', 2 * this->hidden->mixlen);
337 /* We use XAUDIO2_DEFAULT_CHANNELS instead of this->spec.channels. On
338 Xbox360, this means 5.1 output, but on Windows, it means "figure out
339 what the system has." It might be preferable to let XAudio2 blast
340 stereo output to appropriate surround sound configurations
341 instead of clamping to 2 channels, even though we'll configure the
342 Source Voice for whatever number of channels you supply. */
343 result = IXAudio2_CreateMasteringVoice(ixa2, &this->hidden->mastering,
344 XAUDIO2_DEFAULT_CHANNELS,
345 this->spec.freq, 0, devId, NULL);
346 if (result != S_OK) {
347 XAUDIO2_CloseDevice(this);
348 SDL_SetError("XAudio2: Couldn't create mastering voice");
352 SDL_zero(waveformat);
353 if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
354 waveformat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
356 waveformat.wFormatTag = WAVE_FORMAT_PCM;
358 waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
359 waveformat.nChannels = this->spec.channels;
360 waveformat.nSamplesPerSec = this->spec.freq;
361 waveformat.nBlockAlign =
362 waveformat.nChannels * (waveformat.wBitsPerSample / 8);
363 waveformat.nAvgBytesPerSec =
364 waveformat.nSamplesPerSec * waveformat.nBlockAlign;
366 result = IXAudio2_CreateSourceVoice(ixa2, &source, &waveformat,
367 XAUDIO2_VOICE_NOSRC |
368 XAUDIO2_VOICE_NOPITCH,
369 1.0f, &callbacks, NULL, NULL);
370 if (result != S_OK) {
371 XAUDIO2_CloseDevice(this);
372 SDL_SetError("XAudio2: Couldn't create source voice");
375 this->hidden->source = source;
377 /* Start everything playing! */
378 result = IXAudio2_StartEngine(ixa2);
379 if (result != S_OK) {
380 XAUDIO2_CloseDevice(this);
381 SDL_SetError("XAudio2: Couldn't start engine");
385 result = IXAudio2SourceVoice_Start(source, 0, XAUDIO2_COMMIT_NOW);
386 if (result != S_OK) {
387 XAUDIO2_CloseDevice(this);
388 SDL_SetError("XAudio2: Couldn't start source voice");
392 return 1; /* good to go. */
396 XAUDIO2_Deinitialize(void)
398 WIN_CoUninitialize();
401 #endif /* SDL_XAUDIO2_HAS_SDK */
405 XAUDIO2_Init(SDL_AudioDriverImpl * impl)
407 #ifndef SDL_XAUDIO2_HAS_SDK
408 SDL_SetError("XAudio2: SDL was built without XAudio2 support (old DirectX SDK).");
409 return 0; /* no XAudio2 support, ever. Update your SDK! */
411 /* XAudio2Create() is a macro that uses COM; we don't load the .dll */
412 IXAudio2 *ixa2 = NULL;
413 if (FAILED(WIN_CoInitialize())) {
414 SDL_SetError("XAudio2: CoInitialize() failed");
418 if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) {
419 WIN_CoUninitialize();
420 SDL_SetError("XAudio2: XAudio2Create() failed");
421 return 0; /* not available. */
423 IXAudio2_Release(ixa2);
425 /* Set the function pointers */
426 impl->DetectDevices = XAUDIO2_DetectDevices;
427 impl->OpenDevice = XAUDIO2_OpenDevice;
428 impl->PlayDevice = XAUDIO2_PlayDevice;
429 impl->WaitDevice = XAUDIO2_WaitDevice;
430 impl->WaitDone = XAUDIO2_WaitDone;
431 impl->GetDeviceBuf = XAUDIO2_GetDeviceBuf;
432 impl->CloseDevice = XAUDIO2_CloseDevice;
433 impl->Deinitialize = XAUDIO2_Deinitialize;
435 return 1; /* this audio target is available. */
439 AudioBootStrap XAUDIO2_bootstrap = {
440 "xaudio2", "XAudio2", XAUDIO2_Init, 0
443 #endif /* SDL_AUDIO_DRIVER_XAUDIO2 */
445 /* vi: set ts=4 sw=4 expandtab: */