mirror of https://github.com/encounter/SDL.git
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:
parent
15d0618083
commit
2f0816adb7
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
@ -1255,10 +1337,11 @@ PIPEWIRE_Init(SDL_AudioDriverImpl *impl)
|
|||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = PIPEWIRE_DetectDevices;
|
||||
impl->OpenDevice = PIPEWIRE_OpenDevice;
|
||||
impl->CloseDevice = PIPEWIRE_CloseDevice;
|
||||
impl->Deinitialize = PIPEWIRE_Deinitialize;
|
||||
impl->DetectDevices = PIPEWIRE_DetectDevices;
|
||||
impl->OpenDevice = PIPEWIRE_OpenDevice;
|
||||
impl->CloseDevice = PIPEWIRE_CloseDevice;
|
||||
impl->Deinitialize = PIPEWIRE_Deinitialize;
|
||||
impl->GetDefaultAudioInfo = PIPEWIRE_GetDefaultAudioInfo;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->ProvidesOwnCallbackThread = SDL_TRUE;
|
||||
|
|
|
@ -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,30 +772,59 @@ 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
|
||||
HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *data)
|
||||
{
|
||||
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;
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -471,6 +471,40 @@ SDL_IMMDevice_EnumerateEndpoints(SDL_bool useguid)
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue