diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h index f2016fe753..6995fbe102 100644 --- a/src/audio/SDL_sysaudio.h +++ b/src/audio/SDL_sysaudio.h @@ -120,7 +120,7 @@ typedef struct SDL_AudioDriverImpl int (*CaptureFromDevice)(SDL_AudioDevice *device, void *buffer, int buflen); void (*FlushCapture)(SDL_AudioDevice *device); void (*CloseDevice)(SDL_AudioDevice *device); - void (*FreeDeviceHandle)(SDL_AudioDevice *handle); // SDL is done with this device; free the handle from SDL_AddAudioDevice() + void (*FreeDeviceHandle)(SDL_AudioDevice *device); // SDL is done with this device; free the handle from SDL_AddAudioDevice() void (*Deinitialize)(void); // Some flags to push duplicate code into the core and reduce #ifdefs. diff --git a/src/audio/directsound/SDL_directsound.c b/src/audio/directsound/SDL_directsound.c index 3c9bbbdc34..f1ab29706b 100644 --- a/src/audio/directsound/SDL_directsound.c +++ b/src/audio/directsound/SDL_directsound.c @@ -22,34 +22,34 @@ #ifdef SDL_AUDIO_DRIVER_DSOUND -/* Allow access to a raw mixing buffer */ - #include "../SDL_audio_c.h" #include "SDL_directsound.h" #include #ifdef HAVE_MMDEVICEAPI_H #include "../../core/windows/SDL_immdevice.h" -#endif /* HAVE_MMDEVICEAPI_H */ +#endif #ifndef WAVE_FORMAT_IEEE_FLOAT #define WAVE_FORMAT_IEEE_FLOAT 0x0003 #endif -/* For Vista+, we can enumerate DSound devices with IMMDevice */ +// For Vista+, we can enumerate DSound devices with IMMDevice #ifdef HAVE_MMDEVICEAPI_H static SDL_bool SupportsIMMDevice = SDL_FALSE; -#endif /* HAVE_MMDEVICEAPI_H */ +#endif -/* DirectX function pointers for audio */ +// DirectX function pointers for audio static void *DSoundDLL = NULL; typedef HRESULT(WINAPI *fnDirectSoundCreate8)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN); typedef HRESULT(WINAPI *fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID); typedef HRESULT(WINAPI *fnDirectSoundCaptureCreate8)(LPCGUID, LPDIRECTSOUNDCAPTURE8 *, LPUNKNOWN); typedef HRESULT(WINAPI *fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID); +typedef HRESULT(WINAPI *fnGetDeviceID)(LPCGUID, LPGUID); static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL; static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL; static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = NULL; static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = NULL; +static fnGetDeviceID pGetDeviceID = NULL; static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; @@ -60,6 +60,7 @@ static void DSOUND_Unload(void) pDirectSoundEnumerateW = NULL; pDirectSoundCaptureCreate8 = NULL; pDirectSoundCaptureEnumerateW = NULL; + pGetDeviceID = NULL; if (DSoundDLL != NULL) { SDL_UnloadObject(DSoundDLL); @@ -77,18 +78,19 @@ static int DSOUND_Load(void) if (DSoundDLL == NULL) { SDL_SetError("DirectSound: failed to load DSOUND.DLL"); } else { -/* Now make sure we have DirectX 8 or better... */ +// Now make sure we have DirectX 8 or better... #define DSOUNDLOAD(f) \ { \ p##f = (fn##f)SDL_LoadFunction(DSoundDLL, #f); \ if (!p##f) \ loaded = 0; \ } - loaded = 1; /* will reset if necessary. */ + loaded = 1; // will reset if necessary. DSOUNDLOAD(DirectSoundCreate8); DSOUNDLOAD(DirectSoundEnumerateW); DSOUNDLOAD(DirectSoundCaptureCreate8); DSOUNDLOAD(DirectSoundCaptureEnumerateW); + DSOUNDLOAD(GetDeviceID); #undef DSOUNDLOAD if (!loaded) { @@ -150,55 +152,80 @@ static int SetDSerror(const char *function, int code) } static void DSOUND_FreeDeviceHandle(SDL_AudioDevice *device) -{ - SDL_free(device->handle); -} - -static int DSOUND_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture) { #ifdef HAVE_MMDEVICEAPI_H if (SupportsIMMDevice) { - return SDL_IMMDevice_GetDefaultAudioInfo(name, spec, iscapture); + SDL_IMMDevice_FreeDeviceHandle(device); + } else +#endif + { + SDL_free(device->handle); } -#endif /* HAVE_MMDEVICEAPI_H */ - return SDL_Unsupported(); } -static BOOL CALLBACK FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID data) +// FindAllDevs is presumably only used on WinXP; Vista and later can use IMMDevice for better results. +typedef struct FindAllDevsData { - const int iscapture = (int)((size_t)data); - if (guid != NULL) { /* skip default device */ + SDL_bool iscapture; + SDL_AudioDevice **default_device; + LPCGUID default_device_guid; +} FindAllDevsData; + +static BOOL CALLBACK FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID userdata) +{ + FindAllDevsData *data = (FindAllDevsData *) userdata; + if (guid != NULL) { // skip default device char *str = WIN_LookupAudioDeviceName(desc, guid); if (str != NULL) { LPGUID cpyguid = (LPGUID)SDL_malloc(sizeof(GUID)); - SDL_memcpy(cpyguid, guid, sizeof(GUID)); + if (cpyguid) { + SDL_memcpy(cpyguid, guid, sizeof(GUID)); - /* Note that spec is NULL, because we are required to connect to the - * device before getting the channel mask and output format, making - * this information inaccessible at enumeration time - */ - SDL_AddAudioDevice(iscapture, str, NULL, cpyguid); - SDL_free(str); /* addfn() makes a copy of this string. */ + /* Note that spec is NULL, because we are required to connect to the + * device before getting the channel mask and output format, making + * this information inaccessible at enumeration time + */ + SDL_AudioDevice *device = SDL_AddAudioDevice(data->iscapture, str, NULL, cpyguid); + if (device && data->default_device && data->default_device_guid) { + if (SDL_memcmp(cpyguid, data->default_device_guid, sizeof (GUID)) == 0) { + *data->default_device = device; + } + } + } + SDL_free(str); // SDL_AddAudioDevice() makes a copy of this string. } } - return TRUE; /* keep enumerating. */ + return TRUE; // keep enumerating. } -static void DSOUND_DetectDevices(void) +static void DSOUND_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture) { #ifdef HAVE_MMDEVICEAPI_H if (SupportsIMMDevice) { - SDL_IMMDevice_EnumerateEndpoints(SDL_TRUE); - } else { -#endif /* HAVE_MMDEVICEAPI_H */ - pDirectSoundCaptureEnumerateW(FindAllDevs, (void *)((size_t)1)); - pDirectSoundEnumerateW(FindAllDevs, (void *)((size_t)0)); -#ifdef HAVE_MMDEVICEAPI_H + SDL_IMMDevice_EnumerateEndpoints(default_output, default_capture); + } else +#endif + { + // Without IMMDevice, you can enumerate devices and figure out the default devices, + // but you won't get device hotplug or default device change notifications. But this is + // only for WinXP; Windows Vista and later should be using IMMDevice. + FindAllDevsData data; + GUID guid; + + data.iscapture = SDL_TRUE; + data.default_device = default_capture; + data.default_device_guid = (pGetDeviceID(&DSDEVID_DefaultCapture, &guid) == DS_OK) ? &guid : NULL; + pDirectSoundCaptureEnumerateW(FindAllDevs, &data); + + data.iscapture = SDL_FALSE; + data.default_device = default_output; + data.default_device_guid = (pGetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) ? &guid : NULL; + pDirectSoundEnumerateW(FindAllDevs, &data); } -#endif /* HAVE_MMDEVICEAPI_H*/ + } -static void DSOUND_WaitDevice(SDL_AudioDevice *_this) +static void DSOUND_WaitDevice(SDL_AudioDevice *device) { DWORD status = 0; DWORD cursor = 0; @@ -208,11 +235,11 @@ static void DSOUND_WaitDevice(SDL_AudioDevice *_this) /* Semi-busy wait, since we have no way of getting play notification on a primary mixing buffer located in hardware (DirectX 5.0) */ - result = IDirectSoundBuffer_GetCurrentPosition(_this->hidden->mixbuf, + result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf, &junk, &cursor); if (result != DS_OK) { if (result == DSERR_BUFFERLOST) { - IDirectSoundBuffer_Restore(_this->hidden->mixbuf); + IDirectSoundBuffer_Restore(device->hidden->mixbuf); } #ifdef DEBUG_SOUND SetDSerror("DirectSound GetCurrentPosition", result); @@ -220,21 +247,24 @@ static void DSOUND_WaitDevice(SDL_AudioDevice *_this) return; } - while ((cursor / _this->spec.size) == _this->hidden->lastchunk) { - /* FIXME: find out how much time is left and sleep that long */ + while ((cursor / device->buffer_size) == device->hidden->lastchunk) { + if (SDL_AtomicGet(&device->shutdown)) { + return; + } + SDL_Delay(1); - /* Try to restore a lost sound buffer */ - IDirectSoundBuffer_GetStatus(_this->hidden->mixbuf, &status); + // Try to restore a lost sound buffer + IDirectSoundBuffer_GetStatus(device->hidden->mixbuf, &status); if (status & DSBSTATUS_BUFFERLOST) { - IDirectSoundBuffer_Restore(_this->hidden->mixbuf); - IDirectSoundBuffer_GetStatus(_this->hidden->mixbuf, &status); + IDirectSoundBuffer_Restore(device->hidden->mixbuf); + IDirectSoundBuffer_GetStatus(device->hidden->mixbuf, &status); if (status & DSBSTATUS_BUFFERLOST) { break; } } if (!(status & DSBSTATUS_PLAYING)) { - result = IDirectSoundBuffer_Play(_this->hidden->mixbuf, 0, 0, + result = IDirectSoundBuffer_Play(device->hidden->mixbuf, 0, 0, DSBPLAY_LOOPING); if (result == DS_OK) { continue; @@ -245,8 +275,8 @@ static void DSOUND_WaitDevice(SDL_AudioDevice *_this) return; } - /* Find out where we are playing */ - result = IDirectSoundBuffer_GetCurrentPosition(_this->hidden->mixbuf, + // Find out where we are playing + result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf, &junk, &cursor); if (result != DS_OK) { SetDSerror("DirectSound GetCurrentPosition", result); @@ -255,102 +285,100 @@ static void DSOUND_WaitDevice(SDL_AudioDevice *_this) } } -static void DSOUND_PlayDevice(SDL_AudioDevice *_this) +static void DSOUND_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) { - /* Unlock the buffer, allowing it to play */ - if (_this->hidden->locked_buf) { - IDirectSoundBuffer_Unlock(_this->hidden->mixbuf, - _this->hidden->locked_buf, - _this->spec.size, NULL, 0); - } + // Unlock the buffer, allowing it to play + SDL_assert(buflen == device->buffer_size); + IDirectSoundBuffer_Unlock(device->hidden->mixbuf, (LPVOID) buffer, buflen, NULL, 0); } -static Uint8 *DSOUND_GetDeviceBuf(SDL_AudioDevice *_this) +static Uint8 *DSOUND_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) { DWORD cursor = 0; DWORD junk = 0; HRESULT result = DS_OK; - DWORD rawlen = 0; - /* Figure out which blocks to fill next */ - _this->hidden->locked_buf = NULL; - result = IDirectSoundBuffer_GetCurrentPosition(_this->hidden->mixbuf, + SDL_assert(*buffer_size == device->buffer_size); + + // Figure out which blocks to fill next + device->hidden->locked_buf = NULL; + result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf, &junk, &cursor); if (result == DSERR_BUFFERLOST) { - IDirectSoundBuffer_Restore(_this->hidden->mixbuf); - result = IDirectSoundBuffer_GetCurrentPosition(_this->hidden->mixbuf, + IDirectSoundBuffer_Restore(device->hidden->mixbuf); + result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf, &junk, &cursor); } if (result != DS_OK) { SetDSerror("DirectSound GetCurrentPosition", result); return NULL; } - cursor /= _this->spec.size; + cursor /= device->buffer_size; #ifdef DEBUG_SOUND - /* Detect audio dropouts */ + // Detect audio dropouts { DWORD spot = cursor; - if (spot < _this->hidden->lastchunk) { - spot += _this->hidden->num_buffers; + if (spot < device->hidden->lastchunk) { + spot += device->hidden->num_buffers; } - if (spot > _this->hidden->lastchunk + 1) { + if (spot > device->hidden->lastchunk + 1) { fprintf(stderr, "Audio dropout, missed %d fragments\n", - (spot - (_this->hidden->lastchunk + 1))); + (spot - (device->hidden->lastchunk + 1))); } } #endif - _this->hidden->lastchunk = cursor; - cursor = (cursor + 1) % _this->hidden->num_buffers; - cursor *= _this->spec.size; + device->hidden->lastchunk = cursor; + cursor = (cursor + 1) % device->hidden->num_buffers; + cursor *= device->buffer_size; - /* Lock the audio buffer */ - result = IDirectSoundBuffer_Lock(_this->hidden->mixbuf, cursor, - _this->spec.size, - (LPVOID *)&_this->hidden->locked_buf, + // Lock the audio buffer + DWORD rawlen = 0; + result = IDirectSoundBuffer_Lock(device->hidden->mixbuf, cursor, + device->buffer_size, + (LPVOID *)&device->hidden->locked_buf, &rawlen, NULL, &junk, 0); if (result == DSERR_BUFFERLOST) { - IDirectSoundBuffer_Restore(_this->hidden->mixbuf); - result = IDirectSoundBuffer_Lock(_this->hidden->mixbuf, cursor, - _this->spec.size, - (LPVOID *)&_this->hidden->locked_buf, &rawlen, NULL, + IDirectSoundBuffer_Restore(device->hidden->mixbuf); + result = IDirectSoundBuffer_Lock(device->hidden->mixbuf, cursor, + device->buffer_size, + (LPVOID *)&device->hidden->locked_buf, &rawlen, NULL, &junk, 0); } if (result != DS_OK) { SetDSerror("DirectSound Lock", result); return NULL; } - return _this->hidden->locked_buf; + return device->hidden->locked_buf; } -static int DSOUND_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) +static void DSOUND_WaitCaptureDevice(SDL_AudioDevice *device) { - struct SDL_PrivateAudioData *h = _this->hidden; - DWORD junk, cursor, ptr1len, ptr2len; + struct SDL_PrivateAudioData *h = device->hidden; + while (!SDL_AtomicGet(&device->shutdown)) { + DWORD junk, cursor; + if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) != DS_OK) { + SDL_AudioDeviceDisconnected(device); + return; + } else if ((cursor / device->buffer_size) != h->lastchunk) { + return; + } + SDL_Delay(1); + } +} + +static int DSOUND_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen) +{ + struct SDL_PrivateAudioData *h = device->hidden; + DWORD ptr1len, ptr2len; VOID *ptr1, *ptr2; - SDL_assert((Uint32)buflen == _this->spec.size); + SDL_assert(buflen == device->buffer_size); - while (SDL_TRUE) { - if (SDL_AtomicGet(&_this->shutdown)) { /* in case the buffer froze... */ - SDL_memset(buffer, _this->spec.silence, buflen); - return buflen; - } - - if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) != DS_OK) { - return -1; - } - if ((cursor / _this->spec.size) == h->lastchunk) { - SDL_Delay(1); /* FIXME: find out how much time is left and sleep that long */ - } else { - break; - } - } - - if (IDirectSoundCaptureBuffer_Lock(h->capturebuf, h->lastchunk * _this->spec.size, _this->spec.size, &ptr1, &ptr1len, &ptr2, &ptr2len, 0) != DS_OK) { + if (IDirectSoundCaptureBuffer_Lock(h->capturebuf, h->lastchunk * buflen, buflen, &ptr1, &ptr1len, &ptr2, &ptr2len, 0) != DS_OK) { return -1; } - SDL_assert(ptr1len == _this->spec.size); + SDL_assert(ptr1len == buflen); SDL_assert(ptr2 == NULL); SDL_assert(ptr2len == 0); @@ -362,51 +390,54 @@ static int DSOUND_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int bu h->lastchunk = (h->lastchunk + 1) % h->num_buffers; - return ptr1len; + return (int) ptr1len; } -static void DSOUND_FlushCapture(SDL_AudioDevice *_this) +static void DSOUND_FlushCapture(SDL_AudioDevice *device) { - struct SDL_PrivateAudioData *h = _this->hidden; + struct SDL_PrivateAudioData *h = device->hidden; DWORD junk, cursor; if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) == DS_OK) { - h->lastchunk = cursor / _this->spec.size; + h->lastchunk = cursor / device->buffer_size; } } -static void DSOUND_CloseDevice(SDL_AudioDevice *_this) +static void DSOUND_CloseDevice(SDL_AudioDevice *device) { - if (_this->hidden->mixbuf != NULL) { - IDirectSoundBuffer_Stop(_this->hidden->mixbuf); - IDirectSoundBuffer_Release(_this->hidden->mixbuf); + if (device->hidden) { + if (device->hidden->mixbuf != NULL) { + IDirectSoundBuffer_Stop(device->hidden->mixbuf); + IDirectSoundBuffer_Release(device->hidden->mixbuf); + } + if (device->hidden->sound != NULL) { + IDirectSound_Release(device->hidden->sound); + } + if (device->hidden->capturebuf != NULL) { + IDirectSoundCaptureBuffer_Stop(device->hidden->capturebuf); + IDirectSoundCaptureBuffer_Release(device->hidden->capturebuf); + } + if (device->hidden->capture != NULL) { + IDirectSoundCapture_Release(device->hidden->capture); + } + SDL_free(device->hidden); + device->hidden = NULL; } - if (_this->hidden->sound != NULL) { - IDirectSound_Release(_this->hidden->sound); - } - if (_this->hidden->capturebuf != NULL) { - IDirectSoundCaptureBuffer_Stop(_this->hidden->capturebuf); - IDirectSoundCaptureBuffer_Release(_this->hidden->capturebuf); - } - if (_this->hidden->capture != NULL) { - IDirectSoundCapture_Release(_this->hidden->capture); - } - SDL_free(_this->hidden); } /* This function tries to create a secondary audio buffer, and returns the number of audio chunks available in the created buffer. This is for playback devices, not capture. */ -static int CreateSecondary(SDL_AudioDevice *_this, const DWORD bufsize, WAVEFORMATEX *wfmt) +static int CreateSecondary(SDL_AudioDevice *device, const DWORD bufsize, WAVEFORMATEX *wfmt) { - LPDIRECTSOUND sndObj = _this->hidden->sound; - LPDIRECTSOUNDBUFFER *sndbuf = &_this->hidden->mixbuf; + LPDIRECTSOUND sndObj = device->hidden->sound; + LPDIRECTSOUNDBUFFER *sndbuf = &device->hidden->mixbuf; HRESULT result = DS_OK; DSBUFFERDESC format; LPVOID pvAudioPtr1, pvAudioPtr2; DWORD dwAudioBytes1, dwAudioBytes2; - /* Try to create the secondary buffer */ + // Try to create the secondary buffer SDL_zero(format); format.dwSize = sizeof(format); format.dwFlags = DSBCAPS_GETCURRENTPOSITION2; @@ -419,30 +450,29 @@ static int CreateSecondary(SDL_AudioDevice *_this, const DWORD bufsize, WAVEFORM } IDirectSoundBuffer_SetFormat(*sndbuf, wfmt); - /* Silence the initial audio buffer */ + // Silence the initial audio buffer result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes, (LPVOID *)&pvAudioPtr1, &dwAudioBytes1, (LPVOID *)&pvAudioPtr2, &dwAudioBytes2, DSBLOCK_ENTIREBUFFER); if (result == DS_OK) { - SDL_memset(pvAudioPtr1, _this->spec.silence, dwAudioBytes1); + SDL_memset(pvAudioPtr1, device->silence_value, dwAudioBytes1); IDirectSoundBuffer_Unlock(*sndbuf, (LPVOID)pvAudioPtr1, dwAudioBytes1, (LPVOID)pvAudioPtr2, dwAudioBytes2); } - /* We're ready to go */ - return 0; + return 0; // We're ready to go } /* This function tries to create a capture buffer, and returns the number of audio chunks available in the created buffer. This is for capture devices, not playback. */ -static int CreateCaptureBuffer(SDL_AudioDevice *_this, const DWORD bufsize, WAVEFORMATEX *wfmt) +static int CreateCaptureBuffer(SDL_AudioDevice *device, const DWORD bufsize, WAVEFORMATEX *wfmt) { - LPDIRECTSOUNDCAPTURE capture = _this->hidden->capture; - LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &_this->hidden->capturebuf; + LPDIRECTSOUNDCAPTURE capture = device->hidden->capture; + LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &device->hidden->capturebuf; DSCBUFFERDESC format; HRESULT result; @@ -464,7 +494,7 @@ static int CreateCaptureBuffer(SDL_AudioDevice *_this, const DWORD bufsize, WAVE } #if 0 - /* presumably this starts at zero, but just in case... */ + // presumably this starts at zero, but just in case... result = IDirectSoundCaptureBuffer_GetCurrentPosition(*capturebuf, &junk, &cursor); if (result != DS_OK) { IDirectSoundCaptureBuffer_Stop(*capturebuf); @@ -472,42 +502,45 @@ static int CreateCaptureBuffer(SDL_AudioDevice *_this, const DWORD bufsize, WAVE return SetDSerror("DirectSound GetCurrentPosition", result); } - _this->hidden->lastchunk = cursor / _this->spec.size; + device->hidden->lastchunk = cursor / device->buffer_size; #endif return 0; } -static int DSOUND_OpenDevice(SDL_AudioDevice *_this, const char *devname) +static int DSOUND_OpenDevice(SDL_AudioDevice *device) { - const DWORD numchunks = 8; - HRESULT result; - SDL_bool tried_format = SDL_FALSE; - SDL_bool iscapture = _this->iscapture; - SDL_AudioFormat test_format; - const SDL_AudioFormat *closefmts; - LPGUID guid = (LPGUID)_this->handle; - DWORD bufsize; - - /* Initialize all variables that we clean on shutdown */ - _this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*_this->hidden)); - if (_this->hidden == NULL) { + // Initialize all variables that we clean on shutdown + device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden)); + if (device->hidden == NULL) { return SDL_OutOfMemory(); } - SDL_zerop(_this->hidden); - /* Open the audio device */ - if (iscapture) { - result = pDirectSoundCaptureCreate8(guid, &_this->hidden->capture, NULL); + // Open the audio device + LPGUID guid; +#ifdef HAVE_MMDEVICEAPI_H + if (SupportsIMMDevice) { + guid = SDL_IMMDevice_GetDirectSoundGUID(device); + } else +#endif + { + guid = (LPGUID) device->handle; + } + + SDL_assert(guid != NULL); + + HRESULT result; + if (device->iscapture) { + result = pDirectSoundCaptureCreate8(guid, &device->hidden->capture, NULL); if (result != DS_OK) { return SetDSerror("DirectSoundCaptureCreate8", result); } } else { - result = pDirectSoundCreate8(guid, &_this->hidden->sound, NULL); + result = pDirectSoundCreate8(guid, &device->hidden->sound, NULL); if (result != DS_OK) { return SetDSerror("DirectSoundCreate8", result); } - result = IDirectSound_SetCooperativeLevel(_this->hidden->sound, + result = IDirectSound_SetCooperativeLevel(device->hidden->sound, GetDesktopWindow(), DSSCL_NORMAL); if (result != DS_OK) { @@ -515,7 +548,10 @@ static int DSOUND_OpenDevice(SDL_AudioDevice *_this, const char *devname) } } - closefmts = SDL_ClosestAudioFormats(_this->spec.format); + const DWORD numchunks = 8; + SDL_bool tried_format = SDL_FALSE; + SDL_AudioFormat test_format; + const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); while ((test_format = *(closefmts++)) != 0) { switch (test_format) { case SDL_AUDIO_U8: @@ -524,69 +560,68 @@ static int DSOUND_OpenDevice(SDL_AudioDevice *_this, const char *devname) case SDL_AUDIO_F32: tried_format = SDL_TRUE; - _this->spec.format = test_format; + device->spec.format = test_format; - /* Update the fragment size as size in bytes */ - SDL_CalculateAudioSpec(&_this->spec); + // Update the fragment size as size in bytes + SDL_UpdatedAudioDeviceFormat(device); - bufsize = numchunks * _this->spec.size; + const DWORD bufsize = numchunks * device->buffer_size; if ((bufsize < DSBSIZE_MIN) || (bufsize > DSBSIZE_MAX)) { SDL_SetError("Sound buffer size must be between %d and %d", (int)((DSBSIZE_MIN < numchunks) ? 1 : DSBSIZE_MIN / numchunks), (int)(DSBSIZE_MAX / numchunks)); } else { - int rc; WAVEFORMATEXTENSIBLE wfmt; SDL_zero(wfmt); - if (_this->spec.channels > 2) { + if (device->spec.channels > 2) { wfmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfmt.Format.cbSize = sizeof(wfmt) - sizeof(WAVEFORMATEX); - if (SDL_AUDIO_ISFLOAT(_this->spec.format)) { + if (SDL_AUDIO_ISFLOAT(device->spec.format)) { SDL_memcpy(&wfmt.SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)); } else { SDL_memcpy(&wfmt.SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)); } - wfmt.Samples.wValidBitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format); + wfmt.Samples.wValidBitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format); - switch (_this->spec.channels) { - case 3: /* 3.0 (or 2.1) */ + switch (device->spec.channels) { + case 3: // 3.0 (or 2.1) wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER; break; - case 4: /* 4.0 */ + case 4: // 4.0 wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; - case 5: /* 5.0 (or 4.1) */ + case 5: // 5.0 (or 4.1) wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; - case 6: /* 5.1 */ + case 6: // 5.1 wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; - case 7: /* 6.1 */ + case 7: // 6.1 wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_BACK_CENTER; break; - case 8: /* 7.1 */ + case 8: // 7.1 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; break; default: SDL_assert(0 && "Unsupported channel count!"); break; } - } else if (SDL_AUDIO_ISFLOAT(_this->spec.format)) { + } else if (SDL_AUDIO_ISFLOAT(device->spec.format)) { wfmt.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; } else { wfmt.Format.wFormatTag = WAVE_FORMAT_PCM; } - wfmt.Format.wBitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format); - wfmt.Format.nChannels = _this->spec.channels; - wfmt.Format.nSamplesPerSec = _this->spec.freq; + wfmt.Format.wBitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format); + wfmt.Format.nChannels = device->spec.channels; + wfmt.Format.nSamplesPerSec = device->spec.freq; wfmt.Format.nBlockAlign = wfmt.Format.nChannels * (wfmt.Format.wBitsPerSample / 8); wfmt.Format.nAvgBytesPerSec = wfmt.Format.nSamplesPerSec * wfmt.Format.nBlockAlign; - rc = iscapture ? CreateCaptureBuffer(_this, bufsize, (WAVEFORMATEX *)&wfmt) : CreateSecondary(_this, bufsize, (WAVEFORMATEX *)&wfmt); + const int rc = device->iscapture ? CreateCaptureBuffer(device, bufsize, (WAVEFORMATEX *)&wfmt) : CreateSecondary(device, bufsize, (WAVEFORMATEX *)&wfmt); if (rc == 0) { - _this->hidden->num_buffers = numchunks; + device->hidden->num_buffers = numchunks; break; } } @@ -599,14 +634,14 @@ static int DSOUND_OpenDevice(SDL_AudioDevice *_this, const char *devname) if (!test_format) { if (tried_format) { - return -1; /* CreateSecondary() should have called SDL_SetError(). */ + return -1; // CreateSecondary() should have called SDL_SetError(). } return SDL_SetError("%s: Unsupported audio format", "directsound"); } - /* Playback buffers will auto-start playing in DSOUND_WaitDevice() */ + // Playback buffers will auto-start playing in DSOUND_WaitDevice() - return 0; /* good to go. */ + return 0; // good to go. } static void DSOUND_Deinitialize(void) @@ -616,7 +651,7 @@ static void DSOUND_Deinitialize(void) SDL_IMMDevice_Quit(); SupportsIMMDevice = SDL_FALSE; } -#endif /* HAVE_MMDEVICEAPI_H */ +#endif DSOUND_Unload(); } @@ -628,28 +663,27 @@ static SDL_bool DSOUND_Init(SDL_AudioDriverImpl *impl) #ifdef HAVE_MMDEVICEAPI_H SupportsIMMDevice = !(SDL_IMMDevice_Init() < 0); -#endif /* HAVE_MMDEVICEAPI_H */ +#endif - /* Set the function pointers */ impl->DetectDevices = DSOUND_DetectDevices; impl->OpenDevice = DSOUND_OpenDevice; impl->PlayDevice = DSOUND_PlayDevice; impl->WaitDevice = DSOUND_WaitDevice; impl->GetDeviceBuf = DSOUND_GetDeviceBuf; + impl->WaitCaptureDevice = DSOUND_WaitCaptureDevice; impl->CaptureFromDevice = DSOUND_CaptureFromDevice; impl->FlushCapture = DSOUND_FlushCapture; impl->CloseDevice = DSOUND_CloseDevice; impl->FreeDeviceHandle = DSOUND_FreeDeviceHandle; impl->Deinitialize = DSOUND_Deinitialize; - impl->GetDefaultAudioInfo = DSOUND_GetDefaultAudioInfo; impl->HasCaptureSupport = SDL_TRUE; - return SDL_TRUE; /* this audio target is available. */ + return SDL_TRUE; } AudioBootStrap DSOUND_bootstrap = { "directsound", "DirectSound", DSOUND_Init, SDL_FALSE }; -#endif /* SDL_AUDIO_DRIVER_DSOUND */ +#endif // SDL_AUDIO_DRIVER_DSOUND diff --git a/src/audio/directsound/SDL_directsound.h b/src/audio/directsound/SDL_directsound.h index d66c920aec..18b67dac27 100644 --- a/src/audio/directsound/SDL_directsound.h +++ b/src/audio/directsound/SDL_directsound.h @@ -27,9 +27,10 @@ #include "../SDL_sysaudio.h" -/* The DirectSound objects */ +// The DirectSound objects struct SDL_PrivateAudioData { + // !!! FIXME: make this a union with capture/playback sections? LPDIRECTSOUND sound; LPDIRECTSOUNDBUFFER mixbuf; LPDIRECTSOUNDCAPTURE capture; @@ -39,4 +40,4 @@ struct SDL_PrivateAudioData Uint8 *locked_buf; }; -#endif /* SDL_directsound_h_ */ +#endif // SDL_directsound_h_ diff --git a/src/core/windows/SDL_immdevice.c b/src/core/windows/SDL_immdevice.c index 8f91268fb3..c00916a417 100644 --- a/src/core/windows/SDL_immdevice.c +++ b/src/core/windows/SDL_immdevice.c @@ -27,6 +27,12 @@ #include "../../audio/SDL_sysaudio.h" #include /* For CLSIDFromString */ +typedef struct SDL_IMMDevice_HandleData +{ + LPWSTR immdevice_id; + GUID directsound_guid; +} SDL_IMMDevice_HandleData; + static const ERole SDL_IMMDevice_role = eConsole; /* !!! FIXME: should this be eMultimedia? Should be a hint? */ /* This is global to the WASAPI target, to handle hotplug and default device lookup. */ @@ -51,9 +57,26 @@ static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; /* *INDENT-ON* */ /* clang-format on */ -/* these increment as default devices change. Opened default devices pick up changes in their threads. */ -SDL_AtomicInt SDL_IMMDevice_DefaultPlaybackGeneration; -SDL_AtomicInt SDL_IMMDevice_DefaultCaptureGeneration; +static SDL_bool FindByDevIDCallback(SDL_AudioDevice *device, void *userdata) +{ + const SDL_IMMDevice_HandleData *handle = (const SDL_IMMDevice_HandleData *) device->handle; + return (SDL_wcscmp(handle->immdevice_id, (LPCWSTR) userdata) == 0) ? SDL_TRUE : SDL_FALSE; +} + +static SDL_AudioDevice *SDL_IMMDevice_FindByDevID(LPCWSTR devid) +{ + return SDL_FindPhysicalAudioDeviceByCallback(FindByDevIDCallback, (void *) devid); +} + +LPGUID SDL_IMMDevice_GetDirectSoundGUID(SDL_AudioDevice *device) +{ + return (device && device->handle) ? &(((SDL_IMMDevice_HandleData *) device->handle)->directsound_guid) : NULL; +} + +LPCWSTR SDL_IMMDevice_GetDevID(SDL_AudioDevice *device) +{ + return (device && device->handle) ? ((const SDL_IMMDevice_HandleData *) device->handle)->immdevice_id : NULL; +} static void GetMMDeviceInfo(IMMDevice *device, char **utf8dev, WAVEFORMATEXTENSIBLE *fmt, GUID *guid) { @@ -82,94 +105,54 @@ static void GetMMDeviceInfo(IMMDevice *device, char **utf8dev, WAVEFORMATEXTENSI } } -/* This is a list of device id strings we have inflight, so we have consistent pointers to the same device. */ -typedef struct DevIdList +void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device) { - LPWSTR str; - LPGUID guid; - struct DevIdList *next; -} DevIdList; - -static DevIdList *deviceid_list = NULL; - -static void SDL_IMMDevice_Remove(const SDL_bool iscapture, LPCWSTR devid, SDL_bool useguid) -{ - DevIdList *i; - DevIdList *next; - DevIdList *prev = NULL; - for (i = deviceid_list; i; i = next) { - next = i->next; - if (SDL_wcscmp(i->str, devid) == 0) { - if (prev) { - prev->next = next; - } else { - deviceid_list = next; - } - SDL_RemoveAudioDevice(iscapture, useguid ? ((void *)i->guid) : ((void *)i->str)); - SDL_free(i->str); - SDL_free(i); - } else { - prev = i; - } + if (device && device->handle) { + SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *) device->handle; + SDL_free(handle->immdevice_id); + SDL_free(handle); + device->handle = NULL; } } -static void SDL_IMMDevice_Add(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid, GUID *dsoundguid, SDL_bool useguid) +static SDL_AudioDevice *SDL_IMMDevice_Add(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid, GUID *dsoundguid) { - DevIdList *devidlist; - SDL_AudioSpec spec; - LPWSTR devidcopy; - LPGUID cpyguid; - LPVOID driverdata; - /* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever). In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for phones and tablets, where you might have an internal speaker and a headphone jack and expect both to be available and switch automatically. (!!! FIXME...?) */ - /* see if we already have this one. */ - for (devidlist = deviceid_list; devidlist; devidlist = devidlist->next) { - if (SDL_wcscmp(devidlist->str, devid) == 0) { - return; /* we already have this. */ + if (!devname) { + return NULL; + } + + // see if we already have this one first. + SDL_AudioDevice *device = SDL_IMMDevice_FindByDevID(devid); + if (!device) { + // handle is freed by SDL_IMMDevice_FreeDeviceHandle! + SDL_IMMDevice_HandleData *handle = SDL_malloc(sizeof(SDL_IMMDevice_HandleData)); + if (!handle) { + SDL_OutOfMemory(); + return NULL; } - } - - devidlist = (DevIdList *)SDL_malloc(sizeof(*devidlist)); - if (devidlist == NULL) { - return; /* oh well. */ - } - - devidcopy = SDL_wcsdup(devid); - if (!devidcopy) { - SDL_free(devidlist); - return; /* oh well. */ - } - - if (useguid) { - /* This is freed by DSOUND_FreeDeviceData! */ - cpyguid = (LPGUID)SDL_malloc(sizeof(GUID)); - if (!cpyguid) { - SDL_free(devidlist); - SDL_free(devidcopy); - return; /* oh well. */ + handle->immdevice_id = SDL_wcsdup(devid); + if (!handle->immdevice_id) { + SDL_OutOfMemory(); + SDL_free(handle); + return NULL; } - SDL_memcpy(cpyguid, dsoundguid, sizeof(GUID)); - driverdata = cpyguid; - } else { - cpyguid = NULL; - driverdata = devidcopy; + SDL_memcpy(&handle->directsound_guid, dsoundguid, sizeof(GUID)); + + SDL_AudioSpec spec; + SDL_zero(spec); + spec.channels = (Uint8)fmt->Format.nChannels; + spec.freq = fmt->Format.nSamplesPerSec; + spec.format = WaveFormatToSDLFormat((WAVEFORMATEX *)fmt); + + device = SDL_AddAudioDevice(iscapture, devname, &spec, handle); } - devidlist->str = devidcopy; - devidlist->guid = cpyguid; - devidlist->next = deviceid_list; - deviceid_list = devidlist; - - SDL_zero(spec); - spec.channels = (Uint8)fmt->Format.nChannels; - spec.freq = fmt->Format.nSamplesPerSec; - spec.format = WaveFormatToSDLFormat((WAVEFORMATEX *)fmt); - SDL_AddAudioDevice(iscapture, devname, &spec, driverdata); + return device; } /* We need a COM subclass of IMMNotificationClient for hotplug support, which is @@ -184,11 +167,11 @@ typedef struct SDLMMNotificationClient SDL_bool useguid; } SDLMMNotificationClient; -static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotificationClient *this, REFIID iid, void **ppv) +static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotificationClient *client, REFIID iid, void **ppv) { if ((WIN_IsEqualIID(iid, &IID_IUnknown)) || (WIN_IsEqualIID(iid, &SDL_IID_IMMNotificationClient))) { - *ppv = this; - this->lpVtbl->AddRef(this); + *ppv = client; + client->lpVtbl->AddRef(client); return S_OK; } @@ -196,55 +179,34 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotif return E_NOINTERFACE; } -static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_AddRef(IMMNotificationClient *ithis) +static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_AddRef(IMMNotificationClient *iclient) { - SDLMMNotificationClient *this = (SDLMMNotificationClient *)ithis; - return (ULONG)(SDL_AtomicIncRef(&this->refcount) + 1); + SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient; + return (ULONG)(SDL_AtomicIncRef(&client->refcount) + 1); } -static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_Release(IMMNotificationClient *ithis) +static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_Release(IMMNotificationClient *iclient) { - /* this is a static object; we don't ever free it. */ - SDLMMNotificationClient *this = (SDLMMNotificationClient *)ithis; - const ULONG retval = SDL_AtomicDecRef(&this->refcount); + /* client is a static object; we don't ever free it. */ + SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient; + const ULONG retval = SDL_AtomicDecRef(&client->refcount); if (retval == 0) { - SDL_AtomicSet(&this->refcount, 0); /* uhh... */ + SDL_AtomicSet(&client->refcount, 0); /* uhh... */ return 0; } return retval - 1; } -/* These are the entry points called when WASAPI device endpoints change. */ -static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *ithis, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) +// These are the entry points called when WASAPI device endpoints change. +static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *iclient, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) { - if (role != SDL_IMMDevice_role) { - return S_OK; /* ignore it. */ + if (role == SDL_IMMDevice_role) { + SDL_DefaultAudioDeviceChanged(SDL_IMMDevice_FindByDevID(pwstrDeviceId)); } - - /* Increment the "generation," so opened devices will pick this up in their threads. */ - switch (flow) { - case eRender: - SDL_AtomicAdd(&SDL_IMMDevice_DefaultPlaybackGeneration, 1); - break; - - case eCapture: - SDL_AtomicAdd(&SDL_IMMDevice_DefaultCaptureGeneration, 1); - break; - - case eAll: - SDL_AtomicAdd(&SDL_IMMDevice_DefaultPlaybackGeneration, 1); - SDL_AtomicAdd(&SDL_IMMDevice_DefaultCaptureGeneration, 1); - break; - - default: - SDL_assert(!"uhoh, unexpected OnDefaultDeviceChange flow!"); - break; - } - return S_OK; } -static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceAdded(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId) +static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceAdded(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId) { /* we ignore this; devices added here then progress to ACTIVE, if appropriate, in OnDeviceStateChange, making that a better place to deal with device adds. More @@ -254,13 +216,12 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceAdded(IMMNotifi return S_OK; } -static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceRemoved(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId) +static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceRemoved(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId) { - /* See notes in OnDeviceAdded handler about why we ignore this. */ - return S_OK; + return S_OK; // See notes in OnDeviceAdded handler about why we ignore this. } -static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId, DWORD dwNewState) +static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId, DWORD dwNewState) { IMMDevice *device = NULL; @@ -270,18 +231,17 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IM EDataFlow flow; if (SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) { const SDL_bool iscapture = (flow == eCapture); - const SDLMMNotificationClient *client = (SDLMMNotificationClient *)ithis; if (dwNewState == DEVICE_STATE_ACTIVE) { char *utf8dev; WAVEFORMATEXTENSIBLE fmt; GUID dsoundguid; GetMMDeviceInfo(device, &utf8dev, &fmt, &dsoundguid); if (utf8dev) { - SDL_IMMDevice_Add(iscapture, utf8dev, &fmt, pwstrDeviceId, &dsoundguid, client->useguid); + SDL_IMMDevice_Add(iscapture, utf8dev, &fmt, pwstrDeviceId, &dsoundguid); SDL_free(utf8dev); } } else { - SDL_IMMDevice_Remove(iscapture, pwstrDeviceId, client->useguid); + SDL_AudioDeviceDisconnected(SDL_IMMDevice_FindByDevID(pwstrDeviceId)); } } IMMEndpoint_Release(endpoint); @@ -292,9 +252,9 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IM return S_OK; } -static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *this, LPCWSTR pwstrDeviceId, const PROPERTYKEY key) +static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *client, LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { - return S_OK; /* we don't care about these. */ + return S_OK; // we don't care about these. } static const IMMNotificationClientVtbl notification_client_vtbl = { @@ -314,31 +274,25 @@ int SDL_IMMDevice_Init(void) { HRESULT ret; - SDL_AtomicSet(&SDL_IMMDevice_DefaultPlaybackGeneration, 1); - SDL_AtomicSet(&SDL_IMMDevice_DefaultCaptureGeneration, 1); - /* just skip the discussion with COM here. */ if (!WIN_IsWindowsVistaOrGreater()) { - return SDL_SetError("WASAPI support requires Windows Vista or later"); + return SDL_SetError("IMMDevice support requires Windows Vista or later"); } if (FAILED(WIN_CoInitialize())) { - return SDL_SetError("WASAPI: CoInitialize() failed"); + return SDL_SetError("IMMDevice: CoInitialize() failed"); } ret = CoCreateInstance(&SDL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &SDL_IID_IMMDeviceEnumerator, (LPVOID *)&enumerator); if (FAILED(ret)) { WIN_CoUninitialize(); - return WIN_SetErrorFromHRESULT("WASAPI CoCreateInstance(MMDeviceEnumerator)", ret); + return WIN_SetErrorFromHRESULT("IMMDevice CoCreateInstance(MMDeviceEnumerator)", ret); } return 0; } void SDL_IMMDevice_Quit(void) { - DevIdList *devidlist; - DevIdList *next; - if (enumerator) { IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)¬ification_client); IMMDeviceEnumerator_Release(enumerator); @@ -346,178 +300,101 @@ void SDL_IMMDevice_Quit(void) } WIN_CoUninitialize(); - - for (devidlist = deviceid_list; devidlist; devidlist = next) { - next = devidlist->next; - SDL_free(devidlist->str); - SDL_free(devidlist); - } - deviceid_list = NULL; } -int SDL_IMMDevice_Get(LPCWSTR devid, IMMDevice **device, SDL_bool iscapture) +int SDL_IMMDevice_Get(SDL_AudioDevice *device, IMMDevice **immdevice, SDL_bool iscapture) { const Uint64 timeout = SDL_GetTicks() + 8000; /* intel's audio drivers can fail for up to EIGHT SECONDS after a device is connected or we wake from sleep. */ - HRESULT ret; SDL_assert(device != NULL); + SDL_assert(immdevice != NULL); - while (SDL_TRUE) { - if (devid == NULL) { - const EDataFlow dataflow = iscapture ? eCapture : eRender; - ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_IMMDevice_role, device); - } else { - ret = IMMDeviceEnumerator_GetDevice(enumerator, devid, device); + LPCWSTR devid = SDL_IMMDevice_GetDevID(device); + SDL_assert(devid != NULL); + + HRESULT ret; + while ((ret = IMMDeviceEnumerator_GetDevice(enumerator, devid, immdevice)) == E_NOTFOUND) { + const Uint64 now = SDL_GetTicks(); + if (timeout > now) { + const Uint64 ticksleft = timeout - now; + SDL_Delay((Uint32)SDL_min(ticksleft, 300)); /* wait awhile and try again. */ + continue; } - - if (SUCCEEDED(ret)) { - break; - } - - if (ret == E_NOTFOUND) { - const Uint64 now = SDL_GetTicks(); - if (timeout > now) { - const Uint64 ticksleft = timeout - now; - SDL_Delay((Uint32)SDL_min(ticksleft, 300)); /* wait awhile and try again. */ - continue; - } - } - - return WIN_SetErrorFromHRESULT("WASAPI can't find requested audio endpoint", ret); + break; } - return 0; + + return SUCCEEDED(ret) ? 0 : WIN_SetErrorFromHRESULT("WASAPI can't find requested audio endpoint", ret); + } -typedef struct +static void EnumerateEndpointsForFlow(const SDL_bool iscapture, SDL_AudioDevice **default_device) { - LPWSTR devid; - char *devname; - WAVEFORMATEXTENSIBLE fmt; - GUID dsoundguid; -} EndpointItem; - -static int SDLCALL sort_endpoints(const void *_a, const void *_b) -{ - LPWSTR a = ((const EndpointItem *)_a)->devid; - LPWSTR b = ((const EndpointItem *)_b)->devid; - if (!a && !b) { - return 0; - } else if (!a && b) { - return -1; - } else if (a && !b) { - return 1; - } - - while (SDL_TRUE) { - if (*a < *b) { - return -1; - } else if (*a > *b) { - return 1; - } else if (*a == 0) { - break; - } - a++; - b++; - } - - return 0; -} - -static void EnumerateEndpointsForFlow(const SDL_bool iscapture) -{ - IMMDeviceCollection *collection = NULL; - EndpointItem *items; - UINT i, total; - /* Note that WASAPI separates "adapter devices" from "audio endpoint devices" ...one adapter device ("SoundBlaster Pro") might have multiple endpoint devices ("Speakers", "Line-Out"). */ + IMMDeviceCollection *collection = NULL; if (FAILED(IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, iscapture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &collection))) { return; } + UINT total = 0; if (FAILED(IMMDeviceCollection_GetCount(collection, &total))) { IMMDeviceCollection_Release(collection); return; } - items = (EndpointItem *)SDL_calloc(total, sizeof(EndpointItem)); - if (items == NULL) { - return; /* oh well. */ - } - - for (i = 0; i < total; i++) { - EndpointItem *item = items + i; - IMMDevice *device = NULL; - if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &device))) { - if (SUCCEEDED(IMMDevice_GetId(device, &item->devid))) { - GetMMDeviceInfo(device, &item->devname, &item->fmt, &item->dsoundguid); + LPWSTR default_devid = NULL; + if (default_device) { + IMMDevice *default_immdevice = NULL; + const EDataFlow dataflow = iscapture ? eCapture : eRender; + if (SUCCEEDED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_IMMDevice_role, &default_immdevice))) { + LPWSTR devid = NULL; + if (SUCCEEDED(IMMDevice_GetId(default_immdevice, &devid))) { + default_devid = SDL_wcsdup(devid); // if this fails, oh well. + CoTaskMemFree(devid); } - IMMDevice_Release(device); + IMMDevice_Release(default_immdevice); } } - /* sort the list of devices by their guid so list is consistent between runs */ - SDL_qsort(items, total, sizeof(*items), sort_endpoints); - - /* Send the sorted list on to the SDL's higher level. */ - for (i = 0; i < total; i++) { - EndpointItem *item = items + i; - if ((item->devid) && (item->devname)) { - SDL_IMMDevice_Add(iscapture, item->devname, &item->fmt, item->devid, &item->dsoundguid, notification_client.useguid); + for (UINT i = 0; i < total; i++) { + IMMDevice *immdevice = NULL; + if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &immdevice))) { + LPWSTR devid = NULL; + if (SUCCEEDED(IMMDevice_GetId(immdevice, &devid))) { + char *devname = NULL; + WAVEFORMATEXTENSIBLE fmt; + GUID dsoundguid; + SDL_zero(fmt); + SDL_zero(dsoundguid); + GetMMDeviceInfo(immdevice, &devname, &fmt, &dsoundguid); + if (devname) { + SDL_AudioDevice *sdldevice = SDL_IMMDevice_Add(iscapture, devname, &fmt, devid, &dsoundguid); + if (default_device && default_devid && SDL_wcscmp(default_devid, devid)) { + *default_device = sdldevice; + } + SDL_free(devname); + } + CoTaskMemFree(devid); + } + IMMDevice_Release(immdevice); } - SDL_free(item->devname); - CoTaskMemFree(item->devid); } - SDL_free(items); + SDL_free(default_devid); + IMMDeviceCollection_Release(collection); } -void SDL_IMMDevice_EnumerateEndpoints(SDL_bool useguid) +void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture) { - notification_client.useguid = useguid; - - EnumerateEndpointsForFlow(SDL_FALSE); /* playback */ - EnumerateEndpointsForFlow(SDL_TRUE); /* capture */ + EnumerateEndpointsForFlow(SDL_FALSE, default_output); /* playback */ + EnumerateEndpointsForFlow(SDL_TRUE, default_capture); /* capture */ /* if this fails, we just won't get hotplug events. Carry on anyhow. */ IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)¬ification_client); } -int SDL_IMMDevice_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture) -{ - WAVEFORMATEXTENSIBLE fmt; - IMMDevice *device = NULL; - char *filler; - GUID morefiller; - const EDataFlow dataflow = iscapture ? eCapture : eRender; - HRESULT ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_IMMDevice_role, &device); - - if (FAILED(ret)) { - SDL_assert(device == NULL); - return WIN_SetErrorFromHRESULT("WASAPI can't find default audio endpoint", ret); - } - - if (name == NULL) { - name = &filler; - } - - SDL_zero(fmt); - GetMMDeviceInfo(device, name, &fmt, &morefiller); - IMMDevice_Release(device); - - if (name == &filler) { - SDL_free(filler); - } - - SDL_zerop(spec); - spec->channels = (Uint8)fmt.Format.nChannels; - spec->freq = fmt.Format.nSamplesPerSec; - spec->format = WaveFormatToSDLFormat((WAVEFORMATEX *)&fmt); - return 0; -} - SDL_AudioFormat WaveFormatToSDLFormat(WAVEFORMATEX *waveformat) { if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) { diff --git a/src/core/windows/SDL_immdevice.h b/src/core/windows/SDL_immdevice.h index c97402fda1..a5a0a557bb 100644 --- a/src/core/windows/SDL_immdevice.h +++ b/src/core/windows/SDL_immdevice.h @@ -26,16 +26,15 @@ #include #include +typedef struct SDL_AudioDevice SDL_AudioDevice; // this is defined in src/audio/SDL_sysaudio.h + int SDL_IMMDevice_Init(void); void SDL_IMMDevice_Quit(void); -int SDL_IMMDevice_Get(LPCWSTR devid, IMMDevice **device, SDL_bool iscapture); -void SDL_IMMDevice_EnumerateEndpoints(SDL_bool useguid); -int SDL_IMMDevice_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture); - +int SDL_IMMDevice_Get(SDL_AudioDevice *device, IMMDevice **immdevice, SDL_bool iscapture); +void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture); +LPGUID SDL_IMMDevice_GetDirectSoundGUID(SDL_AudioDevice *device); +LPCWSTR SDL_IMMDevice_GetDevID(SDL_AudioDevice *device); +void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device); SDL_AudioFormat WaveFormatToSDLFormat(WAVEFORMATEX *waveformat); -/* these increment as default devices change. Opened default devices pick up changes in their threads. */ -extern SDL_AtomicInt SDL_IMMDevice_DefaultPlaybackGeneration; -extern SDL_AtomicInt SDL_IMMDevice_DefaultCaptureGeneration; - #endif /* SDL_IMMDEVICE_H */