SDL: audio: First shot at the SDL3 audio subsystem redesign!

From 905c4fff5bb88b200701ca8ff3a8d4dff802182d Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Fri, 12 May 2023 23:37:02 -0400
Subject: [PATCH] audio: First shot at the SDL3 audio subsystem redesign!

This is a work in progress! (and this commit will probably get
force-pushed over at some point).
---
 CMakeLists.txt                        |    6 +
 docs/README-migration.md              |  105 +-
 include/SDL3/SDL_audio.h              |  996 +++++--------
 include/SDL3/SDL_test_common.h        |    4 +-
 src/audio/SDL_audio.c                 | 1978 +++++++++++--------------
 src/audio/SDL_audio_c.h               |   55 +-
 src/audio/SDL_audiocvt.c              |  109 +-
 src/audio/SDL_audiodev.c              |    2 +-
 src/audio/SDL_sysaudio.h              |  251 ++--
 src/audio/SDL_wave.c                  |   66 +-
 src/audio/disk/SDL_diskaudio.c        |   88 +-
 src/audio/dummy/SDL_dummyaudio.c      |   29 +-
 src/audio/dummy/SDL_dummyaudio.h      |    2 +
 src/audio/pulseaudio/SDL_pulseaudio.c |  207 ++-
 src/dynapi/SDL_dynapi.sym             |   54 +-
 src/dynapi/SDL_dynapi_overrides.h     |   54 +-
 src/dynapi/SDL_dynapi_procs.h         |   54 +-
 src/test/SDL_test_common.c            |   31 +-
 test/loopwave.c                       |   57 +-
 19 files changed, 1829 insertions(+), 2319 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7e5a28b55fa8..de86f90c109c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -353,6 +353,12 @@ set_option(SDL_CLANG_TIDY          "Run clang-tidy static analysis" OFF)
 
 set(SDL_VENDOR_INFO "" CACHE STRING "Vendor name and/or version to add to SDL_REVISION")
 
+set(SDL_OSS OFF)
+set(SDL_ALSA OFF)
+set(SDL_JACK OFF)
+set(SDL_PIPEWIRE OFF)
+set(SDL_SNDIO OFF)
+
 cmake_dependent_option(SDL_SHARED "Build a shared version of the library" ${SDL_SHARED_DEFAULT} ${SDL_SHARED_AVAILABLE} OFF)
 option(SDL_STATIC "Build a static version of the library" ${SDL_STATIC_DEFAULT})
 option(SDL_TEST_LIBRARY   "Build the SDL3_test library" ON)
diff --git a/docs/README-migration.md b/docs/README-migration.md
index 19b30414b836..0a3c0fd5ed4c 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -53,13 +53,100 @@ The following structures have been renamed:
 
 ## SDL_audio.h
 
+The audio subsystem in SDL3 is dramatically different than SDL2. There is no longer an audio callback; instead you bind SDL_AudioStreams to devices.
+
+The SDL 1.2 audio compatibility API has also been removed, as it was a simplified version of the audio callback interface.
+
+If your app depends on the callback method, you can use the single-header library at https://github.com/libsdl-org/SDL3_audio_callback (to be written!) to simulate it on top of SDL3's new API.
+
+In SDL2, you might have done something like this to play audio:
+
+```c
+    void SDLCALL MyAudioCallback(void *userdata, Uint8 * stream, int len)
+    {
+        /* calculate a little more audio here, maybe using `userdata`, write it to `stream` */
+    }
+
+    /* ...somewhere near startup... */
+    SDL_AudioSpec my_desired_audio_format;
+    SDL_zero(my_desired_audio_format);
+    my_desired_audio_format.format = AUDIO_S16;
+    my_desired_audio_format.channels = 2;
+    my_desired_audio_format.freq = 44100;
+    my_desired_audio_format.samples = 1024;
+    my_desired_audio_format.callback = MyAudioCallback;
+    my_desired_audio_format.userdata = &my_audio_callback_user_data;
+    SDL_AudioDeviceID my_audio_device = SDL_OpenAudioDevice(NULL, 0, &my_desired_audio_format, NULL, 0);
+    SDL_PauseAudioDevice(my_audio_device, 0);
+```
+
+in SDL3:
+
+```c
+    /* ...somewhere near startup... */
+    my_desired_audio_format.callback = MyAudioCallback;  /* etc */
+    SDL_AudioDeviceID my_audio_device = SDL_OpenAudioDevice(0, SDL_AUDIO_S16, 2, 44100);
+    SDL_AudioSteam *stream = SDL_CreateAndBindAudioStream(my_audio_device, SDL_AUDIO_S16, 2, 44100);
+
+    /* ...in your main loop... */
+    /* calculate a little more audio into `buf`, add it to `stream` */
+    SDL_PutAudioStreamData(stream, buf, buflen);
+```
+
 SDL_AudioInit() and SDL_AudioQuit() have been removed. Instead you can call SDL_InitSubSystem() and SDL_QuitSubSystem() with SDL_INIT_AUDIO, which will properly refcount the subsystems. You can choose a specific audio driver using SDL_AUDIO_DRIVER hint.
 
-SDL_PauseAudioDevice() is only used to pause audio playback. Use SDL_PlayAudioDevice() to start playing audio.
+The `SDL_AUDIO_ALLOW_*` symbols have been removed; now one may request the format they desire from the audio device, but ultimately SDL_AudioStream will manage the difference. One can use SDL_GetAudioDeviceFormat() to see what the final format is, if any "allowed" changes should be accomodated by the app.
+
+SDL_AudioDeviceID no longer represents an open audio device's handle, it's now the device's instance ID that the device owns as long as it exists on the system. The separation between device instances and device indexes is gone.
+
+Devices are opened by device instance ID, and a new handle is not generated by the open operation; instead, opens of the same device instance are reference counted. This allows any device to be opened multiple times, possibly by unrelated pieces of code.
+
+Devices are not opened by an arbitrary string name anymore, but by device instance ID (or 0 to request a reasonable default, like a NULL string in SDL2). In SDL2, the string was used to open both a standard list of system devices, but also allowed for arbitrary devices, such as hostnames of network sound servers. In SDL3, many of the backends that supported arbitrary device names are obsolete and have been removed; of those that remain, arbitrary devices will be opened with a device ID of 0 and an SDL_hint, so specific end-users can set an environment variable to fit their needs and apps don't have to concern themselves with it.
+
+Many functions that would accept a device index and an `iscapture` parameter now just take an SDL_AudioDeviceID, as they are unique across all devices, instead of separate indices into output and capture device lists.
+
+Rather than iterating over audio devices using a device index, there is a new function, SDL_GetAudioDevices(), to get the current list of devices, and new functions to get information about devices from their instance ID:
+
+```c
+{
+    if (SDL_InitSubSystem(SDL_INIT_AUDIO) == 0) {
+        int i, num_devices;
+        SDL_AudioDeviceID *devices = SDL_GetAudioDevices(/*iscapture=*/SDL_FALSE, &num_devices);
+        if (devices) {
+            for (i = 0; i < num_devices; ++i) {
+                SDL_AudioDeviceID instance_id = devices[i];
+                char *name = SDL_GetAudioDeviceName(instance_id);
+                SDL_Log("AudioDevice %" SDL_PRIu32 ": %s\n", instance_id, name);
+                SDL_free(name);
+            }
+            SDL_free(devices);
+        }
+        SDL_QuitSubSystem(SDL_INIT_AUDIO);
+    }
+}
+```
+
+SDL_LockAudioDevice() and SDL_UnlockAudioDevice() have been removed, since there is no callback in another thread to protect. Internally, SDL's audio subsystem and SDL_AudioStream maintain their own locks internally, so audio streams are safe to use from any thread.
+
+SDL_PauseAudioDevice() has been removed; unbinding an audio stream from a device with SDL_UnbindAudioStream() will leave the stream still containing any unconsumed data, effectively pausing it until rebound with SDL_BindAudioStream() again. Devices act like they are "paused" after open, like SDL2, until a stream is bound to it.
+
+SDL_GetAudioDeviceStatus() has been removed; there is no more concept of "pausing" a device, just whether streams are bound, so please keep track of your audio streams!
+
+SDL_QueueAudio(), SDL_DequeueAudio, and SDL_ClearQueuedAudio and SDL_GetQueuedAudioSize() have been removed; an SDL_AudioStream bound to a device provides the exact same functionality.
+
+APIs that use channel counts used to use a Uint8 for the channel; now they use int.
+
+SDL_AudioSpec has been removed; things that used it have simply started taking separate arguments for format, channel, and sample rate. SDL_GetSilenceValueForFormat() can provide the information from the SDL_AudioSpec's `silence` field. The other SDL_AudioSpec fields aren't relevant anymore.
+
+SDL_GetAudioDeviceSpec() is removed; use SDL_GetAudioDeviceFormat() instead.
+
+SDL_MixAudio() has been removed, as it relied on legacy SDL 1.2 quirks; SDL_MixAudioFormat() remains and offers the same functionality.
+
+SDL_AudioInit() and SDL_AudioQuit() have been removed. Instead you can call SDL_InitSubSystem() and SDL_QuitSubSystem() with SDL_INIT_AUDIO, which will properly refcount the subsystems. You can choose a specific audio driver using SDL_AUDIO_DRIVER hint.
 
 SDL_FreeWAV has been removed and calls can be replaced with SDL_free.
 
-SDL_AudioCVT interface is removed, SDL_AudioStream interface or SDL_ConvertAudioSamples() helper function can be used.
+SDL_AudioCVT interface has been removed, the SDL_AudioStream interface (for audio supplied in pieces) or the new SDL_ConvertAudioSamples() function (for converting a complete audio buffer in one call) can be used instead.
 
 Code that used to look like this:
 ```c
@@ -103,6 +190,8 @@ If you need to convert U16 audio data to a still-supported format at runtime, th
     }

+All remaining AUDIO_* symbols have been renamed to SDL_AUDIO_* for API consistency, but othewise are identical in value and usage.
+
In SDL2, SDL_AudioStream would convert/resample audio data during input (via SDL_AudioStreamPut). In SDL3, it does this work when requesting audio (via SDL_GetAudioStreamData, which would have been SDL_AudioStreamPut in SDL2. The way you use an AudioStream is roughly the same, just be aware that the workload moved to a different phase.
In SDL2, SDL_AudioStreamAvailable() returns 0 if passed a NULL stream. In SDL3, the equivalent SDL_GetAudioStreamAvailable() call returns -1 and sets an error string, which matches other audiostream APIs’ behavior.

@@ -118,17 +207,25 @@ The following functions have been renamed:

The following functions have been removed:
+* SDL_GetNumAudioDevices()
+* SDL_GetAudioDeviceSpec()

  • SDL_ConvertAudio()
  • SDL_BuildAudioCVT()
  • SDL_OpenAudio()
  • SDL_CloseAudio()
  • SDL_PauseAudio()
    +* SDL_PauseAudioDevice
  • SDL_GetAudioStatus()
    +* SDL_GetAudioDeviceStatus()
  • SDL_LockAudio()
    +* SDL_LockAudioDevice()
  • SDL_UnlockAudio()
    +* SDL_UnlockAudioDevice()
  • SDL_MixAudio()

-Use the SDL_AudioDevice functions instead.
+* SDL_QueueAudio()
+* SDL_DequeueAudio()
+* SDL_ClearAudioQueue()
+* SDL_GetQueuedAudioSize()

The following symbols have been renamed:

  • AUDIO_F32 => SDL_AUDIO_F32
    diff --git a/include/SDL3/SDL_audio.h b/include/SDL3/SDL_audio.h
    index 1dd39fc6f9cd…43ecc1ea12fb 100644
    — a/include/SDL3/SDL_audio.h
    +++ b/include/SDL3/SDL_audio.h
    @@ -43,6 +43,17 @@
    extern “C” {
    #endif

+/*

    • For multi-channel audio, the default SDL channel mapping is:
    • 2: FL FR (stereo)
    • 3: FL FR LFE (2.1 surround)
    • 4: FL FR BL BR (quad)
    • 5: FL FR LFE BL BR (4.1 surround)
    • 6: FL FR FC LFE SL SR (5.1 surround - last two can also be BL BR)
    • 7: FL FR FC LFE BC SL SR (6.1 surround)
    • 8: FL FR FC LFE BL BR SL SR (7.1 surround)
  • */

/**

  • \brief Audio format flags.

@@ -128,62 +139,19 @@ typedef Uint16 SDL_AudioFormat;
#endif
/* @} */

-/**

    • \name Allow change flags
    • Which audio format changes are allowed when opening a device.
  • /
    -/
    @{ /
    -#define SDL_AUDIO_ALLOW_FREQUENCY_CHANGE 0x00000001
    -#define SDL_AUDIO_ALLOW_FORMAT_CHANGE 0x00000002
    -#define SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004
    -#define SDL_AUDIO_ALLOW_SAMPLES_CHANGE 0x00000008
    -#define SDL_AUDIO_ALLOW_ANY_CHANGE (SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_FORMAT_CHANGE|SDL_AUDIO_ALLOW_CHANNELS_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE)
    -/
    @} */

/* @} // Audio flags */

-/**

    • This function is called when the audio device needs more data.
    • \param userdata An application-specific parameter saved in
    •              the SDL_AudioSpec structure
      
    • \param stream A pointer to the audio data buffer.
    • \param len The length of that buffer in bytes.
    • Once the callback returns, the buffer will no longer be valid.
    • Stereo samples are stored in a LRLRLR ordering.
    • You can choose to avoid callbacks and use SDL_QueueAudio() instead, if
    • you like. Just open your audio device with a NULL callback.
  • */
    -typedef void (SDLCALL * SDL_AudioCallback) (void *userdata, Uint8 * stream,
  •                                        int len);
    

-/**

    • The calculated values in this structure are calculated by SDL_OpenAudioDevice().
    • For multi-channel audio, the default SDL channel mapping is:
    • 2: FL FR (stereo)
    • 3: FL FR LFE (2.1 surround)
    • 4: FL FR BL BR (quad)
    • 5: FL FR LFE BL BR (4.1 surround)
    • 6: FL FR FC LFE SL SR (5.1 surround - last two can also be BL BR)
    • 7: FL FR FC LFE BC SL SR (6.1 surround)
    • 8: FL FR FC LFE BL BR SL SR (7.1 surround)
      +/* SDL_AudioStream is an audio conversion interface.
    • It can handle resampling data in chunks without generating
  •  artifacts, when it doesn't have the complete buffer available.
    
    • It can handle incoming data in any variable size.
    • It can handle input/output format changes on the fly.
    • You push data as you have it, and pull it when you need it
    • It can also function as a basic audio data queue even if you
  •  just have sound that needs to pass from one place to another.
    
    */
    -typedef struct SDL_AudioSpec
    -{
  • int freq; /**< DSP frequency – samples per second */
  • SDL_AudioFormat format; /**< Audio data format */
  • Uint8 channels; /**< Number of channels: 1 mono, 2 stereo */
  • Uint8 silence; /**< Audio buffer silence value (calculated) */
  • Uint16 samples; /**< Audio buffer size in sample FRAMES (total samples divided by channel count) */
  • Uint16 padding; /**< Necessary for some compile environments */
  • Uint32 size; /**< Audio buffer size in bytes (calculated) */
  • SDL_AudioCallback callback; /**< Callback that feeds the audio device (NULL to use SDL_QueueAudio()). */
  • void *userdata; /**< Userdata passed to callback (ignored for NULL callbacks). /
    -} SDL_AudioSpec;
    +struct SDL_AudioStream; /
    this is opaque to the outside world. */
    +typedef struct SDL_AudioStream SDL_AudioStream;

/* Function prototypes */
@@ -213,6 +181,8 @@ typedef struct SDL_AudioSpec
*

  • \since This function is available since SDL 3.0.0.
    • \threadsafety It is safe to call this function from any thread.
    • \sa SDL_GetAudioDriver
      */
      extern DECLSPEC int SDLCALL SDL_GetNumAudioDrivers(void);
      @@ -233,6 +203,8 @@ extern DECLSPEC int SDLCALL SDL_GetNumAudioDrivers(void);
    • \returns the name of the audio driver at the requested index, or NULL if an
    •      invalid index was specified.
      
    • \threadsafety It is safe to call this function from any thread.
    • \since This function is available since SDL 3.0.0.
    • \sa SDL_GetNumAudioDrivers
      @@ -252,445 +224,278 @@ extern DECLSPEC const char *SDLCALL SDL_GetAudioDriver(int index);
    • \returns the name of the current audio driver or NULL if no driver has been
    •      initialized.
      
    • \threadsafety It is safe to call this function from any thread.
    • \since This function is available since SDL 3.0.0.
      */
      extern DECLSPEC const char *SDLCALL SDL_GetCurrentAudioDriver(void);

/**

    • SDL Audio Device IDs.
    • SDL Audio Device instance IDs.
      */
      typedef Uint32 SDL_AudioDeviceID;

/**

    • Get the number of built-in audio devices.
    • This function is only valid after successfully initializing the audio
    • subsystem.
    • Get a list of currently-connected audio output devices.
    • Note that audio capture support is not implemented as of SDL 2.0.4, so the
    • iscapture parameter is for future expansion and should always be zero for
    • now.
    • This returns of list of available devices that play sound, perhaps
    • to speakers or headphones (“output” devices). If you want devices
    • that record audio, like a microphone (“capture” devices), use
    • SDL_GetAudioCaptureDevices() instead.
    • This function will return -1 if an explicit list of devices can’t be
    • determined. Returning -1 is not an error. For example, if SDL is set up to
    • talk to a remote audio server, it can’t list every one available on the
    • Internet, but it will still allow a specific host to be specified in
    • SDL_OpenAudioDevice().
    • \param count a pointer filled in with the number of devices returned
    • \returns a 0 terminated array of device instance IDs which should be
    •      freed with SDL_free(), or NULL on error; call SDL_GetError() for
      
    •      more details.
      
    • In many common cases, when this function returns a value <= 0, it can still
    • successfully open the default device (NULL for first argument of
    • SDL_OpenAudioDevice()).
    • \since This function is available since SDL 3.0.0.
    • This function may trigger a complete redetect of available hardware. It
    • should not be called for each iteration of a loop, but rather once at the
    • start of a loop:
    • \threadsafety It is safe to call this function from any thread.
    • // Don’t do this:
    • for (int i = 0; i < SDL_GetNumAudioDevices(0); i++)
    • \sa SDL_OpenAudioDevice
  • */
    +extern DECLSPEC SDL_AudioDeviceID *SDLCALL SDL_GetAudioOutputDevices(int *count);

+/**

    • Get a list of currently-connected audio capture devices.
    • // do this instead:
    • const int count = SDL_GetNumAudioDevices(0);
    • for (int i = 0; i < count; ++i) { do_something_here(); }
    • This returns of list of available devices that record audio, like a
    • microphone (“capture” devices). If you want devices
    • that play sound, perhaps to speakers or headphones (“output” devices),
    • use SDL_GetAudioOutputDevices() instead.
    • \param iscapture zero to request playback devices, non-zero to request
    •              recording devices
      
    • \returns the number of available devices exposed by the current driver or
    •      -1 if an explicit list of devices can't be determined. A return
      
    •      value of -1 does not necessarily mean an error condition.
      
    • \param count a pointer filled in with the number of devices returned
    • \returns a 0 terminated array of device instance IDs which should be
    •      freed with SDL_free(), or NULL on error; call SDL_GetError() for
      
    •      more details.
      
    • \since This function is available since SDL 3.0.0.
    • \sa SDL_GetAudioDeviceName
    • \threadsafety It is safe to call this function from any thread.
    • \sa SDL_OpenAudioDevice
      */
      -extern DECLSPEC int SDLCALL SDL_GetNumAudioDevices(int iscapture);
      +extern DECLSPEC SDL_AudioDeviceID *SDLCALL SDL_GetAudioCaptureDevices(int *count);

/**

  • Get the human-readable name of a specific audio device.
    • This function is only valid after successfully initializing the audio
    • subsystem. The values returned by this function reflect the latest call to
    • SDL_GetNumAudioDevices(); re-call that function to redetect available
    • hardware.
    • The string returned by this function is UTF-8 encoded, read-only, and
    • managed internally. You are not to free it. If you need to keep the string
    • for any length of time, you should make your own copy of it, as it will be
    • invalid next time any of several other SDL functions are called.
    • The string returned by this function is UTF-8 encoded. The caller should
    • call SDL_free on the return value when done with it.
    • \param index the index of the audio device; valid values range from 0 to
    •          SDL_GetNumAudioDevices() - 1
      
    • \param iscapture non-zero to query the list of recording devices, zero to
    •              query the list of output devices.
      
    • \returns the name of the audio device at the requested index, or NULL on
    •      error.
      
    • \param devid the instance ID of the device to query.
    • \returns the name of the audio device, or NULL on error.
    • \since This function is available since SDL 3.0.0.
    • \threadsafety It is safe to call this function from any thread.
    • \sa SDL_GetNumAudioDevices
    • \sa SDL_GetDefaultAudioInfo
      */
      -extern DECLSPEC const char *SDLCALL SDL_GetAudioDeviceName(int index,
  •                                                       int iscapture);
    

+extern DECLSPEC char *SDLCALL SDL_GetAudioDeviceName(SDL_AudioDeviceID devid);

/**

    • Get the preferred audio format of a specific audio device.
    • This function is only valid after a successfully initializing the audio
    • subsystem. The values returned by this function reflect the latest call to
    • SDL_GetNumAudioDevices(); re-call that function to redetect available
    • hardware.
    • Get the current audio format of a specific audio device.
    • spec will be filled with the sample rate, sample format, and channel
    • count.
    • For an opened device, this will report the format the device is
    • currently using. If the device isn’t yet opened, this will report
    • the device’s preferred format (or a reasonable default if this
    • can’t be determined).
    • \param index the index of the audio device; valid values range from 0 to
    •          SDL_GetNumAudioDevices() - 1
      
    • \param iscapture non-zero to query the list of recording devices, zero to
    •              query the list of output devices.
      
    • \param spec The SDL_AudioSpec to be initialized by this function.
    • \param devid the instance ID of the device to query.
    • \param fmt On return, will be set to the device’s data format. Can be NULL.
    • \param channels On return, will be set to the device’s channel count. Can be NULL.
    • \param freq On return, will be set to the device’s sample rate. Can be NULL.
    • \returns 0 on success or a negative error code on failure; call
    •      SDL_GetError() for more information.
      
    • \since This function is available since SDL 3.0.0.
    • \sa SDL_GetNumAudioDevices
    • \sa SDL_GetDefaultAudioInfo
  • */
    -extern DECLSPEC int SDLCALL SDL_GetAudioDeviceSpec(int index,
  •                                               int iscapture,
    
  •                                               SDL_AudioSpec *spec);
    

-/**

    • Get the name and preferred format of the default audio device.
    • Some (but not all!) platforms have an isolated mechanism to get information
    • about the “default” device. This can actually be a completely different
    • device that’s not in the list you get from SDL_GetAudioDeviceSpec(). It can
    • even be a network address! (This is discussed in SDL_OpenAudioDevice().)
    • As a result, this call is not guaranteed to be performant, as it can query
    • the sound server directly every time, unlike the other query functions. You
    • should call this function sparingly!
    • spec will be filled with the sample rate, sample format, and channel
    • count, if a default device exists on the system. If name is provided,
    • will be filled with either a dynamically-allocated UTF-8 string or NULL.
    • \param name A pointer to be filled with the name of the default device (can
    •         be NULL). Please call SDL_free() when you are done with this
      
    •         pointer!
      
    • \param spec The SDL_AudioSpec to be initialized by this function.
    • \param iscapture non-zero to query the default recording device, zero to
    •              query the default output device.
      
    • \returns 0 on success or a negative error code on failure; call
    •      SDL_GetError() for more information.
      
    • \threadsafety It is safe to call this function from any thread.
    • \since This function is available since SDL 3.0.0.
    • \sa SDL_GetAudioDeviceName
    • \sa SDL_GetAudioDeviceSpec
    • \sa SDL_OpenAudioDevice
      */
      -extern DECLSPEC int SDLCALL SDL_GetDefaultAudioInfo(char **name,
  •                                                SDL_AudioSpec *spec,
    
  •                                                int iscapture);
    

+extern DECLSPEC int SDLCALL SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioFormat *fmt, int *channels, int *freq);

/**

  • Open a specific audio device.
    • Passing in a device name of NULL requests the most reasonable default.
    • The device name is a UTF-8 string reported by SDL_GetAudioDeviceName(),
    • but some drivers allow arbitrary and driver-specific strings, such as a
    • hostname/IP address for a remote audio server, or a filename in the
    • diskaudio driver.
    • An opened audio device starts out paused, and should be enabled for playing
    • by calling SDL_PlayAudioDevice(devid) when you are ready for your audio
    • callback function to be called. Since the audio driver may modify the
    • requested size of the audio buffer, you should allocate any local mixing
    • buffers after you open the audio device.
    • The audio callback runs in a separate thread in most cases; you can prevent
    • race conditions between your callback and other threads without fully
    • pausing playback with SDL_LockAudioDevice(). For more information about the
    • callback, see SDL_AudioSpec.
    • Managing the audio spec via ‘desired’ and ‘obtained’:
    • When filling in the desired audio spec structure:
      • desired->freq should be the frequency in sample-frames-per-second (Hz).
      • desired->format should be the audio format (SDL_AUDIO_S16SYS, etc).
      • desired->samples is the desired size of the audio buffer, in _sample
    • frames_ (with stereo output, two samples–left and right–would make a
    • single sample frame). This number should be a power of two, and may be
    • adjusted by the audio driver to a value more suitable for the hardware.
    • Good values seem to range between 512 and 8096 inclusive, depending on
    • the application and CPU speed. Smaller values reduce latency, but can
    • lead to underflow if the application is doing heavy processing and cannot
    • fill the audio buffer in time. Note that the number of sample frames is
    • directly related to time by the following formula: `ms =
    • (sampleframes*1000)/freq`
      • desired->size is the size in bytes of the audio buffer, and is
    • calculated by SDL_OpenAudioDevice(). You don’t initialize this.
      • desired->silence is the value used to set the buffer to silence, and is
    • calculated by SDL_OpenAudioDevice(). You don’t initialize this.
      • desired->callback should be set to a function that will be called when
    • the audio device is ready for more data. It is passed a pointer to the
    • audio buffer, and the length in bytes of the audio buffer. This function
    • usually runs in a separate thread, and so you should protect data
    • structures that it accesses by calling SDL_LockAudioDevice() and
    • SDL_UnlockAudioDevice() in your code. Alternately, you may pass a NULL
    • pointer here, and call SDL_QueueAudio() with some frequency, to queue
    • more audio samples to be played (or for capture devices, call
    • SDL_DequeueAudio() with some frequency, to obtain audio samples).
      • desired->userdata is passed as the first parameter to your callback
    • function. If you passed a NULL callback, this value is ignored.
    • allowed_changes can have the following flags OR’d together:
      • SDL_AUDIO_ALLOW_FREQUENCY_CHANGE
      • SDL_AUDIO_ALLOW_FORMAT_CHANGE
      • SDL_AUDIO_ALLOW_CHANNELS_CHANGE
      • SDL_AUDIO_ALLOW_SAMPLES_CHANGE
      • SDL_AUDIO_ALLOW_ANY_CHANGE
    • These flags specify how SDL should behave when a device cannot offer a
    • specific feature. If the application requests a feature that the hardware
    • doesn’t offer, SDL will always try to get the closest equivalent.
    • For example, if you ask for float32 audio format, but the sound card only
    • supports int16, SDL will set the hardware to int16. If you had set
    • SDL_AUDIO_ALLOW_FORMAT_CHANGE, SDL will change the format in the obtained
    • structure. If that flag was not set, SDL will prepare to convert your
    • callback’s float32 audio to int16 before feeding it to the hardware and
    • will keep the originally requested format in the obtained structure.
    • The resulting audio specs, varying depending on hardware and on what
    • changes were allowed, will then be written back to obtained.
    • If your application can only handle one specific data format, pass a zero
    • for allowed_changes and let SDL transparently handle any differences.
    • \param device a UTF-8 string reported by SDL_GetAudioDeviceName() or a
    •           driver-specific name as appropriate. NULL requests the most
      
    •           reasonable default device.
      
    • \param iscapture non-zero to specify a device should be opened for
    •              recording, not playback
      
    • \param desired an SDL_AudioSpec structure representing the desired output
    •            format
      
    • \param obtained an SDL_AudioSpec structure filled in with the actual output
    •             format
      
    • \param allowed_changes 0, or one or more flags OR’d together
    • \returns a valid device ID that is > 0 on success or 0 on failure; call
    •      SDL_GetError() for more information.
      
    •      For compatibility with SDL 1.2, this will never return 1, since
      
    •      SDL reserves that ID for the legacy SDL_OpenAudio() function.
      
    • Passing in a devid name of zero requests the most reasonable default.
    • You can open both output and capture devices through this function.
    • Output devices will take data from bound audio streams, mix it, and
    • send it to the hardware. Capture devices will feed any bound audio
    • streams with a copy of any incoming data.
    • An opened audio device starts out with no audio streams bound. To
    • start audio playing, bind a stream and supply audio data to it. Unlike
    • SDL2, there is no audio callback; you only bind audio streams and
    • make sure they have data flowing into them.
    • You may request a specific format for the audio device, but there is
    • no promise the device will honor that request for several reasons. As
    • such, it’s only meant to be a hint as to what data your app will
    • provide. Audio streams will accept data in whatever format you specify and
    • manage conversion for you as appropriate. SDL_GetAudioDeviceFormat can
    • tell you the preferred format for the device before opening and the
    • actual format the device is using after opening.
    • It’s legal to open the same device ID more than once; in the end, you must
    • close it the same number of times. This allows libraries to open a device
    • separate from the main app and bind its own streams without conflicting.
    • This function returns the opened device ID on success, so that if you
    • open a device of 0, you’ll have a real ID to bind streams to, but this
    • does not generate new instance IDs. Unlike SDL2, these IDs are assigned
    • to each unique device on the system, open or not, so if you request a
    • specific device, you’ll get
(Patch may be truncated, please check the link at the top of this post.)