SDL: audio: Handle non-power-of-two spec.samples when unsupported

From 90b86b132af02d0a8b1d39103b04ebb667687fec Mon Sep 17 00:00:00 2001
From: Ethan Lee <[EMAIL REDACTED]>
Date: Sun, 17 Jul 2022 10:35:09 -0400
Subject: [PATCH] audio: Handle non-power-of-two spec.samples when unsupported

Fixes #3685
---
 src/audio/SDL_audio.c                   | 13 +++++++++++++
 src/audio/SDL_sysaudio.h                |  1 +
 src/audio/alsa/SDL_alsa_audio.c         |  1 +
 src/audio/coreaudio/SDL_coreaudio.m     |  1 +
 src/audio/directsound/SDL_directsound.c |  1 +
 src/audio/disk/SDL_diskaudio.c          |  1 +
 src/audio/pipewire/SDL_pipewire.c       |  1 +
 src/audio/pulseaudio/SDL_pulseaudio.c   |  1 +
 src/audio/wasapi/SDL_wasapi.c           |  1 +
 9 files changed, 21 insertions(+)

diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index e2306bc03b1..b9c38dc13f0 100644
--- a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -1413,6 +1413,19 @@ open_audio_device(const char *devname, int iscapture,
         }
     }
 
+    /* For backends that require a power-of-two value for spec.samples, take the
+     * value we got from 'desired' and round up to the nearest value
+     */
+    if (!current_audio.impl.SupportsNonPow2Samples && device->spec.samples > 0) {
+        device->spec.samples -= 1;
+        device->spec.samples |= device->spec.samples >> 1;
+        device->spec.samples |= device->spec.samples >> 2;
+        device->spec.samples |= device->spec.samples >> 4;
+        device->spec.samples |= device->spec.samples >> 8;
+        device->spec.samples |= device->spec.samples >> 16;
+        device->spec.samples += 1;
+    }
+
     if (current_audio.impl.OpenDevice(device, devname) < 0) {
         close_audio_device(device);
         return 0;
diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h
index 0f98f90a9c7..6afaae195c6 100644
--- a/src/audio/SDL_sysaudio.h
+++ b/src/audio/SDL_sysaudio.h
@@ -88,6 +88,7 @@ typedef struct SDL_AudioDriverImpl
     SDL_bool OnlyHasDefaultOutputDevice;
     SDL_bool OnlyHasDefaultCaptureDevice;
     SDL_bool AllowsArbitraryDeviceNames;
+    SDL_bool SupportsNonPow2Samples;
 } SDL_AudioDriverImpl;
 
 
diff --git a/src/audio/alsa/SDL_alsa_audio.c b/src/audio/alsa/SDL_alsa_audio.c
index 3f990a2a03a..59de607f8ae 100644
--- a/src/audio/alsa/SDL_alsa_audio.c
+++ b/src/audio/alsa/SDL_alsa_audio.c
@@ -1009,6 +1009,7 @@ ALSA_Init(SDL_AudioDriverImpl * impl)
     impl->FlushCapture = ALSA_FlushCapture;
 
     impl->HasCaptureSupport = SDL_TRUE;
+    impl->SupportsNonPow2Samples = SDL_TRUE;
 
     return SDL_TRUE;   /* this audio target is available. */
 }
diff --git a/src/audio/coreaudio/SDL_coreaudio.m b/src/audio/coreaudio/SDL_coreaudio.m
index 6717feaad99..4d35756579d 100644
--- a/src/audio/coreaudio/SDL_coreaudio.m
+++ b/src/audio/coreaudio/SDL_coreaudio.m
@@ -1173,6 +1173,7 @@ output device (in which case we'll try again). */
 
     impl->ProvidesOwnCallbackThread = SDL_TRUE;
     impl->HasCaptureSupport = SDL_TRUE;
+    impl->SupportsNonPow2Samples = SDL_TRUE;
 
     return SDL_TRUE;   /* this audio target is available. */
 }
diff --git a/src/audio/directsound/SDL_directsound.c b/src/audio/directsound/SDL_directsound.c
index 530d5662d50..885b5f0d541 100644
--- a/src/audio/directsound/SDL_directsound.c
+++ b/src/audio/directsound/SDL_directsound.c
@@ -668,6 +668,7 @@ DSOUND_Init(SDL_AudioDriverImpl * impl)
     impl->GetDefaultAudioInfo = DSOUND_GetDefaultAudioInfo;
 
     impl->HasCaptureSupport = SDL_TRUE;
+    impl->SupportsNonPow2Samples = SDL_TRUE;
 
     return SDL_TRUE;   /* this audio target is available. */
 }
diff --git a/src/audio/disk/SDL_diskaudio.c b/src/audio/disk/SDL_diskaudio.c
index f1edf503eb9..0a7a395e402 100644
--- a/src/audio/disk/SDL_diskaudio.c
+++ b/src/audio/disk/SDL_diskaudio.c
@@ -195,6 +195,7 @@ DISKAUDIO_Init(SDL_AudioDriverImpl * impl)
 
     impl->AllowsArbitraryDeviceNames = SDL_TRUE;
     impl->HasCaptureSupport = SDL_TRUE;
+    impl->SupportsNonPow2Samples = SDL_TRUE;
 
     return SDL_TRUE;   /* this audio target is available. */
 }
diff --git a/src/audio/pipewire/SDL_pipewire.c b/src/audio/pipewire/SDL_pipewire.c
index e56344d155d..0921d26c883 100644
--- a/src/audio/pipewire/SDL_pipewire.c
+++ b/src/audio/pipewire/SDL_pipewire.c
@@ -1417,6 +1417,7 @@ PIPEWIRE_Init(SDL_AudioDriverImpl *impl)
 
     impl->HasCaptureSupport         = SDL_TRUE;
     impl->ProvidesOwnCallbackThread = SDL_TRUE;
+    impl->SupportsNonPow2Samples    = SDL_TRUE;
 
     return SDL_TRUE;
 }
diff --git a/src/audio/pulseaudio/SDL_pulseaudio.c b/src/audio/pulseaudio/SDL_pulseaudio.c
index b61d9989460..288c3d98a26 100644
--- a/src/audio/pulseaudio/SDL_pulseaudio.c
+++ b/src/audio/pulseaudio/SDL_pulseaudio.c
@@ -945,6 +945,7 @@ PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
     impl->GetDefaultAudioInfo = PULSEAUDIO_GetDefaultAudioInfo;
 
     impl->HasCaptureSupport = SDL_TRUE;
+    impl->SupportsNonPow2Samples = SDL_TRUE;
 
     return SDL_TRUE;   /* this audio target is available. */
 }
diff --git a/src/audio/wasapi/SDL_wasapi.c b/src/audio/wasapi/SDL_wasapi.c
index 12236110a03..80a36bdfb69 100644
--- a/src/audio/wasapi/SDL_wasapi.c
+++ b/src/audio/wasapi/SDL_wasapi.c
@@ -617,6 +617,7 @@ WASAPI_Init(SDL_AudioDriverImpl * impl)
     impl->Deinitialize = WASAPI_Deinitialize;
     impl->GetDefaultAudioInfo = WASAPI_GetDefaultAudioInfo;
     impl->HasCaptureSupport = SDL_TRUE;
+    impl->SupportsNonPow2Samples = SDL_TRUE;
 
     return SDL_TRUE;   /* this audio target is available. */
 }