summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/audio/directsound/SDL_directsound.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/audio/directsound/SDL_directsound.c
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/audio/directsound/SDL_directsound.c')
-rw-r--r--contrib/SDL-3.2.8/src/audio/directsound/SDL_directsound.c680
1 files changed, 680 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/audio/directsound/SDL_directsound.c b/contrib/SDL-3.2.8/src/audio/directsound/SDL_directsound.c
new file mode 100644
index 0000000..7b5cb11
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/audio/directsound/SDL_directsound.c
@@ -0,0 +1,680 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
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.
8
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:
12
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.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_AUDIO_DRIVER_DSOUND
24
25#include "../SDL_sysaudio.h"
26#include "SDL_directsound.h"
27#include <mmreg.h>
28#ifdef HAVE_MMDEVICEAPI_H
29#include "../../core/windows/SDL_immdevice.h"
30#endif
31
32#ifndef WAVE_FORMAT_IEEE_FLOAT
33#define WAVE_FORMAT_IEEE_FLOAT 0x0003
34#endif
35
36// For Vista+, we can enumerate DSound devices with IMMDevice
37#ifdef HAVE_MMDEVICEAPI_H
38static bool SupportsIMMDevice = false;
39#endif
40
41// DirectX function pointers for audio
42static SDL_SharedObject *DSoundDLL = NULL;
43typedef HRESULT(WINAPI *fnDirectSoundCreate8)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
44typedef HRESULT(WINAPI *fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
45typedef HRESULT(WINAPI *fnDirectSoundCaptureCreate8)(LPCGUID, LPDIRECTSOUNDCAPTURE8 *, LPUNKNOWN);
46typedef HRESULT(WINAPI *fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
47typedef HRESULT(WINAPI *fnGetDeviceID)(LPCGUID, LPGUID);
48static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL;
49static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL;
50static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = NULL;
51static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = NULL;
52static fnGetDeviceID pGetDeviceID = NULL;
53
54#include <initguid.h>
55DEFINE_GUID(SDL_DSDEVID_DefaultPlayback, 0xdef00000, 0x9c6d, 0x47ed, 0xaa, 0xf1, 0x4d, 0xda, 0x8f, 0x2b, 0x5c, 0x03);
56DEFINE_GUID(SDL_DSDEVID_DefaultCapture, 0xdef00001, 0x9c6d, 0x47ed, 0xaa, 0xf1, 0x4d, 0xda, 0x8f, 0x2b, 0x5c, 0x03);
57
58static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
59static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
60
61static void DSOUND_Unload(void)
62{
63 pDirectSoundCreate8 = NULL;
64 pDirectSoundEnumerateW = NULL;
65 pDirectSoundCaptureCreate8 = NULL;
66 pDirectSoundCaptureEnumerateW = NULL;
67 pGetDeviceID = NULL;
68
69 if (DSoundDLL) {
70 SDL_UnloadObject(DSoundDLL);
71 DSoundDLL = NULL;
72 }
73}
74
75static bool DSOUND_Load(void)
76{
77 bool loaded = false;
78
79 DSOUND_Unload();
80
81 DSoundDLL = SDL_LoadObject("DSOUND.DLL");
82 if (!DSoundDLL) {
83 SDL_SetError("DirectSound: failed to load DSOUND.DLL");
84 } else {
85// Now make sure we have DirectX 8 or better...
86#define DSOUNDLOAD(f) \
87 { \
88 p##f = (fn##f)SDL_LoadFunction(DSoundDLL, #f); \
89 if (!p##f) \
90 loaded = false; \
91 }
92 loaded = true; // will reset if necessary.
93 DSOUNDLOAD(DirectSoundCreate8);
94 DSOUNDLOAD(DirectSoundEnumerateW);
95 DSOUNDLOAD(DirectSoundCaptureCreate8);
96 DSOUNDLOAD(DirectSoundCaptureEnumerateW);
97 DSOUNDLOAD(GetDeviceID);
98#undef DSOUNDLOAD
99
100 if (!loaded) {
101 SDL_SetError("DirectSound: System doesn't appear to have DX8.");
102 }
103 }
104
105 if (!loaded) {
106 DSOUND_Unload();
107 }
108
109 return loaded;
110}
111
112static bool SetDSerror(const char *function, int code)
113{
114 const char *error;
115
116 switch (code) {
117 case E_NOINTERFACE:
118 error = "Unsupported interface -- Is DirectX 8.0 or later installed?";
119 break;
120 case DSERR_ALLOCATED:
121 error = "Audio device in use";
122 break;
123 case DSERR_BADFORMAT:
124 error = "Unsupported audio format";
125 break;
126 case DSERR_BUFFERLOST:
127 error = "Mixing buffer was lost";
128 break;
129 case DSERR_CONTROLUNAVAIL:
130 error = "Control requested is not available";
131 break;
132 case DSERR_INVALIDCALL:
133 error = "Invalid call for the current state";
134 break;
135 case DSERR_INVALIDPARAM:
136 error = "Invalid parameter";
137 break;
138 case DSERR_NODRIVER:
139 error = "No audio device found";
140 break;
141 case DSERR_OUTOFMEMORY:
142 error = "Out of memory";
143 break;
144 case DSERR_PRIOLEVELNEEDED:
145 error = "Caller doesn't have priority";
146 break;
147 case DSERR_UNSUPPORTED:
148 error = "Function not supported";
149 break;
150 default:
151 error = "Unknown DirectSound error";
152 break;
153 }
154
155 return SDL_SetError("%s: %s (0x%x)", function, error, code);
156}
157
158static void DSOUND_FreeDeviceHandle(SDL_AudioDevice *device)
159{
160#ifdef HAVE_MMDEVICEAPI_H
161 if (SupportsIMMDevice) {
162 SDL_IMMDevice_FreeDeviceHandle(device);
163 } else
164#endif
165 {
166 SDL_free(device->handle);
167 }
168}
169
170// FindAllDevs is presumably only used on WinXP; Vista and later can use IMMDevice for better results.
171typedef struct FindAllDevsData
172{
173 bool recording;
174 SDL_AudioDevice **default_device;
175 LPCGUID default_device_guid;
176} FindAllDevsData;
177
178static BOOL CALLBACK FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID userdata)
179{
180 FindAllDevsData *data = (FindAllDevsData *) userdata;
181 if (guid != NULL) { // skip default device
182 char *str = WIN_LookupAudioDeviceName(desc, guid);
183 if (str) {
184 LPGUID cpyguid = (LPGUID)SDL_malloc(sizeof(GUID));
185 if (cpyguid) {
186 SDL_copyp(cpyguid, guid);
187
188 /* Note that spec is NULL, because we are required to connect to the
189 * device before getting the channel mask and output format, making
190 * this information inaccessible at enumeration time
191 */
192 SDL_AudioDevice *device = SDL_AddAudioDevice(data->recording, str, NULL, cpyguid);
193 if (device && data->default_device && data->default_device_guid) {
194 if (SDL_memcmp(cpyguid, data->default_device_guid, sizeof (GUID)) == 0) {
195 *data->default_device = device;
196 }
197 }
198 }
199 SDL_free(str); // SDL_AddAudioDevice() makes a copy of this string.
200 }
201 }
202 return TRUE; // keep enumerating.
203}
204
205static void DSOUND_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording)
206{
207#ifdef HAVE_MMDEVICEAPI_H
208 if (SupportsIMMDevice) {
209 SDL_IMMDevice_EnumerateEndpoints(default_playback, default_recording);
210 } else
211#endif
212 {
213 // Without IMMDevice, you can enumerate devices and figure out the default devices,
214 // but you won't get device hotplug or default device change notifications. But this is
215 // only for WinXP; Windows Vista and later should be using IMMDevice.
216 FindAllDevsData data;
217 GUID guid;
218
219 data.recording = true;
220 data.default_device = default_recording;
221 data.default_device_guid = (pGetDeviceID(&SDL_DSDEVID_DefaultCapture, &guid) == DS_OK) ? &guid : NULL;
222 pDirectSoundCaptureEnumerateW(FindAllDevs, &data);
223
224 data.recording = false;
225 data.default_device = default_playback;
226 data.default_device_guid = (pGetDeviceID(&SDL_DSDEVID_DefaultPlayback, &guid) == DS_OK) ? &guid : NULL;
227 pDirectSoundEnumerateW(FindAllDevs, &data);
228 }
229
230}
231
232static bool DSOUND_WaitDevice(SDL_AudioDevice *device)
233{
234 /* Semi-busy wait, since we have no way of getting play notification
235 on a primary mixing buffer located in hardware (DirectX 5.0)
236 */
237 while (!SDL_GetAtomicInt(&device->shutdown)) {
238 DWORD status = 0;
239 DWORD cursor = 0;
240 DWORD junk = 0;
241 HRESULT result = DS_OK;
242
243 // Try to restore a lost sound buffer
244 IDirectSoundBuffer_GetStatus(device->hidden->mixbuf, &status);
245 if (status & DSBSTATUS_BUFFERLOST) {
246 IDirectSoundBuffer_Restore(device->hidden->mixbuf);
247 } else if (!(status & DSBSTATUS_PLAYING)) {
248 result = IDirectSoundBuffer_Play(device->hidden->mixbuf, 0, 0, DSBPLAY_LOOPING);
249 } else {
250 // Find out where we are playing
251 result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf, &junk, &cursor);
252 if ((result == DS_OK) && ((cursor / device->buffer_size) != device->hidden->lastchunk)) {
253 break; // ready for next chunk!
254 }
255 }
256
257 if ((result != DS_OK) && (result != DSERR_BUFFERLOST)) {
258 return false;
259 }
260
261 SDL_Delay(1); // not ready yet; sleep a bit.
262 }
263
264 return true;
265}
266
267static bool DSOUND_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
268{
269 // Unlock the buffer, allowing it to play
270 SDL_assert(buflen == device->buffer_size);
271 if (IDirectSoundBuffer_Unlock(device->hidden->mixbuf, (LPVOID) buffer, buflen, NULL, 0) != DS_OK) {
272 return false;
273 }
274 return true;
275}
276
277static Uint8 *DSOUND_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
278{
279 DWORD cursor = 0;
280 DWORD junk = 0;
281 HRESULT result = DS_OK;
282
283 SDL_assert(*buffer_size == device->buffer_size);
284
285 // Figure out which blocks to fill next
286 device->hidden->locked_buf = NULL;
287 result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf,
288 &junk, &cursor);
289 if (result == DSERR_BUFFERLOST) {
290 IDirectSoundBuffer_Restore(device->hidden->mixbuf);
291 result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf,
292 &junk, &cursor);
293 }
294 if (result != DS_OK) {
295 SetDSerror("DirectSound GetCurrentPosition", result);
296 return NULL;
297 }
298 cursor /= device->buffer_size;
299#ifdef DEBUG_SOUND
300 // Detect audio dropouts
301 {
302 DWORD spot = cursor;
303 if (spot < device->hidden->lastchunk) {
304 spot += device->hidden->num_buffers;
305 }
306 if (spot > device->hidden->lastchunk + 1) {
307 fprintf(stderr, "Audio dropout, missed %d fragments\n",
308 (spot - (device->hidden->lastchunk + 1)));
309 }
310 }
311#endif
312 device->hidden->lastchunk = cursor;
313 cursor = (cursor + 1) % device->hidden->num_buffers;
314 cursor *= device->buffer_size;
315
316 // Lock the audio buffer
317 DWORD rawlen = 0;
318 result = IDirectSoundBuffer_Lock(device->hidden->mixbuf, cursor,
319 device->buffer_size,
320 (LPVOID *)&device->hidden->locked_buf,
321 &rawlen, NULL, &junk, 0);
322 if (result == DSERR_BUFFERLOST) {
323 IDirectSoundBuffer_Restore(device->hidden->mixbuf);
324 result = IDirectSoundBuffer_Lock(device->hidden->mixbuf, cursor,
325 device->buffer_size,
326 (LPVOID *)&device->hidden->locked_buf, &rawlen, NULL,
327 &junk, 0);
328 }
329 if (result != DS_OK) {
330 SetDSerror("DirectSound Lock", result);
331 return NULL;
332 }
333 return device->hidden->locked_buf;
334}
335
336static bool DSOUND_WaitRecordingDevice(SDL_AudioDevice *device)
337{
338 struct SDL_PrivateAudioData *h = device->hidden;
339 while (!SDL_GetAtomicInt(&device->shutdown)) {
340 DWORD junk, cursor;
341 if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) != DS_OK) {
342 return false;
343 } else if ((cursor / device->buffer_size) != h->lastchunk) {
344 break;
345 }
346 SDL_Delay(1);
347 }
348
349 return true;
350}
351
352static int DSOUND_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen)
353{
354 struct SDL_PrivateAudioData *h = device->hidden;
355 DWORD ptr1len, ptr2len;
356 VOID *ptr1, *ptr2;
357
358 SDL_assert(buflen == device->buffer_size);
359
360 if (IDirectSoundCaptureBuffer_Lock(h->capturebuf, h->lastchunk * buflen, buflen, &ptr1, &ptr1len, &ptr2, &ptr2len, 0) != DS_OK) {
361 return -1;
362 }
363
364 SDL_assert(ptr1len == (DWORD)buflen);
365 SDL_assert(ptr2 == NULL);
366 SDL_assert(ptr2len == 0);
367
368 SDL_memcpy(buffer, ptr1, ptr1len);
369
370 if (IDirectSoundCaptureBuffer_Unlock(h->capturebuf, ptr1, ptr1len, ptr2, ptr2len) != DS_OK) {
371 return -1;
372 }
373
374 h->lastchunk = (h->lastchunk + 1) % h->num_buffers;
375
376 return (int) ptr1len;
377}
378
379static void DSOUND_FlushRecording(SDL_AudioDevice *device)
380{
381 struct SDL_PrivateAudioData *h = device->hidden;
382 DWORD junk, cursor;
383 if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) == DS_OK) {
384 h->lastchunk = cursor / device->buffer_size;
385 }
386}
387
388static void DSOUND_CloseDevice(SDL_AudioDevice *device)
389{
390 if (device->hidden) {
391 if (device->hidden->mixbuf) {
392 IDirectSoundBuffer_Stop(device->hidden->mixbuf);
393 IDirectSoundBuffer_Release(device->hidden->mixbuf);
394 }
395 if (device->hidden->sound) {
396 IDirectSound_Release(device->hidden->sound);
397 }
398 if (device->hidden->capturebuf) {
399 IDirectSoundCaptureBuffer_Stop(device->hidden->capturebuf);
400 IDirectSoundCaptureBuffer_Release(device->hidden->capturebuf);
401 }
402 if (device->hidden->capture) {
403 IDirectSoundCapture_Release(device->hidden->capture);
404 }
405 SDL_free(device->hidden);
406 device->hidden = NULL;
407 }
408}
409
410/* This function tries to create a secondary audio buffer, and returns the
411 number of audio chunks available in the created buffer. This is for
412 playback devices, not recording.
413*/
414static bool CreateSecondary(SDL_AudioDevice *device, const DWORD bufsize, WAVEFORMATEX *wfmt)
415{
416 LPDIRECTSOUND sndObj = device->hidden->sound;
417 LPDIRECTSOUNDBUFFER *sndbuf = &device->hidden->mixbuf;
418 HRESULT result = DS_OK;
419 DSBUFFERDESC format;
420 LPVOID pvAudioPtr1, pvAudioPtr2;
421 DWORD dwAudioBytes1, dwAudioBytes2;
422
423 // Try to create the secondary buffer
424 SDL_zero(format);
425 format.dwSize = sizeof(format);
426 format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
427 format.dwFlags |= DSBCAPS_GLOBALFOCUS;
428 format.dwBufferBytes = bufsize;
429 format.lpwfxFormat = wfmt;
430 result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL);
431 if (result != DS_OK) {
432 return SetDSerror("DirectSound CreateSoundBuffer", result);
433 }
434 IDirectSoundBuffer_SetFormat(*sndbuf, wfmt);
435
436 // Silence the initial audio buffer
437 result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes,
438 (LPVOID *)&pvAudioPtr1, &dwAudioBytes1,
439 (LPVOID *)&pvAudioPtr2, &dwAudioBytes2,
440 DSBLOCK_ENTIREBUFFER);
441 if (result == DS_OK) {
442 SDL_memset(pvAudioPtr1, device->silence_value, dwAudioBytes1);
443 IDirectSoundBuffer_Unlock(*sndbuf,
444 (LPVOID)pvAudioPtr1, dwAudioBytes1,
445 (LPVOID)pvAudioPtr2, dwAudioBytes2);
446 }
447
448 return true; // We're ready to go
449}
450
451/* This function tries to create a capture buffer, and returns the
452 number of audio chunks available in the created buffer. This is for
453 recording devices, not playback.
454*/
455static bool CreateCaptureBuffer(SDL_AudioDevice *device, const DWORD bufsize, WAVEFORMATEX *wfmt)
456{
457 LPDIRECTSOUNDCAPTURE capture = device->hidden->capture;
458 LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &device->hidden->capturebuf;
459 DSCBUFFERDESC format;
460 HRESULT result;
461
462 SDL_zero(format);
463 format.dwSize = sizeof(format);
464 format.dwFlags = DSCBCAPS_WAVEMAPPED;
465 format.dwBufferBytes = bufsize;
466 format.lpwfxFormat = wfmt;
467
468 result = IDirectSoundCapture_CreateCaptureBuffer(capture, &format, capturebuf, NULL);
469 if (result != DS_OK) {
470 return SetDSerror("DirectSound CreateCaptureBuffer", result);
471 }
472
473 result = IDirectSoundCaptureBuffer_Start(*capturebuf, DSCBSTART_LOOPING);
474 if (result != DS_OK) {
475 IDirectSoundCaptureBuffer_Release(*capturebuf);
476 return SetDSerror("DirectSound Start", result);
477 }
478
479#if 0
480 // presumably this starts at zero, but just in case...
481 result = IDirectSoundCaptureBuffer_GetCurrentPosition(*capturebuf, &junk, &cursor);
482 if (result != DS_OK) {
483 IDirectSoundCaptureBuffer_Stop(*capturebuf);
484 IDirectSoundCaptureBuffer_Release(*capturebuf);
485 return SetDSerror("DirectSound GetCurrentPosition", result);
486 }
487
488 device->hidden->lastchunk = cursor / device->buffer_size;
489#endif
490
491 return true;
492}
493
494static bool DSOUND_OpenDevice(SDL_AudioDevice *device)
495{
496 // Initialize all variables that we clean on shutdown
497 device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
498 if (!device->hidden) {
499 return false;
500 }
501
502 // Open the audio device
503 LPGUID guid;
504#ifdef HAVE_MMDEVICEAPI_H
505 if (SupportsIMMDevice) {
506 guid = SDL_IMMDevice_GetDirectSoundGUID(device);
507 } else
508#endif
509 {
510 guid = (LPGUID) device->handle;
511 }
512
513 SDL_assert(guid != NULL);
514
515 HRESULT result;
516 if (device->recording) {
517 result = pDirectSoundCaptureCreate8(guid, &device->hidden->capture, NULL);
518 if (result != DS_OK) {
519 return SetDSerror("DirectSoundCaptureCreate8", result);
520 }
521 } else {
522 result = pDirectSoundCreate8(guid, &device->hidden->sound, NULL);
523 if (result != DS_OK) {
524 return SetDSerror("DirectSoundCreate8", result);
525 }
526 result = IDirectSound_SetCooperativeLevel(device->hidden->sound,
527 GetDesktopWindow(),
528 DSSCL_NORMAL);
529 if (result != DS_OK) {
530 return SetDSerror("DirectSound SetCooperativeLevel", result);
531 }
532 }
533
534 const DWORD numchunks = 8;
535 DWORD bufsize;
536 bool tried_format = false;
537 SDL_AudioFormat test_format;
538 const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
539 while ((test_format = *(closefmts++)) != 0) {
540 switch (test_format) {
541 case SDL_AUDIO_U8:
542 case SDL_AUDIO_S16:
543 case SDL_AUDIO_S32:
544 case SDL_AUDIO_F32:
545 tried_format = true;
546
547 device->spec.format = test_format;
548
549 // Update the fragment size as size in bytes
550 SDL_UpdatedAudioDeviceFormat(device);
551
552 bufsize = numchunks * device->buffer_size;
553 if ((bufsize < DSBSIZE_MIN) || (bufsize > DSBSIZE_MAX)) {
554 SDL_SetError("Sound buffer size must be between %d and %d",
555 (int)((DSBSIZE_MIN < numchunks) ? 1 : DSBSIZE_MIN / numchunks),
556 (int)(DSBSIZE_MAX / numchunks));
557 } else {
558 WAVEFORMATEXTENSIBLE wfmt;
559 SDL_zero(wfmt);
560 if (device->spec.channels > 2) {
561 wfmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
562 wfmt.Format.cbSize = sizeof(wfmt) - sizeof(WAVEFORMATEX);
563
564 if (SDL_AUDIO_ISFLOAT(device->spec.format)) {
565 SDL_memcpy(&wfmt.SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID));
566 } else {
567 SDL_memcpy(&wfmt.SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID));
568 }
569 wfmt.Samples.wValidBitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
570
571 switch (device->spec.channels) {
572 case 3: // 3.0 (or 2.1)
573 wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER;
574 break;
575 case 4: // 4.0
576 wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
577 break;
578 case 5: // 5.0 (or 4.1)
579 wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
580 break;
581 case 6: // 5.1
582 wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
583 break;
584 case 7: // 6.1
585 wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_BACK_CENTER;
586 break;
587 case 8: // 7.1
588 wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT;
589 break;
590 default:
591 SDL_assert(!"Unsupported channel count!");
592 break;
593 }
594 } else if (SDL_AUDIO_ISFLOAT(device->spec.format)) {
595 wfmt.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
596 } else {
597 wfmt.Format.wFormatTag = WAVE_FORMAT_PCM;
598 }
599
600 wfmt.Format.wBitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
601 wfmt.Format.nChannels = (WORD)device->spec.channels;
602 wfmt.Format.nSamplesPerSec = device->spec.freq;
603 wfmt.Format.nBlockAlign = wfmt.Format.nChannels * (wfmt.Format.wBitsPerSample / 8);
604 wfmt.Format.nAvgBytesPerSec = wfmt.Format.nSamplesPerSec * wfmt.Format.nBlockAlign;
605
606 const bool rc = device->recording ? CreateCaptureBuffer(device, bufsize, (WAVEFORMATEX *)&wfmt) : CreateSecondary(device, bufsize, (WAVEFORMATEX *)&wfmt);
607 if (rc) {
608 device->hidden->num_buffers = numchunks;
609 break;
610 }
611 }
612 continue;
613 default:
614 continue;
615 }
616 break;
617 }
618
619 if (!test_format) {
620 if (tried_format) {
621 return false; // CreateSecondary() should have called SDL_SetError().
622 }
623 return SDL_SetError("%s: Unsupported audio format", "directsound");
624 }
625
626 // Playback buffers will auto-start playing in DSOUND_WaitDevice()
627
628 return true; // good to go.
629}
630
631static void DSOUND_DeinitializeStart(void)
632{
633#ifdef HAVE_MMDEVICEAPI_H
634 if (SupportsIMMDevice) {
635 SDL_IMMDevice_Quit();
636 }
637#endif
638}
639
640static void DSOUND_Deinitialize(void)
641{
642 DSOUND_Unload();
643#ifdef HAVE_MMDEVICEAPI_H
644 SupportsIMMDevice = false;
645#endif
646}
647
648static bool DSOUND_Init(SDL_AudioDriverImpl *impl)
649{
650 if (!DSOUND_Load()) {
651 return false;
652 }
653
654#ifdef HAVE_MMDEVICEAPI_H
655 SupportsIMMDevice = SDL_IMMDevice_Init(NULL);
656#endif
657
658 impl->DetectDevices = DSOUND_DetectDevices;
659 impl->OpenDevice = DSOUND_OpenDevice;
660 impl->PlayDevice = DSOUND_PlayDevice;
661 impl->WaitDevice = DSOUND_WaitDevice;
662 impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
663 impl->WaitRecordingDevice = DSOUND_WaitRecordingDevice;
664 impl->RecordDevice = DSOUND_RecordDevice;
665 impl->FlushRecording = DSOUND_FlushRecording;
666 impl->CloseDevice = DSOUND_CloseDevice;
667 impl->FreeDeviceHandle = DSOUND_FreeDeviceHandle;
668 impl->DeinitializeStart = DSOUND_DeinitializeStart;
669 impl->Deinitialize = DSOUND_Deinitialize;
670
671 impl->HasRecordingSupport = true;
672
673 return true;
674}
675
676AudioBootStrap DSOUND_bootstrap = {
677 "directsound", "DirectSound", DSOUND_Init, false, false
678};
679
680#endif // SDL_AUDIO_DRIVER_DSOUND