diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index 972e0b7e78..0001dfa8e5 100644 --- a/include/SDL3/SDL_hints.h +++ b/include/SDL3/SDL_hints.h @@ -493,6 +493,36 @@ extern "C" { */ #define SDL_HINT_AUDIO_DUMMY_TIMESCALE "SDL_AUDIO_DUMMY_TIMESCALE" +/** + * A variable controlling whether SDL enforces a minimum audio device spec. + * + * By default, SDL will require devices to be opened at a minimum spec + * (at time of writing: 44100Hz, stereo, Sint16 format), so if something + * lower quality tries to open the device first, it doesn't ruin audio + * for the next thing that opens a device. For example, if a VoIP library + * wants Uint8, 8000Hz, mono output, it doesn't force the entire game to + * this state simply because it got there first. + * + * However, an app that knows it will definitely be the only thing opening + * a device, and wants to open it at a lower spec, might be able to avoid + * some otherwise-unnecessary conversions by bypassing this minimum + * requirement. + * + * Note that even without the minimum enforcement, the system might choose a + * different format/channels/frequency than requested by the app; this hint + * just removes SDL's minimum policy. + * + * The variable can be set to the following values: + * + * - "0": Audio device mimimum formats _are not_ enforced. + * - "1": Audio device minimum formats _are_ enforced. (default) + * + * This hint should be set before an audio device is opened. + * + * \since This hint is available since SDL 3.4.0. + */ +#define SDL_HINT_AUDIO_ENFORCE_MINIMUM_SPEC "SDL_AUDIO_ENFORCE_MINIMUM_SPEC" + /** * A variable controlling the default audio format. * diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index e084f24cfc..6f20013201 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -1800,18 +1800,21 @@ static bool OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec SDL_copyp(&spec, inspec ? inspec : &device->default_spec); PrepareAudioFormat(device->recording, &spec); - /* We impose a simple minimum on device formats. This prevents something low quality, like an old game using S8/8000Hz audio, - from ruining a music thing playing at CD quality that tries to open later, or some VoIP library that opens for mono output - ruining your surround-sound game because it got there first. - These are just requests! The backend may change any of these values during OpenDevice method! */ + if (!SDL_GetHintBoolean(SDL_HINT_AUDIO_ENFORCE_MINIMUM_SPEC, true)) { + SDL_copyp(&device->spec, &spec); + } else { + /* We impose a simple minimum on device formats. This prevents something low quality, like an old game using S8/8000Hz audio, + from ruining a music thing playing at CD quality that tries to open later, or some VoIP library that opens for mono output + ruining your surround-sound game because it got there first. + These are just requests! The backend may change any of these values during OpenDevice method! */ + const SDL_AudioFormat minimum_format = device->recording ? DEFAULT_AUDIO_RECORDING_FORMAT : DEFAULT_AUDIO_PLAYBACK_FORMAT; + const int minimum_channels = device->recording ? DEFAULT_AUDIO_RECORDING_CHANNELS : DEFAULT_AUDIO_PLAYBACK_CHANNELS; + const int minimum_freq = device->recording ? DEFAULT_AUDIO_RECORDING_FREQUENCY : DEFAULT_AUDIO_PLAYBACK_FREQUENCY; + device->spec.format = (SDL_AUDIO_BITSIZE(minimum_format) >= SDL_AUDIO_BITSIZE(spec.format)) ? minimum_format : spec.format; + device->spec.channels = SDL_max(minimum_channels, spec.channels); + device->spec.freq = SDL_max(minimum_freq, spec.freq); + } - const SDL_AudioFormat minimum_format = device->recording ? DEFAULT_AUDIO_RECORDING_FORMAT : DEFAULT_AUDIO_PLAYBACK_FORMAT; - const int minimum_channels = device->recording ? DEFAULT_AUDIO_RECORDING_CHANNELS : DEFAULT_AUDIO_PLAYBACK_CHANNELS; - const int minimum_freq = device->recording ? DEFAULT_AUDIO_RECORDING_FREQUENCY : DEFAULT_AUDIO_PLAYBACK_FREQUENCY; - - device->spec.format = (SDL_AUDIO_BITSIZE(minimum_format) >= SDL_AUDIO_BITSIZE(spec.format)) ? minimum_format : spec.format; - device->spec.channels = SDL_max(minimum_channels, spec.channels); - device->spec.freq = SDL_max(minimum_freq, spec.freq); device->sample_frames = SDL_GetDefaultSampleFramesFromFreq(device->spec.freq); SDL_UpdatedAudioDeviceFormat(device); // start this off sane.