alsa: Updated for new SDL3 audio API

main
Ryan C. Gordon 2023-07-04 17:28:04 -04:00
parent 0999a090a7
commit 409b544505
No known key found for this signature in database
GPG Key ID: FA148B892AB48044
3 changed files with 185 additions and 176 deletions

View File

@ -354,7 +354,6 @@ set_option(SDL_CLANG_TIDY "Run clang-tidy static analysis" OFF)
set(SDL_VENDOR_INFO "" CACHE STRING "Vendor name and/or version to add to SDL_REVISION") set(SDL_VENDOR_INFO "" CACHE STRING "Vendor name and/or version to add to SDL_REVISION")
set(SDL_OSS OFF) set(SDL_OSS OFF)
set(SDL_ALSA OFF)
set(SDL_JACK OFF) set(SDL_JACK OFF)
set(SDL_SNDIO OFF) set(SDL_SNDIO OFF)

View File

@ -20,6 +20,8 @@
*/ */
#include "SDL_internal.h" #include "SDL_internal.h"
// !!! FIXME: Clean out the fprintf and printf calls, replace with SDL_Log
#ifdef SDL_AUDIO_DRIVER_ALSA #ifdef SDL_AUDIO_DRIVER_ALSA
#ifndef SDL_ALSA_NON_BLOCKING #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_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_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_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 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); 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_open);
SDL_ALSA_SYM(snd_pcm_close); SDL_ALSA_SYM(snd_pcm_close);
SDL_ALSA_SYM(snd_pcm_start);
SDL_ALSA_SYM(snd_pcm_writei); SDL_ALSA_SYM(snd_pcm_writei);
SDL_ALSA_SYM(snd_pcm_readi); SDL_ALSA_SYM(snd_pcm_readi);
SDL_ALSA_SYM(snd_pcm_recover); 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) static const char *get_audio_device(void *handle, const int channels)
{ {
const char *device; SDL_assert(handle != NULL); // SDL2 used NULL to mean "default" but that's not true in SDL3.
if (handle != NULL) { if (SDL_strcmp((const char *) handle, "default") == 0) {
return (const char *)handle; const char *device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */
}
/* !!! 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) { if (device != NULL) {
return device; return device;
} } else if (channels == 6) {
if (channels == 6) {
return "plug:surround51"; return "plug:surround51";
} else if (channels == 4) { } else if (channels == 4) {
return "plug:surround40"; return "plug:surround40";
} }
return "default"; return "default";
} }
/* This function waits until it is possible to write a full sound buffer */ return (const char *)handle;
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! */
}
}
#endif
} }
/* !!! FIXME: is there a channel swizzler in alsalib instead? */ /* !!! 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 */ #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) * const int frame_size = ((SDL_AUDIO_BITSIZE(device->spec.format)) / 8) *
device->spec.channels; 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, int status = ALSA_snd_pcm_writei(device->hidden->pcm_handle,
sample_buf, frames_left); sample_buf, frames_left);
@ -377,17 +376,16 @@ static void ALSA_PlayDevice(SDL_AudioDevice *device)
if (status < 0) { if (status < 0) {
/* Hmm, not much we can do - abort */ /* Hmm, not much we can do - abort */
SDL_LogError(SDL_LOG_CATEGORY_AUDIO, SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
"ALSA write failed (unrecoverable): %s\n", "ALSA write failed (unrecoverable): %s",
ALSA_snd_strerror(status)); ALSA_snd_strerror(status));
SDL_OpenedAudioDeviceDisconnected(device); SDL_AudioDeviceDisconnected(device);
return; return;
} }
continue; continue;
} else if (status == 0) { } else if (status == 0) {
/* No frames were written (no available space in pcm device). /* No frames were written (no available space in pcm device).
Allow other threads to catch up. */ Allow other threads to catch up. */
Uint32 delay = (frames_left / 2 * 1000) / device->spec.freq; SDL_Delay((frames_left / 2 * 1000) / device->spec.freq);
SDL_Delay(delay);
} }
sample_buf += status * frame_size; 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; return device->hidden->mixbuf;
} }
@ -411,10 +409,8 @@ static int ALSA_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buf
SDL_assert((buflen % frame_size) == 0); SDL_assert((buflen % frame_size) == 0);
while (frames_left > 0 && SDL_AtomicGet(&device->enabled)) { while ((frames_left > 0) && !SDL_AtomicGet(&device->shutdown)) {
int status; int status = ALSA_snd_pcm_readi(device->hidden->pcm_handle,
status = ALSA_snd_pcm_readi(device->hidden->pcm_handle,
sample_buf, frames_left); sample_buf, frames_left);
if (status == -EAGAIN) { if (status == -EAGAIN) {
@ -450,18 +446,18 @@ static void ALSA_FlushCapture(SDL_AudioDevice *device)
static void ALSA_CloseDevice(SDL_AudioDevice *device) static void ALSA_CloseDevice(SDL_AudioDevice *device)
{ {
if (device->hidden) {
if (device->hidden->pcm_handle) { if (device->hidden->pcm_handle) {
/* Wait for the submitted audio to drain /* Wait for the submitted audio to drain
ALSA_snd_pcm_drop() can hang, so don't use that. ALSA_snd_pcm_drop() can hang, so don't use that.
*/ */
Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq) * 2; SDL_Delay(((device->sample_frames * 1000) / device->spec.freq) * 2);
SDL_Delay(delay);
ALSA_snd_pcm_close(device->hidden->pcm_handle); ALSA_snd_pcm_close(device->hidden->pcm_handle);
} }
SDL_free(device->hidden->mixbuf); SDL_free(device->hidden->mixbuf);
SDL_free(device->hidden); SDL_free(device->hidden);
} }
}
static int ALSA_set_buffer_size(SDL_AudioDevice *device, snd_pcm_hw_params_t *params) 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); ALSA_snd_pcm_hw_params_copy(hwparams, params);
/* Attempt to match the period size to the requested buffer size */ /* 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( status = ALSA_snd_pcm_hw_params_set_period_size_near(
device->hidden->pcm_handle, hwparams, &persize, NULL); device->hidden->pcm_handle, hwparams, &persize, NULL);
if (status < 0) { if (status < 0) {
@ -502,7 +498,7 @@ static int ALSA_set_buffer_size(SDL_AudioDevice *device, snd_pcm_hw_params_t *pa
return -1; return -1;
} }
device->spec.samples = persize; device->sample_frames = persize;
/* This is useful for debugging */ /* This is useful for debugging */
if (SDL_getenv("SDL_AUDIO_ALSA_DEBUG")) { 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; 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; 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 */ /* 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) { if (device->hidden == NULL) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
SDL_zerop(device->hidden);
/* Open the audio device */ /* Open the audio device */
/* Name of device should depend on # channels in spec */ /* Name of device should depend on # channels in spec */
snd_pcm_t *pcm_handle = NULL;
status = ALSA_snd_pcm_open(&pcm_handle, status = ALSA_snd_pcm_open(&pcm_handle,
get_audio_device(device->handle, device->spec.channels), get_audio_device(device->handle, device->spec.channels),
iscapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, 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; device->hidden->pcm_handle = pcm_handle;
/* Figure out what the hardware is capable of */ /* Figure out what the hardware is capable of */
snd_pcm_hw_params_t *hwparams = NULL;
snd_pcm_hw_params_alloca(&hwparams); snd_pcm_hw_params_alloca(&hwparams);
status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams); status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams);
if (status < 0) { 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 */ /* 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) { while ((test_format = *(closefmts++)) != 0) {
switch (test_format) { switch (test_format) {
case SDL_AUDIO_U8: case SDL_AUDIO_U8:
@ -605,7 +592,7 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device, const char *devname)
} }
} }
if (!test_format) { if (!test_format) {
return SDL_SetError("%s: Unsupported audio format", "alsa"); return SDL_SetError("ALSA: Unsupported audio format");
} }
device->spec.format = test_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; device->hidden->swizzle_func = swizzle_alsa_channels;
#ifdef SND_CHMAP_API_VERSION #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) { if (chmap) {
char chmap_str[64];
if (ALSA_snd_pcm_chmap_print(chmap, sizeof(chmap_str), chmap_str) > 0) { 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 || if (SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0 ||
SDL_strcmp("FL FR FC LFE SL SR", 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 */ /* Set the number of channels */
status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams, status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
device->spec.channels); device->spec.channels);
channels = device->spec.channels; unsigned int channels = device->spec.channels;
if (status < 0) { if (status < 0) {
status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels); status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
if (status < 0) { if (status < 0) {
@ -639,7 +627,7 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device, const char *devname)
} }
/* Set the audio rate */ /* 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, status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams,
&rate, NULL); &rate, NULL);
if (status < 0) { if (status < 0) {
@ -654,12 +642,13 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device, const char *devname)
} }
/* Set the software parameters */ /* Set the software parameters */
snd_pcm_sw_params_t *swparams = NULL;
snd_pcm_sw_params_alloca(&swparams); snd_pcm_sw_params_alloca(&swparams);
status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams); status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
if (status < 0) { if (status < 0) {
return SDL_SetError("ALSA: Couldn't get software config: %s", ALSA_snd_strerror(status)); 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) { if (status < 0) {
return SDL_SetError("Couldn't set minimum available samples: %s", ALSA_snd_strerror(status)); 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)); return SDL_SetError("Couldn't set software audio parameters: %s", ALSA_snd_strerror(status));
} }
/* Calculate the final parameters for this audio specification */ // Calculate the final parameters for this audio specification
SDL_CalculateAudioSpec(&device->spec); SDL_UpdatedAudioDeviceFormat(device);
/* Allocate mixing buffer */ // Allocate mixing buffer
if (!iscapture) { if (!iscapture) {
device->hidden->mixlen = device->spec.size; device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size);
device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->hidden->mixlen);
if (device->hidden->mixbuf == NULL) { if (device->hidden->mixbuf == NULL) {
return SDL_OutOfMemory(); 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 #if !SDL_ALSA_NON_BLOCKING
@ -692,6 +680,8 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device, const char *devname)
} }
#endif #endif
ALSA_snd_pcm_start(pcm_handle);
/* We're ready to rock and roll. :-) */ /* We're ready to rock and roll. :-) */
return 0; return 0;
} }
@ -703,7 +693,7 @@ typedef struct ALSA_Device
struct ALSA_Device *next; struct ALSA_Device *next;
} ALSA_Device; } 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)); ALSA_Device *dev = SDL_malloc(sizeof(ALSA_Device));
char *desc; 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 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; void **hints = NULL;
ALSA_Device *dev; ALSA_Device *unseen = NULL;
ALSA_Device *unseen; ALSA_Device *seen = NULL;
ALSA_Device *seen;
ALSA_Device *next;
ALSA_Device *prev;
if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) { if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) {
int i, j;
const char *match = NULL; const char *match = NULL;
int bestmatch = 0xFFFF; int bestmatch = 0xFFFF;
int has_default = -1;
size_t match_len = 0; size_t match_len = 0;
int defaultdev = -1;
static const char *const prefixes[] = { static const char *const prefixes[] = {
"hw:", "sysdefault:", "default:", NULL "hw:", "sysdefault:", "default:", NULL
}; };
@ -791,18 +777,18 @@ static void ALSA_HotplugIteration(void)
actual hardware. It could be prefixed with "hw:" or "default:" actual hardware. It could be prefixed with "hw:" or "default:"
or "sysdefault:" and maybe others. Go through the list and see or "sysdefault:" and maybe others. Go through the list and see
if we can find a preferred prefix for the system. */ 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"); char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
if (name == NULL) { if (name == NULL) {
continue; continue;
} }
/* full name, not a prefix */ if (SDL_strcmp(name, "default") == 0) {
if ((defaultdev == -1) && (SDL_strcmp(name, "default") == 0)) { if (has_default < 0) {
defaultdev = i; has_default = i;
} }
} else {
for (j = 0; prefixes[j]; j++) { for (int j = 0; prefixes[j]; j++) {
const char *prefix = prefixes[j]; const char *prefix = prefixes[j];
const size_t prefixlen = SDL_strlen(prefix); const size_t prefixlen = SDL_strlen(prefix);
if (SDL_strncmp(name, prefix, prefixlen) == 0) { if (SDL_strncmp(name, prefix, prefixlen) == 0) {
@ -816,38 +802,45 @@ static void ALSA_HotplugIteration(void)
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. */
} }
name = ALSA_snd_device_name_get_hint(hints[i], "NAME"); /* look through the list of device names to find matches */
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) { if (name == NULL) {
continue; continue;
} }
/* only want physical hardware interfaces */ // only want physical hardware interfaces
if (match == NULL || (SDL_strncmp(name, match, match_len) == 0)) { 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"); 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 isoutput = (ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0);
const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0); const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0);
SDL_bool have_output = SDL_FALSE; SDL_bool have_output = SDL_FALSE;
SDL_bool have_input = SDL_FALSE; SDL_bool have_input = SDL_FALSE;
free(ioid); /* This should NOT be SDL_free() */ free(ioid);
if (!isoutput && !isinput) { if (!isoutput && !isinput) {
free(name); /* This should NOT be SDL_free() */ free(name);
continue; continue;
} }
prev = NULL; if (is_default) {
for (dev = unseen; dev; dev = next) { if (has_default_output && isoutput) {
*has_default_output = SDL_TRUE;
} else if (has_default_capture && isinput) {
*has_default_capture = SDL_TRUE;
}
free(name);
continue;
}
ALSA_Device *prev = NULL;
ALSA_Device *next;
for (ALSA_Device *dev = unseen; dev; dev = next) {
next = dev->next; next = dev->next;
if ((SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture))) { if ((SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture))) {
if (prev) { if (prev) {
@ -878,16 +871,24 @@ static void ALSA_HotplugIteration(void)
free(name); /* This should NOT be SDL_free() */ free(name); /* This should NOT be SDL_free() */
} }
}
ALSA_snd_device_name_free_hint(hints); ALSA_snd_device_name_free_hint(hints);
hotplug_devices = seen; /* now we have a known-good list of attached devices. */ hotplug_devices = seen; /* now we have a known-good list of attached devices. */
/* report anything still in unseen as removed. */ /* 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);*/ /*printf("ALSA: removing usb %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
next = dev->next; 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->name);
SDL_free(dev); SDL_free(dev);
} }
@ -909,16 +910,25 @@ static int SDLCALL ALSA_HotplugThread(void *arg)
SDL_Delay(100); SDL_Delay(100);
} }
ALSA_HotplugIteration(); /* run the check. */ ALSA_HotplugIteration(NULL, NULL); /* run the check. */
} }
return 0; return 0;
} }
#endif #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 #if SDL_ALSA_HOTPLUG_THREAD
SDL_AtomicSet(&ALSA_hotplug_shutdown, 0); SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
@ -966,6 +976,7 @@ static SDL_bool ALSA_Init(SDL_AudioDriverImpl *impl)
impl->PlayDevice = ALSA_PlayDevice; impl->PlayDevice = ALSA_PlayDevice;
impl->CloseDevice = ALSA_CloseDevice; impl->CloseDevice = ALSA_CloseDevice;
impl->Deinitialize = ALSA_Deinitialize; impl->Deinitialize = ALSA_Deinitialize;
impl->WaitCaptureDevice = ALSA_WaitDevice;
impl->CaptureFromDevice = ALSA_CaptureFromDevice; impl->CaptureFromDevice = ALSA_CaptureFromDevice;
impl->FlushCapture = ALSA_FlushCapture; impl->FlushCapture = ALSA_FlushCapture;

View File

@ -34,7 +34,6 @@ struct SDL_PrivateAudioData
/* Raw mixing buffer */ /* Raw mixing buffer */
Uint8 *mixbuf; Uint8 *mixbuf;
int mixlen;
/* swizzle function */ /* swizzle function */
void (*swizzle_func)(SDL_AudioDevice *_this, void *buffer, Uint32 bufferlen); void (*swizzle_func)(SDL_AudioDevice *_this, void *buffer, Uint32 bufferlen);