mirror of https://github.com/encounter/SDL.git
wasapi: Handle lost audio device endpoints.
This gracefully recovers when a device format is changed, and will switch to the new default device if the current one is unplugged, etc. This does not handle when a new default device is added; it only notices if the current default goes away. That will be fixed by implementing the stubbed-out MMNotificationClient_OnDefaultDeviceChanged() function.
This commit is contained in:
parent
f2179944cc
commit
c85c57a05d
|
@ -636,12 +636,9 @@ static int SDLCALL
|
||||||
SDL_RunAudio(void *devicep)
|
SDL_RunAudio(void *devicep)
|
||||||
{
|
{
|
||||||
SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
|
SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
|
||||||
const int silence = (int) device->spec.silence;
|
void *udata = device->callbackspec.userdata;
|
||||||
const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq);
|
SDL_AudioCallback callback = device->callbackspec.callback;
|
||||||
const int data_len = device->callbackspec.size;
|
|
||||||
Uint8 *data;
|
Uint8 *data;
|
||||||
void *udata = device->spec.userdata;
|
|
||||||
SDL_AudioCallback callback = device->spec.callback;
|
|
||||||
|
|
||||||
SDL_assert(!device->iscapture);
|
SDL_assert(!device->iscapture);
|
||||||
|
|
||||||
|
@ -654,6 +651,8 @@ SDL_RunAudio(void *devicep)
|
||||||
|
|
||||||
/* Loop, filling the audio buffers */
|
/* Loop, filling the audio buffers */
|
||||||
while (!SDL_AtomicGet(&device->shutdown)) {
|
while (!SDL_AtomicGet(&device->shutdown)) {
|
||||||
|
const int data_len = device->callbackspec.size;
|
||||||
|
|
||||||
/* Fill the current buffer with sound */
|
/* Fill the current buffer with sound */
|
||||||
if (!device->stream && SDL_AtomicGet(&device->enabled)) {
|
if (!device->stream && SDL_AtomicGet(&device->enabled)) {
|
||||||
SDL_assert(data_len == device->spec.size);
|
SDL_assert(data_len == device->spec.size);
|
||||||
|
@ -675,7 +674,7 @@ SDL_RunAudio(void *devicep)
|
||||||
/* !!! FIXME: this should be LockDevice. */
|
/* !!! FIXME: this should be LockDevice. */
|
||||||
SDL_LockMutex(device->mixer_lock);
|
SDL_LockMutex(device->mixer_lock);
|
||||||
if (SDL_AtomicGet(&device->paused)) {
|
if (SDL_AtomicGet(&device->paused)) {
|
||||||
SDL_memset(data, silence, data_len);
|
SDL_memset(data, device->spec.silence, data_len);
|
||||||
} else {
|
} else {
|
||||||
callback(udata, data, data_len);
|
callback(udata, data, data_len);
|
||||||
}
|
}
|
||||||
|
@ -693,6 +692,7 @@ SDL_RunAudio(void *devicep)
|
||||||
SDL_assert((got < 0) || (got == device->spec.size));
|
SDL_assert((got < 0) || (got == device->spec.size));
|
||||||
|
|
||||||
if (data == NULL) { /* device is having issues... */
|
if (data == NULL) { /* device is having issues... */
|
||||||
|
const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq);
|
||||||
SDL_Delay(delay); /* wait for as long as this buffer would have played. Maybe device recovers later? */
|
SDL_Delay(delay); /* wait for as long as this buffer would have played. Maybe device recovers later? */
|
||||||
} else {
|
} else {
|
||||||
if (got != device->spec.size) {
|
if (got != device->spec.size) {
|
||||||
|
@ -704,6 +704,7 @@ SDL_RunAudio(void *devicep)
|
||||||
}
|
}
|
||||||
} else if (data == device->work_buffer) {
|
} else if (data == device->work_buffer) {
|
||||||
/* nothing to do; pause like we queued a buffer to play. */
|
/* nothing to do; pause like we queued a buffer to play. */
|
||||||
|
const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq);
|
||||||
SDL_Delay(delay);
|
SDL_Delay(delay);
|
||||||
} else { /* writing directly to the device. */
|
} else { /* writing directly to the device. */
|
||||||
/* queue this buffer and wait for it to finish playing. */
|
/* queue this buffer and wait for it to finish playing. */
|
||||||
|
|
|
@ -358,10 +358,111 @@ WASAPI_DetectDevices(void)
|
||||||
IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *) ¬ification_client);
|
IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *) ¬ification_client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int PrepWasapiDevice(_THIS, const int iscapture, IMMDevice *device);
|
||||||
|
static void ReleaseWasapiDevice(_THIS);
|
||||||
|
|
||||||
|
static SDL_bool
|
||||||
|
RecoverWasapiDevice(_THIS)
|
||||||
|
{
|
||||||
|
const SDL_AudioSpec oldspec = this->spec;
|
||||||
|
IMMDevice *device = NULL;
|
||||||
|
HRESULT ret = S_OK;
|
||||||
|
|
||||||
|
if (this->hidden->is_default_device) {
|
||||||
|
const EDataFlow dataflow = this->iscapture ? eCapture : eRender;
|
||||||
|
ReleaseWasapiDevice(this); /* dump the lost device's handles. */
|
||||||
|
ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_WASAPI_role, &device);
|
||||||
|
if (FAILED(ret)) {
|
||||||
|
return SDL_FALSE; /* can't find a new default device! */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
device = this->hidden->device;
|
||||||
|
this->hidden->device = NULL; /* don't release this in ReleaseWasapiDevice(). */
|
||||||
|
ReleaseWasapiDevice(this); /* dump the lost device's handles. */
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_assert(device != NULL);
|
||||||
|
|
||||||
|
/* this can fail for lots of reasons, but the most likely is we had a
|
||||||
|
non-default device that was disconnected, so we can't recover. Default
|
||||||
|
devices try to reinitialize whatever the new default is, so it's more
|
||||||
|
likely to carry on here, but this handles a non-default device that
|
||||||
|
simply had its format changed in the Windows Control Panel. */
|
||||||
|
if (PrepWasapiDevice(this, this->iscapture, device) == -1) {
|
||||||
|
return SDL_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Since WASAPI requires us to handle all audio conversion, and our
|
||||||
|
device format might have changed, we might have to add/remove/change
|
||||||
|
the audio stream that the higher level uses to convert data, so
|
||||||
|
SDL keeps firing the callback as if nothing happened here. */
|
||||||
|
|
||||||
|
if ( (this->callbackspec.channels == this->spec.channels) &&
|
||||||
|
(this->callbackspec.format == this->spec.format) &&
|
||||||
|
(this->callbackspec.freq == this->spec.freq) &&
|
||||||
|
(this->callbackspec.samples == this->spec.samples) ) {
|
||||||
|
/* no need to buffer/convert in an AudioStream! */
|
||||||
|
SDL_FreeAudioStream(this->stream);
|
||||||
|
this->stream = NULL;
|
||||||
|
} else if ( (oldspec.channels == this->spec.channels) &&
|
||||||
|
(oldspec.format == this->spec.format) &&
|
||||||
|
(oldspec.freq == this->spec.freq) &&
|
||||||
|
(oldspec.samples == this->spec.samples) ) {
|
||||||
|
/* The existing audio stream is okay to keep using. */
|
||||||
|
} else {
|
||||||
|
/* replace the audiostream for new format */
|
||||||
|
SDL_FreeAudioStream(this->stream);
|
||||||
|
if (this->iscapture) {
|
||||||
|
this->stream = SDL_NewAudioStream(this->spec.format,
|
||||||
|
this->spec.channels, this->spec.freq,
|
||||||
|
this->callbackspec.format,
|
||||||
|
this->callbackspec.channels,
|
||||||
|
this->callbackspec.freq);
|
||||||
|
} else {
|
||||||
|
this->stream = SDL_NewAudioStream(this->callbackspec.format,
|
||||||
|
this->callbackspec.channels,
|
||||||
|
this->callbackspec.freq, this->spec.format,
|
||||||
|
this->spec.channels, this->spec.freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->stream) {
|
||||||
|
return SDL_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make sure our scratch buffer can cover the new device spec. */
|
||||||
|
if (this->spec.size > this->work_buffer_len) {
|
||||||
|
Uint8 *ptr = (Uint8 *) SDL_realloc(this->work_buffer, this->spec.size);
|
||||||
|
if (ptr == NULL) {
|
||||||
|
SDL_OutOfMemory();
|
||||||
|
return SDL_FALSE;
|
||||||
|
}
|
||||||
|
this->work_buffer = ptr;
|
||||||
|
this->work_buffer_len = this->spec.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDL_TRUE; /* okay, carry on with new device details! */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static SDL_bool
|
||||||
|
TryWasapiAgain(_THIS, const HRESULT err)
|
||||||
|
{
|
||||||
|
SDL_bool retval = SDL_FALSE;
|
||||||
|
if (err == AUDCLNT_E_DEVICE_INVALIDATED) {
|
||||||
|
if (SDL_AtomicGet(&this->enabled)) {
|
||||||
|
retval = RecoverWasapiDevice(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
WASAPI_GetPendingBytes(_THIS)
|
WASAPI_GetPendingBytes(_THIS)
|
||||||
{
|
{
|
||||||
UINT32 frames = 0;
|
UINT32 frames = 0;
|
||||||
|
|
||||||
|
/* it's okay to fail with AUDCLNT_E_DEVICE_INVALIDATED; we'll try to recover lost devices in the audio thread. */
|
||||||
if (FAILED(IAudioClient_GetCurrentPadding(this->hidden->client, &frames))) {
|
if (FAILED(IAudioClient_GetCurrentPadding(this->hidden->client, &frames))) {
|
||||||
return 0; /* oh well. */
|
return 0; /* oh well. */
|
||||||
}
|
}
|
||||||
|
@ -374,7 +475,13 @@ WASAPI_GetDeviceBuf(_THIS)
|
||||||
{
|
{
|
||||||
/* get an endpoint buffer from WASAPI. */
|
/* get an endpoint buffer from WASAPI. */
|
||||||
BYTE *buffer = NULL;
|
BYTE *buffer = NULL;
|
||||||
if (FAILED(IAudioRenderClient_GetBuffer(this->hidden->render, this->spec.samples, &buffer))) {
|
HRESULT ret;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = IAudioRenderClient_GetBuffer(this->hidden->render, this->spec.samples, &buffer);
|
||||||
|
} while (TryWasapiAgain(this, ret));
|
||||||
|
|
||||||
|
if (FAILED(ret)) {
|
||||||
IAudioClient_Stop(this->hidden->client);
|
IAudioClient_Stop(this->hidden->client);
|
||||||
SDL_OpenedAudioDeviceDisconnected(this); /* uhoh. */
|
SDL_OpenedAudioDeviceDisconnected(this); /* uhoh. */
|
||||||
SDL_assert(buffer == NULL);
|
SDL_assert(buffer == NULL);
|
||||||
|
@ -385,13 +492,15 @@ WASAPI_GetDeviceBuf(_THIS)
|
||||||
static void
|
static void
|
||||||
WASAPI_PlayDevice(_THIS)
|
WASAPI_PlayDevice(_THIS)
|
||||||
{
|
{
|
||||||
if (SDL_AtomicGet(&this->enabled)) { /* not shutting down? */
|
HRESULT ret = IAudioRenderClient_ReleaseBuffer(this->hidden->render, this->spec.samples, 0);
|
||||||
if (FAILED(IAudioRenderClient_ReleaseBuffer(this->hidden->render, this->spec.samples, 0))) {
|
if (ret == AUDCLNT_E_DEVICE_INVALIDATED) {
|
||||||
|
ret = S_OK; /* it's okay if we lost the device here. Catch it later. */
|
||||||
|
}
|
||||||
|
if (FAILED(ret)) {
|
||||||
IAudioClient_Stop(this->hidden->client);
|
IAudioClient_Stop(this->hidden->client);
|
||||||
SDL_OpenedAudioDeviceDisconnected(this); /* uhoh. */
|
SDL_OpenedAudioDeviceDisconnected(this); /* uhoh. */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
WASAPI_WaitDevice(_THIS)
|
WASAPI_WaitDevice(_THIS)
|
||||||
|
@ -399,7 +508,13 @@ WASAPI_WaitDevice(_THIS)
|
||||||
const UINT32 maxpadding = this->spec.samples;
|
const UINT32 maxpadding = this->spec.samples;
|
||||||
while (SDL_AtomicGet(&this->enabled)) {
|
while (SDL_AtomicGet(&this->enabled)) {
|
||||||
UINT32 padding = 0;
|
UINT32 padding = 0;
|
||||||
if (FAILED(IAudioClient_GetCurrentPadding(this->hidden->client, &padding))) {
|
HRESULT ret;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = IAudioClient_GetCurrentPadding(this->hidden->client, &padding);
|
||||||
|
} while (TryWasapiAgain(this, ret));
|
||||||
|
|
||||||
|
if (FAILED(ret)) {
|
||||||
IAudioClient_Stop(this->hidden->client);
|
IAudioClient_Stop(this->hidden->client);
|
||||||
SDL_OpenedAudioDeviceDisconnected(this);
|
SDL_OpenedAudioDeviceDisconnected(this);
|
||||||
}
|
}
|
||||||
|
@ -430,7 +545,10 @@ WASAPI_CaptureFromDevice(_THIS, void *buffer, int buflen)
|
||||||
UINT32 frames = 0;
|
UINT32 frames = 0;
|
||||||
DWORD flags = 0;
|
DWORD flags = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
|
ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
|
||||||
|
} while (TryWasapiAgain(this, ret));
|
||||||
|
|
||||||
if ((ret == AUDCLNT_S_BUFFER_EMPTY) || !frames) {
|
if ((ret == AUDCLNT_S_BUFFER_EMPTY) || !frames) {
|
||||||
WASAPI_WaitDevice(this);
|
WASAPI_WaitDevice(this);
|
||||||
} else if (ret == S_OK) {
|
} else if (ret == S_OK) {
|
||||||
|
@ -475,6 +593,7 @@ WASAPI_FlushCapture(_THIS)
|
||||||
DWORD flags = 0;
|
DWORD flags = 0;
|
||||||
HRESULT ret;
|
HRESULT ret;
|
||||||
/* just read until we stop getting packets, throwing them away. */
|
/* just read until we stop getting packets, throwing them away. */
|
||||||
|
/* We don't care if we fail with AUDCLNT_E_DEVICE_INVALIDATED here; lost devices will be handled elsewhere. */
|
||||||
while ((ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags, NULL, NULL)) == S_OK) {
|
while ((ret = IAudioCaptureClient_GetBuffer(this->hidden->capture, &ptr, &frames, &flags, NULL, NULL)) == S_OK) {
|
||||||
IAudioCaptureClient_ReleaseBuffer(this->hidden->capture, frames);
|
IAudioCaptureClient_ReleaseBuffer(this->hidden->capture, frames);
|
||||||
}
|
}
|
||||||
|
@ -482,42 +601,53 @@ WASAPI_FlushCapture(_THIS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ReleaseWasapiDevice(_THIS)
|
||||||
|
{
|
||||||
|
if (this->hidden->client) {
|
||||||
|
IAudioClient_Stop(this->hidden->client);
|
||||||
|
this->hidden->client = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->hidden->render) {
|
||||||
|
IAudioRenderClient_Release(this->hidden->render);
|
||||||
|
this->hidden->render = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->hidden->capture) {
|
||||||
|
IAudioCaptureClient_Release(this->hidden->capture);
|
||||||
|
this->hidden->client = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->hidden->waveformat) {
|
||||||
|
CoTaskMemFree(this->hidden->waveformat);
|
||||||
|
this->hidden->waveformat = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->hidden->device) {
|
||||||
|
IMMDevice_Release(this->hidden->device);
|
||||||
|
this->hidden->device = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->hidden->capturestream) {
|
||||||
|
SDL_FreeAudioStream(this->hidden->capturestream);
|
||||||
|
this->hidden->capturestream = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
WASAPI_CloseDevice(_THIS)
|
WASAPI_CloseDevice(_THIS)
|
||||||
{
|
{
|
||||||
/* don't touch this->hidden->task in here; it has to be reverted from
|
/* don't touch this->hidden->task in here; it has to be reverted from
|
||||||
our callback thread. We do that in WASAPI_ThreadDeinit().
|
our callback thread. We do that in WASAPI_ThreadDeinit().
|
||||||
(likewise for this->hidden->coinitialized). */
|
(likewise for this->hidden->coinitialized). */
|
||||||
|
ReleaseWasapiDevice(this);
|
||||||
if (this->hidden->client) {
|
|
||||||
IAudioClient_Stop(this->hidden->client);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->hidden->render) {
|
|
||||||
IAudioRenderClient_Release(this->hidden->render);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->hidden->client) {
|
|
||||||
IAudioClient_Release(this->hidden->client);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->hidden->waveformat) {
|
|
||||||
CoTaskMemFree(this->hidden->waveformat);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->hidden->device) {
|
|
||||||
IMMDevice_Release(this->hidden->device);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->hidden->capturestream) {
|
|
||||||
SDL_FreeAudioStream(this->hidden->capturestream);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_free(this->hidden);
|
SDL_free(this->hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
WASAPI_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
|
PrepWasapiDevice(_THIS, const int iscapture, IMMDevice *device)
|
||||||
{
|
{
|
||||||
/* !!! FIXME: we could request an exclusive mode stream, which is lower latency;
|
/* !!! FIXME: we could request an exclusive mode stream, which is lower latency;
|
||||||
!!! it will write into the kernel's audio buffer directly instead of
|
!!! it will write into the kernel's audio buffer directly instead of
|
||||||
|
@ -531,10 +661,8 @@ WASAPI_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
|
||||||
!!! some point. To be sure, defaulting to shared mode is the right thing to
|
!!! some point. To be sure, defaulting to shared mode is the right thing to
|
||||||
!!! do in any case. */
|
!!! do in any case. */
|
||||||
const AUDCLNT_SHAREMODE sharemode = AUDCLNT_SHAREMODE_SHARED;
|
const AUDCLNT_SHAREMODE sharemode = AUDCLNT_SHAREMODE_SHARED;
|
||||||
const EDataFlow dataflow = iscapture ? eCapture : eRender;
|
|
||||||
UINT32 bufsize = 0; /* this is in sample frames, not samples, not bytes. */
|
UINT32 bufsize = 0; /* this is in sample frames, not samples, not bytes. */
|
||||||
REFERENCE_TIME duration = 0;
|
REFERENCE_TIME duration = 0;
|
||||||
IMMDevice *device = NULL;
|
|
||||||
IAudioClient *client = NULL;
|
IAudioClient *client = NULL;
|
||||||
IAudioRenderClient *render = NULL;
|
IAudioRenderClient *render = NULL;
|
||||||
IAudioCaptureClient *capture = NULL;
|
IAudioCaptureClient *capture = NULL;
|
||||||
|
@ -544,25 +672,6 @@ WASAPI_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
|
||||||
SDL_bool valid_format = SDL_FALSE;
|
SDL_bool valid_format = SDL_FALSE;
|
||||||
HRESULT ret = S_OK;
|
HRESULT ret = S_OK;
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
if (this->hidden == NULL) {
|
|
||||||
return SDL_OutOfMemory();
|
|
||||||
}
|
|
||||||
SDL_zerop(this->hidden);
|
|
||||||
|
|
||||||
if (handle == NULL) {
|
|
||||||
ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_WASAPI_role, &device);
|
|
||||||
} else {
|
|
||||||
ret = IMMDeviceEnumerator_GetDevice(enumerator, (LPCWSTR) handle, &device);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(ret)) {
|
|
||||||
return WIN_SetErrorFromHRESULT("WASAPI can't find requested audio endpoint", ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_assert(device != NULL);
|
|
||||||
this->hidden->device = device;
|
this->hidden->device = device;
|
||||||
|
|
||||||
ret = IMMDevice_Activate(device, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **) &client);
|
ret = IMMDevice_Activate(device, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **) &client);
|
||||||
|
@ -677,6 +786,38 @@ WASAPI_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
|
||||||
return 0; /* good to go. */
|
return 0; /* good to go. */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
WASAPI_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
|
||||||
|
{
|
||||||
|
const EDataFlow dataflow = iscapture ? eCapture : eRender;
|
||||||
|
const SDL_bool is_default_device = (handle == NULL);
|
||||||
|
IMMDevice *device = NULL;
|
||||||
|
HRESULT ret = S_OK;
|
||||||
|
|
||||||
|
/* Initialize all variables that we clean on shutdown */
|
||||||
|
this->hidden = (struct SDL_PrivateAudioData *)
|
||||||
|
SDL_malloc((sizeof *this->hidden));
|
||||||
|
if (this->hidden == NULL) {
|
||||||
|
return SDL_OutOfMemory();
|
||||||
|
}
|
||||||
|
SDL_zerop(this->hidden);
|
||||||
|
|
||||||
|
this->hidden->is_default_device = is_default_device;
|
||||||
|
|
||||||
|
if (is_default_device) {
|
||||||
|
ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_WASAPI_role, &device);
|
||||||
|
} else {
|
||||||
|
ret = IMMDeviceEnumerator_GetDevice(enumerator, (LPCWSTR) handle, &device);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED(ret)) {
|
||||||
|
return WIN_SetErrorFromHRESULT("WASAPI can't find requested audio endpoint", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_assert(device != NULL);
|
||||||
|
return PrepWasapiDevice(this, iscapture, device);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
WASAPI_ThreadInit(_THIS)
|
WASAPI_ThreadInit(_THIS)
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,6 +39,7 @@ struct SDL_PrivateAudioData
|
||||||
HANDLE task;
|
HANDLE task;
|
||||||
SDL_bool coinitialized;
|
SDL_bool coinitialized;
|
||||||
int framesize;
|
int framesize;
|
||||||
|
SDL_bool is_default_device;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* SDL_wasapi_h_ */
|
#endif /* SDL_wasapi_h_ */
|
||||||
|
|
Loading…
Reference in New Issue