diff --git a/CMakeLists.txt b/CMakeLists.txt index 5411cab74291e0..c4778024d9b7c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -451,6 +451,12 @@ option_string(SDL_VENDOR_INFO "Vendor name and/or version to add to SDL_REV set_option(SDL_CCACHE "Use Ccache to speed up build" OFF) set_option(SDL_CLANG_TIDY "Run clang-tidy static analysis" OFF) +set(SDL_OSS OFF) +set(SDL_ALSA OFF) +set(SDL_JACK OFF) +set(SDL_PIPEWIRE OFF) +set(SDL_SNDIO OFF) + option(SDL_WERROR "Enable -Werror" OFF) option(SDL_SHARED "Build a shared version of the library" ${SDL_SHARED_ENABLED_BY_DEFAULT}) diff --git a/docs/README-migration.md b/docs/README-migration.md index 6d649e52ae86e1..7fd4a44a183d9b 100644 --- a/docs/README-migration.md +++ b/docs/README-migration.md @@ -53,13 +53,98 @@ The following structures have been renamed: ## SDL_audio.h -SDL_AudioInit() and SDL_AudioQuit() have been removed. Instead you can call SDL_InitSubSytem() and SDL_QuitSubSytem() with SDL_INIT_AUDIO, which will properly refcount the subsystems. You can choose a specific audio driver using SDL_AUDIO_DRIVER hint. +The audio subsystem in SDL3 is dramatically different than SDL2. There is no longer an audio callback; instead you bind SDL_AudioStreams to devices. -SDL_PauseAudioDevice() is only used to pause audio playback. Use SDL_PlayAudioDevice() to start playing audio. +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); +``` + +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 +188,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 +205,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 544bd98fefe9fd..f421fe5ae0d42e 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,446 +224,277 @@ 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. * - * ```c - * // 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 that same device ID back. + * + * Some backends might offer arbitrary devices (for example, a networked + * audio protocol that can connect to an arbitrary server). For these, as + * a change from SDL2, you should open a device ID of zero and use an SDL + * hint to specify the target if you care, or otherwise let the backend + * figure out a reasonable default. Most backends don't offer anything like + * this, and often this would be an end user setting an environment + * variable for their custom need, and not something an application should + * specifically manage. + * + * \param devid the device instance id to open. 0 requests the most + * reasonable default device. + * \param fmt the requested device format (`SDL_AUDIO_S16`, etc) + * \param channels the requested device channels (1==mono, 2==stereo, etc). + * \param freq the requested device frequency in sample-frames-per-second (Hz) + * \returns The device ID on success, 0 on error; call SDL_GetError() for more information. * * \since This function is available since SDL 3.0.0. * + * \threadsafety It is safe to call this function from any thread. + * * \sa SDL_CloseAudioDevice - * \sa SDL_GetAudioDeviceName - * \sa SDL_LockAudioDevice - * \sa SDL_PlayAudioDevice - * \sa SDL_PauseAudioDevice - * \sa SDL_UnlockAudioDevice + * \sa SDL_GetAudioDeviceFormat */ -extern DECLSPEC SDL_AudioDeviceID SDLCALL SDL_OpenAudioDevice( - const char *device, - int iscapture, - const SDL_AudioSpec *desired, - SDL_AudioSpec *obtained, - int allowed_changes); - +extern DECLSPEC SDL_AudioDeviceID SDLCALL SDL_OpenAudioDevice(SDL_AudioDeviceID devid, SDL_AudioFormat fmt, int channels, int freq); /** - * \name Audio state + * Close a previously-opened audio device. * - * Get the current audio state. - */ -/* @{ */ -typedef enum -{ - SDL_AUDIO_STOPPED = 0, - SDL_AUDIO_PLAYING, - SDL_AUDIO_PAUSED -} SDL_AudioStatus; - -/** - * Use this function to get the current audio state of an audio device. + * The application should close open audio devices once they are no longer + * needed. Audio devices can be opened multiple times; when they are closed + * an equal number of times, its resources are freed, any bound streams are + * unbound, and any audio will stop playing. * - * \param dev the ID of an audio device previously opened with - * SDL_OpenAudioDevice() - * \returns the SDL_AudioStatus of the specified audio device. + * This function may block briefly while pending audio data is played by the + * hardware, so that applications don't drop the last buffer of data they + * supplied. + * + * \param devid an audio device previously opened with SDL_OpenAudioDevice() * * \since This function is available since SDL 3.0.0. * - * \sa SDL_PlayAudioDevice - * \sa SDL_PauseAudioDevice + * \threadsafety It is safe to call this function from any thread. + * + * \sa SDL_OpenAudioDevice */ -extern DECLSPEC SDL_AudioStatus SDLCALL SDL_GetAudioDeviceStatus(SDL_AudioDeviceID dev); -/* @} *//* Audio State */ +extern DECLSPEC void SDLCALL SDL_CloseAudioDevice(SDL_AudioDeviceID devid); /** - * Use this function to play audio on a specified device. + * Bind a list of audio streams to an audio device. * - * Newly-opened audio devices start in the paused state, so you must call this - * function after opening the specified audio device to start playing sound. - * This allows you to safely initialize data for your callback function after - * opening the audio device. Silence will be written to the audio device while - * paused, and the audio callback is guaranteed to not be called. Pausing one - * device does not prevent other unpaused devices from running their - * callbacks. + * Audio data will flow through any bound streams. For an output device, data + * for all bound streams will be mixed together and fed to the device. For a + * capture device, a copy of recorded data will be provided to each bound + * stream. * - * \param dev a device opened by SDL_OpenAudioDevice() - * \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_LockAudioDevice - * \sa SDL_PauseAudioDevice - */ -extern DECLSPEC int SDLCALL SDL_PlayAudioDevice(SDL_AudioDeviceID dev); - - - -/** - * Use this function to pause audio playback on a specified device. + * Audio streams can only be bound to an open device. This operation is + * atomic--all streams bound in the same call will start processing at the same + * time, so they can stay in sync. Also: either all streams will be bound or + * none of them will be. * - * This function pauses the audio callback processing for a given device. - * Silence will be written to the audio device while paused, and the audio - * callback is guaranteed to not be called. Pausing one device does not - * prevent other unpaused devices from running their callbacks. + * It is an error to bind an already-bound stream; it must be explicitly unbound + * first. * - * If you just need to protect a few variables from race conditions vs your - * callback, you shouldn't pause the audio device, as it will lead to dropouts - * in the audio playback. Instead, you should use SDL_LockAudioDevice(). + * Binding a stream to a device will set its output format for output devices, + * and its input format for capture devices, so they match the device's + * settings. The caller is welcome to change the other end of the stream's + * format at any time. * - * \param dev a device opened by SDL_OpenAudioDevice() - * \returns 0 on success or a negative error code on failure; call - * SDL_GetError() for more information. + * \param devid an audio device to bind a stream to. + * \param streams an array of audio streams to unbind. + * \param num_streams Number streams listed in the `streams` array. + * \returns 0 on success, -1 on error; call SDL_GetError() for more information. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_LockAudioDevice - * \sa SDL_PlayAudioDevice + * \threadsafety It is safe to call this function from any thread. + * + * \sa SDL_BindAudioStreams + * \sa SDL_UnbindAudioStreams + * \sa SDL_UnbindAudioStream */ -extern DECLSPEC int SDLCALL SDL_PauseAudioDevice(SDL_AudioDeviceID dev); - +extern DECLSPEC int SDLCALL SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int num_streams); /** - * Load the audio data of a WAVE file into memory. - * - * Loading a WAVE file requires `src`, `spec`, `audio_buf` and `audio_len` to - * be valid pointers. The entire data portion of the file is then loaded into - * memory and decoded if necessary. + * Bind a single audio stream to an audio device. * - * If `freesrc` is non-zero, the data source gets automatically closed and - * freed before the function returns. + * This is a convenience function, equivalent to calling + * `SDL_BindAudioStreams(devid, &stream, 1)`. * - * Supported formats are RIFF WAVE files with the formats PCM (8, 16, 24, and - * 32 bits), IEEE Float (32 bits), Microsoft ADPCM and IMA ADPCM (4 bits), and - * A-law and mu-law (8 bits). Other formats are currently unsupported and - * cause an error. - * - * If this function succeeds, the pointer returned by it is equal to `spec` - * and the pointer to the audio data allocated by the function is written to - * `audio_buf` and its length in bytes to `audio_len`. The SDL_AudioSpec - * members `freq`, `channels`, and `format` are set to the values of the audio - * data in the buffer. The `samples` member is set to a sane default and all - * others are set to zero. + * \param devid an audio device to bind a stream to. + * \param stream an audio stream to bind to a device. + * \returns 0 on success, -1 on error; call SDL_GetError() for more information. * - * It's necessary to use SDL_free() to free the audio data returned in - * `audio_buf` when it is no longer used. + * \since This function is available since SDL 3.0.0. * - * Because of the underspecification of the .WAV format, there are many - * problematic files in the wild that cause issues with strict decoders. To - * provide compatibility with these files, this decoder is lenient in regards - * to the truncation of the file, the fact chunk, and the size of the RIFF - * chunk. The hints `SDL_HINT_WAVE_RIFF_CHUNK_SIZE`, - * `SDL_HINT_WAVE_TRUNCATION`, and `SDL_HINT_WAVE_FACT_CHUNK` can be used to - * tune the behavior of the loading process. + * \threadsafety It is safe to call this function from any thread. * - * Any file that is invalid (due to truncation, corruption, or wrong values in - * the headers), too big, or unsupported causes an error. Additionally, any - * critical I/O error from the data source will terminate the loading process - * with an error. The function returns NULL on error and in all cases (with - * the exception of `src` being NULL), an appropriate error message will be - * set. + * \sa SDL_BindAudioStreams + * \sa SDL_UnbindAudioStreams + * \sa SDL_UnbindAudioStream + */ +extern DECLSPEC int SDLCALL SDL_BindAudioStream(SDL_AudioDeviceID devid, SDL_AudioStream *stream); + +/** + * Unbind a list of audio streams from their audio devices. * - * It is required that the data source supports seeking. + * The streams being unbound do not all have to be on the same device. + * All streams on the same device will be unbound atomically (data will stop flowing + * through them all unbound streams on the same device at the same time). * - * Example: + * Unbinding a stream that isn't bound to a device is a legal no-op. * - * ```c - * SDL_LoadWAV_RW(SDL_RWFromFile("sample.wav", "rb"), 1, &spec, &buf, &len); - * ``` + * \param streams an array of audio streams to unbind. + * \param num_streams Number streams listed in the `streams` array. * - * Note that the SDL_LoadWAV macro does this same thing for you, but in a less - * messy way: + * \since This function is available since SDL 3.0.0. * - * ```c - * SDL_LoadWAV("sample.wav", &spec, &buf, &len); - * ``` + * \threadsafety It is safe to call this function from any thread. * - * \param src The data source for the WAVE data - * \param freesrc If non-zero, SDL will _always_ free the data source - * \param spec An SDL_AudioSpec that will be filled in with the wave file's - * format details - * \param audio_buf A pointer filled with the audio data, allocated by the - * function. - * \param audio_len A pointer filled with the length of the audio data buffer - * in bytes - * \returns This function, if successfully called, returns `spec`, which will - * be filled with the audio data format of the wave source data. - * `audio_buf` will be filled with a pointer to an allocated buffer - * containing the audio data, and `audio_len` is filled with the - * length of that audio buffer in bytes. + * \sa SDL_BindAudioStreams + * \sa SDL_BindAudioStream + * \sa SDL_UnbindAudioStream + */ +extern DECLSPEC void SDLCALL SDL_UnbindAudioStreams(SDL_AudioStream **streams, int num_streams); + +/** + * Unbind a single audio stream from its audio device. * - * This function returns NULL if the .WAV file cannot be opened, uses - * an unknown data format, or is corrupt; call SDL_GetError() for - * more information. + * This is a convenience function, equivalent to calling + * `SDL_UnbindAudioStreams(&stream, 1)`. * - * When the application is done with the data returned in - * `audio_buf`, it should call SDL_free() to dispose of it. + * \param stream an audio stream to unbind from a device. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_free - * \sa SDL_LoadWAV - */ -extern DECLSPEC SDL_AudioSpec *SDLCALL SDL_LoadWAV_RW(SDL_RWops * src, - int freesrc, - SDL_AudioSpec * spec, - Uint8 ** audio_buf, - Uint32 * audio_len); - -/** - * Loads a WAV from a file. - * Compatibility convenience function. + * \threadsafety It is safe to call this function from any thread. + * + * \sa SDL_BindAudioStream + * \sa SDL_BindAudioStreams + * \sa SDL_UnbindAudioStreams */ -#define SDL_LoadWAV(file, spec, audio_buf, audio_len) \ - SDL_LoadWAV_RW(SDL_RWFromFile(file, "rb"),1, spec,audio_buf,audio_len) - +extern DECLSPEC void SDLCALL SDL_UnbindAudioStream(SDL_AudioStream *stream); -/* 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. - - 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. - */ -struct SDL_AudioStream; /* this is opaque to the outside world. */ -typedef struct SDL_AudioStream SDL_AudioStream; /** * Create a new audio stream. @@ -925,301 +728,172 @@ extern DECLSPEC int SDLCALL SDL_ClearAudioStream(SDL_AudioStream *stream); */ extern DECLSPEC void SDLCALL SDL_DestroyAudioStream(SDL_AudioStream *stream); -#define SDL_MIX_MAXVOLUME 128 /** - * Mix audio data in a specified format. + * Convenience function to create and bind an audio stream in one step. * - * This takes an audio buffer `src` of `len` bytes of `format` data and mixes - * it into `dst`, performing addition, volume adjustment, and overflow - * clipping. The buffer pointed to by `dst` must also be `len` bytes of - * `format` data. + * This manages the creation of an audio stream, and setting its format + * correctly to match both the app and the audio device's needs. This is + * optional, but slightly less cumbersome to set up for a common use case. * - * This is provided for convenience -- you can mix your own audio data. + * The format parameters (`fmt`, `channels`, `freq`) represent the app's side + * of the audio stream. That is, for recording audio, this will be the output + * format, and for playing audio, this will be the input format. This function + * will set the other side of the audio stream to the device's format. * - * Do not use this function for mixing together more than two streams of - * sample data. The output from repeated application of this function may be - * distorted by clipping, because there is no accumulator with greater range - * than the input (not to mention this being an inefficient way of doing it). - * - * It is a common misconception that this function is required to write audio - * data to an output stream in an audio callback. While you can do that, - * SDL_MixAudioFormat() is really only needed when you're mixing a single - * audio stream with a volume adjustment. - * - * \param dst the destination for the mixed audio - * \param src the source audio buffer to be mixed - * \param format the SDL_AudioFormat structure representing the desired audio - * format - * \param len the length of the audio buffer in bytes - * \param volume ranges from 0 - 128, and should be set to SDL_MIX_MAXVOLUME - * for full audio volume - * \returns 0 on success or a negative error code on failure; call - * SDL_GetError() for more information. + * \param devid an audio device to bind a stream to. This must be an opened device, and can not be zero. + * \param fmt the audio stream's format (`SDL_AUDIO_S16`, etc) + * \param channels the audio stream's channel count (1==mono, 2==stereo, etc). + * \param freq the audio stream's frequency in sample-frames-per-second (Hz) + * \returns a bound audio stream on success, ready to use. NULL on error; call SDL_GetError() for more information. * * \since This function is available since SDL 3.0.0. - */ -extern DECLSPEC int SDLCALL SDL_MixAudioFormat(Uint8 * dst, - const Uint8 * src, - SDL_AudioFormat format, - Uint32 len, int volume); - -/** - * Queue more audio on non-callback devices. - * - * If you are looking to retrieve queued audio from a non-callback capture - * device, you want SDL_DequeueAudio() instead. SDL_QueueAudio() will return - * -1 to signify an error if you use it with capture devices. - * - * SDL offers two ways to feed audio to the device: you can either supply a - * callback that SDL triggers with some frequency to obtain more audio (pull - * method), or you can supply no callback, and then SDL will expect you to - * supply data at regular intervals (push method) with this function. - * - * There are no limits on the amount of data you can queue, short of - * exhaustion of address space. Queued data will drain to the device as - * necessary without further intervention from you. If the device needs audio - * but there is not enough queued, it will play silence to make up the - * difference. This means you will have skips in your audio playback if you - * aren't routinely queueing sufficient data. - * - * This function copies the supplied data, so you are safe to free it when the - * function returns. This function is thread-safe, but queueing to the same - * device from two threads at once does not promise which buffer will be - * queued first. - * - * You may not queue audio on a device that is using an application-supplied - * callback; doing so returns an error. You have to use the audio callback or - * queue audio with this function, but not both. - * - * You should not call SDL_LockAudio() on the device before queueing; SDL - * handles locking internally for this function. - * - * Note that SDL does not support planar audio. You will need to resample from - * planar audio formats into a non-planar one (see SDL_AudioFormat) before - * queuing audio. - * - * \param dev the device ID to which we will queue audio - * \param data the data to queue to the device for later playback - * \param len the number of bytes (not samples!) to which `data` points - * \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. + * \threadsafety It is safe to call this function from any thread. * - * \sa SDL_ClearQueuedAudio - * \sa SDL_GetQueuedAudioSize + * \sa SDL_BindAudioStreams + * \sa SDL_UnbindAudioStreams + * \sa SDL_UnbindAudioStream */ -extern DECLSPEC int SDLCALL SDL_QueueAudio(SDL_AudioDeviceID dev, const void *data, Uint32 len); +extern DECLSPEC SDL_AudioStream *SDLCALL SDL_CreateAndBindAudioStream(SDL_AudioDeviceID devid, SDL_AudioFormat fmt, int channels, int freq); -/** - * Dequeue more audio on non-callback devices. - * - * If you are looking to queue audio for output on a non-callback playback - * device, you want SDL_QueueAudio() instead. SDL_DequeueAudio() will always - * return 0 if you use it with playback devices. - * - * SDL offers two ways to retrieve audio from a capture device: you can either - * supply a callback that SDL triggers with some frequency as the device - * records more audio data, (push method), or you can supply no callback, and - * then SDL will expect you to retrieve data at regular intervals (pull - * method) with this function. - * - * There are no limits on the amount of data you can queue, short of - * exhaustion of address space. Data from the device will keep queuing as - * necessary without further intervention from you. This means you will - * eventually run out of memory if you aren't routinely dequeueing data. - * - * Capture devices will not queue data when paused; if you are expecting to - * not need captured audio for some length of time, use SDL_PauseAudioDevice() - * to stop the capture device from queueing more data. This can be useful - * during, say, level loading times. When unpaused, capture devices will start - * queueing data from that point, having flushed any capturable data available - * while paused. - * - * This function is thread-safe, but dequeueing from the same device from two - * threads at once does not promise which thread will dequeue data first. - * - * You may not dequeue audio from a device that is using an - * application-supplied callback; doing so returns an error. You have to use - * the audio callback, or dequeue audio with this function, but not both. - * - * You should not call SDL_LockAudio() on the device before dequeueing; SDL - * handles locking internally for this function. - * - * \param dev the device ID from which we will dequeue audio - * \param data a pointer into where audio data should be copied - * \param len the number of bytes (not samples!) to which (data) points - * \returns the number of bytes dequeued, which could be less than requested; - * call SDL_GetError() for more information. - * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_ClearQueuedAudio - * \sa SDL_GetQueuedAudioSize - */ -extern DECLSPEC Uint32 SDLCALL SDL_DequeueAudio(SDL_AudioDeviceID dev, void *data, Uint32 len); /** - * Get the number of bytes of still-queued audio. + * Load the audio data of a WAVE file into memory. * - * For playback devices: this is the number of bytes that have been queued for - * playback with SDL_QueueAudio(), but have not yet been sent to the hardware. + * Loading a WAVE file requires `src`, `spec`, `audio_buf` and `audio_len` to + * be valid pointers. The entire data portion of the file is then loaded into + * memory and decoded if necessary. * - * Once we've sent it to the hardware, this function can not decide the exact - * byte boundary of what has been played. It's possible that we just gave the - * hardware several kilobytes right before you called this function, but it - * hasn't played any of it yet, or maybe half of it, etc. + * If `freesrc` is non-zero, the data source gets automatically closed and + * freed before the function returns. * - * For capture devices, this is the number of bytes that have been captured by - * the device and are waiting for you to dequeue. This number may grow at any - * time, so this only informs of the lower-bound of available data. + * Supported formats are RIFF WAVE files with the formats PCM (8, 16, 24, and + * 32 bits), IEEE Float (32 bits), Microsoft ADPCM and IMA ADPCM (4 bits), and + * A-law and mu-law (8 bits). Other formats are currently unsupported and + * cause an error. * - * You may not queue or dequeue audio on a device that is using an - * application-supplied callback; calling this function on such a device - * always returns 0. You have to use the audio callback or queue audio, but - * not both. + * If this function succeeds, the return value is zero and the pointer to the + * audio data allocated by the function is written to `audio_buf` and its + * length in bytes to `audio_len`. The SDL_AudioSpec members `freq`, + * `channels`, and `format` are set to the values of the audio data in the + * buffer. The `samples` member is set to a sane default and all + * others are set to zero. * - * You should not call SDL_LockAudio() on the device before querying; SDL - * handles locking internally for this function. + * It's necessary to use SDL_free() to free the audio data returned in + * `audio_buf` when it is no longer used. * - * \param dev the device ID of which we will query queued audio size - * \returns the number of bytes (not samples!) of queued audio. + * Because of the underspecification of the .WAV format, there are many + * problematic files in the wild that cause issues with strict decoders. To + * provide compatibility with these files, this decoder is lenient in regards + * to the truncation of the file, the fact chunk, and the size of the RIFF + * chunk. The hints `SDL_HINT_WAVE_RIFF_CHUNK_SIZE`, + * `SDL_HINT_WAVE_TRUNCATION`, and `SDL_HINT_WAVE_FACT_CHUNK` can be used to + * tune the behavior of the loading process. * - * \since This function is available since SDL 3.0.0. + * Any file that is invalid (due to truncation, corruption, or wrong values in + * the headers), too big, or unsupported causes an error. Additionally, any + * critical I/O error from the data source will terminate the loading process + * with an error. The function returns NULL on error and in all cases (with + * the exception of `src` being NULL), an appropriate error message will be + * set. * - * \sa SDL_ClearQueuedAudio - * \sa SDL_QueueAudio - * \sa SDL_DequeueAudio - */ -extern DECLSPEC Uint32 SDLCALL SDL_GetQueuedAudioSize(SDL_AudioDeviceID dev); - -/** - * Drop any queued audio data waiting to be sent to the hardware. + * It is required that the data source supports seeking. * - * Immediately after this call, SDL_GetQueuedAudioSize() will return 0. For - * output devices, the hardware will start playing silence if more audio isn't - * queued. For capture devices, the hardware will start filling the empty - * queue with new data if the capture device isn't paused. + * Example: * - * This will not prevent playback of queued audio that's already been sent to - * the hardware, as we can not undo that, so expect there to be some fraction - * of a second of audio that might still be heard. This can be useful if you - * want to, say, drop any pending music or any unprocessed microphone input - * during a level change in your game. + * ```c + * SDL_LoadWAV_RW(SDL_RWFromFile("sample.wav", "rb"), 1, &spec, &buf, &len); + * ``` * - * You may not queue or dequeue audio on a device that is using an - * application-supplied callback; calling this function on such a device - * always returns 0. You have to use the audio callback or queue audio, but - * not both. + * Note that the SDL_LoadWAV macro does this same thing for you, but in a less + * messy way: + * + * ```c + * SDL_LoadWAV("sample.wav", &spec, &buf, &len); + * ``` * - * You should not call SDL_LockAudio() on the device before clearing the - * queue; SDL handles locking internally for this function. + * \param src The data source for the WAVE data + * \param freesrc If non-zero, SDL will _always_ free the data source + * \param fmt A pointer to an SDL_AudioFormat that will be set to the + * WAVE data's format on successful return. + * \param channels A pointer to an int that will be set to the + * WAVE data's channel count on successful return. + * \param freq A pointer to an int that will be set to the + * WAVE data's sample rate in Hz on successful return. + * \param audio_buf A pointer filled with the audio data, allocated by the + * function. + * \param audio_len A pointer filled with the length of the audio data buffer + * in bytes + * \returns This function, if successfully called, returns 0. `audio_buf` + * will be filled with a pointer to an allocated buffer + * containing the audio data, and `audio_len` is filled with the + * length of that audio buffer in bytes. * - * This function always succeeds and thus returns void. + * This function returns -1 if the .WAV file cannot be opened, uses + * an unknown data format, or is corrupt; call SDL_GetError() for + * more information. * - * \param dev the device ID of which to clear the audio queue - * \returns 0 on success or a negative error code on failure; call - * SDL_GetError() for more information. + * When the application is done with the data returned in + * `audio_buf`, it should call SDL_free() to dispose of it. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_GetQueuedAudioSize - * \sa SDL_QueueAudio - * \sa SDL_DequeueAudio + * \sa SDL_free + * \sa SDL_LoadWAV */ -extern DECLSPEC int SDLCALL SDL_ClearQueuedAudio(SDL_AudioDeviceID dev); - +extern DECLSPEC int SDLCALL SDL_LoadWAV_RW(SDL_RWops * src, int freesrc, + SDL_AudioFormat *fmt, int *channels, int *freq, + Uint8 ** audio_buf, + Uint32 * audio_len); /** - * \name Audio lock functions - * - * The lock manipulated by these functions protects the callback function. - * During a SDL_LockAudio()/SDL_UnlockAudio() pair, you can be guaranteed that - * the callback function is not running. Do not call these from the callback - * function or you will cause deadlock. + * Loads a WAV from a file. + * Compatibility convenience function. */ -/* @{ */ +#define SDL_LoadWAV(file, fmt, channels, freq, audio_buf, audio_len) \ + SDL_LoadWAV_RW(SDL_RWFromFile(file, "rb"), 1, fmt, channels, freq, audio_buf, audio_len) -/** - * Use this function to lock out the audio callback function for a specified - * device. - * - * The lock manipulated by these functions protects the audio callback - * function specified in SDL_OpenAudioDevice(). During a - * SDL_LockAudioDevice()/SDL_UnlockAudioDevice() pair, you can be guaranteed - * that the callback function for that device is not running, even if the - * device is not paused. While a device is locked, any other unpaused, - * unlocked devices may still run their callbacks. - * - * Calling this function from inside your audio callback is unnecessary. SDL - * obtains this lock before calling your function, and releases it when the - * function returns. - * - * You should not hold the lock longer than absolutely necessary. If you hold - * it too long, you'll experience dropouts in your audio playback. Ideally, - * your application locks the device, sets a few variables and unlocks again. - * Do not do heavy work while holding the lock for a device. - * - * It is safe to lock the audio device multiple times, as long as you unlock - * it an equivalent number of times. The callback will not run until the - * device has been unlocked completely in this way. If your application fails - * to unlock the device appropriately, your callback will never run, you might - * hear repeating bursts of audio, and SDL_CloseAudioDevice() will probably - * deadlock. - * - * Internally, the audio device lock is a mutex; if you lock from two threads - * at once, not only will you block the audio callback, you'll block the other - * thread. - * - * \param dev the ID of the device to be locked - * \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_UnlockAudioDevice - */ -extern DECLSPEC int SDLCALL SDL_LockAudioDevice(SDL_AudioDeviceID dev); -/** - * Use this function to unlock the audio callback function for a specified - * device. - * - * This function should be paired with a previous SDL_LockAudioDevice() call. - * - * \param dev the ID of the device to be unlocked - * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_LockAudioDevice - */ -extern DECLSPEC void SDLCALL SDL_UnlockAudioDevice(SDL_AudioDeviceID dev); -/* @} *//* Audio lock functions */ + +#define SDL_MIX_MAXVOLUME 128 /** - * Use this function to shut down audio processing and close the audio device. + * Mix audio data in a specified format. * - * The application should close open audio devices once they are no longer - * needed. Calling this function will wait until the device's audio callback - * is not running, release the audio hardware and then clean up internal - * state. No further audio will play from this device once this function - * returns. + * This takes an audio buffer `src` of `len` bytes of `format` data and mixes + * it into `dst`, performing addition, volume adjustment, and overflow + * clipping. The buffer pointed to by `dst` must also be `len` bytes of + * `format` data. * - * This function may block briefly while pending audio data is played by the - * hardware, so that applications don't drop the last buffer of data they - * supplied. + * This is provided for convenience -- you can mix your own audio data. * - * The device ID is invalid as soon as the device is closed, and is eligible - * for reuse in a new SDL_OpenAudioDevice() call immediately. + * Do not use this function for mixing together more than two streams of + * sample data. The output from repeated application of this function may be + * distorted by clipping, because there is no accumulator with greater range + * than the input (not to mention this being an inefficient way of doing it). * - * \param dev an audio device previously opened with SDL_OpenAudioDevice() + * It is a common misconception that this function is required to write audio + * data to an output stream in an audio callback. While you can do that, + * SDL_MixAudioFormat() is really only needed when you're mixing a single + * audio stream with a volume adjustment. * - * \since This function is available since SDL 3.0.0. + * \param dst the destination for the mixed audio + * \param src the source audio buffer to be mixed + * \param format the SDL_AudioFormat structure representing the desired audio + * format + * \param len the length of the audio buffer in bytes + * \param volume ranges from 0 - 128, and should be set to SDL_MIX_MAXVOLUME + * for full audio volume + * \returns 0 on success or a negative error code on failure; call + * SDL_GetError() for more information. * - * \sa SDL_OpenAudioDevice + * \since This function is available since SDL 3.0.0. */ -extern DECLSPEC void SDLCALL SDL_CloseAudioDevice(SDL_AudioDeviceID dev); +extern DECLSPEC int SDLCALL SDL_MixAudioFormat(Uint8 * dst, + const Uint8 * src, + SDL_AudioFormat format, + Uint32 len, int volume); /** * Convert some audio data of one format to another format. @@ -1254,16 +928,34 @@ extern DECLSPEC void SDLCALL SDL_CloseAudioDevice(SDL_AudioDeviceID dev); * \sa SDL_CreateAudioStream */ extern DECLSPEC int SDLCALL SDL_ConvertAudioSamples(SDL_AudioFormat src_format, - Uint8 src_channels, + int src_channels, int src_rate, const Uint8 *src_data, int src_len, SDL_AudioFormat dst_format, - Uint8 dst_channels, + int dst_channels, int dst_rate, Uint8 **dst_data, int *dst_len); + +/** + * Get the appropriate memset value for silencing an audio format. + * + * The value returned by this function can be used as the second + * argument to memset (or SDL_memset) to set an audio buffer in + * a specific format to silence. + * + * \param format the audio data format to query. + * \returns A byte value that can be passed to memset. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC int SDLCALL SDL_GetSilenceValueForFormat(SDL_AudioFormat format); + + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/include/SDL3/SDL_test_common.h b/include/SDL3/SDL_test_common.h index 4c151471c8105d..f2d66e51e77fca 100644 --- a/include/SDL3/SDL_test_common.h +++ b/include/SDL3/SDL_test_common.h @@ -97,7 +97,9 @@ typedef struct /* Audio info */ const char *audiodriver; - SDL_AudioSpec audiospec; + SDL_AudioFormat audio_format; + int audio_channels; + int audio_freq; SDL_AudioDeviceID audio_id; /* GL settings */ diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 80f65588688c76..05d6e8321046f1 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -20,15 +20,14 @@ */ #include "SDL_internal.h" -/* Allow access to a raw mixing buffer */ - #include "SDL_audio_c.h" #include "SDL_sysaudio.h" #include "../thread/SDL_systhread.h" #include "../SDL_utils_c.h" +extern void Android_JNI_AudioSetThreadPriority(int, int); /* we need this on Android in the audio device threads. */ + static SDL_AudioDriver current_audio; -static SDL_AudioDevice *open_devices[16]; /* Available audio drivers */ static const AudioBootStrap *const bootstrap[] = { @@ -101,71 +100,274 @@ static const AudioBootStrap *const bootstrap[] = { NULL }; -static SDL_AudioDevice *get_audio_device(SDL_AudioDeviceID id) +int SDL_GetNumAudioDrivers(void) +{ + return SDL_arraysize(bootstrap) - 1; +} + +const char *SDL_GetAudioDriver(int index) +{ + if (index >= 0 && index < SDL_GetNumAudioDrivers()) { + return bootstrap[index]->name; + } + return NULL; +} + +const char *SDL_GetCurrentAudioDriver(void) +{ + return current_audio.name; +} + +/* device management and hotplug... */ + + +/* SDL_AudioDevice, in SDL3, represents a piece of hardware, whether it is in use or not, so these objects exist as long as + the system-level device is available. + + Devices get destroyed for three reasons: + - They were lost to the system (a USB cable is kicked out, etc). + - They failed for some other unlikely reason at the API level (which is _also_ probably a USB cable being kicked out). + - We are shutting down, so all allocated resources are being freed. + + They are _not_ destroyed because we are done using them (when we "close" a playing device). +*/ +static void CloseAudioDevice(SDL_AudioDevice *device); + + +/* this must not be called while `device` is still in a device list, or while a device's audio thread is still running (except if the thread calls this while shutting down). */ +static void DestroyAudioDevice(SDL_AudioDevice *device) +{ + SDL_AudioStream *stream; + SDL_AudioStream *next; + + if (!device) { + return; + } + + if (SDL_AtomicGet(&device->refcount) > 0) { + SDL_AtomicSet(&device->refcount, 0); /* it's going down NOW. */ + CloseAudioDevice(device); + } + + /* unbind any still-bound streams... */ + SDL_LockMutex(device->lock); + for (stream = device->bound_streams; stream != NULL; stream = next) { + SDL_LockMutex(stream->lock); + next = stream->next_binding; + stream->next_binding = NULL; + stream->prev_binding = NULL; + stream->bound_device = NULL; + SDL_UnlockMutex(stream->lock); + } + SDL_UnlockMutex(device->lock); + + current_audio.impl.FreeDeviceHandle(device->handle); + + SDL_DestroyMutex(device->lock); + SDL_free(device->work_buffer); + SDL_free(device->name); + SDL_free(device); +} + +static SDL_AudioDevice *CreateAudioDevice(const char *name, SDL_bool iscapture, SDL_AudioFormat fmt, int channels, int freq, void *handle, SDL_AudioDevice **devices, SDL_AtomicInt *device_count) { - id--; - if ((id >= SDL_arraysize(open_devices)) || (open_devices[id] == NULL)) { - SDL_SetError("Invalid audio device ID"); + SDL_AudioDevice *device; + + SDL_assert(name != NULL); + + if (SDL_AtomicGet(¤t_audio.shutting_down)) { + return NULL; /* we're shutting down, don't add any devices that are hotplugged at the last possible moment. */ + } + + device = (SDL_AudioDevice *)SDL_calloc(1, sizeof(SDL_AudioDevice)); + if (!device) { + SDL_OutOfMemory(); + return NULL; + } + + device->name = SDL_strdup(name); + if (!device->name) { + SDL_free(device); + SDL_OutOfMemory(); + return NULL; + } + + device->lock = SDL_CreateMutex(); + if (!device->lock) { + SDL_free(device->name); + SDL_free(device); return NULL; } - return open_devices[id]; + SDL_AtomicSet(&device->shutdown, 0); + SDL_AtomicSet(&device->condemned, 0); + device->iscapture = iscapture; + device->format = device->default_format = fmt; + device->channels = device->default_channels = channels; + device->freq = device->default_freq = freq; + device->silence_value = SDL_GetSilenceValueForFormat(device->format); + device->handle = handle; + device->prev = NULL; + + /* Assign an instance id! Start at 2, in case there are things from the SDL2 era that still think 1 is a special value. */ + /* There's no reasonable scenario where this rolls over, but just in case, we wrap it in a loop. */ + /* Also, make sure capture instance IDs are even, and output IDs are odd, so we know what kind of device it is from just the ID. */ + do { + device->instance_id = (SDL_AudioDeviceID) (SDL_AtomicIncRef(¤t_audio.last_device_instance_id) + 1); + } while ( ((iscapture && (device->instance_id & 1)) || (!iscapture && ((device->instance_id & 1) == 0))) || (device->instance_id < 2) ); + + SDL_LockRWLockForWriting(current_audio.device_list_lock); + device->next = *devices; + *devices = device; + SDL_AtomicIncRef(device_count); + SDL_UnlockRWLock(current_audio.device_list_lock); + + return device; +} + +static SDL_AudioDevice *CreateAudioCaptureDevice(const char *name, SDL_AudioFormat fmt, int channels, int freq, void *handle) +{ + SDL_assert(current_audio.impl.HasCaptureSupport); + return CreateAudioDevice(name, SDL_TRUE, fmt, channels, freq, handle, ¤t_audio.capture_devices, ¤t_audio.capture_device_count); +} + +static SDL_AudioDevice *CreateAudioOutputDevice(const char *name, SDL_AudioFormat fmt, int channels, int freq, void *handle) +{ + return CreateAudioDevice(name, SDL_FALSE, fmt, channels, freq, handle, ¤t_audio.output_devices, ¤t_audio.output_device_count); } -int get_max_num_audio_dev(void) +/* The audio backends call this when a new device is plugged in. */ +SDL_AudioDevice *SDL_AddAudioDevice(const SDL_bool iscapture, const char *name, SDL_AudioFormat fmt, int channels, int freq, void *handle) { - return SDL_arraysize(open_devices); + SDL_AudioDevice *device; + + if (fmt == 0) { + fmt = DEFAULT_AUDIO_FORMAT; + } + if (channels == 0) { + channels = DEFAULT_AUDIO_CHANNELS; + } + if (freq == 0) { + freq = DEFAULT_AUDIO_FREQUENCY; + } + + device = iscapture ? CreateAudioCaptureDevice(name, fmt, channels, freq, handle) : CreateAudioOutputDevice(name, fmt, channels, freq, handle); + if (device) { + /* Post the event, if desired */ + if (SDL_EventEnabled(SDL_EVENT_AUDIO_DEVICE_ADDED)) { + SDL_Event event; + event.type = SDL_EVENT_AUDIO_DEVICE_ADDED; + event.common.timestamp = 0; + event.adevice.which = device->instance_id; + event.adevice.iscapture = iscapture; + SDL_PushEvent(&event); + } + } + return device; } -SDL_AudioDevice *get_audio_dev(SDL_AudioDeviceID id) +/* Called when a device is removed from the system, or it fails unexpectedly, from any thread, possibly even the audio device's thread. */ +void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device) { - return open_devices[id]; + SDL_bool was_live = SDL_FALSE; + SDL_bool should_destroy = SDL_TRUE; + + if (!device) { + return; + } + + /* take it out of the device list. */ + SDL_LockRWLockForWriting(current_audio.device_list_lock); + SDL_LockMutex(device->lock); /* make sure nothing else is messing with the device before continuing. */ + if (device == current_audio.output_devices) { + SDL_assert(device->prev == NULL); + current_audio.output_devices = device->next; + was_live = SDL_TRUE; + } else if (device == current_audio.capture_devices) { + SDL_assert(device->prev == NULL); + current_audio.capture_devices = device->next; + was_live = SDL_TRUE; + } + if (device->prev != NULL) { + device->prev->next = device->next; + was_live = SDL_TRUE; + } + if (device->next != NULL) { + device->next->prev = device->prev; + was_live = SDL_TRUE; + } + SDL_UnlockRWLock(current_audio.device_list_lock); + + /* now device is not in the list, and we own it, so no one should be able to find it again, except the audio thread, which holds a pointer! */ + SDL_AtomicSet(&device->condemned, 1); + SDL_AtomicSet(&device->shutdown, 1); /* tell audio thread to terminate. */ + if (device->thread) { + should_destroy = SDL_FALSE; /* if there's an audio thread, don't free until thread is terminating. */ + } + SDL_UnlockMutex(device->lock); + + /* Post the event, if we haven't tried to before and if it's desired */ + if (was_live && SDL_EventEnabled(SDL_EVENT_AUDIO_DEVICE_REMOVED)) { + SDL_Event event; + event.type = SDL_EVENT_AUDIO_DEVICE_REMOVED; + event.common.timestamp = 0; + event.adevice.which = device->instance_id; + event.adevice.iscapture = device->iscapture ? 1 : 0; + SDL_PushEvent(&event); + } + + if (should_destroy) { + DestroyAudioDevice(device); + } } + /* stubs for audio drivers that don't need a specific entry point... */ + static void SDL_AudioDetectDevices_Default(void) { /* you have to write your own implementation if these assertions fail. */ SDL_assert(current_audio.impl.OnlyHasDefaultOutputDevice); SDL_assert(current_audio.impl.OnlyHasDefaultCaptureDevice || !current_audio.impl.HasCaptureSupport); - SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *)((size_t)0x1)); + SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, 0, 0, 0, (void *)((size_t)0x1)); if (current_audio.impl.HasCaptureSupport) { - SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *)((size_t)0x2)); + SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, 0, 0, 0, (void *)((size_t)0x2)); } } -static void SDL_AudioThreadInit_Default(SDL_AudioDevice *_this) +static void SDL_AudioThreadInit_Default(SDL_AudioDevice *device) { /* no-op. */ } -static void SDL_AudioThreadDeinit_Default(SDL_AudioDevice *_this) +static void SDL_AudioThreadDeinit_Default(SDL_AudioDevice *device) { /* no-op. */ } -static void SDL_AudioWaitDevice_Default(SDL_AudioDevice *_this) +static void SDL_AudioWaitDevice_Default(SDL_AudioDevice *device) { /* no-op. */ } -static void SDL_AudioPlayDevice_Default(SDL_AudioDevice *_this) +static void SDL_AudioPlayDevice_Default(SDL_AudioDevice *device, int buffer_size) { /* no-op. */ } -static Uint8 *SDL_AudioGetDeviceBuf_Default(SDL_AudioDevice *_this) +static Uint8 *SDL_AudioGetDeviceBuf_Default(SDL_AudioDevice *device, int *buffer_size) { + *buffer_size = 0; return NULL; } -static int SDL_AudioCaptureFromDevice_Default(SDL_AudioDevice *_this, void *buffer, int buflen) +static int SDL_AudioCaptureFromDevice_Default(SDL_AudioDevice *device, void *buffer, int buflen) { return -1; /* just fail immediately. */ } -static void SDL_AudioFlushCapture_Default(SDL_AudioDevice *_this) +static void SDL_AudioFlushCapture_Default(SDL_AudioDevice *device) { /* no-op. */ } -static void SDL_AudioCloseDevice_Default(SDL_AudioDevice *_this) +static void SDL_AudioCloseDevice_Default(SDL_AudioDevice *device) { /* no-op. */ } @@ -177,32 +379,15 @@ static void SDL_AudioFreeDeviceHandle_Default(void *handle) { /* no-op. */ } -static int SDL_AudioOpenDevice_Default(SDL_AudioDevice *_this, const char *devname) +static int SDL_AudioOpenDevice_Default(SDL_AudioDevice *device) { return SDL_Unsupported(); } -static void SDL_AudioLockDevice_Default(SDL_AudioDevice *device) -{ - SDL_LockMutex(device->mixer_lock); -} - -static void SDL_AudioUnlockDevice_Default(SDL_AudioDevice *device) -{ - SDL_UnlockMutex(device->mixer_lock); -} - -static void finish_audio_entry_points_init(void) +/* Fill in stub functions for unused driver entry points. This lets us blindly call them without having to check for validity first. */ +static void CompleteAudioEntryPoints(void) { - /* - * Fill in stub functions for unused driver entry points. This lets us - * blindly call them without having to check for validity first. - */ - -#define FILL_STUB(x) \ - if (current_audio.impl.x == NULL) { \ - current_audio.impl.x = SDL_Audio##x##_Default; \ - } + #define FILL_STUB(x) if (!current_audio.impl.x) { current_audio.impl.x = SDL_Audio##x##_Default; } FILL_STUB(DetectDevices); FILL_STUB(OpenDevice); FILL_STUB(ThreadInit); @@ -213,1283 +398,865 @@ static void finish_audio_entry_points_init(void) FILL_STUB(CaptureFromDevice); FILL_STUB(FlushCapture); FILL_STUB(CloseDevice); - FILL_STUB(LockDevice); - FILL_STUB(UnlockDevice); FILL_STUB(FreeDeviceHandle); FILL_STUB(Deinitialize); -#undef FILL_STUB + #undef FILL_STUB } -/* device hotplug support... */ - -static int add_audio_device(const char *name, SDL_AudioSpec *spec, void *handle, SDL_AudioDeviceItem **devices, int *devCount) +/* !!! FIXME: the video subsystem does SDL_VideoInit, not SDL_InitVideo. Make this match. */ +int SDL_InitAudio(const char *driver_name) { - int retval = -1; - SDL_AudioDeviceItem *item; - const SDL_AudioDeviceItem *i; - int dupenum = 0; - - SDL_assert(handle != NULL); /* we reserve NULL, audio backends can't use it. */ - SDL_assert(name != NULL); + SDL_RWLock *device_list_lock = NULL; + SDL_bool initialized = SDL_FALSE; + SDL_bool tried_to_init = SDL_FALSE; + int i; - item = (SDL_AudioDeviceItem *)SDL_malloc(sizeof(SDL_AudioDeviceItem)); - if (!item) { - return SDL_OutOfMemory(); + if (SDL_GetCurrentAudioDriver()) { + SDL_QuitAudio(); /* shutdown driver if already running. */ } - item->original_name = SDL_strdup(name); - if (!item->original_name) { - SDL_free(item); - return SDL_OutOfMemory(); - } + SDL_ChooseAudioConverters(); - item->dupenum = 0; - item->name = item->original_name; - if (spec != NULL) { - SDL_copyp(&item->spec, spec); - } else { - SDL_zero(item->spec); + device_list_lock = SDL_CreateRWLock(); /* create this early, so if it fails we don't have to tear down the whole audio subsystem. */ + if (!device_list_lock) { + return -1; } - item->handle = handle; - - SDL_LockMutex(current_audio.detectionLock); - for (i = *devices; i != NULL; i = i->next) { - if (SDL_strcmp(name, i->original_name) == 0) { - dupenum = i->dupenum + 1; - break; /* stop at the highest-numbered dupe. */ - } + /* Select the proper audio driver */ + if (driver_name == NULL) { + driver_name = SDL_GetHint(SDL_HINT_AUDIO_DRIVER); } - if (dupenum) { - const size_t len = SDL_strlen(name) + 16; - char *replacement = (char *)SDL_malloc(len); - if (!replacement) { - SDL_UnlockMutex(current_audio.detectionLock); - SDL_free(item->original_name); - SDL_free(item); + if (driver_name != NULL && *driver_name != 0) { + char *driver_name_copy = SDL_strdup(driver_name); + const char *driver_attempt = driver_name_copy; + + if (driver_name_copy == NULL) { + SDL_DestroyRWLock(device_list_lock); return SDL_OutOfMemory(); } - (void)SDL_snprintf(replacement, len, "%s (%d)", name, dupenum + 1); - item->dupenum = dupenum; - item->name = replacement; - } - - item->next = *devices; - *devices = item; - retval = (*devCount)++; /* !!! FIXME: this should be an atomic increment */ + while (driver_attempt != NULL && *driver_attempt != 0 && !initialized) { + char *driver_attempt_end = SDL_strchr(driver_attempt, ','); + if (driver_attempt_end != NULL) { + *driver_attempt_end = '\0'; + } - SDL_UnlockMutex(current_audio.detectionLock); + /* SDL 1.2 uses the name "dsound", so we'll support both. */ + if (SDL_strcmp(driver_attempt, "dsound") == 0) { + driver_attempt = "directsound"; + } else if (SDL_strcmp(driver_attempt, "pulse") == 0) { /* likewise, "pulse" was renamed to "pulseaudio" */ + driver_attempt = "pulseaudio"; + } - return retval; -} + for (i = 0; bootstrap[i]; ++i) { + if (SDL_strcasecmp(bootstrap[i]->name, driver_attempt) == 0) { + tried_to_init = SDL_TRUE; + SDL_zero(current_audio); + SDL_AtomicSet(¤t_audio.last_device_instance_id, 2); /* start past 1 because of SDL2's legacy interface. */ + current_audio.device_list_lock = device_list_lock; + if (bootstrap[i]->init(¤t_audio.impl)) { + current_audio.name = bootstrap[i]->name; + current_audio.desc = bootstrap[i]->desc; + initialized = SDL_TRUE; + } + break; + } + } -static SDL_INLINE int add_capture_device(const char *name, SDL_AudioSpec *spec, void *handle) -{ - SDL_assert(current_audio.impl.HasCaptureSupport); - return add_audio_device(name, spec, handle, ¤t_audio.inputDevices, ¤t_audio.inputDeviceCount); -} + driver_attempt = (driver_attempt_end != NULL) ? (driver_attempt_end + 1) : NULL; + } -static SDL_INLINE int add_output_device(const char *name, SDL_AudioSpec *spec, void *handle) -{ - return add_audio_device(name, spec, handle, ¤t_audio.outputDevices, ¤t_audio.outputDeviceCount); -} + SDL_free(driver_name_copy); + } else { + for (i = 0; (!initialized) && (bootstrap[i]); ++i) { + if (bootstrap[i]->demand_only) { + continue; + } -static void free_device_list(SDL_AudioDeviceItem **devices, int *devCount) -{ - SDL_AudioDeviceItem *item, *next; - for (item = *devices; item != NULL; item = next) { - next = item->next; - if (item->handle != NULL) { - current_audio.impl.FreeDeviceHandle(item->handle); - } - /* these two pointers are the same if not a duplicate devname */ - if (item->name != item->original_name) { - SDL_free(item->name); + tried_to_init = SDL_TRUE; + SDL_zero(current_audio); + SDL_AtomicSet(¤t_audio.last_device_instance_id, 2); /* start past 1 because of SDL2's legacy interface. */ + current_audio.device_list_lock = device_list_lock; + if (bootstrap[i]->init(¤t_audio.impl)) { + current_audio.name = bootstrap[i]->name; + current_audio.desc = bootstrap[i]->desc; + initialized = SDL_TRUE; + } } - SDL_free(item->original_name); - SDL_free(item); } - *devices = NULL; - *devCount = 0; -} -/* The audio backends call this when a new device is plugged in. */ -void SDL_AddAudioDevice(const SDL_bool iscapture, const char *name, SDL_AudioSpec *spec, void *handle) -{ - const int device_index = iscapture ? add_capture_device(name, spec, handle) : add_output_device(name, spec, handle); - if (device_index != -1) { - /* Post the event, if desired */ - if (SDL_EventEnabled(SDL_EVENT_AUDIO_DEVICE_ADDED)) { - SDL_Event event; - event.type = SDL_EVENT_AUDIO_DEVICE_ADDED; - event.common.timestamp = 0; - event.adevice.which = device_index; - event.adevice.iscapture = iscapture; - SDL_PushEvent(&event); + if (!initialized) { + /* specific drivers will set the error message if they fail... */ + if (!tried_to_init) { + if (driver_name) { + SDL_SetError("Audio target '%s' not available", driver_name); + } else { + SDL_SetError("No available audio device"); + } } - } -} -/* The audio backends call this when a currently-opened device is lost. */ -void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device) -{ - SDL_assert(get_audio_device(device->id) == device); - - if (!SDL_AtomicGet(&device->enabled)) { - return; /* don't report disconnects more than once. */ + SDL_zero(current_audio); + SDL_DestroyRWLock(device_list_lock); + return -1; /* No driver was available, so fail. */ } - if (SDL_AtomicGet(&device->shutdown)) { - return; /* don't report disconnect if we're trying to close device. */ - } + CompleteAudioEntryPoints(); - /* Ends the audio callback and mark the device as STOPPED, but the - app still needs to close the device to free resources. */ - current_audio.impl.LockDevice(device); - SDL_AtomicSet(&device->enabled, 0); - current_audio.impl.UnlockDevice(device); + /* Make sure we have a list of devices available at startup. */ + current_audio.impl.DetectDevices(); - /* Post the event, if desired */ - if (SDL_EventEnabled(SDL_EVENT_AUDIO_DEVICE_REMOVED)) { - SDL_Event event; - event.type = SDL_EVENT_AUDIO_DEVICE_REMOVED; - event.common.timestamp = 0; - event.adevice.which = device->id; - event.adevice.iscapture = device->iscapture ? 1 : 0; - SDL_PushEvent(&event); - } + return 0; } -static void mark_device_removed(void *handle, SDL_AudioDeviceItem *devices, SDL_bool *removedFlag) +void SDL_QuitAudio(void) { - SDL_AudioDeviceItem *item; - SDL_assert(handle != NULL); - for (item = devices; item != NULL; item = item->next) { - if (item->handle == handle) { - item->handle = NULL; - *removedFlag = SDL_TRUE; - return; - } - } -} + SDL_AudioDevice *devices = NULL; + SDL_AudioDevice *i; -/* The audio backends call this when a device is removed from the system. */ -void SDL_RemoveAudioDevice(const SDL_bool iscapture, void *handle) -{ - int device_index; - SDL_AudioDevice *device = NULL; - SDL_bool device_was_opened = SDL_FALSE; + if (!current_audio.name) { /* not initialized?! */ + return; + } - SDL_LockMutex(current_audio.detectionLock); - if (iscapture) { - mark_device_removed(handle, current_audio.inputDevices, ¤t_audio.captureDevicesRemoved); - } else { - mark_device_removed(handle, current_audio.outputDevices, ¤t_audio.outputDevicesRemoved); + /* merge device lists so we don't have to duplicate work below. */ + SDL_LockRWLockForWriting(current_audio.device_list_lock); + SDL_AtomicSet(¤t_audio.shutting_down, 1); + for (i = current_audio.output_devices; i != NULL; i = i->next) { + devices = i; } - for (device_index = 0; device_index < SDL_arraysize(open_devices); device_index++) { - device = open_devices[device_index]; - if (device != NULL && device->handle == handle) { - device_was_opened = SDL_TRUE; - SDL_OpenedAudioDeviceDisconnected(device); - break; + if (!devices) { + devices = current_audio.capture_devices; + } else { + SDL_assert(devices->next == NULL); + devices->next = current_audio.capture_devices; + devices = current_audio.output_devices; + } + current_audio.output_devices = NULL; + current_audio.capture_devices = NULL; + SDL_UnlockRWLock(current_audio.device_list_lock); + + /* mark all devices for shutdown so all threads can begin to terminate. */ + for (i = devices; i != NULL; i = i->next) { + SDL_AtomicSet(&i->shutdown, 1); + } + + /* now wait on any audio threads. */ + for (i = devices; i != NULL; i = i->next) { + if (i->thread) { + SDL_assert(!SDL_AtomicGet(&i->condemned)); /* these shouldn't have been in the device list still, and thread should have detached. */ + SDL_WaitThread(i->thread, NULL); + i->thread = NULL; } } - /* Devices that aren't opened, as of 2.24.0, will post an - SDL_EVENT_AUDIO_DEVICE_REMOVED event with the `which` field set to zero. - Apps can use this to decide if they need to refresh a list of - available devices instead of closing an opened one. - Note that opened devices will send the non-zero event in - SDL_OpenedAudioDeviceDisconnected(). */ - if (!device_was_opened) { - if (SDL_EventEnabled(SDL_EVENT_AUDIO_DEVICE_REMOVED)) { - SDL_Event event; - event.type = SDL_EVENT_AUDIO_DEVICE_REMOVED; - event.common.timestamp = 0; - event.adevice.which = 0; - event.adevice.iscapture = iscapture ? 1 : 0; - SDL_PushEvent(&event); - } + while (devices) { + i = devices->next; + DestroyAudioDevice(devices); + devices = i; } - SDL_UnlockMutex(current_audio.detectionLock); + /* Free the driver data */ + current_audio.impl.Deinitialize(); + + SDL_DestroyRWLock(current_audio.device_list_lock); - current_audio.impl.FreeDeviceHandle(handle); + SDL_zero(current_audio); } -/* buffer queueing support... */ - -static void SDLCALL SDL_BufferQueueDrainCallback(void *userdata, Uint8 *stream, int len) -{ - /* this function always holds the mixer lock before being called. */ - SDL_AudioDevice *device = (SDL_AudioDevice *)userdata; - size_t dequeued; - SDL_assert(device != NULL); /* this shouldn't ever happen, right?! */ - SDL_assert(!device->iscapture); /* this shouldn't ever happen, right?! */ - SDL_assert(len >= 0); /* this shouldn't ever happen, right?! */ - dequeued = SDL_ReadFromDataQueue(device->buffer_queue, stream, len); - stream += dequeued; - len -= (int)dequeued; - if (len > 0) { /* fill any remaining space in the stream with silence. */ - SDL_assert(SDL_GetDataQueueSize(device->buffer_queue) == 0); - SDL_memset(stream, device->callbackspec.silence, len); - } -} +/* Output device thread. This is split into chunks, so backends that need to control this directly can use the pieces they need without duplicating effort. */ -static void SDLCALL SDL_BufferQueueFillCallback(void *userdata, Uint8 *stream, int len) +void SDL_OutputAudioThreadSetup(SDL_AudioDevice *device) { - /* this function always holds the mixer lock before being called. */ - SDL_AudioDevice *device = (SDL_AudioDevice *)userdata; + SDL_assert(!device->iscapture); - SDL_assert(device != NULL); /* this shouldn't ever happen, right?! */ - SDL_assert(device->iscapture); /* this shouldn't ever happen, right?! */ - SDL_assert(len >= 0); /* this shouldn't ever happen, right?! */ + /* The audio mixing is always a high priority thread */ +#ifdef SDL_AUDIO_DRIVER_ANDROID + Android_JNI_AudioSetThreadPriority(SDL_FALSE, device->id); +#else + SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL); +#endif - /* note that if this needs to allocate more space and run out of memory, - we have no choice but to quietly drop the data and hope it works out - later, but you probably have bigger problems in this case anyhow. */ - SDL_WriteToDataQueue(device->buffer_queue, stream, len); + /* Perform any thread setup */ + current_audio.impl.ThreadInit(device); } -int SDL_QueueAudio(SDL_AudioDeviceID devid, const void *data, Uint32 len) +SDL_bool SDL_OutputAudioThreadIterate(SDL_AudioDevice *device) { - SDL_AudioDevice *device = get_audio_device(devid); - int rc = 0; + SDL_bool retval = SDL_TRUE; + SDL_AudioStream *stream; + int buffer_size = device->buffer_size; + Uint8 *mix_buffer; - if (!device) { - return -1; /* get_audio_device() will have set the error state */ - } else if (device->iscapture) { - return SDL_SetError("This is a capture device, queueing not allowed"); - } else if (device->callbackspec.callback != SDL_BufferQueueDrainCallback) { - return SDL_SetError("Audio device has a callback, queueing not allowed"); - } + SDL_assert(!device->iscapture); + + SDL_LockMutex(device->lock); - if (len > 0) { - current_audio.impl.LockDevice(device); - rc = SDL_WriteToDataQueue(device->buffer_queue, data, len); - current_audio.impl.UnlockDevice(device); + if (SDL_AtomicGet(&device->shutdown)) { + SDL_UnlockMutex(device->lock); + return SDL_FALSE; /* we're done, shut it down. */ } - return rc; -} + mix_buffer = current_audio.impl.GetDeviceBuf(device, &buffer_size); + if (!mix_buffer) { + retval = SDL_FALSE; + } else { + SDL_assert(buffer_size <= device->buffer_size); /* you can ask for less, but not more. */ + SDL_memset(mix_buffer, device->silence_value, buffer_size); /* start with silence. */ + for (stream = device->bound_streams; stream != NULL; stream = stream->next_binding) { + /* this will hold a lock on `stream` while getting. We don't explicitly lock the streams for iterating here because the binding linked list can only change while the device lock is held. */ + /* (we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind the same stream to different devices at the same time, though.) */ + const int br = SDL_GetAudioStreamData(stream, device->work_buffer, buffer_size); + if (br < 0) { + /* oh crud, we probably ran out of memory. This is possibly an overreaction to kill the audio device, but it's likely the whole thing is going down in a moment anyhow. */ + retval = SDL_FALSE; + break; + } else if (br > 0) { /* it's okay if we get less than requested, we mix what we have. */ + if (SDL_MixAudioFormat(mix_buffer, device->work_buffer, device->format, br, SDL_MIX_MAXVOLUME) < 0) { /* !!! FIXME: allow streams to specify gain? */ + SDL_assert(!"We probably ended up with some totally unexpected audio format here"); + retval = SDL_FALSE; /* uh...? */ + break; + } + } + } + /* !!! FIXME: have PlayDevice return a value and do disconnects in here with it. */ + current_audio.impl.PlayDevice(device, buffer_size); /* this SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitDevice! */ + } -Uint32 SDL_DequeueAudio(SDL_AudioDeviceID devid, void *data, Uint32 len) -{ - SDL_AudioDevice *device = get_audio_device(devid); - Uint32 rc; + SDL_UnlockMutex(device->lock); - if ((len == 0) || /* nothing to do? */ - (!device) || /* called with bogus device id */ - (!device->iscapture) || /* playback devices can't dequeue */ - (device->callbackspec.callback != SDL_BufferQueueFillCallback)) { /* not set for queueing */ - return 0; /* just report zero bytes dequeued. */ + if (!retval) { + SDL_AudioDeviceDisconnected(device); /* doh. */ } - current_audio.impl.LockDevice(device); - rc = (Uint32)SDL_ReadFromDataQueue(device->buffer_queue, data, len); - current_audio.impl.UnlockDevice(device); - return rc; + return retval; } -Uint32 SDL_GetQueuedAudioSize(SDL_AudioDeviceID devid) +void SDL_OutputAudioThreadShutdown(SDL_AudioDevice *device) { - Uint32 retval = 0; - SDL_AudioDevice *device = get_audio_device(devid); - - if (!device) { - return 0; + const int samples = (device->buffer_size / (SDL_AUDIO_BITSIZE(device->format) / 8)) / device->channels; + SDL_assert(!device->iscapture); + /* Wait for the audio to drain. */ /* !!! FIXME: don't bother waiting if device is lost. */ + SDL_Delay(((samples * 1000) / device->freq) * 2); + current_audio.impl.ThreadDeinit(device); + if (SDL_AtomicGet(&device->condemned)) { + SDL_DetachThread(device->thread); /* no one is waiting for us, just detach ourselves. */ + device->thread = NULL; + DestroyAudioDevice(device); } +} - /* Nothing to do unless we're set up for queueing. */ - if (device->callbackspec.callback == SDL_BufferQueueDrainCallback || - device->callbackspec.callback == SDL_BufferQueueFillCallback) { - current_audio.impl.LockDevice(device); - retval = (Uint32)SDL_GetDataQueueSize(device->buffer_queue); - current_audio.impl.UnlockDevice(device); - } +/* thread entry point */ +static int SDLCALL OutputAudioThread(void *devicep) +{ + SDL_AudioDevice *device = (SDL_AudioDevice *)devicep; + SDL_assert(device != NULL); + SDL_assert(!device->iscapture); + SDL_OutputAudioThreadSetup(device); + do { + current_audio.impl.WaitDevice(device); + } while (SDL_OutputAudioThreadIterate(device)); - return retval; + SDL_OutputAudioThreadShutdown(device); + return 0; } -int SDL_ClearQueuedAudio(SDL_AudioDeviceID devid) -{ - SDL_AudioDevice *device = get_audio_device(devid); - if (!device) { - return SDL_InvalidParamError("devid"); - } - /* Blank out the device and release the mutex. Free it afterwards. */ - current_audio.impl.LockDevice(device); - /* Keep up to two packets in the pool to reduce future memory allocation pressure. */ - SDL_ClearDataQueue(device->buffer_queue, SDL_AUDIOBUFFERQUEUE_PACKETLEN * 2); +/* Capture device thread. This is split into chunks, so backends that need to control this directly can use the pieces they need without duplicating effort. */ - current_audio.impl.UnlockDevice(device); - return 0; -} - -#ifdef SDL_AUDIO_DRIVER_ANDROID -extern void Android_JNI_AudioSetThreadPriority(int, int); -#endif - -/* The general mixing thread function */ -static int SDLCALL SDL_RunAudio(void *devicep) +void SDL_CaptureAudioThreadSetup(SDL_AudioDevice *device) { - SDL_AudioDevice *device = (SDL_AudioDevice *)devicep; - void *udata = device->callbackspec.userdata; - SDL_AudioCallback callback = device->callbackspec.callback; - int data_len = 0; - Uint8 *data; - - SDL_assert(!device->iscapture); + SDL_assert(device->iscapture); + /* The audio mixing is always a high priority thread */ #ifdef SDL_AUDIO_DRIVER_ANDROID - { - /* Set thread priority to THREAD_PRIORITY_AUDIO */ - Android_JNI_AudioSetThreadPriority(device->iscapture, device->id); - } + Android_JNI_AudioSetThreadPriority(SDL_TRUE, device->id); #else - /* The audio mixing is always a high priority thread */ - SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL); + SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); #endif /* Perform any thread setup */ - device->threadid = SDL_ThreadID(); current_audio.impl.ThreadInit(device); - - /* Loop, filling the audio buffers */ - while (!SDL_AtomicGet(&device->shutdown)) { - data_len = device->callbackspec.size; - - /* Fill the current buffer with sound */ - if (!device->stream && SDL_AtomicGet(&device->enabled)) { - SDL_assert((Uint32)data_len == device->spec.size); - data = current_audio.impl.GetDeviceBuf(device); - } else { - /* if the device isn't enabled, we still write to the - work_buffer, so the app's callback will fire with - a regular frequency, in case they depend on that - for timing or progress. They can use hotplug - now to know if the device failed. - Streaming playback uses work_buffer, too. */ - data = NULL; - } - - if (data == NULL) { - data = device->work_buffer; - } - - /* !!! FIXME: this should be LockDevice. */ - SDL_LockMutex(device->mixer_lock); - if (SDL_AtomicGet(&device->paused)) { - SDL_memset(data, device->callbackspec.silence, data_len); - } else { - callback(udata, data, data_len); - } - SDL_UnlockMutex(device->mixer_lock); - - if (device->stream) { - /* Stream available audio to device, converting/resampling. */ - /* if this fails...oh well. We'll play silence here. */ - SDL_PutAudioStreamData(device->stream, data, data_len); - - while (SDL_GetAudioStreamAvailable(device->stream) >= ((int)device->spec.size)) { - int got; - data = SDL_AtomicGet(&device->enabled) ? current_audio.impl.GetDeviceBuf(device) : NULL; - got = SDL_GetAudioStreamData(device->stream, data ? data : device->work_buffer, device->spec.size); - SDL_assert((got <= 0) || ((Uint32)got == device->spec.size)); - - if (data == NULL) { /* device is having issues... */ - const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq); - SDL_Delay(delay); /* wait for as long as this buffer would have played. Maybe device recovers later? */ - } else { - if ((Uint32)got != device->spec.size) { - SDL_memset(data, device->spec.silence, device->spec.size); - } - current_audio.impl.PlayDevice(device); - current_audio.impl.WaitDevice(device); - } - } - } else if (data == device->work_buffer) { - /* nothing to do; pause like we queued a buffer to play. */ - const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq); - SDL_Delay(delay); - } else { /* writing directly to the device. */ - /* queue this buffer and wait for it to finish playing. */ - current_audio.impl.PlayDevice(device); - current_audio.impl.WaitDevice(device); - } - } - - /* Wait for the audio to drain. */ - SDL_Delay(((device->spec.samples * 1000) / device->spec.freq) * 2); - - current_audio.impl.ThreadDeinit(device); - - return 0; } -/* !!! FIXME: this needs to deal with device spec changes. */ -/* The general capture thread function */ -static int SDLCALL SDL_CaptureAudio(void *devicep) +SDL_bool SDL_CaptureAudioThreadIterate(SDL_AudioDevice *device) { - SDL_AudioDevice *device = (SDL_AudioDevice *)devicep; - const int silence = (int)device->spec.silence; - const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq); - const int data_len = device->spec.size; - Uint8 *data; - void *udata = device->callbackspec.userdata; - SDL_AudioCallback callback = device->callbackspec.callback; + SDL_bool retval = SDL_TRUE; SDL_assert(device->iscapture); -#ifdef SDL_AUDIO_DRIVER_ANDROID - { - /* Set thread priority to THREAD_PRIORITY_AUDIO */ - Android_JNI_AudioSetThreadPriority(device->iscapture, device->id); - } -#else - /* The audio mixing is always a high priority thread */ - SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); -#endif - - /* Perform any thread setup */ - device->threadid = SDL_ThreadID(); - current_audio.impl.ThreadInit(device); - - /* Loop, filling the audio buffers */ - while (!SDL_AtomicGet(&device->shutdown)) { - int still_need; - Uint8 *ptr; - - if (SDL_AtomicGet(&device->paused)) { - SDL_Delay(delay); /* just so we don't cook the CPU. */ - if (device->stream) { - SDL_ClearAudioStream(device->stream); - } - current_audio.impl.FlushCapture(device); /* dump anything pending. */ - continue; - } - - /* Fill the current buffer with sound */ - still_need = data_len; + SDL_LockMutex(device->lock); - /* Use the work_buffer to hold data read from the device. */ - data = device->work_buffer; - SDL_assert(data != NULL); - - ptr = data; - - /* We still read from the device when "paused" to keep the state sane, - and block when there isn't data so this thread isn't eating CPU. - But we don't process it further or call the app's callback. */ - - if (!SDL_AtomicGet(&device->enabled)) { - SDL_Delay(delay); /* try to keep callback firing at normal pace. */ - } else { - while (still_need > 0) { - const int rc = current_audio.impl.CaptureFromDevice(device, ptr, still_need); - SDL_assert(rc <= still_need); /* device should not overflow buffer. :) */ - if (rc > 0) { - still_need -= rc; - ptr += rc; - } else { /* uhoh, device failed for some reason! */ - SDL_OpenedAudioDeviceDisconnected(device); + if (SDL_AtomicGet(&device->shutdown)) { + retval = SDL_FALSE; /* we're done, shut it down. */ + } else if (device->bound_streams == NULL) { + current_audio.impl.FlushCapture(device); /* nothing wants data, dump anything pending. */ + } else { + const int rc = current_audio.impl.CaptureFromDevice(device, device->work_buffer, device->buffer_size); + if (rc < 0) { /* uhoh, device failed for some reason! */ + retval = SDL_FALSE; + } else if (rc > 0) { /* queue the new data to each bound stream. */ + SDL_AudioStream *stream; + for (stream = device->bound_streams; stream != NULL; stream = stream->next_binding) { + /* this will hold a lock on `stream` while putting. We don't explicitly lock the streams for iterating here because the binding linked list can only change while the device lock is held. */ + /* (we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind the same stream to different devices at the same time, though.) */ + if (SDL_PutAudioStreamData(stream, device->work_buffer, rc) < 0) { + /* oh crud, we probably ran out of memory. This is possibly an overreaction to kill the audio device, but it's likely the whole thing is going down in a moment anyhow. */ + retval = SDL_FALSE; break; } } } - - if (still_need > 0) { - /* Keep any data we already read, silence the rest. */ - SDL_memset(ptr, silence, still_need); - } - - if (device->stream) { - /* if this fails...oh well. */ - SDL_PutAudioStreamData(device->stream, data, data_len); - - while (SDL_GetAudioStreamAvailable(device->stream) >= ((int)device->callbackspec.size)) { - const int got = SDL_GetAudioStreamData(device->stream, device->work_buffer, device->callbackspec.size); - SDL_assert((got < 0) || ((Uint32)got == device->callbackspec.size)); - if ((Uint32)got != device->callbackspec.size) { - SDL_memset(device->work_buffer, device->spec.silence, device->callbackspec.size); - } - - /* !!! FIXME: this should be LockDevice. */ - SDL_LockMutex(device->mixer_lock); - if (!SDL_AtomicGet(&device->paused)) { - callback(udata, device->work_buffer, device->callbackspec.size); - } - SDL_UnlockMutex(device->mixer_lock); - } - } else { /* feeding user callback directly without streaming. */ - /* !!! FIXME: this should be LockDevice. */ - SDL_LockMutex(device->mixer_lock); - if (!SDL_AtomicGet(&device->paused)) { - callback(udata, data, device->callbackspec.size); - } - SDL_UnlockMutex(device->mixer_lock); - } } - current_audio.impl.FlushCapture(device); + SDL_UnlockMutex(device->lock); - current_audio.impl.ThreadDeinit(device); + if (!retval) { + SDL_AudioDeviceDisconnected(device); /* doh. */ + } - return 0; + return retval; } -static SDL_AudioFormat SDL_ParseAudioFormat(const char *string) +void SDL_CaptureAudioThreadShutdown(SDL_AudioDevice *device) { -#define CHECK_FMT_STRING(x) \ - if (SDL_strcmp(string, #x) == 0) \ - return SDL_AUDIO_##x - CHECK_FMT_STRING(U8); - CHECK_FMT_STRING(S8); - CHECK_FMT_STRING(S16LSB); - CHECK_FMT_STRING(S16MSB); - CHECK_FMT_STRING(S16); - CHECK_FMT_STRING(S32LSB); - CHECK_FMT_STRING(S32MSB); - CHECK_FMT_STRING(S32SYS); - CHECK_FMT_STRING(S32); - CHECK_FMT_STRING(F32LSB); - CHECK_FMT_STRING(F32MSB); - CHECK_FMT_STRING(F32SYS); - CHECK_FMT_STRING(F32); -#undef CHECK_FMT_STRING - return 0; + SDL_assert(device->iscapture); + current_audio.impl.FlushCapture(device); + current_audio.impl.ThreadDeinit(device); + if (SDL_AtomicGet(&device->condemned)) { + DestroyAudioDevice(device); + } } -int SDL_GetNumAudioDrivers(void) +/* thread entry point */ +static int SDLCALL CaptureAudioThread(void *devicep) { - return SDL_arraysize(bootstrap) - 1; + SDL_AudioDevice *device = (SDL_AudioDevice *)devicep; + SDL_assert(device != NULL); + SDL_assert(device->iscapture); + SDL_CaptureAudioThreadSetup(device); + while (SDL_CaptureAudioThreadIterate(device)) { /* spin, CaptureAudioThreadIterate will block if necessary. !!! FIXME: maybe this is bad. */ } + SDL_CaptureAudioThreadShutdown(device); + return 0; } -const char *SDL_GetAudioDriver(int index) -{ - if (index >= 0 && index < SDL_GetNumAudioDrivers()) { - return bootstrap[index]->name; - } - return NULL; -} -int SDL_InitAudio(const char *driver_name) +static SDL_AudioDeviceID *GetAudioDevices(int *reqcount, SDL_AudioDevice **devices, SDL_AtomicInt *device_count) { - int i; - SDL_bool initialized = SDL_FALSE, tried_to_init = SDL_FALSE; - - if (SDL_GetCurrentAudioDriver()) { - SDL_QuitAudio(); /* shutdown driver if already running. */ - } + SDL_AudioDeviceID *retval = NULL; + int num_devices = 0; - SDL_zeroa(open_devices); - - /* Select the proper audio driver */ - if (driver_name == NULL) { - driver_name = SDL_GetHint(SDL_HINT_AUDIO_DRIVER); + if (!SDL_GetCurrentAudioDriver()) { + SDL_SetError("Audio subsystem is not initialized"); + return NULL; } - if (driver_name != NULL && *driver_name != 0) { - const char *driver_attempt = driver_name; - while (driver_attempt != NULL && *driver_attempt != 0 && !initialized) { - const char *driver_attempt_end = SDL_strchr(driver_attempt, ','); - size_t driver_attempt_len = (driver_attempt_end != NULL) ? (driver_attempt_end - driver_attempt) - : SDL_strlen(driver_attempt); -#ifdef SDL_AUDIO_DRIVER_DSOUND - /* SDL 1.2 uses the name "dsound", so we'll support both. */ - if (driver_attempt_len == SDL_strlen("dsound") && - (SDL_strncasecmp(driver_attempt, "dsound", driver_attempt_len) == 0)) { - driver_attempt = "directsound"; - driver_attempt_len = SDL_strlen("directsound"); - } -#endif - -#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO - /* SDL 1.2 uses the name "pulse", so we'll support both. */ - if (driver_attempt_len == SDL_strlen("pulse") && - (SDL_strncasecmp(driver_attempt, "pulse", driver_attempt_len) == 0)) { - driver_attempt = "pulseaudio"; - driver_attempt_len = SDL_strlen("pulseaudio"); - } -#endif - - for (i = 0; bootstrap[i]; ++i) { - if ((driver_attempt_len == SDL_strlen(bootstrap[i]->name)) && - (SDL_strncasecmp(bootstrap[i]->name, driver_attempt, driver_attempt_len) == 0)) { - tried_to_init = SDL_TRUE; - SDL_zero(current_audio); - current_audio.name = bootstrap[i]->name; - current_audio.desc = bootstrap[i]->desc; - initialized = bootstrap[i]->init(¤t_audio.impl); - break; - } - } - - driver_attempt = (driver_attempt_end != NULL) ? (driver_attempt_end + 1) : NULL; - } + SDL_LockRWLockForReading(current_audio.device_list_lock); + num_devices = SDL_AtomicGet(device_count); + retval = (SDL_AudioDeviceID *) SDL_malloc((num_devices + 1) * sizeof (SDL_AudioDeviceID)); + if (retval == NULL) { + num_devices = 0; + SDL_OutOfMemory(); } else { - for (i = 0; (!initialized) && (bootstrap[i]); ++i) { - if (bootstrap[i]->demand_only) { - continue; - } - - tried_to_init = SDL_TRUE; - SDL_zero(current_audio); - current_audio.name = bootstrap[i]->name; - current_audio.desc = bootstrap[i]->desc; - initialized = bootstrap[i]->init(¤t_audio.impl); + const SDL_AudioDevice *dev = *devices; + int i; + for (i = 0; i < num_devices; i++) { + SDL_assert(dev != NULL); + SDL_assert(!SDL_AtomicGet((SDL_AtomicInt *) &dev->condemned)); /* shouldn't be in the list if pending deletion. */ + retval[i] = dev->instance_id; + dev = dev->next; } + SDL_assert(dev == NULL); /* did the whole list? */ + retval[num_devices] = 0; /* null-terminated. */ } + SDL_UnlockRWLock(current_audio.device_list_lock); - if (!initialized) { - /* specific drivers will set the error message if they fail... */ - if (!tried_to_init) { - if (driver_name) { - SDL_SetError("Audio target '%s' not available", driver_name); - } else { - SDL_SetError("No available audio device"); - } - } - - SDL_zero(current_audio); - return -1; /* No driver was available, so fail. */ + if (reqcount != NULL) { + *reqcount = num_devices; } - current_audio.detectionLock = SDL_CreateMutex(); - - finish_audio_entry_points_init(); - - /* Make sure we have a list of devices available at startup. */ - current_audio.impl.DetectDevices(); - - return 0; + return retval; } -/* - * Get the current audio driver name - */ -const char *SDL_GetCurrentAudioDriver(void) +SDL_AudioDeviceID *SDL_GetAudioOutputDevices(int *count) { - return current_audio.name; + return GetAudioDevices(count, ¤t_audio.output_devices, ¤t_audio.output_device_count); } -/* Clean out devices that we've removed but had to keep around for stability. */ -static void clean_out_device_list(SDL_AudioDeviceItem **devices, int *devCount, SDL_bool *removedFlag) +SDL_AudioDeviceID *SDL_GetAudioCaptureDevices(int *count) { - SDL_AudioDeviceItem *item = *devices; - SDL_AudioDeviceItem *prev = NULL; - int total = 0; - - while (item) { - SDL_AudioDeviceItem *next = item->next; - if (item->handle != NULL) { - total++; - prev = item; - } else { - if (prev) { - prev->next = next; - } else { - *devices = next; - } - /* these two pointers are the same if not a duplicate devname */ - if (item->name != item->original_name) { - SDL_free(item->name); - } - SDL_free(item->original_name); - SDL_free(item); - } - item = next; - } - - *devCount = total; - *removedFlag = SDL_FALSE; + return GetAudioDevices(count, ¤t_audio.capture_devices, ¤t_audio.capture_device_count); } -int SDL_GetNumAudioDevices(int iscapture) + +/* this finds the device object associated with `devid` and locks it for use. */ +static SDL_AudioDevice *ObtainAudioDevice(SDL_AudioDeviceID devid) { - int retval = 0; + /* capture instance ids are even and output devices are odd, so we know which list to iterate from the devid. */ + const SDL_bool iscapture = (devid & 1) ? SDL_FALSE : SDL_TRUE; + SDL_AudioDevice *dev = NULL; if (!SDL_GetCurrentAudioDriver()) { - return -1; + SDL_SetError("Audio subsystem is not initialized"); + return NULL; } - SDL_LockMutex(current_audio.detectionLock); - if (iscapture && current_audio.captureDevicesRemoved) { - clean_out_device_list(¤t_audio.inputDevices, ¤t_audio.inputDeviceCount, ¤t_audio.captureDevicesRemoved); - } + SDL_LockRWLockForReading(current_audio.device_list_lock); - if (!iscapture && current_audio.outputDevicesRemoved) { - clean_out_device_list(¤t_audio.outputDevices, ¤t_audio.outputDeviceCount, ¤t_audio.outputDevicesRemoved); + for (dev = iscapture ? current_audio.capture_devices : current_audio.output_devices; dev != NULL; dev = dev->next) { + if (dev->instance_id == devid) { /* found it? */ + SDL_LockMutex(dev->lock); /* caller must unlock. */ + SDL_assert(!SDL_AtomicGet(&dev->condemned)); /* shouldn't be in the list if pending deletion. */ + break; + } } - retval = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount; - SDL_UnlockMutex(current_audio.detectionLock); + SDL_UnlockRWLock(current_audio.device_list_lock); - return retval; + if (!dev) { + SDL_SetError("Invalid audio device instance ID"); + } + + return dev; } -const char *SDL_GetAudioDeviceName(int index, int iscapture) +SDL_AudioDevice *SDL_ObtainAudioDeviceByHandle(void *handle) { - SDL_AudioDeviceItem *item; - int i; - const char *retval; + SDL_AudioDevice *dev = NULL; if (!SDL_GetCurrentAudioDriver()) { SDL_SetError("Audio subsystem is not initialized"); return NULL; } - SDL_LockMutex(current_audio.detectionLock); - item = iscapture ? current_audio.inputDevices : current_audio.outputDevices; - i = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount; - if (index >= 0 && index < i) { - for (i--; i > index; i--, item = item->next) { - SDL_assert(item != NULL); + SDL_LockRWLockForReading(current_audio.device_list_lock); + + for (dev = current_audio.output_devices; dev != NULL; dev = dev->next) { + if (dev->handle == handle) { /* found it? */ + SDL_LockMutex(dev->lock); /* caller must unlock. */ + SDL_assert(!SDL_AtomicGet(&dev->condemned)); /* shouldn't be in the list if pending deletion. */ + break; + } + } + if (!dev) { + for (dev = current_audio.capture_devices; dev != NULL; dev = dev->next) { + if (dev->handle == handle) { /* found it? */ + SDL_LockMutex(dev->lock); /* caller must unlock. */ + SDL_assert(!SDL_AtomicGet(&dev->condemned)); /* shouldn't be in the list if pending deletion. */ + break; + } } - SDL_assert(item != NULL); - retval = item->name; - } else { - SDL_InvalidParamError("index"); - retval = NULL; } - SDL_UnlockMutex(current_audio.detectionLock); - return retval; + SDL_UnlockRWLock(current_audio.device_list_lock); + + if (!dev) { + SDL_SetError("Device handle not found"); + } + + return dev; } -int SDL_GetAudioDeviceSpec(int index, int iscapture, SDL_AudioSpec *spec) +char *SDL_GetAudioDeviceName(SDL_AudioDeviceID devid) { - SDL_AudioDeviceItem *item; - int i, retval; - - if (spec == NULL) { - return SDL_InvalidParamError("spec"); + char *retval; + SDL_AudioDevice *device = ObtainAudioDevice(devid); + if (!device) { + return NULL; } - if (!SDL_GetCurrentAudioDriver()) { - return SDL_SetError("Audio subsystem is not initialized"); + retval = SDL_strdup(device->name); + if (!retval) { + SDL_OutOfMemory(); } - SDL_LockMutex(current_audio.detectionLock); - item = iscapture ? current_audio.inputDevices : current_audio.outputDevices; - i = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount; - if (index >= 0 && index < i) { - for (i--; i > index; i--, item = item->next) { - SDL_assert(item != NULL); - } - SDL_assert(item != NULL); - SDL_copyp(spec, &item->spec); - retval = 0; - } else { - retval = SDL_InvalidParamError("index"); - } - SDL_UnlockMutex(current_audio.detectionLock); + SDL_UnlockMutex(device->lock); return retval; } -int SDL_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture) +int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioFormat *fmt, int *channels, int *freq) { - if (spec == NULL) { - return SDL_InvalidParamError("spec"); + SDL_AudioDevice *device = ObtainAudioDevice(devid); + if (!device) { + return -1; } - if (!SDL_GetCurrentAudioDriver()) { - return SDL_SetError("Audio subsystem is not initialized"); + if (fmt) { + *fmt = device->format; } - - if (current_audio.impl.GetDefaultAudioInfo == NULL) { - return SDL_Unsupported(); + if (channels) { + *channels = device->channels; } - return current_audio.impl.GetDefaultAudioInfo(name, spec, iscapture); -} - -static void close_audio_device(SDL_AudioDevice *device) -{ - if (!device) { - return; + if (freq) { + *freq = device->freq; } - /* make sure the device is paused before we do anything else, so the - audio callback definitely won't fire again. */ - current_audio.impl.LockDevice(device); - SDL_AtomicSet(&device->paused, 1); - SDL_AtomicSet(&device->shutdown, 1); - SDL_AtomicSet(&device->enabled, 0); - current_audio.impl.UnlockDevice(device); + SDL_UnlockMutex(device->lock); + + return 0; +} +/* this expects the device lock to be held. */ +static void CloseAudioDevice(SDL_AudioDevice *device) +{ if (device->thread != NULL) { + SDL_AtomicSet(&device->shutdown, 1); SDL_WaitThread(device->thread, NULL); - } - if (device->mixer_lock != NULL) { - SDL_DestroyMutex(device->mixer_lock); + device->thread = NULL; + SDL_AtomicSet(&device->shutdown, 0); } - SDL_free(device->work_buffer); - SDL_DestroyAudioStream(device->stream); + current_audio.impl.CloseDevice(device); - if (device->id > 0) { - SDL_AudioDevice *opendev = open_devices[device->id - 1]; - SDL_assert((opendev == device) || (opendev == NULL)); - if (opendev == device) { - open_devices[device->id - 1] = NULL; - } + if (device->work_buffer) { + SDL_aligned_free(device->work_buffer); + device->work_buffer = NULL; } - if (device->hidden != NULL) { - current_audio.impl.CloseDevice(device); - } - - SDL_DestroyDataQueue(device->buffer_queue); + device->format = device->default_format; + device->channels = device->default_channels; + device->freq = device->default_freq; + device->sample_frames = 0; + device->silence_value = SDL_GetSilenceValueForFormat(device->format); +} - SDL_free(device); +void SDL_CloseAudioDevice(SDL_AudioDeviceID devid) +{ + SDL_AudioDevice *device = ObtainAudioDevice(devid); + if (device) { /* if NULL, maybe it was already lost? */ + if (SDL_AtomicDecRef(&device->refcount)) { + SDL_UnlockMutex(device->lock); /* can't hold the lock or the audio thread will deadlock while we WaitThread it. */ + CloseAudioDevice(device); + } else { + if (SDL_AtomicGet(&device->refcount) < 0) { + SDL_AtomicSet(&device->refcount, 0); /* something closed more than it should, force this back to zero. Best we can do. */ + } + SDL_UnlockMutex(device->lock); /* can't hold the lock or the audio thread will deadlock while we WaitThread it. */ + } + } } -static Uint16 GetDefaultSamplesFromFreq(int freq) + +static SDL_AudioFormat ParseAudioFormatString(const char *string) { - /* Pick a default of ~46 ms at desired frequency */ - /* !!! FIXME: remove this when the non-Po2 resampling is in. */ - const Uint16 max_sample = (Uint16)((freq / 1000) * 46); - Uint16 current_sample = 1; - while (current_sample < max_sample) { - current_sample *= 2; + if (string) { +#define CHECK_FMT_STRING(x) \ + if (SDL_strcmp(string, #x) == 0) \ + return SDL_AUDIO_##x + CHECK_FMT_STRING(U8); + CHECK_FMT_STRING(S8); + CHECK_FMT_STRING(S16LSB); + CHECK_FMT_STRING(S16MSB); + CHECK_FMT_STRING(S16); + CHECK_FMT_STRING(S32LSB); + CHECK_FMT_STRING(S32MSB); + CHECK_FMT_STRING(S32SYS); + CHECK_FMT_STRING(S32); + CHECK_FMT_STRING(F32LSB); + CHECK_FMT_STRING(F32MSB); + CHECK_FMT_STRING(F32SYS); + CHECK_FMT_STRING(F32); +#undef CHECK_FMT_STRING } - return current_sample; + return 0; } -/* - * Sanity check desired AudioSpec for SDL_OpenAudio() in (orig). - * Fills in a sanitized copy in (prepared). - * Returns non-zero if okay, zero on fatal parameters in (orig). - */ -static int prepare_audiospec(const SDL_AudioSpec *orig, SDL_AudioSpec *prepared) +static void PrepareAudioFormat(SDL_AudioFormat *fmt, int *channels, int *freq) { - SDL_copyp(prepared, orig); + const char *env; - if (orig->freq == 0) { - static const int DEFAULT_FREQ = 22050; - const char *env = SDL_getenv("SDL_AUDIO_FREQUENCY"); + if (*freq == 0) { + *freq = DEFAULT_AUDIO_FREQUENCY; + env = SDL_getenv("SDL_AUDIO_FREQUENCY"); if (env != NULL) { - int freq = SDL_atoi(env); - prepared->freq = freq != 0 ? freq : DEFAULT_FREQ; - } else { - prepared->freq = DEFAULT_FREQ; + const int val = SDL_atoi(env); + if (val > 0) { + *freq = val; + } } } - if (orig->format == 0) { - const char *env = SDL_getenv("SDL_AUDIO_FORMAT"); + if (*channels == 0) { + *channels = DEFAULT_AUDIO_CHANNELS; + env = SDL_getenv("SDL_AUDIO_CHANNELS"); if (env != NULL) { - const SDL_AudioFormat format = SDL_ParseAudioFormat(env); - prepared->format = format != 0 ? format : SDL_AUDIO_S16; - } else { - prepared->format = SDL_AUDIO_S16; + const int val = SDL_atoi(env); + if (val > 0) { + *channels = val; + } } } - if (orig->channels == 0) { - const char *env = SDL_getenv("SDL_AUDIO_CHANNELS"); - if (env != NULL) { - Uint8 channels = (Uint8)SDL_atoi(env); - prepared->channels = channels != 0 ? channels : 2; - } else { - prepared->channels = 2; - } - } else if (orig->channels > 8) { - SDL_SetError("Unsupported number of audio channels."); - return 0; + if (*fmt == 0) { + const SDL_AudioFormat val = ParseAudioFormatString(SDL_getenv("SDL_AUDIO_FORMAT")); + *fmt = (val != 0) ? val : DEFAULT_AUDIO_FORMAT; } +} - if (orig->samples == 0) { - const char *env = SDL_getenv("SDL_AUDIO_SAMPLES"); - if (env != NULL) { - Uint16 samples = (Uint16)SDL_atoi(env); - prepared->samples = samples != 0 ? samples : GetDefaultSamplesFromFreq(prepared->freq); - } else { - prepared->samples = GetDefaultSamplesFromFreq(prepared->freq); - } +static int GetDefaultSampleFramesFromFreq(int freq) +{ + /* Pick the closest power-of-two to ~46 ms at desired frequency */ + const int max_sample = ((freq / 1000) * 46); + int current_sample = 1; + while (current_sample < max_sample) { + current_sample *= 2; } - - /* Calculate the silence and size of the audio specification */ - SDL_CalculateAudioSpec(prepared); - - return 1; + return current_sample; } -static SDL_AudioDeviceID open_audio_device(const char *devname, int iscapture, - const SDL_AudioSpec *desired, SDL_AudioSpec *obtained, - int allowed_changes, int min_id) +void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device) { - const SDL_bool is_internal_thread = (desired->callback == NULL); - SDL_AudioDeviceID id = 0; - SDL_AudioSpec _obtained; - SDL_AudioDevice *device; - SDL_bool build_stream; - void *handle = NULL; - int i = 0; + device->silence_value = SDL_GetSilenceValueForFormat(device->format); + device->buffer_size = device->sample_frames * (SDL_AUDIO_BITSIZE(device->format) / 8) * device->channels; +} - if (!SDL_GetCurrentAudioDriver()) { - SDL_SetError("Audio subsystem is not initialized"); - return 0; +/* this expects the device lock to be held. */ +static int OpenAudioDevice(SDL_AudioDevice *device, SDL_AudioFormat fmt, int channels, int freq) +{ + SDL_assert(SDL_AtomicGet(&device->refcount) == 1); + + PrepareAudioFormat(&fmt, &channels, &freq); + + /* we allow the device format to change if it's better than the current settings (by various definitions of "better"). 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! */ + device->format = (SDL_AUDIO_BITSIZE(device->default_format) >= SDL_AUDIO_BITSIZE(fmt)) ? device->default_format : fmt; + device->freq = SDL_max(device->default_freq, freq); + device->channels = SDL_max(device->default_channels, channels); + device->sample_frames = GetDefaultSampleFramesFromFreq(device->freq); + SDL_UpdatedAudioDeviceFormat(device); /* start this off sane. */ + + if (current_audio.impl.OpenDevice(device) < 0) { + CloseAudioDevice(device); /* clean up anything the backend left half-initialized. */ + return -1; } - if (iscapture && !current_audio.impl.HasCaptureSupport) { - SDL_SetError("No capture support"); - return 0; - } + SDL_UpdatedAudioDeviceFormat(device); /* in case the backend changed things and forgot to call this. */ - SDL_LockMutex(current_audio.detectionLock); - /* Find an available device ID... */ - for (id = min_id - 1; id < SDL_arraysize(open_devices); id++) { - if (open_devices[id] == NULL) { - break; - } + /* Allocate a scratch audio buffer */ + device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->buffer_size); + if (device->work_buffer == NULL) { + CloseAudioDevice(device); + return SDL_OutOfMemory(); } - if (id == SDL_arraysize(open_devices)) { - SDL_SetError("Too many open audio devices"); - SDL_UnlockMutex(current_audio.detectionLock); - return 0; - } + /* Start the audio thread if necessary */ + if (!current_audio.impl.ProvidesOwnCallbackThread) { + const size_t stacksize = 64 * 1024; /* We only need a little space, so make the stack tiny. We may adjust this as necessary later. */ + char threadname[64]; - if (!obtained) { - obtained = &_obtained; - } - if (!prepare_audiospec(desired, obtained)) { - SDL_UnlockMutex(current_audio.detectionLock); - return 0; - } + (void)SDL_snprintf(threadname, sizeof(threadname), "SDLAudio%c%d", (device->iscapture) ? 'C' : 'P', (int) device->instance_id); + device->thread = SDL_CreateThreadInternal(device->iscapture ? CaptureAudioThread : OutputAudioThread, threadname, stacksize, device); - /* If app doesn't care about a specific device, let the user override. */ - if (devname == NULL) { - devname = SDL_getenv("SDL_AUDIO_DEVICE_NAME"); + if (device->thread == NULL) { + CloseAudioDevice(device); + return SDL_SetError("Couldn't create audio thread"); + } } - /* - * Catch device names at the high level for the simple case... - * This lets us have a basic "device enumeration" for systems that - * don't have multiple devices, but makes sure the device name is - * always NULL when it hits the low level. - * - * Also make sure that the simple case prevents multiple simultaneous - * opens of the default system device. - */ - - if ((iscapture) && (current_audio.impl.OnlyHasDefaultCaptureDevice)) { - if ((devname) && (SDL_strcmp(devname, DEFAULT_INPUT_DEVNAME) != 0)) { - SDL_SetError("No such device"); - SDL_UnlockMutex(current_audio.detectionLock); - return 0; - } - devname = NULL; + return 0; +} - for (i = 0; i < SDL_arraysize(open_devices); i++) { - if ((open_devices[i]) && (open_devices[i]->iscapture)) { - SDL_SetError("Audio device already open"); - SDL_UnlockMutex(current_audio.detectionLock); - return 0; - } - } - } else if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) { - if ((devname) && (SDL_strcmp(devname, DEFAULT_OUTPUT_DEVNAME) != 0)) { - SDL_UnlockMutex(current_audio.detectionLock); - SDL_SetError("No such device"); - return 0; - } - devname = NULL; +SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, SDL_AudioFormat fmt, int channels, int freq) +{ + SDL_AudioDevice *device = ObtainAudioDevice(devid); /* !!! FIXME: need to choose default device for devid==0 */ + int retval = 0; - for (i = 0; i < SDL_arraysize(open_devices); i++) { - if ((open_devices[i]) && (!open_devices[i]->iscapture)) { - SDL_UnlockMutex(current_audio.detectionLock); - SDL_SetError("Audio device already open"); - return 0; - } - } - } else if (devname != NULL) { - /* if the app specifies an exact string, we can pass the backend - an actual device handle thingey, which saves them the effort of - figuring out what device this was (such as, reenumerating - everything again to find the matching human-readable name). - It might still need to open a device based on the string for, - say, a network audio server, but this optimizes some cases. */ - SDL_AudioDeviceItem *item; - for (item = iscapture ? current_audio.inputDevices : current_audio.outputDevices; item; item = item->next) { - if ((item->handle != NULL) && (SDL_strcmp(item->name, devname) == 0)) { - handle = item->handle; - break; + if (device) { + retval = device->instance_id; + if (SDL_AtomicIncRef(&device->refcount) == 0) { + if (OpenAudioDevice(device, fmt, channels, freq) == -1) { + retval = 0; } } + SDL_UnlockMutex(device->lock); } - if (!current_audio.impl.AllowsArbitraryDeviceNames) { - /* has to be in our device list, or the default device. */ - if ((handle == NULL) && (devname != NULL)) { - SDL_SetError("No such device."); - SDL_UnlockMutex(current_audio.detectionLock); - return 0; - } - } - - device = (SDL_AudioDevice *)SDL_calloc(1, sizeof(SDL_AudioDevice)); - if (device == NULL) { - SDL_OutOfMemory(); - SDL_UnlockMutex(current_audio.detectionLock); - return 0; - } - device->id = id + 1; - device->spec = *obtained; - device->iscapture = iscapture ? SDL_TRUE : SDL_FALSE; - device->handle = handle; + return retval; +} - SDL_AtomicSet(&device->shutdown, 0); /* just in case. */ - SDL_AtomicSet(&device->paused, 1); - SDL_AtomicSet(&device->enabled, 1); - - /* Create a mutex for locking the sound buffers */ - if (current_audio.impl.LockDevice == SDL_AudioLockDevice_Default) { - device->mixer_lock = SDL_CreateMutex(); - if (device->mixer_lock == NULL) { - close_audio_device(device); - SDL_UnlockMutex(current_audio.detectionLock); - SDL_SetError("Couldn't create mixer lock"); - return 0; - } - } +int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int num_streams) +{ + SDL_AudioDevice *device; + int retval = 0; + int i; - /* 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) { - int samples = SDL_powerof2(device->spec.samples); - if (samples <= SDL_MAX_UINT16) { - device->spec.samples = (Uint16)samples; + if (num_streams == 0) { + return 0; /* nothing to do */ + } else if (num_streams < 0) { + return SDL_InvalidParamError("num_streams"); + } else if (streams == NULL) { + return SDL_InvalidParamError("streams"); + } else if ((device = ObtainAudioDevice(devid)) == NULL) { + return -1; /* ObtainAudioDevice set the error message. */ + } else if (SDL_AtomicGet(&device->refcount) == 0) { + SDL_UnlockMutex(device->lock); + return SDL_SetError("Device is not opened"); + } + + SDL_assert(!device->bound_streams || (device->bound_streams->prev_binding == NULL)); + + /* lock all the streams upfront, so we can verify they aren't bound elsewhere and add them all in one block, as this is intended to add everything or nothing. */ + for (i = 0; i < num_streams; i++) { + SDL_AudioStream *stream = streams[i]; + if (stream == NULL) { + retval = SDL_SetError("Stream #%d is NULL", i); } else { - SDL_SetError("Couldn't hold sample count %d\n", samples); - return 0; + SDL_LockMutex(stream->lock); + SDL_assert((stream->bound_device == NULL) == ((stream->prev_binding == NULL) || (stream->next_binding == NULL))); + if (stream->bound_device) { + retval = SDL_SetError("Stream #%d is already bound to a device", i); + } } - } - - if (current_audio.impl.OpenDevice(device, devname) < 0) { - close_audio_device(device); - SDL_UnlockMutex(current_audio.detectionLock); - return 0; - } - - /* if your target really doesn't need it, set it to 0x1 or something. */ - /* otherwise, close_audio_device() won't call impl.CloseDevice(). */ - SDL_assert(device->hidden != NULL); - /* See if we need to do any conversion */ - build_stream = SDL_FALSE; - if (obtained->freq != device->spec.freq) { - if (allowed_changes & SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) { - obtained->freq = device->spec.freq; - } else { - build_stream = SDL_TRUE; - } - } - if (obtained->format != device->spec.format) { - if (allowed_changes & SDL_AUDIO_ALLOW_FORMAT_CHANGE) { - obtained->format = device->spec.format; - } else { - build_stream = SDL_TRUE; - } - } - if (obtained->channels != device->spec.channels) { - if (allowed_changes & SDL_AUDIO_ALLOW_CHANNELS_CHANGE) { - obtained->channels = device->spec.channels; - } else { - build_stream = SDL_TRUE; - } - } - if (device->spec.samples != obtained->samples) { - if (allowed_changes & SDL_AUDIO_ALLOW_SAMPLES_CHANGE) { - obtained->samples = device->spec.samples; - } else { - build_stream = SDL_TRUE; + if (retval != 0) { + int j; + for (j = 0; j <= i; j++) { + SDL_UnlockMutex(streams[j]->lock); + } + break; } } - SDL_CalculateAudioSpec(obtained); /* recalc after possible changes. */ - - device->callbackspec = *obtained; + if (retval == 0) { + /* Now that everything is verified, chain everything together. */ + for (i = 0; i < num_streams; i++) { + SDL_AudioStream *stream = streams[i]; + SDL_AudioFormat src_format, dst_format; + int src_channels, dst_channels; + int src_rate, dst_rate; - if (build_stream) { - if (iscapture) { - device->stream = SDL_CreateAudioStream(device->spec.format, - device->spec.channels, device->spec.freq, - obtained->format, obtained->channels, obtained->freq); - } else { - device->stream = SDL_CreateAudioStream(obtained->format, obtained->channels, - obtained->freq, device->spec.format, - device->spec.channels, device->spec.freq); - } + /* set the proper end of the stream to the device's format. */ + SDL_GetAudioStreamFormat(stream, &src_format, &src_channels, &src_rate, &dst_format, &dst_channels, &dst_rate); + if (device->iscapture) { + SDL_SetAudioStreamFormat(stream, device->format, device->channels, device->freq, dst_format, dst_channels, dst_rate); + } else { + SDL_SetAudioStreamFormat(stream, src_format, src_channels, src_rate, device->format, device->channels, device->freq); + } - if (!device->stream) { - close_audio_device(device); - SDL_UnlockMutex(current_audio.detectionLock); - return 0; - } - } + stream->bound_device = device; + stream->prev_binding = NULL; + stream->next_binding = device->bound_streams; + if (device->bound_streams) { + device->bound_streams->prev_binding = stream; + } + device->bound_streams = stream; - if (device->spec.callback == NULL) { /* use buffer queueing? */ - /* pool a few packets to start. Enough for two callbacks. */ - device->buffer_queue = SDL_CreateDataQueue(SDL_AUDIOBUFFERQUEUE_PACKETLEN, obtained->size * 2); - if (!device->buffer_queue) { - close_audio_device(device); - SDL_UnlockMutex(current_audio.detectionLock); - SDL_SetError("Couldn't create audio buffer queue"); - return 0; + SDL_UnlockMutex(stream->lock); } - device->callbackspec.callback = iscapture ? SDL_BufferQueueFillCallback : SDL_BufferQueueDrainCallback; - device->callbackspec.userdata = device; - } - - /* Allocate a scratch audio buffer */ - device->work_buffer_len = build_stream ? device->callbackspec.size : 0; - if (device->spec.size > device->work_buffer_len) { - device->work_buffer_len = device->spec.size; - } - SDL_assert(device->work_buffer_len > 0); - - device->work_buffer = (Uint8 *)SDL_malloc(device->work_buffer_len); - if (device->work_buffer == NULL) { - close_audio_device(device); - SDL_UnlockMutex(current_audio.detectionLock); - SDL_OutOfMemory(); - return 0; } - open_devices[id] = device; /* add it to our list of open devices. */ + SDL_UnlockMutex(device->lock); - /* Start the audio thread if necessary */ - if (!current_audio.impl.ProvidesOwnCallbackThread) { - /* Start the audio thread */ - /* !!! FIXME: we don't force the audio thread stack size here if it calls into user code, but maybe we should? */ - /* buffer queueing callback only needs a few bytes, so make the stack tiny. */ - const size_t stacksize = is_internal_thread ? 64 * 1024 : 0; - char threadname[64]; - - (void)SDL_snprintf(threadname, sizeof(threadname), "SDLAudio%c%" SDL_PRIu32, (iscapture) ? 'C' : 'P', device->id); - device->thread = SDL_CreateThreadInternal(iscapture ? SDL_CaptureAudio : SDL_RunAudio, threadname, stacksize, device); - - if (device->thread == NULL) { - close_audio_device(device); - SDL_SetError("Couldn't create audio thread"); - SDL_UnlockMutex(current_audio.detectionLock); - return 0; - } - } - SDL_UnlockMutex(current_audio.detectionLock); - - return device->id; + return retval; } -SDL_AudioDeviceID SDL_OpenAudioDevice(const char *device, int iscapture, - const SDL_AudioSpec *desired, SDL_AudioSpec *obtained, - int allowed_changes) +int SDL_BindAudioStream(SDL_AudioDeviceID devid, SDL_AudioStream *stream) { - return open_audio_device(device, iscapture, desired, obtained, - allowed_changes, 2); + return SDL_BindAudioStreams(devid, &stream, 1); } -SDL_AudioStatus SDL_GetAudioDeviceStatus(SDL_AudioDeviceID devid) +void SDL_UnbindAudioStreams(SDL_AudioStream **streams, int num_streams) { - SDL_AudioDevice *device = get_audio_device(devid); - SDL_AudioStatus status = SDL_AUDIO_STOPPED; - if (device && SDL_AtomicGet(&device->enabled)) { - if (SDL_AtomicGet(&device->paused)) { - status = SDL_AUDIO_PAUSED; - } else { - status = SDL_AUDIO_PLAYING; + int i; + + /* to prevent deadlock when holding both locks, we _must_ lock the device first, and the stream second, as that is the order the audio thread will do it. + But this means we have an unlikely, pathological case where a stream could change its binding between when we lookup its bound device and when we lock everything, + so we double-check here. */ + for (i = 0; i < num_streams; i++) { + SDL_AudioStream *stream = streams[i]; + if (!stream) { + continue; /* nothing to do, it's a NULL stream. */ } - } - return status; -} -int SDL_PauseAudioDevice(SDL_AudioDeviceID devid) -{ - SDL_AudioDevice *device = get_audio_device(devid); - if (!device) { - return SDL_InvalidParamError("devid"); - } - current_audio.impl.LockDevice(device); - SDL_AtomicSet(&device->paused, 1); - current_audio.impl.UnlockDevice(device); - return 0; -} + while (SDL_TRUE) { + SDL_AudioDevice *bounddev; -int SDL_PlayAudioDevice(SDL_AudioDeviceID devid) -{ - SDL_AudioDevice *device = get_audio_device(devid); - if (!device) { - return SDL_InvalidParamError("devid"); + SDL_LockMutex(stream->lock); /* lock to check this and then release it, in case the device isn't locked yet. */ + bounddev = stream->bound_device; + SDL_UnlockMutex(stream->lock); + + /* lock in correct order. */ + if (bounddev) { + SDL_LockMutex(bounddev->lock); /* this requires recursive mutexes, since we're likely locking the same device multiple times. */ + } + SDL_LockMutex(stream->lock); + + if (bounddev == stream->bound_device) { + break; /* the binding didn't change in the small window where it could, so we're good. */ + } else { + SDL_UnlockMutex(stream->lock); /* it changed bindings! Try again. */ + if (bounddev) { + SDL_UnlockMutex(bounddev->lock); + } + } + } } - current_audio.impl.LockDevice(device); - SDL_AtomicSet(&device->paused, 0); - current_audio.impl.UnlockDevice(device); - return 0; -} -int SDL_LockAudioDevice(SDL_AudioDeviceID devid) -{ - /* Obtain a lock on the mixing buffers */ - SDL_AudioDevice *device = get_audio_device(devid); - if (!device) { - return SDL_InvalidParamError("devid"); + /* everything is locked, start unbinding streams. */ + for (i = 0; i < num_streams; i++) { + SDL_AudioStream *stream = streams[i]; + if (stream && stream->bound_device) { + if (stream->bound_device->bound_streams == stream) { + SDL_assert(stream->prev_binding == NULL); + stream->bound_device->bound_streams = stream->next_binding; + } + if (stream->prev_binding) { + stream->prev_binding->next_binding = stream->next_binding; + } + if (stream->next_binding) { + stream->next_binding->prev_binding = stream->prev_binding; + } + stream->prev_binding = stream->next_binding = NULL; + } } - current_audio.impl.LockDevice(device); - return 0; -} -void SDL_UnlockAudioDevice(SDL_AudioDeviceID devid) -{ - /* Obtain a lock on the mixing buffers */ - SDL_AudioDevice *device = get_audio_device(devid); - if (!device) { - return; + /* Finalize and unlock everything. */ + for (i = 0; i < num_streams; i++) { + SDL_AudioStream *stream = streams[i]; + if (stream && stream->bound_device) { + SDL_AudioDevice *dev = stream->bound_device; + stream->bound_device = NULL; + SDL_UnlockMutex(stream->lock); + if (dev) { + SDL_UnlockMutex(dev->lock); + } + } } - current_audio.impl.UnlockDevice(device); } -void SDL_CloseAudioDevice(SDL_AudioDeviceID devid) +void SDL_UnbindAudioStream(SDL_AudioStream *stream) { - SDL_AudioDevice *device = get_audio_device(devid); - if (!device) { - return; - } - close_audio_device(device); + SDL_UnbindAudioStreams(&stream, 1); } -void SDL_QuitAudio(void) -{ - SDL_AudioDeviceID i; - if (!current_audio.name) { /* not initialized?! */ - return; - } +SDL_AudioStream *SDL_CreateAndBindAudioStream(SDL_AudioDeviceID devid, SDL_AudioFormat fmt, int channels, int freq) +{ + SDL_AudioStream *stream = NULL; + SDL_AudioDevice *device = ObtainAudioDevice(devid); + if (device) { + const SDL_bool iscapture = (devid & 1) ? SDL_FALSE : SDL_TRUE; /* capture instance ids are even and output devices are odd */ + if (iscapture) { + stream = SDL_CreateAudioStream(device->format, device->channels, device->freq, fmt, channels, freq); + } else { + stream = SDL_CreateAudioStream(fmt, channels, freq, device->format, device->channels, device->freq); + } - for (i = 0; i < SDL_arraysize(open_devices); i++) { - close_audio_device(open_devices[i]); + if (stream) { + if (SDL_BindAudioStream(devid, stream) == -1) { + SDL_DestroyAudioStream(stream); + stream = NULL; + } + } + SDL_UnlockMutex(device->lock); } - - free_device_list(¤t_audio.outputDevices, ¤t_audio.outputDeviceCount); - free_device_list(¤t_audio.inputDevices, ¤t_audio.inputDeviceCount); - - /* Free the driver data */ - current_audio.impl.Deinitialize(); - - SDL_DestroyMutex(current_audio.detectionLock); - - SDL_zero(current_audio); - SDL_zeroa(open_devices); + return stream; } #define NUM_FORMATS 8 @@ -1515,69 +1282,8 @@ const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format) return &format_list[0][NUM_FORMATS]; /* not found; return what looks like a list with only a zero in it. */ } -Uint8 SDL_GetSilenceValueForFormat(const SDL_AudioFormat format) +int SDL_GetSilenceValueForFormat(SDL_AudioFormat format) { return (format == SDL_AUDIO_U8) ? 0x80 : 0x00; } -void SDL_CalculateAudioSpec(SDL_AudioSpec *spec) -{ - spec->silence = SDL_GetSilenceValueForFormat(spec->format); - spec->size = SDL_AUDIO_BITSIZE(spec->format) / 8; - spec->size *= spec->channels; - spec->size *= spec->samples; -} - -/* !!! FIXME: move this to SDL_audiocvt.c */ -int SDL_ConvertAudioSamples(SDL_AudioFormat src_format, Uint8 src_channels, int src_rate, const Uint8 *src_data, int src_len, - SDL_AudioFormat dst_format, Uint8 dst_channels, int dst_rate, Uint8 **dst_data, int *dst_len) -{ - int ret = -1; - SDL_AudioStream *stream = NULL; - Uint8 *dst = NULL; - int dstlen = 0; - - if (dst_data) { - *dst_data = NULL; - } - - if (dst_len) { - *dst_len = 0; - } - - if (src_data == NULL) { - return SDL_InvalidParamError("src_data"); - } else if (src_len < 0) { - return SDL_InvalidParamError("src_len"); - } else if (dst_data == NULL) { - return SDL_InvalidParamError("dst_data"); - } else if (dst_len == NULL) { - return SDL_InvalidParamError("dst_len"); - } - - stream = SDL_CreateAudioStream(src_format, src_channels, src_rate, dst_format, dst_channels, dst_rate); - if (stream != NULL) { - if ((SDL_PutAudioStreamData(stream, src_data, src_len) == 0) && (SDL_FlushAudioStream(stream) == 0)) { - dstlen = SDL_GetAudioStreamAvailable(stream); - if (dstlen >= 0) { - dst = (Uint8 *)SDL_malloc(dstlen); - if (!dst) { - SDL_OutOfMemory(); - } else { - ret = (SDL_GetAudioStreamData(stream, dst, dstlen) >= 0) ? 0 : -1; - } - } - } - } - - if (ret == -1) { - SDL_free(dst); - } else { - *dst_data = dst; - *dst_len = dstlen; - } - - SDL_DestroyAudioStream(stream); - return ret; -} - diff --git a/src/audio/SDL_audio_c.h b/src/audio/SDL_audio_c.h index d69184a54cfb4d..7b00ceacc9b500 100644 --- a/src/audio/SDL_audio_c.h +++ b/src/audio/SDL_audio_c.h @@ -22,59 +22,8 @@ #ifndef SDL_audio_c_h_ #define SDL_audio_c_h_ -#include "SDL_internal.h" +/* !!! FIXME: remove this header and have things just include SDL_sysaudio.h directly. */ -#define DEBUG_AUDIOSTREAM 0 -#define DEBUG_AUDIO_CONVERT 0 - -#if DEBUG_AUDIO_CONVERT -#define LOG_DEBUG_AUDIO_CONVERT(from, to) SDL_Log("SDL_AUDIO_CONVERT: Converting %s to %s.\n", from, to); -#else -#define LOG_DEBUG_AUDIO_CONVERT(from, to) -#endif - -/* Functions and variables exported from SDL_audio.c for SDL_sysaudio.c */ - -/* Function to get a list of audio formats, ordered most similar to `format` to least, 0-terminated. Don't free results. */ -const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format); - -/* Function to calculate the size and silence for a SDL_AudioSpec */ -extern Uint8 SDL_GetSilenceValueForFormat(const SDL_AudioFormat format); -extern void SDL_CalculateAudioSpec(SDL_AudioSpec *spec); - -/* Must be called at least once before using converters (SDL_CreateAudioStream will call it). */ -extern void SDL_ChooseAudioConverters(void); - -/* These pointers get set during SDL_ChooseAudioConverters() to various SIMD implementations. */ -extern void (*SDL_Convert_S8_to_F32)(float *dst, const Sint8 *src, int num_samples); -extern void (*SDL_Convert_U8_to_F32)(float *dst, const Uint8 *src, int num_samples); -extern void (*SDL_Convert_S16_to_F32)(float *dst, const Sint16 *src, int num_samples); -extern void (*SDL_Convert_S32_to_F32)(float *dst, const Sint32 *src, int num_samples); -extern void (*SDL_Convert_F32_to_S8)(Sint8 *dst, const float *src, int num_samples); -extern void (*SDL_Convert_F32_to_U8)(Uint8 *dst, const float *src, int num_samples); -extern void (*SDL_Convert_F32_to_S16)(Sint16 *dst, const float *src, int num_samples); -extern void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_samples); - -/** - * Use this function to initialize a particular audio driver. - * - * This function is used internally, and should not be used unless you have a - * specific need to designate the audio driver you want to use. You should - * normally use SDL_Init() or SDL_InitSubSystem(). - * - * \param driver_name the name of the desired audio driver - * \returns 0 on success or a negative error code on failure; call - * SDL_GetError() for more information. - */ -extern int SDL_InitAudio(const char *driver_name); - -/** - * Use this function to shut down audio if you initialized it with SDL_InitAudio(). - * - * This function is used internally, and should not be used unless you have a - * specific need to specify the audio driver you want to use. You should - * normally use SDL_Quit() or SDL_QuitSubSystem(). - */ -extern void SDL_QuitAudio(void); +#include "SDL_sysaudio.h" #endif /* SDL_audio_c_h_ */ diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c index 8c3badcb487e8f..c4d5182adaf170 100644 --- a/src/audio/SDL_audiocvt.c +++ b/src/audio/SDL_audiocvt.c @@ -434,49 +434,6 @@ static void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_fo SDL_assert(src == dst); /* if we got here, we _had_ to have done _something_. Otherwise, we should have memcpy'd! */ } -struct SDL_AudioStream -{ - SDL_DataQueue *queue; - SDL_Mutex *lock; /* this is just a copy of `queue`'s mutex. We share a lock. */ - - Uint8 *work_buffer; /* used for scratch space during data conversion/resampling. */ - Uint8 *history_buffer; /* history for left padding and future sample rate changes. */ - Uint8 *future_buffer; /* stuff that left the queue for the right padding and will be next read's data. */ - float *left_padding; /* left padding for resampling. */ - float *right_padding; /* right padding for resampling. */ - - SDL_bool flushed; - - size_t work_buffer_allocation; - size_t history_buffer_allocation; - size_t future_buffer_allocation; - size_t resampler_padding_allocation; - - int resampler_padding_frames; - int history_buffer_frames; - int future_buffer_filled_frames; - - int max_sample_frame_size; - - int src_sample_frame_size; - SDL_AudioFormat src_format; - int src_channels; - int src_rate; - - int dst_sample_frame_size; - SDL_AudioFormat dst_format; - int dst_channels; - int dst_rate; - - int pre_resample_channels; - int packetlen; -}; - -static int GetMemsetSilenceValue(const SDL_AudioFormat fmt) -{ - return (fmt == SDL_AUDIO_U8) ? 0x80 : 0x00; -} - /* figure out the largest thing we might need for ConvertAudio, which might grow data in-place. */ static int CalculateMaxSampleFrameSize(SDL_AudioFormat src_format, int src_channels, SDL_AudioFormat dst_format, int dst_channels) { @@ -560,7 +517,7 @@ static int SetAudioStreamFormat(SDL_AudioStream *stream, SDL_AudioFormat src_for if (stream->future_buffer) { ConvertAudio(stream->future_buffer_filled_frames, stream->future_buffer, stream->src_format, stream->src_channels, future_buffer, src_format, src_channels); } else if (future_buffer != NULL) { - SDL_memset(future_buffer, GetMemsetSilenceValue(src_format), future_buffer_allocation); + SDL_memset(future_buffer, SDL_GetSilenceValueForFormat(src_format), future_buffer_allocation); } if (stream->history_buffer) { @@ -568,10 +525,10 @@ static int SetAudioStreamFormat(SDL_AudioStream *stream, SDL_AudioFormat src_for ConvertAudio(history_buffer_frames, stream->history_buffer, stream->src_format, stream->src_channels, history_buffer, src_format, src_channels); } else { ConvertAudio(prev_history_buffer_frames, stream->history_buffer, stream->src_format, stream->src_channels, history_buffer + ((history_buffer_frames - prev_history_buffer_frames) * src_sample_frame_size), src_format, src_channels); - SDL_memset(history_buffer, GetMemsetSilenceValue(src_format), (history_buffer_frames - prev_history_buffer_frames) * src_sample_frame_size); /* silence oldest history samples. */ + SDL_memset(history_buffer, SDL_GetSilenceValueForFormat(src_format), (history_buffer_frames - prev_history_buffer_frames) * src_sample_frame_size); /* silence oldest history samples. */ } } else if (history_buffer != NULL) { - SDL_memset(history_buffer, GetMemsetSilenceValue(src_format), history_buffer_allocation); + SDL_memset(history_buffer, SDL_GetSilenceValueForFormat(src_format), history_buffer_allocation); } if (future_buffer != stream->future_buffer) { @@ -612,6 +569,8 @@ SDL_AudioStream *SDL_CreateAudioStream(SDL_AudioFormat src_format, int packetlen = 4096; /* !!! FIXME: good enough for now. */ SDL_AudioStream *retval; + /* !!! FIXME: fail if audio isn't initialized? */ + if (!SDL_IsSupportedChannelCount(src_channels)) { SDL_InvalidParamError("src_channels"); return NULL; @@ -917,7 +876,7 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le stream->future_buffer_filled_frames = future_buffer_filled_frames; if (br < cpy) { /* we couldn't fill the future buffer with enough padding! */ if (stream->flushed) { /* that's okay, we're flushing, just silence the still-needed padding. */ - SDL_memset(future_buffer + (future_buffer_filled_frames * src_sample_frame_size), GetMemsetSilenceValue(src_format), cpy - br); + SDL_memset(future_buffer + (future_buffer_filled_frames * src_sample_frame_size), SDL_GetSilenceValueForFormat(src_format), cpy - br); } else { /* Drastic measures: steal from the work buffer! */ const int stealcpyframes = SDL_min(workbuf_frames, cpyframes - brframes); const int stealcpy = stealcpyframes * src_sample_frame_size; @@ -1100,7 +1059,7 @@ int SDL_ClearAudioStream(SDL_AudioStream *stream) SDL_LockMutex(stream->lock); SDL_ClearDataQueue(stream->queue, (size_t)stream->packetlen * 2); - SDL_memset(stream->history_buffer, GetMemsetSilenceValue(stream->src_format), stream->history_buffer_frames * stream->src_channels * sizeof (float)); + SDL_memset(stream->history_buffer, SDL_GetSilenceValueForFormat(stream->src_format), stream->history_buffer_frames * stream->src_channels * sizeof (float)); stream->future_buffer_filled_frames = 0; stream->flushed = SDL_FALSE; SDL_UnlockMutex(stream->lock); @@ -1110,6 +1069,7 @@ int SDL_ClearAudioStream(SDL_AudioStream *stream) void SDL_DestroyAudioStream(SDL_AudioStream *stream) { if (stream) { + SDL_UnbindAudioStream(stream); /* do not destroy stream->lock! it's a copy of `stream->queue`'s mutex, so destroying the queue will handle it. */ SDL_DestroyDataQueue(stream->queue); SDL_aligned_free(stream->work_buffer); @@ -1120,3 +1080,56 @@ void SDL_DestroyAudioStream(SDL_AudioStream *stream) SDL_free(stream); } } + +int SDL_ConvertAudioSamples(SDL_AudioFormat src_format, int src_channels, int src_rate, const Uint8 *src_data, int src_len, + SDL_AudioFormat dst_format, int dst_channels, int dst_rate, Uint8 **dst_data, int *dst_len) +{ + int ret = -1; + SDL_AudioStream *stream = NULL; + Uint8 *dst = NULL; + int dstlen = 0; + + if (dst_data) { + *dst_data = NULL; + } + + if (dst_len) { + *dst_len = 0; + } + + if (src_data == NULL) { + return SDL_InvalidParamError("src_data"); + } else if (src_len < 0) { + return SDL_InvalidParamError("src_len"); + } else if (dst_data == NULL) { + return SDL_InvalidParamError("dst_data"); + } else if (dst_len == NULL) { + return SDL_InvalidParamError("dst_len"); + } + + stream = SDL_CreateAudioStream(src_format, src_channels, src_rate, dst_format, dst_channels, dst_rate); + if (stream != NULL) { + if ((SDL_PutAudioStreamData(stream, src_data, src_len) == 0) && (SDL_FlushAudioStream(stream) == 0)) { + dstlen = SDL_GetAudioStreamAvailable(stream); + if (dstlen >= 0) { + dst = (Uint8 *)SDL_malloc(dstlen); + if (!dst) { + SDL_OutOfMemory(); + } else { + ret = (SDL_GetAudioStreamData(stream, dst, dstlen) >= 0) ? 0 : -1; + } + } + } + } + + if (ret == -1) { + SDL_free(dst); + } else { + *dst_data = dst; + *dst_len = dstlen; + } + + SDL_DestroyAudioStream(stream); + return ret; +} + diff --git a/src/audio/SDL_audiodev.c b/src/audio/SDL_audiodev.c index b26740dbee916a..50bcddf86ce31e 100644 --- a/src/audio/SDL_audiodev.c +++ b/src/audio/SDL_audiodev.c @@ -63,7 +63,7 @@ static void test_device(const int iscapture, const char *fname, int flags, int ( * information, making this information inaccessible at * enumeration time */ - SDL_AddAudioDevice(iscapture, fname, NULL, (void *)(uintptr_t)dummyhandle); + SDL_AddAudioDevice(iscapture, fname, 0, 0, 0, (void *)(uintptr_t)dummyhandle, ); } } } diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h index 7c6fd29851f242..7fa95c81208564 100644 --- a/src/audio/SDL_sysaudio.h +++ b/src/audio/SDL_sysaudio.h @@ -24,60 +24,90 @@ #define SDL_sysaudio_h_ #include "../SDL_dataqueue.h" -#include "./SDL_audio_c.h" + +#define DEBUG_AUDIOSTREAM 0 +#define DEBUG_AUDIO_CONVERT 0 + +#if DEBUG_AUDIO_CONVERT +#define LOG_DEBUG_AUDIO_CONVERT(from, to) SDL_Log("SDL_AUDIO_CONVERT: Converting %s to %s.\n", from, to); +#else +#define LOG_DEBUG_AUDIO_CONVERT(from, to) +#endif + +/* These pointers get set during SDL_ChooseAudioConverters() to various SIMD implementations. */ +extern void (*SDL_Convert_S8_to_F32)(float *dst, const Sint8 *src, int num_samples); +extern void (*SDL_Convert_U8_to_F32)(float *dst, const Uint8 *src, int num_samples); +extern void (*SDL_Convert_S16_to_F32)(float *dst, const Sint16 *src, int num_samples); +extern void (*SDL_Convert_S32_to_F32)(float *dst, const Sint32 *src, int num_samples); +extern void (*SDL_Convert_F32_to_S8)(Sint8 *dst, const float *src, int num_samples); +extern void (*SDL_Convert_F32_to_U8)(Uint8 *dst, const float *src, int num_samples); +extern void (*SDL_Convert_F32_to_S16)(Sint16 *dst, const float *src, int num_samples); +extern void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_samples); + /* !!! FIXME: These are wordy and unlocalized... */ #define DEFAULT_OUTPUT_DEVNAME "System audio output device" #define DEFAULT_INPUT_DEVNAME "System audio capture device" +/* these are used when no better specifics are known. We default to CD audio quality. */ +#define DEFAULT_AUDIO_FORMAT SDL_AUDIO_S16 +#define DEFAULT_AUDIO_CHANNELS 2 +#define DEFAULT_AUDIO_FREQUENCY 44100 + + /* The SDL audio driver */ typedef struct SDL_AudioDevice SDL_AudioDevice; +/* Used by src/SDL.c to initialize a particular audio driver. */ +extern int SDL_InitAudio(const char *driver_name); + +/* Used by src/SDL.c to shut down previously-initialized audio. */ +extern void SDL_QuitAudio(void); + +/* Function to get a list of audio formats, ordered most similar to `format` to least, 0-terminated. Don't free results. */ +const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format); + +/* Must be called at least once before using converters (SDL_CreateAudioStream will call it !!! FIXME but probably shouldn't). */ +extern void SDL_ChooseAudioConverters(void); + /* Audio targets should call this as devices are added to the system (such as a USB headset being plugged in), and should also be called for for every device found during DetectDevices(). */ -extern void SDL_AddAudioDevice(const SDL_bool iscapture, const char *name, SDL_AudioSpec *spec, void *handle); - -/* Audio targets should call this as devices are removed, so SDL can update - its list of available devices. */ -extern void SDL_RemoveAudioDevice(const SDL_bool iscapture, void *handle); - -/* Audio targets should call this if an opened audio device is lost while - being used. This can happen due to i/o errors, or a device being unplugged, - etc. If the device is totally gone, please also call SDL_RemoveAudioDevice() - as appropriate so SDL's list of devices is accurate. */ -extern void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device); - -/* This is the size of a packet when using SDL_QueueAudio(). We allocate - these as necessary and pool them, under the assumption that we'll - eventually end up with a handful that keep recycling, meeting whatever - the app needs. We keep packing data tightly as more arrives to avoid - wasting space, and if we get a giant block of data, we'll split them - into multiple packets behind the scenes. My expectation is that most - apps will have 2-3 of these in the pool. 8k should cover most needs, but - if this is crippling for some embedded system, we can #ifdef this. - The system preallocates enough packets for 2 callbacks' worth of data. */ -#define SDL_AUDIOBUFFERQUEUE_PACKETLEN (8 * 1024) +extern SDL_AudioDevice *SDL_AddAudioDevice(const SDL_bool iscapture, const char *name, SDL_AudioFormat fmt, int channels, int freq, void *handle); + +/* Audio targets should call this if an opened audio device is lost. + This can happen due to i/o errors, or a device being unplugged, etc. */ +extern void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device); + +/* Find the SDL_AudioDevice associated with the handle supplied to SDL_AddAudioDevice. NULL if not found. Locks the device! You must unlock!! */ +extern SDL_AudioDevice *SDL_ObtainAudioDeviceByHandle(void *handle); + +/* Backends should call this if they change the device format, channels, freq, or sample_frames to keep other state correct. */ +extern void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device); + + +/* These functions are the heart of the audio threads. Backends can call them directly if they aren't using the SDL-provided thread. */ +extern void SDL_OutputAudioThreadSetup(SDL_AudioDevice *device); +extern SDL_bool SDL_OutputAudioThreadIterate(SDL_AudioDevice *device); +extern void SDL_OutputAudioThreadShutdown(SDL_AudioDevice *device); +extern void SDL_CaptureAudioThreadSetup(SDL_AudioDevice *device); +extern SDL_bool SDL_CaptureAudioThreadIterate(SDL_AudioDevice *device); +extern void SDL_CaptureAudioThreadShutdown(SDL_AudioDevice *device); typedef struct SDL_AudioDriverImpl { void (*DetectDevices)(void); - int (*OpenDevice)(SDL_AudioDevice *_this, const char *devname); - void (*ThreadInit)(SDL_AudioDevice *_this); /* Called by audio thread at start */ - void (*ThreadDeinit)(SDL_AudioDevice *_this); /* Called by audio thread at end */ - void (*WaitDevice)(SDL_AudioDevice *_this); - void (*PlayDevice)(SDL_AudioDevice *_this); - Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *_this); - int (*CaptureFromDevice)(SDL_AudioDevice *_this, void *buffer, int buflen); - void (*FlushCapture)(SDL_AudioDevice *_this); - void (*CloseDevice)(SDL_AudioDevice *_this); - void (*LockDevice)(SDL_AudioDevice *_this); - void (*UnlockDevice)(SDL_AudioDevice *_this); + int (*OpenDevice)(SDL_AudioDevice *device); + void (*ThreadInit)(SDL_AudioDevice *device); /* Called by audio thread at start */ + void (*ThreadDeinit)(SDL_AudioDevice *device); /* Called by audio thread at end */ + void (*WaitDevice)(SDL_AudioDevice *device); + void (*PlayDevice)(SDL_AudioDevice *device, int buffer_size); + Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *device, int *buffer_size); + int (*CaptureFromDevice)(SDL_AudioDevice *device, void *buffer, int buflen); + void (*FlushCapture)(SDL_AudioDevice *device); + void (*CloseDevice)(SDL_AudioDevice *device); void (*FreeDeviceHandle)(void *handle); /**< SDL is done with handle from SDL_AddAudioDevice() */ void (*Deinitialize)(void); - int (*GetDefaultAudioInfo)(char **name, SDL_AudioSpec *spec, int iscapture); - - /* !!! FIXME: add pause(), so we can optimize instead of mixing silence. */ /* Some flags to push duplicate code into the core and reduce #ifdefs. */ SDL_bool ProvidesOwnCallbackThread; @@ -88,81 +118,120 @@ typedef struct SDL_AudioDriverImpl SDL_bool SupportsNonPow2Samples; } SDL_AudioDriverImpl; -typedef struct SDL_AudioDeviceItem +typedef struct SDL_AudioDriver { - void *handle; - char *name; - char *original_name; - SDL_AudioSpec spec; - int dupenum; - struct SDL_AudioDeviceItem *next; -} SDL_AudioDeviceItem; + const char *name; /* The name of this audio driver */ + const char *desc; /* The description of this audio driver */ + SDL_AudioDriverImpl impl; /* the backend's interface */ + SDL_RWLock *device_list_lock; /* A mutex for device detection */ + SDL_AudioDevice *output_devices; /* the list of currently-available audio output devices. */ + SDL_AudioDevice *capture_devices; /* the list of currently-available audio capture devices. */ + SDL_AtomicInt output_device_count; + SDL_AtomicInt capture_device_count; + SDL_AtomicInt last_device_instance_id; /* increments on each device add to provide unique instance IDs */ + SDL_AtomicInt shutting_down; /* non-zero during SDL_Quit, so we known not to accept any last-minute device hotplugs. */ +} SDL_AudioDriver; -typedef struct SDL_AudioDriver +struct SDL_AudioStream { - /* * * */ - /* The name of this audio driver */ - const char *name; + SDL_DataQueue *queue; + SDL_Mutex *lock; /* this is just a copy of `queue`'s mutex. We share a lock. */ - /* * * */ - /* The description of this audio driver */ - const char *desc; + Uint8 *work_buffer; /* used for scratch space during data conversion/resampling. */ + Uint8 *history_buffer; /* history for left padding and future sample rate changes. */ + Uint8 *future_buffer; /* stuff that left the queue for the right padding and will be next read's data. */ + float *left_padding; /* left padding for resampling. */ + float *right_padding; /* right padding for resampling. */ - SDL_AudioDriverImpl impl; + SDL_bool flushed; - /* A mutex for device detection */ - SDL_Mutex *detectionLock; - SDL_bool captureDevicesRemoved; - SDL_bool outputDevicesRemoved; - int outputDeviceCount; - int inputDeviceCount; - SDL_AudioDeviceItem *outputDevices; - SDL_AudioDeviceItem *inputDevices; -} SDL_AudioDriver; + size_t work_buffer_allocation; + size_t history_buffer_allocation; + size_t future_buffer_allocation; + size_t resampler_padding_allocation; + + int resampler_padding_frames; + int history_buffer_frames; + int future_buffer_filled_frames; + + int max_sample_frame_size; + + int src_sample_frame_size; + SDL_AudioFormat src_format; + int src_channels; + int src_rate; + + int dst_sample_frame_size; + SDL_AudioFormat dst_format; + int dst_channels; + int dst_rate; + + int pre_resample_channels; + int packetlen; + + SDL_AudioDevice *bound_device; + SDL_AudioStream *next_binding; + SDL_AudioStream *prev_binding; +}; -/* Define the SDL audio driver structure */ struct SDL_AudioDevice { - /* * * */ - /* Data common to all devices */ - SDL_AudioDeviceID id; + /* A mutex for locking access to this struct */ + SDL_Mutex *lock; + + /* human-readable name of the device. ("SoundBlaster Pro 16") */ + char *name; + + /* the unique instance ID of this device. */ + SDL_AudioDeviceID instance_id; + + /* a way for the backend to identify this device _when not opened_ */ + void *handle; /* The device's current audio specification */ - SDL_AudioSpec spec; + SDL_AudioFormat format; + int freq; + int channels; + Uint32 buffer_size; - /* The callback's expected audio specification (converted vs device's spec). */ - SDL_AudioSpec callbackspec; + /* The device's default audio specification */ + SDL_AudioFormat default_format; + int default_freq; + int default_channels; - /* Stream that converts and resamples. NULL if not needed. */ - SDL_AudioStream *stream; + /* Number of sample frames the devices wants per-buffer. */ + int sample_frames; - /* Current state flags */ - SDL_AtomicInt shutdown; /* true if we are signaling the play thread to end. */ - SDL_AtomicInt enabled; /* true if device is functioning and connected. */ - SDL_AtomicInt paused; - SDL_bool iscapture; + /* Value to use for SDL_memset to silence a buffer in this device's format */ + int silence_value; - /* Scratch buffer used in the bridge between SDL and the user callback. */ - Uint8 *work_buffer; + /* non-zero if we are signaling the audio thread to end. */ + SDL_AtomicInt shutdown; - /* Size, in bytes, of work_buffer. */ - Uint32 work_buffer_len; + /* non-zero if we want the device to be destroyed (so audio thread knows to do it on termination). */ + SDL_AtomicInt condemned; - /* A mutex for locking the mixing buffers */ - SDL_Mutex *mixer_lock; + /* SDL_TRUE if this is a capture device instead of an output device */ + SDL_bool iscapture; + + /* Scratch buffer used for mixing. */ + Uint8 *work_buffer; /* A thread to feed the audio device */ SDL_Thread *thread; - SDL_threadID threadid; - - /* Queued buffers (if app not using callback). */ - SDL_DataQueue *buffer_queue; - /* * * */ /* Data private to this driver */ struct SDL_PrivateAudioData *hidden; - void *handle; + /* Each device open increases the refcount. We actually close the system device when this hits zero again. */ + SDL_AtomicInt refcount; + + /* double-linked list of all audio streams currently bound to this device. */ + SDL_AudioStream *bound_streams; + + /* double-linked list of all devices. */ + struct SDL_AudioDevice *prev; + struct SDL_AudioDevice *next; }; typedef struct AudioBootStrap @@ -170,7 +239,7 @@ typedef struct AudioBootStrap const char *name; const char *desc; SDL_bool (*init)(SDL_AudioDriverImpl *impl); - SDL_bool demand_only; /* 1==request explicitly, or it won't be available. */ + SDL_bool demand_only; /* if SDL_TRUE: request explicitly, or it won't be available. */ } AudioBootStrap; /* Not all of these are available in a given build. Use #ifdefs, etc. */ @@ -188,8 +257,8 @@ extern AudioBootStrap HAIKUAUDIO_bootstrap; extern AudioBootStrap COREAUDIO_bootstrap; extern AudioBootStrap DISKAUDIO_bootstrap; extern AudioBootStrap DUMMYAUDIO_bootstrap; -extern AudioBootStrap aaudio_bootstrap; -extern AudioBootStrap openslES_bootstrap; +extern AudioBootStrap aaudio_bootstrap; /* !!! FIXME: capitalize this to match the others */ +extern AudioBootStrap openslES_bootstrap; /* !!! FIXME: capitalize this to match the others */ extern AudioBootStrap ANDROIDAUDIO_bootstrap; extern AudioBootStrap PS2AUDIO_bootstrap; extern AudioBootStrap PSPAUDIO_bootstrap; diff --git a/src/audio/SDL_wave.c b/src/audio/SDL_wave.c index d7e6ac3c36a45e..d27661ba101b21 100644 --- a/src/audio/SDL_wave.c +++ b/src/audio/SDL_wave.c @@ -1241,7 +1241,7 @@ static int LAW_Decode(WaveFile *file, Uint8 **audio_buf, Uint32 *audio_len) dst = (Sint16 *)src; - /* Work backwards, since we're expanding in-place. SDL_AudioSpec.format will + /* Work backwards, since we're expanding in-place. `format` will * inform the caller about the byte order. */ i = sample_count; @@ -1667,15 +1667,13 @@ static int WaveCheckFormat(WaveFile *file, size_t datalength) if (format->channels == 0) { return SDL_SetError("Invalid number of channels"); - } else if (format->channels > 255) { - /* Limit given by SDL_AudioSpec.channels. */ - return SDL_SetError("Number of channels exceeds limit of 255"); + } else if (format->channels > INT_MAX) { + return SDL_SetError("Number of channels exceeds limit of %d", INT_MAX); } if (format->frequency == 0) { return SDL_SetError("Invalid sample rate"); } else if (format->frequency > INT_MAX) { - /* Limit given by SDL_AudioSpec.freq. */ return SDL_SetError("Sample rate exceeds limit of %d", INT_MAX); } @@ -1766,7 +1764,7 @@ static int WaveCheckFormat(WaveFile *file, size_t datalength) return 0; } -static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) +static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioFormat *fmt, int *channels, int *freq, Uint8 **audio_buf, Uint32 *audio_len) { int result; Uint32 chunkcount = 0; @@ -2025,13 +2023,11 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 * break; } - /* Setting up the SDL_AudioSpec. All unsupported formats were filtered out + /* Setting up the specs. All unsupported formats were filtered out * by checks earlier in this function. */ - SDL_zerop(spec); - spec->freq = format->frequency; - spec->channels = (Uint8)format->channels; - spec->samples = 4096; /* Good default buffer size */ + *freq = format->frequency; + *channels = (Uint8)format->channels; switch (format->encoding) { case MS_ADPCM_CODE: @@ -2039,22 +2035,22 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 * case ALAW_CODE: case MULAW_CODE: /* These can be easily stored in the byte order of the system. */ - spec->format = SDL_AUDIO_S16SYS; + *fmt = SDL_AUDIO_S16SYS; break; case IEEE_FLOAT_CODE: - spec->format = SDL_AUDIO_F32LSB; + *fmt = SDL_AUDIO_F32LSB; break; case PCM_CODE: switch (format->bitspersample) { case 8: - spec->format = SDL_AUDIO_U8; + *fmt = SDL_AUDIO_U8; break; case 16: - spec->format = SDL_AUDIO_S16LSB; + *fmt = SDL_AUDIO_S16LSB; break; case 24: /* Has been shifted to 32 bits. */ case 32: - spec->format = SDL_AUDIO_S32LSB; + *fmt = SDL_AUDIO_S32LSB; break; default: /* Just in case something unexpected happened in the checks. */ @@ -2063,8 +2059,6 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 * break; } - spec->silence = SDL_GetSilenceValueForFormat(spec->format); - /* Report the end position back to the cleanup code. */ if (RIFFlengthknown) { chunk->position = RIFFend; @@ -2075,39 +2069,37 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 * return 0; } -SDL_AudioSpec *SDL_LoadWAV_RW(SDL_RWops *src, int freesrc, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) +int SDL_LoadWAV_RW(SDL_RWops *src, int freesrc, SDL_AudioFormat *fmt, int *channels, int *freq, Uint8 **audio_buf, Uint32 *audio_len) { int result; WaveFile file; - SDL_zero(file); - /* Make sure we are passed a valid data source */ if (src == NULL) { - /* Error may come from RWops. */ - return NULL; - } else if (spec == NULL) { - SDL_InvalidParamError("spec"); - return NULL; + return -1; /* Error may come from RWops. */ + } else if (fmt == NULL) { + return SDL_InvalidParamError("fmt"); + } else if (channels == NULL) { + return SDL_InvalidParamError("channels"); + } else if (freq == NULL) { + return SDL_InvalidParamError("freq"); } else if (audio_buf == NULL) { - SDL_InvalidParamError("audio_buf"); - return NULL; + return SDL_InvalidParamError("audio_buf"); } else if (audio_len == NULL) { - SDL_InvalidParamError("audio_len"); - return NULL; + return SDL_InvalidParamError("audio_len"); } *audio_buf = NULL; *audio_len = 0; + SDL_zero(file); file.riffhint = WaveGetRiffSizeHint(); file.trunchint = WaveGetTruncationHint(); file.facthint = WaveGetFactChunkHint(); - result = WaveLoad(src, &file, spec, audio_buf, audio_len); + result = WaveLoad(src, &file, fmt, channels, freq, audio_buf, audio_len); if (result < 0) { SDL_free(*audio_buf); - spec = NULL; audio_buf = NULL; audio_len = 0; } @@ -2121,5 +2113,5 @@ SDL_AudioSpec *SDL_LoadWAV_RW(SDL_RWops *src, int freesrc, SDL_AudioSpec *spec, WaveFreeChunkData(&file.chunk); SDL_free(file.decoderdata); - return spec; + return result; } diff --git a/src/audio/disk/SDL_diskaudio.c b/src/audio/disk/SDL_diskaudio.c index d351ed90a0dc2e..2e83a111b8913f 100644 --- a/src/audio/disk/SDL_diskaudio.c +++ b/src/audio/disk/SDL_diskaudio.c @@ -24,10 +24,6 @@ /* Output raw audio data to a file. */ -#ifdef HAVE_STDIO_H -#include -#endif - #include "../SDL_audio_c.h" #include "SDL_diskaudio.h" @@ -40,34 +36,34 @@ #define DISKENVR_IODELAY "SDL_DISKAUDIODELAY" /* This function waits until it is possible to write a full sound buffer */ -static void DISKAUDIO_WaitDevice(SDL_AudioDevice *_this) +static void DISKAUDIO_WaitDevice(SDL_AudioDevice *device) { - SDL_Delay(_this->hidden->io_delay); + SDL_Delay(device->hidden->io_delay); } -static void DISKAUDIO_PlayDevice(SDL_AudioDevice *_this) +static void DISKAUDIO_PlayDevice(SDL_AudioDevice *device, int buffer_size) { - const Sint64 written = SDL_RWwrite(_this->hidden->io, - _this->hidden->mixbuf, - _this->spec.size); + const Sint64 written = SDL_RWwrite(device->hidden->io, + device->hidden->mixbuf, + buffer_size); /* If we couldn't write, assume fatal error for now */ - if (written != _this->spec.size) { - SDL_OpenedAudioDeviceDisconnected(_this); + if (written != buffer_size) { + SDL_AudioDeviceDisconnected(device); } #ifdef DEBUG_AUDIO - fprintf(stderr, "Wrote %d bytes of audio data\n", (int) written); + SDL_Log("DISKAUDIO: Wrote %d bytes of audio data", (int) written); #endif } -static Uint8 *DISKAUDIO_GetDeviceBuf(SDL_AudioDevice *_this) +static Uint8 *DISKAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) { - return _this->hidden->mixbuf; + return device->hidden->mixbuf; } -static int DISKAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) +static int DISKAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen) { - struct SDL_PrivateAudioData *h = _this->hidden; + struct SDL_PrivateAudioData *h = device->hidden; const int origbuflen = buflen; SDL_Delay(h->io_delay); @@ -83,70 +79,66 @@ static int DISKAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int } /* if we ran out of file, just write silence. */ - SDL_memset(buffer, _this->spec.silence, buflen); + SDL_memset(buffer, device->silence_value, buflen); return origbuflen; } -static void DISKAUDIO_FlushCapture(SDL_AudioDevice *_this) +static void DISKAUDIO_FlushCapture(SDL_AudioDevice *device) { /* no op...we don't advance the file pointer or anything. */ } -static void DISKAUDIO_CloseDevice(SDL_AudioDevice *_this) +static void DISKAUDIO_CloseDevice(SDL_AudioDevice *device) { - if (_this->hidden->io != NULL) { - SDL_RWclose(_this->hidden->io); + if (device->hidden->io != NULL) { + SDL_RWclose(device->hidden->io); } - SDL_free(_this->hidden->mixbuf); - SDL_free(_this->hidden); + SDL_free(device->hidden->mixbuf); + SDL_free(device->hidden); } -static const char *get_filename(const SDL_bool iscapture, const char *devname) +static const char *get_filename(const SDL_bool iscapture) { + const char *devname = SDL_getenv(iscapture ? DISKENVR_INFILE : DISKENVR_OUTFILE); if (devname == NULL) { - devname = SDL_getenv(iscapture ? DISKENVR_INFILE : DISKENVR_OUTFILE); - if (devname == NULL) { - devname = iscapture ? DISKDEFAULT_INFILE : DISKDEFAULT_OUTFILE; - } + devname = iscapture ? DISKDEFAULT_INFILE : DISKDEFAULT_OUTFILE; } return devname; } -static int DISKAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) +static int DISKAUDIO_OpenDevice(SDL_AudioDevice *device) { - void *handle = _this->handle; - /* handle != NULL means "user specified the placeholder name on the fake detected device list" */ - SDL_bool iscapture = _this->iscapture; - const char *fname = get_filename(iscapture, handle ? NULL : devname); + SDL_bool iscapture = device->iscapture; + const char *fname = get_filename(iscapture); const char *envr = SDL_getenv(DISKENVR_IODELAY); - _this->hidden = (struct SDL_PrivateAudioData *) - SDL_malloc(sizeof(*_this->hidden)); - if (_this->hidden == NULL) { + device->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc(sizeof(*device->hidden)); + if (device->hidden == NULL) { return SDL_OutOfMemory(); } - SDL_zerop(_this->hidden); + SDL_zerop(device->hidden); if (envr != NULL) { - _this->hidden->io_delay = SDL_atoi(envr); + device->hidden->io_delay = SDL_atoi(envr); } else { - _this->hidden->io_delay = ((_this->spec.samples * 1000) / _this->spec.freq); + device->hidden->io_delay = ((device->sample_frames * 1000) / device->freq); } - /* Open the audio device */ - _this->hidden->io = SDL_RWFromFile(fname, iscapture ? "rb" : "wb"); - if (_this->hidden->io == NULL) { + /* Open the "audio device" */ + device->hidden->io = SDL_RWFromFile(fname, iscapture ? "rb" : "wb"); + if (device->hidden->io == NULL) { return -1; } /* Allocate mixing buffer */ if (!iscapture) { - _this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->spec.size); - if (_this->hidden->mixbuf == NULL) { + device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size); + if (device->hidden->mixbuf == NULL) { return SDL_OutOfMemory(); } - SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->spec.size); + SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size); } SDL_LogCritical(SDL_LOG_CATEGORY_AUDIO, @@ -161,8 +153,8 @@ static int DISKAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) static void DISKAUDIO_DetectDevices(void) { - SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *)0x1); - SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *)0x2); + SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, 0, 0, 0, (void *)0x1); + SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, 0, 0, 0, (void *)0x2); } static SDL_bool DISKAUDIO_Init(SDL_AudioDriverImpl *impl) diff --git a/src/audio/dummy/SDL_dummyaudio.c b/src/audio/dummy/SDL_dummyaudio.c index 1f12a1a2af72a6..ce1ecca5dce7ab 100644 --- a/src/audio/dummy/SDL_dummyaudio.c +++ b/src/audio/dummy/SDL_dummyaudio.c @@ -25,20 +25,35 @@ #include "../SDL_audio_c.h" #include "SDL_dummyaudio.h" -static int DUMMYAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) +/* !!! FIXME: add a dummy WaitDevice to simulate real audio better? */ + +static int DUMMYAUDIO_OpenDevice(SDL_AudioDevice *device) +{ + device->hidden = (struct SDL_PrivateAudioData *) SDL_malloc(device->buffer_size); + if (!device->hidden) { + return SDL_OutOfMemory(); + } + return 0; /* don't change reported device format. */ +} + +static void DUMMYAUDIO_CloseDevice(SDL_AudioDevice *device) { - _this->hidden = (void *)0x1; /* just something non-NULL */ + SDL_free(device->hidden); + device->hidden = NULL; +} - return 0; /* always succeeds. */ +static Uint8 *DUMMYAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) +{ + return (Uint8 *) device->hidden; } -static int DUMMYAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) +static int DUMMYAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen) { /* Delay to make this sort of simulate real audio input. */ - SDL_Delay((_this->spec.samples * 1000) / _this->spec.freq); + SDL_Delay((device->sample_frames * 1000) / device->freq); /* always return a full buffer of silence. */ - SDL_memset(buffer, _this->spec.silence, buflen); + SDL_memset(buffer, device->silence_value, buflen); return buflen; } @@ -46,6 +61,8 @@ static SDL_bool DUMMYAUDIO_Init(SDL_AudioDriverImpl *impl) { /* Set the function pointers */ impl->OpenDevice = DUMMYAUDIO_OpenDevice; + impl->CloseDevice = DUMMYAUDIO_CloseDevice; + impl->GetDeviceBuf = DUMMYAUDIO_GetDeviceBuf; impl->CaptureFromDevice = DUMMYAUDIO_CaptureFromDevice; impl->OnlyHasDefaultOutputDevice = SDL_TRUE; diff --git a/src/audio/dummy/SDL_dummyaudio.h b/src/audio/dummy/SDL_dummyaudio.h index a4bddb44d904a2..78709ac0027773 100644 --- a/src/audio/dummy/SDL_dummyaudio.h +++ b/src/audio/dummy/SDL_dummyaudio.h @@ -25,6 +25,8 @@ #include "../SDL_sysaudio.h" +/* !!! FIXME: none of this is actually used. Dump this whole file. */ + struct SDL_PrivateAudioData { /* The file descriptor for the audio device */ diff --git a/src/audio/pulseaudio/SDL_pulseaudio.c b/src/audio/pulseaudio/SDL_pulseaudio.c index f791c463fb2393..ce2c54f5e5145c 100644 --- a/src/audio/pulseaudio/SDL_pulseaudio.c +++ b/src/audio/pulseaudio/SDL_pulseaudio.c @@ -366,12 +366,6 @@ static int ConnectToPulseServer(void) return -1; } -/* This function waits until it is possible to write a full sound buffer */ -static void PULSEAUDIO_WaitDevice(SDL_AudioDevice *_this) -{ - /* this is a no-op; we wait in PULSEAUDIO_PlayDevice now. */ -} - static void WriteCallback(pa_stream *p, size_t nbytes, void *userdata) { struct SDL_PrivateAudioData *h = (struct SDL_PrivateAudioData *)userdata; @@ -380,50 +374,58 @@ static void WriteCallback(pa_stream *p, size_t nbytes, void *userdata) PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); } -static void PULSEAUDIO_PlayDevice(SDL_AudioDevice *_this) +/* This function waits until it is possible to write a full sound buffer */ +static void PULSEAUDIO_WaitDevice(SDL_AudioDevice *device) { - struct SDL_PrivateAudioData *h = _this->hidden; - int available = h->mixlen; - int written = 0; - int cpy; + struct SDL_PrivateAudioData *h = device->hidden; /*printf("PULSEAUDIO PLAYDEVICE START! mixlen=%d\n", available);*/ PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - while (SDL_AtomicGet(&_this->enabled) && (available > 0)) { - cpy = SDL_min(h->bytes_requested, available); - if (cpy) { - if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf + written, cpy, NULL, 0LL, PA_SEEK_RELATIVE) < 0) { - SDL_OpenedAudioDeviceDisconnected(_this); - break; - } - /*printf("PULSEAUDIO FEED! nbytes=%u\n", (unsigned int) cpy);*/ - h->bytes_requested -= cpy; - written += cpy; - available -= cpy; - } - - if (available > 0) { - /* let WriteCallback fire if necessary. */ - /*printf("PULSEAUDIO WAIT IN PLAYDEVICE!\n");*/ - PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); + while (!SDL_AtomicGet(&device->shutdown) && (h->bytes_requested < (device->buffer_size / 2))) { + /*printf("PULSEAUDIO WAIT IN WAITDEVICE!\n");*/ + PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); - if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) { - /*printf("PULSEAUDIO DEVICE FAILURE IN PLAYDEVICE!\n");*/ - SDL_OpenedAudioDeviceDisconnected(_this); - break; - } + if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) { + /*printf("PULSEAUDIO DEVICE FAILURE IN WAITDEVICE!\n");*/ + SDL_AudioDeviceDisconnected(device); + break; } } + PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); +} + +static void PULSEAUDIO_PlayDevice(SDL_AudioDevice *device, int buffer_size) +{ + struct SDL_PrivateAudioData *h = device->hidden; + const int available = buffer_size; + int rc; + + /*printf("PULSEAUDIO PLAYDEVICE START! mixlen=%d\n", available);*/ + SDL_assert(h->bytes_requested >= available); + + PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); + rc = PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, available, NULL, 0LL, PA_SEEK_RELATIVE); PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); + + if (rc < 0) { + SDL_AudioDeviceDisconnected(device); + return; + } + + /*printf("PULSEAUDIO FEED! nbytes=%u\n", (unsigned int) available);*/ + h->bytes_requested -= available; + /*printf("PULSEAUDIO PLAYDEVICE END! written=%d\n", written);*/ } -static Uint8 *PULSEAUDIO_GetDeviceBuf(SDL_AudioDevice *_this) +static Uint8 *PULSEAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) { - return _this->hidden->mixbuf; + struct SDL_PrivateAudioData *h = device->hidden; + *buffer_size = SDL_min(*buffer_size, h->bytes_requested); + return device->hidden->mixbuf; } static void ReadCallback(pa_stream *p, size_t nbytes, void *userdata) @@ -432,16 +434,16 @@ static void ReadCallback(pa_stream *p, size_t nbytes, void *userdata) PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); /* the capture code queries what it needs, we just need to signal to end any wait */ } -static int PULSEAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) +static int PULSEAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen) { - struct SDL_PrivateAudioData *h = _this->hidden; + struct SDL_PrivateAudioData *h = device->hidden; const void *data = NULL; size_t nbytes = 0; int retval = 0; PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - while (SDL_AtomicGet(&_this->enabled)) { + while (!SDL_AtomicGet(&device->shutdown)) { if (h->capturebuf != NULL) { const int cpy = SDL_min(buflen, h->capturelen); SDL_memcpy(buffer, h->capturebuf, cpy); @@ -456,17 +458,17 @@ static int PULSEAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, in break; } - while (SDL_AtomicGet(&_this->enabled) && (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0)) { + while (!SDL_AtomicGet(&device->shutdown) && (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0)) { PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) { /*printf("PULSEAUDIO DEVICE FAILURE IN CAPTUREFROMDEVICE!\n");*/ - SDL_OpenedAudioDeviceDisconnected(_this); + SDL_AudioDeviceDisconnected(device); retval = -1; break; } } - if ((retval == -1) || !SDL_AtomicGet(&_this->enabled)) { /* in case this happened while we were blocking. */ + if ((retval == -1) || SDL_AtomicGet(&device->shutdown)) { /* in case this happened while we were blocking. */ retval = -1; break; } @@ -490,9 +492,9 @@ static int PULSEAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, in return retval; } -static void PULSEAUDIO_FlushCapture(SDL_AudioDevice *_this) +static void PULSEAUDIO_FlushCapture(SDL_AudioDevice *device) { - struct SDL_PrivateAudioData *h = _this->hidden; + struct SDL_PrivateAudioData *h = device->hidden; const void *data = NULL; size_t nbytes = 0; @@ -504,11 +506,11 @@ static void PULSEAUDIO_FlushCapture(SDL_AudioDevice *_this) h->capturelen = 0; } - while (SDL_AtomicGet(&_this->enabled) && (PULSEAUDIO_pa_stream_readable_size(h->stream) > 0)) { + while (!SDL_AtomicGet(&device->shutdown) && (PULSEAUDIO_pa_stream_readable_size(h->stream) > 0)) { PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) { /*printf("PULSEAUDIO DEVICE FAILURE IN FLUSHCAPTURE!\n");*/ - SDL_OpenedAudioDeviceDisconnected(_this); + SDL_AudioDeviceDisconnected(device); break; } @@ -522,22 +524,22 @@ static void PULSEAUDIO_FlushCapture(SDL_AudioDevice *_this) PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); } -static void PULSEAUDIO_CloseDevice(SDL_AudioDevice *_this) +static void PULSEAUDIO_CloseDevice(SDL_AudioDevice *device) { PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - if (_this->hidden->stream) { - if (_this->hidden->capturebuf != NULL) { - PULSEAUDIO_pa_stream_drop(_this->hidden->stream); + if (device->hidden->stream) { + if (device->hidden->capturebuf != NULL) { + PULSEAUDIO_pa_stream_drop(device->hidden->stream); } - PULSEAUDIO_pa_stream_disconnect(_this->hidden->stream); - PULSEAUDIO_pa_stream_unref(_this->hidden->stream); + PULSEAUDIO_pa_stream_disconnect(device->hidden->stream); + PULSEAUDIO_pa_stream_unref(device->hidden->stream); } PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); - SDL_free(_this->hidden->mixbuf); - SDL_free(_this->hidden->device_name); - SDL_free(_this->hidden); + SDL_free(device->hidden->mixbuf); + SDL_free(device->hidden->device_name); + SDL_free(device->hidden); } static void SinkDeviceNameCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data) @@ -580,8 +582,9 @@ static void PulseStreamStateChangeCallback(pa_stream *stream, void *userdata) PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); /* just signal any waiting code, it can look up the details. */ } -static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) +static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *device) { + const SDL_bool iscapture = device->iscapture; struct SDL_PrivateAudioData *h = NULL; SDL_AudioFormat test_format; const SDL_AudioFormat *closefmts; @@ -589,7 +592,6 @@ static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) pa_buffer_attr paattr; pa_channel_map pacmap; pa_stream_flags_t flags = 0; - SDL_bool iscapture = _this->iscapture; int format = PA_SAMPLE_INVALID; int retval = 0; @@ -597,14 +599,14 @@ static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) SDL_assert(pulseaudio_context != NULL); /* Initialize all variables that we clean on shutdown */ - h = _this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*_this->hidden)); - if (_this->hidden == NULL) { + h = device->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*device->hidden)); + if (device->hidden == NULL) { return SDL_OutOfMemory(); } - SDL_zerop(_this->hidden); + SDL_zerop(device->hidden); /* Try for a closest match on audio format */ - closefmts = SDL_ClosestAudioFormats(_this->spec.format); + closefmts = SDL_ClosestAudioFormats(device->format); while ((test_format = *(closefmts++)) != 0) { #ifdef DEBUG_AUDIO fprintf(stderr, "Trying format 0x%4.4x\n", test_format); @@ -639,27 +641,27 @@ static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) if (!test_format) { return SDL_SetError("pulseaudio: Unsupported audio format"); } - _this->spec.format = test_format; + device->format = test_format; paspec.format = format; /* Calculate the final parameters for this audio specification */ - SDL_CalculateAudioSpec(&_this->spec); + SDL_UpdatedAudioDeviceFormat(device); /* Allocate mixing buffer */ if (!iscapture) { - h->mixlen = _this->spec.size; + h->mixlen = device->buffer_size; h->mixbuf = (Uint8 *)SDL_malloc(h->mixlen); if (h->mixbuf == NULL) { return SDL_OutOfMemory(); } - SDL_memset(h->mixbuf, _this->spec.silence, _this->spec.size); + SDL_memset(h->mixbuf, device->silence_value, device->buffer_size); } - paspec.channels = _this->spec.channels; - paspec.rate = _this->spec.freq; + paspec.channels = device->channels; + paspec.rate = device->freq; /* Reduced prebuffering compared to the defaults. */ - paattr.fragsize = _this->spec.size; + paattr.fragsize = device->buffer_size; paattr.tlength = h->mixlen; paattr.prebuf = -1; paattr.maxlength = -1; @@ -668,14 +670,13 @@ static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname) PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - if (!FindDeviceName(h, iscapture, _this->handle)) { + if (!FindDeviceName(h, iscapture, device->handle)) { retval = SDL_SetError("Requested PulseAudio sink/source missing?"); } else { const char *name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME); /* The SDL ALSA output hints us that we use Windows' channel mapping */ /* https://bugzilla.libsdl.org/show_bug.cgi?id=110 */ - PULSEAUDIO_pa_channel_map_init_auto(&pacmap, _this->spec.channels, - PA_CHANNEL_MAP_WAVEEX); + PULSEAUDIO_pa_channel_map_init_auto(&pacmap, device->channels, PA_CHANNEL_MAP_WAVEEX); h->stream = PULSEAUDIO_pa_stream_new( pulseaudio_context, @@ -754,26 +755,18 @@ static SDL_AudioFormat PulseFormatToSDLFormat(pa_sample_format_t format) /* This is called when PulseAudio adds an output ("sink") device. */ static void SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data) { - SDL_AudioSpec spec; - SDL_bool add = (SDL_bool)((intptr_t)data); if (i) { - spec.freq = i->sample_spec.rate; - spec.channels = i->sample_spec.channels; - spec.format = PulseFormatToSDLFormat(i->sample_spec.format); - spec.silence = 0; - spec.samples = 0; - spec.size = 0; - spec.callback = NULL; - spec.userdata = NULL; + const SDL_bool add = (SDL_bool)((intptr_t)data); + const SDL_AudioFormat fmt = PulseFormatToSDLFormat(i->sample_spec.format); + const int channels = i->sample_spec.channels; + const int freq = i->sample_spec.rate; if (add) { - SDL_AddAudioDevice(SDL_FALSE, i->description, &spec, (void *)((intptr_t)i->index + 1)); + SDL_AddAudioDevice(SDL_FALSE, i->description, fmt, channels, freq, (void *)((intptr_t)i->index + 1)); } if (default_sink_path != NULL && SDL_strcmp(i->name, default_sink_path) == 0) { - if (default_sink_name != NULL) { - SDL_free(default_sink_name); - } + SDL_free(default_sink_name); default_sink_name = SDL_strdup(i->description); } } @@ -783,30 +776,20 @@ static void SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, /* This is called when PulseAudio adds a capture ("source") device. */ static void SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *data) { - SDL_AudioSpec spec; - SDL_bool add = (SDL_bool)((intptr_t)data); - if (i) { - /* Maybe skip "monitor" sources. These are just output from other sinks. */ - if (include_monitors || (i->monitor_of_sink == PA_INVALID_INDEX)) { - spec.freq = i->sample_spec.rate; - spec.channels = i->sample_spec.channels; - spec.format = PulseFormatToSDLFormat(i->sample_spec.format); - spec.silence = 0; - spec.samples = 0; - spec.size = 0; - spec.callback = NULL; - spec.userdata = NULL; - - if (add) { - SDL_AddAudioDevice(SDL_TRUE, i->description, &spec, (void *)((intptr_t)i->index + 1)); - } + /* Maybe skip "monitor" sources. These are just output from other sinks. */ + if (i && (include_monitors || (i->monitor_of_sink == PA_INVALID_INDEX))) { + const SDL_bool add = (SDL_bool)((intptr_t)data); + const SDL_AudioFormat fmt = PulseFormatToSDLFormat(i->sample_spec.format); + const int channels = i->sample_spec.channels; + const int freq = i->sample_spec.rate; - if (default_source_path != NULL && SDL_strcmp(i->name, default_source_path) == 0) { - if (default_source_name != NULL) { - SDL_free(default_source_name); - } - default_source_name = SDL_strdup(i->description); - } + if (add) { + SDL_AddAudioDevice(SDL_TRUE, i->description, fmt, channels, freq, (void *)((intptr_t)i->index + 1)); + } + + if (default_source_path != NULL && SDL_strcmp(i->name, default_source_path) == 0) { + SDL_free(default_source_name); + default_source_name = SDL_strdup(i->description); } } PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); @@ -846,7 +829,11 @@ static void HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint3 PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_source_info_by_index(pulseaudio_context, idx, SourceInfoCallback, (void *)((intptr_t)added))); } else if (removed && (sink || source)) { /* removes we can handle just with the device index. */ - SDL_RemoveAudioDevice(source != 0, (void *)((intptr_t)idx + 1)); + SDL_AudioDevice *device = SDL_ObtainAudioDeviceByHandle((void *)((intptr_t)idx + 1)); /* !!! FIXME: maybe just have a "disconnect by handle" function instead. */ + if (device) { + SDL_UnlockMutex(device->lock); /* AudioDeviceDisconnected will relock and verify it's still in the list, but in case this is destroyed, unlock now. */ + SDL_AudioDeviceDisconnected(device); + } } } PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); @@ -879,6 +866,7 @@ static void PULSEAUDIO_DetectDevices(void) pulseaudio_hotplug_thread = SDL_CreateThreadInternal(HotplugThread, "PulseHotplug", 256 * 1024, NULL); /* !!! FIXME: this can probably survive in significantly less stack space. */ } +#if 0 static int PULSEAUDIO_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture) { int i; @@ -909,6 +897,7 @@ static int PULSEAUDIO_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int } return SDL_SetError("Could not find default PulseAudio device"); } +#endif static void PULSEAUDIO_Deinitialize(void) { @@ -956,7 +945,9 @@ static SDL_bool PULSEAUDIO_Init(SDL_AudioDriverImpl *impl) impl->Deinitialize = PULSEAUDIO_Deinitialize; impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice; impl->FlushCapture = PULSEAUDIO_FlushCapture; + #if 0 impl->GetDefaultAudioInfo = PULSEAUDIO_GetDefaultAudioInfo; + #endif impl->HasCaptureSupport = SDL_TRUE; impl->SupportsNonPow2Samples = SDL_TRUE; diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index ac611a0b6bff35..f455c3748605bc 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -34,12 +34,9 @@ SDL3_0.0.0 { SDL_BlitSurfaceUnchecked; SDL_BlitSurfaceUncheckedScaled; SDL_CaptureMouse; - SDL_ClearAudioStream; SDL_ClearComposition; SDL_ClearError; SDL_ClearHints; - SDL_ClearQueuedAudio; - SDL_CloseAudioDevice; SDL_CloseGamepad; SDL_CloseJoystick; SDL_CloseSensor; @@ -51,7 +48,6 @@ SDL3_0.0.0 { SDL_ConvertPixels; SDL_ConvertSurface; SDL_ConvertSurfaceFormat; - SDL_CreateAudioStream; SDL_CreateColorCursor; SDL_CreateCondition; SDL_CreateCursor; @@ -78,8 +74,6 @@ SDL3_0.0.0 { SDL_DelHintCallback; SDL_Delay; SDL_DelayNS; - SDL_DequeueAudio; - SDL_DestroyAudioStream; SDL_DestroyCondition; SDL_DestroyMutex; SDL_DestroyPalette; @@ -106,7 +100,6 @@ SDL3_0.0.0 { SDL_FillSurfaceRects; SDL_FilterEvents; SDL_FlashWindow; - SDL_FlushAudioStream; SDL_FlushEvent; SDL_FlushEvents; SDL_DestroyCursor; @@ -144,22 +137,14 @@ SDL3_0.0.0 { SDL_GetAndroidSDKVersion; SDL_GetAssertionHandler; SDL_GetAssertionReport; - SDL_GetAudioDeviceName; - SDL_GetAudioDeviceSpec; - SDL_GetAudioDeviceStatus; - SDL_GetAudioDriver; - SDL_GetAudioStreamAvailable; - SDL_GetAudioStreamData; SDL_GetBasePath; SDL_GetCPUCacheLineSize; SDL_GetCPUCount; SDL_GetClipboardText; - SDL_GetCurrentAudioDriver; SDL_GetCurrentDisplayMode; SDL_GetCurrentVideoDriver; SDL_GetCursor; SDL_GetDefaultAssertionHandler; - SDL_GetDefaultAudioInfo; SDL_GetDefaultCursor; SDL_GetDesktopDisplayMode; SDL_GetDisplayBounds; @@ -256,8 +241,6 @@ SDL3_0.0.0 { SDL_GetMouseFocus; SDL_GetMouseState; SDL_GetNumAllocations; - SDL_GetNumAudioDevices; - SDL_GetNumAudioDrivers; SDL_GetNumGamepadMappings; SDL_GetNumJoystickAxes; SDL_GetNumJoystickButtons; @@ -276,7 +259,6 @@ SDL3_0.0.0 { SDL_GetPrefPath; SDL_GetPreferredLocales; SDL_GetPrimarySelectionText; - SDL_GetQueuedAudioSize; SDL_GetRGB; SDL_GetRGBA; SDL_GetRectAndLineIntersection; @@ -436,8 +418,6 @@ SDL3_0.0.0 { SDL_LoadFile_RW; SDL_LoadFunction; SDL_LoadObject; - SDL_LoadWAV_RW; - SDL_LockAudioDevice; SDL_LockJoysticks; SDL_LockMutex; SDL_LockSurface; @@ -467,7 +447,6 @@ SDL3_0.0.0 { SDL_Metal_DestroyView; SDL_Metal_GetLayer; SDL_MinimizeWindow; - SDL_MixAudioFormat; SDL_MouseIsHaptic; SDL_NumHaptics; SDL_OnApplicationDidBecomeActive; @@ -477,20 +456,16 @@ SDL3_0.0.0 { SDL_OnApplicationWillEnterForeground; SDL_OnApplicationWillResignActive; SDL_OnApplicationWillTerminate; - SDL_OpenAudioDevice; SDL_OpenGamepad; SDL_OpenJoystick; SDL_OpenSensor; SDL_OpenURL; - SDL_PauseAudioDevice; SDL_PeepEvents; SDL_PollEvent; SDL_PremultiplyAlpha; SDL_PumpEvents; SDL_PushEvent; - SDL_PutAudioStreamData; SDL_QueryTexture; - SDL_QueueAudio; SDL_Quit; SDL_QuitSubSystem; SDL_RWFromConstMem; @@ -643,7 +618,6 @@ SDL3_0.0.0 { SDL_ThreadID; SDL_TryLockMutex; SDL_UnloadObject; - SDL_UnlockAudioDevice; SDL_UnlockJoysticks; SDL_UnlockMutex; SDL_UnlockSurface; @@ -825,10 +799,8 @@ SDL3_0.0.0 { SDL_modf; SDL_modff; SDL_GetRenderVSync; - SDL_PlayAudioDevice; SDL_aligned_alloc; SDL_aligned_free; - SDL_ConvertAudioSamples; SDL_GetDisplays; SDL_GetPrimaryDisplay; SDL_GetFullscreenDisplayModes; @@ -845,8 +817,6 @@ SDL3_0.0.0 { SDL_GetClipboardUserdata; SDL_GetClipboardData; SDL_HasClipboardData; - SDL_GetAudioStreamFormat; - SDL_SetAudioStreamFormat; SDL_CreateRWLock; SDL_LockRWLockForReading; SDL_LockRWLockForWriting; @@ -864,6 +834,33 @@ SDL3_0.0.0 { SDL_hid_get_input_report; SDL_hid_get_device_info; SDL_hid_get_report_descriptor; + SDL_GetNumAudioDrivers; + SDL_GetAudioDriver; + SDL_GetCurrentAudioDriver; + SDL_GetAudioOutputDevices; + SDL_GetAudioCaptureDevices; + SDL_GetAudioDeviceName; + SDL_GetAudioDeviceFormat; + SDL_OpenAudioDevice; + SDL_CloseAudioDevice; + SDL_BindAudioStreams; + SDL_BindAudioStream; + SDL_UnbindAudioStreams; + SDL_UnbindAudioStream; + SDL_CreateAudioStream; + SDL_GetAudioStreamFormat; + SDL_SetAudioStreamFormat; + SDL_PutAudioStreamData; + SDL_GetAudioStreamData; + SDL_GetAudioStreamAvailable; + SDL_FlushAudioStream; + SDL_ClearAudioStream; + SDL_DestroyAudioStream; + SDL_CreateAndBindAudioStream; + SDL_LoadWAV_RW; + SDL_MixAudioFormat; + SDL_ConvertAudioSamples; + SDL_GetSilenceValueForFormat; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 95406eea5677c1..2f52403e2a09be 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -58,12 +58,9 @@ #define SDL_BlitSurfaceUnchecked SDL_BlitSurfaceUnchecked_REAL #define SDL_BlitSurfaceUncheckedScaled SDL_BlitSurfaceUncheckedScaled_REAL #define SDL_CaptureMouse SDL_CaptureMouse_REAL -#define SDL_ClearAudioStream SDL_ClearAudioStream_REAL #define SDL_ClearComposition SDL_ClearComposition_REAL #define SDL_ClearError SDL_ClearError_REAL #define SDL_ClearHints SDL_ClearHints_REAL -#define SDL_ClearQueuedAudio SDL_ClearQueuedAudio_REAL -#define SDL_CloseAudioDevice SDL_CloseAudioDevice_REAL #define SDL_CloseGamepad SDL_CloseGamepad_REAL #define SDL_CloseJoystick SDL_CloseJoystick_REAL #define SDL_CloseSensor SDL_CloseSensor_REAL @@ -75,7 +72,6 @@ #define SDL_ConvertPixels SDL_ConvertPixels_REAL #define SDL_ConvertSurface SDL_ConvertSurface_REAL #define SDL_ConvertSurfaceFormat SDL_ConvertSurfaceFormat_REAL -#define SDL_CreateAudioStream SDL_CreateAudioStream_REAL #define SDL_CreateColorCursor SDL_CreateColorCursor_REAL #define SDL_CreateCondition SDL_CreateCondition_REAL #define SDL_CreateCursor SDL_CreateCursor_REAL @@ -102,8 +98,6 @@ #define SDL_DelHintCallback SDL_DelHintCallback_REAL #define SDL_Delay SDL_Delay_REAL #define SDL_DelayNS SDL_DelayNS_REAL -#define SDL_DequeueAudio SDL_DequeueAudio_REAL -#define SDL_DestroyAudioStream SDL_DestroyAudioStream_REAL #define SDL_DestroyCondition SDL_DestroyCondition_REAL #define SDL_DestroyMutex SDL_DestroyMutex_REAL #define SDL_DestroyPalette SDL_DestroyPalette_REAL @@ -130,7 +124,6 @@ #define SDL_FillSurfaceRects SDL_FillSurfaceRects_REAL #define SDL_FilterEvents SDL_FilterEvents_REAL #define SDL_FlashWindow SDL_FlashWindow_REAL -#define SDL_FlushAudioStream SDL_FlushAudioStream_REAL #define SDL_FlushEvent SDL_FlushEvent_REAL #define SDL_FlushEvents SDL_FlushEvents_REAL #define SDL_DestroyCursor SDL_DestroyCursor_REAL @@ -168,22 +161,14 @@ #define SDL_GetAndroidSDKVersion SDL_GetAndroidSDKVersion_REAL #define SDL_GetAssertionHandler SDL_GetAssertionHandler_REAL #define SDL_GetAssertionReport SDL_GetAssertionReport_REAL -#define SDL_GetAudioDeviceName SDL_GetAudioDeviceName_REAL -#define SDL_GetAudioDeviceSpec SDL_GetAudioDeviceSpec_REAL -#define SDL_GetAudioDeviceStatus SDL_GetAudioDeviceStatus_REAL -#define SDL_GetAudioDriver SDL_GetAudioDriver_REAL -#define SDL_GetAudioStreamAvailable SDL_GetAudioStreamAvailable_REAL -#define SDL_GetAudioStreamData SDL_GetAudioStreamData_REAL #define SDL_GetBasePath SDL_GetBasePath_REAL #define SDL_GetCPUCacheLineSize SDL_GetCPUCacheLineSize_REAL #define SDL_GetCPUCount SDL_GetCPUCount_REAL #define SDL_GetClipboardText SDL_GetClipboardText_REAL -#define SDL_GetCurrentAudioDriver SDL_GetCurrentAudioDriver_REAL #define SDL_GetCurrentDisplayMode SDL_GetCurrentDisplayMode_REAL #define SDL_GetCurrentVideoDriver SDL_GetCurrentVideoDriver_REAL #define SDL_GetCursor SDL_GetCursor_REAL #define SDL_GetDefaultAssertionHandler SDL_GetDefaultAssertionHandler_REAL -#define SDL_GetDefaultAudioInfo SDL_GetDefaultAudioInfo_REAL #define SDL_GetDefaultCursor SDL_GetDefaultCursor_REAL #define SDL_GetDesktopDisplayMode SDL_GetDesktopDisplayMode_REAL #define SDL_GetDisplayBounds SDL_GetDisplayBounds_REAL @@ -280,8 +265,6 @@ #define SDL_GetMouseFocus SDL_GetMouseFocus_REAL #define SDL_GetMouseState SDL_GetMouseState_REAL #define SDL_GetNumAllocations SDL_GetNumAllocations_REAL -#define SDL_GetNumAudioDevices SDL_GetNumAudioDevices_REAL -#define SDL_GetNumAudioDrivers SDL_GetNumAudioDrivers_REAL #define SDL_GetNumGamepadMappings SDL_GetNumGamepadMappings_REAL #define SDL_GetNumJoystickAxes SDL_GetNumJoystickAxes_REAL #define SDL_GetNumJoystickButtons SDL_GetNumJoystickButtons_REAL @@ -300,7 +283,6 @@ #define SDL_GetPrefPath SDL_GetPrefPath_REAL #define SDL_GetPreferredLocales SDL_GetPreferredLocales_REAL #define SDL_GetPrimarySelectionText SDL_GetPrimarySelectionText_REAL -#define SDL_GetQueuedAudioSize SDL_GetQueuedAudioSize_REAL #define SDL_GetRGB SDL_GetRGB_REAL #define SDL_GetRGBA SDL_GetRGBA_REAL #define SDL_GetRectAndLineIntersection SDL_GetRectAndLineIntersection_REAL @@ -460,8 +442,6 @@ #define SDL_LoadFile_RW SDL_LoadFile_RW_REAL #define SDL_LoadFunction SDL_LoadFunction_REAL #define SDL_LoadObject SDL_LoadObject_REAL -#define SDL_LoadWAV_RW SDL_LoadWAV_RW_REAL -#define SDL_LockAudioDevice SDL_LockAudioDevice_REAL #define SDL_LockJoysticks SDL_LockJoysticks_REAL #define SDL_LockMutex SDL_LockMutex_REAL #define SDL_LockSurface SDL_LockSurface_REAL @@ -491,7 +471,6 @@ #define SDL_Metal_DestroyView SDL_Metal_DestroyView_REAL #define SDL_Metal_GetLayer SDL_Metal_GetLayer_REAL #define SDL_MinimizeWindow SDL_MinimizeWindow_REAL -#define SDL_MixAudioFormat SDL_MixAudioFormat_REAL #define SDL_MouseIsHaptic SDL_MouseIsHaptic_REAL #define SDL_NumHaptics SDL_NumHaptics_REAL #define SDL_OnApplicationDidBecomeActive SDL_OnApplicationDidBecomeActive_REAL @@ -501,20 +480,16 @@ #define SDL_OnApplicationWillEnterForeground SDL_OnApplicationWillEnterForeground_REAL #define SDL_OnApplicationWillResignActive SDL_OnApplicationWillResignActive_REAL #define SDL_OnApplicationWillTerminate SDL_OnApplicationWillTerminate_REAL -#define SDL_OpenAudioDevice SDL_OpenAudioDevice_REAL #define SDL_OpenGamepad SDL_OpenGamepad_REAL #define SDL_OpenJoystick SDL_OpenJoystick_REAL #define SDL_OpenSensor SDL_OpenSensor_REAL #define SDL_OpenURL SDL_OpenURL_REAL -#define SDL_PauseAudioDevice SDL_PauseAudioDevice_REAL #define SDL_PeepEvents SDL_PeepEvents_REAL #define SDL_PollEvent SDL_PollEvent_REAL #define SDL_PremultiplyAlpha SDL_PremultiplyAlpha_REAL #define SDL_PumpEvents SDL_PumpEvents_REAL #define SDL_PushEvent SDL_PushEvent_REAL -#define SDL_PutAudioStreamData SDL_PutAudioStreamData_REAL #define SDL_QueryTexture SDL_QueryTexture_REAL -#define SDL_QueueAudio SDL_QueueAudio_REAL #define SDL_Quit SDL_Quit_REAL #define SDL_QuitSubSystem SDL_QuitSubSystem_REAL #define SDL_RWFromConstMem SDL_RWFromConstMem_REAL @@ -667,7 +642,6 @@ #define SDL_ThreadID SDL_ThreadID_REAL #define SDL_TryLockMutex SDL_TryLockMutex_REAL #define SDL_UnloadObject SDL_UnloadObject_REAL -#define SDL_UnlockAudioDevice SDL_UnlockAudioDevice_REAL #define SDL_UnlockJoysticks SDL_UnlockJoysticks_REAL #define SDL_UnlockMutex SDL_UnlockMutex_REAL #define SDL_UnlockSurface SDL_UnlockSurface_REAL @@ -851,10 +825,8 @@ #define SDL_modf SDL_modf_REAL #define SDL_modff SDL_modff_REAL #define SDL_GetRenderVSync SDL_GetRenderVSync_REAL -#define SDL_PlayAudioDevice SDL_PlayAudioDevice_REAL #define SDL_aligned_alloc SDL_aligned_alloc_REAL #define SDL_aligned_free SDL_aligned_free_REAL -#define SDL_ConvertAudioSamples SDL_ConvertAudioSamples_REAL #define SDL_GetDisplays SDL_GetDisplays_REAL #define SDL_GetPrimaryDisplay SDL_GetPrimaryDisplay_REAL #define SDL_GetFullscreenDisplayModes SDL_GetFullscreenDisplayModes_REAL @@ -871,8 +843,6 @@ #define SDL_GetClipboardUserdata SDL_GetClipboardUserdata_REAL #define SDL_GetClipboardData SDL_GetClipboardData_REAL #define SDL_HasClipboardData SDL_HasClipboardData_REAL -#define SDL_GetAudioStreamFormat SDL_GetAudioStreamFormat_REAL -#define SDL_SetAudioStreamFormat SDL_SetAudioStreamFormat_REAL #define SDL_CreateRWLock SDL_CreateRWLock_REAL #define SDL_LockRWLockForReading SDL_LockRWLockForReading_REAL #define SDL_LockRWLockForWriting SDL_LockRWLockForWriting_REAL @@ -890,3 +860,30 @@ #define SDL_hid_get_input_report SDL_hid_get_input_report_REAL #define SDL_hid_get_device_info SDL_hid_get_device_info_REAL #define SDL_hid_get_report_descriptor SDL_hid_get_report_descriptor_REAL +#define SDL_GetNumAudioDrivers SDL_GetNumAudioDrivers_REAL +#define SDL_GetAudioDriver SDL_GetAudioDriver_REAL +#define SDL_GetCurrentAudioDriver SDL_GetCurrentAudioDriver_REAL +#define SDL_GetAudioOutputDevices SDL_GetAudioOutputDevices_REAL +#define SDL_GetAudioCaptureDevices SDL_GetAudioCaptureDevices_REAL +#define SDL_GetAudioDeviceName SDL_GetAudioDeviceName_REAL +#define SDL_GetAudioDeviceFormat SDL_GetAudioDeviceFormat_REAL +#define SDL_OpenAudioDevice SDL_OpenAudioDevice_REAL +#define SDL_CloseAudioDevice SDL_CloseAudioDevice_REAL +#define SDL_BindAudioStreams SDL_BindAudioStreams_REAL +#define SDL_BindAudioStream SDL_BindAudioStream_REAL +#define SDL_UnbindAudioStreams SDL_UnbindAudioStreams_REAL +#define SDL_UnbindAudioStream SDL_UnbindAudioStream_REAL +#define SDL_CreateAudioStream SDL_CreateAudioStream_REAL +#define SDL_GetAudioStreamFormat SDL_GetAudioStreamFormat_REAL +#define SDL_SetAudioStreamFormat SDL_SetAudioStreamFormat_REAL +#define SDL_PutAudioStreamData SDL_PutAudioStreamData_REAL +#define SDL_GetAudioStreamData SDL_GetAudioStreamData_REAL +#define SDL_GetAudioStreamAvailable SDL_GetAudioStreamAvailable_REAL +#define SDL_FlushAudioStream SDL_FlushAudioStream_REAL +#define SDL_ClearAudioStream SDL_ClearAudioStream_REAL +#define SDL_DestroyAudioStream SDL_DestroyAudioStream_REAL +#define SDL_CreateAndBindAudioStream SDL_CreateAndBindAudioStream_REAL +#define SDL_LoadWAV_RW SDL_LoadWAV_RW_REAL +#define SDL_MixAudioFormat SDL_MixAudioFormat_REAL +#define SDL_ConvertAudioSamples SDL_ConvertAudioSamples_REAL +#define SDL_GetSilenceValueForFormat SDL_GetSilenceValueForFormat_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 2a722a1ff6dd12..54320ef527a30a 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -140,12 +140,9 @@ SDL_DYNAPI_PROC(int,SDL_BlitSurfaceScaled,(SDL_Surface *a, const SDL_Rect *b, SD SDL_DYNAPI_PROC(int,SDL_BlitSurfaceUnchecked,(SDL_Surface *a, SDL_Rect *b, SDL_Surface *c, SDL_Rect *d),(a,b,c,d),return) SDL_DYNAPI_PROC(int,SDL_BlitSurfaceUncheckedScaled,(SDL_Surface *a, SDL_Rect *b, SDL_Surface *c, SDL_Rect *d),(a,b,c,d),return) SDL_DYNAPI_PROC(int,SDL_CaptureMouse,(SDL_bool a),(a),return) -SDL_DYNAPI_PROC(int,SDL_ClearAudioStream,(SDL_AudioStream *a),(a),return) SDL_DYNAPI_PROC(void,SDL_ClearComposition,(void),(),) SDL_DYNAPI_PROC(void,SDL_ClearError,(void),(),) SDL_DYNAPI_PROC(void,SDL_ClearHints,(void),(),) -SDL_DYNAPI_PROC(int,SDL_ClearQueuedAudio,(SDL_AudioDeviceID a),(a),return) -SDL_DYNAPI_PROC(void,SDL_CloseAudioDevice,(SDL_AudioDeviceID a),(a),) SDL_DYNAPI_PROC(void,SDL_CloseGamepad,(SDL_Gamepad *a),(a),) SDL_DYNAPI_PROC(void,SDL_CloseJoystick,(SDL_Joystick *a),(a),) SDL_DYNAPI_PROC(void,SDL_CloseSensor,(SDL_Sensor *a),(a),) @@ -157,7 +154,6 @@ SDL_DYNAPI_PROC(int,SDL_WaitConditionTimeout,(SDL_Condition *a, SDL_Mutex *b, Si SDL_DYNAPI_PROC(int,SDL_ConvertPixels,(int a, int b, Uint32 c, const void *d, int e, Uint32 f, void *g, int h),(a,b,c,d,e,f,g,h),return) SDL_DYNAPI_PROC(SDL_Surface*,SDL_ConvertSurface,(SDL_Surface *a, const SDL_PixelFormat *b),(a,b),return) SDL_DYNAPI_PROC(SDL_Surface*,SDL_ConvertSurfaceFormat,(SDL_Surface *a, Uint32 b),(a,b),return) -SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_CreateAudioStream,(SDL_AudioFormat a, int b, int c, SDL_AudioFormat d, int e, int f),(a,b,c,d,e,f),return) SDL_DYNAPI_PROC(SDL_Cursor*,SDL_CreateColorCursor,(SDL_Surface *a, int b, int c),(a,b,c),return) SDL_DYNAPI_PROC(SDL_Condition*,SDL_CreateCondition,(void),(),return) SDL_DYNAPI_PROC(SDL_Cursor*,SDL_CreateCursor,(const Uint8 *a, const Uint8 *b, int c, int d, int e, int f),(a,b,c,d,e,f),return) @@ -181,8 +177,6 @@ SDL_DYNAPI_PROC(void,SDL_DelEventWatch,(SDL_EventFilter a, void *b),(a,b),) SDL_DYNAPI_PROC(void,SDL_DelHintCallback,(const char *a, SDL_HintCallback b, void *c),(a,b,c),) SDL_DYNAPI_PROC(void,SDL_Delay,(Uint32 a),(a),) SDL_DYNAPI_PROC(void,SDL_DelayNS,(Uint64 a),(a),) -SDL_DYNAPI_PROC(Uint32,SDL_DequeueAudio,(SDL_AudioDeviceID a, void *b, Uint32 c),(a,b,c),return) -SDL_DYNAPI_PROC(void,SDL_DestroyAudioStream,(SDL_AudioStream *a),(a),) SDL_DYNAPI_PROC(void,SDL_DestroyCondition,(SDL_Condition *a),(a),) SDL_DYNAPI_PROC(void,SDL_DestroyMutex,(SDL_Mutex *a),(a),) SDL_DYNAPI_PROC(void,SDL_DestroyPalette,(SDL_Palette *a),(a),) @@ -208,7 +202,6 @@ SDL_DYNAPI_PROC(int,SDL_FillSurfaceRect,(SDL_Surface *a, const SDL_Rect *b, Uint SDL_DYNAPI_PROC(int,SDL_FillSurfaceRects,(SDL_Surface *a, const SDL_Rect *b, int c, Uint32 d),(a,b,c,d),return) SDL_DYNAPI_PROC(void,SDL_FilterEvents,(SDL_EventFilter a, void *b),(a,b),) SDL_DYNAPI_PROC(int,SDL_FlashWindow,(SDL_Window *a, SDL_FlashOperation b),(a,b),return) -SDL_DYNAPI_PROC(int,SDL_FlushAudioStream,(SDL_AudioStream *a),(a),return) SDL_DYNAPI_PROC(void,SDL_FlushEvent,(Uint32 a),(a),) SDL_DYNAPI_PROC(void,SDL_FlushEvents,(Uint32 a, Uint32 b),(a,b),) SDL_DYNAPI_PROC(void,SDL_DestroyCursor,(SDL_Cursor *a),(a),) @@ -243,22 +236,14 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadHasSensor,(SDL_Gamepad *a, SDL_SensorType b) SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadSensorEnabled,(SDL_Gamepad *a, SDL_SensorType b),(a,b),return) SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetAssertionHandler,(void **a),(a),return) SDL_DYNAPI_PROC(const SDL_AssertData*,SDL_GetAssertionReport,(void),(),return) -SDL_DYNAPI_PROC(const char*,SDL_GetAudioDeviceName,(int a, int b),(a,b),return) -SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceSpec,(int a, int b, SDL_AudioSpec *c),(a,b,c),return) -SDL_DYNAPI_PROC(SDL_AudioStatus,SDL_GetAudioDeviceStatus,(SDL_AudioDeviceID a),(a),return) -SDL_DYNAPI_PROC(const char*,SDL_GetAudioDriver,(int a),(a),return) -SDL_DYNAPI_PROC(int,SDL_GetAudioStreamAvailable,(SDL_AudioStream *a),(a),return) -SDL_DYNAPI_PROC(int,SDL_GetAudioStreamData,(SDL_AudioStream *a, void *b, int c),(a,b,c),return) SDL_DYNAPI_PROC(char*,SDL_GetBasePath,(void),(),return) SDL_DYNAPI_PROC(int,SDL_GetCPUCacheLineSize,(void),(),return) SDL_DYNAPI_PROC(int,SDL_GetCPUCount,(void),(),return) SDL_DYNAPI_PROC(char*,SDL_GetClipboardText,(void),(),return) -SDL_DYNAPI_PROC(const char*,SDL_GetCurrentAudioDriver,(void),(),return) SDL_DYNAPI_PROC(const SDL_DisplayMode*,SDL_GetCurrentDisplayMode,(SDL_DisplayID a),(a),return) SDL_DYNAPI_PROC(const char*,SDL_GetCurrentVideoDriver,(void),(),return) SDL_DYNAPI_PROC(SDL_Cursor*,SDL_GetCursor,(void),(),return) SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetDefaultAssertionHandler,(void),(),return) -SDL_DYNAPI_PROC(int,SDL_GetDefaultAudioInfo,(char **a, SDL_AudioSpec *b, int c),(a,b,c),return) SDL_DYNAPI_PROC(SDL_Cursor*,SDL_GetDefaultCursor,(void),(),return) SDL_DYNAPI_PROC(const SDL_DisplayMode*,SDL_GetDesktopDisplayMode,(SDL_DisplayID a),(a),return) SDL_DYNAPI_PROC(int,SDL_GetDisplayBounds,(SDL_DisplayID a, SDL_Rect *b),(a,b),return) @@ -355,8 +340,6 @@ SDL_DYNAPI_PROC(SDL_Keymod,SDL_GetModState,(void),(),return) SDL_DYNAPI_PROC(SDL_Window*,SDL_GetMouseFocus,(void),(),return) SDL_DYNAPI_PROC(Uint32,SDL_GetMouseState,(float *a, float *b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_GetNumAllocations,(void),(),return) -SDL_DYNAPI_PROC(int,SDL_GetNumAudioDevices,(int a),(a),return) -SDL_DYNAPI_PROC(int,SDL_GetNumAudioDrivers,(void),(),return) SDL_DYNAPI_PROC(int,SDL_GetNumGamepadMappings,(void),(),return) SDL_DYNAPI_PROC(int,SDL_GetNumJoystickAxes,(SDL_Joystick *a),(a),return) SDL_DYNAPI_PROC(int,SDL_GetNumJoystickButtons,(SDL_Joystick *a),(a),return) @@ -375,7 +358,6 @@ SDL_DYNAPI_PROC(SDL_PowerState,SDL_GetPowerInfo,(int *a, int *b),(a,b),return) SDL_DYNAPI_PROC(char*,SDL_GetPrefPath,(const char *a, const char *b),(a,b),return) SDL_DYNAPI_PROC(SDL_Locale*,SDL_GetPreferredLocales,(void),(),return) SDL_DYNAPI_PROC(char*,SDL_GetPrimarySelectionText,(void),(),return) -SDL_DYNAPI_PROC(Uint32,SDL_GetQueuedAudioSize,(SDL_AudioDeviceID a),(a),return) SDL_DYNAPI_PROC(void,SDL_GetRGB,(Uint32 a, const SDL_PixelFormat *b, Uint8 *c, Uint8 *d, Uint8 *e),(a,b,c,d,e),) SDL_DYNAPI_PROC(void,SDL_GetRGBA,(Uint32 a, const SDL_PixelFormat *b, Uint8 *c, Uint8 *d, Uint8 *e, Uint8 *f),(a,b,c,d,e,f),) SDL_DYNAPI_PROC(SDL_bool,SDL_GetRectAndLineIntersection,(const SDL_Rect *a, int *b, int *c, int *d, int *e),(a,b,c,d,e),return) @@ -528,8 +510,6 @@ SDL_DYNAPI_PROC(void*,SDL_LoadFile,(const char *a, size_t *b),(a,b),return) SDL_DYNAPI_PROC(void*,SDL_LoadFile_RW,(SDL_RWops *a, size_t *b, int c),(a,b,c),return) SDL_DYNAPI_PROC(SDL_FunctionPointer,SDL_LoadFunction,(void *a, const char *b),(a,b),return) SDL_DYNAPI_PROC(void*,SDL_LoadObject,(const char *a),(a),return) -SDL_DYNAPI_PROC(SDL_AudioSpec*,SDL_LoadWAV_RW,(SDL_RWops *a, int b, SDL_AudioSpec *c, Uint8 **d, Uint32 *e),(a,b,c,d,e),return) -SDL_DYNAPI_PROC(int,SDL_LockAudioDevice,(SDL_AudioDeviceID a),(a),return) SDL_DYNAPI_PROC(void,SDL_LockJoysticks,(void),(),) SDL_DYNAPI_PROC(int,SDL_LockMutex,(SDL_Mutex *a),(a),return) SDL_DYNAPI_PROC(int,SDL_LockSurface,(SDL_Surface *a),(a),return) @@ -551,7 +531,6 @@ SDL_DYNAPI_PROC(SDL_MetalView,SDL_Metal_CreateView,(SDL_Window *a),(a),return) SDL_DYNAPI_PROC(void,SDL_Metal_DestroyView,(SDL_MetalView a),(a),) SDL_DYNAPI_PROC(void*,SDL_Metal_GetLayer,(SDL_MetalView a),(a),return) SDL_DYNAPI_PROC(int,SDL_MinimizeWindow,(SDL_Window *a),(a),return) -SDL_DYNAPI_PROC(int,SDL_MixAudioFormat,(Uint8 *a, const Uint8 *b, SDL_AudioFormat c, Uint32 d, int e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(int,SDL_MouseIsHaptic,(void),(),return) SDL_DYNAPI_PROC(int,SDL_NumHaptics,(void),(),return) SDL_DYNAPI_PROC(void,SDL_OnApplicationDidBecomeActive,(void),(),) @@ -560,20 +539,16 @@ SDL_DYNAPI_PROC(void,SDL_OnApplicationDidReceiveMemoryWarning,(void),(),) SDL_DYNAPI_PROC(void,SDL_OnApplicationWillEnterForeground,(void),(),) SDL_DYNAPI_PROC(void,SDL_OnApplicationWillResignActive,(void),(),) SDL_DYNAPI_PROC(void,SDL_OnApplicationWillTerminate,(void),(),) -SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_OpenAudioDevice,(const char *a, int b, const SDL_AudioSpec *c, SDL_AudioSpec *d, int e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(SDL_Gamepad*,SDL_OpenGamepad,(SDL_JoystickID a),(a),return) SDL_DYNAPI_PROC(SDL_Joystick*,SDL_OpenJoystick,(SDL_JoystickID a),(a),return) SDL_DYNAPI_PROC(SDL_Sensor*,SDL_OpenSensor,(SDL_SensorID a),(a),return) SDL_DYNAPI_PROC(int,SDL_OpenURL,(const char *a),(a),return) -SDL_DYNAPI_PROC(int,SDL_PauseAudioDevice,(SDL_AudioDeviceID a),(a),return) SDL_DYNAPI_PROC(int,SDL_PeepEvents,(SDL_Event *a, int b, SDL_eventaction c, Uint32 d, Uint32 e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(int,SDL_PollEvent,(SDL_Event *a),(a),return) SDL_DYNAPI_PROC(int,SDL_PremultiplyAlpha,(int a, int b, Uint32 c, const void *d, int e, Uint32 f, void *g, int h),(a,b,c,d,e,f,g,h),return) SDL_DYNAPI_PROC(void,SDL_PumpEvents,(void),(),) SDL_DYNAPI_PROC(int,SDL_PushEvent,(SDL_Event *a),(a),return) -SDL_DYNAPI_PROC(int,SDL_PutAudioStreamData,(SDL_AudioStream *a, const void *b, int c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_QueryTexture,(SDL_Texture *a, Uint32 *b, int *c, int *d, int *e),(a,b,c,d,e),return) -SDL_DYNAPI_PROC(int,SDL_QueueAudio,(SDL_AudioDeviceID a, const void *b, Uint32 c),(a,b,c),return) SDL_DYNAPI_PROC(void,SDL_Quit,(void),(),) SDL_DYNAPI_PROC(void,SDL_QuitSubSystem,(Uint32 a),(a),) SDL_DYNAPI_PROC(SDL_RWops*,SDL_RWFromConstMem,(const void *a, int b),(a,b),return) @@ -722,7 +697,6 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_TextInputShown,(void),(),return) SDL_DYNAPI_PROC(SDL_threadID,SDL_ThreadID,(void),(),return) SDL_DYNAPI_PROC(int,SDL_TryLockMutex,(SDL_Mutex *a),(a),return) SDL_DYNAPI_PROC(void,SDL_UnloadObject,(void *a),(a),) -SDL_DYNAPI_PROC(void,SDL_UnlockAudioDevice,(SDL_AudioDeviceID a),(a),) SDL_DYNAPI_PROC(void,SDL_UnlockJoysticks,(void),(),) SDL_DYNAPI_PROC(int,SDL_UnlockMutex,(SDL_Mutex *a),(a),return) SDL_DYNAPI_PROC(void,SDL_UnlockSurface,(SDL_Surface *a),(a),) @@ -897,10 +871,8 @@ SDL_DYNAPI_PROC(wchar_t*,SDL_wcsstr,(const wchar_t *a, const wchar_t *b),(a,b),r SDL_DYNAPI_PROC(double,SDL_modf,(double a, double *b),(a,b),return) SDL_DYNAPI_PROC(float,SDL_modff,(float a, float *b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_GetRenderVSync,(SDL_Renderer *a, int *b),(a,b),return) -SDL_DYNAPI_PROC(int,SDL_PlayAudioDevice,(SDL_AudioDeviceID a),(a),return) SDL_DYNAPI_PROC(void*,SDL_aligned_alloc,(size_t a, size_t b),(a,b),return) SDL_DYNAPI_PROC(void,SDL_aligned_free,(void *a),(a),) -SDL_DYNAPI_PROC(int,SDL_ConvertAudioSamples,(SDL_AudioFormat a, Uint8 b, int c, const Uint8 *d, int e, SDL_AudioFormat f, Uint8 g, int h, Uint8 **i, int *j),(a,b,c,d,e,f,g,h,i,j),return) SDL_DYNAPI_PROC(SDL_DisplayID*,SDL_GetDisplays,(int *a),(a),return) SDL_DYNAPI_PROC(SDL_DisplayID,SDL_GetPrimaryDisplay,(void),(),return) SDL_DYNAPI_PROC(const SDL_DisplayMode**,SDL_GetFullscreenDisplayModes,(SDL_DisplayID a, int *b),(a,b),return) @@ -917,8 +889,6 @@ SDL_DYNAPI_PROC(int,SDL_SetClipboardData,(SDL_ClipboardDataCallback a, size_t b, SDL_DYNAPI_PROC(void*,SDL_GetClipboardUserdata,(void),(),return) SDL_DYNAPI_PROC(void*,SDL_GetClipboardData,(size_t *a, const char *b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_HasClipboardData,(const char *a),(a),return) -SDL_DYNAPI_PROC(int,SDL_GetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioFormat *b, int *c, int *d, SDL_AudioFormat *e, int *f, int *g),(a,b,c,d,e,f,g),return) -SDL_DYNAPI_PROC(int,SDL_SetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioFormat b, int c, int d, SDL_AudioFormat e, int f, int g),(a,b,c,d,e,f,g),return) SDL_DYNAPI_PROC(SDL_RWLock*,SDL_CreateRWLock,(void),(),return) SDL_DYNAPI_PROC(int,SDL_LockRWLockForReading,(SDL_RWLock *a),(a),return) SDL_DYNAPI_PROC(int,SDL_LockRWLockForWriting,(SDL_RWLock *a),(a),return) @@ -935,3 +905,30 @@ SDL_DYNAPI_PROC(int,SDL_vswprintf,(wchar_t *a, size_t b, const wchar_t *c, va_li SDL_DYNAPI_PROC(int,SDL_hid_get_input_report,(SDL_hid_device *a, unsigned char *b, size_t c),(a,b,c),return) SDL_DYNAPI_PROC(SDL_hid_device_info*,SDL_hid_get_device_info,(SDL_hid_device *a),(a),return) SDL_DYNAPI_PROC(int,SDL_hid_get_report_descriptor,(SDL_hid_device *a, unsigned char *b, size_t c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_GetNumAudioDrivers,(void),(),return) +SDL_DYNAPI_PROC(const char*,SDL_GetAudioDriver,(int a),(a),return) +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_AudioFormat *b, int *c, int *d),(a,b,c,d),return) +SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_OpenAudioDevice,(SDL_AudioDeviceID a, SDL_AudioFormat b, int c, int d),(a,b,c,d),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) +SDL_DYNAPI_PROC(int,SDL_BindAudioStream,(SDL_AudioDeviceID a, SDL_AudioStream *b),(a,b),return) +SDL_DYNAPI_PROC(void,SDL_UnbindAudioStreams,(SDL_AudioStream **a, int b),(a,b),) +SDL_DYNAPI_PROC(void,SDL_UnbindAudioStream,(SDL_AudioStream *a),(a),) +SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_CreateAudioStream,(SDL_AudioFormat a, int b, int c, SDL_AudioFormat d, int e, int f),(a,b,c,d,e,f),return) +SDL_DYNAPI_PROC(int,SDL_GetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioFormat *b, int *c, int *d, SDL_AudioFormat *e, int *f, int *g),(a,b,c,d,e,f,g),return) +SDL_DYNAPI_PROC(int,SDL_SetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioFormat b, int c, int d, SDL_AudioFormat e, int f, int g),(a,b,c,d,e,f,g),return) +SDL_DYNAPI_PROC(int,SDL_PutAudioStreamData,(SDL_AudioStream *a, const void *b, int c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_GetAudioStreamData,(SDL_AudioStream *a, void *b, int c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_GetAudioStreamAvailable,(SDL_AudioStream *a),(a),return) +SDL_DYNAPI_PROC(int,SDL_FlushAudioStream,(SDL_AudioStream *a),(a),return) +SDL_DYNAPI_PROC(int,SDL_ClearAudioStream,(SDL_AudioStream *a),(a),return) +SDL_DYNAPI_PROC(void,SDL_DestroyAudioStream,(SDL_AudioStream *a),(a),) +SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_CreateAndBindAudioStream,(SDL_AudioDeviceID a, SDL_AudioFormat b, int c, int d),(a,b,c,d),return) +SDL_DYNAPI_PROC(int,SDL_LoadWAV_RW,(SDL_RWops *a, int b, SDL_AudioFormat *c, int *d, int *e, Uint8 **f, Uint32 *g),(a,b,c,d,e,f,g),return) +SDL_DYNAPI_PROC(int,SDL_MixAudioFormat,(Uint8 *a, const Uint8 *b, SDL_AudioFormat c, Uint32 d, int e),(a,b,c,d,e),return) +SDL_DYNAPI_PROC(int,SDL_ConvertAudioSamples,(SDL_AudioFormat a, int b, int c, const Uint8 *d, int e, SDL_AudioFormat f, int g, int h, Uint8 **i, int *j),(a,b,c,d,e,f,g,h,i,j),return) +SDL_DYNAPI_PROC(int,SDL_GetSilenceValueForFormat,(SDL_AudioFormat a),(a),return) diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index 7533d0446fb400..7db4189105a4a6 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -95,10 +95,9 @@ SDLTest_CommonState *SDLTest_CommonCreateState(char **argv, Uint32 flags) state->logical_presentation = SDL_LOGICAL_PRESENTATION_DISABLED; state->logical_scale_mode = SDL_SCALEMODE_LINEAR; state->num_windows = 1; - state->audiospec.freq = 22050; - state->audiospec.format = SDL_AUDIO_S16; - state->audiospec.channels = 2; - state->audiospec.samples = 2048; + state->audio_freq = 22050; + state->audio_format = SDL_AUDIO_S16; + state->audio_channels = 2; /* Set some very sane GL defaults */ state->gl_red_size = 8; @@ -582,7 +581,7 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index) if (!argv[index]) { return -1; } - state->audiospec.freq = SDL_atoi(argv[index]); + state->audio_freq = SDL_atoi(argv[index]); return 2; } if (SDL_strcasecmp(argv[index], "--format") == 0) { @@ -591,23 +590,23 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index) return -1; } if (SDL_strcasecmp(argv[index], "U8") == 0) { - state->audiospec.format = SDL_AUDIO_U8; + state->audio_format = SDL_AUDIO_U8; return 2; } if (SDL_strcasecmp(argv[index], "S8") == 0) { - state->audiospec.format = SDL_AUDIO_S8; + state->audio_format = SDL_AUDIO_S8; return 2; } if (SDL_strcasecmp(argv[index], "S16") == 0) { - state->audiospec.format = SDL_AUDIO_S16; + state->audio_format = SDL_AUDIO_S16; return 2; } if (SDL_strcasecmp(argv[index], "S16LE") == 0) { - state->audiospec.format = SDL_AUDIO_S16LSB; + state->audio_format = SDL_AUDIO_S16LSB; return 2; } if (SDL_strcasecmp(argv[index], "S16BE") == 0) { - state->audiospec.format = SDL_AUDIO_S16MSB; + state->audio_format = SDL_AUDIO_S16MSB; return 2; } @@ -620,15 +619,7 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index) if (!argv[index]) { return -1; } - state->audiospec.channels = (Uint8) SDL_atoi(argv[index]); - return 2; - } - if (SDL_strcasecmp(argv[index], "--samples") == 0) { - ++index; - if (!argv[index]) { - return -1; - } - state->audiospec.samples = (Uint16) SDL_atoi(argv[index]); + state->audio_channels = (Uint8) SDL_atoi(argv[index]); return 2; } } @@ -1434,7 +1425,7 @@ SDL_bool SDLTest_CommonInit(SDLTest_CommonState *state) SDL_GetCurrentAudioDriver()); } - state->audio_id = SDL_OpenAudioDevice(NULL, 0, &state->audiospec, NULL, 0); + state->audio_id = SDL_OpenAudioDevice(0, state->audio_format, state->audio_channels, state->audio_freq); if (!state->audio_id) { SDL_Log("Couldn't open audio: %s\n", SDL_GetError()); return SDL_FALSE; diff --git a/test/loopwave.c b/test/loopwave.c index ac5f84d3522a20..871fc8eec2aa0b 100644 --- a/test/loopwave.c +++ b/test/loopwave.c @@ -28,13 +28,15 @@ static struct { - SDL_AudioSpec spec; + SDL_AudioFormat fmt; + int channels; + int freq; Uint8 *sound; /* Pointer to wave data */ Uint32 soundlen; /* Length of wave data */ - int soundpos; /* Current play position */ } wave; static SDL_AudioDeviceID device; +static SDL_AudioStream *stream; /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ static void @@ -51,6 +53,8 @@ static void close_audio(void) { if (device != 0) { + SDL_DestroyAudioStream(stream); + stream = NULL; SDL_CloseAudioDevice(device); device = 0; } @@ -59,16 +63,21 @@ close_audio(void) static void open_audio(void) { - /* Initialize fillerup() variables */ - device = SDL_OpenAudioDevice(NULL, SDL_FALSE, &wave.spec, NULL, 0); + SDL_AudioDeviceID *devices = SDL_GetAudioOutputDevices(NULL); + device = devices ? SDL_OpenAudioDevice(devices[0], wave.fmt, wave.channels, wave.freq) : 0; + SDL_free(devices); if (!device) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", SDL_GetError()); SDL_free(wave.sound); quit(2); } - - /* Let the audio run */ - SDL_PlayAudioDevice(device); + stream = SDL_CreateAndBindAudioStream(device, wave.fmt, wave.channels, wave.freq); + if (!stream) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create audio stream: %s\n", SDL_GetError()); + SDL_CloseAudioDevice(device); + SDL_free(wave.sound); + quit(2); + } } #ifndef __EMSCRIPTEN__ @@ -79,36 +88,24 @@ static void reopen_audio(void) } #endif -static void SDLCALL -fillerup(void *unused, Uint8 *stream, int len) + +static int done = 0; + +static void fillerup(void) { - Uint8 *waveptr; - int waveleft; - - /* Set up the pointers */ - waveptr = wave.sound + wave.soundpos; - waveleft = wave.soundlen - wave.soundpos; - - /* Go! */ - while (waveleft <= len) { - SDL_memcpy(stream, waveptr, waveleft); - stream += waveleft; - len -= waveleft; - waveptr = wave.sound; - waveleft = wave.soundlen; - wave.soundpos = 0; + if (SDL_GetAudioStreamAvailable(stream) < (wave.soundlen / 2)) { + SDL_PutAudioStreamData(stream, wave.sound, wave.soundlen); } - SDL_memcpy(stream, waveptr, len); - wave.soundpos += len; } -static int done = 0; #ifdef __EMSCRIPTEN__ static void loop(void) { if (done || (SDL_GetAudioDeviceStatus(device) != SDL_AUDIO_PLAYING)) { emscripten_cancel_main_loop(); + } else { + fillerup(); } } #endif @@ -162,13 +159,11 @@ int main(int argc, char *argv[]) } /* Load the wave file into memory */ - if (SDL_LoadWAV(filename, &wave.spec, &wave.sound, &wave.soundlen) == NULL) { + if (SDL_LoadWAV(filename, &wave.fmt, &wave.channels, &wave.freq, &wave.sound, &wave.soundlen) == -1) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename, SDL_GetError()); quit(1); } - wave.spec.callback = fillerup; - /* Show the list of available drivers */ SDL_Log("Available audio drivers:"); for (i = 0; i < SDL_GetNumAudioDrivers(); ++i) { @@ -196,6 +191,8 @@ int main(int argc, char *argv[]) reopen_audio(); } } + + fillerup(); SDL_Delay(100); } #endif