From 67e8522d31b875874ffd24086678ee6c861ff91c Mon Sep 17 00:00:00 2001
From: Ethan Lee <[EMAIL REDACTED]>
Date: Sat, 27 Feb 2021 17:37:25 -0500
Subject: [PATCH] Add SDL_GetAudioDeviceSpec.
This API is supported by pipewire, pulseaudio, coreaudio, wasapi, and disk.
---
include/SDL_audio.h | 19 ++++++++
src/audio/SDL_audio.c | 61 +++++++++++++++++++++----
src/audio/SDL_audiodev.c | 8 +++-
src/audio/SDL_sysaudio.h | 3 +-
src/audio/alsa/SDL_alsa_audio.c | 6 ++-
src/audio/coreaudio/SDL_coreaudio.m | 33 ++++++++-----
src/audio/directsound/SDL_directsound.c | 7 ++-
src/audio/disk/SDL_diskaudio.c | 4 +-
src/audio/os2/SDL_os2audio.c | 4 +-
src/audio/pipewire/SDL_pipewire.c | 4 +-
src/audio/pulseaudio/SDL_pulseaudio.c | 47 ++++++++++++++++++-
src/audio/qsa/SDL_qsa_audio.c | 12 ++++-
src/audio/wasapi/SDL_wasapi.c | 49 ++++++++++++--------
src/audio/wasapi/SDL_wasapi.h | 2 +-
src/audio/wasapi/SDL_wasapi_win32.c | 26 +++++++----
src/audio/wasapi/SDL_wasapi_winrt.cpp | 27 +++++++++--
src/audio/winmm/SDL_winmm.c | 9 +++-
src/dynapi/SDL_dynapi_overrides.h | 1 +
src/dynapi/SDL_dynapi_procs.h | 1 +
19 files changed, 256 insertions(+), 67 deletions(-)
diff --git a/include/SDL_audio.h b/include/SDL_audio.h
index 46f03164e..46fefe2e2 100644
--- a/include/SDL_audio.h
+++ b/include/SDL_audio.h
@@ -359,6 +359,25 @@ extern DECLSPEC int SDLCALL SDL_GetNumAudioDevices(int iscapture);
extern DECLSPEC const char *SDLCALL SDL_GetAudioDeviceName(int index,
int iscapture);
+/**
+ * Get the audio format of a specific audio device.
+ * Must be a value between 0 and (number of audio devices-1).
+ * Only valid after a successfully initializing the audio subsystem.
+ * The values returned by this function reflect the latest call to
+ * SDL_GetNumAudioDevices(); recall that function to redetect available
+ * hardware.
+ *
+ * The spec will be filled with the sample rate, sample format, and channel
+ * count. All other values in the structure are filled with 0. When the
+ * supported struct members are 0, SDL was unable to get the property from the
+ * backend.
+ *
+ * \return 0 on success, nonzero on error
+ */
+extern DECLSPEC int SDLCALL SDL_GetAudioDeviceSpec(int index,
+ int iscapture,
+ SDL_AudioSpec *spec);
+
/**
* Open a specific audio device. Passing in a device name of NULL requests
diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index e0d6d450a..725e169ce 100644
--- a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -223,9 +223,9 @@ SDL_AudioDetectDevices_Default(void)
SDL_assert(current_audio.impl.OnlyHasDefaultOutputDevice);
SDL_assert(current_audio.impl.OnlyHasDefaultCaptureDevice || !current_audio.impl.HasCaptureSupport);
- SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, (void *) ((size_t) 0x1));
+ SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *) ((size_t) 0x1));
if (current_audio.impl.HasCaptureSupport) {
- SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, (void *) ((size_t) 0x2));
+ SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *) ((size_t) 0x2));
}
}
@@ -377,7 +377,7 @@ finish_audio_entry_points_init(void)
/* device hotplug support... */
static int
-add_audio_device(const char *name, void *handle, SDL_AudioDeviceItem **devices, int *devCount)
+add_audio_device(const char *name, SDL_AudioSpec *spec, void *handle, SDL_AudioDeviceItem **devices, int *devCount)
{
int retval = -1;
SDL_AudioDeviceItem *item;
@@ -400,6 +400,11 @@ add_audio_device(const char *name, void *handle, SDL_AudioDeviceItem **devices,
item->dupenum = 0;
item->name = item->original_name;
+ if (spec != NULL) {
+ SDL_memcpy(&item->spec, spec, sizeof(SDL_AudioSpec));
+ } else {
+ SDL_zero(item->spec);
+ }
item->handle = handle;
SDL_LockMutex(current_audio.detectionLock);
@@ -437,16 +442,16 @@ add_audio_device(const char *name, void *handle, SDL_AudioDeviceItem **devices,
}
static SDL_INLINE int
-add_capture_device(const char *name, void *handle)
+add_capture_device(const char *name, SDL_AudioSpec *spec, void *handle)
{
SDL_assert(current_audio.impl.HasCaptureSupport);
- return add_audio_device(name, handle, ¤t_audio.inputDevices, ¤t_audio.inputDeviceCount);
+ return add_audio_device(name, spec, handle, ¤t_audio.inputDevices, ¤t_audio.inputDeviceCount);
}
static SDL_INLINE int
-add_output_device(const char *name, void *handle)
+add_output_device(const char *name, SDL_AudioSpec *spec, void *handle)
{
- return add_audio_device(name, handle, ¤t_audio.outputDevices, ¤t_audio.outputDeviceCount);
+ return add_audio_device(name, spec, handle, ¤t_audio.outputDevices, ¤t_audio.outputDeviceCount);
}
static void
@@ -472,9 +477,9 @@ free_device_list(SDL_AudioDeviceItem **devices, int *devCount)
/* The audio backends call this when a new device is plugged in. */
void
-SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
+SDL_AddAudioDevice(const int iscapture, const char *name, SDL_AudioSpec *spec, void *handle)
{
- const int device_index = iscapture ? add_capture_device(name, handle) : add_output_device(name, handle);
+ const int device_index = iscapture ? add_capture_device(name, spec, handle) : add_output_device(name, spec, handle);
if (device_index != -1) {
/* Post the event, if desired */
if (SDL_GetEventState(SDL_AUDIODEVICEADDED) == SDL_ENABLE) {
@@ -1112,6 +1117,44 @@ SDL_GetAudioDeviceName(int index, int iscapture)
}
+int
+SDL_GetAudioDeviceSpec(int index, int iscapture, SDL_AudioSpec *spec)
+{
+ if (spec == NULL) {
+ return SDL_InvalidParamError("spec");
+ }
+
+ SDL_zerop(spec);
+
+ if (!SDL_WasInit(SDL_INIT_AUDIO)) {
+ return SDL_SetError("Audio subsystem is not initialized");
+ }
+
+ if (iscapture && !current_audio.impl.HasCaptureSupport) {
+ return SDL_SetError("No capture support");
+ }
+
+ if (index >= 0) {
+ SDL_AudioDeviceItem *item;
+ int i;
+
+ SDL_LockMutex(current_audio.detectionLock);
+ item = iscapture ? current_audio.inputDevices : current_audio.outputDevices;
+ i = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount;
+ if (index < i) {
+ for (i--; i > index; i--, item = item->next) {
+ SDL_assert(item != NULL);
+ }
+ SDL_assert(item != NULL);
+ SDL_memcpy(spec, &item->spec, sizeof(SDL_AudioSpec));
+ }
+ SDL_UnlockMutex(current_audio.detectionLock);
+ }
+
+ return 0;
+}
+
+
static void
close_audio_device(SDL_AudioDevice * device)
{
diff --git a/src/audio/SDL_audiodev.c b/src/audio/SDL_audiodev.c
index 78c85b55c..42364027c 100644
--- a/src/audio/SDL_audiodev.c
+++ b/src/audio/SDL_audiodev.c
@@ -59,7 +59,13 @@ test_device(const int iscapture, const char *fname, int flags, int (*test) (int
static size_t dummyhandle = 0;
dummyhandle++;
SDL_assert(dummyhandle != 0);
- SDL_AddAudioDevice(iscapture, fname, (void *) dummyhandle);
+
+ /* Note that spec is NULL; while we are opening the device
+ * endpoint here, the endpoint does not provide any mix format
+ * information, making this information inaccessible at
+ * enumeration time
+ */
+ SDL_AddAudioDevice(iscapture, fname, NULL, (void *) dummyhandle);
}
}
}
diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h
index ad9a164a9..0c66ec657 100644
--- a/src/audio/SDL_sysaudio.h
+++ b/src/audio/SDL_sysaudio.h
@@ -39,7 +39,7 @@ typedef struct SDL_AudioDevice SDL_AudioDevice;
/* Audio targets should call this as devices are added to the system (such as
a USB headset being plugged in), and should also be called for
for every device found during DetectDevices(). */
-extern void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle);
+extern void SDL_AddAudioDevice(const int iscapture, const char *name, SDL_AudioSpec *spec, void *handle);
/* Audio targets should call this as devices are removed, so SDL can update
its list of available devices. */
@@ -99,6 +99,7 @@ typedef struct SDL_AudioDeviceItem
void *handle;
char *name;
char *original_name;
+ SDL_AudioSpec spec;
int dupenum;
struct SDL_AudioDeviceItem *next;
} SDL_AudioDeviceItem;
diff --git a/src/audio/alsa/SDL_alsa_audio.c b/src/audio/alsa/SDL_alsa_audio.c
index 547855b75..7a6b7344b 100644
--- a/src/audio/alsa/SDL_alsa_audio.c
+++ b/src/audio/alsa/SDL_alsa_audio.c
@@ -765,7 +765,11 @@ add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSee
return;
}
- SDL_AddAudioDevice(iscapture, desc, handle);
+ /* Note that spec is NULL, because we are required to open the device before
+ * acquiring the mix format, making this information inaccessible at
+ * enumeration time
+ */
+ SDL_AddAudioDevice(iscapture, desc, NULL, handle);
if (hint)
free(desc);
dev->name = handle;
diff --git a/src/audio/coreaudio/SDL_coreaudio.m b/src/audio/coreaudio/SDL_coreaudio.m
index f438fcda3..f84a1c558 100644
--- a/src/audio/coreaudio/SDL_coreaudio.m
+++ b/src/audio/coreaudio/SDL_coreaudio.m
@@ -56,7 +56,7 @@
kAudioObjectPropertyElementMaster
};
-typedef void (*addDevFn)(const char *name, const int iscapture, AudioDeviceID devId, void *data);
+typedef void (*addDevFn)(const char *name, SDL_AudioSpec *spec, const int iscapture, AudioDeviceID devId, void *data);
typedef struct AudioDeviceList
{
@@ -88,10 +88,10 @@
}
static void
-addToDevList(const char *name, const int iscapture, AudioDeviceID devId, void *data)
+addToDevList(const char *name, SDL_AudioSpec *spec, const int iscapture, AudioDeviceID devId, void *data)
{
if (add_to_internal_dev_list(iscapture, devId)) {
- SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
+ SDL_AddAudioDevice(iscapture, name, spec, (void *) ((size_t) devId));
}
}
@@ -126,17 +126,23 @@
AudioBufferList *buflist = NULL;
int usable = 0;
CFIndex len = 0;
+ double sampleRate = 0;
+ SDL_AudioSpec spec;
const AudioObjectPropertyAddress addr = {
kAudioDevicePropertyStreamConfiguration,
iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
};
-
const AudioObjectPropertyAddress nameaddr = {
kAudioObjectPropertyName,
iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
};
+ const AudioObjectPropertyAddress freqaddr = {
+ kAudioDevicePropertyNominalSampleRate,
+ iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMaster
+ };
result = AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size);
if (result != noErr)
@@ -149,21 +155,24 @@
result = AudioObjectGetPropertyData(dev, &addr, 0, NULL,
&size, buflist);
+ SDL_zero(spec);
if (result == noErr) {
UInt32 j;
for (j = 0; j < buflist->mNumberBuffers; j++) {
- if (buflist->mBuffers[j].mNumberChannels > 0) {
- usable = 1;
- break;
- }
+ spec.channels += buflist->mBuffers[j].mNumberChannels;
}
}
SDL_free(buflist);
- if (!usable)
+ if (spec.channels == 0)
continue;
+ size = sizeof (sampleRate);
+ result = AudioObjectGetPropertyData(dev, &freqaddr, 0, NULL, &size, &sampleRate);
+ if (result == noErr) {
+ spec.freq = (int) sampleRate;
+ }
size = sizeof (CFStringRef);
result = AudioObjectGetPropertyData(dev, &nameaddr, 0, NULL, &size, &cfstr);
@@ -197,7 +206,7 @@
((iscapture) ? "capture" : "output"),
(int) i, ptr, (int) dev);
#endif
- addfn(ptr, iscapture, dev, addfndata);
+ addfn(ptr, &spec, iscapture, dev, addfndata);
}
SDL_free(ptr); /* addfn() would have copied the string. */
}
@@ -223,7 +232,7 @@
}
static void
-build_device_change_list(const char *name, const int iscapture, AudioDeviceID devId, void *data)
+build_device_change_list(const char *name, SDL_AudioSpec *spec, const int iscapture, AudioDeviceID devId, void *data)
{
AudioDeviceList **list = (AudioDeviceList **) data;
AudioDeviceList *item;
@@ -235,7 +244,7 @@
}
add_to_internal_dev_list(iscapture, devId); /* new device, add it. */
- SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
+ SDL_AddAudioDevice(iscapture, name, spec, (void *) ((size_t) devId));
}
static void
diff --git a/src/audio/directsound/SDL_directsound.c b/src/audio/directsound/SDL_directsound.c
index 008565297..700a40084 100644
--- a/src/audio/directsound/SDL_directsound.c
+++ b/src/audio/directsound/SDL_directsound.c
@@ -163,7 +163,12 @@ FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID data)
if (str != NULL) {
LPGUID cpyguid = (LPGUID) SDL_malloc(sizeof (GUID));
SDL_memcpy(cpyguid, guid, sizeof (GUID));
- SDL_AddAudioDevice(iscapture, str, cpyguid);
+
+ /* 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. */
}
}
diff --git a/src/audio/disk/SDL_diskaudio.c b/src/audio/disk/SDL_diskaudio.c
index 8a2ef491f..5b2fa145f 100644
--- a/src/audio/disk/SDL_diskaudio.c
+++ b/src/audio/disk/SDL_diskaudio.c
@@ -173,8 +173,8 @@ DISKAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
static void
DISKAUDIO_DetectDevices(void)
{
- SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, (void *) 0x1);
- SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, (void *) 0x2);
+ SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *) 0x1);
+ SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *) 0x2);
}
static int
diff --git a/src/audio/os2/SDL_os2audio.c b/src/audio/os2/SDL_os2audio.c
index abcbc1ca5..9369eaf2d 100644
--- a/src/audio/os2/SDL_os2audio.c
+++ b/src/audio/os2/SDL_os2audio.c
@@ -160,9 +160,9 @@ static void OS2_DetectDevices(void)
}
ulHandle++;
- SDL_AddAudioDevice(0, stLogDevice.szProductInfo, (void *)(ulHandle));
+ SDL_AddAudioDevice(0, stLogDevice.szProductInfo, NULL, (void *)(ulHandle));
ulHandle++;
- SDL_AddAudioDevice(1, stLogDevice.szProductInfo, (void *)(ulHandle));
+ SDL_AddAudioDevice(1, stLogDevice.szProductInfo, NULL, (void *)(ulHandle));
}
}
diff --git a/src/audio/pipewire/SDL_pipewire.c b/src/audio/pipewire/SDL_pipewire.c
index 637b02607..a073f8f1f 100644
--- a/src/audio/pipewire/SDL_pipewire.c
+++ b/src/audio/pipewire/SDL_pipewire.c
@@ -266,7 +266,7 @@ io_list_check_add(struct io_node *node)
spa_list_append(&hotplug_io_list, &node->link);
if (SDL_AtomicGet(&hotplug_events_enabled)) {
- SDL_AddAudioDevice(node->is_capture, node->name, PW_ID_TO_HANDLE(node->id));
+ SDL_AddAudioDevice(node->is_capture, node->name, &node->spec, PW_ID_TO_HANDLE(node->id));
}
dup_found:
@@ -768,7 +768,7 @@ PIPEWIRE_DetectDevices()
io_list_sort();
spa_list_for_each (io, &hotplug_io_list, link) {
- SDL_AddAudioDevice(io->is_capture, io->name, PW_ID_TO_HANDLE(io->id));
+ SDL_AddAudioDevice(io->is_capture, io->name, &io->spec, PW_ID_TO_HANDLE(io->id));
}
SDL_AtomicSet(&hotplug_events_enabled, 1);
diff --git a/src/audio/pulseaudio/SDL_pulseaudio.c b/src/audio/pulseaudio/SDL_pulseaudio.c
index 25dba67d3..bb5997ed1 100644
--- a/src/audio/pulseaudio/SDL_pulseaudio.c
+++ b/src/audio/pulseaudio/SDL_pulseaudio.c
@@ -696,12 +696,45 @@ static SDL_Thread *hotplug_thread = NULL;
/* device handles are device index + 1, cast to void*, so we never pass a NULL. */
+static SDL_AudioFormat
+PulseFormatToSDLFormat(pa_sample_format_t format)
+{
+ switch (format) {
+ case PA_SAMPLE_U8:
+ return AUDIO_U8;
+ case PA_SAMPLE_S16LE:
+ return AUDIO_S16LSB;
+ case PA_SAMPLE_S16BE:
+ return AUDIO_S16MSB;
+ case PA_SAMPLE_S32LE:
+ return AUDIO_S32LSB;
+ case PA_SAMPLE_S32BE:
+ return AUDIO_S32MSB;
+ case PA_SAMPLE_FLOAT32LE:
+ return AUDIO_F32LSB;
+ case PA_SAMPLE_FLOAT32BE:
+ return AUDIO_F32MSB;
+ default:
+ return 0;
+ }
+}
+
/* This is called when PulseAudio adds an output ("sink") device. */
static void
SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
{
+ SDL_AudioSpec spec;
if (i) {
- SDL_AddAudioDevice(SDL_FALSE, i->description, (void *) ((size_t) i->index+1));
+ spec.freq = i->sample_spec.rate;
+ spec.channels = i->sample_spec.channels;
+ spec.format = PulseFormatToSDLFormat(i->sample_spec.format);
+ spec.silence = 0;
+ spec.samples = 0;
+ spec.size = 0;
+ spec.callback = NULL;
+ spec.userdata = NULL;
+
+ SDL_AddAudioDevice(SDL_FALSE, i->description, &spec, (void *) ((size_t) i->index+1));
}
}
@@ -709,10 +742,20 @@ SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
static void
SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
{
+ SDL_AudioSpec spec;
if (i) {
/* Skip "monitor" sources. These are just output from other sinks. */
if (i->monitor_of_sink == PA_INVALID_INDEX) {
- SDL_AddAudioDevice(SDL_TRUE, i->description, (void *) ((size_t) i->index+1));
+ spec.freq = i->sample_spec.rate;
+ spec.channels = i->sample_spec.channels;
+ spec.format = PulseFormatToSDLFormat(i->sample_spec.format);
+ spec.silence = 0;
+ spec.samples = 0;
+ spec.size = 0;
+ spec.callback = NULL;
+ spec.userdata = NULL;
+
+ SDL_AddAudioDevice(SDL_TRUE, i->description, &spec, (void *) ((size_t) i->index+1));
}
}
}
diff --git a/src/audio/qsa/SDL_qsa_audio.c b/src/audio/qsa/SDL_qsa_audio.c
index b50bfd700..cb95551f9 100644
--- a/src/audio/qsa/SDL_qsa_audio.c
+++ b/src/audio/qsa/SDL_qsa_audio.c
@@ -528,7 +528,11 @@ QSA_DetectDevices(void)
devices;
status = snd_pcm_close(handle);
if (status == EOK) {
- SDL_AddAudioDevice(SDL_FALSE, qsa_playback_device[qsa_playback_devices].name, &qsa_playback_device[qsa_playback_devices]);
+ /* Note that spec is NULL, because we are required to open the device before
+ * acquiring the mix format, making this information inaccessible at
+ * enumeration time
+ */
+ SDL_AddAudioDevice(SDL_FALSE, qsa_playback_device[qsa_playback_devices].name, NULL, &qsa_playback_device[qsa_playback_devices]);
qsa_playback_devices++;
}
} else {
@@ -586,7 +590,11 @@ QSA_DetectDevices(void)
devices;
status = snd_pcm_close(handle);
if (status == EOK) {
- SDL_AddAudioDevice(SDL_TRUE, qsa_capture_device[qsa_capture_devices].name, &qsa_capture_device[qsa_capture_devices]);
+ /* Note that spec is NULL, because we are required to open the device before
+ * acquiring the mix format, making this information inaccessible at
+ * enumeration time
+ */
+ SDL_AddAudioDevice(SDL_TRUE, qsa_capture_device[qsa_capture_devices].name, NULL, &qsa_capture_device[qsa_capture_devices]);
qsa_capture_devices++;
}
} else {
diff --git a/src/audio/wasapi/SDL_wasapi.c b/src/audio/wasapi/SDL_wasapi.c
index 8f7f7374c..3f86c801b 100644
--- a/src/audio/wasapi/SDL_wasapi.c
+++ b/src/audio/wasapi/SDL_wasapi.c
@@ -117,10 +117,34 @@ WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid)
}
}
+static SDL_AudioFormat
+WaveFormatToSDLFormat(WAVEFORMATEX *waveformat)
+{
+ if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
+ return AUDIO_F32SYS;
+ } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
+ return AUDIO_S16SYS;
+ } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
+ return AUDIO_S32SYS;
+ } else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
+ const WAVEFORMATEXTENSIBLE *ext = (const WAVEFORMATEXTENSIBLE *) waveformat;
+ if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
+ return AUDIO_F32SYS;
+ } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
+ return AUDIO_S16SYS;
+ } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
+ return AUDIO_S32SYS;
+ }
+ }
+ SDL_assert(0 && "Unrecognized wFormatTag!");
+ return 0;
+}
+
void
-WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, LPCWSTR devid)
+WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid)
{
DevIdList *devidlist;
+ SDL_AudioSpec spec;
/* 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
@@ -149,7 +173,11 @@ WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, LPCWSTR devid)
devidlist->next = deviceid_list;
deviceid_list = devidlist;
- SDL_AddAudioDevice(iscapture, devname, (void *) devid);
+ SDL_zero(spec);
+ spec.channels = fmt->Format.nChannels;
+ spec.freq = fmt->Format.nSamplesPerSec;
+ spec.format = WaveFormatToSDLFormat((WAVEFORMATEX *) fmt);
+ SDL_AddAudioDevice(iscapture, devname, &spec, (void *) devid);
}
static void
@@ -539,22 +567,7 @@ WASAPI_PrepDevice(_THIS, const SDL_bool updatestream)
this->spec.channels = (Uint8) waveformat->nChannels;
/* Make sure we have a valid format that we can convert to whatever WASAPI wants. */
- if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
- wasapi_format = AUDIO_F32SYS;
- } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
- wasapi_format = AUDIO_S16SYS;
- } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
- wasapi_format = AUDIO_S32SYS;
- } else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
- const WAVEFORMATEXTENSIBLE *ext = (const WAVEFORMATEXTENSIBLE *) waveformat;
- if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
- wasapi_format = AUDIO_F32SYS;
- } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
- wasapi_format = AUDIO_S16SYS;
- } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof (GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
- wasapi_format = AUDIO_S32SYS;
- }
- }
+ wasapi_format = WaveFormatToSDLFormat(waveformat);
while ((!valid_format) && (test_format)) {
if (test_format == wasapi_format) {
diff --git a/src/audio/wasapi/SDL_wasapi.h b/src/audio/wasapi/SDL_wasapi.h
index eb6ddc0f6..9ee26157b 100644
--- a/src/audio/wasapi/SDL_wasapi.h
+++ b/src/audio/wasapi/SDL_wasapi.h
@@ -63,7 +63,7 @@ extern SDL_atomic_t WASAPI_DefaultCaptureGeneration;
int WASAPI_PrepDevice(_THIS, const SDL_bool updatestream);
void WASAPI_RefDevice(_THIS);
void WASAPI_UnrefDevice(_THIS);
-void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, LPCWSTR devid);
+void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid);
void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid);
/* These are functions that are implemented differently for Windows vs WinRT. */
diff --git a/src/audio/wasapi/SDL_wasapi_win32.c b/src/audio/wasapi/SDL_wasapi_win32.c
index 23b48ac3d..173ab00eb 100644
--- a/src/audio/wasapi/SDL_wasapi_win32.c
+++ b/src/audio/wasapi/SDL_wasapi_win32.c
@@ -65,26 +65,31 @@ static const IID SDL_IID_IMMNotificationClient = { 0x7991eec9, 0x7e89, 0x4d85,{
static const IID SDL_IID_IMMEndpoint = { 0x1be09788, 0x6894, 0x4089,{ 0x85, 0x86, 0x9a, 0x2a, 0x6c, 0x26, 0x5a, 0xc5 } };
static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32,{ 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } };
static const PROPERTYKEY SDL_PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd,{ 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, } }, 14 };
+static const PROPERTYKEY SDL_PKEY_AudioEngine_DeviceFormat = { { 0xf19f064d, 0x82c, 0x4e27,{ 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c, } }, 0 };
-static char *
-GetWasapiDeviceName(IMMDevice *device)
+static void
+GetWasapiDeviceInfo(IMMDevice *device, char **utf8dev, WAVEFORMATEXTENSIBLE *fmt)
{
/* PKEY_Device_FriendlyName gives you "Speakers (SoundBlaster Pro)" which drives me nuts. I'd rather it be
"SoundBlaster Pro (Speakers)" but I guess that's developers vs users. Windows uses the FriendlyName in
its own UIs, like Volume Control, etc. */
- char *utf8dev = NULL;
IPropertyStore *props = NULL;
+ *utf8dev = NULL;
+ SDL_zerop(fmt);
if (SUCCEEDED(IMMDevice_OpenPropertyStore(device, STGM_READ, &props))) {
PROPVARIANT var;
PropVariantInit(&var);
if (SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_Device_FriendlyName, &var))) {
- utf8dev = WIN_StringToUTF8W(var.pwszVal);
+ *utf8dev = WIN_StringToUTF8W(var.pwszVal);
+ }
+ PropVariantClear(&var);
+ if (SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_AudioEngine_DeviceFormat, &var))) {
+ SDL_memcpy(fmt, var.blob.pBlobData, SDL_min(var.blob.cbSize, sizeof(WAVEFORMATEXTENSIBLE)));
}
PropVariantClear(&var);
IPropertyStore_Release(props);
}
- return utf8dev;
}
@@ -194,9 +199,11 @@ SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *ithis, LPCWS
if (SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) {
const SDL_bool iscapture = (flow == eCapture);
if (dwNewState == DEVICE_STATE_ACTIVE) {
- char *utf8dev = GetWasapiDeviceName(device);
+ char *utf8dev;
+ WAVEFORMATEXTENSIBLE fmt;
+ GetWasapiDeviceInfo(device, &utf8dev, &fmt);
if (utf8dev) {
- WASAPI_AddDevice(iscapture, utf8dev, pwstrDeviceId);
+ WASAPI_AddDevice(iscapture, utf8dev, &fmt, pwstrDeviceId);
SDL_free(utf8dev);
}
} else {
@@ -352,6 +359,7 @@ typedef struct
{
LPWSTR devid;
char *devname;
+ WAVEFORMATEXTENSIBLE fmt;
} EndpointItem;
static int sort_endpoints(const void *_a, const void *_b)
@@ -408,7 +416,7 @@ WASAPI_EnumerateEndpointsForFlow(const SDL_bool iscapture)
IMMDevice *device = NULL;
if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &device))) {
if (SUCCEEDED(IMMDevice_GetId(device, &item->devid))) {
- item->devname = GetWasapiDeviceName(device);
+ GetWasapiDeviceInfo(device, &item->devname, &item->fmt);
}
IMMDevice_Release(device);
}
@@ -421,7 +429,7 @@ WASAPI_EnumerateEndpointsForFlow(const SDL_bool iscapture)
for (i = 0; i < total; i++) {
EndpointItem *item = items + i;
if ((item->devid) && (item->devname)) {
- WASAPI_AddDevice(iscapture, item->devname, item->devid);
+ WASAPI_AddDevice(iscapture, item->devname, &item->fmt, item->devid);
}
SDL_free(item->devname);
CoTaskMemFree(item->devid);
diff --git a/src/audio/wasapi/SDL_wasapi_winrt.cpp b/src/audio/wasapi/SDL_wasapi_winrt.cpp
index c70b865d8..b8c5adc45 100644
--- a/src/audio/wasapi/SDL_wasapi_winrt.cpp
+++ b/src/audio/wasapi/SDL_wasapi_winrt.cpp
@@ -32,6 +32,7 @@
#include <windows.devices.enumeration.h>
#include <windows.media.devices.h>
#include <wrl/implements.h>
+#include <collection.h>
extern "C" {
#include "../../core/windows/SDL_windows.h"
@@ -52,6 +53,8 @@ using namespace Windows::Media::Devices;
using namespace Windows::Foundation;
using namespace Microsoft::WRL;
+static Platform::String^ SDL_PKEY_AudioEngine_DeviceFormat = L"{f19f064d-082c-4e27-bc73-6882a1bb8e4c} 0";
+
class SDL_WasapiDeviceEventHandler
{
public:
@@ -78,9 +81,16 @@ class SDL_WasapiDeviceEventHandler
SDL_WasapiDeviceEventHandler::SDL_WasapiDeviceEventHandler(const SDL_bool _iscapture)
: iscapture(_iscapture)
, completed(SDL_CreateSemaphore(0))
- , watcher(DeviceInformation::CreateWatcher(_iscapture ? DeviceClass::AudioCapture : DeviceClass::AudioRender))
{
- if (!watcher || !completed)
+ if (!completed)
+ return; // uhoh.
+
+ Platform::String^ selector = _iscapture ? MediaDevice::GetAudioCaptureSelector() :
+ MediaDevice::GetAudioRenderSelector();
+ Platform::Collections::Vector<Platform::String^> properties;
+ properties.Append(SDL_PKEY_AudioEngine_DeviceFormat);
+ watcher = DeviceInformation::CreateWatcher(selector, properties.GetView());
+ if (!watcher)
return; // uhoh.
// !!! FIXME: this doesn't need a lambda here, I think, if I make SDL_WasapiDeviceEventHandler a proper C++/CX class. --ryan.
@@ -124,7 +134,18 @@ SDL_WasapiDeviceEventHandler::OnDeviceAdded(DeviceWatcher^ sender, DeviceInforma
SDL_assert(sender == this->watcher);
char *utf8dev = WIN_StringTo
(Patch may be truncated, please check the link at the top of this post.)