SDL: QNX: ALSA audio support. (#16304)

From e23869d99ba77b0600706b25649229aad3dc3098 Mon Sep 17 00:00:00 2001
From: eleir9268 <[EMAIL REDACTED]>
Date: Sun, 13 Sep 2026 12:10:10 -0400
Subject: [PATCH] QNX: ALSA audio support. (#16304)

---
 docs/README-qnx.md              |  6 ++++
 src/audio/SDL_sysaudio.h        |  4 +++
 src/audio/alsa/SDL_alsa_audio.c | 58 +++++++++++++++++++++++++++++++++
 3 files changed, 68 insertions(+)

diff --git a/docs/README-qnx.md b/docs/README-qnx.md
index bf08e2a78462e..d79d52a3c2670 100644
--- a/docs/README-qnx.md
+++ b/docs/README-qnx.md
@@ -42,3 +42,9 @@ You can find the cross-compiled build tools at https://github.com/qnx-ports/buil
 - Currently, only software and OpenGLES2 rendering is supported.
 - Unless your application is managed by a window manager capable of closing the application, you will need to quit it yourself.
 - Restraining the mouse to a window or warping the mouse cursor will not work.
+
+## Notes - alsa
+- For sound to work on the rpi, you may need to run,
+  ```bash
+  export SDL_AUDIO_DEVICE_SAMPLE_FRAMES=6912 # Or any other value retrieved from speaker-test
+  ```
diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h
index 858354a9aa05a..df22b84cbc703 100644
--- a/src/audio/SDL_sysaudio.h
+++ b/src/audio/SDL_sysaudio.h
@@ -40,7 +40,11 @@
 // these are used when no better specifics are known. We default to CD audio quality.
 #define DEFAULT_AUDIO_PLAYBACK_FORMAT SDL_AUDIO_S16
 #define DEFAULT_AUDIO_PLAYBACK_CHANNELS 2
+#ifdef SDL_PLATFORM_QNXNTO
+#define DEFAULT_AUDIO_PLAYBACK_FREQUENCY 48000
+#else
 #define DEFAULT_AUDIO_PLAYBACK_FREQUENCY 44100
+#endif
 
 #define DEFAULT_AUDIO_RECORDING_FORMAT SDL_AUDIO_S16
 #define DEFAULT_AUDIO_RECORDING_CHANNELS 1
diff --git a/src/audio/alsa/SDL_alsa_audio.c b/src/audio/alsa/SDL_alsa_audio.c
index aa89b1ad9f5aa..976bfbd6d00b1 100644
--- a/src/audio/alsa/SDL_alsa_audio.c
+++ b/src/audio/alsa/SDL_alsa_audio.c
@@ -86,7 +86,11 @@ static snd_pcm_state_t (*ALSA_snd_pcm_state)(snd_pcm_t *);
 static int (*ALSA_snd_device_name_hint)(int, const char *, void ***);
 static char *(*ALSA_snd_device_name_get_hint)(const void *, const char *);
 static int (*ALSA_snd_device_name_free_hint)(void **);
+#ifdef SDL_PLATFORM_QNXNTO
+static int (*ALSA_snd_pcm_avail_delay)(snd_pcm_t *, snd_pcm_sframes_t *, snd_pcm_sframes_t *);
+#else
 static snd_pcm_sframes_t (*ALSA_snd_pcm_avail)(snd_pcm_t *);
+#endif
 static size_t (*ALSA_snd_ctl_card_info_sizeof)(void);
 static size_t (*ALSA_snd_pcm_info_sizeof)(void);
 static int (*ALSA_snd_card_next)(int *);
@@ -176,7 +180,11 @@ static bool load_alsa_syms(void)
     SDL_ALSA_SYM(snd_device_name_hint);
     SDL_ALSA_SYM(snd_device_name_get_hint);
     SDL_ALSA_SYM(snd_device_name_free_hint);
+#ifdef SDL_PLATFORM_QNXNTO
+    SDL_ALSA_SYM(snd_pcm_avail_delay);
+#else
     SDL_ALSA_SYM(snd_pcm_avail);
+#endif
     SDL_ALSA_SYM(snd_ctl_card_info_sizeof);
     SDL_ALSA_SYM(snd_pcm_info_sizeof);
     SDL_ALSA_SYM(snd_card_next);
@@ -379,7 +387,13 @@ static bool ALSA_WaitDevice(SDL_AudioDevice *device)
     const int delay = SDL_clamp(fulldelay, 1, 5);
 
     while (!SDL_GetAtomicInt(&device->shutdown)) {
+#ifdef SDL_PLATFORM_QNXNTO
+        snd_pcm_sframes_t avail_frames;
+        snd_pcm_sframes_t delay_frames;
+        const int rc = ALSA_snd_pcm_avail_delay(device->hidden->pcm, &avail_frames, &delay_frames);
+#else
         const int rc = ALSA_snd_pcm_avail(device->hidden->pcm);
+#endif
         if (rc < 0) {
             const int status = RecoverALSADevice(device->hidden->pcm, rc);
             if (status < 0) {
@@ -388,9 +402,15 @@ static bool ALSA_WaitDevice(SDL_AudioDevice *device)
                 return false;
             }
         }
+#ifdef SDL_PLATFORM_QNXNTO
+        if (avail_frames >= sample_frames) {
+            break;
+        }
+#else
         if (rc >= sample_frames) {
             break;
         }
+#endif
         SDL_Delay(delay);
     }
     return true;
@@ -427,6 +447,29 @@ static bool ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int bu
 
 static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
 {
+#ifdef SDL_PLATFORM_QNXNTO
+    snd_pcm_sframes_t avail_frames;
+    snd_pcm_sframes_t delay_frames;
+    int rc = ALSA_snd_pcm_avail_delay(device->hidden->pcm, &avail_frames, &delay_frames);
+    if ((rc < 0) || (avail_frames == 0)) {
+        // Wait a bit and try again, maybe the hardware isn't quite ready yet?
+        SDL_Delay(1);
+
+        rc = ALSA_snd_pcm_avail_delay(device->hidden->pcm, &avail_frames, &delay_frames);
+        if ((rc < 0) || (avail_frames == 0)) {
+            // We'll catch it next time
+            *buffer_size = 0;
+            return NULL;
+        }
+    }
+
+    const int requested_frames = SDL_min(device->sample_frames, avail_frames);
+    const int requested_bytes = requested_frames * SDL_AUDIO_FRAMESIZE(device->spec);
+    SDL_assert(requested_bytes <= *buffer_size);
+    //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA GETDEVICEBUF: NEED %d BYTES", requested_bytes);
+    *buffer_size = requested_bytes;
+    return device->hidden->mixbuf;
+#else
     snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(device->hidden->pcm);
     if (rc <= 0) {
         // Wait a bit and try again, maybe the hardware isn't quite ready yet?
@@ -446,6 +489,7 @@ static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
     //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA GETDEVICEBUF: NEED %d BYTES", requested_bytes);
     *buffer_size = requested_bytes;
     return device->hidden->mixbuf;
+#endif
 }
 
 static int ALSA_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen)
@@ -453,10 +497,20 @@ static int ALSA_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen)
     const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec);
     SDL_assert((buflen % frame_size) == 0);
 
+#ifdef SDL_PLATFORM_QNXNTO
+    snd_pcm_sframes_t total_available;
+    snd_pcm_sframes_t total_delay;
+
+    (void)ALSA_snd_pcm_avail_delay(device->hidden->pcm, &total_available, &total_delay);
+    if (total_available == 0) {
+        return 0;  // go back to WaitDevice and try again.
+    }
+#else
     const snd_pcm_sframes_t total_available = ALSA_snd_pcm_avail(device->hidden->pcm);
     if (total_available == 0) {
         return 0;  // go back to WaitDevice and try again.
     }
+#endif
 
     const int total_frames = SDL_min(buflen / frame_size, total_available);
     const int rc = ALSA_snd_pcm_readi(device->hidden->pcm, buffer, total_frames);
@@ -1047,12 +1101,16 @@ 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.
+#ifdef SDL_PLATFORM_QNXNTO
+        return CHANS_N_CONFIGURED;
+#else
         status = alsa_chmap_cfg(ctx);
         if (status < 0) {
             return status; // we forward the SDL error
         } else if (status == CHMAP_INSTALLED) {
             return CHANS_N_CONFIGURED; // we are finished here
         }
+#endif
 
         // status == CHANS_N_NEXT
         ALSA_snd_pcm_free_chmaps(ctx->chmap_queries);