From 2f43f7bc53e9912e7858aca5d4410d4f8b0fa743 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Wed, 13 Sep 2023 11:03:17 -0400
Subject: [PATCH] audio: Allow querying of device buffer size.
---
include/SDL3/SDL_audio.h | 14 ++++++++-
src/audio/SDL_audio.c | 53 +++++++++++++++++++----------------
src/dynapi/SDL_dynapi_procs.h | 2 +-
test/testaudio.c | 14 +++++----
test/testaudiocapture.c | 4 +--
test/testaudioinfo.c | 11 ++++++--
test/testsurround.c | 2 +-
7 files changed, 62 insertions(+), 38 deletions(-)
diff --git a/include/SDL3/SDL_audio.h b/include/SDL3/SDL_audio.h
index dcbb71154f6b..b870d0771255 100644
--- a/include/SDL3/SDL_audio.h
+++ b/include/SDL3/SDL_audio.h
@@ -327,8 +327,20 @@ extern DECLSPEC char *SDLCALL SDL_GetAudioDeviceName(SDL_AudioDeviceID devid);
* reasonable recommendation before opening the system-recommended default
* device.
*
+ * You can also use this to request the current device buffer size. This is
+ * specified in sample frames and represents the amount of data SDL will
+ * feed to the physical hardware in each chunk. This can be converted to
+ * milliseconds of audio with the following equation:
+ *
+ * `ms = (int) ((((Sint64) frames) * 1000) / spec.freq);`
+ *
+ * Buffer size is only important if you need low-level control over the audio
+ * playback timing. Most apps do not need this.
+ *
* \param devid the instance ID of the device to query.
* \param spec On return, will be filled with device details.
+ * \param sample_frames Pointer to store device buffer size, in sample frames.
+ * Can be NULL.
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*
@@ -336,7 +348,7 @@ extern DECLSPEC char *SDLCALL SDL_GetAudioDeviceName(SDL_AudioDeviceID devid);
*
* \since This function is available since SDL 3.0.0.
*/
-extern DECLSPEC int SDLCALL SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec);
+extern DECLSPEC int SDLCALL SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec, int *sample_frames);
/**
diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index c994a1b8f406..6dfd8b7de96c 100644
--- a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -116,6 +116,30 @@ const char *SDL_GetCurrentAudioDriver(void)
return current_audio.name;
}
+static int GetDefaultSampleFramesFromFreq(const int freq)
+{
+ const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES);
+ if (hint) {
+ const int val = SDL_atoi(hint);
+ if (val > 0) {
+ return val;
+ }
+ }
+
+ if (freq <= 11025) {
+ return 512;
+ } else if (freq <= 22050) {
+ return 1024;
+ } else if (freq <= 48000) {
+ return 2048;
+ } else if (freq <= 96000) {
+ return 4096;
+ }
+
+ return 8192; // shrug
+}
+
+
// device management and hotplug...
@@ -238,6 +262,7 @@ static SDL_AudioDevice *CreatePhysicalAudioDevice(const char *name, SDL_bool isc
device->iscapture = iscapture;
SDL_memcpy(&device->spec, spec, sizeof (SDL_AudioSpec));
SDL_memcpy(&device->default_spec, spec, sizeof (SDL_AudioSpec));
+ device->sample_frames = GetDefaultSampleFramesFromFreq(device->spec.freq);
device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format);
device->handle = handle;
device->prev = NULL;
@@ -1112,7 +1137,7 @@ char *SDL_GetAudioDeviceName(SDL_AudioDeviceID devid)
return retval;
}
-int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec)
+int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec, int *sample_frames)
{
if (!spec) {
return SDL_InvalidParamError("spec");
@@ -1137,6 +1162,9 @@ int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec)
}
SDL_memcpy(spec, &device->spec, sizeof (SDL_AudioSpec));
+ if (sample_frames) {
+ *sample_frames = device->sample_frames;
+ }
SDL_UnlockMutex(device->lock);
return 0;
@@ -1246,29 +1274,6 @@ static void PrepareAudioFormat(SDL_bool iscapture, SDL_AudioSpec *spec)
}
}
-static int GetDefaultSampleFramesFromFreq(const int freq)
-{
- const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES);
- if (hint) {
- const int val = SDL_atoi(hint);
- if (val > 0) {
- return val;
- }
- }
-
- if (freq <= 11025) {
- return 512;
- } else if (freq <= 22050) {
- return 1024;
- } else if (freq <= 48000) {
- return 2048;
- } else if (freq <= 96000) {
- return 4096;
- }
-
- return 8192; // shrug
-}
-
void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device)
{
device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format);
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index ce7170667dec..71eba0275e06 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -926,7 +926,7 @@ SDL_DYNAPI_PROC(const char*,SDL_GetCurrentAudioDriver,(void),(),return)
SDL_DYNAPI_PROC(SDL_AudioDeviceID*,SDL_GetAudioOutputDevices,(int *a),(a),return)
SDL_DYNAPI_PROC(SDL_AudioDeviceID*,SDL_GetAudioCaptureDevices,(int *a),(a),return)
SDL_DYNAPI_PROC(char*,SDL_GetAudioDeviceName,(SDL_AudioDeviceID a),(a),return)
-SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceFormat,(SDL_AudioDeviceID a, SDL_AudioSpec *b),(a,b),return)
+SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceFormat,(SDL_AudioDeviceID a, SDL_AudioSpec *b, int *c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_OpenAudioDevice,(SDL_AudioDeviceID a, const SDL_AudioSpec *b),(a,b),return)
SDL_DYNAPI_PROC(void,SDL_CloseAudioDevice,(SDL_AudioDeviceID a),(a),)
SDL_DYNAPI_PROC(int,SDL_BindAudioStreams,(SDL_AudioDeviceID a, SDL_AudioStream **b, int c),(a,b,c),return)
diff --git a/test/testaudio.c b/test/testaudio.c
index 628c1ec9d550..bd158383119d 100644
--- a/test/testaudio.c
+++ b/test/testaudio.c
@@ -765,9 +765,10 @@ static void DeviceThing_ondrag(Thing *thing, int button, float x, float y)
static void SetLogicalDeviceTitlebar(Thing *thing)
{
SDL_AudioSpec *spec = &thing->data.logdev.spec;
- SDL_GetAudioDeviceFormat(thing->data.logdev.devid, spec);
+ int frames = 0;
+ SDL_GetAudioDeviceFormat(thing->data.logdev.devid, spec, &frames);
SDL_free(thing->titlebar);
- SDL_asprintf(&thing->titlebar, "Logical device #%u (%s, %s, %s, %uHz)", (unsigned int) thing->data.logdev.devid, thing->data.logdev.iscapture ? "CAPTURE" : "OUTPUT", AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq);
+ SDL_asprintf(&thing->titlebar, "Logical device #%u (%s, %s, %s, %uHz, %d frames)", (unsigned int) thing->data.logdev.devid, thing->data.logdev.iscapture ? "CAPTURE" : "OUTPUT", AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames);
}
static void LogicalDeviceThing_ondrop(Thing *thing, int button, float x, float y)
@@ -938,15 +939,16 @@ static Thing *CreateLogicalDeviceThing(Thing *parent, const SDL_AudioDeviceID wh
static void SetPhysicalDeviceTitlebar(Thing *thing)
{
+ int frames = 0;
SDL_AudioSpec *spec = &thing->data.physdev.spec;
- SDL_GetAudioDeviceFormat(thing->data.physdev.devid, spec);
+ SDL_GetAudioDeviceFormat(thing->data.physdev.devid, spec, &frames);
SDL_free(thing->titlebar);
if (thing->data.physdev.devid == SDL_AUDIO_DEVICE_DEFAULT_CAPTURE) {
- SDL_asprintf(&thing->titlebar, "Default system device (CAPTURE, %s, %s, %uHz)", AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq);
+ SDL_asprintf(&thing->titlebar, "Default system device (CAPTURE, %s, %s, %uHz, %d frames)", AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames);
} else if (thing->data.physdev.devid == SDL_AUDIO_DEVICE_DEFAULT_OUTPUT) {
- SDL_asprintf(&thing->titlebar, "Default system device (OUTPUT, %s, %s, %uHz)", AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq);
+ SDL_asprintf(&thing->titlebar, "Default system device (OUTPUT, %s, %s, %uHz, %d frames)", AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames);
} else {
- SDL_asprintf(&thing->titlebar, "Physical device #%u (%s, \"%s\", %s, %s, %uHz)", (unsigned int) thing->data.physdev.devid, thing->data.physdev.iscapture ? "CAPTURE" : "OUTPUT", thing->data.physdev.name, AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq);
+ SDL_asprintf(&thing->titlebar, "Physical device #%u (%s, \"%s\", %s, %s, %uHz, %d frames)", (unsigned int) thing->data.physdev.devid, thing->data.physdev.iscapture ? "CAPTURE" : "OUTPUT", thing->data.physdev.name, AudioFmtToString(spec->format), AudioChansToStr(spec->channels), (unsigned int) spec->freq, frames);
}
}
diff --git a/test/testaudiocapture.c b/test/testaudiocapture.c
index ec7659df2ed6..efc4be1010fe 100644
--- a/test/testaudiocapture.c
+++ b/test/testaudiocapture.c
@@ -179,7 +179,7 @@ int main(int argc, char **argv)
exit(1);
}
SDL_PauseAudioDevice(device);
- SDL_GetAudioDeviceFormat(device, &outspec);
+ SDL_GetAudioDeviceFormat(device, &outspec, NULL);
stream_out = SDL_CreateAudioStream(&outspec, &outspec);
if (!stream_out) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create an audio stream for playback: %s!\n", SDL_GetError());
@@ -203,7 +203,7 @@ int main(int argc, char **argv)
exit(1);
}
SDL_PauseAudioDevice(device);
- SDL_GetAudioDeviceFormat(device, &inspec);
+ SDL_GetAudioDeviceFormat(device, &inspec, NULL);
stream_in = SDL_CreateAudioStream(&inspec, &inspec);
if (!stream_in) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create an audio stream for capture: %s!\n", SDL_GetError());
diff --git a/test/testaudioinfo.c b/test/testaudioinfo.c
index 636d4aaf7bff..8034f5623c48 100644
--- a/test/testaudioinfo.c
+++ b/test/testaudioinfo.c
@@ -19,6 +19,7 @@ print_devices(SDL_bool iscapture)
SDL_AudioSpec spec;
const char *typestr = ((iscapture) ? "capture" : "output");
int n = 0;
+ int frames;
SDL_AudioDeviceID *devices = iscapture ? SDL_GetAudioCaptureDevices(&n) : SDL_GetAudioOutputDevices(&n);
if (devices == NULL) {
@@ -37,10 +38,11 @@ print_devices(SDL_bool iscapture)
SDL_Log(" %d Error: %s\n", i, SDL_GetError());
}
- if (SDL_GetAudioDeviceFormat(devices[i], &spec) == 0) {
+ if (SDL_GetAudioDeviceFormat(devices[i], &spec, &frames) == 0) {
SDL_Log(" Sample Rate: %d\n", spec.freq);
SDL_Log(" Channels: %d\n", spec.channels);
SDL_Log(" SDL_AudioFormat: %X\n", spec.format);
+ SDL_Log(" Buffer Size: %d frames\n", frames);
}
}
SDL_Log("\n");
@@ -53,6 +55,7 @@ int main(int argc, char **argv)
SDL_AudioSpec spec;
int i;
int n;
+ int frames;
SDLTest_CommonState *state;
/* Initialize test framework */
@@ -92,22 +95,24 @@ int main(int argc, char **argv)
print_devices(SDL_FALSE);
print_devices(SDL_TRUE);
- if (SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec) < 0) {
+ if (SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec, &frames) < 0) {
SDL_Log("Error when calling SDL_GetAudioDeviceFormat(default output): %s\n", SDL_GetError());
} else {
SDL_Log("Default Output Device:\n");
SDL_Log("Sample Rate: %d\n", spec.freq);
SDL_Log("Channels: %d\n", spec.channels);
SDL_Log("SDL_AudioFormat: %X\n", spec.format);
+ SDL_Log("Buffer Size: %d frames\n", frames);
}
- if (SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_CAPTURE, &spec) < 0) {
+ if (SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_CAPTURE, &spec, &frames) < 0) {
SDL_Log("Error when calling SDL_GetAudioDeviceFormat(default capture): %s\n", SDL_GetError());
} else {
SDL_Log("Default Capture Device:\n");
SDL_Log("Sample Rate: %d\n", spec.freq);
SDL_Log("Channels: %d\n", spec.channels);
SDL_Log("SDL_AudioFormat: %X\n", spec.format);
+ SDL_Log("Buffer Size: %d frames\n", frames);
}
SDL_Quit();
diff --git a/test/testsurround.c b/test/testsurround.c
index 551f316b9d75..1edd1faf6971 100644
--- a/test/testsurround.c
+++ b/test/testsurround.c
@@ -197,7 +197,7 @@ int main(int argc, char *argv[])
SDL_Log("Testing audio device: %s\n", devname);
SDL_free(devname);
- if (SDL_GetAudioDeviceFormat(devices[i], &spec) != 0) {
+ if (SDL_GetAudioDeviceFormat(devices[i], &spec, NULL) != 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GetAudioDeviceFormat() failed: %s\n", SDL_GetError());
continue;
}