From 483ceb53559f2966164de6b47181b639e0a9f09d Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Sun, 15 Dec 2024 16:48:21 -0500
Subject: [PATCH] alsa: Reenable SDL's internal channel map support when ALSA
can't swizzle.
---
src/audio/alsa/SDL_alsa_audio.c | 288 ++++----------------------------
src/audio/alsa/SDL_alsa_audio.h | 6 -
2 files changed, 36 insertions(+), 258 deletions(-)
diff --git a/src/audio/alsa/SDL_alsa_audio.c b/src/audio/alsa/SDL_alsa_audio.c
index 1b635dbf38962..8969a6448f7e6 100644
--- a/src/audio/alsa/SDL_alsa_audio.c
+++ b/src/audio/alsa/SDL_alsa_audio.c
@@ -345,223 +345,6 @@ static char *get_pcm_str(void *handle)
return pcm_str;
}
-// SDL channel map with alsa names "FL FR"
-// The literal names are SDL names.
-// Faith: loading the whole frame in one shot may help naive compilers.
-#define SWIZ2(T) \
- static void swizzle_alsa_channels_2_##T(int *swizzle_map, void *buffer, const Uint32 bufferlen) \
- { \
- T *ptr = (T *)buffer; \
- Uint32 i; \
- for (i = 0; i < bufferlen; i++, ptr += 2) { \
- const T front_left = ptr[0]; \
- const T front_right = ptr[1]; \
- ptr[swizzle_map[0]] = front_left; \
- ptr[swizzle_map[1]] = front_right; \
- } \
- }
-// SDL channel map with alsa names "FL FR LFE"
-// The literal names are SDL names.
-// Faith: loading the whole frame in one shot may help naive compilers.
-#define SWIZ3(T) \
- static void swizzle_alsa_channels_3_##T(int *swizzle_map, void *buffer, const Uint32 bufferlen) \
- { \
- T *ptr = (T *)buffer; \
- Uint32 i; \
- for (i = 0; i < bufferlen; i++, ptr += 3) { \
- const T front_left = ptr[0]; \
- const T front_right = ptr[1]; \
- const T subwoofer = ptr[2]; \
- ptr[swizzle_map[0]] = front_left; \
- ptr[swizzle_map[1]] = front_right; \
- ptr[swizzle_map[2]] = subwoofer; \
- } \
- }
-// SDL channel map with alsa names "FL FR RL RR";
-// The literal names are SDL names.
-// Faith: loading the whole frame in one shot may help naive compilers.
-#define SWIZ4(T) \
- static void swizzle_alsa_channels_4_##T(int *swizzle_map, void *buffer, const Uint32 bufferlen) \
- { \
- T *ptr = (T *)buffer; \
- Uint32 i; \
- for (i = 0; i < bufferlen; i++, ptr += 4) { \
- const T front_left = ptr[0]; \
- const T front_right = ptr[1]; \
- const T back_left = ptr[2]; \
- const T back_right = ptr[3]; \
- ptr[swizzle_map[0]] = front_left; \
- ptr[swizzle_map[1]] = front_right; \
- ptr[swizzle_map[2]] = back_left; \
- ptr[swizzle_map[3]] = back_right; \
- } \
- }
-// SDL channel map with alsa names "FL FR LFE RL RR"
-// The literal names are SDL names.
-// Faith: loading the whole frame in one shot may help naive compilers.
-#define SWIZ5(T) \
- static void swizzle_alsa_channels_5_##T(int *swizzle_map, void *buffer, const Uint32 bufferlen) \
- { \
- T *ptr = (T *)buffer; \
- Uint32 i; \
- for (i = 0; i < bufferlen; i++, ptr += 5) { \
- const T front_left = ptr[0]; \
- const T front_right = ptr[1]; \
- const T subwoofer = ptr[2]; \
- const T back_left = ptr[3]; \
- const T back_right = ptr[4]; \
- ptr[swizzle_map[0]] = front_left; \
- ptr[swizzle_map[1]] = front_right; \
- ptr[swizzle_map[2]] = subwoofer; \
- ptr[swizzle_map[3]] = back_left; \
- ptr[swizzle_map[4]] = back_right; \
- } \
- }
-// SDL channel map with alsa names "FL FR FC LFE [SL|RL] [SR|RR]"
-// The literal names are SDL names.
-// Faith: loading the whole frame in one shot may help naive compilers.
-#define SWIZ6(T) \
- static void swizzle_alsa_channels_6_##T(int *swizzle_map, void *buffer, const Uint32 bufferlen) \
- { \
- T *ptr = (T *)buffer; \
- Uint32 i; \
- for (i = 0; i < bufferlen; i++, ptr += 6) { \
- const T front_left = ptr[0]; \
- const T front_right = ptr[1]; \
- const T front_center = ptr[2]; \
- const T subwoofer = ptr[3]; \
- const T side_left = ptr[4]; \
- const T side_right = ptr[5]; \
- ptr[swizzle_map[0]] = front_left; \
- ptr[swizzle_map[1]] = front_right; \
- ptr[swizzle_map[2]] = front_center; \
- ptr[swizzle_map[3]] = subwoofer; \
- ptr[swizzle_map[4]] = side_left; \
- ptr[swizzle_map[5]] = side_right; \
- } \
- }
-// SDL channel map with alsa names "FL FR FC LFE RC SL SR".
-// The literal names are SDL names.
-// Faith: loading the whole frame in one shot may help naive compilers.
-#define SWIZ7(T) \
- static void swizzle_alsa_channels_7_##T(int *swizzle_map, void *buffer, const Uint32 bufferlen) \
- { \
- T *ptr = (T *)buffer; \
- Uint32 i; \
- for (i = 0; i < bufferlen; i++, ptr += 7) { \
- const T front_left = ptr[0]; \
- const T front_right = ptr[1]; \
- const T front_center = ptr[2]; \
- const T subwoofer = ptr[3]; \
- const T back_center = ptr[4]; \
- const T side_left = ptr[5]; \
- const T side_right = ptr[6]; \
- ptr[swizzle_map[0]] = front_left; \
- ptr[swizzle_map[1]] = front_right; \
- ptr[swizzle_map[2]] = front_center; \
- ptr[swizzle_map[3]] = subwoofer; \
- ptr[swizzle_map[4]] = back_center; \
- ptr[swizzle_map[5]] = side_left; \
- ptr[swizzle_map[6]] = side_right; \
- } \
- }
-
-// SDL channel map with alsa names "FL FR FC LFE RL RR SL SR"
-// The literal names are SDL names.
-// Faith: loading the whole frame in one shot may help naive compilers.
-#define SWIZ8(T) \
- static void swizzle_alsa_channels_8_##T(int *swizzle_map, void *buffer, const Uint32 bufferlen) \
- { \
- T *ptr = (T *)buffer; \
- Uint32 i; \
- for (i = 0; i < bufferlen; i++, ptr += 8) { \
- const T front_left = ptr[0]; \
- const T front_right = ptr[1]; \
- const T front_center = ptr[2]; \
- const T subwoofer = ptr[3]; \
- const T back_left = ptr[4]; \
- const T back_right = ptr[5]; \
- const T side_left = ptr[6]; \
- const T side_right = ptr[7]; \
- ptr[swizzle_map[0]] = front_left; \
- ptr[swizzle_map[1]] = front_right; \
- ptr[swizzle_map[2]] = front_center; \
- ptr[swizzle_map[3]] = subwoofer; \
- ptr[swizzle_map[4]] = back_left; \
- ptr[swizzle_map[5]] = back_right; \
- ptr[swizzle_map[6]] = side_left; \
- ptr[swizzle_map[7]] = side_right; \
- } \
- }
-
-#define CHANNEL_SWIZZLE(x) \
- x(Uint64) \
- x(Uint32) \
- x(Uint16) \
- x(Uint8)
-
-CHANNEL_SWIZZLE(SWIZ2)
-CHANNEL_SWIZZLE(SWIZ3)
-CHANNEL_SWIZZLE(SWIZ4)
-CHANNEL_SWIZZLE(SWIZ5)
-CHANNEL_SWIZZLE(SWIZ6)
-CHANNEL_SWIZZLE(SWIZ7)
-CHANNEL_SWIZZLE(SWIZ8)
-
-#undef CHANNEL_SWIZZLE
-#undef SWIZ2
-#undef SWIZ3
-#undef SWIZ4
-#undef SWIZ5
-#undef SWIZ6
-#undef SWIZ7
-#undef SWIZ8
-
-// Called right before feeding device->hidden->mixbuf to the hardware. Swizzle
-// channels from Windows/Mac order to the format alsalib will want.
-static void swizzle_alsa_channels(SDL_AudioDevice *device, void *buffer, Uint32 bufferlen)
-{
- int *swizzle_map = device->hidden->swizzle_map;
- switch (device->spec.channels) {
-#define CHANSWIZ(chans) \
- case chans: \
- switch ((device->spec.format & (0xFF))) { \
- case 8: \
- swizzle_alsa_channels_##chans##_Uint8(swizzle_map, buffer, bufferlen); \
- break; \
- case 16: \
- swizzle_alsa_channels_##chans##_Uint16(swizzle_map, buffer, bufferlen); \
- break; \
- case 32: \
- swizzle_alsa_channels_##chans##_Uint32(swizzle_map, buffer, bufferlen); \
- break; \
- case 64: \
- swizzle_alsa_channels_##chans##_Uint64(swizzle_map, buffer, bufferlen); \
- break; \
- default: \
- SDL_assert(!"unhandled bitsize"); \
- break; \
- } \
- return;
-
- CHANSWIZ(2);
- CHANSWIZ(3);
- CHANSWIZ(4);
- CHANSWIZ(5);
- CHANSWIZ(6);
- CHANSWIZ(7);
- CHANSWIZ(8);
-#undef CHANSWIZ
- default:
- break;
- }
-}
-
-// Some devices have the right channel map, no swizzling necessary
-static void no_swizzle(SDL_AudioDevice *device, void *buffer, Uint32 bufferlen)
-{
-}
-
// This function waits until it is possible to write a full sound buffer
static bool ALSA_WaitDevice(SDL_AudioDevice *device)
{
@@ -597,8 +380,6 @@ static bool ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int bu
const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec);
snd_pcm_uframes_t frames_left = (snd_pcm_uframes_t) (buflen / frame_size);
- device->hidden->swizzle_func(device, sample_buf, frames_left);
-
while ((frames_left > 0) && !SDL_GetAtomicInt(&device->shutdown)) {
const int rc = ALSA_snd_pcm_writei(device->hidden->pcm, sample_buf, frames_left);
//SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA PLAYDEVICE: WROTE %d of %d bytes", (rc >= 0) ? ((int) (rc * frame_size)) : rc, (int) (frames_left * frame_size));
@@ -664,8 +445,6 @@ static int ALSA_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen)
return -1;
}
return 0; // go back to WaitDevice and try again.
- } else if (rc > 0) {
- device->hidden->swizzle_func(device, buffer, total_frames - rc);
}
//SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA: recorded %d bytes", rc * frame_size);
@@ -692,23 +471,6 @@ static void ALSA_CloseDevice(SDL_AudioDevice *device)
}
-// Swizzle channels to match SDL defaults.
-// These are swizzles _from_ SDL's layouts to what ALSA wants.
-
-#if 0
-// 5.1 swizzle:
-// https://bugzilla.libsdl.org/show_bug.cgi?id=110
-// "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
-// and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
-static const int swizzle_alsa_channels_6[6] = { 0, 1, 4, 5, 2, 3 };
-
-// 7.1 swizzle:
-// https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/mapping-stream-formats-to-speaker-configurations
-// For Linux ALSA, this appears to be FL-FR-RL-RR-C-LFE-SL-SR
-// and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-SL-SR-RL-RR"
-static const int swizzle_alsa_channels_8[8] = { 0, 1, 6, 7, 2, 3, 4, 5 };
-#endif
-
// To make easier to track parameters during the whole alsa pcm configuration:
struct ALSA_pcm_cfg_ctx {
SDL_AudioDevice *device;
@@ -876,23 +638,28 @@ static void sdl_6chans_set_rear_or_side_channels_from_alsa_6chans(unsigned int *
#undef HAVE_SIDE
#undef HAVE_BOTH
-static void swizzle_map_compute_alsa_subscan(struct ALSA_pcm_cfg_ctx *ctx, unsigned int sdl_pos_idx)
+static void swizzle_map_compute_alsa_subscan(struct ALSA_pcm_cfg_ctx *ctx, int *swizzle_map, unsigned int sdl_pos_idx)
{
+ swizzle_map[sdl_pos_idx] = -1;
for (unsigned int alsa_pos_idx = 0; ; alsa_pos_idx++) {
SDL_assert(alsa_pos_idx != ctx->chans_n); // no 0 channels or not found matching position should happen here (actually enforce playback/recording symmetry).
if (ctx->alsa_chmap_installed[alsa_pos_idx] == ctx->sdl_chmap[sdl_pos_idx]) {
LOGDEBUG("swizzle SDL %u <-> alsa %u", sdl_pos_idx,alsa_pos_idx);
- ctx->device->hidden->swizzle_map[sdl_pos_idx] = alsa_pos_idx;
+ swizzle_map[sdl_pos_idx] = (int) alsa_pos_idx;
return;
}
}
}
// XXX: this must stay playback/recording symetric.
-static void swizzle_map_compute(struct ALSA_pcm_cfg_ctx *ctx)
+static void swizzle_map_compute(struct ALSA_pcm_cfg_ctx *ctx, int *swizzle_map, bool *needs_swizzle)
{
+ *needs_swizzle = false;
for (unsigned int sdl_pos_idx = 0; sdl_pos_idx != ctx->chans_n; sdl_pos_idx++) {
- swizzle_map_compute_alsa_subscan(ctx, sdl_pos_idx);
+ swizzle_map_compute_alsa_subscan(ctx, swizzle_map, sdl_pos_idx);
+ if (swizzle_map[sdl_pos_idx] != sdl_pos_idx) {
+ *needs_swizzle = true;
+ }
}
}
@@ -1093,7 +860,6 @@ static int alsa_chmap_cfg(struct ALSA_pcm_cfg_ctx *ctx)
if (ctx->chmap_queries == NULL) {
// We couldn't query the channel map, assume no swizzle necessary
LOGDEBUG("couldn't query channel map, swizzling off");
- ctx->device->hidden->swizzle_func = no_swizzle;
return CHMAP_INSTALLED;
}
@@ -1101,23 +867,39 @@ static int alsa_chmap_cfg(struct ALSA_pcm_cfg_ctx *ctx)
status = alsa_chmap_cfg_ordered(ctx); // we prefer first channel maps we don't need to swizzle
if (status == CHMAP_INSTALLED) {
LOGDEBUG("swizzling off");
- ctx->device->hidden->swizzle_func = no_swizzle;
return status;
- }
- if (status != CHMAP_NOT_FOUND) {
+ } else if (status != CHMAP_NOT_FOUND) {
return status; // < 0 error code
}
+
// Fall-thru
//----------------------------------------------------------------------------------------------
status = alsa_chmap_cfg_unordered(ctx); // those we will have to swizzle
if (status == CHMAP_INSTALLED) {
LOGDEBUG("swizzling on");
- swizzle_map_compute(ctx); // fine grained swizzle configuration
- ctx->device->hidden->swizzle_func = swizzle_alsa_channels;
- return status;
+
+ bool isstack;
+ int *swizzle_map = SDL_small_alloc(int, ctx->chans_n, &isstack);
+ if (!swizzle_map) {
+ status = -1;
+ } else {
+ bool needs_swizzle;
+ swizzle_map_compute(ctx, swizzle_map, &needs_swizzle); // fine grained swizzle configuration
+ if (needs_swizzle) {
+ // let SDL's swizzler handle this one.
+ ctx->device->chmap = SDL_ChannelMapDup(swizzle_map, ctx->chans_n);
+ if (!ctx->device->chmap) {
+ status = -1;
+ }
+ }
+ SDL_small_free(swizzle_map, isstack);
+ }
}
- if (status == CHMAP_NOT_FOUND)
+
+ if (status == CHMAP_NOT_FOUND) {
return CHANS_N_NEXT;
+ }
+
return status; // < 0 error code
}
@@ -1249,10 +1031,12 @@ static int ALSA_pcm_cfg_hw_chans_n_scan(struct ALSA_pcm_cfg_ctx *ctx, unsigned i
// Here the alsa pcm is in SND_PCM_STATE_PREPARED state, let's figure out a good fit for
// SDL channel map, it may request to change the target number of channels though.
status = alsa_chmap_cfg(ctx);
- if (status < 0)
+ if (status < 0) {
return status; // we forward the SDL error
- if (status == CHMAP_INSTALLED)
+ } else if (status == CHMAP_INSTALLED) {
return CHANS_N_CONFIGURED; // we are finished here
+ }
+
// status == CHANS_N_NEXT
ALSA_snd_pcm_free_chmaps(ctx->chmap_queries);
ALSA_snd_pcm_hw_free(ctx->device->hidden->pcm); // uninstall those hw params
diff --git a/src/audio/alsa/SDL_alsa_audio.h b/src/audio/alsa/SDL_alsa_audio.h
index 952bfb26b7c88..e840b2f6c5a37 100644
--- a/src/audio/alsa/SDL_alsa_audio.h
+++ b/src/audio/alsa/SDL_alsa_audio.h
@@ -36,12 +36,6 @@ struct SDL_PrivateAudioData
// Raw mixing buffer
Uint8 *mixbuf;
-
- // swizzle function
- void (*swizzle_func)(SDL_AudioDevice *_this, void *buffer, Uint32 bufferlen);
- // Up to a channel map of 8 channels, will define the sample indexes into the alsa frame
- // from a sdl sample index.
- int swizzle_map[SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX];
};
#endif // SDL_ALSA_audio_h_