|
@@ -20,6 +20,8 @@
|
|
|
*/
|
|
|
#include "SDL_internal.h"
|
|
|
|
|
|
+// !!! FIXME: Clean out the fprintf and printf calls, replace with SDL_Log
|
|
|
+
|
|
|
#ifdef SDL_AUDIO_DRIVER_ALSA
|
|
|
|
|
|
#ifndef SDL_ALSA_NON_BLOCKING
|
|
@@ -45,6 +47,7 @@
|
|
|
|
|
|
static int (*ALSA_snd_pcm_open)(snd_pcm_t **, const char *, snd_pcm_stream_t, int);
|
|
|
static int (*ALSA_snd_pcm_close)(snd_pcm_t *pcm);
|
|
|
+static int (*ALSA_snd_pcm_start)(snd_pcm_t *pcm);
|
|
|
static snd_pcm_sframes_t (*ALSA_snd_pcm_writei)(snd_pcm_t *, const void *, snd_pcm_uframes_t);
|
|
|
static snd_pcm_sframes_t (*ALSA_snd_pcm_readi)(snd_pcm_t *, void *, snd_pcm_uframes_t);
|
|
|
static int (*ALSA_snd_pcm_recover)(snd_pcm_t *, int, int);
|
|
@@ -115,6 +118,7 @@ static int load_alsa_syms(void)
|
|
|
{
|
|
|
SDL_ALSA_SYM(snd_pcm_open);
|
|
|
SDL_ALSA_SYM(snd_pcm_close);
|
|
|
+ SDL_ALSA_SYM(snd_pcm_start);
|
|
|
SDL_ALSA_SYM(snd_pcm_writei);
|
|
|
SDL_ALSA_SYM(snd_pcm_readi);
|
|
|
SDL_ALSA_SYM(snd_pcm_recover);
|
|
@@ -203,48 +207,21 @@ static int LoadALSALibrary(void)
|
|
|
|
|
|
static const char *get_audio_device(void *handle, const int channels)
|
|
|
{
|
|
|
- const char *device;
|
|
|
-
|
|
|
- if (handle != NULL) {
|
|
|
- return (const char *)handle;
|
|
|
- }
|
|
|
-
|
|
|
- /* !!! FIXME: we also check "SDL_AUDIO_DEVICE_NAME" at the higher level. */
|
|
|
- device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */
|
|
|
- if (device != NULL) {
|
|
|
- return device;
|
|
|
- }
|
|
|
-
|
|
|
- if (channels == 6) {
|
|
|
- return "plug:surround51";
|
|
|
- } else if (channels == 4) {
|
|
|
- return "plug:surround40";
|
|
|
- }
|
|
|
-
|
|
|
- return "default";
|
|
|
-}
|
|
|
-
|
|
|
-/* This function waits until it is possible to write a full sound buffer */
|
|
|
-static void ALSA_WaitDevice(SDL_AudioDevice *device)
|
|
|
-{
|
|
|
-#if SDL_ALSA_NON_BLOCKING
|
|
|
- const snd_pcm_sframes_t needed = (snd_pcm_sframes_t)device->spec.samples;
|
|
|
- while (SDL_AtomicGet(&device->enabled)) {
|
|
|
- const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(device->hidden->pcm_handle);
|
|
|
- if ((rc < 0) && (rc != -EAGAIN)) {
|
|
|
- /* Hmm, not much we can do - abort */
|
|
|
- fprintf(stderr, "ALSA snd_pcm_avail failed (unrecoverable): %s\n",
|
|
|
- ALSA_snd_strerror(rc));
|
|
|
- SDL_OpenedAudioDeviceDisconnected(device);
|
|
|
- return;
|
|
|
- } else if (rc < needed) {
|
|
|
- const Uint32 delay = ((needed - (SDL_max(rc, 0))) * 1000) / device->spec.freq;
|
|
|
- SDL_Delay(SDL_max(delay, 10));
|
|
|
- } else {
|
|
|
- break; /* ready to go! */
|
|
|
+ SDL_assert(handle != NULL); // SDL2 used NULL to mean "default" but that's not true in SDL3.
|
|
|
+
|
|
|
+ if (SDL_strcmp((const char *) handle, "default") == 0) {
|
|
|
+ const char *device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */
|
|
|
+ if (device != NULL) {
|
|
|
+ return device;
|
|
|
+ } else if (channels == 6) {
|
|
|
+ return "plug:surround51";
|
|
|
+ } else if (channels == 4) {
|
|
|
+ return "plug:surround40";
|
|
|
}
|
|
|
+ return "default";
|
|
|
}
|
|
|
-#endif
|
|
|
+
|
|
|
+ return (const char *)handle;
|
|
|
}
|
|
|
|
|
|
/* !!! FIXME: is there a channel swizzler in alsalib instead? */
|
|
@@ -353,16 +330,38 @@ static void no_swizzle(SDL_AudioDevice *device, void *buffer, Uint32 bufferlen)
|
|
|
}
|
|
|
#endif /* SND_CHMAP_API_VERSION */
|
|
|
|
|
|
-static void ALSA_PlayDevice(SDL_AudioDevice *device)
|
|
|
+/* This function waits until it is possible to write a full sound buffer */
|
|
|
+static void ALSA_WaitDevice(SDL_AudioDevice *device)
|
|
|
{
|
|
|
- const Uint8 *sample_buf = (const Uint8 *)device->hidden->mixbuf;
|
|
|
+ const snd_pcm_sframes_t needed = (snd_pcm_sframes_t)device->sample_frames;
|
|
|
+ while (!SDL_AtomicGet(&device->shutdown)) {
|
|
|
+ const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(device->hidden->pcm_handle);
|
|
|
+ if ((rc < 0) && (rc != -EAGAIN)) {
|
|
|
+ /* Hmm, not much we can do - abort */
|
|
|
+ fprintf(stderr, "ALSA snd_pcm_avail failed (unrecoverable): %s\n",
|
|
|
+ ALSA_snd_strerror(rc));
|
|
|
+ SDL_AudioDeviceDisconnected(device);
|
|
|
+ return;
|
|
|
+ } else if (rc < needed) {
|
|
|
+ const Uint32 delay = ((needed - (SDL_max(rc, 0))) * 1000) / device->spec.freq;
|
|
|
+ SDL_Delay(SDL_max(delay, 10));
|
|
|
+ } else {
|
|
|
+ break; /* ready to go! */
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
|
|
|
+{
|
|
|
+ SDL_assert(buffer == device->hidden->mixbuf);
|
|
|
+ Uint8 *sample_buf = device->hidden->mixbuf;
|
|
|
const int frame_size = ((SDL_AUDIO_BITSIZE(device->spec.format)) / 8) *
|
|
|
device->spec.channels;
|
|
|
- snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t)device->spec.samples);
|
|
|
+ snd_pcm_uframes_t frames_left = (snd_pcm_uframes_t) (buflen / frame_size);
|
|
|
|
|
|
- device->hidden->swizzle_func(device, device->hidden->mixbuf, frames_left);
|
|
|
+ device->hidden->swizzle_func(device, sample_buf, frames_left);
|
|
|
|
|
|
- while (frames_left > 0 && SDL_AtomicGet(&device->enabled)) {
|
|
|
+ while ((frames_left > 0) && !SDL_AtomicGet(&device->shutdown)) {
|
|
|
int status = ALSA_snd_pcm_writei(device->hidden->pcm_handle,
|
|
|
sample_buf, frames_left);
|
|
|
|
|
@@ -377,17 +376,16 @@ static void ALSA_PlayDevice(SDL_AudioDevice *device)
|
|
|
if (status < 0) {
|
|
|
/* Hmm, not much we can do - abort */
|
|
|
SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
|
|
|
- "ALSA write failed (unrecoverable): %s\n",
|
|
|
+ "ALSA write failed (unrecoverable): %s",
|
|
|
ALSA_snd_strerror(status));
|
|
|
- SDL_OpenedAudioDeviceDisconnected(device);
|
|
|
+ SDL_AudioDeviceDisconnected(device);
|
|
|
return;
|
|
|
}
|
|
|
continue;
|
|
|
} else if (status == 0) {
|
|
|
/* No frames were written (no available space in pcm device).
|
|
|
Allow other threads to catch up. */
|
|
|
- Uint32 delay = (frames_left / 2 * 1000) / device->spec.freq;
|
|
|
- SDL_Delay(delay);
|
|
|
+ SDL_Delay((frames_left / 2 * 1000) / device->spec.freq);
|
|
|
}
|
|
|
|
|
|
sample_buf += status * frame_size;
|
|
@@ -395,7 +393,7 @@ static void ALSA_PlayDevice(SDL_AudioDevice *device)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device)
|
|
|
+static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
|
|
|
{
|
|
|
return device->hidden->mixbuf;
|
|
|
}
|
|
@@ -411,11 +409,9 @@ static int ALSA_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buf
|
|
|
|
|
|
SDL_assert((buflen % frame_size) == 0);
|
|
|
|
|
|
- while (frames_left > 0 && SDL_AtomicGet(&device->enabled)) {
|
|
|
- int status;
|
|
|
-
|
|
|
- status = ALSA_snd_pcm_readi(device->hidden->pcm_handle,
|
|
|
- sample_buf, frames_left);
|
|
|
+ while ((frames_left > 0) && !SDL_AtomicGet(&device->shutdown)) {
|
|
|
+ int status = ALSA_snd_pcm_readi(device->hidden->pcm_handle,
|
|
|
+ sample_buf, frames_left);
|
|
|
|
|
|
if (status == -EAGAIN) {
|
|
|
ALSA_snd_pcm_wait(device->hidden->pcm_handle, wait_time);
|
|
@@ -450,17 +446,17 @@ static void ALSA_FlushCapture(SDL_AudioDevice *device)
|
|
|
|
|
|
static void ALSA_CloseDevice(SDL_AudioDevice *device)
|
|
|
{
|
|
|
- if (device->hidden->pcm_handle) {
|
|
|
- /* Wait for the submitted audio to drain
|
|
|
- ALSA_snd_pcm_drop() can hang, so don't use that.
|
|
|
- */
|
|
|
- Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq) * 2;
|
|
|
- SDL_Delay(delay);
|
|
|
-
|
|
|
- ALSA_snd_pcm_close(device->hidden->pcm_handle);
|
|
|
+ if (device->hidden) {
|
|
|
+ if (device->hidden->pcm_handle) {
|
|
|
+ /* Wait for the submitted audio to drain
|
|
|
+ ALSA_snd_pcm_drop() can hang, so don't use that.
|
|
|
+ */
|
|
|
+ SDL_Delay(((device->sample_frames * 1000) / device->spec.freq) * 2);
|
|
|
+ ALSA_snd_pcm_close(device->hidden->pcm_handle);
|
|
|
+ }
|
|
|
+ SDL_free(device->hidden->mixbuf);
|
|
|
+ SDL_free(device->hidden);
|
|
|
}
|
|
|
- SDL_free(device->hidden->mixbuf);
|
|
|
- SDL_free(device->hidden);
|
|
|
}
|
|
|
|
|
|
static int ALSA_set_buffer_size(SDL_AudioDevice *device, snd_pcm_hw_params_t *params)
|
|
@@ -475,7 +471,7 @@ static int ALSA_set_buffer_size(SDL_AudioDevice *device, snd_pcm_hw_params_t *pa
|
|
|
ALSA_snd_pcm_hw_params_copy(hwparams, params);
|
|
|
|
|
|
/* Attempt to match the period size to the requested buffer size */
|
|
|
- persize = device->spec.samples;
|
|
|
+ persize = device->sample_frames;
|
|
|
status = ALSA_snd_pcm_hw_params_set_period_size_near(
|
|
|
device->hidden->pcm_handle, hwparams, &persize, NULL);
|
|
|
if (status < 0) {
|
|
@@ -502,7 +498,7 @@ static int ALSA_set_buffer_size(SDL_AudioDevice *device, snd_pcm_hw_params_t *pa
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
- device->spec.samples = persize;
|
|
|
+ device->sample_frames = persize;
|
|
|
|
|
|
/* This is useful for debugging */
|
|
|
if (SDL_getenv("SDL_AUDIO_ALSA_DEBUG")) {
|
|
@@ -518,32 +514,20 @@ static int ALSA_set_buffer_size(SDL_AudioDevice *device, snd_pcm_hw_params_t *pa
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int ALSA_OpenDevice(SDL_AudioDevice *device, const char *devname)
|
|
|
+static int ALSA_OpenDevice(SDL_AudioDevice *device)
|
|
|
{
|
|
|
+ const SDL_bool iscapture = device->iscapture;
|
|
|
int status = 0;
|
|
|
- SDL_bool iscapture = device->iscapture;
|
|
|
- snd_pcm_t *pcm_handle = NULL;
|
|
|
- snd_pcm_hw_params_t *hwparams = NULL;
|
|
|
- snd_pcm_sw_params_t *swparams = NULL;
|
|
|
- snd_pcm_format_t format = 0;
|
|
|
- SDL_AudioFormat test_format = 0;
|
|
|
- const SDL_AudioFormat *closefmts;
|
|
|
- unsigned int rate = 0;
|
|
|
- unsigned int channels = 0;
|
|
|
-#ifdef SND_CHMAP_API_VERSION
|
|
|
- snd_pcm_chmap_t *chmap;
|
|
|
- char chmap_str[64];
|
|
|
-#endif
|
|
|
|
|
|
/* Initialize all variables that we clean on shutdown */
|
|
|
- device->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*device->hidden));
|
|
|
+ device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
|
|
|
if (device->hidden == NULL) {
|
|
|
return SDL_OutOfMemory();
|
|
|
}
|
|
|
- SDL_zerop(device->hidden);
|
|
|
|
|
|
/* Open the audio device */
|
|
|
/* Name of device should depend on # channels in spec */
|
|
|
+ snd_pcm_t *pcm_handle = NULL;
|
|
|
status = ALSA_snd_pcm_open(&pcm_handle,
|
|
|
get_audio_device(device->handle, device->spec.channels),
|
|
|
iscapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
|
|
@@ -556,6 +540,7 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device, const char *devname)
|
|
|
device->hidden->pcm_handle = pcm_handle;
|
|
|
|
|
|
/* Figure out what the hardware is capable of */
|
|
|
+ snd_pcm_hw_params_t *hwparams = NULL;
|
|
|
snd_pcm_hw_params_alloca(&hwparams);
|
|
|
status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams);
|
|
|
if (status < 0) {
|
|
@@ -570,7 +555,9 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device, const char *devname)
|
|
|
}
|
|
|
|
|
|
/* Try for a closest match on audio format */
|
|
|
- closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
|
|
+ snd_pcm_format_t format = 0;
|
|
|
+ const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
|
|
|
+ SDL_AudioFormat test_format;
|
|
|
while ((test_format = *(closefmts++)) != 0) {
|
|
|
switch (test_format) {
|
|
|
case SDL_AUDIO_U8:
|
|
@@ -605,7 +592,7 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device, const char *devname)
|
|
|
}
|
|
|
}
|
|
|
if (!test_format) {
|
|
|
- return SDL_SetError("%s: Unsupported audio format", "alsa");
|
|
|
+ return SDL_SetError("ALSA: Unsupported audio format");
|
|
|
}
|
|
|
device->spec.format = test_format;
|
|
|
|
|
@@ -614,8 +601,9 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device, const char *devname)
|
|
|
*/
|
|
|
device->hidden->swizzle_func = swizzle_alsa_channels;
|
|
|
#ifdef SND_CHMAP_API_VERSION
|
|
|
- chmap = ALSA_snd_pcm_get_chmap(pcm_handle);
|
|
|
+ snd_pcm_chmap_t *chmap = ALSA_snd_pcm_get_chmap(pcm_handle);
|
|
|
if (chmap) {
|
|
|
+ char chmap_str[64];
|
|
|
if (ALSA_snd_pcm_chmap_print(chmap, sizeof(chmap_str), chmap_str) > 0) {
|
|
|
if (SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0 ||
|
|
|
SDL_strcmp("FL FR FC LFE SL SR", chmap_str) == 0) {
|
|
@@ -629,7 +617,7 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device, const char *devname)
|
|
|
/* Set the number of channels */
|
|
|
status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
|
|
|
device->spec.channels);
|
|
|
- channels = device->spec.channels;
|
|
|
+ unsigned int channels = device->spec.channels;
|
|
|
if (status < 0) {
|
|
|
status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
|
|
|
if (status < 0) {
|
|
@@ -639,7 +627,7 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device, const char *devname)
|
|
|
}
|
|
|
|
|
|
/* Set the audio rate */
|
|
|
- rate = device->spec.freq;
|
|
|
+ unsigned int rate = device->spec.freq;
|
|
|
status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams,
|
|
|
&rate, NULL);
|
|
|
if (status < 0) {
|
|
@@ -654,12 +642,13 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device, const char *devname)
|
|
|
}
|
|
|
|
|
|
/* Set the software parameters */
|
|
|
+ snd_pcm_sw_params_t *swparams = NULL;
|
|
|
snd_pcm_sw_params_alloca(&swparams);
|
|
|
status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
|
|
|
if (status < 0) {
|
|
|
return SDL_SetError("ALSA: Couldn't get software config: %s", ALSA_snd_strerror(status));
|
|
|
}
|
|
|
- status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, device->spec.samples);
|
|
|
+ status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, device->sample_frames);
|
|
|
if (status < 0) {
|
|
|
return SDL_SetError("Couldn't set minimum available samples: %s", ALSA_snd_strerror(status));
|
|
|
}
|
|
@@ -673,17 +662,16 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device, const char *devname)
|
|
|
return SDL_SetError("Couldn't set software audio parameters: %s", ALSA_snd_strerror(status));
|
|
|
}
|
|
|
|
|
|
- /* Calculate the final parameters for this audio specification */
|
|
|
- SDL_CalculateAudioSpec(&device->spec);
|
|
|
+ // Calculate the final parameters for this audio specification
|
|
|
+ SDL_UpdatedAudioDeviceFormat(device);
|
|
|
|
|
|
- /* Allocate mixing buffer */
|
|
|
+ // Allocate mixing buffer
|
|
|
if (!iscapture) {
|
|
|
- device->hidden->mixlen = device->spec.size;
|
|
|
- device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->hidden->mixlen);
|
|
|
+ device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
|
|
|
if (device->hidden->mixbuf == NULL) {
|
|
|
return SDL_OutOfMemory();
|
|
|
}
|
|
|
- SDL_memset(device->hidden->mixbuf, device->spec.silence, device->hidden->mixlen);
|
|
|
+ SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size);
|
|
|
}
|
|
|
|
|
|
#if !SDL_ALSA_NON_BLOCKING
|
|
@@ -692,6 +680,8 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device, const char *devname)
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
+ ALSA_snd_pcm_start(pcm_handle);
|
|
|
+
|
|
|
/* We're ready to rock and roll. :-) */
|
|
|
return 0;
|
|
|
}
|
|
@@ -703,7 +693,7 @@ typedef struct ALSA_Device
|
|
|
struct ALSA_Device *next;
|
|
|
} ALSA_Device;
|
|
|
|
|
|
-static void add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSeen)
|
|
|
+static void add_device(const SDL_bool iscapture, const char *name, void *hint, ALSA_Device **pSeen)
|
|
|
{
|
|
|
ALSA_Device *dev = SDL_malloc(sizeof(ALSA_Device));
|
|
|
char *desc;
|
|
@@ -765,21 +755,17 @@ static void add_device(const int iscapture, const char *name, void *hint, ALSA_D
|
|
|
|
|
|
static ALSA_Device *hotplug_devices = NULL;
|
|
|
|
|
|
-static void ALSA_HotplugIteration(void)
|
|
|
+static void ALSA_HotplugIteration(SDL_bool *has_default_output, SDL_bool *has_default_capture)
|
|
|
{
|
|
|
void **hints = NULL;
|
|
|
- ALSA_Device *dev;
|
|
|
- ALSA_Device *unseen;
|
|
|
- ALSA_Device *seen;
|
|
|
- ALSA_Device *next;
|
|
|
- ALSA_Device *prev;
|
|
|
+ ALSA_Device *unseen = NULL;
|
|
|
+ ALSA_Device *seen = NULL;
|
|
|
|
|
|
if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) {
|
|
|
- int i, j;
|
|
|
const char *match = NULL;
|
|
|
int bestmatch = 0xFFFF;
|
|
|
+ int has_default = -1;
|
|
|
size_t match_len = 0;
|
|
|
- int defaultdev = -1;
|
|
|
static const char *const prefixes[] = {
|
|
|
"hw:", "sysdefault:", "default:", NULL
|
|
|
};
|
|
@@ -791,92 +777,100 @@ static void ALSA_HotplugIteration(void)
|
|
|
actual hardware. It could be prefixed with "hw:" or "default:"
|
|
|
or "sysdefault:" and maybe others. Go through the list and see
|
|
|
if we can find a preferred prefix for the system. */
|
|
|
- for (i = 0; hints[i]; i++) {
|
|
|
+ for (int i = 0; hints[i]; i++) {
|
|
|
char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
|
|
|
if (name == NULL) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- /* full name, not a prefix */
|
|
|
- if ((defaultdev == -1) && (SDL_strcmp(name, "default") == 0)) {
|
|
|
- defaultdev = i;
|
|
|
- }
|
|
|
-
|
|
|
- for (j = 0; prefixes[j]; j++) {
|
|
|
- const char *prefix = prefixes[j];
|
|
|
- const size_t prefixlen = SDL_strlen(prefix);
|
|
|
- if (SDL_strncmp(name, prefix, prefixlen) == 0) {
|
|
|
- if (j < bestmatch) {
|
|
|
- bestmatch = j;
|
|
|
- match = prefix;
|
|
|
- match_len = prefixlen;
|
|
|
+ if (SDL_strcmp(name, "default") == 0) {
|
|
|
+ if (has_default < 0) {
|
|
|
+ has_default = i;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for (int j = 0; prefixes[j]; j++) {
|
|
|
+ const char *prefix = prefixes[j];
|
|
|
+ const size_t prefixlen = SDL_strlen(prefix);
|
|
|
+ if (SDL_strncmp(name, prefix, prefixlen) == 0) {
|
|
|
+ if (j < bestmatch) {
|
|
|
+ bestmatch = j;
|
|
|
+ match = prefix;
|
|
|
+ match_len = prefixlen;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- free(name); /* This should NOT be SDL_free() */
|
|
|
+ free(name); /* This should NOT be SDL_free() */
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/* look through the list of device names to find matches */
|
|
|
- for (i = 0; hints[i]; i++) {
|
|
|
- char *name;
|
|
|
-
|
|
|
- /* if we didn't find a device name prefix we like at all... */
|
|
|
- if ((match == NULL) && (defaultdev != i)) {
|
|
|
- continue; /* ...skip anything that isn't the default device. */
|
|
|
- }
|
|
|
+ if (match || (has_default >= 0)) { // did we find a device name prefix we like at all...?
|
|
|
+ for (int i = 0; hints[i]; i++) {
|
|
|
+ char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
|
|
|
+ if (name == NULL) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
|
|
|
- if (name == NULL) {
|
|
|
- continue;
|
|
|
- }
|
|
|
+ // only want physical hardware interfaces
|
|
|
+ const SDL_bool is_default = (has_default == i) ? SDL_TRUE : SDL_FALSE;
|
|
|
+ if (is_default || SDL_strncmp(name, match, match_len) == 0) {
|
|
|
+ char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID");
|
|
|
+ const SDL_bool isoutput = (ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0);
|
|
|
+ const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0);
|
|
|
+ SDL_bool have_output = SDL_FALSE;
|
|
|
+ SDL_bool have_input = SDL_FALSE;
|
|
|
|
|
|
- /* only want physical hardware interfaces */
|
|
|
- if (match == NULL || (SDL_strncmp(name, match, match_len) == 0)) {
|
|
|
- char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID");
|
|
|
- const SDL_bool isoutput = (ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0);
|
|
|
- const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0);
|
|
|
- SDL_bool have_output = SDL_FALSE;
|
|
|
- SDL_bool have_input = SDL_FALSE;
|
|
|
+ free(ioid);
|
|
|
|
|
|
- free(ioid); /* This should NOT be SDL_free() */
|
|
|
+ if (!isoutput && !isinput) {
|
|
|
+ free(name);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- if (!isoutput && !isinput) {
|
|
|
- free(name); /* This should NOT be SDL_free() */
|
|
|
- continue;
|
|
|
- }
|
|
|
+ if (is_default) {
|
|
|
+ if (has_default_output && isoutput) {
|
|
|
+ *has_default_output = SDL_TRUE;
|
|
|
+ } else if (has_default_capture && isinput) {
|
|
|
+ *has_default_capture = SDL_TRUE;
|
|
|
+ }
|
|
|
+ free(name);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- prev = NULL;
|
|
|
- for (dev = unseen; dev; dev = next) {
|
|
|
- next = dev->next;
|
|
|
- if ((SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture))) {
|
|
|
- if (prev) {
|
|
|
- prev->next = next;
|
|
|
+ ALSA_Device *prev = NULL;
|
|
|
+ ALSA_Device *next;
|
|
|
+ for (ALSA_Device *dev = unseen; dev; dev = next) {
|
|
|
+ next = dev->next;
|
|
|
+ if ((SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture))) {
|
|
|
+ if (prev) {
|
|
|
+ prev->next = next;
|
|
|
+ } else {
|
|
|
+ unseen = next;
|
|
|
+ }
|
|
|
+ dev->next = seen;
|
|
|
+ seen = dev;
|
|
|
+ if (isinput) {
|
|
|
+ have_input = SDL_TRUE;
|
|
|
+ }
|
|
|
+ if (isoutput) {
|
|
|
+ have_output = SDL_TRUE;
|
|
|
+ }
|
|
|
} else {
|
|
|
- unseen = next;
|
|
|
- }
|
|
|
- dev->next = seen;
|
|
|
- seen = dev;
|
|
|
- if (isinput) {
|
|
|
- have_input = SDL_TRUE;
|
|
|
- }
|
|
|
- if (isoutput) {
|
|
|
- have_output = SDL_TRUE;
|
|
|
+ prev = dev;
|
|
|
}
|
|
|
- } else {
|
|
|
- prev = dev;
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- if (isinput && !have_input) {
|
|
|
- add_device(SDL_TRUE, name, hints[i], &seen);
|
|
|
- }
|
|
|
- if (isoutput && !have_output) {
|
|
|
- add_device(SDL_FALSE, name, hints[i], &seen);
|
|
|
+ if (isinput && !have_input) {
|
|
|
+ add_device(SDL_TRUE, name, hints[i], &seen);
|
|
|
+ }
|
|
|
+ if (isoutput && !have_output) {
|
|
|
+ add_device(SDL_FALSE, name, hints[i], &seen);
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- free(name); /* This should NOT be SDL_free() */
|
|
|
+ free(name); /* This should NOT be SDL_free() */
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
ALSA_snd_device_name_free_hint(hints);
|
|
@@ -884,10 +878,17 @@ static void ALSA_HotplugIteration(void)
|
|
|
hotplug_devices = seen; /* now we have a known-good list of attached devices. */
|
|
|
|
|
|
/* report anything still in unseen as removed. */
|
|
|
- for (dev = unseen; dev; dev = next) {
|
|
|
+ ALSA_Device *next = NULL;
|
|
|
+ for (ALSA_Device *dev = unseen; dev; dev = next) {
|
|
|
/*printf("ALSA: removing usb %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
|
|
|
next = dev->next;
|
|
|
- SDL_RemoveAudioDevice(dev->iscapture, dev->name);
|
|
|
+
|
|
|
+ SDL_AudioDevice *device = SDL_ObtainPhysicalAudioDeviceByHandle(dev->name);
|
|
|
+ if (device) {
|
|
|
+ SDL_UnlockMutex(device->lock); // AudioDeviceDisconnected will relock and verify it's still in the list, but in case this is destroyed, unlock now.
|
|
|
+ SDL_AudioDeviceDisconnected(device);
|
|
|
+ }
|
|
|
+
|
|
|
SDL_free(dev->name);
|
|
|
SDL_free(dev);
|
|
|
}
|
|
@@ -909,16 +910,25 @@ static int SDLCALL ALSA_HotplugThread(void *arg)
|
|
|
SDL_Delay(100);
|
|
|
}
|
|
|
|
|
|
- ALSA_HotplugIteration(); /* run the check. */
|
|
|
+ ALSA_HotplugIteration(NULL, NULL); /* run the check. */
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
-static void ALSA_DetectDevices(void)
|
|
|
+static void ALSA_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
|
|
{
|
|
|
- ALSA_HotplugIteration(); /* run once now before a thread continues to check. */
|
|
|
+ // ALSA doesn't have a concept of a changeable default device, afaik, so we expose a generic default
|
|
|
+ // device here. It's the best we can do at this level.
|
|
|
+ SDL_bool has_default_output = SDL_FALSE, has_default_capture = SDL_FALSE;
|
|
|
+ ALSA_HotplugIteration(&has_default_output, &has_default_capture); // run once now before a thread continues to check. */
|
|
|
+ if (has_default_output) {
|
|
|
+ *default_output = SDL_AddAudioDevice(/*iscapture=*/SDL_FALSE, "ALSA default output device", NULL, SDL_strdup("default"));
|
|
|
+ }
|
|
|
+ if (has_default_capture) {
|
|
|
+ *default_capture = SDL_AddAudioDevice(/*iscapture=*/SDL_TRUE, "ALSA default capture device", NULL, SDL_strdup("default"));
|
|
|
+ }
|
|
|
|
|
|
#if SDL_ALSA_HOTPLUG_THREAD
|
|
|
SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
|
|
@@ -966,6 +976,7 @@ static SDL_bool ALSA_Init(SDL_AudioDriverImpl *impl)
|
|
|
impl->PlayDevice = ALSA_PlayDevice;
|
|
|
impl->CloseDevice = ALSA_CloseDevice;
|
|
|
impl->Deinitialize = ALSA_Deinitialize;
|
|
|
+ impl->WaitCaptureDevice = ALSA_WaitDevice;
|
|
|
impl->CaptureFromDevice = ALSA_CaptureFromDevice;
|
|
|
impl->FlushCapture = ALSA_FlushCapture;
|
|
|
|