diff options
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.c | 680 |
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 | ||
| 38 | static bool SupportsIMMDevice = false; | ||
| 39 | #endif | ||
| 40 | |||
| 41 | // DirectX function pointers for audio | ||
| 42 | static SDL_SharedObject *DSoundDLL = NULL; | ||
| 43 | typedef HRESULT(WINAPI *fnDirectSoundCreate8)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN); | ||
| 44 | typedef HRESULT(WINAPI *fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID); | ||
| 45 | typedef HRESULT(WINAPI *fnDirectSoundCaptureCreate8)(LPCGUID, LPDIRECTSOUNDCAPTURE8 *, LPUNKNOWN); | ||
| 46 | typedef HRESULT(WINAPI *fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID); | ||
| 47 | typedef HRESULT(WINAPI *fnGetDeviceID)(LPCGUID, LPGUID); | ||
| 48 | static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL; | ||
| 49 | static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL; | ||
| 50 | static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = NULL; | ||
| 51 | static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = NULL; | ||
| 52 | static fnGetDeviceID pGetDeviceID = NULL; | ||
| 53 | |||
| 54 | #include <initguid.h> | ||
| 55 | DEFINE_GUID(SDL_DSDEVID_DefaultPlayback, 0xdef00000, 0x9c6d, 0x47ed, 0xaa, 0xf1, 0x4d, 0xda, 0x8f, 0x2b, 0x5c, 0x03); | ||
| 56 | DEFINE_GUID(SDL_DSDEVID_DefaultCapture, 0xdef00001, 0x9c6d, 0x47ed, 0xaa, 0xf1, 0x4d, 0xda, 0x8f, 0x2b, 0x5c, 0x03); | ||
| 57 | |||
| 58 | static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; | ||
| 59 | static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; | ||
| 60 | |||
| 61 | static 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 | |||
| 75 | static 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 | |||
| 112 | static 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 | |||
| 158 | static 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. | ||
| 171 | typedef struct FindAllDevsData | ||
| 172 | { | ||
| 173 | bool recording; | ||
| 174 | SDL_AudioDevice **default_device; | ||
| 175 | LPCGUID default_device_guid; | ||
| 176 | } FindAllDevsData; | ||
| 177 | |||
| 178 | static 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 | |||
| 205 | static 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 | |||
| 232 | static 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 | |||
| 267 | static 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 | |||
| 277 | static 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 | |||
| 336 | static 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 | |||
| 352 | static 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 | |||
| 379 | static 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 | |||
| 388 | static 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 | */ | ||
| 414 | static 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 | */ | ||
| 455 | static 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 | |||
| 494 | static 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 | |||
| 631 | static void DSOUND_DeinitializeStart(void) | ||
| 632 | { | ||
| 633 | #ifdef HAVE_MMDEVICEAPI_H | ||
| 634 | if (SupportsIMMDevice) { | ||
| 635 | SDL_IMMDevice_Quit(); | ||
| 636 | } | ||
| 637 | #endif | ||
| 638 | } | ||
| 639 | |||
| 640 | static void DSOUND_Deinitialize(void) | ||
| 641 | { | ||
| 642 | DSOUND_Unload(); | ||
| 643 | #ifdef HAVE_MMDEVICEAPI_H | ||
| 644 | SupportsIMMDevice = false; | ||
| 645 | #endif | ||
| 646 | } | ||
| 647 | |||
| 648 | static 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 | |||
| 676 | AudioBootStrap DSOUND_bootstrap = { | ||
| 677 | "directsound", "DirectSound", DSOUND_Init, false, false | ||
| 678 | }; | ||
| 679 | |||
| 680 | #endif // SDL_AUDIO_DRIVER_DSOUND | ||
