Add SDL_GetDefaultAudioInfo.

This API is supported on pipewire, pulseaudio, wasapi, and directsound.

Co-authored-by: Frank Praznik <frank.praznik@gmail.com>
This commit is contained in:
Ethan Lee 2022-07-11 13:08:30 -04:00
parent 15d0618083
commit 2f0816adb7
15 changed files with 334 additions and 31 deletions

View File

@ -487,6 +487,7 @@ extern DECLSPEC int SDLCALL SDL_GetNumAudioDevices(int iscapture);
* \since This function is available since SDL 2.0.0.
*
* \sa SDL_GetNumAudioDevices
* \sa SDL_GetDefaultAudioInfo
*/
extern DECLSPEC const char *SDLCALL SDL_GetAudioDeviceName(int index,
int iscapture);
@ -512,12 +513,48 @@ extern DECLSPEC const char *SDLCALL SDL_GetAudioDeviceName(int index,
* \since This function is available since SDL 2.0.16.
*
* \sa SDL_GetNumAudioDevices
* \sa SDL_GetDefaultAudioInfo
*/
extern DECLSPEC int SDLCALL SDL_GetAudioDeviceSpec(int index,
int iscapture,
SDL_AudioSpec *spec);
/**
* Get the name and preferred format of the default audio device.
*
* Some (but not all!) platforms have an isolated mechanism to get information
* about the "default" device. This can actually be a completely different
* device that's not in the list you get from SDL_GetAudioDeviceSpec(). It can
* even be a network address! (This is discussed in SDL_OpenAudioDevice().)
*
* As a result, this call is not guaranteed to be performant, as it can query
* the sound server directly every time, unlike the other query functions. You
* should call this function sparingly!
*
* `spec` will be filled with the sample rate, sample format, and channel
* count, if a default device exists on the system. If `name` is provided, will
* be filled with either a dynamically-allocated UTF-8 string or NULL.
*
* \param name A pointer to be filled with the name of the default device (can
be NULL). Please call SDL_free() when you are done with this
pointer!
* \param spec The SDL_AudioSpec to be initialized by this function.
* \param iscapture non-zero to query the default recording device, zero to
* query the default output device.
* \returns 0 on success, nonzero on error
*
* \since This function is available since SDL 2.24.0.
*
* \sa SDL_GetAudioDeviceName
* \sa SDL_GetAudioDeviceSpec
* \sa SDL_OpenAudioDevice
*/
extern DECLSPEC int SDLCALL SDL_GetDefaultAudioInfo(char **name,
SDL_AudioSpec *spec,
int iscapture);
/**
* Open a specific audio device.
*

View File

@ -1154,6 +1154,24 @@ SDL_GetAudioDeviceSpec(int index, int iscapture, SDL_AudioSpec *spec)
}
int
SDL_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
{
if (spec == NULL) {
return SDL_InvalidParamError("spec");
}
if (!SDL_GetCurrentAudioDriver()) {
return SDL_SetError("Audio subsystem is not initialized");
}
if (current_audio.impl.GetDefaultAudioInfo == NULL) {
return SDL_Unsupported();
}
return current_audio.impl.GetDefaultAudioInfo(name, spec, iscapture);
}
static void
close_audio_device(SDL_AudioDevice * device)
{

View File

@ -78,6 +78,7 @@ typedef struct SDL_AudioDriverImpl
void (*UnlockDevice) (_THIS);
void (*FreeDeviceHandle) (void *handle); /**< SDL is done with handle from SDL_AddAudioDevice() */
void (*Deinitialize) (void);
int (*GetDefaultAudioInfo) (char **name, SDL_AudioSpec *spec, int iscapture);
/* !!! FIXME: add pause(), so we can optimize instead of mixing silence. */

View File

@ -156,6 +156,17 @@ DSOUND_FreeDeviceHandle(void *handle)
SDL_free(handle);
}
static int
DSOUND_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
{
#if HAVE_MMDEVICEAPI_H
if (SupportsIMMDevice) {
return SDL_IMMDevice_GetDefaultAudioInfo(name, spec, iscapture);
}
#endif /* HAVE_MMDEVICEAPI_H */
return SDL_Unsupported();
}
static BOOL CALLBACK
FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID data)
{
@ -615,6 +626,7 @@ DSOUND_Init(SDL_AudioDriverImpl * impl)
impl->CloseDevice = DSOUND_CloseDevice;
impl->FreeDeviceHandle = DSOUND_FreeDeviceHandle;
impl->Deinitialize = DSOUND_Deinitialize;
impl->GetDefaultAudioInfo = DSOUND_GetDefaultAudioInfo;
impl->HasCaptureSupport = SDL_TRUE;

View File

@ -30,6 +30,7 @@
#include <pipewire/extensions/metadata.h>
#include <spa/param/audio/format-utils.h>
#include <spa/utils/json.h>
/*
* The following keys are defined for compatability when building against older versions of Pipewire
@ -249,7 +250,11 @@ struct io_node
SDL_bool is_capture;
SDL_AudioSpec spec;
char name[];
/* FIXME: These sizes are arbitrary! */
#define MAX_FRIENDLY_NAME 256
#define MAX_IDENTIFIER_PATH 256
char name[MAX_FRIENDLY_NAME]; /* Friendly name */
char path[MAX_IDENTIFIER_PATH]; /* OS identifier (i.e. ALSA endpoint) */
};
/* The global hotplug thread and associated objects. */
@ -265,8 +270,8 @@ static int hotplug_init_seq_val;
static SDL_bool hotplug_init_complete;
static SDL_bool hotplug_events_enabled;
static Uint32 pipewire_default_sink_id = SPA_ID_INVALID;
static Uint32 pipewire_default_source_id = SPA_ID_INVALID;
static char *pipewire_default_sink_id = NULL;
static char *pipewire_default_source_id = NULL;
/* The active node list */
static SDL_bool
@ -324,10 +329,10 @@ io_list_sort()
/* Find and move the default nodes to the beginning of the list */
spa_list_for_each_safe (n, temp, &hotplug_io_list, link) {
if (n->id == pipewire_default_sink_id) {
if (pipewire_default_sink_id != NULL && SDL_strcmp(n->path, pipewire_default_sink_id) == 0) {
default_sink = n;
spa_list_remove(&n->link);
} else if (n->id == pipewire_default_source_id) {
} else if (pipewire_default_source_id != NULL && SDL_strcmp(n->path, pipewire_default_source_id) == 0) {
default_source = n;
spa_list_remove(&n->link);
}
@ -353,6 +358,18 @@ io_list_clear()
}
}
static struct io_node*
io_list_get(char *path)
{
struct io_node *n, *temp;
spa_list_for_each_safe (n, temp, &hotplug_io_list, link) {
if (SDL_strcmp(n->path, path) == 0) {
return n;
}
}
return NULL;
}
static void
node_object_destroy(struct node_object *node)
{
@ -591,17 +608,43 @@ node_event_param(void *object, int seq, uint32_t id, uint32_t index, uint32_t ne
static const struct pw_node_events interface_node_events = { PW_VERSION_NODE_EVENTS, .info = node_event_info,
.param = node_event_param };
static char*
get_name_from_json(const char *json)
{
struct spa_json parser[2];
char key[7]; /* "name" */
char value[MAX_IDENTIFIER_PATH];
spa_json_init(&parser[0], json, SDL_strlen(json));
if (spa_json_enter_object(&parser[0], &parser[1]) <= 0) {
/* Not actually JSON */
return NULL;
}
if (spa_json_get_string(&parser[1], key, sizeof(key)) <= 0) {
/* Not actually a key/value pair */
return NULL;
}
if (spa_json_get_string(&parser[1], value, sizeof(value)) <= 0) {
/* Somehow had a key with no value? */
return NULL;
}
return SDL_strdup(value);
}
/* Metadata node callback */
static int
metadata_property(void *object, Uint32 subject, const char *key, const char *type, const char *value)
{
if (subject == PW_ID_CORE && key != NULL && value != NULL) {
Uint32 val = SDL_atoi(value);
if (!SDL_strcmp(key, "default.audio.sink")) {
pipewire_default_sink_id = val;
if (pipewire_default_sink_id != NULL) {
SDL_free(pipewire_default_sink_id);
}
pipewire_default_sink_id = get_name_from_json(value);
} else if (!SDL_strcmp(key, "default.audio.source")) {
pipewire_default_source_id = val;
if (pipewire_default_source_id != NULL) {
SDL_free(pipewire_default_source_id);
}
pipewire_default_source_id = get_name_from_json(value);
}
}
@ -623,9 +666,9 @@ registry_event_global_callback(void *object, uint32_t id, uint32_t permissions,
if (media_class) {
const char *node_desc;
const char *node_path;
struct io_node *io;
SDL_bool is_capture;
int str_buffer_len;
/* Just want sink and capture */
if (!SDL_strcasecmp(media_class, "Audio/Sink")) {
@ -637,8 +680,9 @@ registry_event_global_callback(void *object, uint32_t id, uint32_t permissions,
}
node_desc = spa_dict_lookup(props, PW_KEY_NODE_DESCRIPTION);
node_path = spa_dict_lookup(props, PW_KEY_NODE_NAME);
if (node_desc) {
if (node_desc && node_path) {
node = node_object_new(id, type, version, &interface_node_events, &interface_core_events);
if (node == NULL) {
SDL_SetError("Pipewire: Failed to allocate interface node");
@ -646,8 +690,7 @@ registry_event_global_callback(void *object, uint32_t id, uint32_t permissions,
}
/* Allocate and initialize the I/O node information struct */
str_buffer_len = SDL_strlen(node_desc) + 1;
node->userdata = io = SDL_calloc(1, sizeof(struct io_node) + str_buffer_len);
node->userdata = io = SDL_calloc(1, sizeof(struct io_node));
if (io == NULL) {
node_object_destroy(node);
SDL_OutOfMemory();
@ -658,7 +701,8 @@ registry_event_global_callback(void *object, uint32_t id, uint32_t permissions,
io->id = id;
io->is_capture = is_capture;
io->spec.format = AUDIO_F32; /* Pipewire uses floats internally, other formats require conversion. */
SDL_strlcpy(io->name, node_desc, str_buffer_len);
SDL_strlcpy(io->name, node_desc, sizeof(io->name));
SDL_strlcpy(io->path, node_path, sizeof(io->path));
/* Update sync points */
hotplug_core_sync(node);
@ -744,8 +788,14 @@ hotplug_loop_destroy()
hotplug_init_complete = SDL_FALSE;
hotplug_events_enabled = SDL_FALSE;
pipewire_default_sink_id = SPA_ID_INVALID;
pipewire_default_source_id = SPA_ID_INVALID;
if (pipewire_default_sink_id != NULL) {
SDL_free(pipewire_default_sink_id);
pipewire_default_sink_id = NULL;
}
if (pipewire_default_source_id != NULL) {
SDL_free(pipewire_default_source_id);
pipewire_default_source_id = NULL;
}
if (hotplug_registry) {
PIPEWIRE_pw_proxy_destroy((struct pw_proxy *)hotplug_registry);
@ -1228,6 +1278,38 @@ static void PIPEWIRE_CloseDevice(_THIS)
SDL_free(this->hidden);
}
static int
PIPEWIRE_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
{
struct io_node *node;
char *target;
if (iscapture) {
if (pipewire_default_source_id == NULL) {
return SDL_SetError("PipeWire could not find a default source");
}
target = pipewire_default_source_id;
} else {
if (pipewire_default_sink_id == NULL) {
return SDL_SetError("PipeWire could not find a default sink");
}
target = pipewire_default_sink_id;
}
PIPEWIRE_pw_thread_loop_lock(hotplug_loop);
node = io_list_get(target);
if (node == NULL) {
PIPEWIRE_pw_thread_loop_unlock(hotplug_loop);
return SDL_SetError("PipeWire device list is out of sync with defaults");
}
if (name != NULL) {
*name = SDL_strdup(node->name);
}
SDL_copyp(spec, &node->spec);
PIPEWIRE_pw_thread_loop_unlock(hotplug_loop);
return 0;
}
static void
PIPEWIRE_Deinitialize()
{
@ -1259,6 +1341,7 @@ PIPEWIRE_Init(SDL_AudioDriverImpl *impl)
impl->OpenDevice = PIPEWIRE_OpenDevice;
impl->CloseDevice = PIPEWIRE_CloseDevice;
impl->Deinitialize = PIPEWIRE_Deinitialize;
impl->GetDefaultAudioInfo = PIPEWIRE_GetDefaultAudioInfo;
impl->HasCaptureSupport = SDL_TRUE;
impl->ProvidesOwnCallbackThread = SDL_TRUE;

View File

@ -118,6 +118,7 @@ static pa_operation * (*PULSEAUDIO_pa_stream_flush) (pa_stream *,
static int (*PULSEAUDIO_pa_stream_disconnect) (pa_stream *);
static void (*PULSEAUDIO_pa_stream_unref) (pa_stream *);
static void (*PULSEAUDIO_pa_stream_set_write_callback)(pa_stream *, pa_stream_request_cb_t, void *);
static pa_operation * (*PULSEAUDIO_pa_context_get_server_info)(pa_context *, pa_server_info_cb_t, void *);
static int load_pulseaudio_syms(void);
@ -230,6 +231,7 @@ load_pulseaudio_syms(void)
SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto);
SDL_PULSEAUDIO_SYM(pa_strerror);
SDL_PULSEAUDIO_SYM(pa_stream_set_write_callback);
SDL_PULSEAUDIO_SYM(pa_context_get_server_info);
return 0;
}
@ -527,7 +529,7 @@ SourceDeviceNameCallback(pa_context *c, const pa_source_info *i, int is_last, vo
static SDL_bool
FindDeviceName(struct SDL_PrivateAudioData *h, const SDL_bool iscapture, void *handle)
{
const uint32_t idx = ((uint32_t) ((size_t) handle)) - 1;
const uint32_t idx = ((uint32_t) ((intptr_t) handle)) - 1;
if (handle == NULL) { /* NULL == default device. */
return SDL_TRUE;
@ -691,6 +693,13 @@ static pa_mainloop *hotplug_mainloop = NULL;
static pa_context *hotplug_context = NULL;
static SDL_Thread *hotplug_thread = NULL;
/* These are the OS identifiers (i.e. ALSA strings)... */
static char *default_sink_path = NULL;
static char *default_source_path = NULL;
/* ... and these are the descriptions we use in GetDefaultAudioInfo. */
static char *default_sink_name = NULL;
static char *default_source_name = NULL;
/* device handles are device index + 1, cast to void*, so we never pass a NULL. */
static SDL_AudioFormat
@ -721,6 +730,7 @@ static void
SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
{
SDL_AudioSpec spec;
SDL_bool add = (SDL_bool) ((intptr_t) data);
if (i) {
spec.freq = i->sample_spec.rate;
spec.channels = i->sample_spec.channels;
@ -731,7 +741,16 @@ SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
spec.callback = NULL;
spec.userdata = NULL;
SDL_AddAudioDevice(SDL_FALSE, i->description, &spec, (void *) ((size_t) i->index+1));
if (add) {
SDL_AddAudioDevice(SDL_FALSE, i->description, &spec, (void *) ((intptr_t) i->index+1));
}
if (default_sink_path != NULL && SDL_strcmp(i->name, default_sink_path) == 0) {
if (default_sink_name != NULL) {
SDL_free(default_sink_name);
}
default_sink_name = SDL_strdup(i->description);
}
}
}
@ -740,6 +759,7 @@ static void
SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
{
SDL_AudioSpec spec;
SDL_bool add = (SDL_bool) ((intptr_t) data);
if (i) {
/* Maybe skip "monitor" sources. These are just output from other sinks. */
if (include_monitors || (i->monitor_of_sink == PA_INVALID_INDEX)) {
@ -752,10 +772,32 @@ SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *da
spec.callback = NULL;
spec.userdata = NULL;
SDL_AddAudioDevice(SDL_TRUE, i->description, &spec, (void *) ((size_t) i->index+1));
if (add) {
SDL_AddAudioDevice(SDL_TRUE, i->description, &spec, (void *) ((intptr_t) i->index+1));
}
if (default_source_path != NULL && SDL_strcmp(i->name, default_source_path) == 0) {
if (default_source_name != NULL) {
SDL_free(default_source_name);
}
default_source_name = SDL_strdup(i->description);
}
}
}
}
static void
ServerInfoCallback(pa_context *c, const pa_server_info *i, void *data)
{
if (default_sink_path != NULL) {
SDL_free(default_sink_path);
}
if (default_source_path != NULL) {
SDL_free(default_source_path);
}
default_sink_path = SDL_strdup(i->default_sink_name);
default_source_path = SDL_strdup(i->default_source_name);
}
/* This is called when PulseAudio has a device connected/removed/changed. */
static void
@ -763,19 +805,26 @@ HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, voi
{
const SDL_bool added = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW);
const SDL_bool removed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE);
const SDL_bool changed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE);
if (added || removed) { /* we only care about add/remove events. */
if (added || removed || changed) { /* we only care about add/remove events. */
const SDL_bool sink = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK);
const SDL_bool source = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
/* adds need sink details from the PulseAudio server. Another callback... */
if (added && sink) {
PULSEAUDIO_pa_context_get_sink_info_by_index(hotplug_context, idx, SinkInfoCallback, NULL);
} else if (added && source) {
PULSEAUDIO_pa_context_get_source_info_by_index(hotplug_context, idx, SourceInfoCallback, NULL);
if ((added || changed) && sink) {
if (changed) {
PULSEAUDIO_pa_context_get_server_info(hotplug_context, ServerInfoCallback, NULL);
}
PULSEAUDIO_pa_context_get_sink_info_by_index(hotplug_context, idx, SinkInfoCallback, (void*) ((intptr_t) added));
} else if ((added || changed) && source) {
if (changed) {
PULSEAUDIO_pa_context_get_server_info(hotplug_context, ServerInfoCallback, NULL);
}
PULSEAUDIO_pa_context_get_source_info_by_index(hotplug_context, idx, SourceInfoCallback, (void*) ((intptr_t) added));
} else if (removed && (sink || source)) {
/* removes we can handle just with the device index. */
SDL_RemoveAudioDevice(source != 0, (void *) ((size_t) idx+1));
SDL_RemoveAudioDevice(source != 0, (void *) ((intptr_t) idx+1));
}
}
}
@ -796,13 +845,46 @@ HotplugThread(void *data)
static void
PULSEAUDIO_DetectDevices()
{
WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_sink_info_list(hotplug_context, SinkInfoCallback, NULL));
WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_source_info_list(hotplug_context, SourceInfoCallback, NULL));
WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_server_info(hotplug_context, ServerInfoCallback, NULL));
WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_sink_info_list(hotplug_context, SinkInfoCallback, (void*) ((intptr_t) SDL_TRUE)));
WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_source_info_list(hotplug_context, SourceInfoCallback, (void*) ((intptr_t) SDL_TRUE)));
/* ok, we have a sane list, let's set up hotplug notifications now... */
hotplug_thread = SDL_CreateThreadInternal(HotplugThread, "PulseHotplug", 256 * 1024, NULL);
}
static int
PULSEAUDIO_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
{
int i;
int numdevices;
char *target;
if (iscapture) {
if (default_source_name == NULL) {
return SDL_SetError("PulseAudio could not find a default source");
}
target = default_source_name;
} else {
if (default_sink_name == NULL) {
return SDL_SetError("PulseAudio could not find a default sink");
}
target = default_sink_name;
}
numdevices = SDL_GetNumAudioDevices(iscapture);
for (i = 0; i < numdevices; i += 1) {
if (SDL_strcmp(SDL_GetAudioDeviceName(i, iscapture), target) == 0) {
if (name != NULL) {
*name = SDL_strdup(target);
}
SDL_GetAudioDeviceSpec(i, iscapture, spec);
return 0;
}
}
return SDL_SetError("Could not find default PulseAudio device");
}
static void
PULSEAUDIO_Deinitialize(void)
{
@ -816,6 +898,23 @@ PULSEAUDIO_Deinitialize(void)
hotplug_mainloop = NULL;
hotplug_context = NULL;
if (default_sink_path != NULL) {
SDL_free(default_sink_path);
default_sink_path = NULL;
}
if (default_source_path != NULL) {
SDL_free(default_source_path);
default_source_path = NULL;
}
if (default_sink_name != NULL) {
SDL_free(default_sink_name);
default_sink_name = NULL;
}
if (default_source_name != NULL) {
SDL_free(default_source_name);
default_source_name = NULL;
}
UnloadPulseAudioLibrary();
}
@ -843,6 +942,7 @@ PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
impl->Deinitialize = PULSEAUDIO_Deinitialize;
impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice;
impl->FlushCapture = PULSEAUDIO_FlushCapture;
impl->GetDefaultAudioInfo = PULSEAUDIO_GetDefaultAudioInfo;
impl->HasCaptureSupport = SDL_TRUE;

View File

@ -634,6 +634,7 @@ WASAPI_Init(SDL_AudioDriverImpl * impl)
impl->FlushCapture = WASAPI_FlushCapture;
impl->CloseDevice = WASAPI_CloseDevice;
impl->Deinitialize = WASAPI_Deinitialize;
impl->GetDefaultAudioInfo = WASAPI_GetDefaultAudioInfo;
impl->HasCaptureSupport = SDL_TRUE;
return SDL_TRUE; /* this audio target is available. */

View File

@ -64,6 +64,7 @@ void WASAPI_UnrefDevice(_THIS);
int WASAPI_PlatformInit(void);
void WASAPI_PlatformDeinit(void);
void WASAPI_EnumerateEndpoints(void);
int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture);
int WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery);
void WASAPI_PlatformThreadInit(_THIS);
void WASAPI_PlatformThreadDeinit(_THIS);

View File

@ -145,6 +145,12 @@ WASAPI_EnumerateEndpoints(void)
SDL_IMMDevice_EnumerateEndpoints(SDL_FALSE);
}
int
WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
{
return SDL_IMMDevice_GetDefaultAudioInfo(name, spec, iscapture);
}
void
WASAPI_PlatformDeleteActivationHandler(void *handler)
{

View File

@ -243,6 +243,12 @@ WASAPI_PlatformDeleteActivationHandler(void *handler)
((SDL_WasapiActivationHandler *) handler)->Release();
}
int
WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
{
return SDL_Unsupported();
}
int
WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery)
{

View File

@ -471,6 +471,40 @@ SDL_IMMDevice_EnumerateEndpoints(SDL_bool useguid)
IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)&notification_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)
{

View File

@ -33,6 +33,7 @@ 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);
SDL_AudioFormat WaveFormatToSDLFormat(WAVEFORMATEX *waveformat);

View File

@ -853,3 +853,4 @@
# ++'_SDL_GDKRunApp'.'SDL2.dll'.'SDL_GDKRunApp'
++'_SDL_GetOriginalMemoryFunctions'.'SDL2.dll'.'SDL_GetOriginalMemoryFunctions'
++'_SDL_ResetKeyboard'.'SDL2.dll'.'SDL_ResetKeyboard'
++'_SDL_GetDefaultAudioInfo'.'SDL2.dll'.'SDL_GetDefaultAudioInfo'

View File

@ -879,3 +879,4 @@
#define SDL_GDKRunApp SDL_GDKRunApp_REAL
#define SDL_GetOriginalMemoryFunctions SDL_GetOriginalMemoryFunctions_REAL
#define SDL_ResetKeyboard SDL_ResetKeyboard_REAL
#define SDL_GetDefaultAudioInfo SDL_GetDefaultAudioInfo_REAL

View File

@ -962,3 +962,4 @@ SDL_DYNAPI_PROC(int,SDL_GDKRunApp,(SDL_main_func a, void *b),(a,b),return)
#endif
SDL_DYNAPI_PROC(void,SDL_GetOriginalMemoryFunctions,(SDL_malloc_func *a, SDL_calloc_func *b, SDL_realloc_func *c, SDL_free_func *d),(a,b,c,d),)
SDL_DYNAPI_PROC(void,SDL_ResetKeyboard,(void),(),)
SDL_DYNAPI_PROC(int,SDL_GetDefaultAudioInfo,(char **a, SDL_AudioSpec *b, int c),(a,b,c),return)