SDL: pipewire: Report correct device default formats instead of hardcoding Float32. (afc1d)

From afc1d9122b39df6fd50849cf7ae2fa9ba87e6bca Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Thu, 30 Jan 2025 02:41:43 -0500
Subject: [PATCH] pipewire: Report correct device default formats instead of
 hardcoding Float32.

The comment in the source wasn't true; PipeWire doesn't _have_ to work in
float format. It presumably does if it has to mix, but if a game is the only
thing making noise on the system--a common scenario--then it might be able to
pass, say, Sint16 data straight through to the hardware without conversion.

Fixes #12129.

(cherry picked from commit 943c4abcb4ad633818471f3290c5e8477d7b5579)
---
 src/audio/pipewire/SDL_pipewire.c | 32 ++++++++++++++++++++++++++++++-
 1 file changed, 31 insertions(+), 1 deletion(-)

diff --git a/src/audio/pipewire/SDL_pipewire.c b/src/audio/pipewire/SDL_pipewire.c
index 5d1bfc28dedb7..8b0772bc9e77c 100644
--- a/src/audio/pipewire/SDL_pipewire.c
+++ b/src/audio/pipewire/SDL_pipewire.c
@@ -574,6 +574,25 @@ static SDL_bool get_int_param(const struct spa_pod *param, Uint32 key, int *val)
     return SDL_FALSE;
 }
 
+static SDL_AudioFormat SPAFormatToSDL(enum spa_audio_format spafmt)
+{
+    switch (spafmt) {
+        #define CHECKFMT(spa,sdl) case SPA_AUDIO_FORMAT_##spa: return AUDIO_##sdl
+        CHECKFMT(U8, U8);
+        CHECKFMT(S8, S8);
+        CHECKFMT(S16_LE, S16LSB);
+        CHECKFMT(S16_BE, S16MSB);
+        CHECKFMT(S32_LE, S32LSB);
+        CHECKFMT(S32_BE, S32MSB);
+        CHECKFMT(F32_LE, F32LSB);
+        CHECKFMT(F32_BE, F32MSB);
+        #undef CHECKFMT
+        default: break;
+    }
+
+    return 0;
+}
+
 /* Interface node callbacks */
 static void node_event_info(void *object, const struct pw_node_info *info)
 {
@@ -602,6 +621,15 @@ static void node_event_param(void *object, int seq, uint32_t id, uint32_t index,
     struct node_object *node = object;
     struct io_node *io = node->userdata;
 
+    if ((id == SPA_PARAM_Format) && (io->spec.format == 0)) {
+        struct spa_audio_info_raw info;
+        SDL_zero(info);
+        if (spa_format_audio_raw_parse(param, &info) == 0) {
+            /*SDL_Log("Sink Format: %d, Rate: %d Hz, Channels: %d", info.format, info.rate, info.channels);*/
+            io->spec.format = SPAFormatToSDL(info.format);
+        }
+    }
+
     /* Get the default frequency */
     if (io->spec.freq == 0) {
         get_range_param(param, SPA_FORMAT_AUDIO_rate, &io->spec.freq, NULL, NULL);
@@ -719,7 +747,9 @@ static void registry_event_global_callback(void *object, uint32_t id, uint32_t p
                 /* Begin setting the node properties */
                 io->id = id;
                 io->is_capture = is_capture;
-                io->spec.format = AUDIO_F32; /* Pipewire uses floats internally, other formats require conversion. */
+                if (io->spec.format == 0) {
+                    io->spec.format = AUDIO_S16;  /* we'll go conservative here if for some reason the format isn't known. */
+                }
                 io->name = io->buf;
                 io->path = io->buf + desc_buffer_len;
                 SDL_strlcpy(io->buf, node_desc, desc_buffer_len);