Cleaned up audio device detection. Cleared out a lot of cut-and-paste.
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2011 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"
22 #include "../../core/windows/SDL_windows.h"
23 #include "SDL_audio.h"
24 #include "../SDL_audio_c.h"
25 #include "SDL_assert.h"
28 #include "SDL_xaudio2.h"
30 static __inline__ char *
31 utf16_to_utf8(const WCHAR *S)
33 /* !!! FIXME: this should be UTF-16, not UCS-2! */
34 return SDL_iconv_string("UTF-8", "UCS-2", (char *)(S),
35 (SDL_wcslen(S)+1)*sizeof(WCHAR));
39 XAUDIO2_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
41 IXAudio2 *ixa2 = NULL;
47 SDL_SetError("XAudio2: capture devices unsupported.");
49 } else if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) {
50 SDL_SetError("XAudio2: XAudio2Create() failed.");
52 } else if (IXAudio2_GetDeviceCount(ixa2, &devcount) != S_OK) {
53 SDL_SetError("XAudio2: IXAudio2::GetDeviceCount() failed.");
54 IXAudio2_Release(ixa2);
58 for (i = 0; i < devcount; i++) {
59 XAUDIO2_DEVICE_DETAILS details;
60 if (IXAudio2_GetDeviceDetails(ixa2, i, &details) == S_OK) {
61 char *str = utf16_to_utf8(details.DisplayName);
64 SDL_free(str); /* addfn() made a copy of the string. */
69 IXAudio2_Release(ixa2);
72 static void STDMETHODCALLTYPE
73 VoiceCBOnBufferEnd(THIS_ void *data)
75 /* Just signal the SDL audio thread and get out of XAudio2's way. */
76 SDL_AudioDevice *this = (SDL_AudioDevice *) data;
77 ReleaseSemaphore(this->hidden->semaphore, 1, NULL);
80 static void STDMETHODCALLTYPE
81 VoiceCBOnVoiceError(THIS_ void *data, HRESULT Error)
83 /* !!! FIXME: attempt to recover, or mark device disconnected. */
84 SDL_assert(0 && "write me!");
87 /* no-op callbacks... */
88 static void STDMETHODCALLTYPE VoiceCBOnStreamEnd(THIS) {}
89 static void STDMETHODCALLTYPE VoiceCBOnVoiceProcessPassStart(THIS_ UINT32 b) {}
90 static void STDMETHODCALLTYPE VoiceCBOnVoiceProcessPassEnd(THIS) {}
91 static void STDMETHODCALLTYPE VoiceCBOnBufferStart(THIS_ void *data) {}
92 static void STDMETHODCALLTYPE VoiceCBOnLoopEnd(THIS_ void *data) {}
96 XAUDIO2_GetDeviceBuf(_THIS)
98 return this->hidden->nextbuf;
102 XAUDIO2_PlayDevice(_THIS)
104 XAUDIO2_BUFFER buffer;
105 Uint8 *mixbuf = this->hidden->mixbuf;
106 Uint8 *nextbuf = this->hidden->nextbuf;
107 const int mixlen = this->hidden->mixlen;
108 IXAudio2SourceVoice *source = this->hidden->source;
109 HRESULT result = S_OK;
111 if (!this->enabled) { /* shutting down? */
115 /* Submit the next filled buffer */
117 buffer.AudioBytes = mixlen;
118 buffer.pAudioData = nextbuf;
119 buffer.pContext = this;
121 if (nextbuf == mixbuf) {
126 this->hidden->nextbuf = nextbuf;
128 result = IXAudio2SourceVoice_SubmitSourceBuffer(source, &buffer, NULL);
129 if (result == XAUDIO2_E_DEVICE_INVALIDATED) {
130 /* !!! FIXME: possibly disconnected or temporary lost. Recover? */
133 if (result != S_OK) { /* uhoh, panic! */
134 IXAudio2SourceVoice_FlushSourceBuffers(source);
140 XAUDIO2_WaitDevice(_THIS)
143 WaitForSingleObject(this->hidden->semaphore, INFINITE);
148 XAUDIO2_WaitDone(_THIS)
150 IXAudio2SourceVoice *source = this->hidden->source;
151 XAUDIO2_VOICE_STATE state;
152 SDL_assert(!this->enabled); /* flag that stops playing. */
153 IXAudio2SourceVoice_Discontinuity(source);
154 IXAudio2SourceVoice_GetState(source, &state);
155 while (state.BuffersQueued > 0) {
156 WaitForSingleObject(this->hidden->semaphore, INFINITE);
157 IXAudio2SourceVoice_GetState(source, &state);
163 XAUDIO2_CloseDevice(_THIS)
165 if (this->hidden != NULL) {
166 IXAudio2 *ixa2 = this->hidden->ixa2;
167 IXAudio2SourceVoice *source = this->hidden->source;
168 IXAudio2MasteringVoice *mastering = this->hidden->mastering;
170 if (source != NULL) {
171 IXAudio2SourceVoice_Stop(source, 0, XAUDIO2_COMMIT_NOW);
172 IXAudio2SourceVoice_FlushSourceBuffers(source);
173 IXAudio2SourceVoice_DestroyVoice(source);
176 IXAudio2_StopEngine(ixa2);
178 if (mastering != NULL) {
179 IXAudio2MasteringVoice_DestroyVoice(mastering);
182 IXAudio2_Release(ixa2);
184 if (this->hidden->mixbuf != NULL) {
185 SDL_free(this->hidden->mixbuf);
187 if (this->hidden->semaphore != NULL) {
188 CloseHandle(this->hidden->semaphore);
191 SDL_free(this->hidden);
197 XAUDIO2_OpenDevice(_THIS, const char *devname, int iscapture)
199 HRESULT result = S_OK;
200 WAVEFORMATEX waveformat;
201 int valid_format = 0;
202 SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
203 IXAudio2 *ixa2 = NULL;
204 IXAudio2SourceVoice *source = NULL;
205 UINT32 devId = 0; /* 0 == system default device. */
207 static IXAudio2VoiceCallbackVtbl callbacks_vtable = {
208 VoiceCBOnVoiceProcessPassStart,
209 VoiceCBOnVoiceProcessPassEnd,
211 VoiceCBOnBufferStart,
217 static IXAudio2VoiceCallback callbacks = { &callbacks_vtable };
220 SDL_SetError("XAudio2: capture devices unsupported.");
222 } else if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) {
223 SDL_SetError("XAudio2: XAudio2Create() failed.");
227 if (devname != NULL) {
231 if (IXAudio2_GetDeviceCount(ixa2, &devcount) != S_OK) {
232 IXAudio2_Release(ixa2);
233 SDL_SetError("XAudio2: IXAudio2_GetDeviceCount() failed.");
236 for (i = 0; i < devcount; i++) {
237 XAUDIO2_DEVICE_DETAILS details;
238 if (IXAudio2_GetDeviceDetails(ixa2, i, &details) == S_OK) {
239 char *str = utf16_to_utf8(details.DisplayName);
241 const int match = (SDL_strcmp(str, devname) == 0);
252 IXAudio2_Release(ixa2);
253 SDL_SetError("XAudio2: Requested device not found.");
258 /* Initialize all variables that we clean on shutdown */
259 this->hidden = (struct SDL_PrivateAudioData *)
260 SDL_malloc((sizeof *this->hidden));
261 if (this->hidden == NULL) {
262 IXAudio2_Release(ixa2);
266 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
268 this->hidden->ixa2 = ixa2;
269 this->hidden->semaphore = CreateSemaphore(NULL, 1, 2, NULL);
270 if (this->hidden->semaphore == NULL) {
271 XAUDIO2_CloseDevice(this);
272 SDL_SetError("XAudio2: CreateSemaphore() failed!");
276 while ((!valid_format) && (test_format)) {
277 switch (test_format) {
282 this->spec.format = test_format;
286 test_format = SDL_NextAudioFormat();
290 XAUDIO2_CloseDevice(this);
291 SDL_SetError("XAudio2: Unsupported audio format");
295 /* Update the fragment size as size in bytes */
296 SDL_CalculateAudioSpec(&this->spec);
298 /* We feed a Source, it feeds the Mastering, which feeds the device. */
299 this->hidden->mixlen = this->spec.size;
300 this->hidden->mixbuf = (Uint8 *) SDL_malloc(2 * this->hidden->mixlen);
301 if (this->hidden->mixbuf == NULL) {
302 XAUDIO2_CloseDevice(this);
306 this->hidden->nextbuf = this->hidden->mixbuf;
307 SDL_memset(this->hidden->mixbuf, '\0', 2 * this->hidden->mixlen);
309 /* We use XAUDIO2_DEFAULT_CHANNELS instead of this->spec.channels. On
310 Xbox360, this means 5.1 output, but on Windows, it means "figure out
311 what the system has." It might be preferable to let XAudio2 blast
312 stereo output to appropriate surround sound configurations
313 instead of clamping to 2 channels, even though we'll configure the
314 Source Voice for whatever number of channels you supply. */
315 result = IXAudio2_CreateMasteringVoice(ixa2, &this->hidden->mastering,
316 XAUDIO2_DEFAULT_CHANNELS,
317 this->spec.freq, 0, devId, NULL);
318 if (result != S_OK) {
319 XAUDIO2_CloseDevice(this);
320 SDL_SetError("XAudio2: Couldn't create mastering voice");
324 SDL_zero(waveformat);
325 if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
326 waveformat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
328 waveformat.wFormatTag = WAVE_FORMAT_PCM;
330 waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
331 waveformat.nChannels = this->spec.channels;
332 waveformat.nSamplesPerSec = this->spec.freq;
333 waveformat.nBlockAlign =
334 waveformat.nChannels * (waveformat.wBitsPerSample / 8);
335 waveformat.nAvgBytesPerSec =
336 waveformat.nSamplesPerSec * waveformat.nBlockAlign;
338 result = IXAudio2_CreateSourceVoice(ixa2, &source, &waveformat,
339 XAUDIO2_VOICE_NOSRC |
340 XAUDIO2_VOICE_NOPITCH,
341 1.0f, &callbacks, NULL, NULL);
342 if (result != S_OK) {
343 XAUDIO2_CloseDevice(this);
344 SDL_SetError("XAudio2: Couldn't create source voice");
347 this->hidden->source = source;
349 /* Start everything playing! */
350 result = IXAudio2_StartEngine(ixa2);
351 if (result != S_OK) {
352 XAUDIO2_CloseDevice(this);
353 SDL_SetError("XAudio2: Couldn't start engine");
357 result = IXAudio2SourceVoice_Start(source, 0, XAUDIO2_COMMIT_NOW);
358 if (result != S_OK) {
359 XAUDIO2_CloseDevice(this);
360 SDL_SetError("XAudio2: Couldn't start source voice");
364 return 1; /* good to go. */
368 XAUDIO2_Deinitialize(void)
370 WIN_CoUninitialize();
374 XAUDIO2_Init(SDL_AudioDriverImpl * impl)
376 /* XAudio2Create() is a macro that uses COM; we don't load the .dll */
377 IXAudio2 *ixa2 = NULL;
378 if (FAILED(WIN_CoInitialize())) {
379 SDL_SetError("XAudio2: CoInitialize() failed");
383 if (XAudio2Create(&ixa2, 0, XAUDIO2_DEFAULT_PROCESSOR) != S_OK) {
384 WIN_CoUninitialize();
385 SDL_SetError("XAudio2: XAudio2Create() failed");
386 return 0; /* not available. */
388 IXAudio2_Release(ixa2);
390 /* Set the function pointers */
391 impl->DetectDevices = XAUDIO2_DetectDevices;
392 impl->OpenDevice = XAUDIO2_OpenDevice;
393 impl->PlayDevice = XAUDIO2_PlayDevice;
394 impl->WaitDevice = XAUDIO2_WaitDevice;
395 impl->WaitDone = XAUDIO2_WaitDone;
396 impl->GetDeviceBuf = XAUDIO2_GetDeviceBuf;
397 impl->CloseDevice = XAUDIO2_CloseDevice;
398 impl->Deinitialize = XAUDIO2_Deinitialize;
400 return 1; /* this audio target is available. */
403 AudioBootStrap XAUDIO2_bootstrap = {
404 "xaudio2", "XAudio2", XAUDIO2_Init, 0
407 /* vi: set ts=4 sw=4 expandtab: */