audio: Allow querying of device buffer size.

This commit is contained in:
Ryan C. Gordon 2023-09-13 11:03:17 -04:00
parent cf95721130
commit 2f43f7bc53
No known key found for this signature in database
GPG key ID: FA148B892AB48044
7 changed files with 62 additions and 38 deletions

View file

@ -327,8 +327,20 @@ extern DECLSPEC char *SDLCALL SDL_GetAudioDeviceName(SDL_AudioDeviceID devid);
* reasonable recommendation before opening the system-recommended default * reasonable recommendation before opening the system-recommended default
* device. * 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 devid the instance ID of the device to query.
* \param spec On return, will be filled with device details. * \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 * \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information. * 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. * \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);
/** /**

View file

@ -116,6 +116,30 @@ const char *SDL_GetCurrentAudioDriver(void)
return current_audio.name; 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... // device management and hotplug...
@ -238,6 +262,7 @@ static SDL_AudioDevice *CreatePhysicalAudioDevice(const char *name, SDL_bool isc
device->iscapture = iscapture; device->iscapture = iscapture;
SDL_memcpy(&device->spec, spec, sizeof (SDL_AudioSpec)); SDL_memcpy(&device->spec, spec, sizeof (SDL_AudioSpec));
SDL_memcpy(&device->default_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->silence_value = SDL_GetSilenceValueForFormat(device->spec.format);
device->handle = handle; device->handle = handle;
device->prev = NULL; device->prev = NULL;
@ -1112,7 +1137,7 @@ char *SDL_GetAudioDeviceName(SDL_AudioDeviceID devid)
return retval; 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) { if (!spec) {
return SDL_InvalidParamError("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)); SDL_memcpy(spec, &device->spec, sizeof (SDL_AudioSpec));
if (sample_frames) {
*sample_frames = device->sample_frames;
}
SDL_UnlockMutex(device->lock); SDL_UnlockMutex(device->lock);
return 0; 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) void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device)
{ {
device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format); device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format);

View file

@ -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_GetAudioOutputDevices,(int *a),(a),return)
SDL_DYNAPI_PROC(SDL_AudioDeviceID*,SDL_GetAudioCaptureDevices,(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(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(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(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) SDL_DYNAPI_PROC(int,SDL_BindAudioStreams,(SDL_AudioDeviceID a, SDL_AudioStream **b, int c),(a,b,c),return)

View file

@ -765,9 +765,10 @@ static void DeviceThing_ondrag(Thing *thing, int button, float x, float y)
static void SetLogicalDeviceTitlebar(Thing *thing) static void SetLogicalDeviceTitlebar(Thing *thing)
{ {
SDL_AudioSpec *spec = &thing->data.logdev.spec; 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_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) 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) static void SetPhysicalDeviceTitlebar(Thing *thing)
{ {
int frames = 0;
SDL_AudioSpec *spec = &thing->data.physdev.spec; 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); SDL_free(thing->titlebar);
if (thing->data.physdev.devid == SDL_AUDIO_DEVICE_DEFAULT_CAPTURE) { 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) { } 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 { } 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);
} }
} }

View file

@ -179,7 +179,7 @@ int main(int argc, char **argv)
exit(1); exit(1);
} }
SDL_PauseAudioDevice(device); SDL_PauseAudioDevice(device);
SDL_GetAudioDeviceFormat(device, &outspec); SDL_GetAudioDeviceFormat(device, &outspec, NULL);
stream_out = SDL_CreateAudioStream(&outspec, &outspec); stream_out = SDL_CreateAudioStream(&outspec, &outspec);
if (!stream_out) { if (!stream_out) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create an audio stream for playback: %s!\n", SDL_GetError()); 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); exit(1);
} }
SDL_PauseAudioDevice(device); SDL_PauseAudioDevice(device);
SDL_GetAudioDeviceFormat(device, &inspec); SDL_GetAudioDeviceFormat(device, &inspec, NULL);
stream_in = SDL_CreateAudioStream(&inspec, &inspec); stream_in = SDL_CreateAudioStream(&inspec, &inspec);
if (!stream_in) { if (!stream_in) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create an audio stream for capture: %s!\n", SDL_GetError()); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create an audio stream for capture: %s!\n", SDL_GetError());

View file

@ -19,6 +19,7 @@ print_devices(SDL_bool iscapture)
SDL_AudioSpec spec; SDL_AudioSpec spec;
const char *typestr = ((iscapture) ? "capture" : "output"); const char *typestr = ((iscapture) ? "capture" : "output");
int n = 0; int n = 0;
int frames;
SDL_AudioDeviceID *devices = iscapture ? SDL_GetAudioCaptureDevices(&n) : SDL_GetAudioOutputDevices(&n); SDL_AudioDeviceID *devices = iscapture ? SDL_GetAudioCaptureDevices(&n) : SDL_GetAudioOutputDevices(&n);
if (devices == NULL) { if (devices == NULL) {
@ -37,10 +38,11 @@ print_devices(SDL_bool iscapture)
SDL_Log(" %d Error: %s\n", i, SDL_GetError()); 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(" Sample Rate: %d\n", spec.freq);
SDL_Log(" Channels: %d\n", spec.channels); SDL_Log(" Channels: %d\n", spec.channels);
SDL_Log(" SDL_AudioFormat: %X\n", spec.format); SDL_Log(" SDL_AudioFormat: %X\n", spec.format);
SDL_Log(" Buffer Size: %d frames\n", frames);
} }
} }
SDL_Log("\n"); SDL_Log("\n");
@ -53,6 +55,7 @@ int main(int argc, char **argv)
SDL_AudioSpec spec; SDL_AudioSpec spec;
int i; int i;
int n; int n;
int frames;
SDLTest_CommonState *state; SDLTest_CommonState *state;
/* Initialize test framework */ /* Initialize test framework */
@ -92,22 +95,24 @@ int main(int argc, char **argv)
print_devices(SDL_FALSE); print_devices(SDL_FALSE);
print_devices(SDL_TRUE); 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()); SDL_Log("Error when calling SDL_GetAudioDeviceFormat(default output): %s\n", SDL_GetError());
} else { } else {
SDL_Log("Default Output Device:\n"); SDL_Log("Default Output Device:\n");
SDL_Log("Sample Rate: %d\n", spec.freq); SDL_Log("Sample Rate: %d\n", spec.freq);
SDL_Log("Channels: %d\n", spec.channels); SDL_Log("Channels: %d\n", spec.channels);
SDL_Log("SDL_AudioFormat: %X\n", spec.format); 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()); SDL_Log("Error when calling SDL_GetAudioDeviceFormat(default capture): %s\n", SDL_GetError());
} else { } else {
SDL_Log("Default Capture Device:\n"); SDL_Log("Default Capture Device:\n");
SDL_Log("Sample Rate: %d\n", spec.freq); SDL_Log("Sample Rate: %d\n", spec.freq);
SDL_Log("Channels: %d\n", spec.channels); SDL_Log("Channels: %d\n", spec.channels);
SDL_Log("SDL_AudioFormat: %X\n", spec.format); SDL_Log("SDL_AudioFormat: %X\n", spec.format);
SDL_Log("Buffer Size: %d frames\n", frames);
} }
SDL_Quit(); SDL_Quit();

View file

@ -197,7 +197,7 @@ int main(int argc, char *argv[])
SDL_Log("Testing audio device: %s\n", devname); SDL_Log("Testing audio device: %s\n", devname);
SDL_free(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()); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GetAudioDeviceFormat() failed: %s\n", SDL_GetError());
continue; continue;
} }