From 30f41c02aec763d32e62351452da9ef582bc3472 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 6 Mar 2026 13:30:59 -0800 Subject: Move contrib libraries to contrib repo --- contrib/SDL-3.2.8/src/audio/SDL_audio.c | 2534 -------------------- contrib/SDL-3.2.8/src/audio/SDL_audio_c.h | 27 - .../src/audio/SDL_audio_channel_converters.h | 1068 --------- contrib/SDL-3.2.8/src/audio/SDL_audiocvt.c | 1381 ----------- contrib/SDL-3.2.8/src/audio/SDL_audiodev.c | 124 - contrib/SDL-3.2.8/src/audio/SDL_audiodev_c.h | 41 - contrib/SDL-3.2.8/src/audio/SDL_audioqueue.c | 652 ----- contrib/SDL-3.2.8/src/audio/SDL_audioqueue.h | 79 - contrib/SDL-3.2.8/src/audio/SDL_audioresample.c | 706 ------ contrib/SDL-3.2.8/src/audio/SDL_audioresample.h | 43 - contrib/SDL-3.2.8/src/audio/SDL_audiotypecvt.c | 925 ------- contrib/SDL-3.2.8/src/audio/SDL_mixer.c | 290 --- contrib/SDL-3.2.8/src/audio/SDL_sysaudio.h | 392 --- contrib/SDL-3.2.8/src/audio/SDL_wave.c | 2151 ----------------- contrib/SDL-3.2.8/src/audio/SDL_wave.h | 151 -- contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.c | 551 ----- contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.h | 38 - .../SDL-3.2.8/src/audio/aaudio/SDL_aaudiofuncs.h | 82 - contrib/SDL-3.2.8/src/audio/alsa/SDL_alsa_audio.c | 1519 ------------ contrib/SDL-3.2.8/src/audio/alsa/SDL_alsa_audio.h | 41 - .../SDL-3.2.8/src/audio/coreaudio/SDL_coreaudio.h | 68 - .../SDL-3.2.8/src/audio/coreaudio/SDL_coreaudio.m | 1040 -------- .../src/audio/directsound/SDL_directsound.c | 680 ------ .../src/audio/directsound/SDL_directsound.h | 43 - contrib/SDL-3.2.8/src/audio/disk/SDL_diskaudio.c | 171 -- contrib/SDL-3.2.8/src/audio/disk/SDL_diskaudio.h | 36 - contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.c | 303 --- contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.h | 37 - contrib/SDL-3.2.8/src/audio/dummy/SDL_dummyaudio.c | 135 -- contrib/SDL-3.2.8/src/audio/dummy/SDL_dummyaudio.h | 34 - .../src/audio/emscripten/SDL_emscriptenaudio.c | 359 --- .../src/audio/emscripten/SDL_emscriptenaudio.h | 33 - .../SDL-3.2.8/src/audio/haiku/SDL_haikuaudio.cc | 222 -- contrib/SDL-3.2.8/src/audio/haiku/SDL_haikuaudio.h | 35 - contrib/SDL-3.2.8/src/audio/jack/SDL_jackaudio.c | 435 ---- contrib/SDL-3.2.8/src/audio/jack/SDL_jackaudio.h | 35 - contrib/SDL-3.2.8/src/audio/n3ds/SDL_n3dsaudio.c | 287 --- contrib/SDL-3.2.8/src/audio/n3ds/SDL_n3dsaudio.h | 40 - .../SDL-3.2.8/src/audio/netbsd/SDL_netbsdaudio.c | 328 --- .../SDL-3.2.8/src/audio/netbsd/SDL_netbsdaudio.h | 44 - .../SDL-3.2.8/src/audio/openslES/SDL_openslES.c | 807 ------- .../SDL-3.2.8/src/audio/openslES/SDL_openslES.h | 38 - .../SDL-3.2.8/src/audio/pipewire/SDL_pipewire.c | 1349 ----------- .../SDL-3.2.8/src/audio/pipewire/SDL_pipewire.h | 43 - contrib/SDL-3.2.8/src/audio/ps2/SDL_ps2audio.c | 159 -- contrib/SDL-3.2.8/src/audio/ps2/SDL_ps2audio.h | 42 - contrib/SDL-3.2.8/src/audio/psp/SDL_pspaudio.c | 183 -- contrib/SDL-3.2.8/src/audio/psp/SDL_pspaudio.h | 41 - .../src/audio/pulseaudio/SDL_pulseaudio.c | 1037 -------- .../src/audio/pulseaudio/SDL_pulseaudio.h | 44 - contrib/SDL-3.2.8/src/audio/qnx/SDL_qsa_audio.c | 451 ---- contrib/SDL-3.2.8/src/audio/qnx/SDL_qsa_audio.h | 40 - contrib/SDL-3.2.8/src/audio/sndio/SDL_sndioaudio.c | 356 --- contrib/SDL-3.2.8/src/audio/sndio/SDL_sndioaudio.h | 38 - contrib/SDL-3.2.8/src/audio/vita/SDL_vitaaudio.c | 238 -- contrib/SDL-3.2.8/src/audio/vita/SDL_vitaaudio.h | 41 - contrib/SDL-3.2.8/src/audio/wasapi/SDL_wasapi.c | 963 -------- contrib/SDL-3.2.8/src/audio/wasapi/SDL_wasapi.h | 61 - 58 files changed, 23091 deletions(-) delete mode 100644 contrib/SDL-3.2.8/src/audio/SDL_audio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/SDL_audio_c.h delete mode 100644 contrib/SDL-3.2.8/src/audio/SDL_audio_channel_converters.h delete mode 100644 contrib/SDL-3.2.8/src/audio/SDL_audiocvt.c delete mode 100644 contrib/SDL-3.2.8/src/audio/SDL_audiodev.c delete mode 100644 contrib/SDL-3.2.8/src/audio/SDL_audiodev_c.h delete mode 100644 contrib/SDL-3.2.8/src/audio/SDL_audioqueue.c delete mode 100644 contrib/SDL-3.2.8/src/audio/SDL_audioqueue.h delete mode 100644 contrib/SDL-3.2.8/src/audio/SDL_audioresample.c delete mode 100644 contrib/SDL-3.2.8/src/audio/SDL_audioresample.h delete mode 100644 contrib/SDL-3.2.8/src/audio/SDL_audiotypecvt.c delete mode 100644 contrib/SDL-3.2.8/src/audio/SDL_mixer.c delete mode 100644 contrib/SDL-3.2.8/src/audio/SDL_sysaudio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/SDL_wave.c delete mode 100644 contrib/SDL-3.2.8/src/audio/SDL_wave.h delete mode 100644 contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudiofuncs.h delete mode 100644 contrib/SDL-3.2.8/src/audio/alsa/SDL_alsa_audio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/alsa/SDL_alsa_audio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/coreaudio/SDL_coreaudio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/coreaudio/SDL_coreaudio.m delete mode 100644 contrib/SDL-3.2.8/src/audio/directsound/SDL_directsound.c delete mode 100644 contrib/SDL-3.2.8/src/audio/directsound/SDL_directsound.h delete mode 100644 contrib/SDL-3.2.8/src/audio/disk/SDL_diskaudio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/disk/SDL_diskaudio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/dummy/SDL_dummyaudio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/dummy/SDL_dummyaudio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/emscripten/SDL_emscriptenaudio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/emscripten/SDL_emscriptenaudio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/haiku/SDL_haikuaudio.cc delete mode 100644 contrib/SDL-3.2.8/src/audio/haiku/SDL_haikuaudio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/jack/SDL_jackaudio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/jack/SDL_jackaudio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/n3ds/SDL_n3dsaudio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/n3ds/SDL_n3dsaudio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/netbsd/SDL_netbsdaudio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/netbsd/SDL_netbsdaudio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.c delete mode 100644 contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.h delete mode 100644 contrib/SDL-3.2.8/src/audio/pipewire/SDL_pipewire.c delete mode 100644 contrib/SDL-3.2.8/src/audio/pipewire/SDL_pipewire.h delete mode 100644 contrib/SDL-3.2.8/src/audio/ps2/SDL_ps2audio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/ps2/SDL_ps2audio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/psp/SDL_pspaudio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/psp/SDL_pspaudio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/pulseaudio/SDL_pulseaudio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/pulseaudio/SDL_pulseaudio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/qnx/SDL_qsa_audio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/qnx/SDL_qsa_audio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/sndio/SDL_sndioaudio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/sndio/SDL_sndioaudio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/vita/SDL_vitaaudio.c delete mode 100644 contrib/SDL-3.2.8/src/audio/vita/SDL_vitaaudio.h delete mode 100644 contrib/SDL-3.2.8/src/audio/wasapi/SDL_wasapi.c delete mode 100644 contrib/SDL-3.2.8/src/audio/wasapi/SDL_wasapi.h (limited to 'contrib/SDL-3.2.8/src/audio') diff --git a/contrib/SDL-3.2.8/src/audio/SDL_audio.c b/contrib/SDL-3.2.8/src/audio/SDL_audio.c deleted file mode 100644 index 583a159..0000000 --- a/contrib/SDL-3.2.8/src/audio/SDL_audio.c +++ /dev/null @@ -1,2534 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#include "SDL_audio_c.h" -#include "SDL_sysaudio.h" -#include "../thread/SDL_systhread.h" - -// Available audio drivers -static const AudioBootStrap *const bootstrap[] = { -#ifdef SDL_AUDIO_DRIVER_PRIVATE - &PRIVATEAUDIO_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO -#ifdef SDL_AUDIO_DRIVER_PIPEWIRE - &PIPEWIRE_PREFERRED_bootstrap, -#endif - &PULSEAUDIO_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_PIPEWIRE - &PIPEWIRE_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_ALSA - &ALSA_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_SNDIO - &SNDIO_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_NETBSD - &NETBSDAUDIO_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_WASAPI - &WASAPI_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_DSOUND - &DSOUND_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_HAIKU - &HAIKUAUDIO_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_COREAUDIO - &COREAUDIO_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_AAUDIO - &AAUDIO_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_OPENSLES - &OPENSLES_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_PS2 - &PS2AUDIO_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_PSP - &PSPAUDIO_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_VITA - &VITAAUD_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_N3DS - &N3DSAUDIO_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_EMSCRIPTEN - &EMSCRIPTENAUDIO_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_JACK - &JACK_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_OSS - &DSP_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_QNX - &QSAAUDIO_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_DISK - &DISKAUDIO_bootstrap, -#endif -#ifdef SDL_AUDIO_DRIVER_DUMMY - &DUMMYAUDIO_bootstrap, -#endif - NULL -}; - -static SDL_AudioDriver current_audio; - -// Deduplicated list of audio bootstrap drivers. -static const AudioBootStrap *deduped_bootstrap[SDL_arraysize(bootstrap) - 1]; - -int SDL_GetNumAudioDrivers(void) -{ - static int num_drivers = -1; - - if (num_drivers >= 0) { - return num_drivers; - } - - num_drivers = 0; - - // Build a list of unique audio drivers. - for (int i = 0; bootstrap[i] != NULL; ++i) { - bool duplicate = false; - for (int j = 0; j < i; ++j) { - if (SDL_strcmp(bootstrap[i]->name, bootstrap[j]->name) == 0) { - duplicate = true; - break; - } - } - - if (!duplicate) { - deduped_bootstrap[num_drivers++] = bootstrap[i]; - } - } - - return num_drivers; -} - -const char *SDL_GetAudioDriver(int index) -{ - if (index >= 0 && index < SDL_GetNumAudioDrivers()) { - return deduped_bootstrap[index]->name; - } - SDL_InvalidParamError("index"); - return NULL; -} - -const char *SDL_GetCurrentAudioDriver(void) -{ - return current_audio.name; -} - -int SDL_GetDefaultSampleFramesFromFreq(const int freq) -{ - const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES); - if (hint) { - const int val = SDL_atoi(hint); - if (val > 0) { - return val; - } - } - - if (freq <= 22050) { - return 512; - } else if (freq <= 48000) { - return 1024; - } else if (freq <= 96000) { - return 2048; - } else { - return 4096; - } -} - -int *SDL_ChannelMapDup(const int *origchmap, int channels) -{ - const size_t chmaplen = sizeof (*origchmap) * channels; - int *chmap = (int *)SDL_malloc(chmaplen); - if (chmap) { - SDL_memcpy(chmap, origchmap, chmaplen); - } - return chmap; -} - -void OnAudioStreamCreated(SDL_AudioStream *stream) -{ - SDL_assert(stream != NULL); - - // NOTE that you can create an audio stream without initializing the audio subsystem, - // but it will not be automatically destroyed during a later call to SDL_Quit! - // You must explicitly destroy it yourself! - if (current_audio.device_hash_lock) { - // this isn't really part of the "device list" but it's a convenient lock to use here. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); - if (current_audio.existing_streams) { - current_audio.existing_streams->prev = stream; - } - stream->prev = NULL; - stream->next = current_audio.existing_streams; - current_audio.existing_streams = stream; - SDL_UnlockRWLock(current_audio.device_hash_lock); - } -} - -void OnAudioStreamDestroy(SDL_AudioStream *stream) -{ - SDL_assert(stream != NULL); - - // NOTE that you can create an audio stream without initializing the audio subsystem, - // but it will not be automatically destroyed during a later call to SDL_Quit! - // You must explicitly destroy it yourself! - if (current_audio.device_hash_lock) { - // this isn't really part of the "device list" but it's a convenient lock to use here. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); - if (stream->prev) { - stream->prev->next = stream->next; - } - if (stream->next) { - stream->next->prev = stream->prev; - } - if (stream == current_audio.existing_streams) { - current_audio.existing_streams = stream->next; - } - SDL_UnlockRWLock(current_audio.device_hash_lock); - } -} - -// device should be locked when calling this. -static bool AudioDeviceCanUseSimpleCopy(SDL_AudioDevice *device) -{ - SDL_assert(device != NULL); - return ( - device->logical_devices && // there's a logical device - !device->logical_devices->next && // there's only _ONE_ logical device - !device->logical_devices->postmix && // there isn't a postmix callback - device->logical_devices->bound_streams && // there's a bound stream - !device->logical_devices->bound_streams->next_binding // there's only _ONE_ bound stream. - ); -} - -// should hold device->lock before calling. -static void UpdateAudioStreamFormatsPhysical(SDL_AudioDevice *device) -{ - if (!device) { - return; - } - - const bool recording = device->recording; - SDL_AudioSpec spec; - SDL_copyp(&spec, &device->spec); - - const SDL_AudioFormat devformat = spec.format; - - if (!recording) { - const bool simple_copy = AudioDeviceCanUseSimpleCopy(device); - device->simple_copy = simple_copy; - if (!simple_copy) { - spec.format = SDL_AUDIO_F32; // mixing and postbuf operates in float32 format. - } - } - - for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev; logdev = logdev->next) { - if (recording) { - const bool need_float32 = (logdev->postmix || logdev->gain != 1.0f); - spec.format = need_float32 ? SDL_AUDIO_F32 : devformat; - } - - for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) { - // set the proper end of the stream to the device's format. - // SDL_SetAudioStreamFormat does a ton of validation just to memcpy an audiospec. - SDL_AudioSpec *streamspec = recording ? &stream->src_spec : &stream->dst_spec; - int **streamchmap = recording ? &stream->src_chmap : &stream->dst_chmap; - SDL_LockMutex(stream->lock); - SDL_copyp(streamspec, &spec); - SetAudioStreamChannelMap(stream, streamspec, streamchmap, device->chmap, device->spec.channels, -1); // this should be fast for normal cases, though! - SDL_UnlockMutex(stream->lock); - } - } -} - -bool SDL_AudioSpecsEqual(const SDL_AudioSpec *a, const SDL_AudioSpec *b, const int *channel_map_a, const int *channel_map_b) -{ - if ((a->format != b->format) || (a->channels != b->channels) || (a->freq != b->freq) || ((channel_map_a != NULL) != (channel_map_b != NULL))) { - return false; - } else if (channel_map_a && (SDL_memcmp(channel_map_a, channel_map_b, sizeof (*channel_map_a) * a->channels) != 0)) { - return false; - } - return true; -} - -bool SDL_AudioChannelMapsEqual(int channels, const int *channel_map_a, const int *channel_map_b) -{ - if (channel_map_a == channel_map_b) { - return true; - } else if ((channel_map_a != NULL) != (channel_map_b != NULL)) { - return false; - } else if (channel_map_a && (SDL_memcmp(channel_map_a, channel_map_b, sizeof (*channel_map_a) * channels) != 0)) { - return false; - } - return true; -} - - -// Zombie device implementation... - -// These get used when a device is disconnected or fails, so audiostreams don't overflow with data that isn't being -// consumed and apps relying on audio callbacks don't stop making progress. -static bool ZombieWaitDevice(SDL_AudioDevice *device) -{ - if (!SDL_GetAtomicInt(&device->shutdown)) { - const int frames = device->buffer_size / SDL_AUDIO_FRAMESIZE(device->spec); - SDL_Delay((frames * 1000) / device->spec.freq); - } - return true; -} - -static bool ZombiePlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) -{ - return true; // no-op, just throw the audio away. -} - -static Uint8 *ZombieGetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - return device->work_buffer; -} - -static int ZombieRecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - // return a full buffer of silence every time. - SDL_memset(buffer, device->silence_value, buflen); - return buflen; -} - -static void ZombieFlushRecording(SDL_AudioDevice *device) -{ - // no-op, this is all imaginary. -} - - - -// device management and hotplug... - - -/* SDL_AudioDevice, in SDL3, represents a piece of physical hardware, whether it is in use or not, so these objects exist as long as - the system-level device is available. - - Physical 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 ClosePhysicalAudioDevice(SDL_AudioDevice *device); - - -SDL_COMPILE_TIME_ASSERT(check_lowest_audio_default_value, SDL_AUDIO_DEVICE_DEFAULT_RECORDING < SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK); - -static SDL_AtomicInt last_device_instance_id; // increments on each device add to provide unique instance IDs -static SDL_AudioDeviceID AssignAudioDeviceInstanceId(bool recording, bool islogical) -{ - /* Assign an instance id! Start at 2, in case there are things from the SDL2 era that still think 1 is a special value. - Also, make sure we don't assign SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, etc. */ - - // The bottom two bits of the instance id tells you if it's an playback device (1<<0), and if it's a physical device (1<<1). - const SDL_AudioDeviceID flags = (recording ? 0 : (1<<0)) | (islogical ? 0 : (1<<1)); - - const SDL_AudioDeviceID instance_id = (((SDL_AudioDeviceID) (SDL_AtomicIncRef(&last_device_instance_id) + 1)) << 2) | flags; - SDL_assert( (instance_id >= 2) && (instance_id < SDL_AUDIO_DEVICE_DEFAULT_RECORDING) ); - return instance_id; -} - -bool SDL_IsAudioDevicePhysical(SDL_AudioDeviceID devid) -{ - return (devid & (1 << 1)) != 0; -} - -bool SDL_IsAudioDevicePlayback(SDL_AudioDeviceID devid) -{ - return (devid & (1 << 0)) != 0; -} - -static void ObtainPhysicalAudioDeviceObj(SDL_AudioDevice *device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXMEL SDL_ACQUIRE -{ - if (device) { - RefPhysicalAudioDevice(device); - SDL_LockMutex(device->lock); - } -} - -static void ReleaseAudioDevice(SDL_AudioDevice *device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXME: SDL_RELEASE -{ - if (device) { - SDL_UnlockMutex(device->lock); - UnrefPhysicalAudioDevice(device); - } -} - -// If found, this locks _the physical device_ this logical device is associated with, before returning. -static SDL_LogicalAudioDevice *ObtainLogicalAudioDevice(SDL_AudioDeviceID devid, SDL_AudioDevice **_device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXME: SDL_ACQUIRE -{ - SDL_assert(_device != NULL); - - if (!SDL_GetCurrentAudioDriver()) { - SDL_SetError("Audio subsystem is not initialized"); - *_device = NULL; - return NULL; - } - - SDL_AudioDevice *device = NULL; - SDL_LogicalAudioDevice *logdev = NULL; - - // bit #1 of devid is set for physical devices and unset for logical. - const bool islogical = !(devid & (1<<1)); - if (islogical) { // don't bother looking if it's not a logical device id value. - SDL_LockRWLockForReading(current_audio.device_hash_lock); - SDL_FindInHashTable(current_audio.device_hash, (const void *) (uintptr_t) devid, (const void **) &logdev); - if (logdev) { - device = logdev->physical_device; - SDL_assert(device != NULL); - RefPhysicalAudioDevice(device); // reference it, in case the logical device migrates to a new default. - } - SDL_UnlockRWLock(current_audio.device_hash_lock); - - if (logdev) { - // we have to release the device_hash_lock before we take the device lock, to avoid deadlocks, so do a loop - // to make sure the correct physical device gets locked, in case we're in a race with the default changing. - while (true) { - SDL_LockMutex(device->lock); - SDL_AudioDevice *recheck_device = (SDL_AudioDevice *) SDL_GetAtomicPointer((void **) &logdev->physical_device); - if (device == recheck_device) { - break; - } - - // default changed from under us! Try again! - RefPhysicalAudioDevice(recheck_device); - SDL_UnlockMutex(device->lock); - UnrefPhysicalAudioDevice(device); - device = recheck_device; - } - } - } - - if (!logdev) { - SDL_SetError("Invalid audio device instance ID"); - } - - *_device = device; - return logdev; -} - - -/* this finds the physical device associated with `devid` and locks it for use. - Note that a logical device instance id will return its associated physical device! */ -static SDL_AudioDevice *ObtainPhysicalAudioDevice(SDL_AudioDeviceID devid) // !!! FIXME: SDL_ACQUIRE -{ - SDL_AudioDevice *device = NULL; - - // bit #1 of devid is set for physical devices and unset for logical. - const bool islogical = !(devid & (1<<1)); - if (islogical) { - ObtainLogicalAudioDevice(devid, &device); - } else if (!SDL_GetCurrentAudioDriver()) { // (the `islogical` path, above, checks this in ObtainLogicalAudioDevice.) - SDL_SetError("Audio subsystem is not initialized"); - } else { - SDL_LockRWLockForReading(current_audio.device_hash_lock); - SDL_FindInHashTable(current_audio.device_hash, (const void *) (uintptr_t) devid, (const void **) &device); - SDL_UnlockRWLock(current_audio.device_hash_lock); - - if (!device) { - SDL_SetError("Invalid audio device instance ID"); - } else { - ObtainPhysicalAudioDeviceObj(device); - } - } - - return device; -} - -static SDL_AudioDevice *ObtainPhysicalAudioDeviceDefaultAllowed(SDL_AudioDeviceID devid) // !!! FIXME: SDL_ACQUIRE -{ - const bool wants_default = ((devid == SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) || (devid == SDL_AUDIO_DEVICE_DEFAULT_RECORDING)); - if (!wants_default) { - return ObtainPhysicalAudioDevice(devid); - } - - const SDL_AudioDeviceID orig_devid = devid; - - while (true) { - SDL_LockRWLockForReading(current_audio.device_hash_lock); - if (orig_devid == SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) { - devid = current_audio.default_playback_device_id; - } else if (orig_devid == SDL_AUDIO_DEVICE_DEFAULT_RECORDING) { - devid = current_audio.default_recording_device_id; - } - SDL_UnlockRWLock(current_audio.device_hash_lock); - - if (devid == 0) { - SDL_SetError("No default audio device available"); - break; - } - - SDL_AudioDevice *device = ObtainPhysicalAudioDevice(devid); - if (!device) { - break; - } - - // make sure the default didn't change while we were waiting for the lock... - bool got_it = false; - SDL_LockRWLockForReading(current_audio.device_hash_lock); - if ((orig_devid == SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) && (devid == current_audio.default_playback_device_id)) { - got_it = true; - } else if ((orig_devid == SDL_AUDIO_DEVICE_DEFAULT_RECORDING) && (devid == current_audio.default_recording_device_id)) { - got_it = true; - } - SDL_UnlockRWLock(current_audio.device_hash_lock); - - if (got_it) { - return device; - } - - ReleaseAudioDevice(device); // let it go and try again. - } - - return NULL; -} - -// this assumes you hold the _physical_ device lock for this logical device! This will not unlock the lock or close the physical device! -// It also will not unref the physical device, since we might be shutting down; SDL_CloseAudioDevice handles the unref. -static void DestroyLogicalAudioDevice(SDL_LogicalAudioDevice *logdev) -{ - // Remove ourselves from the device_hash hashtable. - if (current_audio.device_hash) { // will be NULL while shutting down. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); - SDL_RemoveFromHashTable(current_audio.device_hash, (const void *) (uintptr_t) logdev->instance_id); - SDL_UnlockRWLock(current_audio.device_hash_lock); - } - - // remove ourselves from the physical device's list of logical devices. - if (logdev->next) { - logdev->next->prev = logdev->prev; - } - if (logdev->prev) { - logdev->prev->next = logdev->next; - } - if (logdev->physical_device->logical_devices == logdev) { - logdev->physical_device->logical_devices = logdev->next; - } - - // unbind any still-bound streams... - SDL_AudioStream *next; - for (SDL_AudioStream *stream = logdev->bound_streams; stream; 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); - } - - UpdateAudioStreamFormatsPhysical(logdev->physical_device); - SDL_free(logdev); -} - -// this must not be called while `device` is still in a device list, or while a device's audio thread is still running. -static void DestroyPhysicalAudioDevice(SDL_AudioDevice *device) -{ - if (!device) { - return; - } - - // Destroy any logical devices that still exist... - SDL_LockMutex(device->lock); // don't use ObtainPhysicalAudioDeviceObj because we don't want to change refcounts while destroying. - while (device->logical_devices) { - DestroyLogicalAudioDevice(device->logical_devices); - } - - ClosePhysicalAudioDevice(device); - - current_audio.impl.FreeDeviceHandle(device); - - SDL_UnlockMutex(device->lock); // don't use ReleaseAudioDevice because we don't want to change refcounts while destroying. - - SDL_DestroyMutex(device->lock); - SDL_DestroyCondition(device->close_cond); - SDL_free(device->work_buffer); - SDL_free(device->chmap); - SDL_free(device->name); - SDL_free(device); -} - -// Don't hold the device lock when calling this, as we may destroy the device! -void UnrefPhysicalAudioDevice(SDL_AudioDevice *device) -{ - if (SDL_AtomicDecRef(&device->refcount)) { - // take it out of the device list. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); - if (SDL_RemoveFromHashTable(current_audio.device_hash, (const void *) (uintptr_t) device->instance_id)) { - SDL_AddAtomicInt(device->recording ? ¤t_audio.recording_device_count : ¤t_audio.playback_device_count, -1); - } - SDL_UnlockRWLock(current_audio.device_hash_lock); - DestroyPhysicalAudioDevice(device); // ...and nuke it. - } -} - -void RefPhysicalAudioDevice(SDL_AudioDevice *device) -{ - SDL_AtomicIncRef(&device->refcount); -} - -static SDL_AudioDevice *CreatePhysicalAudioDevice(const char *name, bool recording, const SDL_AudioSpec *spec, void *handle, SDL_AtomicInt *device_count) -{ - SDL_assert(name != NULL); - - SDL_LockRWLockForReading(current_audio.device_hash_lock); - const int shutting_down = SDL_GetAtomicInt(¤t_audio.shutting_down); - SDL_UnlockRWLock(current_audio.device_hash_lock); - if (shutting_down) { - return NULL; // we're shutting down, don't add any devices that are hotplugged at the last possible moment. - } - - SDL_AudioDevice *device = (SDL_AudioDevice *)SDL_calloc(1, sizeof(SDL_AudioDevice)); - if (!device) { - return NULL; - } - - device->name = SDL_strdup(name); - if (!device->name) { - SDL_free(device); - return NULL; - } - - device->lock = SDL_CreateMutex(); - if (!device->lock) { - SDL_free(device->name); - SDL_free(device); - return NULL; - } - - device->close_cond = SDL_CreateCondition(); - if (!device->close_cond) { - SDL_DestroyMutex(device->lock); - SDL_free(device->name); - SDL_free(device); - return NULL; - } - - SDL_SetAtomicInt(&device->shutdown, 0); - SDL_SetAtomicInt(&device->zombie, 0); - device->recording = recording; - SDL_copyp(&device->spec, spec); - SDL_copyp(&device->default_spec, spec); - device->sample_frames = SDL_GetDefaultSampleFramesFromFreq(device->spec.freq); - device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format); - device->handle = handle; - - device->instance_id = AssignAudioDeviceInstanceId(recording, /*islogical=*/false); - - SDL_LockRWLockForWriting(current_audio.device_hash_lock); - if (SDL_InsertIntoHashTable(current_audio.device_hash, (const void *) (uintptr_t) device->instance_id, device, false)) { - SDL_AddAtomicInt(device_count, 1); - } else { - SDL_DestroyCondition(device->close_cond); - SDL_DestroyMutex(device->lock); - SDL_free(device->name); - SDL_free(device); - device = NULL; - } - SDL_UnlockRWLock(current_audio.device_hash_lock); - - RefPhysicalAudioDevice(device); // unref'd on device disconnect. - return device; -} - -static SDL_AudioDevice *CreateAudioRecordingDevice(const char *name, const SDL_AudioSpec *spec, void *handle) -{ - SDL_assert(current_audio.impl.HasRecordingSupport); - return CreatePhysicalAudioDevice(name, true, spec, handle, ¤t_audio.recording_device_count); -} - -static SDL_AudioDevice *CreateAudioPlaybackDevice(const char *name, const SDL_AudioSpec *spec, void *handle) -{ - return CreatePhysicalAudioDevice(name, false, spec, handle, ¤t_audio.playback_device_count); -} - -// The audio backends call this when a new device is plugged in. -SDL_AudioDevice *SDL_AddAudioDevice(bool recording, const char *name, const SDL_AudioSpec *inspec, void *handle) -{ - // device handles MUST be unique! If the target reuses the same handle for hardware with both recording and playback interfaces, wrap it in a pointer you SDL_malloc'd! - SDL_assert(SDL_FindPhysicalAudioDeviceByHandle(handle) == NULL); - - const SDL_AudioFormat default_format = recording ? DEFAULT_AUDIO_RECORDING_FORMAT : DEFAULT_AUDIO_PLAYBACK_FORMAT; - const int default_channels = recording ? DEFAULT_AUDIO_RECORDING_CHANNELS : DEFAULT_AUDIO_PLAYBACK_CHANNELS; - const int default_freq = recording ? DEFAULT_AUDIO_RECORDING_FREQUENCY : DEFAULT_AUDIO_PLAYBACK_FREQUENCY; - - SDL_AudioSpec spec; - SDL_zero(spec); - if (!inspec) { - spec.format = default_format; - spec.channels = default_channels; - spec.freq = default_freq; - } else { - spec.format = (inspec->format != 0) ? inspec->format : default_format; - spec.channels = (inspec->channels != 0) ? inspec->channels : default_channels; - spec.freq = (inspec->freq != 0) ? inspec->freq : default_freq; - } - - SDL_AudioDevice *device = recording ? CreateAudioRecordingDevice(name, &spec, handle) : CreateAudioPlaybackDevice(name, &spec, handle); - - // Add a device add event to the pending list, to be pushed when the event queue is pumped (away from any of our internal threads). - if (device) { - SDL_PendingAudioDeviceEvent *p = (SDL_PendingAudioDeviceEvent *) SDL_malloc(sizeof (SDL_PendingAudioDeviceEvent)); - if (p) { // if allocation fails, you won't get an event, but we can't help that. - p->type = SDL_EVENT_AUDIO_DEVICE_ADDED; - p->devid = device->instance_id; - p->next = NULL; - SDL_LockRWLockForWriting(current_audio.device_hash_lock); - SDL_assert(current_audio.pending_events_tail != NULL); - SDL_assert(current_audio.pending_events_tail->next == NULL); - current_audio.pending_events_tail->next = p; - current_audio.pending_events_tail = p; - SDL_UnlockRWLock(current_audio.device_hash_lock); - } - } - - return device; -} - -// 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) -{ - if (!device) { - return; - } - - // Save off removal info in a list so we can send events for each, next - // time the event queue pumps, in case something tries to close a device - // from an event filter, as this would risk deadlocks and other disasters - // if done from the device thread. - SDL_PendingAudioDeviceEvent pending; - pending.next = NULL; - SDL_PendingAudioDeviceEvent *pending_tail = &pending; - - ObtainPhysicalAudioDeviceObj(device); - - SDL_LockRWLockForReading(current_audio.device_hash_lock); - const SDL_AudioDeviceID devid = device->instance_id; - const bool is_default_device = ((devid == current_audio.default_playback_device_id) || (devid == current_audio.default_recording_device_id)); - SDL_UnlockRWLock(current_audio.device_hash_lock); - - const bool first_disconnect = SDL_CompareAndSwapAtomicInt(&device->zombie, 0, 1); - if (first_disconnect) { // if already disconnected this device, don't do it twice. - // Swap in "Zombie" versions of the usual platform interfaces, so the device will keep - // making progress until the app closes it. Otherwise, streams might continue to - // accumulate waste data that never drains, apps that depend on audio callbacks to - // progress will freeze, etc. - device->WaitDevice = ZombieWaitDevice; - device->GetDeviceBuf = ZombieGetDeviceBuf; - device->PlayDevice = ZombiePlayDevice; - device->WaitRecordingDevice = ZombieWaitDevice; - device->RecordDevice = ZombieRecordDevice; - device->FlushRecording = ZombieFlushRecording; - - // on default devices, dump any logical devices that explicitly opened this device. Things that opened the system default can stay. - // on non-default devices, dump everything. - // (by "dump" we mean send a REMOVED event; the zombie will keep consuming audio data for these logical devices until explicitly closed.) - for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev; logdev = logdev->next) { - if (!is_default_device || !logdev->opened_as_default) { // if opened as a default, leave it on the zombie device for later migration. - SDL_PendingAudioDeviceEvent *p = (SDL_PendingAudioDeviceEvent *) SDL_malloc(sizeof (SDL_PendingAudioDeviceEvent)); - if (p) { // if this failed, no event for you, but you have deeper problems anyhow. - p->type = SDL_EVENT_AUDIO_DEVICE_REMOVED; - p->devid = logdev->instance_id; - p->next = NULL; - pending_tail->next = p; - pending_tail = p; - } - } - } - - SDL_PendingAudioDeviceEvent *p = (SDL_PendingAudioDeviceEvent *) SDL_malloc(sizeof (SDL_PendingAudioDeviceEvent)); - if (p) { // if this failed, no event for you, but you have deeper problems anyhow. - p->type = SDL_EVENT_AUDIO_DEVICE_REMOVED; - p->devid = device->instance_id; - p->next = NULL; - pending_tail->next = p; - pending_tail = p; - } - } - - ReleaseAudioDevice(device); - - if (first_disconnect) { - if (pending.next) { // NULL if event is disabled or disaster struck. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); - SDL_assert(current_audio.pending_events_tail != NULL); - SDL_assert(current_audio.pending_events_tail->next == NULL); - current_audio.pending_events_tail->next = pending.next; - current_audio.pending_events_tail = pending_tail; - SDL_UnlockRWLock(current_audio.device_hash_lock); - } - - UnrefPhysicalAudioDevice(device); - } -} - - -// stubs for audio drivers that don't need a specific entry point... - -static void SDL_AudioThreadDeinit_Default(SDL_AudioDevice *device) { /* no-op. */ } -static bool SDL_AudioWaitDevice_Default(SDL_AudioDevice *device) { return true; /* no-op. */ } -static bool SDL_AudioPlayDevice_Default(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { return true; /* no-op. */ } -static bool SDL_AudioWaitRecordingDevice_Default(SDL_AudioDevice *device) { return true; /* no-op. */ } -static void SDL_AudioFlushRecording_Default(SDL_AudioDevice *device) { /* no-op. */ } -static void SDL_AudioCloseDevice_Default(SDL_AudioDevice *device) { /* no-op. */ } -static void SDL_AudioDeinitializeStart_Default(void) { /* no-op. */ } -static void SDL_AudioDeinitialize_Default(void) { /* no-op. */ } -static void SDL_AudioFreeDeviceHandle_Default(SDL_AudioDevice *device) { /* no-op. */ } - -static void SDL_AudioThreadInit_Default(SDL_AudioDevice *device) -{ - SDL_SetCurrentThreadPriority(device->recording ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL); -} - -static void SDL_AudioDetectDevices_Default(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) -{ - // you have to write your own implementation if these assertions fail. - SDL_assert(current_audio.impl.OnlyHasDefaultPlaybackDevice); - SDL_assert(current_audio.impl.OnlyHasDefaultRecordingDevice || !current_audio.impl.HasRecordingSupport); - - *default_playback = SDL_AddAudioDevice(false, DEFAULT_PLAYBACK_DEVNAME, NULL, (void *)((size_t)0x1)); - if (current_audio.impl.HasRecordingSupport) { - *default_recording = SDL_AddAudioDevice(true, DEFAULT_RECORDING_DEVNAME, NULL, (void *)((size_t)0x2)); - } -} - -static Uint8 *SDL_AudioGetDeviceBuf_Default(SDL_AudioDevice *device, int *buffer_size) -{ - *buffer_size = 0; - return NULL; -} - -static int SDL_AudioRecordDevice_Default(SDL_AudioDevice *device, void *buffer, int buflen) -{ - SDL_Unsupported(); - return -1; -} - -static bool SDL_AudioOpenDevice_Default(SDL_AudioDevice *device) -{ - return SDL_Unsupported(); -} - -// 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) -{ - #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); - FILL_STUB(ThreadDeinit); - FILL_STUB(WaitDevice); - FILL_STUB(PlayDevice); - FILL_STUB(GetDeviceBuf); - FILL_STUB(WaitRecordingDevice); - FILL_STUB(RecordDevice); - FILL_STUB(FlushRecording); - FILL_STUB(CloseDevice); - FILL_STUB(FreeDeviceHandle); - FILL_STUB(DeinitializeStart); - FILL_STUB(Deinitialize); - #undef FILL_STUB -} - -typedef struct FindLowestDeviceIDData -{ - const bool recording; - SDL_AudioDeviceID highest; - SDL_AudioDevice *result; -} FindLowestDeviceIDData; - -static bool SDLCALL FindLowestDeviceID(void *userdata, const SDL_HashTable *table, const void *key, const void *value) -{ - FindLowestDeviceIDData *data = (FindLowestDeviceIDData *) userdata; - const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; - // bit #0 of devid is set for playback devices and unset for recording. - // bit #1 of devid is set for physical devices and unset for logical. - const bool devid_recording = !(devid & (1 << 0)); - const bool isphysical = !!(devid & (1 << 1)); - if (isphysical && (devid_recording == data->recording) && (devid < data->highest)) { - data->highest = devid; - data->result = (SDL_AudioDevice *) value; - } - return true; // keep iterating. -} - -static SDL_AudioDevice *GetFirstAddedAudioDevice(const bool recording) -{ - const SDL_AudioDeviceID highest = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK; // According to AssignAudioDeviceInstanceId, nothing can have a value this large. - - // (Device IDs increase as new devices are added, so the first device added has the lowest SDL_AudioDeviceID value.) - FindLowestDeviceIDData data = { recording, highest, NULL }; - SDL_LockRWLockForReading(current_audio.device_hash_lock); - SDL_IterateHashTable(current_audio.device_hash, FindLowestDeviceID, &data); - SDL_UnlockRWLock(current_audio.device_hash_lock); - return data.result; -} - -static Uint32 SDLCALL HashAudioDeviceID(void *userdata, const void *key) -{ - // shift right 2, to dump the first two bits, since these are flags - // (recording vs playback, logical vs physical) and the rest are unique incrementing integers. - return ((Uint32) ((uintptr_t) key)) >> 2; -} - -// !!! FIXME: the video subsystem does SDL_VideoInit, not SDL_InitVideo. Make this match. -bool SDL_InitAudio(const char *driver_name) -{ - if (SDL_GetCurrentAudioDriver()) { - SDL_QuitAudio(); // shutdown driver if already running. - } - - // make sure device IDs start at 2 (because of SDL2 legacy interface), but don't reset the counter on each init, in case the app is holding an old device ID somewhere. - SDL_CompareAndSwapAtomicInt(&last_device_instance_id, 0, 2); - - SDL_ChooseAudioConverters(); - SDL_SetupAudioResampler(); - - SDL_RWLock *device_hash_lock = SDL_CreateRWLock(); // create this early, so if it fails we don't have to tear down the whole audio subsystem. - if (!device_hash_lock) { - return false; - } - - SDL_HashTable *device_hash = SDL_CreateHashTable(0, false, HashAudioDeviceID, SDL_KeyMatchID, NULL, NULL); - if (!device_hash) { - SDL_DestroyRWLock(device_hash_lock); - return false; - } - - // Select the proper audio driver - if (!driver_name) { - driver_name = SDL_GetHint(SDL_HINT_AUDIO_DRIVER); - } - - bool initialized = false; - bool tried_to_init = false; - - if (driver_name && *driver_name != 0) { - char *driver_name_copy = SDL_strdup(driver_name); - const char *driver_attempt = driver_name_copy; - - if (!driver_name_copy) { - SDL_DestroyRWLock(device_hash_lock); - SDL_DestroyHashTable(device_hash); - return false; - } - - while (driver_attempt && *driver_attempt != 0 && !initialized) { - char *driver_attempt_end = SDL_strchr(driver_attempt, ','); - if (driver_attempt_end) { - *driver_attempt_end = '\0'; - } - - // 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"; - } - - for (int i = 0; bootstrap[i]; ++i) { - if (!bootstrap[i]->is_preferred && SDL_strcasecmp(bootstrap[i]->name, driver_attempt) == 0) { - tried_to_init = true; - SDL_zero(current_audio); - current_audio.pending_events_tail = ¤t_audio.pending_events; - current_audio.device_hash_lock = device_hash_lock; - current_audio.device_hash = device_hash; - if (bootstrap[i]->init(¤t_audio.impl)) { - current_audio.name = bootstrap[i]->name; - current_audio.desc = bootstrap[i]->desc; - initialized = true; - break; - } - } - } - - driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL; - } - - SDL_free(driver_name_copy); - } else { - for (int i = 0; (!initialized) && (bootstrap[i]); ++i) { - if (bootstrap[i]->demand_only) { - continue; - } - - tried_to_init = true; - SDL_zero(current_audio); - current_audio.pending_events_tail = ¤t_audio.pending_events; - current_audio.device_hash_lock = device_hash_lock; - current_audio.device_hash = device_hash; - if (bootstrap[i]->init(¤t_audio.impl)) { - current_audio.name = bootstrap[i]->name; - current_audio.desc = bootstrap[i]->desc; - initialized = true; - } - } - } - - if (!initialized) { - // specific drivers will set the error message if they fail, but otherwise we do it here. - if (!tried_to_init) { - if (driver_name) { - SDL_SetError("Audio target '%s' not available", driver_name); - } else { - SDL_SetError("No available audio device"); - } - } - - SDL_DestroyRWLock(device_hash_lock); - SDL_DestroyHashTable(device_hash); - SDL_zero(current_audio); - return false; // No driver was available, so fail. - } - - CompleteAudioEntryPoints(); - - // Make sure we have a list of devices available at startup... - SDL_AudioDevice *default_playback = NULL; - SDL_AudioDevice *default_recording = NULL; - current_audio.impl.DetectDevices(&default_playback, &default_recording); - - // If no default was _ever_ specified, just take the first device we see, if any. - if (!default_playback) { - default_playback = GetFirstAddedAudioDevice(/*recording=*/false); - } - - if (!default_recording) { - default_recording = GetFirstAddedAudioDevice(/*recording=*/true); - } - - if (default_playback) { - current_audio.default_playback_device_id = default_playback->instance_id; - RefPhysicalAudioDevice(default_playback); // extra ref on default devices. - } - - if (default_recording) { - current_audio.default_recording_device_id = default_recording->instance_id; - RefPhysicalAudioDevice(default_recording); // extra ref on default devices. - } - - return true; -} - -static bool SDLCALL DestroyOnePhysicalAudioDevice(void *userdata, const SDL_HashTable *table, const void *key, const void *value) -{ - // bit #1 of devid is set for physical devices and unset for logical. - const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; - const bool isphysical = !!(devid & (1<<1)); - if (isphysical) { - DestroyPhysicalAudioDevice((SDL_AudioDevice *) value); - } - return true; // keep iterating. -} - -void SDL_QuitAudio(void) -{ - if (!current_audio.name) { // not initialized?! - return; - } - - current_audio.impl.DeinitializeStart(); - - // Destroy any audio streams that still exist... - while (current_audio.existing_streams) { - SDL_DestroyAudioStream(current_audio.existing_streams); - } - - SDL_LockRWLockForWriting(current_audio.device_hash_lock); - SDL_SetAtomicInt(¤t_audio.shutting_down, 1); - SDL_HashTable *device_hash = current_audio.device_hash; - current_audio.device_hash = NULL; - SDL_PendingAudioDeviceEvent *pending_events = current_audio.pending_events.next; - current_audio.pending_events.next = NULL; - SDL_SetAtomicInt(¤t_audio.playback_device_count, 0); - SDL_SetAtomicInt(¤t_audio.recording_device_count, 0); - SDL_UnlockRWLock(current_audio.device_hash_lock); - - SDL_PendingAudioDeviceEvent *pending_next = NULL; - for (SDL_PendingAudioDeviceEvent *i = pending_events; i; i = pending_next) { - pending_next = i->next; - SDL_free(i); - } - - SDL_IterateHashTable(device_hash, DestroyOnePhysicalAudioDevice, NULL); - - // Free the driver data - current_audio.impl.Deinitialize(); - - SDL_DestroyRWLock(current_audio.device_hash_lock); - SDL_DestroyHashTable(device_hash); - - SDL_zero(current_audio); -} - - -void SDL_AudioThreadFinalize(SDL_AudioDevice *device) -{ -} - -static void MixFloat32Audio(float *dst, const float *src, const int buffer_size) -{ - if (!SDL_MixAudio((Uint8 *) dst, (const Uint8 *) src, SDL_AUDIO_F32, buffer_size, 1.0f)) { - SDL_assert(!"This shouldn't happen."); - } -} - - -// Playback device thread. This is split into chunks, so backends that need to control this directly can use the pieces they need without duplicating effort. - -void SDL_PlaybackAudioThreadSetup(SDL_AudioDevice *device) -{ - SDL_assert(!device->recording); - current_audio.impl.ThreadInit(device); -} - -bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device) -{ - SDL_assert(!device->recording); - - SDL_LockMutex(device->lock); - - if (SDL_GetAtomicInt(&device->shutdown)) { - SDL_UnlockMutex(device->lock); - return false; // we're done, shut it down. - } - - bool failed = false; - int buffer_size = device->buffer_size; - Uint8 *device_buffer = device->GetDeviceBuf(device, &buffer_size); - if (buffer_size == 0) { - // WASAPI (maybe others, later) does this to say "just abandon this iteration and try again next time." - } else if (!device_buffer) { - failed = true; - } else { - SDL_assert(buffer_size <= device->buffer_size); // you can ask for less, but not more. - SDL_assert(AudioDeviceCanUseSimpleCopy(device) == device->simple_copy); // make sure this hasn't gotten out of sync. - - // can we do a basic copy without silencing/mixing the buffer? This is an extremely likely scenario, so we special-case it. - if (device->simple_copy) { - SDL_LogicalAudioDevice *logdev = device->logical_devices; - SDL_AudioStream *stream = logdev->bound_streams; - - // We should have updated this elsewhere if the format changed! - SDL_assert(SDL_AudioSpecsEqual(&stream->dst_spec, &device->spec, NULL, NULL)); - - const int br = SDL_GetAtomicInt(&logdev->paused) ? 0 : SDL_GetAudioStreamDataAdjustGain(stream, device_buffer, buffer_size, logdev->gain); - if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow. - failed = true; - SDL_memset(device_buffer, device->silence_value, buffer_size); // just supply silence to the device before we die. - } else if (br < buffer_size) { - SDL_memset(device_buffer + br, device->silence_value, buffer_size - br); // silence whatever we didn't write to. - } - - // generally channel maps will line up, but if the audio stream's chmap has been explicitly changed, do a final swizzle to device layout. - if ((br > 0) && (!SDL_AudioChannelMapsEqual(device->spec.channels, stream->dst_chmap, device->chmap))) { - ConvertAudio(br / SDL_AUDIO_FRAMESIZE(device->spec), device_buffer, device->spec.format, device->spec.channels, NULL, - device_buffer, device->spec.format, device->spec.channels, device->chmap, NULL, 1.0f); - } - } else { // need to actually mix (or silence the buffer) - float *final_mix_buffer = (float *) ((device->spec.format == SDL_AUDIO_F32) ? device_buffer : device->mix_buffer); - const int needed_samples = buffer_size / SDL_AUDIO_BYTESIZE(device->spec.format); - const int work_buffer_size = needed_samples * sizeof (float); - SDL_AudioSpec outspec; - - SDL_assert(work_buffer_size <= device->work_buffer_size); - - SDL_copyp(&outspec, &device->spec); - outspec.format = SDL_AUDIO_F32; - - SDL_memset(final_mix_buffer, '\0', work_buffer_size); // start with silence. - - for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev; logdev = logdev->next) { - if (SDL_GetAtomicInt(&logdev->paused)) { - continue; // paused? Skip this logical device. - } - - const SDL_AudioPostmixCallback postmix = logdev->postmix; - float *mix_buffer = final_mix_buffer; - if (postmix) { - mix_buffer = device->postmix_buffer; - SDL_memset(mix_buffer, '\0', work_buffer_size); // start with silence. - } - - for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) { - // We should have updated this elsewhere if the format changed! - SDL_assert(SDL_AudioSpecsEqual(&stream->dst_spec, &outspec, NULL, NULL)); - - /* 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_GetAudioStreamDataAdjustGain(stream, device->work_buffer, work_buffer_size, logdev->gain); - if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow. - failed = true; - break; - } else if (br > 0) { // it's okay if we get less than requested, we mix what we have. - // generally channel maps will line up, but if the audio stream's chmap has been explicitly changed, do a final swizzle to device layout. - if (!SDL_AudioChannelMapsEqual(device->spec.channels, stream->dst_chmap, device->chmap)) { - ConvertAudio(br / SDL_AUDIO_FRAMESIZE(device->spec), device->work_buffer, device->spec.format, device->spec.channels, NULL, - device->work_buffer, device->spec.format, device->spec.channels, device->chmap, NULL, 1.0f); - } - MixFloat32Audio(mix_buffer, (float *) device->work_buffer, br); - } - } - - if (postmix) { - SDL_assert(mix_buffer == device->postmix_buffer); - postmix(logdev->postmix_userdata, &outspec, mix_buffer, work_buffer_size); - MixFloat32Audio(final_mix_buffer, mix_buffer, work_buffer_size); - } - } - - if (((Uint8 *) final_mix_buffer) != device_buffer) { - // !!! FIXME: we can't promise the device buf is aligned/padded for SIMD. - //ConvertAudio(needed_samples / device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, NULL, device_buffer, device->spec.format, device->spec.channels, NULL, NULL, 1.0f); - ConvertAudio(needed_samples / device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, NULL, device->work_buffer, device->spec.format, device->spec.channels, NULL, NULL, 1.0f); - SDL_memcpy(device_buffer, device->work_buffer, buffer_size); - } - } - - // PlayDevice SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitDevice instead! - if (!device->PlayDevice(device, device_buffer, buffer_size)) { - failed = true; - } - } - - SDL_UnlockMutex(device->lock); - - if (failed) { - SDL_AudioDeviceDisconnected(device); // doh. - } - - return true; // always go on if not shutting down, even if device failed. -} - -void SDL_PlaybackAudioThreadShutdown(SDL_AudioDevice *device) -{ - SDL_assert(!device->recording); - const int frames = device->buffer_size / SDL_AUDIO_FRAMESIZE(device->spec); - // Wait for the audio to drain if device didn't die. - if (!SDL_GetAtomicInt(&device->zombie)) { - SDL_Delay(((frames * 1000) / device->spec.freq) * 2); - } - current_audio.impl.ThreadDeinit(device); - SDL_AudioThreadFinalize(device); -} - -static int SDLCALL PlaybackAudioThread(void *devicep) // thread entry point -{ - SDL_AudioDevice *device = (SDL_AudioDevice *)devicep; - SDL_assert(device != NULL); - SDL_assert(!device->recording); - SDL_PlaybackAudioThreadSetup(device); - - do { - if (!device->WaitDevice(device)) { - SDL_AudioDeviceDisconnected(device); // doh. (but don't break out of the loop, just be a zombie for now!) - } - } while (SDL_PlaybackAudioThreadIterate(device)); - - SDL_PlaybackAudioThreadShutdown(device); - return 0; -} - - - -// Recording device thread. This is split into chunks, so backends that need to control this directly can use the pieces they need without duplicating effort. - -void SDL_RecordingAudioThreadSetup(SDL_AudioDevice *device) -{ - SDL_assert(device->recording); - current_audio.impl.ThreadInit(device); -} - -bool SDL_RecordingAudioThreadIterate(SDL_AudioDevice *device) -{ - SDL_assert(device->recording); - - SDL_LockMutex(device->lock); - - if (SDL_GetAtomicInt(&device->shutdown)) { - SDL_UnlockMutex(device->lock); - return false; // we're done, shut it down. - } - - bool failed = false; - - if (!device->logical_devices) { - device->FlushRecording(device); // nothing wants data, dump anything pending. - } else { - // this SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitRecordingDevice! - int br = device->RecordDevice(device, device->work_buffer, device->buffer_size); - if (br < 0) { // uhoh, device failed for some reason! - failed = true; - } else if (br > 0) { // queue the new data to each bound stream. - for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev; logdev = logdev->next) { - if (SDL_GetAtomicInt(&logdev->paused)) { - continue; // paused? Skip this logical device. - } - - void *output_buffer = device->work_buffer; - - // I don't know why someone would want a postmix on a recording device, but we offer it for API consistency. - if (logdev->postmix || (logdev->gain != 1.0f)) { - // move to float format. - SDL_AudioSpec outspec; - SDL_copyp(&outspec, &device->spec); - outspec.format = SDL_AUDIO_F32; - output_buffer = device->postmix_buffer; - const int frames = br / SDL_AUDIO_FRAMESIZE(device->spec); - br = frames * SDL_AUDIO_FRAMESIZE(outspec); - ConvertAudio(frames, device->work_buffer, device->spec.format, outspec.channels, NULL, device->postmix_buffer, SDL_AUDIO_F32, outspec.channels, NULL, NULL, logdev->gain); - if (logdev->postmix) { - logdev->postmix(logdev->postmix_userdata, &outspec, device->postmix_buffer, br); - } - } - - for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) { - // We should have updated this elsewhere if the format changed! - SDL_assert(stream->src_spec.format == ((logdev->postmix || (logdev->gain != 1.0f)) ? SDL_AUDIO_F32 : device->spec.format)); - SDL_assert(stream->src_spec.channels == device->spec.channels); - SDL_assert(stream->src_spec.freq == device->spec.freq); - - void *final_buf = output_buffer; - - // generally channel maps will line up, but if the audio stream's chmap has been explicitly changed, do a final swizzle to stream layout. - if (!SDL_AudioChannelMapsEqual(device->spec.channels, stream->src_chmap, device->chmap)) { - final_buf = device->mix_buffer; // this is otherwise unused on recording devices, so it makes convenient scratch space here. - ConvertAudio(br / SDL_AUDIO_FRAMESIZE(device->spec), output_buffer, device->spec.format, device->spec.channels, NULL, - final_buf, device->spec.format, device->spec.channels, stream->src_chmap, NULL, 1.0f); - } - - /* 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, final_buf, br)) { - // 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. - failed = true; - break; - } - } - } - } - } - - SDL_UnlockMutex(device->lock); - - if (failed) { - SDL_AudioDeviceDisconnected(device); // doh. - } - - return true; // always go on if not shutting down, even if device failed. -} - -void SDL_RecordingAudioThreadShutdown(SDL_AudioDevice *device) -{ - SDL_assert(device->recording); - device->FlushRecording(device); - current_audio.impl.ThreadDeinit(device); - SDL_AudioThreadFinalize(device); -} - -static int SDLCALL RecordingAudioThread(void *devicep) // thread entry point -{ - SDL_AudioDevice *device = (SDL_AudioDevice *)devicep; - SDL_assert(device != NULL); - SDL_assert(device->recording); - SDL_RecordingAudioThreadSetup(device); - - do { - if (!device->WaitRecordingDevice(device)) { - SDL_AudioDeviceDisconnected(device); // doh. (but don't break out of the loop, just be a zombie for now!) - } - } while (SDL_RecordingAudioThreadIterate(device)); - - SDL_RecordingAudioThreadShutdown(device); - return 0; -} - -typedef struct CountAudioDevicesData -{ - int devs_seen; - const int num_devices; - SDL_AudioDeviceID *result; - const bool recording; -} CountAudioDevicesData; - -static bool SDLCALL CountAudioDevices(void *userdata, const SDL_HashTable *table, const void *key, const void *value) -{ - CountAudioDevicesData *data = (CountAudioDevicesData *) userdata; - const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; - // bit #0 of devid is set for playback devices and unset for recording. - // bit #1 of devid is set for physical devices and unset for logical. - const bool devid_recording = !(devid & (1<<0)); - const bool isphysical = !!(devid & (1<<1)); - if (isphysical && (devid_recording == data->recording)) { - SDL_assert(data->devs_seen < data->num_devices); - data->result[data->devs_seen++] = devid; - } - return true; // keep iterating. -} - -static SDL_AudioDeviceID *GetAudioDevices(int *count, bool recording) -{ - SDL_AudioDeviceID *result = NULL; - int num_devices = 0; - - if (SDL_GetCurrentAudioDriver()) { - SDL_LockRWLockForReading(current_audio.device_hash_lock); - { - num_devices = SDL_GetAtomicInt(recording ? ¤t_audio.recording_device_count : ¤t_audio.playback_device_count); - result = (SDL_AudioDeviceID *) SDL_malloc((num_devices + 1) * sizeof (SDL_AudioDeviceID)); - if (result) { - CountAudioDevicesData data = { 0, num_devices, result, recording }; - SDL_IterateHashTable(current_audio.device_hash, CountAudioDevices, &data); - SDL_assert(data.devs_seen == num_devices); - result[data.devs_seen] = 0; // null-terminated. - } - } - SDL_UnlockRWLock(current_audio.device_hash_lock); - } else { - SDL_SetError("Audio subsystem is not initialized"); - } - - if (count) { - if (result) { - *count = num_devices; - } else { - *count = 0; - } - } - return result; -} - -SDL_AudioDeviceID *SDL_GetAudioPlaybackDevices(int *count) -{ - return GetAudioDevices(count, false); -} - -SDL_AudioDeviceID *SDL_GetAudioRecordingDevices(int *count) -{ - return GetAudioDevices(count, true); -} - -typedef struct FindAudioDeviceByCallbackData -{ - bool (*callback)(SDL_AudioDevice *device, void *userdata); - void *userdata; - SDL_AudioDevice *retval; -} FindAudioDeviceByCallbackData; - -static bool SDLCALL FindAudioDeviceByCallback(void *userdata, const SDL_HashTable *table, const void *key, const void *value) -{ - FindAudioDeviceByCallbackData *data = (FindAudioDeviceByCallbackData *) userdata; - const SDL_AudioDeviceID devid = (SDL_AudioDeviceID) (uintptr_t) key; - // bit #1 of devid is set for physical devices and unset for logical. - const bool isphysical = !!(devid & (1<<1)); - if (isphysical) { - SDL_AudioDevice *device = (SDL_AudioDevice *) value; - if (data->callback(device, data->userdata)) { // found it? - data->retval = device; - return false; // stop iterating, we found it. - } - } - return true; // keep iterating. -} - -// !!! FIXME: SDL convention is for userdata to come first in the callback's params. Fix this at some point. -SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByCallback(bool (*callback)(SDL_AudioDevice *device, void *userdata), void *userdata) -{ - if (!SDL_GetCurrentAudioDriver()) { - SDL_SetError("Audio subsystem is not initialized"); - return NULL; - } - - FindAudioDeviceByCallbackData data = { callback, userdata, NULL }; - SDL_LockRWLockForReading(current_audio.device_hash_lock); - SDL_IterateHashTable(current_audio.device_hash, FindAudioDeviceByCallback, &data); - SDL_UnlockRWLock(current_audio.device_hash_lock); - - if (!data.retval) { - SDL_SetError("Device not found"); - } - - return data.retval; -} - -static bool TestDeviceHandleCallback(SDL_AudioDevice *device, void *handle) -{ - return device->handle == handle; -} - -SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByHandle(void *handle) -{ - return SDL_FindPhysicalAudioDeviceByCallback(TestDeviceHandleCallback, handle); -} - -const char *SDL_GetAudioDeviceName(SDL_AudioDeviceID devid) -{ - const char *result = NULL; - SDL_AudioDevice *device = ObtainPhysicalAudioDevice(devid); - if (device) { - result = SDL_GetPersistentString(device->name); - } - ReleaseAudioDevice(device); - - return result; -} - -bool SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec, int *sample_frames) -{ - if (!spec) { - return SDL_InvalidParamError("spec"); - } - - bool result = false; - SDL_AudioDevice *device = ObtainPhysicalAudioDeviceDefaultAllowed(devid); - if (device) { - SDL_copyp(spec, &device->spec); - if (sample_frames) { - *sample_frames = device->sample_frames; - } - result = true; - } - ReleaseAudioDevice(device); - - return result; -} - -int *SDL_GetAudioDeviceChannelMap(SDL_AudioDeviceID devid, int *count) -{ - int *result = NULL; - int channels = 0; - SDL_AudioDevice *device = ObtainPhysicalAudioDeviceDefaultAllowed(devid); - if (device) { - channels = device->spec.channels; - result = SDL_ChannelMapDup(device->chmap, channels); - } - ReleaseAudioDevice(device); - - if (count) { - *count = channels; - } - - return result; -} - - -// this is awkward, but this makes sure we can release the device lock -// so the device thread can terminate but also not have two things -// race to close or open the device while the lock is unprotected. -// you hold the lock when calling this, it will release the lock and -// wait while the shutdown flag is set. -// BE CAREFUL WITH THIS. -static void SerializePhysicalDeviceClose(SDL_AudioDevice *device) -{ - while (SDL_GetAtomicInt(&device->shutdown)) { - SDL_WaitCondition(device->close_cond, device->lock); - } -} - -// this expects the device lock to be held. -static void ClosePhysicalAudioDevice(SDL_AudioDevice *device) -{ - SerializePhysicalDeviceClose(device); - - SDL_SetAtomicInt(&device->shutdown, 1); - - // YOU MUST PROTECT KEY POINTS WITH SerializePhysicalDeviceClose() WHILE THE THREAD JOINS - SDL_UnlockMutex(device->lock); - - if (device->thread) { - SDL_WaitThread(device->thread, NULL); - device->thread = NULL; - } - - if (device->currently_opened) { - current_audio.impl.CloseDevice(device); // if ProvidesOwnCallbackThread, this must join on any existing device thread before returning! - device->currently_opened = false; - device->hidden = NULL; // just in case. - } - - SDL_LockMutex(device->lock); - SDL_SetAtomicInt(&device->shutdown, 0); // ready to go again. - SDL_BroadcastCondition(device->close_cond); // release anyone waiting in SerializePhysicalDeviceClose; they'll still block until we release device->lock, though. - - SDL_aligned_free(device->work_buffer); - device->work_buffer = NULL; - - SDL_aligned_free(device->mix_buffer); - device->mix_buffer = NULL; - - SDL_aligned_free(device->postmix_buffer); - device->postmix_buffer = NULL; - - SDL_copyp(&device->spec, &device->default_spec); - device->sample_frames = 0; - device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format); -} - -void SDL_CloseAudioDevice(SDL_AudioDeviceID devid) -{ - SDL_AudioDevice *device = NULL; - SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device); - if (logdev) { - DestroyLogicalAudioDevice(logdev); - } - - if (device) { - if (!device->logical_devices) { // no more logical devices? Close the physical device, too. - ClosePhysicalAudioDevice(device); - } - UnrefPhysicalAudioDevice(device); // one reference for each logical device. - } - - ReleaseAudioDevice(device); -} - - -static SDL_AudioFormat ParseAudioFormatString(const char *string) -{ - 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(S16LE); - CHECK_FMT_STRING(S16BE); - CHECK_FMT_STRING(S16); - CHECK_FMT_STRING(S32LE); - CHECK_FMT_STRING(S32BE); - CHECK_FMT_STRING(S32); - CHECK_FMT_STRING(F32LE); - CHECK_FMT_STRING(F32BE); - CHECK_FMT_STRING(F32); - #undef CHECK_FMT_STRING - } - return SDL_AUDIO_UNKNOWN; -} - -static void PrepareAudioFormat(bool recording, SDL_AudioSpec *spec) -{ - if (spec->freq == 0) { - spec->freq = recording ? DEFAULT_AUDIO_RECORDING_FREQUENCY : DEFAULT_AUDIO_PLAYBACK_FREQUENCY; - - const char *hint = SDL_GetHint(SDL_HINT_AUDIO_FREQUENCY); - if (hint) { - const int val = SDL_atoi(hint); - if (val > 0) { - spec->freq = val; - } - } - } - - if (spec->channels == 0) { - spec->channels = recording ? DEFAULT_AUDIO_RECORDING_CHANNELS : DEFAULT_AUDIO_PLAYBACK_CHANNELS; - - const char *hint = SDL_GetHint(SDL_HINT_AUDIO_CHANNELS); - if (hint) { - const int val = SDL_atoi(hint); - if (val > 0) { - spec->channels = val; - } - } - } - - if (spec->format == 0) { - const SDL_AudioFormat val = ParseAudioFormatString(SDL_GetHint(SDL_HINT_AUDIO_FORMAT)); - spec->format = (val != SDL_AUDIO_UNKNOWN) ? val : (recording ? DEFAULT_AUDIO_RECORDING_FORMAT : DEFAULT_AUDIO_PLAYBACK_FORMAT); - } -} - -void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device) -{ - device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format); - device->buffer_size = device->sample_frames * SDL_AUDIO_FRAMESIZE(device->spec); - device->work_buffer_size = device->sample_frames * sizeof (float) * device->spec.channels; - device->work_buffer_size = SDL_max(device->buffer_size, device->work_buffer_size); // just in case we end up with a 64-bit audio format at some point. -} - -char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen) -{ - (void)SDL_snprintf(buf, buflen, "SDLAudio%c%d", (device->recording) ? 'C' : 'P', (int) device->instance_id); - return buf; -} - - -// this expects the device lock to be held. -static bool OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec *inspec) -{ - SerializePhysicalDeviceClose(device); // make sure another thread that's closing didn't release the lock to let the device thread join... - - if (device->currently_opened) { - return true; // we're already good. - } - - // Just pretend to open a zombie device. It can still collect logical devices on a default device under the assumption they will all migrate when the default device is officially changed. - if (SDL_GetAtomicInt(&device->zombie)) { - return true; // Braaaaaaaaains. - } - - // These start with the backend's implementation, but we might swap them out with zombie versions later. - device->WaitDevice = current_audio.impl.WaitDevice; - device->PlayDevice = current_audio.impl.PlayDevice; - device->GetDeviceBuf = current_audio.impl.GetDeviceBuf; - device->WaitRecordingDevice = current_audio.impl.WaitRecordingDevice; - device->RecordDevice = current_audio.impl.RecordDevice; - device->FlushRecording = current_audio.impl.FlushRecording; - - SDL_AudioSpec spec; - SDL_copyp(&spec, inspec ? inspec : &device->default_spec); - PrepareAudioFormat(device->recording, &spec); - - /* 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->spec.format = (SDL_AUDIO_BITSIZE(device->default_spec.format) >= SDL_AUDIO_BITSIZE(spec.format)) ? device->default_spec.format : spec.format; - device->spec.freq = SDL_max(device->default_spec.freq, spec.freq); - device->spec.channels = SDL_max(device->default_spec.channels, spec.channels); - device->sample_frames = SDL_GetDefaultSampleFramesFromFreq(device->spec.freq); - SDL_UpdatedAudioDeviceFormat(device); // start this off sane. - - device->currently_opened = true; // mark this true even if impl.OpenDevice fails, so we know to clean up. - if (!current_audio.impl.OpenDevice(device)) { - ClosePhysicalAudioDevice(device); // clean up anything the backend left half-initialized. - return false; - } - - SDL_UpdatedAudioDeviceFormat(device); // in case the backend changed things and forgot to call this. - - // Allocate a scratch audio buffer - device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), device->work_buffer_size); - if (!device->work_buffer) { - ClosePhysicalAudioDevice(device); - return false; - } - - if (device->spec.format != SDL_AUDIO_F32) { - device->mix_buffer = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), device->work_buffer_size); - if (!device->mix_buffer) { - ClosePhysicalAudioDevice(device); - return false; - } - } - - // Start the audio thread if necessary - if (!current_audio.impl.ProvidesOwnCallbackThread) { - char threadname[64]; - SDL_GetAudioThreadName(device, threadname, sizeof (threadname)); - device->thread = SDL_CreateThread(device->recording ? RecordingAudioThread : PlaybackAudioThread, threadname, device); - - if (!device->thread) { - ClosePhysicalAudioDevice(device); - return SDL_SetError("Couldn't create audio thread"); - } - } - - return true; -} - -SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec) -{ - if (!SDL_GetCurrentAudioDriver()) { - SDL_SetError("Audio subsystem is not initialized"); - return 0; - } - - bool wants_default = ((devid == SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK) || (devid == SDL_AUDIO_DEVICE_DEFAULT_RECORDING)); - - // this will let you use a logical device to make a new logical device on the parent physical device. Could be useful? - SDL_AudioDevice *device = NULL; - const bool islogical = (!wants_default && !(devid & (1<<1))); - if (!islogical) { - device = ObtainPhysicalAudioDeviceDefaultAllowed(devid); - } else { - SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device); - if (logdev) { - wants_default = logdev->opened_as_default; // was the original logical device meant to be a default? Make this one, too. - } - } - - SDL_AudioDeviceID result = 0; - - if (device) { - SDL_LogicalAudioDevice *logdev = NULL; - if (!wants_default && SDL_GetAtomicInt(&device->zombie)) { - // uhoh, this device is undead, and just waiting to be cleaned up. Refuse explicit opens. - SDL_SetError("Device was already lost and can't accept new opens"); - } else if ((logdev = (SDL_LogicalAudioDevice *) SDL_calloc(1, sizeof (SDL_LogicalAudioDevice))) == NULL) { - // SDL_calloc already called SDL_OutOfMemory - } else if (!OpenPhysicalAudioDevice(device, spec)) { // if this is the first thing using this physical device, open at the OS level if necessary... - SDL_free(logdev); - } else { - RefPhysicalAudioDevice(device); // unref'd on successful SDL_CloseAudioDevice - SDL_SetAtomicInt(&logdev->paused, 0); - result = logdev->instance_id = AssignAudioDeviceInstanceId(device->recording, /*islogical=*/true); - logdev->physical_device = device; - logdev->gain = 1.0f; - logdev->opened_as_default = wants_default; - logdev->next = device->logical_devices; - if (device->logical_devices) { - device->logical_devices->prev = logdev; - } - device->logical_devices = logdev; - UpdateAudioStreamFormatsPhysical(device); - } - ReleaseAudioDevice(device); - - if (result) { - SDL_LockRWLockForWriting(current_audio.device_hash_lock); - const bool inserted = SDL_InsertIntoHashTable(current_audio.device_hash, (const void *) (uintptr_t) result, logdev, false); - SDL_UnlockRWLock(current_audio.device_hash_lock); - if (!inserted) { - SDL_CloseAudioDevice(result); - result = 0; - } - } - } - - return result; -} - -static bool SetLogicalAudioDevicePauseState(SDL_AudioDeviceID devid, int value) -{ - SDL_AudioDevice *device = NULL; - SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device); - if (logdev) { - SDL_SetAtomicInt(&logdev->paused, value); - } - ReleaseAudioDevice(device); - return logdev ? true : false; // ObtainLogicalAudioDevice will have set an error. -} - -bool SDL_PauseAudioDevice(SDL_AudioDeviceID devid) -{ - return SetLogicalAudioDevicePauseState(devid, 1); -} - -bool SDLCALL SDL_ResumeAudioDevice(SDL_AudioDeviceID devid) -{ - return SetLogicalAudioDevicePauseState(devid, 0); -} - -bool SDL_AudioDevicePaused(SDL_AudioDeviceID devid) -{ - SDL_AudioDevice *device = NULL; - SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device); - bool result = false; - if (logdev && SDL_GetAtomicInt(&logdev->paused)) { - result = true; - } - ReleaseAudioDevice(device); - return result; -} - -float SDL_GetAudioDeviceGain(SDL_AudioDeviceID devid) -{ - SDL_AudioDevice *device = NULL; - SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device); - const float result = logdev ? logdev->gain : -1.0f; - ReleaseAudioDevice(device); - return result; -} - -bool SDL_SetAudioDeviceGain(SDL_AudioDeviceID devid, float gain) -{ - if (gain < 0.0f) { - return SDL_InvalidParamError("gain"); - } - - SDL_AudioDevice *device = NULL; - SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device); - bool result = false; - if (logdev) { - logdev->gain = gain; - UpdateAudioStreamFormatsPhysical(device); - result = true; - } - ReleaseAudioDevice(device); - return result; -} - -bool SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallback callback, void *userdata) -{ - SDL_AudioDevice *device = NULL; - SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device); - bool result = true; - if (logdev) { - if (callback && !device->postmix_buffer) { - device->postmix_buffer = (float *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), device->work_buffer_size); - if (!device->postmix_buffer) { - result = false; - } - } - - if (result) { - logdev->postmix = callback; - logdev->postmix_userdata = userdata; - } - - UpdateAudioStreamFormatsPhysical(device); - } - ReleaseAudioDevice(device); - return result; -} - -bool SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream * const *streams, int num_streams) -{ - const bool islogical = !(devid & (1<<1)); - SDL_AudioDevice *device = NULL; - SDL_LogicalAudioDevice *logdev = NULL; - bool result = true; - - if (num_streams == 0) { - return true; // nothing to do - } else if (num_streams < 0) { - return SDL_InvalidParamError("num_streams"); - } else if (!streams) { - return SDL_InvalidParamError("streams"); - } else if (!islogical) { - return SDL_SetError("Audio streams are bound to device ids from SDL_OpenAudioDevice, not raw physical devices"); - } - - logdev = ObtainLogicalAudioDevice(devid, &device); - if (!logdev) { - result = false; // ObtainLogicalAudioDevice set the error string. - } else if (logdev->simplified) { - result = SDL_SetError("Cannot change stream bindings on device opened with SDL_OpenAudioDeviceStream"); - } else { - - // !!! FIXME: We'll set the device's side's format below, but maybe we should refuse to bind a stream if the app's side doesn't have a format set yet. - // !!! FIXME: Actually, why do we allow there to be an invalid format, again? - - // make sure start of list is sane. - SDL_assert(!logdev->bound_streams || (logdev->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 (int i = 0; i < num_streams; i++) { - SDL_AudioStream *stream = streams[i]; - if (!stream) { - SDL_SetError("Stream #%d is NULL", i); - result = false; // to pacify the static analyzer, that doesn't realize SDL_SetError() always returns false. - } else { - SDL_LockMutex(stream->lock); - SDL_assert((stream->bound_device == NULL) == ((stream->prev_binding == NULL) || (stream->next_binding == NULL))); - if (stream->bound_device) { - result = SDL_SetError("Stream #%d is already bound to a device", i); - } else if (stream->simplified) { // You can get here if you closed the device instead of destroying the stream. - result = SDL_SetError("Cannot change binding on a stream created with SDL_OpenAudioDeviceStream"); - } - } - - if (!result) { - int j; - for (j = 0; j < i; j++) { - SDL_UnlockMutex(streams[j]->lock); - } - if (stream) { - SDL_UnlockMutex(stream->lock); - } - break; - } - } - } - - if (result) { - // Now that everything is verified, chain everything together. - for (int i = 0; i < num_streams; i++) { - SDL_AudioStream *stream = streams[i]; - if (stream) { // shouldn't be NULL, but just in case... - stream->bound_device = logdev; - stream->prev_binding = NULL; - stream->next_binding = logdev->bound_streams; - if (logdev->bound_streams) { - logdev->bound_streams->prev_binding = stream; - } - logdev->bound_streams = stream; - SDL_UnlockMutex(stream->lock); - } - } - } - - UpdateAudioStreamFormatsPhysical(device); - - ReleaseAudioDevice(device); - - return result; -} - -bool SDL_BindAudioStream(SDL_AudioDeviceID devid, SDL_AudioStream *stream) -{ - return SDL_BindAudioStreams(devid, &stream, 1); -} - -// !!! FIXME: this and BindAudioStreams are mutex nightmares. :/ -void SDL_UnbindAudioStreams(SDL_AudioStream * const *streams, int num_streams) -{ - if (num_streams <= 0 || !streams) { - return; // nothing to do - } - - /* 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 (int i = 0; i < num_streams; i++) { - SDL_AudioStream *stream = streams[i]; - if (!stream) { - continue; // nothing to do, it's a NULL stream. - } - - while (true) { - SDL_LockMutex(stream->lock); // lock to check this and then release it, in case the device isn't locked yet. - SDL_LogicalAudioDevice *bounddev = stream->bound_device; - SDL_UnlockMutex(stream->lock); - - // lock in correct order. - if (bounddev) { - SDL_LockMutex(bounddev->physical_device->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->physical_device->lock); - } - } - } - } - - // everything is locked, start unbinding streams. - for (int i = 0; i < num_streams; i++) { - SDL_AudioStream *stream = streams[i]; - // don't allow unbinding from "simplified" devices (opened with SDL_OpenAudioDeviceStream). Just ignore them. - if (stream && stream->bound_device && !stream->bound_device->simplified) { - if (stream->bound_device->bound_streams == stream) { - SDL_assert(!stream->prev_binding); - 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; - } - } - - // Finalize and unlock everything. - for (int i = 0; i < num_streams; i++) { - SDL_AudioStream *stream = streams[i]; - if (stream) { - SDL_LogicalAudioDevice *logdev = stream->bound_device; - stream->bound_device = NULL; - SDL_UnlockMutex(stream->lock); - if (logdev) { - UpdateAudioStreamFormatsPhysical(logdev->physical_device); - SDL_UnlockMutex(logdev->physical_device->lock); - } - } - } -} - -void SDL_UnbindAudioStream(SDL_AudioStream *stream) -{ - SDL_UnbindAudioStreams(&stream, 1); -} - -SDL_AudioDeviceID SDL_GetAudioStreamDevice(SDL_AudioStream *stream) -{ - SDL_AudioDeviceID result = 0; - - if (!stream) { - SDL_InvalidParamError("stream"); - return 0; - } - - SDL_LockMutex(stream->lock); - if (stream->bound_device) { - result = stream->bound_device->instance_id; - } else { - SDL_SetError("Audio stream not bound to an audio device"); - } - SDL_UnlockMutex(stream->lock); - - return result; -} - -SDL_AudioStream *SDL_OpenAudioDeviceStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec, SDL_AudioStreamCallback callback, void *userdata) -{ - SDL_AudioDeviceID logdevid = SDL_OpenAudioDevice(devid, spec); - if (!logdevid) { - return NULL; // error string should already be set. - } - - bool failed = false; - SDL_AudioStream *stream = NULL; - SDL_AudioDevice *device = NULL; - SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(logdevid, &device); - if (!logdev) { // this shouldn't happen, but just in case. - failed = true; - } else { - SDL_SetAtomicInt(&logdev->paused, 1); // start the device paused, to match SDL2. - - SDL_assert(device != NULL); - const bool recording = device->recording; - - // if the app didn't request a format _at all_, just make a stream that does no conversion; they can query for it later. - SDL_AudioSpec tmpspec; - if (!spec) { - SDL_copyp(&tmpspec, &device->spec); - spec = &tmpspec; - } - - if (recording) { - stream = SDL_CreateAudioStream(&device->spec, spec); - } else { - stream = SDL_CreateAudioStream(spec, &device->spec); - } - - if (!stream) { - failed = true; - } else { - // don't do all the complicated validation and locking of SDL_BindAudioStream just to set a few fields here. - logdev->bound_streams = stream; - logdev->simplified = true; // forbid further binding changes on this logical device. - - stream->bound_device = logdev; - stream->simplified = true; // so we know to close the audio device when this is destroyed. - - UpdateAudioStreamFormatsPhysical(device); - - if (callback) { - bool rc; - if (recording) { - rc = SDL_SetAudioStreamPutCallback(stream, callback, userdata); - } else { - rc = SDL_SetAudioStreamGetCallback(stream, callback, userdata); - } - SDL_assert(rc); // should only fail if stream==NULL atm. - } - } - } - - ReleaseAudioDevice(device); - - if (failed) { - SDL_DestroyAudioStream(stream); - SDL_CloseAudioDevice(logdevid); - stream = NULL; - } - - return stream; -} - -bool SDL_PauseAudioStreamDevice(SDL_AudioStream *stream) -{ - SDL_AudioDeviceID devid = SDL_GetAudioStreamDevice(stream); - if (!devid) { - return false; - } - - return SDL_PauseAudioDevice(devid); -} - -bool SDL_ResumeAudioStreamDevice(SDL_AudioStream *stream) -{ - SDL_AudioDeviceID devid = SDL_GetAudioStreamDevice(stream); - if (!devid) { - return false; - } - - return SDL_ResumeAudioDevice(devid); -} - -bool SDL_AudioStreamDevicePaused(SDL_AudioStream *stream) -{ - SDL_AudioDeviceID devid = SDL_GetAudioStreamDevice(stream); - if (!devid) { - return false; - } - - return SDL_AudioDevicePaused(devid); -} - -#if SDL_BYTEORDER == SDL_LIL_ENDIAN -#define NATIVE(type) SDL_AUDIO_##type##LE -#define SWAPPED(type) SDL_AUDIO_##type##BE -#else -#define NATIVE(type) SDL_AUDIO_##type##BE -#define SWAPPED(type) SDL_AUDIO_##type##LE -#endif - -#define NUM_FORMATS 8 -// always favor Float32 in native byte order, since we're probably going to convert to that for processing anyhow. -static const SDL_AudioFormat format_list[NUM_FORMATS][NUM_FORMATS + 1] = { - { SDL_AUDIO_U8, NATIVE(F32), SWAPPED(F32), SDL_AUDIO_S8, NATIVE(S16), SWAPPED(S16), NATIVE(S32), SWAPPED(S32), SDL_AUDIO_UNKNOWN }, - { SDL_AUDIO_S8, NATIVE(F32), SWAPPED(F32), SDL_AUDIO_U8, NATIVE(S16), SWAPPED(S16), NATIVE(S32), SWAPPED(S32), SDL_AUDIO_UNKNOWN }, - { NATIVE(S16), NATIVE(F32), SWAPPED(F32), SWAPPED(S16), NATIVE(S32), SWAPPED(S32), SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_UNKNOWN }, - { SWAPPED(S16), NATIVE(F32), SWAPPED(F32), NATIVE(S16), SWAPPED(S32), NATIVE(S32), SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_UNKNOWN }, - { NATIVE(S32), NATIVE(F32), SWAPPED(F32), SWAPPED(S32), NATIVE(S16), SWAPPED(S16), SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_UNKNOWN }, - { SWAPPED(S32), NATIVE(F32), SWAPPED(F32), NATIVE(S32), SWAPPED(S16), NATIVE(S16), SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_UNKNOWN }, - { NATIVE(F32), SWAPPED(F32), NATIVE(S32), SWAPPED(S32), NATIVE(S16), SWAPPED(S16), SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_UNKNOWN }, - { SWAPPED(F32), NATIVE(F32), SWAPPED(S32), NATIVE(S32), SWAPPED(S16), NATIVE(S16), SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_UNKNOWN }, -}; - -#undef NATIVE -#undef SWAPPED - -const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format) -{ - for (int i = 0; i < NUM_FORMATS; i++) { - if (format_list[i][0] == format) { - return &format_list[i][0]; - } - } - return &format_list[0][NUM_FORMATS]; // not found; return what looks like a list with only a zero in it. -} - -const char *SDL_GetAudioFormatName(SDL_AudioFormat format) -{ - switch (format) { -#define CASE(X) \ - case X: return #X; - CASE(SDL_AUDIO_U8) - CASE(SDL_AUDIO_S8) - CASE(SDL_AUDIO_S16LE) - CASE(SDL_AUDIO_S16BE) - CASE(SDL_AUDIO_S32LE) - CASE(SDL_AUDIO_S32BE) - CASE(SDL_AUDIO_F32LE) - CASE(SDL_AUDIO_F32BE) -#undef CASE - default: - return "SDL_AUDIO_UNKNOWN"; - } -} - -int SDL_GetSilenceValueForFormat(SDL_AudioFormat format) -{ - return (format == SDL_AUDIO_U8) ? 0x80 : 0x00; -} - -// called internally by backends when the system default device changes. -void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) -{ - if (!new_default_device) { // !!! FIXME: what should we do in this case? Maybe all devices are lost, so there _isn't_ a default? - return; // uhoh. - } - - const bool recording = new_default_device->recording; - - // change the official default over right away, so new opens will go to the new device. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); - const SDL_AudioDeviceID current_devid = recording ? current_audio.default_recording_device_id : current_audio.default_playback_device_id; - const bool is_already_default = (new_default_device->instance_id == current_devid); - if (!is_already_default) { - if (recording) { - current_audio.default_recording_device_id = new_default_device->instance_id; - } else { - current_audio.default_playback_device_id = new_default_device->instance_id; - } - } - SDL_UnlockRWLock(current_audio.device_hash_lock); - - if (is_already_default) { - return; // this is already the default. - } - - // Queue up events to push to the queue next time it pumps (presumably - // in a safer thread). - // !!! FIXME: this duplicates some code we could probably refactor. - SDL_PendingAudioDeviceEvent pending; - pending.next = NULL; - SDL_PendingAudioDeviceEvent *pending_tail = &pending; - - // Default device gets an extra ref, so it lives until a new default replaces it, even if disconnected. - RefPhysicalAudioDevice(new_default_device); - - ObtainPhysicalAudioDeviceObj(new_default_device); - - SDL_AudioDevice *current_default_device = ObtainPhysicalAudioDevice(current_devid); - - if (current_default_device) { - // migrate any logical devices that were opened as a default to the new physical device... - - SDL_assert(current_default_device->recording == recording); - - // See if we have to open the new physical device, and if so, find the best audiospec for it. - SDL_AudioSpec spec; - bool needs_migration = false; - SDL_zero(spec); - - for (SDL_LogicalAudioDevice *logdev = current_default_device->logical_devices; logdev; logdev = logdev->next) { - if (logdev->opened_as_default) { - needs_migration = true; - for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) { - const SDL_AudioSpec *streamspec = recording ? &stream->dst_spec : &stream->src_spec; - if (SDL_AUDIO_BITSIZE(streamspec->format) > SDL_AUDIO_BITSIZE(spec.format)) { - spec.format = streamspec->format; - } - if (streamspec->channels > spec.channels) { - spec.channels = streamspec->channels; - } - if (streamspec->freq > spec.freq) { - spec.freq = streamspec->freq; - } - } - } - } - - if (needs_migration) { - // New default physical device not been opened yet? Open at the OS level... - if (!OpenPhysicalAudioDevice(new_default_device, &spec)) { - needs_migration = false; // uhoh, just leave everything on the old default, nothing to be done. - } - } - - if (needs_migration) { - // we don't currently report channel map changes, so we'll leave them as NULL for now. - const bool spec_changed = !SDL_AudioSpecsEqual(¤t_default_device->spec, &new_default_device->spec, NULL, NULL); - SDL_LogicalAudioDevice *next = NULL; - for (SDL_LogicalAudioDevice *logdev = current_default_device->logical_devices; logdev; logdev = next) { - next = logdev->next; - - if (!logdev->opened_as_default) { - continue; // not opened as a default, leave it on the current physical device. - } - - // now migrate the logical device. Hold device_hash_lock so ObtainLogicalAudioDevice doesn't get a device in the middle of transition. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); - if (logdev->next) { - logdev->next->prev = logdev->prev; - } - if (logdev->prev) { - logdev->prev->next = logdev->next; - } - if (current_default_device->logical_devices == logdev) { - current_default_device->logical_devices = logdev->next; - } - - logdev->physical_device = new_default_device; - logdev->prev = NULL; - logdev->next = new_default_device->logical_devices; - new_default_device->logical_devices = logdev; - SDL_UnlockRWLock(current_audio.device_hash_lock); - - SDL_assert(SDL_GetAtomicInt(¤t_default_device->refcount) > 1); // we should hold at least one extra reference to this device, beyond logical devices, during this phase... - RefPhysicalAudioDevice(new_default_device); - UnrefPhysicalAudioDevice(current_default_device); - - SDL_SetAudioPostmixCallback(logdev->instance_id, logdev->postmix, logdev->postmix_userdata); - - SDL_PendingAudioDeviceEvent *p; - - // Queue an event for each logical device we moved. - if (spec_changed) { - p = (SDL_PendingAudioDeviceEvent *)SDL_malloc(sizeof(SDL_PendingAudioDeviceEvent)); - if (p) { // if this failed, no event for you, but you have deeper problems anyhow. - p->type = SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED; - p->devid = logdev->instance_id; - p->next = NULL; - pending_tail->next = p; - pending_tail = p; - } - } - } - - UpdateAudioStreamFormatsPhysical(current_default_device); - UpdateAudioStreamFormatsPhysical(new_default_device); - - if (!current_default_device->logical_devices) { // nothing left on the current physical device, close it. - ClosePhysicalAudioDevice(current_default_device); - } - } - - ReleaseAudioDevice(current_default_device); - } - - ReleaseAudioDevice(new_default_device); - - // Default device gets an extra ref, so it lives until a new default replaces it, even if disconnected. - if (current_default_device) { // (despite the name, it's no longer current at this point) - UnrefPhysicalAudioDevice(current_default_device); - } - - if (pending.next) { - SDL_LockRWLockForWriting(current_audio.device_hash_lock); - SDL_assert(current_audio.pending_events_tail != NULL); - SDL_assert(current_audio.pending_events_tail->next == NULL); - current_audio.pending_events_tail->next = pending.next; - current_audio.pending_events_tail = pending_tail; - SDL_UnlockRWLock(current_audio.device_hash_lock); - } -} - -bool SDL_AudioDeviceFormatChangedAlreadyLocked(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames) -{ - const int orig_work_buffer_size = device->work_buffer_size; - - // we don't currently have any place where channel maps change from under you, but we can check that if necessary later. - if (SDL_AudioSpecsEqual(&device->spec, newspec, NULL, NULL) && (new_sample_frames == device->sample_frames)) { - return true; // we're already in that format. - } - - SDL_copyp(&device->spec, newspec); - UpdateAudioStreamFormatsPhysical(device); - - bool kill_device = false; - - device->sample_frames = new_sample_frames; - SDL_UpdatedAudioDeviceFormat(device); - if (device->work_buffer && (device->work_buffer_size > orig_work_buffer_size)) { - SDL_aligned_free(device->work_buffer); - device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), device->work_buffer_size); - if (!device->work_buffer) { - kill_device = true; - } - - if (device->postmix_buffer) { - SDL_aligned_free(device->postmix_buffer); - device->postmix_buffer = (float *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), device->work_buffer_size); - if (!device->postmix_buffer) { - kill_device = true; - } - } - - SDL_aligned_free(device->mix_buffer); - device->mix_buffer = NULL; - if (device->spec.format != SDL_AUDIO_F32) { - device->mix_buffer = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), device->work_buffer_size); - if (!device->mix_buffer) { - kill_device = true; - } - } - } - - // Post an event for the physical device, and each logical device on this physical device. - if (!kill_device) { - // Queue up events to push to the queue next time it pumps (presumably - // in a safer thread). - // !!! FIXME: this duplicates some code we could probably refactor. - SDL_PendingAudioDeviceEvent pending; - pending.next = NULL; - SDL_PendingAudioDeviceEvent *pending_tail = &pending; - - SDL_PendingAudioDeviceEvent *p; - - p = (SDL_PendingAudioDeviceEvent *)SDL_malloc(sizeof(SDL_PendingAudioDeviceEvent)); - if (p) { // if this failed, no event for you, but you have deeper problems anyhow. - p->type = SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED; - p->devid = device->instance_id; - p->next = NULL; - pending_tail->next = p; - pending_tail = p; - } - - for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev; logdev = logdev->next) { - p = (SDL_PendingAudioDeviceEvent *)SDL_malloc(sizeof(SDL_PendingAudioDeviceEvent)); - if (p) { // if this failed, no event for you, but you have deeper problems anyhow. - p->type = SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED; - p->devid = logdev->instance_id; - p->next = NULL; - pending_tail->next = p; - pending_tail = p; - } - } - - if (pending.next) { - SDL_LockRWLockForWriting(current_audio.device_hash_lock); - SDL_assert(current_audio.pending_events_tail != NULL); - SDL_assert(current_audio.pending_events_tail->next == NULL); - current_audio.pending_events_tail->next = pending.next; - current_audio.pending_events_tail = pending_tail; - SDL_UnlockRWLock(current_audio.device_hash_lock); - } - } - - if (kill_device) { - return false; - } - return true; -} - -bool SDL_AudioDeviceFormatChanged(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames) -{ - ObtainPhysicalAudioDeviceObj(device); - const bool result = SDL_AudioDeviceFormatChangedAlreadyLocked(device, newspec, new_sample_frames); - ReleaseAudioDevice(device); - return result; -} - -// This is an internal function, so SDL_PumpEvents() can check for pending audio device events. -// ("UpdateSubsystem" is the same naming that the other things that hook into PumpEvents use.) -void SDL_UpdateAudio(void) -{ - SDL_LockRWLockForReading(current_audio.device_hash_lock); - SDL_PendingAudioDeviceEvent *pending_events = current_audio.pending_events.next; - SDL_UnlockRWLock(current_audio.device_hash_lock); - - if (!pending_events) { - return; // nothing to do, check next time. - } - - // okay, let's take this whole list of events so we can dump the lock, and new ones can queue up for a later update. - SDL_LockRWLockForWriting(current_audio.device_hash_lock); - pending_events = current_audio.pending_events.next; // in case this changed... - current_audio.pending_events.next = NULL; - current_audio.pending_events_tail = ¤t_audio.pending_events; - SDL_UnlockRWLock(current_audio.device_hash_lock); - - SDL_PendingAudioDeviceEvent *pending_next = NULL; - for (SDL_PendingAudioDeviceEvent *i = pending_events; i; i = pending_next) { - pending_next = i->next; - if (SDL_EventEnabled(i->type)) { - SDL_Event event; - SDL_zero(event); - event.type = i->type; - event.adevice.which = (Uint32) i->devid; - event.adevice.recording = ((i->devid & (1<<0)) == 0); // bit #0 of devid is set for playback devices and unset for recording. - SDL_PushEvent(&event); - } - SDL_free(i); - } -} - diff --git a/contrib/SDL-3.2.8/src/audio/SDL_audio_c.h b/contrib/SDL-3.2.8/src/audio/SDL_audio_c.h deleted file mode 100644 index 0e673f1..0000000 --- a/contrib/SDL-3.2.8/src/audio/SDL_audio_c.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef SDL_audio_c_h_ -#define SDL_audio_c_h_ - -extern void SDL_UpdateAudio(void); - -#endif // SDL_audio_c_h_ diff --git a/contrib/SDL-3.2.8/src/audio/SDL_audio_channel_converters.h b/contrib/SDL-3.2.8/src/audio/SDL_audio_channel_converters.h deleted file mode 100644 index ab682e6..0000000 --- a/contrib/SDL-3.2.8/src/audio/SDL_audio_channel_converters.h +++ /dev/null @@ -1,1068 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -// DO NOT EDIT, THIS FILE WAS GENERATED BY build-scripts/gen_audio_channel_conversion.c - - -typedef void (*SDL_AudioChannelConverter)(float *dst, const float *src, int num_frames); - -static void SDL_ConvertMonoToStereo(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("mono", "stereo"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1); - dst += (num_frames-1) * 2; - for (i = num_frames; i; i--, src--, dst -= 2) { - const float srcFC = src[0]; - dst[1] /* FR */ = srcFC; - dst[0] /* FL */ = srcFC; - } - -} - -static void SDL_ConvertMonoTo21(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("mono", "2.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1); - dst += (num_frames-1) * 3; - for (i = num_frames; i; i--, src--, dst -= 3) { - const float srcFC = src[0]; - dst[2] /* LFE */ = 0.0f; - dst[1] /* FR */ = srcFC; - dst[0] /* FL */ = srcFC; - } - -} - -static void SDL_ConvertMonoToQuad(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("mono", "quad"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1); - dst += (num_frames-1) * 4; - for (i = num_frames; i; i--, src--, dst -= 4) { - const float srcFC = src[0]; - dst[3] /* BR */ = 0.0f; - dst[2] /* BL */ = 0.0f; - dst[1] /* FR */ = srcFC; - dst[0] /* FL */ = srcFC; - } - -} - -static void SDL_ConvertMonoTo41(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("mono", "4.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1); - dst += (num_frames-1) * 5; - for (i = num_frames; i; i--, src--, dst -= 5) { - const float srcFC = src[0]; - dst[4] /* BR */ = 0.0f; - dst[3] /* BL */ = 0.0f; - dst[2] /* LFE */ = 0.0f; - dst[1] /* FR */ = srcFC; - dst[0] /* FL */ = srcFC; - } - -} - -static void SDL_ConvertMonoTo51(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("mono", "5.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1); - dst += (num_frames-1) * 6; - for (i = num_frames; i; i--, src--, dst -= 6) { - const float srcFC = src[0]; - dst[5] /* BR */ = 0.0f; - dst[4] /* BL */ = 0.0f; - dst[3] /* LFE */ = 0.0f; - dst[2] /* FC */ = 0.0f; - dst[1] /* FR */ = srcFC; - dst[0] /* FL */ = srcFC; - } - -} - -static void SDL_ConvertMonoTo61(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("mono", "6.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1); - dst += (num_frames-1) * 7; - for (i = num_frames; i; i--, src--, dst -= 7) { - const float srcFC = src[0]; - dst[6] /* SR */ = 0.0f; - dst[5] /* SL */ = 0.0f; - dst[4] /* BC */ = 0.0f; - dst[3] /* LFE */ = 0.0f; - dst[2] /* FC */ = 0.0f; - dst[1] /* FR */ = srcFC; - dst[0] /* FL */ = srcFC; - } - -} - -static void SDL_ConvertMonoTo71(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("mono", "7.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1); - dst += (num_frames-1) * 8; - for (i = num_frames; i; i--, src--, dst -= 8) { - const float srcFC = src[0]; - dst[7] /* SR */ = 0.0f; - dst[6] /* SL */ = 0.0f; - dst[5] /* BR */ = 0.0f; - dst[4] /* BL */ = 0.0f; - dst[3] /* LFE */ = 0.0f; - dst[2] /* FC */ = 0.0f; - dst[1] /* FR */ = srcFC; - dst[0] /* FL */ = srcFC; - } - -} - -static void SDL_ConvertStereoToMono(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("stereo", "mono"); - - for (i = num_frames; i; i--, src += 2, dst++) { - dst[0] /* FC */ = (src[0] * 0.500000000f) + (src[1] * 0.500000000f); - } - -} - -static void SDL_ConvertStereoTo21(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("stereo", "2.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 2; - dst += (num_frames-1) * 3; - for (i = num_frames; i; i--, src -= 2, dst -= 3) { - dst[2] /* LFE */ = 0.0f; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_ConvertStereoToQuad(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("stereo", "quad"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 2; - dst += (num_frames-1) * 4; - for (i = num_frames; i; i--, src -= 2, dst -= 4) { - dst[3] /* BR */ = 0.0f; - dst[2] /* BL */ = 0.0f; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_ConvertStereoTo41(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("stereo", "4.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 2; - dst += (num_frames-1) * 5; - for (i = num_frames; i; i--, src -= 2, dst -= 5) { - dst[4] /* BR */ = 0.0f; - dst[3] /* BL */ = 0.0f; - dst[2] /* LFE */ = 0.0f; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_ConvertStereoTo51(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("stereo", "5.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 2; - dst += (num_frames-1) * 6; - for (i = num_frames; i; i--, src -= 2, dst -= 6) { - dst[5] /* BR */ = 0.0f; - dst[4] /* BL */ = 0.0f; - dst[3] /* LFE */ = 0.0f; - dst[2] /* FC */ = 0.0f; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_ConvertStereoTo61(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("stereo", "6.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 2; - dst += (num_frames-1) * 7; - for (i = num_frames; i; i--, src -= 2, dst -= 7) { - dst[6] /* SR */ = 0.0f; - dst[5] /* SL */ = 0.0f; - dst[4] /* BC */ = 0.0f; - dst[3] /* LFE */ = 0.0f; - dst[2] /* FC */ = 0.0f; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_ConvertStereoTo71(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("stereo", "7.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 2; - dst += (num_frames-1) * 8; - for (i = num_frames; i; i--, src -= 2, dst -= 8) { - dst[7] /* SR */ = 0.0f; - dst[6] /* SL */ = 0.0f; - dst[5] /* BR */ = 0.0f; - dst[4] /* BL */ = 0.0f; - dst[3] /* LFE */ = 0.0f; - dst[2] /* FC */ = 0.0f; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_Convert21ToMono(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("2.1", "mono"); - - for (i = num_frames; i; i--, src += 3, dst++) { - dst[0] /* FC */ = (src[0] * 0.333333343f) + (src[1] * 0.333333343f) + (src[2] * 0.333333343f); - } - -} - -static void SDL_Convert21ToStereo(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("2.1", "stereo"); - - for (i = num_frames; i; i--, src += 3, dst += 2) { - const float srcLFE = src[2]; - dst[0] /* FL */ = (src[0] * 0.800000012f) + (srcLFE * 0.200000003f); - dst[1] /* FR */ = (src[1] * 0.800000012f) + (srcLFE * 0.200000003f); - } - -} - -static void SDL_Convert21ToQuad(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("2.1", "quad"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 3; - dst += (num_frames-1) * 4; - for (i = num_frames; i; i--, src -= 3, dst -= 4) { - const float srcLFE = src[2]; - dst[3] /* BR */ = (srcLFE * 0.111111112f); - dst[2] /* BL */ = (srcLFE * 0.111111112f); - dst[1] /* FR */ = (srcLFE * 0.111111112f) + (src[1] * 0.888888896f); - dst[0] /* FL */ = (srcLFE * 0.111111112f) + (src[0] * 0.888888896f); - } - -} - -static void SDL_Convert21To41(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("2.1", "4.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 3; - dst += (num_frames-1) * 5; - for (i = num_frames; i; i--, src -= 3, dst -= 5) { - dst[4] /* BR */ = 0.0f; - dst[3] /* BL */ = 0.0f; - dst[2] /* LFE */ = src[2]; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_Convert21To51(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("2.1", "5.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 3; - dst += (num_frames-1) * 6; - for (i = num_frames; i; i--, src -= 3, dst -= 6) { - dst[5] /* BR */ = 0.0f; - dst[4] /* BL */ = 0.0f; - dst[3] /* LFE */ = src[2]; - dst[2] /* FC */ = 0.0f; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_Convert21To61(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("2.1", "6.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 3; - dst += (num_frames-1) * 7; - for (i = num_frames; i; i--, src -= 3, dst -= 7) { - dst[6] /* SR */ = 0.0f; - dst[5] /* SL */ = 0.0f; - dst[4] /* BC */ = 0.0f; - dst[3] /* LFE */ = src[2]; - dst[2] /* FC */ = 0.0f; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_Convert21To71(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("2.1", "7.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 3; - dst += (num_frames-1) * 8; - for (i = num_frames; i; i--, src -= 3, dst -= 8) { - dst[7] /* SR */ = 0.0f; - dst[6] /* SL */ = 0.0f; - dst[5] /* BR */ = 0.0f; - dst[4] /* BL */ = 0.0f; - dst[3] /* LFE */ = src[2]; - dst[2] /* FC */ = 0.0f; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_ConvertQuadToMono(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("quad", "mono"); - - for (i = num_frames; i; i--, src += 4, dst++) { - dst[0] /* FC */ = (src[0] * 0.250000000f) + (src[1] * 0.250000000f) + (src[2] * 0.250000000f) + (src[3] * 0.250000000f); - } - -} - -static void SDL_ConvertQuadToStereo(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("quad", "stereo"); - - for (i = num_frames; i; i--, src += 4, dst += 2) { - const float srcBL = src[2]; - const float srcBR = src[3]; - dst[0] /* FL */ = (src[0] * 0.421000004f) + (srcBL * 0.358999997f) + (srcBR * 0.219999999f); - dst[1] /* FR */ = (src[1] * 0.421000004f) + (srcBL * 0.219999999f) + (srcBR * 0.358999997f); - } - -} - -static void SDL_ConvertQuadTo21(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("quad", "2.1"); - - for (i = num_frames; i; i--, src += 4, dst += 3) { - const float srcBL = src[2]; - const float srcBR = src[3]; - dst[0] /* FL */ = (src[0] * 0.421000004f) + (srcBL * 0.358999997f) + (srcBR * 0.219999999f); - dst[1] /* FR */ = (src[1] * 0.421000004f) + (srcBL * 0.219999999f) + (srcBR * 0.358999997f); - dst[2] /* LFE */ = 0.0f; - } - -} - -static void SDL_ConvertQuadTo41(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("quad", "4.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 4; - dst += (num_frames-1) * 5; - for (i = num_frames; i; i--, src -= 4, dst -= 5) { - dst[4] /* BR */ = src[3]; - dst[3] /* BL */ = src[2]; - dst[2] /* LFE */ = 0.0f; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_ConvertQuadTo51(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("quad", "5.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 4; - dst += (num_frames-1) * 6; - for (i = num_frames; i; i--, src -= 4, dst -= 6) { - dst[5] /* BR */ = src[3]; - dst[4] /* BL */ = src[2]; - dst[3] /* LFE */ = 0.0f; - dst[2] /* FC */ = 0.0f; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_ConvertQuadTo61(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("quad", "6.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 4; - dst += (num_frames-1) * 7; - for (i = num_frames; i; i--, src -= 4, dst -= 7) { - const float srcBL = src[2]; - const float srcBR = src[3]; - dst[6] /* SR */ = (srcBR * 0.796000004f); - dst[5] /* SL */ = (srcBL * 0.796000004f); - dst[4] /* BC */ = (srcBR * 0.500000000f) + (srcBL * 0.500000000f); - dst[3] /* LFE */ = 0.0f; - dst[2] /* FC */ = 0.0f; - dst[1] /* FR */ = (src[1] * 0.939999998f); - dst[0] /* FL */ = (src[0] * 0.939999998f); - } - -} - -static void SDL_ConvertQuadTo71(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("quad", "7.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 4; - dst += (num_frames-1) * 8; - for (i = num_frames; i; i--, src -= 4, dst -= 8) { - dst[7] /* SR */ = 0.0f; - dst[6] /* SL */ = 0.0f; - dst[5] /* BR */ = src[3]; - dst[4] /* BL */ = src[2]; - dst[3] /* LFE */ = 0.0f; - dst[2] /* FC */ = 0.0f; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_Convert41ToMono(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("4.1", "mono"); - - for (i = num_frames; i; i--, src += 5, dst++) { - dst[0] /* FC */ = (src[0] * 0.200000003f) + (src[1] * 0.200000003f) + (src[2] * 0.200000003f) + (src[3] * 0.200000003f) + (src[4] * 0.200000003f); - } - -} - -static void SDL_Convert41ToStereo(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("4.1", "stereo"); - - for (i = num_frames; i; i--, src += 5, dst += 2) { - const float srcLFE = src[2]; - const float srcBL = src[3]; - const float srcBR = src[4]; - dst[0] /* FL */ = (src[0] * 0.374222219f) + (srcLFE * 0.111111112f) + (srcBL * 0.319111109f) + (srcBR * 0.195555553f); - dst[1] /* FR */ = (src[1] * 0.374222219f) + (srcLFE * 0.111111112f) + (srcBL * 0.195555553f) + (srcBR * 0.319111109f); - } - -} - -static void SDL_Convert41To21(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("4.1", "2.1"); - - for (i = num_frames; i; i--, src += 5, dst += 3) { - const float srcBL = src[3]; - const float srcBR = src[4]; - dst[0] /* FL */ = (src[0] * 0.421000004f) + (srcBL * 0.358999997f) + (srcBR * 0.219999999f); - dst[1] /* FR */ = (src[1] * 0.421000004f) + (srcBL * 0.219999999f) + (srcBR * 0.358999997f); - dst[2] /* LFE */ = src[2]; - } - -} - -static void SDL_Convert41ToQuad(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("4.1", "quad"); - - for (i = num_frames; i; i--, src += 5, dst += 4) { - const float srcLFE = src[2]; - dst[0] /* FL */ = (src[0] * 0.941176474f) + (srcLFE * 0.058823530f); - dst[1] /* FR */ = (src[1] * 0.941176474f) + (srcLFE * 0.058823530f); - dst[2] /* BL */ = (srcLFE * 0.058823530f) + (src[3] * 0.941176474f); - dst[3] /* BR */ = (srcLFE * 0.058823530f) + (src[4] * 0.941176474f); - } - -} - -static void SDL_Convert41To51(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("4.1", "5.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 5; - dst += (num_frames-1) * 6; - for (i = num_frames; i; i--, src -= 5, dst -= 6) { - dst[5] /* BR */ = src[4]; - dst[4] /* BL */ = src[3]; - dst[3] /* LFE */ = src[2]; - dst[2] /* FC */ = 0.0f; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_Convert41To61(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("4.1", "6.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 5; - dst += (num_frames-1) * 7; - for (i = num_frames; i; i--, src -= 5, dst -= 7) { - const float srcBL = src[3]; - const float srcBR = src[4]; - dst[6] /* SR */ = (srcBR * 0.796000004f); - dst[5] /* SL */ = (srcBL * 0.796000004f); - dst[4] /* BC */ = (srcBR * 0.500000000f) + (srcBL * 0.500000000f); - dst[3] /* LFE */ = src[2]; - dst[2] /* FC */ = 0.0f; - dst[1] /* FR */ = (src[1] * 0.939999998f); - dst[0] /* FL */ = (src[0] * 0.939999998f); - } - -} - -static void SDL_Convert41To71(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("4.1", "7.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 5; - dst += (num_frames-1) * 8; - for (i = num_frames; i; i--, src -= 5, dst -= 8) { - dst[7] /* SR */ = 0.0f; - dst[6] /* SL */ = 0.0f; - dst[5] /* BR */ = src[4]; - dst[4] /* BL */ = src[3]; - dst[3] /* LFE */ = src[2]; - dst[2] /* FC */ = 0.0f; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_Convert51ToMono(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("5.1", "mono"); - - for (i = num_frames; i; i--, src += 6, dst++) { - dst[0] /* FC */ = (src[0] * 0.166666672f) + (src[1] * 0.166666672f) + (src[2] * 0.166666672f) + (src[3] * 0.166666672f) + (src[4] * 0.166666672f) + (src[5] * 0.166666672f); - } - -} - -static void SDL_Convert51ToStereo(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("5.1", "stereo"); - - for (i = num_frames; i; i--, src += 6, dst += 2) { - const float srcFC = src[2]; - const float srcLFE = src[3]; - const float srcBL = src[4]; - const float srcBR = src[5]; - dst[0] /* FL */ = (src[0] * 0.294545442f) + (srcFC * 0.208181813f) + (srcLFE * 0.090909094f) + (srcBL * 0.251818180f) + (srcBR * 0.154545456f); - dst[1] /* FR */ = (src[1] * 0.294545442f) + (srcFC * 0.208181813f) + (srcLFE * 0.090909094f) + (srcBL * 0.154545456f) + (srcBR * 0.251818180f); - } - -} - -static void SDL_Convert51To21(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("5.1", "2.1"); - - for (i = num_frames; i; i--, src += 6, dst += 3) { - const float srcFC = src[2]; - const float srcBL = src[4]; - const float srcBR = src[5]; - dst[0] /* FL */ = (src[0] * 0.324000001f) + (srcFC * 0.229000002f) + (srcBL * 0.277000010f) + (srcBR * 0.170000002f); - dst[1] /* FR */ = (src[1] * 0.324000001f) + (srcFC * 0.229000002f) + (srcBL * 0.170000002f) + (srcBR * 0.277000010f); - dst[2] /* LFE */ = src[3]; - } - -} - -static void SDL_Convert51ToQuad(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("5.1", "quad"); - - for (i = num_frames; i; i--, src += 6, dst += 4) { - const float srcFC = src[2]; - const float srcLFE = src[3]; - dst[0] /* FL */ = (src[0] * 0.558095276f) + (srcFC * 0.394285709f) + (srcLFE * 0.047619049f); - dst[1] /* FR */ = (src[1] * 0.558095276f) + (srcFC * 0.394285709f) + (srcLFE * 0.047619049f); - dst[2] /* BL */ = (srcLFE * 0.047619049f) + (src[4] * 0.558095276f); - dst[3] /* BR */ = (srcLFE * 0.047619049f) + (src[5] * 0.558095276f); - } - -} - -static void SDL_Convert51To41(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("5.1", "4.1"); - - for (i = num_frames; i; i--, src += 6, dst += 5) { - const float srcFC = src[2]; - dst[0] /* FL */ = (src[0] * 0.586000025f) + (srcFC * 0.414000005f); - dst[1] /* FR */ = (src[1] * 0.586000025f) + (srcFC * 0.414000005f); - dst[2] /* LFE */ = src[3]; - dst[3] /* BL */ = (src[4] * 0.586000025f); - dst[4] /* BR */ = (src[5] * 0.586000025f); - } - -} - -static void SDL_Convert51To61(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("5.1", "6.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 6; - dst += (num_frames-1) * 7; - for (i = num_frames; i; i--, src -= 6, dst -= 7) { - const float srcBL = src[4]; - const float srcBR = src[5]; - dst[6] /* SR */ = (srcBR * 0.796000004f); - dst[5] /* SL */ = (srcBL * 0.796000004f); - dst[4] /* BC */ = (srcBR * 0.500000000f) + (srcBL * 0.500000000f); - dst[3] /* LFE */ = src[3]; - dst[2] /* FC */ = (src[2] * 0.939999998f); - dst[1] /* FR */ = (src[1] * 0.939999998f); - dst[0] /* FL */ = (src[0] * 0.939999998f); - } - -} - -static void SDL_Convert51To71(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("5.1", "7.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 6; - dst += (num_frames-1) * 8; - for (i = num_frames; i; i--, src -= 6, dst -= 8) { - dst[7] /* SR */ = 0.0f; - dst[6] /* SL */ = 0.0f; - dst[5] /* BR */ = src[5]; - dst[4] /* BL */ = src[4]; - dst[3] /* LFE */ = src[3]; - dst[2] /* FC */ = src[2]; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_Convert61ToMono(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("6.1", "mono"); - - for (i = num_frames; i; i--, src += 7, dst++) { - dst[0] /* FC */ = (src[0] * 0.143142849f) + (src[1] * 0.143142849f) + (src[2] * 0.143142849f) + (src[3] * 0.142857149f) + (src[4] * 0.143142849f) + (src[5] * 0.143142849f) + (src[6] * 0.143142849f); - } - -} - -static void SDL_Convert61ToStereo(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("6.1", "stereo"); - - for (i = num_frames; i; i--, src += 7, dst += 2) { - const float srcFC = src[2]; - const float srcLFE = src[3]; - const float srcBC = src[4]; - const float srcSL = src[5]; - const float srcSR = src[6]; - dst[0] /* FL */ = (src[0] * 0.247384623f) + (srcFC * 0.174461529f) + (srcLFE * 0.076923080f) + (srcBC * 0.174461529f) + (srcSL * 0.226153851f) + (srcSR * 0.100615382f); - dst[1] /* FR */ = (src[1] * 0.247384623f) + (srcFC * 0.174461529f) + (srcLFE * 0.076923080f) + (srcBC * 0.174461529f) + (srcSL * 0.100615382f) + (srcSR * 0.226153851f); - } - -} - -static void SDL_Convert61To21(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("6.1", "2.1"); - - for (i = num_frames; i; i--, src += 7, dst += 3) { - const float srcFC = src[2]; - const float srcBC = src[4]; - const float srcSL = src[5]; - const float srcSR = src[6]; - dst[0] /* FL */ = (src[0] * 0.268000007f) + (srcFC * 0.188999996f) + (srcBC * 0.188999996f) + (srcSL * 0.245000005f) + (srcSR * 0.108999997f); - dst[1] /* FR */ = (src[1] * 0.268000007f) + (srcFC * 0.188999996f) + (srcBC * 0.188999996f) + (srcSL * 0.108999997f) + (srcSR * 0.245000005f); - dst[2] /* LFE */ = src[3]; - } - -} - -static void SDL_Convert61ToQuad(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("6.1", "quad"); - - for (i = num_frames; i; i--, src += 7, dst += 4) { - const float srcFC = src[2]; - const float srcLFE = src[3]; - const float srcBC = src[4]; - const float srcSL = src[5]; - const float srcSR = src[6]; - dst[0] /* FL */ = (src[0] * 0.463679999f) + (srcFC * 0.327360004f) + (srcLFE * 0.040000003f) + (srcSL * 0.168960005f); - dst[1] /* FR */ = (src[1] * 0.463679999f) + (srcFC * 0.327360004f) + (srcLFE * 0.040000003f) + (srcSR * 0.168960005f); - dst[2] /* BL */ = (srcLFE * 0.040000003f) + (srcBC * 0.327360004f) + (srcSL * 0.431039989f); - dst[3] /* BR */ = (srcLFE * 0.040000003f) + (srcBC * 0.327360004f) + (srcSR * 0.431039989f); - } - -} - -static void SDL_Convert61To41(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("6.1", "4.1"); - - for (i = num_frames; i; i--, src += 7, dst += 5) { - const float srcFC = src[2]; - const float srcBC = src[4]; - const float srcSL = src[5]; - const float srcSR = src[6]; - dst[0] /* FL */ = (src[0] * 0.483000010f) + (srcFC * 0.340999991f) + (srcSL * 0.175999999f); - dst[1] /* FR */ = (src[1] * 0.483000010f) + (srcFC * 0.340999991f) + (srcSR * 0.175999999f); - dst[2] /* LFE */ = src[3]; - dst[3] /* BL */ = (srcBC * 0.340999991f) + (srcSL * 0.449000001f); - dst[4] /* BR */ = (srcBC * 0.340999991f) + (srcSR * 0.449000001f); - } - -} - -static void SDL_Convert61To51(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("6.1", "5.1"); - - for (i = num_frames; i; i--, src += 7, dst += 6) { - const float srcBC = src[4]; - const float srcSL = src[5]; - const float srcSR = src[6]; - dst[0] /* FL */ = (src[0] * 0.611000001f) + (srcSL * 0.223000005f); - dst[1] /* FR */ = (src[1] * 0.611000001f) + (srcSR * 0.223000005f); - dst[2] /* FC */ = (src[2] * 0.611000001f); - dst[3] /* LFE */ = src[3]; - dst[4] /* BL */ = (srcBC * 0.432000011f) + (srcSL * 0.568000019f); - dst[5] /* BR */ = (srcBC * 0.432000011f) + (srcSR * 0.568000019f); - } - -} - -static void SDL_Convert61To71(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("6.1", "7.1"); - - // convert backwards, since output is growing in-place. - src += (num_frames-1) * 7; - dst += (num_frames-1) * 8; - for (i = num_frames; i; i--, src -= 7, dst -= 8) { - const float srcBC = src[4]; - dst[7] /* SR */ = src[6]; - dst[6] /* SL */ = src[5]; - dst[5] /* BR */ = (srcBC * 0.707000017f); - dst[4] /* BL */ = (srcBC * 0.707000017f); - dst[3] /* LFE */ = src[3]; - dst[2] /* FC */ = src[2]; - dst[1] /* FR */ = src[1]; - dst[0] /* FL */ = src[0]; - } - -} - -static void SDL_Convert71ToMono(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("7.1", "mono"); - - for (i = num_frames; i; i--, src += 8, dst++) { - dst[0] /* FC */ = (src[0] * 0.125125006f) + (src[1] * 0.125125006f) + (src[2] * 0.125125006f) + (src[3] * 0.125000000f) + (src[4] * 0.125125006f) + (src[5] * 0.125125006f) + (src[6] * 0.125125006f) + (src[7] * 0.125125006f); - } - -} - -static void SDL_Convert71ToStereo(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("7.1", "stereo"); - - for (i = num_frames; i; i--, src += 8, dst += 2) { - const float srcFC = src[2]; - const float srcLFE = src[3]; - const float srcBL = src[4]; - const float srcBR = src[5]; - const float srcSL = src[6]; - const float srcSR = src[7]; - dst[0] /* FL */ = (src[0] * 0.211866662f) + (srcFC * 0.150266662f) + (srcLFE * 0.066666670f) + (srcBL * 0.181066677f) + (srcBR * 0.111066669f) + (srcSL * 0.194133341f) + (srcSR * 0.085866667f); - dst[1] /* FR */ = (src[1] * 0.211866662f) + (srcFC * 0.150266662f) + (srcLFE * 0.066666670f) + (srcBL * 0.111066669f) + (srcBR * 0.181066677f) + (srcSL * 0.085866667f) + (srcSR * 0.194133341f); - } - -} - -static void SDL_Convert71To21(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("7.1", "2.1"); - - for (i = num_frames; i; i--, src += 8, dst += 3) { - const float srcFC = src[2]; - const float srcBL = src[4]; - const float srcBR = src[5]; - const float srcSL = src[6]; - const float srcSR = src[7]; - dst[0] /* FL */ = (src[0] * 0.226999998f) + (srcFC * 0.160999998f) + (srcBL * 0.194000006f) + (srcBR * 0.119000003f) + (srcSL * 0.208000004f) + (srcSR * 0.092000000f); - dst[1] /* FR */ = (src[1] * 0.226999998f) + (srcFC * 0.160999998f) + (srcBL * 0.119000003f) + (srcBR * 0.194000006f) + (srcSL * 0.092000000f) + (srcSR * 0.208000004f); - dst[2] /* LFE */ = src[3]; - } - -} - -static void SDL_Convert71ToQuad(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("7.1", "quad"); - - for (i = num_frames; i; i--, src += 8, dst += 4) { - const float srcFC = src[2]; - const float srcLFE = src[3]; - const float srcSL = src[6]; - const float srcSR = src[7]; - dst[0] /* FL */ = (src[0] * 0.466344833f) + (srcFC * 0.329241365f) + (srcLFE * 0.034482758f) + (srcSL * 0.169931039f); - dst[1] /* FR */ = (src[1] * 0.466344833f) + (srcFC * 0.329241365f) + (srcLFE * 0.034482758f) + (srcSR * 0.169931039f); - dst[2] /* BL */ = (srcLFE * 0.034482758f) + (src[4] * 0.466344833f) + (srcSL * 0.433517247f); - dst[3] /* BR */ = (srcLFE * 0.034482758f) + (src[5] * 0.466344833f) + (srcSR * 0.433517247f); - } - -} - -static void SDL_Convert71To41(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("7.1", "4.1"); - - for (i = num_frames; i; i--, src += 8, dst += 5) { - const float srcFC = src[2]; - const float srcSL = src[6]; - const float srcSR = src[7]; - dst[0] /* FL */ = (src[0] * 0.483000010f) + (srcFC * 0.340999991f) + (srcSL * 0.175999999f); - dst[1] /* FR */ = (src[1] * 0.483000010f) + (srcFC * 0.340999991f) + (srcSR * 0.175999999f); - dst[2] /* LFE */ = src[3]; - dst[3] /* BL */ = (src[4] * 0.483000010f) + (srcSL * 0.449000001f); - dst[4] /* BR */ = (src[5] * 0.483000010f) + (srcSR * 0.449000001f); - } - -} - -static void SDL_Convert71To51(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("7.1", "5.1"); - - for (i = num_frames; i; i--, src += 8, dst += 6) { - const float srcSL = src[6]; - const float srcSR = src[7]; - dst[0] /* FL */ = (src[0] * 0.518000007f) + (srcSL * 0.188999996f); - dst[1] /* FR */ = (src[1] * 0.518000007f) + (srcSR * 0.188999996f); - dst[2] /* FC */ = (src[2] * 0.518000007f); - dst[3] /* LFE */ = src[3]; - dst[4] /* BL */ = (src[4] * 0.518000007f) + (srcSL * 0.481999993f); - dst[5] /* BR */ = (src[5] * 0.518000007f) + (srcSR * 0.481999993f); - } - -} - -static void SDL_Convert71To61(float *dst, const float *src, int num_frames) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("7.1", "6.1"); - - for (i = num_frames; i; i--, src += 8, dst += 7) { - const float srcBL = src[4]; - const float srcBR = src[5]; - dst[0] /* FL */ = (src[0] * 0.541000009f); - dst[1] /* FR */ = (src[1] * 0.541000009f); - dst[2] /* FC */ = (src[2] * 0.541000009f); - dst[3] /* LFE */ = src[3]; - dst[4] /* BC */ = (srcBL * 0.287999988f) + (srcBR * 0.287999988f); - dst[5] /* SL */ = (srcBL * 0.458999991f) + (src[6] * 0.541000009f); - dst[6] /* SR */ = (srcBR * 0.458999991f) + (src[7] * 0.541000009f); - } - -} - -static const SDL_AudioChannelConverter channel_converters[8][8] = { // [from][to] - { NULL, SDL_ConvertMonoToStereo, SDL_ConvertMonoTo21, SDL_ConvertMonoToQuad, SDL_ConvertMonoTo41, SDL_ConvertMonoTo51, SDL_ConvertMonoTo61, SDL_ConvertMonoTo71 }, - { SDL_ConvertStereoToMono, NULL, SDL_ConvertStereoTo21, SDL_ConvertStereoToQuad, SDL_ConvertStereoTo41, SDL_ConvertStereoTo51, SDL_ConvertStereoTo61, SDL_ConvertStereoTo71 }, - { SDL_Convert21ToMono, SDL_Convert21ToStereo, NULL, SDL_Convert21ToQuad, SDL_Convert21To41, SDL_Convert21To51, SDL_Convert21To61, SDL_Convert21To71 }, - { SDL_ConvertQuadToMono, SDL_ConvertQuadToStereo, SDL_ConvertQuadTo21, NULL, SDL_ConvertQuadTo41, SDL_ConvertQuadTo51, SDL_ConvertQuadTo61, SDL_ConvertQuadTo71 }, - { SDL_Convert41ToMono, SDL_Convert41ToStereo, SDL_Convert41To21, SDL_Convert41ToQuad, NULL, SDL_Convert41To51, SDL_Convert41To61, SDL_Convert41To71 }, - { SDL_Convert51ToMono, SDL_Convert51ToStereo, SDL_Convert51To21, SDL_Convert51ToQuad, SDL_Convert51To41, NULL, SDL_Convert51To61, SDL_Convert51To71 }, - { SDL_Convert61ToMono, SDL_Convert61ToStereo, SDL_Convert61To21, SDL_Convert61ToQuad, SDL_Convert61To41, SDL_Convert61To51, NULL, SDL_Convert61To71 }, - { SDL_Convert71ToMono, SDL_Convert71ToStereo, SDL_Convert71To21, SDL_Convert71ToQuad, SDL_Convert71To41, SDL_Convert71To51, SDL_Convert71To61, NULL } -}; - diff --git a/contrib/SDL-3.2.8/src/audio/SDL_audiocvt.c b/contrib/SDL-3.2.8/src/audio/SDL_audiocvt.c deleted file mode 100644 index f751b0e..0000000 --- a/contrib/SDL-3.2.8/src/audio/SDL_audiocvt.c +++ /dev/null @@ -1,1381 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#include "SDL_sysaudio.h" - -#include "SDL_audioqueue.h" -#include "SDL_audioresample.h" - -#ifndef SDL_INT_MAX -#define SDL_INT_MAX ((int)(~0u>>1)) -#endif - -#ifdef SDL_SSE3_INTRINSICS -// Convert from stereo to mono. Average left and right. -static void SDL_TARGETING("sse3") SDL_ConvertStereoToMono_SSE3(float *dst, const float *src, int num_frames) -{ - LOG_DEBUG_AUDIO_CONVERT("stereo", "mono (using SSE3)"); - - const __m128 divby2 = _mm_set1_ps(0.5f); - int i = num_frames; - - /* Do SSE blocks as long as we have 16 bytes available. - Just use unaligned load/stores, if the memory at runtime is - aligned it'll be just as fast on modern processors */ - while (i >= 4) { // 4 * float32 - _mm_storeu_ps(dst, _mm_mul_ps(_mm_hadd_ps(_mm_loadu_ps(src), _mm_loadu_ps(src + 4)), divby2)); - i -= 4; - src += 8; - dst += 4; - } - - // Finish off any leftovers with scalar operations. - while (i) { - *dst = (src[0] + src[1]) * 0.5f; - dst++; - i--; - src += 2; - } -} -#endif - -#ifdef SDL_SSE_INTRINSICS -// Convert from mono to stereo. Duplicate to stereo left and right. -static void SDL_TARGETING("sse") SDL_ConvertMonoToStereo_SSE(float *dst, const float *src, int num_frames) -{ - LOG_DEBUG_AUDIO_CONVERT("mono", "stereo (using SSE)"); - - // convert backwards, since output is growing in-place. - src += (num_frames-4) * 1; - dst += (num_frames-4) * 2; - - /* Do SSE blocks as long as we have 16 bytes available. - Just use unaligned load/stores, if the memory at runtime is - aligned it'll be just as fast on modern processors */ - // convert backwards, since output is growing in-place. - int i = num_frames; - while (i >= 4) { // 4 * float32 - const __m128 input = _mm_loadu_ps(src); // A B C D - _mm_storeu_ps(dst, _mm_unpacklo_ps(input, input)); // A A B B - _mm_storeu_ps(dst + 4, _mm_unpackhi_ps(input, input)); // C C D D - i -= 4; - src -= 4; - dst -= 8; - } - - // Finish off any leftovers with scalar operations. - src += 3; - dst += 6; // adjust for smaller buffers. - while (i) { // convert backwards, since output is growing in-place. - const float srcFC = src[0]; - dst[1] /* FR */ = srcFC; - dst[0] /* FL */ = srcFC; - i--; - src--; - dst -= 2; - } -} -#endif - -// Include the autogenerated channel converters... -#include "SDL_audio_channel_converters.h" - -static bool SDL_IsSupportedAudioFormat(const SDL_AudioFormat fmt) -{ - switch (fmt) { - case SDL_AUDIO_U8: - case SDL_AUDIO_S8: - case SDL_AUDIO_S16LE: - case SDL_AUDIO_S16BE: - case SDL_AUDIO_S32LE: - case SDL_AUDIO_S32BE: - case SDL_AUDIO_F32LE: - case SDL_AUDIO_F32BE: - return true; // supported. - - default: - break; - } - - return false; // unsupported. -} - -static bool SDL_IsSupportedChannelCount(const int channels) -{ - return ((channels >= 1) && (channels <= 8)); -} - -bool SDL_ChannelMapIsBogus(const int *chmap, int channels) -{ - if (chmap) { - for (int i = 0; i < channels; i++) { - const int mapping = chmap[i]; - if ((mapping < -1) || (mapping >= channels)) { - return true; - } - } - } - return false; -} - -bool SDL_ChannelMapIsDefault(const int *chmap, int channels) -{ - if (chmap) { - for (int i = 0; i < channels; i++) { - if (chmap[i] != i) { - return false; - } - } - } - return true; -} - -// Swizzle audio channels. src and dst can be the same pointer. It does not change the buffer size. -static void SwizzleAudio(const int num_frames, void *dst, const void *src, int channels, const int *map, SDL_AudioFormat fmt) -{ - const int bitsize = (int) SDL_AUDIO_BITSIZE(fmt); - - bool has_null_mappings = false; // !!! FIXME: calculate this when setting the channel map instead. - for (int i = 0; i < channels; i++) { - if (map[i] == -1) { - has_null_mappings = true; - break; - } - } - - #define CHANNEL_SWIZZLE(bits) { \ - Uint##bits *tdst = (Uint##bits *) dst; /* treat as UintX; we only care about moving bits and not the type here. */ \ - const Uint##bits *tsrc = (const Uint##bits *) src; \ - if (src != dst) { /* don't need to copy to a temporary frame first. */ \ - if (has_null_mappings) { \ - const Uint##bits silence = (Uint##bits) SDL_GetSilenceValueForFormat(fmt); \ - for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \ - for (int ch = 0; ch < channels; ch++) { \ - const int m = map[ch]; \ - tdst[ch] = (m == -1) ? silence : tsrc[m]; \ - } \ - } \ - } else { \ - for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \ - for (int ch = 0; ch < channels; ch++) { \ - tdst[ch] = tsrc[map[ch]]; \ - } \ - } \ - } \ - } else { \ - bool isstack; \ - Uint##bits *tmp = (Uint##bits *) SDL_small_alloc(int, channels, &isstack); /* !!! FIXME: allocate this when setting the channel map instead. */ \ - if (tmp) { \ - if (has_null_mappings) { \ - const Uint##bits silence = (Uint##bits) SDL_GetSilenceValueForFormat(fmt); \ - for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \ - for (int ch = 0; ch < channels; ch++) { \ - const int m = map[ch]; \ - tmp[ch] = (m == -1) ? silence : tsrc[m]; \ - } \ - for (int ch = 0; ch < channels; ch++) { \ - tdst[ch] = tmp[ch]; \ - } \ - } \ - } else { \ - for (int i = 0; i < num_frames; i++, tsrc += channels, tdst += channels) { \ - for (int ch = 0; ch < channels; ch++) { \ - tmp[ch] = tsrc[map[ch]]; \ - } \ - for (int ch = 0; ch < channels; ch++) { \ - tdst[ch] = tmp[ch]; \ - } \ - } \ - } \ - SDL_small_free(tmp, isstack); \ - } \ - } \ - } - - switch (bitsize) { - case 8: CHANNEL_SWIZZLE(8); break; - case 16: CHANNEL_SWIZZLE(16); break; - case 32: CHANNEL_SWIZZLE(32); break; - // we don't currently have int64 or double audio datatypes, so no `case 64` for now. - default: SDL_assert(!"Unsupported audio datatype size"); break; - } - - #undef CHANNEL_SWIZZLE -} - - -// This does type and channel conversions _but not resampling_ (resampling happens in SDL_AudioStream). -// This does not check parameter validity, (beyond asserts), it expects you did that already! -// All of this has to function as if src==dst==scratch (conversion in-place), but as a convenience -// if you're just going to copy the final output elsewhere, you can specify a different output pointer. -// -// The scratch buffer must be able to store `num_frames * CalculateMaxSampleFrameSize(src_format, src_channels, dst_format, dst_channels)` bytes. -// If the scratch buffer is NULL, this restriction applies to the output buffer instead. -// -// Since this is a convenient point that audio goes through even if it doesn't need format conversion, -// we also handle gain adjustment here, so we don't have to make another pass over the data later. -// Strictly speaking, this is also a "conversion". :) -void ConvertAudio(int num_frames, - const void *src, SDL_AudioFormat src_format, int src_channels, const int *src_map, - void *dst, SDL_AudioFormat dst_format, int dst_channels, const int *dst_map, - void *scratch, float gain) -{ - SDL_assert(src != NULL); - SDL_assert(dst != NULL); - SDL_assert(SDL_IsSupportedAudioFormat(src_format)); - SDL_assert(SDL_IsSupportedAudioFormat(dst_format)); - SDL_assert(SDL_IsSupportedChannelCount(src_channels)); - SDL_assert(SDL_IsSupportedChannelCount(dst_channels)); - - if (!num_frames) { - return; // no data to convert, quit. - } - -#if DEBUG_AUDIO_CONVERT - SDL_Log("SDL_AUDIO_CONVERT: Convert format %04x->%04x, channels %u->%u", src_format, dst_format, src_channels, dst_channels); -#endif - - const int dst_bitsize = (int) SDL_AUDIO_BITSIZE(dst_format); - const int dst_sample_frame_size = (dst_bitsize / 8) * dst_channels; - - const bool chmaps_match = (src_channels == dst_channels) && SDL_AudioChannelMapsEqual(src_channels, src_map, dst_map); - if (chmaps_match) { - src_map = dst_map = NULL; // NULL both these out so we don't do any unnecessary swizzling. - } - - /* Type conversion goes like this now: - - swizzle through source channel map to "standard" layout. - - byteswap to CPU native format first if necessary. - - convert to native Float32 if necessary. - - change channel count if necessary. - - convert to final data format. - - byteswap back to foreign format if necessary. - - swizzle through dest channel map from "standard" layout. - - The expectation is we can process data faster in float32 - (possibly with SIMD), and making several passes over the same - buffer is likely to be CPU cache-friendly, avoiding the - biggest performance hit in modern times. Previously we had - (script-generated) custom converters for every data type and - it was a bloat on SDL compile times and final library size. */ - - // swizzle input to "standard" format if necessary. - if (src_map) { - void* buf = scratch ? scratch : dst; // use scratch if available, since it has to be big enough to hold src, unless it's NULL, then dst has to be. - SwizzleAudio(num_frames, buf, src, src_channels, src_map, src_format); - src = buf; - } - - // see if we can skip float conversion entirely. - if ((src_channels == dst_channels) && (gain == 1.0f)) { - if (src_format == dst_format) { - // nothing to do, we're already in the right format, just copy it over if necessary. - if (dst_map) { - SwizzleAudio(num_frames, dst, src, dst_channels, dst_map, dst_format); - } else if (src != dst) { - SDL_memcpy(dst, src, num_frames * dst_sample_frame_size); - } - return; - } - - // just a byteswap needed? - if ((src_format ^ dst_format) == SDL_AUDIO_MASK_BIG_ENDIAN) { - if (dst_map) { // do this first, in case we duplicate channels, we can avoid an extra copy if src != dst. - SwizzleAudio(num_frames, dst, src, dst_channels, dst_map, dst_format); - src = dst; - } - ConvertAudioSwapEndian(dst, src, num_frames * dst_channels, dst_bitsize); - return; // all done. - } - } - - if (!scratch) { - scratch = dst; - } - - const bool srcconvert = src_format != SDL_AUDIO_F32; - const bool channelconvert = src_channels != dst_channels; - const bool dstconvert = dst_format != SDL_AUDIO_F32; - - // get us to float format. - if (srcconvert) { - void* buf = (channelconvert || dstconvert) ? scratch : dst; - ConvertAudioToFloat((float *) buf, src, num_frames * src_channels, src_format); - src = buf; - } - - // Gain adjustment - if (gain != 1.0f) { - float *buf = (float *)((channelconvert || dstconvert) ? scratch : dst); - const int total_samples = num_frames * src_channels; - if (src == buf) { - for (int i = 0; i < total_samples; i++) { - buf[i] *= gain; - } - } else { - float *fsrc = (float *)src; - for (int i = 0; i < total_samples; i++) { - buf[i] = fsrc[i] * gain; - } - } - src = buf; - } - - // Channel conversion - - if (channelconvert) { - SDL_AudioChannelConverter channel_converter; - SDL_AudioChannelConverter override = NULL; - - // SDL_IsSupportedChannelCount should have caught these asserts, or we added a new format and forgot to update the table. - SDL_assert(src_channels <= SDL_arraysize(channel_converters)); - SDL_assert(dst_channels <= SDL_arraysize(channel_converters[0])); - - channel_converter = channel_converters[src_channels - 1][dst_channels - 1]; - SDL_assert(channel_converter != NULL); - - // swap in some SIMD versions for a few of these. - if (channel_converter == SDL_ConvertStereoToMono) { - #ifdef SDL_SSE3_INTRINSICS - if (!override && SDL_HasSSE3()) { override = SDL_ConvertStereoToMono_SSE3; } - #endif - } else if (channel_converter == SDL_ConvertMonoToStereo) { - #ifdef SDL_SSE_INTRINSICS - if (!override && SDL_HasSSE()) { override = SDL_ConvertMonoToStereo_SSE; } - #endif - } - - if (override) { - channel_converter = override; - } - - void* buf = dstconvert ? scratch : dst; - channel_converter((float *) buf, (const float *) src, num_frames); - src = buf; - } - - // Resampling is not done in here. SDL_AudioStream handles that. - - // Move to final data type. - if (dstconvert) { - ConvertAudioFromFloat(dst, (const float *) src, num_frames * dst_channels, dst_format); - src = dst; - } - - SDL_assert(src == dst); // if we got here, we _had_ to have done _something_. Otherwise, we should have memcpy'd! - - if (dst_map) { - SwizzleAudio(num_frames, dst, src, dst_channels, dst_map, dst_format); - } -} - -// Calculate the largest frame size needed to convert between the two formats. -static int CalculateMaxFrameSize(SDL_AudioFormat src_format, int src_channels, SDL_AudioFormat dst_format, int dst_channels) -{ - const int src_format_size = SDL_AUDIO_BYTESIZE(src_format); - const int dst_format_size = SDL_AUDIO_BYTESIZE(dst_format); - const int max_app_format_size = SDL_max(src_format_size, dst_format_size); - const int max_format_size = SDL_max(max_app_format_size, sizeof (float)); // ConvertAudio and ResampleAudio use floats. - const int max_channels = SDL_max(src_channels, dst_channels); - return max_format_size * max_channels; -} - -static Sint64 GetAudioStreamResampleRate(SDL_AudioStream* stream, int src_freq, Sint64 resample_offset) -{ - src_freq = (int)((float)src_freq * stream->freq_ratio); - - Sint64 resample_rate = SDL_GetResampleRate(src_freq, stream->dst_spec.freq); - - // If src_freq == dst_freq, and we aren't between frames, don't resample - if ((resample_rate == 0x100000000) && (resample_offset == 0)) { - resample_rate = 0; - } - - return resample_rate; -} - -static bool UpdateAudioStreamInputSpec(SDL_AudioStream *stream, const SDL_AudioSpec *spec, const int *chmap) -{ - if (SDL_AudioSpecsEqual(&stream->input_spec, spec, stream->input_chmap, chmap)) { - return true; - } - - if (!SDL_ResetAudioQueueHistory(stream->queue, SDL_GetResamplerHistoryFrames())) { - return false; - } - - if (!chmap) { - stream->input_chmap = NULL; - } else { - const size_t chmaplen = sizeof (*chmap) * spec->channels; - stream->input_chmap = stream->input_chmap_storage; - SDL_memcpy(stream->input_chmap, chmap, chmaplen); - } - - SDL_copyp(&stream->input_spec, spec); - - return true; -} - -SDL_AudioStream *SDL_CreateAudioStream(const SDL_AudioSpec *src_spec, const SDL_AudioSpec *dst_spec) -{ - SDL_ChooseAudioConverters(); - SDL_SetupAudioResampler(); - - SDL_AudioStream *result = (SDL_AudioStream *)SDL_calloc(1, sizeof(SDL_AudioStream)); - if (!result) { - return NULL; - } - - result->freq_ratio = 1.0f; - result->gain = 1.0f; - result->queue = SDL_CreateAudioQueue(8192); - - if (!result->queue) { - SDL_free(result); - return NULL; - } - - result->lock = SDL_CreateMutex(); - if (!result->lock) { - SDL_free(result->queue); - SDL_free(result); - return NULL; - } - - OnAudioStreamCreated(result); - - if (!SDL_SetAudioStreamFormat(result, src_spec, dst_spec)) { - SDL_DestroyAudioStream(result); - return NULL; - } - - return result; -} - -SDL_PropertiesID SDL_GetAudioStreamProperties(SDL_AudioStream *stream) -{ - if (!stream) { - SDL_InvalidParamError("stream"); - return 0; - } - SDL_LockMutex(stream->lock); - if (stream->props == 0) { - stream->props = SDL_CreateProperties(); - } - SDL_UnlockMutex(stream->lock); - return stream->props; -} - -bool SDL_SetAudioStreamGetCallback(SDL_AudioStream *stream, SDL_AudioStreamCallback callback, void *userdata) -{ - if (!stream) { - return SDL_InvalidParamError("stream"); - } - SDL_LockMutex(stream->lock); - stream->get_callback = callback; - stream->get_callback_userdata = userdata; - SDL_UnlockMutex(stream->lock); - return true; -} - -bool SDL_SetAudioStreamPutCallback(SDL_AudioStream *stream, SDL_AudioStreamCallback callback, void *userdata) -{ - if (!stream) { - return SDL_InvalidParamError("stream"); - } - SDL_LockMutex(stream->lock); - stream->put_callback = callback; - stream->put_callback_userdata = userdata; - SDL_UnlockMutex(stream->lock); - return true; -} - -bool SDL_LockAudioStream(SDL_AudioStream *stream) -{ - if (!stream) { - return SDL_InvalidParamError("stream"); - } - SDL_LockMutex(stream->lock); - return true; -} - -bool SDL_UnlockAudioStream(SDL_AudioStream *stream) -{ - if (!stream) { - return SDL_InvalidParamError("stream"); - } - SDL_UnlockMutex(stream->lock); - return true; -} - -bool SDL_GetAudioStreamFormat(SDL_AudioStream *stream, SDL_AudioSpec *src_spec, SDL_AudioSpec *dst_spec) -{ - if (!stream) { - return SDL_InvalidParamError("stream"); - } - - SDL_LockMutex(stream->lock); - if (src_spec) { - SDL_copyp(src_spec, &stream->src_spec); - } - if (dst_spec) { - SDL_copyp(dst_spec, &stream->dst_spec); - } - SDL_UnlockMutex(stream->lock); - - if (src_spec && src_spec->format == 0) { - return SDL_SetError("Stream has no source format"); - } else if (dst_spec && dst_spec->format == 0) { - return SDL_SetError("Stream has no destination format"); - } - - return true; -} - -bool SDL_SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_spec, const SDL_AudioSpec *dst_spec) -{ - if (!stream) { - return SDL_InvalidParamError("stream"); - } - - // note that while we've removed the maximum frequency checks, SDL _will_ - // fail to resample to extremely high sample rates correctly. Really high, - // like 196608000Hz. File a bug. :P - - if (src_spec) { - if (!SDL_IsSupportedAudioFormat(src_spec->format)) { - return SDL_InvalidParamError("src_spec->format"); - } else if (!SDL_IsSupportedChannelCount(src_spec->channels)) { - return SDL_InvalidParamError("src_spec->channels"); - } else if (src_spec->freq <= 0) { - return SDL_InvalidParamError("src_spec->freq"); - } - } - - if (dst_spec) { - if (!SDL_IsSupportedAudioFormat(dst_spec->format)) { - return SDL_InvalidParamError("dst_spec->format"); - } else if (!SDL_IsSupportedChannelCount(dst_spec->channels)) { - return SDL_InvalidParamError("dst_spec->channels"); - } else if (dst_spec->freq <= 0) { - return SDL_InvalidParamError("dst_spec->freq"); - } - } - - SDL_LockMutex(stream->lock); - - // quietly refuse to change the format of the end currently bound to a device. - if (stream->bound_device) { - if (stream->bound_device->physical_device->recording) { - src_spec = NULL; - } else { - dst_spec = NULL; - } - } - - if (src_spec) { - if (src_spec->channels != stream->src_spec.channels) { - SDL_free(stream->src_chmap); - stream->src_chmap = NULL; - } - SDL_copyp(&stream->src_spec, src_spec); - } - - if (dst_spec) { - if (dst_spec->channels != stream->dst_spec.channels) { - SDL_free(stream->dst_chmap); - stream->dst_chmap = NULL; - } - SDL_copyp(&stream->dst_spec, dst_spec); - } - - SDL_UnlockMutex(stream->lock); - - return true; -} - -bool SetAudioStreamChannelMap(SDL_AudioStream *stream, const SDL_AudioSpec *spec, int **stream_chmap, const int *chmap, int channels, int isinput) -{ - if (!stream) { - return SDL_InvalidParamError("stream"); - } - - bool result = true; - - SDL_LockMutex(stream->lock); - - if (channels != spec->channels) { - result = SDL_SetError("Wrong number of channels"); - } else if (!*stream_chmap && !chmap) { - // already at default, we're good. - } else if (*stream_chmap && chmap && (SDL_memcmp(*stream_chmap, chmap, sizeof (*chmap) * channels) == 0)) { - // already have this map, don't allocate/copy it again. - } else if (SDL_ChannelMapIsBogus(chmap, channels)) { - result = SDL_SetError("Invalid channel mapping"); - } else { - if (SDL_ChannelMapIsDefault(chmap, channels)) { - chmap = NULL; // just apply a default mapping. - } - if (chmap) { - int *dupmap = SDL_ChannelMapDup(chmap, channels); - if (!dupmap) { - result = SDL_SetError("Invalid channel mapping"); - } else { - SDL_free(*stream_chmap); - *stream_chmap = dupmap; - } - } else { - SDL_free(*stream_chmap); - *stream_chmap = NULL; - } - } - - SDL_UnlockMutex(stream->lock); - return result; -} - -bool SDL_SetAudioStreamInputChannelMap(SDL_AudioStream *stream, const int *chmap, int channels) -{ - return SetAudioStreamChannelMap(stream, &stream->src_spec, &stream->src_chmap, chmap, channels, 1); -} - -bool SDL_SetAudioStreamOutputChannelMap(SDL_AudioStream *stream, const int *chmap, int channels) -{ - return SetAudioStreamChannelMap(stream, &stream->dst_spec, &stream->dst_chmap, chmap, channels, 0); -} - -int *SDL_GetAudioStreamInputChannelMap(SDL_AudioStream *stream, int *count) -{ - int *result = NULL; - int channels = 0; - if (stream) { - SDL_LockMutex(stream->lock); - channels = stream->src_spec.channels; - result = SDL_ChannelMapDup(stream->src_chmap, channels); - SDL_UnlockMutex(stream->lock); - } - - if (count) { - *count = channels; - } - - return result; -} - -int *SDL_GetAudioStreamOutputChannelMap(SDL_AudioStream *stream, int *count) -{ - int *result = NULL; - int channels = 0; - if (stream) { - SDL_LockMutex(stream->lock); - channels = stream->dst_spec.channels; - result = SDL_ChannelMapDup(stream->dst_chmap, channels); - SDL_UnlockMutex(stream->lock); - } - - if (count) { - *count = channels; - } - - return result; -} - -float SDL_GetAudioStreamFrequencyRatio(SDL_AudioStream *stream) -{ - if (!stream) { - SDL_InvalidParamError("stream"); - return 0.0f; - } - - SDL_LockMutex(stream->lock); - const float freq_ratio = stream->freq_ratio; - SDL_UnlockMutex(stream->lock); - - return freq_ratio; -} - -bool SDL_SetAudioStreamFrequencyRatio(SDL_AudioStream *stream, float freq_ratio) -{ - if (!stream) { - return SDL_InvalidParamError("stream"); - } - - // Picked mostly arbitrarily. - const float min_freq_ratio = 0.01f; - const float max_freq_ratio = 100.0f; - - if (freq_ratio < min_freq_ratio) { - return SDL_SetError("Frequency ratio is too low"); - } else if (freq_ratio > max_freq_ratio) { - return SDL_SetError("Frequency ratio is too high"); - } - - SDL_LockMutex(stream->lock); - stream->freq_ratio = freq_ratio; - SDL_UnlockMutex(stream->lock); - - return true; -} - -float SDL_GetAudioStreamGain(SDL_AudioStream *stream) -{ - if (!stream) { - SDL_InvalidParamError("stream"); - return -1.0f; - } - - SDL_LockMutex(stream->lock); - const float gain = stream->gain; - SDL_UnlockMutex(stream->lock); - - return gain; -} - -bool SDL_SetAudioStreamGain(SDL_AudioStream *stream, float gain) -{ - if (!stream) { - return SDL_InvalidParamError("stream"); - } else if (gain < 0.0f) { - return SDL_InvalidParamError("gain"); - } - - SDL_LockMutex(stream->lock); - stream->gain = gain; - SDL_UnlockMutex(stream->lock); - - return true; -} - -static bool CheckAudioStreamIsFullySetup(SDL_AudioStream *stream) -{ - if (stream->src_spec.format == 0) { - return SDL_SetError("Stream has no source format"); - } else if (stream->dst_spec.format == 0) { - return SDL_SetError("Stream has no destination format"); - } - - return true; -} - -static bool PutAudioStreamBuffer(SDL_AudioStream *stream, const void *buf, int len, SDL_ReleaseAudioBufferCallback callback, void* userdata) -{ -#if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: wants to put %d bytes", len); -#endif - - SDL_LockMutex(stream->lock); - - if (!CheckAudioStreamIsFullySetup(stream)) { - SDL_UnlockMutex(stream->lock); - return false; - } - - if ((len % SDL_AUDIO_FRAMESIZE(stream->src_spec)) != 0) { - SDL_UnlockMutex(stream->lock); - return SDL_SetError("Can't add partial sample frames"); - } - - SDL_AudioTrack* track = NULL; - - if (callback) { - track = SDL_CreateAudioTrack(stream->queue, &stream->src_spec, stream->src_chmap, (Uint8 *)buf, len, len, callback, userdata); - - if (!track) { - SDL_UnlockMutex(stream->lock); - return false; - } - } - - const int prev_available = stream->put_callback ? SDL_GetAudioStreamAvailable(stream) : 0; - - bool result = true; - - if (track) { - SDL_AddTrackToAudioQueue(stream->queue, track); - } else { - result = SDL_WriteToAudioQueue(stream->queue, &stream->src_spec, stream->src_chmap, (const Uint8 *)buf, len); - } - - if (result) { - if (stream->put_callback) { - const int newavail = SDL_GetAudioStreamAvailable(stream) - prev_available; - stream->put_callback(stream->put_callback_userdata, stream, newavail, newavail); - } - } - - SDL_UnlockMutex(stream->lock); - - return result; -} - -static void SDLCALL FreeAllocatedAudioBuffer(void *userdata, const void *buf, int len) -{ - SDL_free((void*) buf); -} - -bool SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len) -{ - if (!stream) { - return SDL_InvalidParamError("stream"); - } else if (!buf) { - return SDL_InvalidParamError("buf"); - } else if (len < 0) { - return SDL_InvalidParamError("len"); - } else if (len == 0) { - return true; // nothing to do. - } - - // When copying in large amounts of data, try and do as much work as possible - // outside of the stream lock, otherwise the output device is likely to be starved. - const int large_input_thresh = 64 * 1024; - - if (len >= large_input_thresh) { - void *data = SDL_malloc(len); - - if (!data) { - return false; - } - - SDL_memcpy(data, buf, len); - buf = data; - - bool ret = PutAudioStreamBuffer(stream, buf, len, FreeAllocatedAudioBuffer, NULL); - if (!ret) { - SDL_free(data); - } - return ret; - } - - return PutAudioStreamBuffer(stream, buf, len, NULL, NULL); -} - -bool SDL_FlushAudioStream(SDL_AudioStream *stream) -{ - if (!stream) { - return SDL_InvalidParamError("stream"); - } - - SDL_LockMutex(stream->lock); - SDL_FlushAudioQueue(stream->queue); - SDL_UnlockMutex(stream->lock); - - return true; -} - -/* this does not save the previous contents of stream->work_buffer. It's a work buffer!! - The returned buffer is aligned/padded for use with SIMD instructions. */ -static Uint8 *EnsureAudioStreamWorkBufferSize(SDL_AudioStream *stream, size_t newlen) -{ - if (stream->work_buffer_allocation >= newlen) { - return stream->work_buffer; - } - - Uint8 *ptr = (Uint8 *) SDL_aligned_alloc(SDL_GetSIMDAlignment(), newlen); - if (!ptr) { - return NULL; // previous work buffer is still valid! - } - - SDL_aligned_free(stream->work_buffer); - stream->work_buffer = ptr; - stream->work_buffer_allocation = newlen; - return ptr; -} - -static Sint64 NextAudioStreamIter(SDL_AudioStream* stream, void** inout_iter, - Sint64* inout_resample_offset, SDL_AudioSpec* out_spec, int **out_chmap, bool* out_flushed) -{ - SDL_AudioSpec spec; - bool flushed; - int *chmap; - size_t queued_bytes = SDL_NextAudioQueueIter(stream->queue, inout_iter, &spec, &chmap, &flushed); - - if (out_spec) { - SDL_copyp(out_spec, &spec); - } - - if (out_chmap) { - *out_chmap = chmap; - } - - // There is infinite audio available, whether or not we are resampling - if (queued_bytes == SDL_SIZE_MAX) { - *inout_resample_offset = 0; - - if (out_flushed) { - *out_flushed = false; - } - - return SDL_MAX_SINT32; - } - - Sint64 resample_offset = *inout_resample_offset; - Sint64 resample_rate = GetAudioStreamResampleRate(stream, spec.freq, resample_offset); - Sint64 output_frames = (Sint64)(queued_bytes / SDL_AUDIO_FRAMESIZE(spec)); - - if (resample_rate) { - // Resampling requires padding frames to the left and right of the current position. - // Past the end of the track, the right padding is filled with silence. - // But we only want to do that if the track is actually finished (flushed). - if (!flushed) { - output_frames -= SDL_GetResamplerPaddingFrames(resample_rate); - } - - output_frames = SDL_GetResamplerOutputFrames(output_frames, resample_rate, &resample_offset); - } - - if (flushed) { - resample_offset = 0; - } - - *inout_resample_offset = resample_offset; - - if (out_flushed) { - *out_flushed = flushed; - } - - return output_frames; -} - -static Sint64 GetAudioStreamAvailableFrames(SDL_AudioStream* stream, Sint64* out_resample_offset) -{ - void* iter = SDL_BeginAudioQueueIter(stream->queue); - - Sint64 resample_offset = stream->resample_offset; - Sint64 output_frames = 0; - - while (iter) { - output_frames += NextAudioStreamIter(stream, &iter, &resample_offset, NULL, NULL, NULL); - - // Already got loads of frames. Just clamp it to something reasonable - if (output_frames >= SDL_MAX_SINT32) { - output_frames = SDL_MAX_SINT32; - break; - } - } - - if (out_resample_offset) { - *out_resample_offset = resample_offset; - } - - return output_frames; -} - -static Sint64 GetAudioStreamHead(SDL_AudioStream* stream, SDL_AudioSpec* out_spec, int **out_chmap, bool* out_flushed) -{ - void* iter = SDL_BeginAudioQueueIter(stream->queue); - - if (!iter) { - SDL_zerop(out_spec); - *out_flushed = false; - return 0; - } - - Sint64 resample_offset = stream->resample_offset; - return NextAudioStreamIter(stream, &iter, &resample_offset, out_spec, out_chmap, out_flushed); -} - -// You must hold stream->lock and validate your parameters before calling this! -// Enough input data MUST be available! -static bool GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int output_frames, float gain) -{ - const SDL_AudioSpec* src_spec = &stream->input_spec; - const SDL_AudioSpec* dst_spec = &stream->dst_spec; - - const SDL_AudioFormat src_format = src_spec->format; - const int src_channels = src_spec->channels; - - const SDL_AudioFormat dst_format = dst_spec->format; - const int dst_channels = dst_spec->channels; - const int *dst_map = stream->dst_chmap; - - const int max_frame_size = CalculateMaxFrameSize(src_format, src_channels, dst_format, dst_channels); - const Sint64 resample_rate = GetAudioStreamResampleRate(stream, src_spec->freq, stream->resample_offset); - -#if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: asking for %d frames.", output_frames); -#endif - - SDL_assert(output_frames > 0); - - // Not resampling? It's an easy conversion (and maybe not even that!) - if (resample_rate == 0) { - Uint8* work_buffer = NULL; - - // Ensure we have enough scratch space for any conversions - if ((src_format != dst_format) || (src_channels != dst_channels) || (gain != 1.0f)) { - work_buffer = EnsureAudioStreamWorkBufferSize(stream, output_frames * max_frame_size); - - if (!work_buffer) { - return false; - } - } - - if (SDL_ReadFromAudioQueue(stream->queue, (Uint8 *)buf, dst_format, dst_channels, dst_map, 0, output_frames, 0, work_buffer, gain) != buf) { - return SDL_SetError("Not enough data in queue"); - } - - return true; - } - - // Time to do some resampling! - // Calculate the number of input frames necessary for this request. - // Because resampling happens "between" frames, The same number of output_frames - // can require a different number of input_frames, depending on the resample_offset. - // In fact, input_frames can sometimes even be zero when upsampling. - const int input_frames = (int) SDL_GetResamplerInputFrames(output_frames, resample_rate, stream->resample_offset); - - const int padding_frames = SDL_GetResamplerPaddingFrames(resample_rate); - - const SDL_AudioFormat resample_format = SDL_AUDIO_F32; - - // If increasing channels, do it after resampling, since we'd just - // do more work to resample duplicate channels. If we're decreasing, do - // it first so we resample the interpolated data instead of interpolating - // the resampled data. - const int resample_channels = SDL_min(src_channels, dst_channels); - - // The size of the frame used when resampling - const int resample_frame_size = SDL_AUDIO_BYTESIZE(resample_format) * resample_channels; - - // The main portion of the work_buffer can be used to store 3 things: - // src_sample_frame_size * (left_padding+input_buffer+right_padding) - // resample_frame_size * (left_padding+input_buffer+right_padding) - // dst_sample_frame_size * output_frames - // - // ResampleAudio also requires an additional buffer if it can't write straight to the output: - // resample_frame_size * output_frames - // - // Note, ConvertAudio requires (num_frames * max_sample_frame_size) of scratch space - const int work_buffer_frames = input_frames + (padding_frames * 2); - int work_buffer_capacity = work_buffer_frames * max_frame_size; - int resample_buffer_offset = -1; - - // Check if we can resample directly into the output buffer. - // Note, this is just to avoid extra copies. - // Some other formats may fit directly into the output buffer, but i'd rather process data in a SIMD-aligned buffer. - if ((dst_format != resample_format) || (dst_channels != resample_channels)) { - // Allocate space for converting the resampled output to the destination format - int resample_convert_bytes = output_frames * max_frame_size; - work_buffer_capacity = SDL_max(work_buffer_capacity, resample_convert_bytes); - - // SIMD-align the buffer - int simd_alignment = (int) SDL_GetSIMDAlignment(); - work_buffer_capacity += simd_alignment - 1; - work_buffer_capacity -= work_buffer_capacity % simd_alignment; - - // Allocate space for the resampled output - int resample_bytes = output_frames * resample_frame_size; - resample_buffer_offset = work_buffer_capacity; - work_buffer_capacity += resample_bytes; - } - - Uint8* work_buffer = EnsureAudioStreamWorkBufferSize(stream, work_buffer_capacity); - - if (!work_buffer) { - return false; - } - - // adjust gain either before resampling or after, depending on which point has less - // samples to process. - const float preresample_gain = (input_frames > output_frames) ? 1.0f : gain; - const float postresample_gain = (input_frames > output_frames) ? gain : 1.0f; - - // (dst channel map is NULL because we'll do the final swizzle on ConvertAudio after resample.) - const Uint8* input_buffer = SDL_ReadFromAudioQueue(stream->queue, - NULL, resample_format, resample_channels, NULL, - padding_frames, input_frames, padding_frames, work_buffer, preresample_gain); - - if (!input_buffer) { - return SDL_SetError("Not enough data in queue (resample)"); - } - - input_buffer += padding_frames * resample_frame_size; - - // Decide where the resampled output goes - void* resample_buffer = (resample_buffer_offset != -1) ? (work_buffer + resample_buffer_offset) : buf; - - SDL_ResampleAudio(resample_channels, - (const float *) input_buffer, input_frames, - (float*) resample_buffer, output_frames, - resample_rate, &stream->resample_offset); - - // Convert to the final format, if necessary (src channel map is NULL because SDL_ReadFromAudioQueue already handled this). - ConvertAudio(output_frames, resample_buffer, resample_format, resample_channels, NULL, buf, dst_format, dst_channels, dst_map, work_buffer, postresample_gain); - - return true; -} - -// get converted/resampled data from the stream -int SDL_GetAudioStreamDataAdjustGain(SDL_AudioStream *stream, void *voidbuf, int len, float extra_gain) -{ - Uint8 *buf = (Uint8 *) voidbuf; - -#if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: want to get %d converted bytes", len); -#endif - - if (!stream) { - SDL_InvalidParamError("stream"); - return -1; - } else if (!buf) { - SDL_InvalidParamError("buf"); - return -1; - } else if (len < 0) { - SDL_InvalidParamError("len"); - return -1; - } else if (len == 0) { - return 0; // nothing to do. - } - - SDL_LockMutex(stream->lock); - - if (!CheckAudioStreamIsFullySetup(stream)) { - SDL_UnlockMutex(stream->lock); - return -1; - } - - const float gain = stream->gain * extra_gain; - const int dst_frame_size = SDL_AUDIO_FRAMESIZE(stream->dst_spec); - - len -= len % dst_frame_size; // chop off any fractional sample frame. - - // give the callback a chance to fill in more stream data if it wants. - if (stream->get_callback) { - Sint64 total_request = len / dst_frame_size; // start with sample frames desired - Sint64 additional_request = total_request; - - Sint64 resample_offset = 0; - Sint64 available_frames = GetAudioStreamAvailableFrames(stream, &resample_offset); - - additional_request -= SDL_min(additional_request, available_frames); - - Sint64 resample_rate = GetAudioStreamResampleRate(stream, stream->src_spec.freq, resample_offset); - - if (resample_rate) { - total_request = SDL_GetResamplerInputFrames(total_request, resample_rate, resample_offset); - additional_request = SDL_GetResamplerInputFrames(additional_request, resample_rate, resample_offset); - } - - total_request *= SDL_AUDIO_FRAMESIZE(stream->src_spec); // convert sample frames to bytes. - additional_request *= SDL_AUDIO_FRAMESIZE(stream->src_spec); // convert sample frames to bytes. - stream->get_callback(stream->get_callback_userdata, stream, (int) SDL_min(additional_request, SDL_INT_MAX), (int) SDL_min(total_request, SDL_INT_MAX)); - } - - // Process the data in chunks to avoid allocating too much memory (and potential integer overflows) - const int chunk_size = 4096; - - int total = 0; - - while (total < len) { - // Audio is processed a track at a time. - SDL_AudioSpec input_spec; - int *input_chmap; - bool flushed; - const Sint64 available_frames = GetAudioStreamHead(stream, &input_spec, &input_chmap, &flushed); - - if (available_frames == 0) { - if (flushed) { - SDL_PopAudioQueueHead(stream->queue); - SDL_zero(stream->input_spec); - stream->resample_offset = 0; - stream->input_chmap = NULL; - continue; - } - // There are no frames available, but the track hasn't been flushed, so more might be added later. - break; - } - - if (!UpdateAudioStreamInputSpec(stream, &input_spec, input_chmap)) { - total = total ? total : -1; - break; - } - - // Clamp the output length to the maximum currently available. - // GetAudioStreamDataInternal requires enough input data is available. - int output_frames = (len - total) / dst_frame_size; - output_frames = SDL_min(output_frames, chunk_size); - output_frames = (int) SDL_min(output_frames, available_frames); - - if (!GetAudioStreamDataInternal(stream, &buf[total], output_frames, gain)) { - total = total ? total : -1; - break; - } - - total += output_frames * dst_frame_size; - } - - SDL_UnlockMutex(stream->lock); - -#if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: Final result was %d", total); -#endif - - return total; -} - -int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len) -{ - return SDL_GetAudioStreamDataAdjustGain(stream, voidbuf, len, 1.0f); -} - -// number of converted/resampled bytes available for output -int SDL_GetAudioStreamAvailable(SDL_AudioStream *stream) -{ - if (!stream) { - SDL_InvalidParamError("stream"); - return -1; - } - - SDL_LockMutex(stream->lock); - - if (!CheckAudioStreamIsFullySetup(stream)) { - SDL_UnlockMutex(stream->lock); - return 0; - } - - Sint64 count = GetAudioStreamAvailableFrames(stream, NULL); - - // convert from sample frames to bytes in destination format. - count *= SDL_AUDIO_FRAMESIZE(stream->dst_spec); - - SDL_UnlockMutex(stream->lock); - - // if this overflows an int, just clamp it to a maximum. - return (int) SDL_min(count, SDL_INT_MAX); -} - -// number of sample frames that are currently queued as input. -int SDL_GetAudioStreamQueued(SDL_AudioStream *stream) -{ - if (!stream) { - SDL_InvalidParamError("stream"); - return -1; - } - - SDL_LockMutex(stream->lock); - - size_t total = SDL_GetAudioQueueQueued(stream->queue); - - SDL_UnlockMutex(stream->lock); - - // if this overflows an int, just clamp it to a maximum. - return (int) SDL_min(total, SDL_INT_MAX); -} - -bool SDL_ClearAudioStream(SDL_AudioStream *stream) -{ - if (!stream) { - return SDL_InvalidParamError("stream"); - } - - SDL_LockMutex(stream->lock); - - SDL_ClearAudioQueue(stream->queue); - SDL_zero(stream->input_spec); - stream->input_chmap = NULL; - stream->resample_offset = 0; - - SDL_UnlockMutex(stream->lock); - return true; -} - -void SDL_DestroyAudioStream(SDL_AudioStream *stream) -{ - if (!stream) { - return; - } - - SDL_DestroyProperties(stream->props); - - OnAudioStreamDestroy(stream); - - const bool simplified = stream->simplified; - if (simplified) { - if (stream->bound_device) { - SDL_assert(stream->bound_device->simplified); - SDL_CloseAudioDevice(stream->bound_device->instance_id); // this will unbind the stream. - } - } else { - SDL_UnbindAudioStream(stream); - } - - SDL_aligned_free(stream->work_buffer); - SDL_DestroyAudioQueue(stream->queue); - SDL_DestroyMutex(stream->lock); - - SDL_free(stream); -} - -static void SDLCALL DontFreeThisAudioBuffer(void *userdata, const void *buf, int len) -{ - // We don't own the buffer, but know it will outlive the stream -} - -bool SDL_ConvertAudioSamples(const SDL_AudioSpec *src_spec, const Uint8 *src_data, int src_len, const SDL_AudioSpec *dst_spec, Uint8 **dst_data, int *dst_len) -{ - if (dst_data) { - *dst_data = NULL; - } - - if (dst_len) { - *dst_len = 0; - } - - if (!src_data) { - return SDL_InvalidParamError("src_data"); - } else if (src_len < 0) { - return SDL_InvalidParamError("src_len"); - } else if (!dst_data) { - return SDL_InvalidParamError("dst_data"); - } else if (!dst_len) { - return SDL_InvalidParamError("dst_len"); - } - - bool result = false; - Uint8 *dst = NULL; - int dstlen = 0; - - SDL_AudioStream *stream = SDL_CreateAudioStream(src_spec, dst_spec); - if (stream) { - if (PutAudioStreamBuffer(stream, src_data, src_len, DontFreeThisAudioBuffer, NULL) && - SDL_FlushAudioStream(stream)) { - dstlen = SDL_GetAudioStreamAvailable(stream); - if (dstlen >= 0) { - dst = (Uint8 *)SDL_malloc(dstlen); - if (dst) { - result = (SDL_GetAudioStreamData(stream, dst, dstlen) == dstlen); - } - } - } - } - - if (result) { - *dst_data = dst; - *dst_len = dstlen; - } else { - SDL_free(dst); - } - - SDL_DestroyAudioStream(stream); - return result; -} diff --git a/contrib/SDL-3.2.8/src/audio/SDL_audiodev.c b/contrib/SDL-3.2.8/src/audio/SDL_audiodev.c deleted file mode 100644 index 623a380..0000000 --- a/contrib/SDL-3.2.8/src/audio/SDL_audiodev.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -// Get the name of the audio device we use for output - -#if defined(SDL_AUDIO_DRIVER_NETBSD) || defined(SDL_AUDIO_DRIVER_OSS) - -#include -#include -#include -#include // For close() - -#include "SDL_audiodev_c.h" - -#ifndef SDL_PATH_DEV_DSP -#if defined(SDL_PLATFORM_NETBSD) || defined(SDL_PLATFORM_OPENBSD) -#define SDL_PATH_DEV_DSP "/dev/audio" -#else -#define SDL_PATH_DEV_DSP "/dev/dsp" -#endif -#endif -#ifndef SDL_PATH_DEV_DSP24 -#define SDL_PATH_DEV_DSP24 "/dev/sound/dsp" -#endif -#ifndef SDL_PATH_DEV_AUDIO -#define SDL_PATH_DEV_AUDIO "/dev/audio" -#endif - -static void test_device(const bool recording, const char *fname, int flags, bool (*test)(int fd)) -{ - struct stat sb; - const int audio_fd = open(fname, flags | O_CLOEXEC, 0); - if (audio_fd >= 0) { - if ((fstat(audio_fd, &sb) == 0) && (S_ISCHR(sb.st_mode))) { - const bool okay = test(audio_fd); - close(audio_fd); - if (okay) { - static size_t dummyhandle = 0; - dummyhandle++; - SDL_assert(dummyhandle != 0); - - /* Note that spec is NULL; while we are opening the device - * endpoint here, the endpoint does not provide any mix format - * information, making this information inaccessible at - * enumeration time - */ - SDL_AddAudioDevice(recording, fname, NULL, (void *)(uintptr_t)dummyhandle); - } - } else { - close(audio_fd); - } - } -} - -static bool test_stub(int fd) -{ - return true; -} - -static void SDL_EnumUnixAudioDevices_Internal(const bool recording, const bool classic, bool (*test)(int)) -{ - const int flags = recording ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT; - const char *audiodev; - char audiopath[1024]; - - if (!test) { - test = test_stub; - } - - // Figure out what our audio device is - audiodev = SDL_getenv("AUDIODEV"); - if (!audiodev) { - if (classic) { - audiodev = SDL_PATH_DEV_AUDIO; - } else { - struct stat sb; - - // Added support for /dev/sound/\* in Linux 2.4 - if (((stat("/dev/sound", &sb) == 0) && S_ISDIR(sb.st_mode)) && ((stat(SDL_PATH_DEV_DSP24, &sb) == 0) && S_ISCHR(sb.st_mode))) { - audiodev = SDL_PATH_DEV_DSP24; - } else { - audiodev = SDL_PATH_DEV_DSP; - } - } - } - test_device(recording, audiodev, flags, test); - - if (SDL_strlen(audiodev) < (sizeof(audiopath) - 3)) { - int instance = 0; - while (instance <= 64) { - (void)SDL_snprintf(audiopath, SDL_arraysize(audiopath), - "%s%d", audiodev, instance); - instance++; - test_device(recording, audiopath, flags, test); - } - } -} - -void SDL_EnumUnixAudioDevices(const bool classic, bool (*test)(int)) -{ - SDL_EnumUnixAudioDevices_Internal(true, classic, test); - SDL_EnumUnixAudioDevices_Internal(false, classic, test); -} - -#endif // Audio device selection diff --git a/contrib/SDL-3.2.8/src/audio/SDL_audiodev_c.h b/contrib/SDL-3.2.8/src/audio/SDL_audiodev_c.h deleted file mode 100644 index ded13fc..0000000 --- a/contrib/SDL-3.2.8/src/audio/SDL_audiodev_c.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef SDL_audiodev_c_h_ -#define SDL_audiodev_c_h_ - -#include "SDL_internal.h" -#include "SDL_sysaudio.h" - -// Open the audio device for playback, and don't block if busy -//#define USE_BLOCKING_WRITES - -#ifdef USE_BLOCKING_WRITES -#define OPEN_FLAGS_OUTPUT O_WRONLY -#define OPEN_FLAGS_INPUT O_RDONLY -#else -#define OPEN_FLAGS_OUTPUT (O_WRONLY | O_NONBLOCK) -#define OPEN_FLAGS_INPUT (O_RDONLY | O_NONBLOCK) -#endif - -extern void SDL_EnumUnixAudioDevices(const bool classic, bool (*test)(int)); - -#endif // SDL_audiodev_c_h_ diff --git a/contrib/SDL-3.2.8/src/audio/SDL_audioqueue.c b/contrib/SDL-3.2.8/src/audio/SDL_audioqueue.c deleted file mode 100644 index df7cac8..0000000 --- a/contrib/SDL-3.2.8/src/audio/SDL_audioqueue.c +++ /dev/null @@ -1,652 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#include "SDL_audioqueue.h" -#include "SDL_sysaudio.h" - -typedef struct SDL_MemoryPool SDL_MemoryPool; - -struct SDL_MemoryPool -{ - void *free_blocks; - size_t block_size; - size_t num_free; - size_t max_free; -}; - -struct SDL_AudioTrack -{ - SDL_AudioSpec spec; - int *chmap; - bool flushed; - SDL_AudioTrack *next; - - void *userdata; - SDL_ReleaseAudioBufferCallback callback; - - Uint8 *data; - size_t head; - size_t tail; - size_t capacity; - - int chmap_storage[SDL_MAX_CHANNELMAP_CHANNELS]; // !!! FIXME: this needs to grow if SDL ever supports more channels. But if it grows, we should probably be more clever about allocations. -}; - -struct SDL_AudioQueue -{ - SDL_AudioTrack *head; - SDL_AudioTrack *tail; - - Uint8 *history_buffer; - size_t history_length; - size_t history_capacity; - - SDL_MemoryPool track_pool; - SDL_MemoryPool chunk_pool; -}; - -// Allocate a new block, avoiding checking for ones already in the pool -static void *AllocNewMemoryPoolBlock(const SDL_MemoryPool *pool) -{ - return SDL_malloc(pool->block_size); -} - -// Allocate a new block, first checking if there are any in the pool -static void *AllocMemoryPoolBlock(SDL_MemoryPool *pool) -{ - if (pool->num_free == 0) { - return AllocNewMemoryPoolBlock(pool); - } - - void *block = pool->free_blocks; - pool->free_blocks = *(void **)block; - --pool->num_free; - return block; -} - -// Free a block, or add it to the pool if there's room -static void FreeMemoryPoolBlock(SDL_MemoryPool *pool, void *block) -{ - if (pool->num_free < pool->max_free) { - *(void **)block = pool->free_blocks; - pool->free_blocks = block; - ++pool->num_free; - } else { - SDL_free(block); - } -} - -// Destroy a pool and all of its blocks -static void DestroyMemoryPool(SDL_MemoryPool *pool) -{ - void *block = pool->free_blocks; - pool->free_blocks = NULL; - pool->num_free = 0; - - while (block) { - void *next = *(void **)block; - SDL_free(block); - block = next; - } -} - -// Keeping a list of free chunks reduces memory allocations, -// But also increases the amount of work to perform when freeing the track. -static void InitMemoryPool(SDL_MemoryPool *pool, size_t block_size, size_t max_free) -{ - SDL_zerop(pool); - - SDL_assert(block_size >= sizeof(void *)); - pool->block_size = block_size; - pool->max_free = max_free; -} - -// Allocates a number of blocks and adds them to the pool -static bool ReserveMemoryPoolBlocks(SDL_MemoryPool *pool, size_t num_blocks) -{ - for (; num_blocks; --num_blocks) { - void *block = AllocNewMemoryPoolBlock(pool); - - if (block == NULL) { - return false; - } - - *(void **)block = pool->free_blocks; - pool->free_blocks = block; - ++pool->num_free; - } - - return true; -} - -void SDL_DestroyAudioQueue(SDL_AudioQueue *queue) -{ - SDL_ClearAudioQueue(queue); - - DestroyMemoryPool(&queue->track_pool); - DestroyMemoryPool(&queue->chunk_pool); - SDL_aligned_free(queue->history_buffer); - - SDL_free(queue); -} - -SDL_AudioQueue *SDL_CreateAudioQueue(size_t chunk_size) -{ - SDL_AudioQueue *queue = (SDL_AudioQueue *)SDL_calloc(1, sizeof(*queue)); - - if (!queue) { - return NULL; - } - - InitMemoryPool(&queue->track_pool, sizeof(SDL_AudioTrack), 8); - InitMemoryPool(&queue->chunk_pool, chunk_size, 4); - - if (!ReserveMemoryPoolBlocks(&queue->track_pool, 2)) { - SDL_DestroyAudioQueue(queue); - return NULL; - } - - return queue; -} - -static void DestroyAudioTrack(SDL_AudioQueue *queue, SDL_AudioTrack *track) -{ - track->callback(track->userdata, track->data, (int)track->capacity); - - FreeMemoryPoolBlock(&queue->track_pool, track); -} - -void SDL_ClearAudioQueue(SDL_AudioQueue *queue) -{ - SDL_AudioTrack *track = queue->head; - - queue->head = NULL; - queue->tail = NULL; - queue->history_length = 0; - - while (track) { - SDL_AudioTrack *next = track->next; - DestroyAudioTrack(queue, track); - track = next; - } -} - -static void FlushAudioTrack(SDL_AudioTrack *track) -{ - track->flushed = true; -} - -void SDL_FlushAudioQueue(SDL_AudioQueue *queue) -{ - SDL_AudioTrack *track = queue->tail; - - if (track) { - FlushAudioTrack(track); - } -} - -void SDL_PopAudioQueueHead(SDL_AudioQueue *queue) -{ - SDL_AudioTrack *track = queue->head; - - for (;;) { - bool flushed = track->flushed; - - SDL_AudioTrack *next = track->next; - DestroyAudioTrack(queue, track); - track = next; - - if (flushed) { - break; - } - } - - queue->head = track; - queue->history_length = 0; - - if (!track) { - queue->tail = NULL; - } -} - -SDL_AudioTrack *SDL_CreateAudioTrack( - SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const int *chmap, - Uint8 *data, size_t len, size_t capacity, - SDL_ReleaseAudioBufferCallback callback, void *userdata) -{ - SDL_AudioTrack *track = (SDL_AudioTrack *)AllocMemoryPoolBlock(&queue->track_pool); - - if (!track) { - return NULL; - } - - SDL_zerop(track); - - if (chmap) { - SDL_assert(SDL_arraysize(track->chmap_storage) >= spec->channels); - SDL_memcpy(track->chmap_storage, chmap, sizeof (*chmap) * spec->channels); - track->chmap = track->chmap_storage; - } - - SDL_copyp(&track->spec, spec); - - track->userdata = userdata; - track->callback = callback; - track->data = data; - track->head = 0; - track->tail = len; - track->capacity = capacity; - - return track; -} - -static void SDLCALL FreeChunkedAudioBuffer(void *userdata, const void *buf, int len) -{ - SDL_AudioQueue *queue = (SDL_AudioQueue *)userdata; - - FreeMemoryPoolBlock(&queue->chunk_pool, (void *)buf); -} - -static SDL_AudioTrack *CreateChunkedAudioTrack(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const int *chmap) -{ - Uint8 *chunk = (Uint8 *)AllocMemoryPoolBlock(&queue->chunk_pool); - - if (!chunk) { - return NULL; - } - - size_t capacity = queue->chunk_pool.block_size; - capacity -= capacity % SDL_AUDIO_FRAMESIZE(*spec); - - SDL_AudioTrack *track = SDL_CreateAudioTrack(queue, spec, chmap, chunk, 0, capacity, FreeChunkedAudioBuffer, queue); - - if (!track) { - FreeMemoryPoolBlock(&queue->chunk_pool, chunk); - return NULL; - } - - return track; -} - -void SDL_AddTrackToAudioQueue(SDL_AudioQueue *queue, SDL_AudioTrack *track) -{ - SDL_AudioTrack *tail = queue->tail; - - if (tail) { - // If the spec has changed, make sure to flush the previous track - if (!SDL_AudioSpecsEqual(&tail->spec, &track->spec, tail->chmap, track->chmap)) { - FlushAudioTrack(tail); - } - - tail->next = track; - } else { - queue->head = track; - } - - queue->tail = track; -} - -static size_t WriteToAudioTrack(SDL_AudioTrack *track, const Uint8 *data, size_t len) -{ - if (track->flushed || track->tail >= track->capacity) { - return 0; - } - - len = SDL_min(len, track->capacity - track->tail); - SDL_memcpy(&track->data[track->tail], data, len); - track->tail += len; - - return len; -} - -bool SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const int *chmap, const Uint8 *data, size_t len) -{ - if (len == 0) { - return true; - } - - SDL_AudioTrack *track = queue->tail; - - if (track) { - if (!SDL_AudioSpecsEqual(&track->spec, spec, track->chmap, chmap)) { - FlushAudioTrack(track); - } - } else { - SDL_assert(!queue->head); - track = CreateChunkedAudioTrack(queue, spec, chmap); - - if (!track) { - return false; - } - - queue->head = track; - queue->tail = track; - } - - for (;;) { - const size_t written = WriteToAudioTrack(track, data, len); - data += written; - len -= written; - - if (len == 0) { - break; - } - - SDL_AudioTrack *new_track = CreateChunkedAudioTrack(queue, spec, chmap); - - if (!new_track) { - return false; - } - - track->next = new_track; - queue->tail = new_track; - track = new_track; - } - - return true; -} - -void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue) -{ - return queue->head; -} - -size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, int **out_chmap, bool *out_flushed) -{ - SDL_AudioTrack *iter = (SDL_AudioTrack *)(*inout_iter); - SDL_assert(iter != NULL); - - SDL_copyp(out_spec, &iter->spec); - *out_chmap = iter->chmap; - - bool flushed = false; - size_t queued_bytes = 0; - - while (iter) { - SDL_AudioTrack *track = iter; - iter = iter->next; - - size_t avail = track->tail - track->head; - - if (avail >= SDL_SIZE_MAX - queued_bytes) { - queued_bytes = SDL_SIZE_MAX; - flushed = false; - break; - } - - queued_bytes += avail; - flushed = track->flushed; - - if (flushed) { - break; - } - } - - *inout_iter = iter; - *out_flushed = flushed; - - return queued_bytes; -} - -static const Uint8 *PeekIntoAudioQueuePast(SDL_AudioQueue *queue, Uint8 *data, size_t len) -{ - SDL_AudioTrack *track = queue->head; - - if (track->head >= len) { - return &track->data[track->head - len]; - } - - size_t past = len - track->head; - - if (past > queue->history_length) { - return NULL; - } - - SDL_memcpy(data, &queue->history_buffer[queue->history_length - past], past); - SDL_memcpy(&data[past], track->data, track->head); - - return data; -} - -static void UpdateAudioQueueHistory(SDL_AudioQueue *queue, - const Uint8 *data, size_t len) -{ - Uint8 *history_buffer = queue->history_buffer; - size_t history_bytes = queue->history_length; - - if (len >= history_bytes) { - SDL_memcpy(history_buffer, &data[len - history_bytes], history_bytes); - } else { - size_t preserve = history_bytes - len; - SDL_memmove(history_buffer, &history_buffer[len], preserve); - SDL_memcpy(&history_buffer[preserve], data, len); - } -} - -static const Uint8 *ReadFromAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len) -{ - SDL_AudioTrack *track = queue->head; - - if (track->tail - track->head >= len) { - const Uint8 *ptr = &track->data[track->head]; - track->head += len; - return ptr; - } - - size_t total = 0; - - for (;;) { - size_t avail = SDL_min(len - total, track->tail - track->head); - SDL_memcpy(&data[total], &track->data[track->head], avail); - track->head += avail; - total += avail; - - if (total == len) { - break; - } - - if (track->flushed) { - SDL_SetError("Reading past end of flushed track"); - return NULL; - } - - SDL_AudioTrack *next = track->next; - - if (!next) { - SDL_SetError("Reading past end of incomplete track"); - return NULL; - } - - UpdateAudioQueueHistory(queue, track->data, track->tail); - - queue->head = next; - DestroyAudioTrack(queue, track); - track = next; - } - - return data; -} - -static const Uint8 *PeekIntoAudioQueueFuture(SDL_AudioQueue *queue, Uint8 *data, size_t len) -{ - SDL_AudioTrack *track = queue->head; - - if (track->tail - track->head >= len) { - return &track->data[track->head]; - } - - size_t total = 0; - - for (;;) { - size_t avail = SDL_min(len - total, track->tail - track->head); - SDL_memcpy(&data[total], &track->data[track->head], avail); - total += avail; - - if (total == len) { - break; - } - - if (track->flushed) { - // If we have run out of data, fill the rest with silence. - SDL_memset(&data[total], SDL_GetSilenceValueForFormat(track->spec.format), len - total); - break; - } - - track = track->next; - - if (!track) { - SDL_SetError("Peeking past end of incomplete track"); - return NULL; - } - } - - return data; -} - -const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue, - Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels, const int *dst_map, - int past_frames, int present_frames, int future_frames, - Uint8 *scratch, float gain) -{ - SDL_AudioTrack *track = queue->head; - - if (!track) { - return NULL; - } - - SDL_AudioFormat src_format = track->spec.format; - int src_channels = track->spec.channels; - const int *src_map = track->chmap; - - size_t src_frame_size = SDL_AUDIO_BYTESIZE(src_format) * src_channels; - size_t dst_frame_size = SDL_AUDIO_BYTESIZE(dst_format) * dst_channels; - - size_t src_past_bytes = past_frames * src_frame_size; - size_t src_present_bytes = present_frames * src_frame_size; - size_t src_future_bytes = future_frames * src_frame_size; - - size_t dst_past_bytes = past_frames * dst_frame_size; - size_t dst_present_bytes = present_frames * dst_frame_size; - size_t dst_future_bytes = future_frames * dst_frame_size; - - const bool convert = (src_format != dst_format) || (src_channels != dst_channels) || (gain != 1.0f); - - if (convert && !dst) { - // The user didn't ask for the data to be copied, but we need to convert it, so store it in the scratch buffer - dst = scratch; - } - - // Can we get all of the data straight from this track? - if ((track->head >= src_past_bytes) && ((track->tail - track->head) >= (src_present_bytes + src_future_bytes))) { - const Uint8 *ptr = &track->data[track->head - src_past_bytes]; - track->head += src_present_bytes; - - // Do we still need to copy/convert the data? - if (dst) { - ConvertAudio(past_frames + present_frames + future_frames, ptr, - src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch, gain); - ptr = dst; - } - - return ptr; - } - - if (!dst) { - // The user didn't ask for the data to be copied, but we need to, so store it in the scratch buffer - dst = scratch; - } else if (!convert) { - // We are only copying, not converting, so copy straight into the dst buffer - scratch = dst; - } - - Uint8 *ptr = dst; - - if (src_past_bytes) { - ConvertAudio(past_frames, PeekIntoAudioQueuePast(queue, scratch, src_past_bytes), src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch, gain); - dst += dst_past_bytes; - scratch += dst_past_bytes; - } - - if (src_present_bytes) { - ConvertAudio(present_frames, ReadFromAudioQueue(queue, scratch, src_present_bytes), src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch, gain); - dst += dst_present_bytes; - scratch += dst_present_bytes; - } - - if (src_future_bytes) { - ConvertAudio(future_frames, PeekIntoAudioQueueFuture(queue, scratch, src_future_bytes), src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch, gain); - dst += dst_future_bytes; - scratch += dst_future_bytes; - } - - return ptr; -} - -size_t SDL_GetAudioQueueQueued(SDL_AudioQueue *queue) -{ - size_t total = 0; - void *iter = SDL_BeginAudioQueueIter(queue); - - while (iter) { - SDL_AudioSpec src_spec; - int *src_chmap; - bool flushed; - - size_t avail = SDL_NextAudioQueueIter(queue, &iter, &src_spec, &src_chmap, &flushed); - - if (avail >= SDL_SIZE_MAX - total) { - total = SDL_SIZE_MAX; - break; - } - - total += avail; - } - - return total; -} - -bool SDL_ResetAudioQueueHistory(SDL_AudioQueue *queue, int num_frames) -{ - SDL_AudioTrack *track = queue->head; - - if (!track) { - return false; - } - - size_t length = num_frames * SDL_AUDIO_FRAMESIZE(track->spec); - Uint8 *history_buffer = queue->history_buffer; - - if (queue->history_capacity < length) { - history_buffer = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), length); - if (!history_buffer) { - return false; - } - SDL_aligned_free(queue->history_buffer); - queue->history_buffer = history_buffer; - queue->history_capacity = length; - } - - queue->history_length = length; - SDL_memset(history_buffer, SDL_GetSilenceValueForFormat(track->spec.format), length); - - return true; -} diff --git a/contrib/SDL-3.2.8/src/audio/SDL_audioqueue.h b/contrib/SDL-3.2.8/src/audio/SDL_audioqueue.h deleted file mode 100644 index 4662946..0000000 --- a/contrib/SDL-3.2.8/src/audio/SDL_audioqueue.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_audioqueue_h_ -#define SDL_audioqueue_h_ - -// Internal functions used by SDL_AudioStream for queueing audio. - -typedef void (SDLCALL *SDL_ReleaseAudioBufferCallback)(void *userdata, const void *buffer, int buflen); - -typedef struct SDL_AudioQueue SDL_AudioQueue; -typedef struct SDL_AudioTrack SDL_AudioTrack; - -// Create a new audio queue -extern SDL_AudioQueue *SDL_CreateAudioQueue(size_t chunk_size); - -// Destroy an audio queue -extern void SDL_DestroyAudioQueue(SDL_AudioQueue *queue); - -// Completely clear the queue -extern void SDL_ClearAudioQueue(SDL_AudioQueue *queue); - -// Mark the last track as flushed -extern void SDL_FlushAudioQueue(SDL_AudioQueue *queue); - -// Pop the current head track -// REQUIRES: The head track must exist, and must have been flushed -extern void SDL_PopAudioQueueHead(SDL_AudioQueue *queue); - -// Write data to the end of queue -// REQUIRES: If the spec has changed, the last track must have been flushed -extern bool SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const int *chmap, const Uint8 *data, size_t len); - -// Create a track where the input data is owned by the caller -extern SDL_AudioTrack *SDL_CreateAudioTrack(SDL_AudioQueue *queue, - const SDL_AudioSpec *spec, const int *chmap, Uint8 *data, size_t len, size_t capacity, - SDL_ReleaseAudioBufferCallback callback, void *userdata); - -// Add a track to the end of the queue -// REQUIRES: `track != NULL` -extern void SDL_AddTrackToAudioQueue(SDL_AudioQueue *queue, SDL_AudioTrack *track); - -// Iterate over the tracks in the queue -extern void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue); - -// Query and update the track iterator -// REQUIRES: `*inout_iter != NULL` (a valid iterator) -extern size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, int **out_chmap, bool *out_flushed); - -extern const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue, - Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels, const int *dst_map, - int past_frames, int present_frames, int future_frames, - Uint8 *scratch, float gain); - -// Get the total number of bytes currently queued -extern size_t SDL_GetAudioQueueQueued(SDL_AudioQueue *queue); - -extern bool SDL_ResetAudioQueueHistory(SDL_AudioQueue *queue, int num_frames); - -#endif // SDL_audioqueue_h_ diff --git a/contrib/SDL-3.2.8/src/audio/SDL_audioresample.c b/contrib/SDL-3.2.8/src/audio/SDL_audioresample.c deleted file mode 100644 index 371002e..0000000 --- a/contrib/SDL-3.2.8/src/audio/SDL_audioresample.c +++ /dev/null @@ -1,706 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#include "SDL_sysaudio.h" - -#include "SDL_audioresample.h" - -// SDL's resampler uses a "bandlimited interpolation" algorithm: -// https://ccrma.stanford.edu/~jos/resample/ - -// TODO: Support changing this at runtime? -#if defined(SDL_SSE_INTRINSICS) || defined(SDL_NEON_INTRINSICS) -// In , SSE is basically mandatory anyway -// We want RESAMPLER_SAMPLES_PER_FRAME to be a multiple of 4, to make SIMD easier -#define RESAMPLER_ZERO_CROSSINGS 6 -#else -#define RESAMPLER_ZERO_CROSSINGS 5 -#endif - -#define RESAMPLER_SAMPLES_PER_FRAME (RESAMPLER_ZERO_CROSSINGS * 2) - -// For a given srcpos, `srcpos + frame` are sampled, where `-RESAMPLER_ZERO_CROSSINGS < frame <= RESAMPLER_ZERO_CROSSINGS`. -// Note, when upsampling, it is also possible to start sampling from `srcpos = -1`. -#define RESAMPLER_MAX_PADDING_FRAMES (RESAMPLER_ZERO_CROSSINGS + 1) - -// More bits gives more precision, at the cost of a larger table. -#define RESAMPLER_BITS_PER_ZERO_CROSSING 3 -#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING) -#define RESAMPLER_FILTER_INTERP_BITS (32 - RESAMPLER_BITS_PER_ZERO_CROSSING) -#define RESAMPLER_FILTER_INTERP_RANGE (1 << RESAMPLER_FILTER_INTERP_BITS) - -// ResampleFrame is just a vector/matrix/matrix multiplication. -// It performs cubic interpolation of the filter, then multiplies that with the input. -// dst = [1, frac, frac^2, frac^3] * filter * src - -// Cubic Polynomial -typedef union Cubic -{ - float v[4]; - -#ifdef SDL_SSE_INTRINSICS - // Aligned loads can be used directly as memory operands for mul/add - __m128 v128; -#endif - -#ifdef SDL_NEON_INTRINSICS - float32x4_t v128; -#endif - -} Cubic; - -static void ResampleFrame_Generic(const float *src, float *dst, const Cubic *filter, float frac, int chans) -{ - const float frac2 = frac * frac; - const float frac3 = frac * frac2; - - int i, chan; - float scales[RESAMPLER_SAMPLES_PER_FRAME]; - - for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; ++i, ++filter) { - scales[i] = filter->v[0] + (filter->v[1] * frac) + (filter->v[2] * frac2) + (filter->v[3] * frac3); - } - - for (chan = 0; chan < chans; ++chan) { - float out = 0.0f; - - for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; ++i) { - out += src[i * chans + chan] * scales[i]; - } - - dst[chan] = out; - } -} - -static void ResampleFrame_Mono(const float *src, float *dst, const Cubic *filter, float frac, int chans) -{ - const float frac2 = frac * frac; - const float frac3 = frac * frac2; - - int i; - float out = 0.0f; - - for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; ++i, ++filter) { - // Interpolate between the nearest two filters - const float scale = filter->v[0] + (filter->v[1] * frac) + (filter->v[2] * frac2) + (filter->v[3] * frac3); - - out += src[i] * scale; - } - - dst[0] = out; -} - -static void ResampleFrame_Stereo(const float *src, float *dst, const Cubic *filter, float frac, int chans) -{ - const float frac2 = frac * frac; - const float frac3 = frac * frac2; - - int i; - float out0 = 0.0f; - float out1 = 0.0f; - - for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; ++i, ++filter) { - // Interpolate between the nearest two filters - const float scale = filter->v[0] + (filter->v[1] * frac) + (filter->v[2] * frac2) + (filter->v[3] * frac3); - - out0 += src[i * 2 + 0] * scale; - out1 += src[i * 2 + 1] * scale; - } - - dst[0] = out0; - dst[1] = out1; -} - -#ifdef SDL_SSE_INTRINSICS -#define sdl_madd_ps(a, b, c) _mm_add_ps(a, _mm_mul_ps(b, c)) // Not-so-fused multiply-add - -static void SDL_TARGETING("sse") ResampleFrame_Generic_SSE(const float *src, float *dst, const Cubic *filter, float frac, int chans) -{ -#if RESAMPLER_SAMPLES_PER_FRAME != 12 -#error Invalid samples per frame -#endif - - __m128 f0, f1, f2; - - { - const __m128 frac1 = _mm_set1_ps(frac); - const __m128 frac2 = _mm_mul_ps(frac1, frac1); - const __m128 frac3 = _mm_mul_ps(frac1, frac2); - -// Transposed in SetupAudioResampler -// Explicitly use _mm_load_ps to workaround ICE in GCC 4.9.4 accessing Cubic.v128 -#define X(out) \ - out = _mm_load_ps(filter[0].v); \ - out = sdl_madd_ps(out, frac1, _mm_load_ps(filter[1].v)); \ - out = sdl_madd_ps(out, frac2, _mm_load_ps(filter[2].v)); \ - out = sdl_madd_ps(out, frac3, _mm_load_ps(filter[3].v)); \ - filter += 4 - - X(f0); - X(f1); - X(f2); - -#undef X - } - - if (chans == 2) { - // Duplicate each of the filter elements and multiply by the input - // Use two accumulators to improve throughput - __m128 out0 = _mm_mul_ps(_mm_loadu_ps(src + 0), _mm_unpacklo_ps(f0, f0)); - __m128 out1 = _mm_mul_ps(_mm_loadu_ps(src + 4), _mm_unpackhi_ps(f0, f0)); - out0 = sdl_madd_ps(out0, _mm_loadu_ps(src + 8), _mm_unpacklo_ps(f1, f1)); - out1 = sdl_madd_ps(out1, _mm_loadu_ps(src + 12), _mm_unpackhi_ps(f1, f1)); - out0 = sdl_madd_ps(out0, _mm_loadu_ps(src + 16), _mm_unpacklo_ps(f2, f2)); - out1 = sdl_madd_ps(out1, _mm_loadu_ps(src + 20), _mm_unpackhi_ps(f2, f2)); - - // Add the accumulators together - __m128 out = _mm_add_ps(out0, out1); - - // Add the lower and upper pairs together - out = _mm_add_ps(out, _mm_movehl_ps(out, out)); - - // Store the result - _mm_storel_pi((__m64 *)dst, out); - return; - } - - if (chans == 1) { - // Multiply the filter by the input - __m128 out = _mm_mul_ps(f0, _mm_loadu_ps(src + 0)); - out = sdl_madd_ps(out, f1, _mm_loadu_ps(src + 4)); - out = sdl_madd_ps(out, f2, _mm_loadu_ps(src + 8)); - - // Horizontal sum - __m128 shuf = _mm_shuffle_ps(out, out, _MM_SHUFFLE(2, 3, 0, 1)); - out = _mm_add_ps(out, shuf); - out = _mm_add_ss(out, _mm_movehl_ps(shuf, out)); - - _mm_store_ss(dst, out); - return; - } - - int chan = 0; - - // Process 4 channels at once - for (; chan + 4 <= chans; chan += 4) { - const float *in = &src[chan]; - __m128 out0 = _mm_setzero_ps(); - __m128 out1 = _mm_setzero_ps(); - -#define X(a, b, out) \ - out = sdl_madd_ps(out, _mm_loadu_ps(in), _mm_shuffle_ps(a, a, _MM_SHUFFLE(b, b, b, b))); \ - in += chans - -#define Y(a) \ - X(a, 0, out0); \ - X(a, 1, out1); \ - X(a, 2, out0); \ - X(a, 3, out1) - - Y(f0); - Y(f1); - Y(f2); - -#undef X -#undef Y - - // Add the accumulators together - __m128 out = _mm_add_ps(out0, out1); - - _mm_storeu_ps(&dst[chan], out); - } - - // Process the remaining channels one at a time. - // Channel counts 1,2,4,8 are already handled above, leaving 3,5,6,7 to deal with (looping 3,1,2,3 times). - // Without vgatherdps (AVX2), this gets quite messy. - for (; chan < chans; ++chan) { - const float *in = &src[chan]; - __m128 v0, v1, v2; - -#define X(x) \ - x = _mm_unpacklo_ps(_mm_load_ss(in), _mm_load_ss(in + chans)); \ - in += chans + chans; \ - x = _mm_movelh_ps(x, _mm_unpacklo_ps(_mm_load_ss(in), _mm_load_ss(in + chans))); \ - in += chans + chans - - X(v0); - X(v1); - X(v2); - -#undef X - - __m128 out = _mm_mul_ps(f0, v0); - out = sdl_madd_ps(out, f1, v1); - out = sdl_madd_ps(out, f2, v2); - - // Horizontal sum - __m128 shuf = _mm_shuffle_ps(out, out, _MM_SHUFFLE(2, 3, 0, 1)); - out = _mm_add_ps(out, shuf); - out = _mm_add_ss(out, _mm_movehl_ps(shuf, out)); - - _mm_store_ss(&dst[chan], out); - } -} - -#undef sdl_madd_ps -#endif - -#ifdef SDL_NEON_INTRINSICS -static void ResampleFrame_Generic_NEON(const float *src, float *dst, const Cubic *filter, float frac, int chans) -{ -#if RESAMPLER_SAMPLES_PER_FRAME != 12 -#error Invalid samples per frame -#endif - - float32x4_t f0, f1, f2; - - { - const float32x4_t frac1 = vdupq_n_f32(frac); - const float32x4_t frac2 = vmulq_f32(frac1, frac1); - const float32x4_t frac3 = vmulq_f32(frac1, frac2); - -// Transposed in SetupAudioResampler -#define X(out) \ - out = vmlaq_f32(vmlaq_f32(vmlaq_f32(filter[0].v128, filter[1].v128, frac1), filter[2].v128, frac2), filter[3].v128, frac3); \ - filter += 4 - - X(f0); - X(f1); - X(f2); - -#undef X - } - - if (chans == 2) { - float32x4x2_t g0 = vzipq_f32(f0, f0); - float32x4x2_t g1 = vzipq_f32(f1, f1); - float32x4x2_t g2 = vzipq_f32(f2, f2); - - // Duplicate each of the filter elements and multiply by the input - // Use two accumulators to improve throughput - float32x4_t out0 = vmulq_f32(vld1q_f32(src + 0), g0.val[0]); - float32x4_t out1 = vmulq_f32(vld1q_f32(src + 4), g0.val[1]); - out0 = vmlaq_f32(out0, vld1q_f32(src + 8), g1.val[0]); - out1 = vmlaq_f32(out1, vld1q_f32(src + 12), g1.val[1]); - out0 = vmlaq_f32(out0, vld1q_f32(src + 16), g2.val[0]); - out1 = vmlaq_f32(out1, vld1q_f32(src + 20), g2.val[1]); - - // Add the accumulators together - out0 = vaddq_f32(out0, out1); - - // Add the lower and upper pairs together - float32x2_t out = vadd_f32(vget_low_f32(out0), vget_high_f32(out0)); - - // Store the result - vst1_f32(dst, out); - return; - } - - if (chans == 1) { - // Multiply the filter by the input - float32x4_t out = vmulq_f32(f0, vld1q_f32(src + 0)); - out = vmlaq_f32(out, f1, vld1q_f32(src + 4)); - out = vmlaq_f32(out, f2, vld1q_f32(src + 8)); - - // Horizontal sum - float32x2_t sum = vadd_f32(vget_low_f32(out), vget_high_f32(out)); - sum = vpadd_f32(sum, sum); - - vst1_lane_f32(dst, sum, 0); - return; - } - - int chan = 0; - - // Process 4 channels at once - for (; chan + 4 <= chans; chan += 4) { - const float *in = &src[chan]; - float32x4_t out0 = vdupq_n_f32(0); - float32x4_t out1 = vdupq_n_f32(0); - -#define X(a, b, out) \ - out = vmlaq_f32(out, vld1q_f32(in), vdupq_lane_f32(a, b)); \ - in += chans - -#define Y(a) \ - X(vget_low_f32(a), 0, out0); \ - X(vget_low_f32(a), 1, out1); \ - X(vget_high_f32(a), 0, out0); \ - X(vget_high_f32(a), 1, out1) - - Y(f0); - Y(f1); - Y(f2); - -#undef X -#undef Y - - // Add the accumulators together - float32x4_t out = vaddq_f32(out0, out1); - - vst1q_f32(&dst[chan], out); - } - - // Process the remaining channels one at a time. - // Channel counts 1,2,4,8 are already handled above, leaving 3,5,6,7 to deal with (looping 3,1,2,3 times). - for (; chan < chans; ++chan) { - const float *in = &src[chan]; - float32x4_t v0, v1, v2; - -#define X(x) \ - x = vld1q_dup_f32(in); \ - in += chans; \ - x = vld1q_lane_f32(in, x, 1); \ - in += chans; \ - x = vld1q_lane_f32(in, x, 2); \ - in += chans; \ - x = vld1q_lane_f32(in, x, 3); \ - in += chans - - X(v0); - X(v1); - X(v2); - -#undef X - - float32x4_t out = vmulq_f32(f0, v0); - out = vmlaq_f32(out, f1, v1); - out = vmlaq_f32(out, f2, v2); - - // Horizontal sum - float32x2_t sum = vadd_f32(vget_low_f32(out), vget_high_f32(out)); - sum = vpadd_f32(sum, sum); - - vst1_lane_f32(&dst[chan], sum, 0); - } -} -#endif - -// Calculate the cubic equation which passes through all four points. -// https://en.wikipedia.org/wiki/Ordinary_least_squares -// https://en.wikipedia.org/wiki/Polynomial_regression -static void CubicLeastSquares(Cubic *coeffs, float y0, float y1, float y2, float y3) -{ - // Least squares matrix for xs = [0, 1/3, 2/3, 1] - // [ 1.0 0.0 0.0 0.0 ] - // [ -5.5 9.0 -4.5 1.0 ] - // [ 9.0 -22.5 18.0 -4.5 ] - // [ -4.5 13.5 -13.5 4.5 ] - - coeffs->v[0] = y0; - coeffs->v[1] = -5.5f * y0 + 9.0f * y1 - 4.5f * y2 + y3; - coeffs->v[2] = 9.0f * y0 - 22.5f * y1 + 18.0f * y2 - 4.5f * y3; - coeffs->v[3] = -4.5f * y0 + 13.5f * y1 - 13.5f * y2 + 4.5f * y3; -} - -// Zeroth-order modified Bessel function of the first kind -// https://mathworld.wolfram.com/ModifiedBesselFunctionoftheFirstKind.html -static float BesselI0(float x) -{ - float sum = 0.0f; - float i = 1.0f; - float t = 1.0f; - x *= x * 0.25f; - - while (t >= sum * SDL_FLT_EPSILON) { - sum += t; - t *= x / (i * i); - ++i; - } - - return sum; -} - -// Pre-calculate 180 degrees of sin(pi * x) / pi -// The speedup from this isn't huge, but it also avoids precision issues. -// If sinf isn't available, SDL_sinf just calls SDL_sin. -// Know what SDL_sin(SDL_PI_F) equals? Not quite zero. -static void SincTable(float *table, int len) -{ - int i; - - for (i = 0; i < len; ++i) { - table[i] = SDL_sinf(i * (SDL_PI_F / len)) / SDL_PI_F; - } -} - -// Calculate Sinc(x/y), using a lookup table -static float Sinc(const float *table, int x, int y) -{ - float s = table[x % y]; - s = ((x / y) & 1) ? -s : s; - return (s * y) / x; -} - -static Cubic ResamplerFilter[RESAMPLER_SAMPLES_PER_ZERO_CROSSING][RESAMPLER_SAMPLES_PER_FRAME]; - -static void GenerateResamplerFilter(void) -{ - enum - { - // Generate samples at 3x the target resolution, so that we have samples at [0, 1/3, 2/3, 1] of each position - TABLE_SAMPLES_PER_ZERO_CROSSING = RESAMPLER_SAMPLES_PER_ZERO_CROSSING * 3, - TABLE_SIZE = RESAMPLER_ZERO_CROSSINGS * TABLE_SAMPLES_PER_ZERO_CROSSING, - }; - - // if dB > 50, beta=(0.1102 * (dB - 8.7)), according to Matlab. - const float dB = 80.0f; - const float beta = 0.1102f * (dB - 8.7f); - const float bessel_beta = BesselI0(beta); - const float lensqr = TABLE_SIZE * TABLE_SIZE; - - int i, j; - - float sinc[TABLE_SAMPLES_PER_ZERO_CROSSING]; - SincTable(sinc, TABLE_SAMPLES_PER_ZERO_CROSSING); - - // Generate one wing of the filter - // https://en.wikipedia.org/wiki/Kaiser_window - // https://en.wikipedia.org/wiki/Whittaker%E2%80%93Shannon_interpolation_formula - float filter[TABLE_SIZE + 1]; - filter[0] = 1.0f; - - for (i = 1; i <= TABLE_SIZE; ++i) { - float b = BesselI0(beta * SDL_sqrtf((lensqr - (i * i)) / lensqr)) / bessel_beta; - float s = Sinc(sinc, i, TABLE_SAMPLES_PER_ZERO_CROSSING); - filter[i] = b * s; - } - - // Generate the coefficients for each point - // When interpolating, the fraction represents how far we are between input samples, - // so we need to align the filter by "moving" it to the right. - // - // For the left wing, this means interpolating "forwards" (away from the center) - // For the right wing, this means interpolating "backwards" (towards the center) - // - // The center of the filter is at the end of the left wing (RESAMPLER_ZERO_CROSSINGS - 1) - // The left wing is the filter, but reversed - // The right wing is the filter, but offset by 1 - // - // Since the right wing is offset by 1, this just means we interpolate backwards - // between the same points, instead of forwards - // interp(p[n], p[n+1], t) = interp(p[n+1], p[n+1-1], 1 - t) = interp(p[n+1], p[n], 1 - t) - for (i = 0; i < RESAMPLER_SAMPLES_PER_ZERO_CROSSING; ++i) { - for (j = 0; j < RESAMPLER_ZERO_CROSSINGS; ++j) { - const float *ys = &filter[((j * RESAMPLER_SAMPLES_PER_ZERO_CROSSING) + i) * 3]; - - Cubic *fwd = &ResamplerFilter[i][RESAMPLER_ZERO_CROSSINGS - j - 1]; - Cubic *rev = &ResamplerFilter[RESAMPLER_SAMPLES_PER_ZERO_CROSSING - i - 1][RESAMPLER_ZERO_CROSSINGS + j]; - - // Calculate the cubic equation of the 4 points - CubicLeastSquares(fwd, ys[0], ys[1], ys[2], ys[3]); - CubicLeastSquares(rev, ys[3], ys[2], ys[1], ys[0]); - } - } -} - -typedef void (*ResampleFrameFunc)(const float *src, float *dst, const Cubic *filter, float frac, int chans); -static ResampleFrameFunc ResampleFrame[8]; - -// Transpose 4x4 floats -static void Transpose4x4(Cubic *data) -{ - int i, j; - - Cubic temp[4] = { data[0], data[1], data[2], data[3] }; - - for (i = 0; i < 4; ++i) { - for (j = 0; j < 4; ++j) { - data[i].v[j] = temp[j].v[i]; - } - } -} - -static void SetupAudioResampler(void) -{ - int i, j; - bool transpose = false; - - GenerateResamplerFilter(); - -#ifdef SDL_SSE_INTRINSICS - if (SDL_HasSSE()) { - for (i = 0; i < 8; ++i) { - ResampleFrame[i] = ResampleFrame_Generic_SSE; - } - transpose = true; - } else -#endif -#ifdef SDL_NEON_INTRINSICS - if (SDL_HasNEON()) { - for (i = 0; i < 8; ++i) { - ResampleFrame[i] = ResampleFrame_Generic_NEON; - } - transpose = true; - } else -#endif - { - for (i = 0; i < 8; ++i) { - ResampleFrame[i] = ResampleFrame_Generic; - } - - ResampleFrame[0] = ResampleFrame_Mono; - ResampleFrame[1] = ResampleFrame_Stereo; - } - - if (transpose) { - // Transpose each set of 4 coefficients, to reduce work when resampling - for (i = 0; i < RESAMPLER_SAMPLES_PER_ZERO_CROSSING; ++i) { - for (j = 0; j + 4 <= RESAMPLER_SAMPLES_PER_FRAME; j += 4) { - Transpose4x4(&ResamplerFilter[i][j]); - } - } - } -} - -void SDL_SetupAudioResampler(void) -{ - static SDL_InitState init; - - if (SDL_ShouldInit(&init)) { - SetupAudioResampler(); - SDL_SetInitialized(&init, true); - } -} - -Sint64 SDL_GetResampleRate(int src_rate, int dst_rate) -{ - SDL_assert(src_rate > 0); - SDL_assert(dst_rate > 0); - - Sint64 numerator = (Sint64)src_rate << 32; - Sint64 denominator = (Sint64)dst_rate; - - // Generally it's expected that `dst_frames = (src_frames * dst_rate) / src_rate` - // To match this as closely as possible without infinite precision, always round up the resample rate. - // For example, without rounding up, a sample ratio of 2:3 would have `sample_rate = 0xAAAAAAAA` - // After 3 frames, the position would be 0x1.FFFFFFFE, meaning we haven't fully consumed the second input frame. - // By rounding up to 0xAAAAAAAB, we would instead reach 0x2.00000001, fulling consuming the second frame. - // Technically you could say this is kicking the can 0x100000000 steps down the road, but I'm fine with that :) - // sample_rate = div_ceil(numerator, denominator) - Sint64 sample_rate = ((numerator - 1) / denominator) + 1; - - SDL_assert(sample_rate > 0); - - return sample_rate; -} - -int SDL_GetResamplerHistoryFrames(void) -{ - // Even if we aren't currently resampling, make sure to keep enough history in case we need to later. - - return RESAMPLER_MAX_PADDING_FRAMES; -} - -int SDL_GetResamplerPaddingFrames(Sint64 resample_rate) -{ - // This must always be <= SDL_GetResamplerHistoryFrames() - - return resample_rate ? RESAMPLER_MAX_PADDING_FRAMES : 0; -} - -// These are not general purpose. They do not check for all possible underflow/overflow -SDL_FORCE_INLINE bool ResamplerAdd(Sint64 a, Sint64 b, Sint64 *ret) -{ - if ((b > 0) && (a > SDL_MAX_SINT64 - b)) { - return false; - } - - *ret = a + b; - return true; -} - -SDL_FORCE_INLINE bool ResamplerMul(Sint64 a, Sint64 b, Sint64 *ret) -{ - if ((b > 0) && (a > SDL_MAX_SINT64 / b)) { - return false; - } - - *ret = a * b; - return true; -} - -Sint64 SDL_GetResamplerInputFrames(Sint64 output_frames, Sint64 resample_rate, Sint64 resample_offset) -{ - // Calculate the index of the last input frame, then add 1. - // ((((output_frames - 1) * resample_rate) + resample_offset) >> 32) + 1 - - Sint64 output_offset; - if (!ResamplerMul(output_frames, resample_rate, &output_offset) || - !ResamplerAdd(output_offset, -resample_rate + resample_offset + 0x100000000, &output_offset)) { - output_offset = SDL_MAX_SINT64; - } - - Sint64 input_frames = (Sint64)(Sint32)(output_offset >> 32); - input_frames = SDL_max(input_frames, 0); - - return input_frames; -} - -Sint64 SDL_GetResamplerOutputFrames(Sint64 input_frames, Sint64 resample_rate, Sint64 *inout_resample_offset) -{ - Sint64 resample_offset = *inout_resample_offset; - - // input_offset = (input_frames << 32) - resample_offset; - Sint64 input_offset; - if (!ResamplerMul(input_frames, 0x100000000, &input_offset) || - !ResamplerAdd(input_offset, -resample_offset, &input_offset)) { - input_offset = SDL_MAX_SINT64; - } - - // output_frames = div_ceil(input_offset, resample_rate) - Sint64 output_frames = (input_offset > 0) ? ((input_offset - 1) / resample_rate) + 1 : 0; - - *inout_resample_offset = (output_frames * resample_rate) - input_offset; - - return output_frames; -} - -void SDL_ResampleAudio(int chans, const float *src, int inframes, float *dst, int outframes, - Sint64 resample_rate, Sint64 *inout_resample_offset) -{ - int i; - Sint64 srcpos = *inout_resample_offset; - ResampleFrameFunc resample_frame = ResampleFrame[chans - 1]; - - SDL_assert(resample_rate > 0); - - src -= (RESAMPLER_ZERO_CROSSINGS - 1) * chans; - - for (i = 0; i < outframes; ++i) { - int srcindex = (int)(Sint32)(srcpos >> 32); - Uint32 srcfraction = (Uint32)(srcpos & 0xFFFFFFFF); - srcpos += resample_rate; - - SDL_assert(srcindex >= -1 && srcindex < inframes); - - const Cubic *filter = ResamplerFilter[srcfraction >> RESAMPLER_FILTER_INTERP_BITS]; - const float frac = (float)(srcfraction & (RESAMPLER_FILTER_INTERP_RANGE - 1)) * (1.0f / RESAMPLER_FILTER_INTERP_RANGE); - - const float *frame = &src[srcindex * chans]; - resample_frame(frame, dst, filter, frac, chans); - - dst += chans; - } - - *inout_resample_offset = srcpos - ((Sint64)inframes << 32); -} diff --git a/contrib/SDL-3.2.8/src/audio/SDL_audioresample.h b/contrib/SDL-3.2.8/src/audio/SDL_audioresample.h deleted file mode 100644 index 6620073..0000000 --- a/contrib/SDL-3.2.8/src/audio/SDL_audioresample.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_audioresample_h_ -#define SDL_audioresample_h_ - -// Internal functions used by SDL_AudioStream for resampling audio. -// The resampler uses 32:32 fixed-point arithmetic to track its position. - -Sint64 SDL_GetResampleRate(int src_rate, int dst_rate); - -int SDL_GetResamplerHistoryFrames(void); -int SDL_GetResamplerPaddingFrames(Sint64 resample_rate); - -Sint64 SDL_GetResamplerInputFrames(Sint64 output_frames, Sint64 resample_rate, Sint64 resample_offset); -Sint64 SDL_GetResamplerOutputFrames(Sint64 input_frames, Sint64 resample_rate, Sint64 *inout_resample_offset); - -// Resample some audio. -// REQUIRES: `inframes >= SDL_GetResamplerInputFrames(outframes)` -// REQUIRES: At least `SDL_GetResamplerPaddingFrames(...)` extra frames to the left of src, and right of src+inframes -void SDL_ResampleAudio(int chans, const float *src, int inframes, float *dst, int outframes, - Sint64 resample_rate, Sint64 *inout_resample_offset); - -#endif // SDL_audioresample_h_ diff --git a/contrib/SDL-3.2.8/src/audio/SDL_audiotypecvt.c b/contrib/SDL-3.2.8/src/audio/SDL_audiotypecvt.c deleted file mode 100644 index d80a831..0000000 --- a/contrib/SDL-3.2.8/src/audio/SDL_audiotypecvt.c +++ /dev/null @@ -1,925 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#include "SDL_sysaudio.h" - -#define DIVBY2147483648 0.0000000004656612873077392578125f // 0x1p-31f - -// start fallback scalar converters - -// This code requires that floats are in the IEEE-754 binary32 format -SDL_COMPILE_TIME_ASSERT(float_bits, sizeof(float) == sizeof(Uint32)); - -union float_bits { - Uint32 u32; - float f32; -}; - -static void SDL_Convert_S8_to_F32_Scalar(float *dst, const Sint8 *src, int num_samples) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("S8", "F32"); - - for (i = num_samples - 1; i >= 0; --i) { - /* 1) Construct a float in the range [65536.0, 65538.0) - * 2) Shift the float range to [-1.0, 1.0) */ - union float_bits x; - x.u32 = (Uint8)src[i] ^ 0x47800080u; - dst[i] = x.f32 - 65537.0f; - } -} - -static void SDL_Convert_U8_to_F32_Scalar(float *dst, const Uint8 *src, int num_samples) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("U8", "F32"); - - for (i = num_samples - 1; i >= 0; --i) { - /* 1) Construct a float in the range [65536.0, 65538.0) - * 2) Shift the float range to [-1.0, 1.0) */ - union float_bits x; - x.u32 = src[i] ^ 0x47800000u; - dst[i] = x.f32 - 65537.0f; - } -} - -static void SDL_Convert_S16_to_F32_Scalar(float *dst, const Sint16 *src, int num_samples) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("S16", "F32"); - - for (i = num_samples - 1; i >= 0; --i) { - /* 1) Construct a float in the range [256.0, 258.0) - * 2) Shift the float range to [-1.0, 1.0) */ - union float_bits x; - x.u32 = (Uint16)src[i] ^ 0x43808000u; - dst[i] = x.f32 - 257.0f; - } -} - -static void SDL_Convert_S32_to_F32_Scalar(float *dst, const Sint32 *src, int num_samples) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("S32", "F32"); - - for (i = num_samples - 1; i >= 0; --i) { - dst[i] = (float)src[i] * DIVBY2147483648; - } -} - -// Create a bit-mask based on the sign-bit. Should optimize to a single arithmetic-shift-right -#define SIGNMASK(x) (Uint32)(0u - ((Uint32)(x) >> 31)) - -static void SDL_Convert_F32_to_S8_Scalar(Sint8 *dst, const float *src, int num_samples) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("F32", "S8"); - - for (i = 0; i < num_samples; ++i) { - /* 1) Shift the float range from [-1.0, 1.0] to [98303.0, 98305.0] - * 2) Shift the integer range from [0x47BFFF80, 0x47C00080] to [-128, 128] - * 3) Clamp the value to [-128, 127] */ - union float_bits x; - x.f32 = src[i] + 98304.0f; - - Uint32 y = x.u32 - 0x47C00000u; - Uint32 z = 0x7Fu - (y ^ SIGNMASK(y)); - y = y ^ (z & SIGNMASK(z)); - - dst[i] = (Sint8)(y & 0xFF); - } -} - -static void SDL_Convert_F32_to_U8_Scalar(Uint8 *dst, const float *src, int num_samples) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("F32", "U8"); - - for (i = 0; i < num_samples; ++i) { - /* 1) Shift the float range from [-1.0, 1.0] to [98303.0, 98305.0] - * 2) Shift the integer range from [0x47BFFF80, 0x47C00080] to [-128, 128] - * 3) Clamp the value to [-128, 127] - * 4) Shift the integer range from [-128, 127] to [0, 255] */ - union float_bits x; - x.f32 = src[i] + 98304.0f; - - Uint32 y = x.u32 - 0x47C00000u; - Uint32 z = 0x7Fu - (y ^ SIGNMASK(y)); - y = (y ^ 0x80u) ^ (z & SIGNMASK(z)); - - dst[i] = (Uint8)(y & 0xFF); - } -} - -static void SDL_Convert_F32_to_S16_Scalar(Sint16 *dst, const float *src, int num_samples) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("F32", "S16"); - - for (i = 0; i < num_samples; ++i) { - /* 1) Shift the float range from [-1.0, 1.0] to [383.0, 385.0] - * 2) Shift the integer range from [0x43BF8000, 0x43C08000] to [-32768, 32768] - * 3) Clamp values outside the [-32768, 32767] range */ - union float_bits x; - x.f32 = src[i] + 384.0f; - - Uint32 y = x.u32 - 0x43C00000u; - Uint32 z = 0x7FFFu - (y ^ SIGNMASK(y)); - y = y ^ (z & SIGNMASK(z)); - - dst[i] = (Sint16)(y & 0xFFFF); - } -} - -static void SDL_Convert_F32_to_S32_Scalar(Sint32 *dst, const float *src, int num_samples) -{ - int i; - - LOG_DEBUG_AUDIO_CONVERT("F32", "S32"); - - for (i = 0; i < num_samples; ++i) { - /* 1) Shift the float range from [-1.0, 1.0] to [-2147483648.0, 2147483648.0] - * 2) Set values outside the [-2147483648.0, 2147483647.0] range to -2147483648.0 - * 3) Convert the float to an integer, and fixup values outside the valid range */ - union float_bits x; - x.f32 = src[i]; - - Uint32 y = x.u32 + 0x0F800000u; - Uint32 z = y - 0xCF000000u; - z &= SIGNMASK(y ^ z); - x.u32 = y - z; - - dst[i] = (Sint32)x.f32 ^ (Sint32)SIGNMASK(z); - } -} - -#undef SIGNMASK - -static void SDL_Convert_Swap16_Scalar(Uint16* dst, const Uint16* src, int num_samples) -{ - int i; - - for (i = 0; i < num_samples; ++i) { - dst[i] = SDL_Swap16(src[i]); - } -} - -static void SDL_Convert_Swap32_Scalar(Uint32* dst, const Uint32* src, int num_samples) -{ - int i; - - for (i = 0; i < num_samples; ++i) { - dst[i] = SDL_Swap32(src[i]); - } -} - -// end fallback scalar converters - -// Convert forwards, when sizeof(*src) >= sizeof(*dst) -#define CONVERT_16_FWD(CVT1, CVT16) \ - int i = 0; \ - if (num_samples >= 16) { \ - while ((uintptr_t)(&dst[i]) & 15) { CVT1 ++i; } \ - while ((i + 16) <= num_samples) { CVT16 i += 16; } \ - } \ - while (i < num_samples) { CVT1 ++i; } - -// Convert backwards, when sizeof(*src) <= sizeof(*dst) -#define CONVERT_16_REV(CVT1, CVT16) \ - int i = num_samples; \ - if (i >= 16) { \ - while ((uintptr_t)(&dst[i]) & 15) { --i; CVT1 } \ - while (i >= 16) { i -= 16; CVT16 } \ - } \ - while (i > 0) { --i; CVT1 } - -#ifdef SDL_SSE2_INTRINSICS -static void SDL_TARGETING("sse2") SDL_Convert_S8_to_F32_SSE2(float *dst, const Sint8 *src, int num_samples) -{ - /* 1) Flip the sign bit to convert from S8 to U8 format - * 2) Construct a float in the range [65536.0, 65538.0) - * 3) Shift the float range to [-1.0, 1.0) - * dst[i] = i2f((src[i] ^ 0x80) | 0x47800000) - 65537.0 */ - const __m128i zero = _mm_setzero_si128(); - const __m128i flipper = _mm_set1_epi8(-0x80); - const __m128i caster = _mm_set1_epi16(0x4780 /* 0x47800000 = f2i(65536.0) */); - const __m128 offset = _mm_set1_ps(-65537.0); - - LOG_DEBUG_AUDIO_CONVERT("S8", "F32 (using SSE2)"); - - CONVERT_16_REV({ - _mm_store_ss(&dst[i], _mm_add_ss(_mm_castsi128_ps(_mm_cvtsi32_si128((Uint8)src[i] ^ 0x47800080u)), offset)); - }, { - const __m128i bytes = _mm_xor_si128(_mm_loadu_si128((const __m128i *)&src[i]), flipper); - - const __m128i shorts0 = _mm_unpacklo_epi8(bytes, zero); - const __m128i shorts1 = _mm_unpackhi_epi8(bytes, zero); - - const __m128 floats0 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts0, caster)), offset); - const __m128 floats1 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts0, caster)), offset); - const __m128 floats2 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts1, caster)), offset); - const __m128 floats3 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts1, caster)), offset); - - _mm_store_ps(&dst[i], floats0); - _mm_store_ps(&dst[i + 4], floats1); - _mm_store_ps(&dst[i + 8], floats2); - _mm_store_ps(&dst[i + 12], floats3); - }) -} - -static void SDL_TARGETING("sse2") SDL_Convert_U8_to_F32_SSE2(float *dst, const Uint8 *src, int num_samples) -{ - /* 1) Construct a float in the range [65536.0, 65538.0) - * 2) Shift the float range to [-1.0, 1.0) - * dst[i] = i2f(src[i] | 0x47800000) - 65537.0 */ - const __m128i zero = _mm_setzero_si128(); - const __m128i caster = _mm_set1_epi16(0x4780 /* 0x47800000 = f2i(65536.0) */); - const __m128 offset = _mm_set1_ps(-65537.0); - - LOG_DEBUG_AUDIO_CONVERT("U8", "F32 (using SSE2)"); - - CONVERT_16_REV({ - _mm_store_ss(&dst[i], _mm_add_ss(_mm_castsi128_ps(_mm_cvtsi32_si128((Uint8)src[i] ^ 0x47800000u)), offset)); - }, { - const __m128i bytes = _mm_loadu_si128((const __m128i *)&src[i]); - - const __m128i shorts0 = _mm_unpacklo_epi8(bytes, zero); - const __m128i shorts1 = _mm_unpackhi_epi8(bytes, zero); - - const __m128 floats0 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts0, caster)), offset); - const __m128 floats1 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts0, caster)), offset); - const __m128 floats2 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts1, caster)), offset); - const __m128 floats3 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts1, caster)), offset); - - _mm_store_ps(&dst[i], floats0); - _mm_store_ps(&dst[i + 4], floats1); - _mm_store_ps(&dst[i + 8], floats2); - _mm_store_ps(&dst[i + 12], floats3); - }) -} - -static void SDL_TARGETING("sse2") SDL_Convert_S16_to_F32_SSE2(float *dst, const Sint16 *src, int num_samples) -{ - /* 1) Flip the sign bit to convert from S16 to U16 format - * 2) Construct a float in the range [256.0, 258.0) - * 3) Shift the float range to [-1.0, 1.0) - * dst[i] = i2f((src[i] ^ 0x8000) | 0x43800000) - 257.0 */ - const __m128i flipper = _mm_set1_epi16(-0x8000); - const __m128i caster = _mm_set1_epi16(0x4380 /* 0x43800000 = f2i(256.0) */); - const __m128 offset = _mm_set1_ps(-257.0f); - - LOG_DEBUG_AUDIO_CONVERT("S16", "F32 (using SSE2)"); - - CONVERT_16_REV({ - _mm_store_ss(&dst[i], _mm_add_ss(_mm_castsi128_ps(_mm_cvtsi32_si128((Uint16)src[i] ^ 0x43808000u)), offset)); - }, { - const __m128i shorts0 = _mm_xor_si128(_mm_loadu_si128((const __m128i *)&src[i]), flipper); - const __m128i shorts1 = _mm_xor_si128(_mm_loadu_si128((const __m128i *)&src[i + 8]), flipper); - - const __m128 floats0 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts0, caster)), offset); - const __m128 floats1 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts0, caster)), offset); - const __m128 floats2 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts1, caster)), offset); - const __m128 floats3 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts1, caster)), offset); - - _mm_store_ps(&dst[i], floats0); - _mm_store_ps(&dst[i + 4], floats1); - _mm_store_ps(&dst[i + 8], floats2); - _mm_store_ps(&dst[i + 12], floats3); - }) -} - -static void SDL_TARGETING("sse2") SDL_Convert_S32_to_F32_SSE2(float *dst, const Sint32 *src, int num_samples) -{ - // dst[i] = f32(src[i]) / f32(0x80000000) - const __m128 scaler = _mm_set1_ps(DIVBY2147483648); - - LOG_DEBUG_AUDIO_CONVERT("S32", "F32 (using SSE2)"); - - CONVERT_16_FWD({ - _mm_store_ss(&dst[i], _mm_mul_ss(_mm_cvt_si2ss(_mm_setzero_ps(), src[i]), scaler)); - }, { - const __m128i ints0 = _mm_loadu_si128((const __m128i *)&src[i]); - const __m128i ints1 = _mm_loadu_si128((const __m128i *)&src[i + 4]); - const __m128i ints2 = _mm_loadu_si128((const __m128i *)&src[i + 8]); - const __m128i ints3 = _mm_loadu_si128((const __m128i *)&src[i + 12]); - - const __m128 floats0 = _mm_mul_ps(_mm_cvtepi32_ps(ints0), scaler); - const __m128 floats1 = _mm_mul_ps(_mm_cvtepi32_ps(ints1), scaler); - const __m128 floats2 = _mm_mul_ps(_mm_cvtepi32_ps(ints2), scaler); - const __m128 floats3 = _mm_mul_ps(_mm_cvtepi32_ps(ints3), scaler); - - _mm_store_ps(&dst[i], floats0); - _mm_store_ps(&dst[i + 4], floats1); - _mm_store_ps(&dst[i + 8], floats2); - _mm_store_ps(&dst[i + 12], floats3); - }) -} - -static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S8_SSE2(Sint8 *dst, const float *src, int num_samples) -{ - /* 1) Shift the float range from [-1.0, 1.0] to [98303.0, 98305.0] - * 2) Extract the lowest 16 bits and clamp to [-128, 127] - * Overflow is correctly handled for inputs between roughly [-255.0, 255.0] - * dst[i] = clamp(i16(f2i(src[i] + 98304.0) & 0xFFFF), -128, 127) */ - const __m128 offset = _mm_set1_ps(98304.0f); - const __m128i mask = _mm_set1_epi16(0xFF); - - LOG_DEBUG_AUDIO_CONVERT("F32", "S8 (using SSE2)"); - - CONVERT_16_FWD({ - const __m128i ints = _mm_castps_si128(_mm_add_ss(_mm_load_ss(&src[i]), offset)); - dst[i] = (Sint8)(_mm_cvtsi128_si32(_mm_packs_epi16(ints, ints)) & 0xFF); - }, { - const __m128 floats0 = _mm_loadu_ps(&src[i]); - const __m128 floats1 = _mm_loadu_ps(&src[i + 4]); - const __m128 floats2 = _mm_loadu_ps(&src[i + 8]); - const __m128 floats3 = _mm_loadu_ps(&src[i + 12]); - - const __m128i ints0 = _mm_castps_si128(_mm_add_ps(floats0, offset)); - const __m128i ints1 = _mm_castps_si128(_mm_add_ps(floats1, offset)); - const __m128i ints2 = _mm_castps_si128(_mm_add_ps(floats2, offset)); - const __m128i ints3 = _mm_castps_si128(_mm_add_ps(floats3, offset)); - - const __m128i shorts0 = _mm_and_si128(_mm_packs_epi16(ints0, ints1), mask); - const __m128i shorts1 = _mm_and_si128(_mm_packs_epi16(ints2, ints3), mask); - - const __m128i bytes = _mm_packus_epi16(shorts0, shorts1); - - _mm_store_si128((__m128i*)&dst[i], bytes); - }) -} - -static void SDL_TARGETING("sse2") SDL_Convert_F32_to_U8_SSE2(Uint8 *dst, const float *src, int num_samples) -{ - /* 1) Shift the float range from [-1.0, 1.0] to [98304.0, 98306.0] - * 2) Extract the lowest 16 bits and clamp to [0, 255] - * Overflow is correctly handled for inputs between roughly [-254.0, 254.0] - * dst[i] = clamp(i16(f2i(src[i] + 98305.0) & 0xFFFF), 0, 255) */ - const __m128 offset = _mm_set1_ps(98305.0f); - const __m128i mask = _mm_set1_epi16(0xFF); - - LOG_DEBUG_AUDIO_CONVERT("F32", "U8 (using SSE2)"); - - CONVERT_16_FWD({ - const __m128i ints = _mm_castps_si128(_mm_add_ss(_mm_load_ss(&src[i]), offset)); - dst[i] = (Uint8)(_mm_cvtsi128_si32(_mm_packus_epi16(ints, ints)) & 0xFF); - }, { - const __m128 floats0 = _mm_loadu_ps(&src[i]); - const __m128 floats1 = _mm_loadu_ps(&src[i + 4]); - const __m128 floats2 = _mm_loadu_ps(&src[i + 8]); - const __m128 floats3 = _mm_loadu_ps(&src[i + 12]); - - const __m128i ints0 = _mm_castps_si128(_mm_add_ps(floats0, offset)); - const __m128i ints1 = _mm_castps_si128(_mm_add_ps(floats1, offset)); - const __m128i ints2 = _mm_castps_si128(_mm_add_ps(floats2, offset)); - const __m128i ints3 = _mm_castps_si128(_mm_add_ps(floats3, offset)); - - const __m128i shorts0 = _mm_and_si128(_mm_packus_epi16(ints0, ints1), mask); - const __m128i shorts1 = _mm_and_si128(_mm_packus_epi16(ints2, ints3), mask); - - const __m128i bytes = _mm_packus_epi16(shorts0, shorts1); - - _mm_store_si128((__m128i*)&dst[i], bytes); - }) -} - -static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S16_SSE2(Sint16 *dst, const float *src, int num_samples) -{ - /* 1) Shift the float range from [-1.0, 1.0] to [256.0, 258.0] - * 2) Shift the int range from [0x43800000, 0x43810000] to [-32768,32768] - * 3) Clamp to range [-32768,32767] - * Overflow is correctly handled for inputs between roughly [-257.0, +inf) - * dst[i] = clamp(f2i(src[i] + 257.0) - 0x43808000, -32768, 32767) */ - const __m128 offset = _mm_set1_ps(257.0f); - - LOG_DEBUG_AUDIO_CONVERT("F32", "S16 (using SSE2)"); - - CONVERT_16_FWD({ - const __m128i ints = _mm_sub_epi32(_mm_castps_si128(_mm_add_ss(_mm_load_ss(&src[i]), offset)), _mm_castps_si128(offset)); - dst[i] = (Sint16)(_mm_cvtsi128_si32(_mm_packs_epi32(ints, ints)) & 0xFFFF); - }, { - const __m128 floats0 = _mm_loadu_ps(&src[i]); - const __m128 floats1 = _mm_loadu_ps(&src[i + 4]); - const __m128 floats2 = _mm_loadu_ps(&src[i + 8]); - const __m128 floats3 = _mm_loadu_ps(&src[i + 12]); - - const __m128i ints0 = _mm_sub_epi32(_mm_castps_si128(_mm_add_ps(floats0, offset)), _mm_castps_si128(offset)); - const __m128i ints1 = _mm_sub_epi32(_mm_castps_si128(_mm_add_ps(floats1, offset)), _mm_castps_si128(offset)); - const __m128i ints2 = _mm_sub_epi32(_mm_castps_si128(_mm_add_ps(floats2, offset)), _mm_castps_si128(offset)); - const __m128i ints3 = _mm_sub_epi32(_mm_castps_si128(_mm_add_ps(floats3, offset)), _mm_castps_si128(offset)); - - const __m128i shorts0 = _mm_packs_epi32(ints0, ints1); - const __m128i shorts1 = _mm_packs_epi32(ints2, ints3); - - _mm_store_si128((__m128i*)&dst[i], shorts0); - _mm_store_si128((__m128i*)&dst[i + 8], shorts1); - }) -} - -static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S32_SSE2(Sint32 *dst, const float *src, int num_samples) -{ - /* 1) Scale the float range from [-1.0, 1.0] to [-2147483648.0, 2147483648.0] - * 2) Convert to integer (values too small/large become 0x80000000 = -2147483648) - * 3) Fixup values which were too large (0x80000000 ^ 0xFFFFFFFF = 2147483647) - * dst[i] = i32(src[i] * 2147483648.0) ^ ((src[i] >= 2147483648.0) ? 0xFFFFFFFF : 0x00000000) */ - const __m128 limit = _mm_set1_ps(2147483648.0f); - - LOG_DEBUG_AUDIO_CONVERT("F32", "S32 (using SSE2)"); - - CONVERT_16_FWD({ - const __m128 floats = _mm_load_ss(&src[i]); - const __m128 values = _mm_mul_ss(floats, limit); - const __m128i ints = _mm_xor_si128(_mm_cvttps_epi32(values), _mm_castps_si128(_mm_cmpge_ss(values, limit))); - dst[i] = (Sint32)_mm_cvtsi128_si32(ints); - }, { - const __m128 floats0 = _mm_loadu_ps(&src[i]); - const __m128 floats1 = _mm_loadu_ps(&src[i + 4]); - const __m128 floats2 = _mm_loadu_ps(&src[i + 8]); - const __m128 floats3 = _mm_loadu_ps(&src[i + 12]); - - const __m128 values1 = _mm_mul_ps(floats0, limit); - const __m128 values2 = _mm_mul_ps(floats1, limit); - const __m128 values3 = _mm_mul_ps(floats2, limit); - const __m128 values4 = _mm_mul_ps(floats3, limit); - - const __m128i ints0 = _mm_xor_si128(_mm_cvttps_epi32(values1), _mm_castps_si128(_mm_cmpge_ps(values1, limit))); - const __m128i ints1 = _mm_xor_si128(_mm_cvttps_epi32(values2), _mm_castps_si128(_mm_cmpge_ps(values2, limit))); - const __m128i ints2 = _mm_xor_si128(_mm_cvttps_epi32(values3), _mm_castps_si128(_mm_cmpge_ps(values3, limit))); - const __m128i ints3 = _mm_xor_si128(_mm_cvttps_epi32(values4), _mm_castps_si128(_mm_cmpge_ps(values4, limit))); - - _mm_store_si128((__m128i*)&dst[i], ints0); - _mm_store_si128((__m128i*)&dst[i + 4], ints1); - _mm_store_si128((__m128i*)&dst[i + 8], ints2); - _mm_store_si128((__m128i*)&dst[i + 12], ints3); - }) -} -#endif - -// FIXME: SDL doesn't have SSSE3 detection, so use the next one up -#ifdef SDL_SSE4_1_INTRINSICS -static void SDL_TARGETING("ssse3") SDL_Convert_Swap16_SSSE3(Uint16* dst, const Uint16* src, int num_samples) -{ - const __m128i shuffle = _mm_set_epi8(14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1); - - CONVERT_16_FWD({ - dst[i] = SDL_Swap16(src[i]); - }, { - __m128i ints0 = _mm_loadu_si128((const __m128i*)&src[i]); - __m128i ints1 = _mm_loadu_si128((const __m128i*)&src[i + 8]); - - ints0 = _mm_shuffle_epi8(ints0, shuffle); - ints1 = _mm_shuffle_epi8(ints1, shuffle); - - _mm_store_si128((__m128i*)&dst[i], ints0); - _mm_store_si128((__m128i*)&dst[i + 8], ints1); - }) -} - -static void SDL_TARGETING("ssse3") SDL_Convert_Swap32_SSSE3(Uint32* dst, const Uint32* src, int num_samples) -{ - const __m128i shuffle = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3); - - CONVERT_16_FWD({ - dst[i] = SDL_Swap32(src[i]); - }, { - __m128i ints0 = _mm_loadu_si128((const __m128i*)&src[i]); - __m128i ints1 = _mm_loadu_si128((const __m128i*)&src[i + 4]); - __m128i ints2 = _mm_loadu_si128((const __m128i*)&src[i + 8]); - __m128i ints3 = _mm_loadu_si128((const __m128i*)&src[i + 12]); - - ints0 = _mm_shuffle_epi8(ints0, shuffle); - ints1 = _mm_shuffle_epi8(ints1, shuffle); - ints2 = _mm_shuffle_epi8(ints2, shuffle); - ints3 = _mm_shuffle_epi8(ints3, shuffle); - - _mm_store_si128((__m128i*)&dst[i], ints0); - _mm_store_si128((__m128i*)&dst[i + 4], ints1); - _mm_store_si128((__m128i*)&dst[i + 8], ints2); - _mm_store_si128((__m128i*)&dst[i + 12], ints3); - }) -} -#endif - -#ifdef SDL_NEON_INTRINSICS -static void SDL_Convert_S8_to_F32_NEON(float *dst, const Sint8 *src, int num_samples) -{ - LOG_DEBUG_AUDIO_CONVERT("S8", "F32 (using NEON)"); - - CONVERT_16_REV({ - vst1_lane_f32(&dst[i], vcvt_n_f32_s32(vdup_n_s32(src[i]), 7), 0); - }, { - int8x16_t bytes = vld1q_s8(&src[i]); - - int16x8_t shorts0 = vmovl_s8(vget_low_s8(bytes)); - int16x8_t shorts1 = vmovl_s8(vget_high_s8(bytes)); - - float32x4_t floats0 = vcvtq_n_f32_s32(vmovl_s16(vget_low_s16(shorts0)), 7); - float32x4_t floats1 = vcvtq_n_f32_s32(vmovl_s16(vget_high_s16(shorts0)), 7); - float32x4_t floats2 = vcvtq_n_f32_s32(vmovl_s16(vget_low_s16(shorts1)), 7); - float32x4_t floats3 = vcvtq_n_f32_s32(vmovl_s16(vget_high_s16(shorts1)), 7); - - vst1q_f32(&dst[i], floats0); - vst1q_f32(&dst[i + 4], floats1); - vst1q_f32(&dst[i + 8], floats2); - vst1q_f32(&dst[i + 12], floats3); - }) -} - -static void SDL_Convert_U8_to_F32_NEON(float *dst, const Uint8 *src, int num_samples) -{ - LOG_DEBUG_AUDIO_CONVERT("U8", "F32 (using NEON)"); - - uint8x16_t flipper = vdupq_n_u8(0x80); - - CONVERT_16_REV({ - vst1_lane_f32(&dst[i], vcvt_n_f32_s32(vdup_n_s32((Sint8)(src[i] ^ 0x80)), 7), 0); - }, { - int8x16_t bytes = vreinterpretq_s8_u8(veorq_u8(vld1q_u8(&src[i]), flipper)); - - int16x8_t shorts0 = vmovl_s8(vget_low_s8(bytes)); - int16x8_t shorts1 = vmovl_s8(vget_high_s8(bytes)); - - float32x4_t floats0 = vcvtq_n_f32_s32(vmovl_s16(vget_low_s16(shorts0)), 7); - float32x4_t floats1 = vcvtq_n_f32_s32(vmovl_s16(vget_high_s16(shorts0)), 7); - float32x4_t floats2 = vcvtq_n_f32_s32(vmovl_s16(vget_low_s16(shorts1)), 7); - float32x4_t floats3 = vcvtq_n_f32_s32(vmovl_s16(vget_high_s16(shorts1)), 7); - - vst1q_f32(&dst[i], floats0); - vst1q_f32(&dst[i + 4], floats1); - vst1q_f32(&dst[i + 8], floats2); - vst1q_f32(&dst[i + 12], floats3); - }) -} - -static void SDL_Convert_S16_to_F32_NEON(float *dst, const Sint16 *src, int num_samples) -{ - LOG_DEBUG_AUDIO_CONVERT("S16", "F32 (using NEON)"); - - CONVERT_16_REV({ - vst1_lane_f32(&dst[i], vcvt_n_f32_s32(vdup_n_s32(src[i]), 15), 0); - }, { - int16x8_t shorts0 = vld1q_s16(&src[i]); - int16x8_t shorts1 = vld1q_s16(&src[i + 8]); - - float32x4_t floats0 = vcvtq_n_f32_s32(vmovl_s16(vget_low_s16(shorts0)), 15); - float32x4_t floats1 = vcvtq_n_f32_s32(vmovl_s16(vget_high_s16(shorts0)), 15); - float32x4_t floats2 = vcvtq_n_f32_s32(vmovl_s16(vget_low_s16(shorts1)), 15); - float32x4_t floats3 = vcvtq_n_f32_s32(vmovl_s16(vget_high_s16(shorts1)), 15); - - vst1q_f32(&dst[i], floats0); - vst1q_f32(&dst[i + 4], floats1); - vst1q_f32(&dst[i + 8], floats2); - vst1q_f32(&dst[i + 12], floats3); - }) -} - -static void SDL_Convert_S32_to_F32_NEON(float *dst, const Sint32 *src, int num_samples) -{ - LOG_DEBUG_AUDIO_CONVERT("S32", "F32 (using NEON)"); - - CONVERT_16_FWD({ - vst1_lane_f32(&dst[i], vcvt_n_f32_s32(vld1_dup_s32(&src[i]), 31), 0); - }, { - int32x4_t ints0 = vld1q_s32(&src[i]); - int32x4_t ints1 = vld1q_s32(&src[i + 4]); - int32x4_t ints2 = vld1q_s32(&src[i + 8]); - int32x4_t ints3 = vld1q_s32(&src[i + 12]); - - float32x4_t floats0 = vcvtq_n_f32_s32(ints0, 31); - float32x4_t floats1 = vcvtq_n_f32_s32(ints1, 31); - float32x4_t floats2 = vcvtq_n_f32_s32(ints2, 31); - float32x4_t floats3 = vcvtq_n_f32_s32(ints3, 31); - - vst1q_f32(&dst[i], floats0); - vst1q_f32(&dst[i + 4], floats1); - vst1q_f32(&dst[i + 8], floats2); - vst1q_f32(&dst[i + 12], floats3); - }) -} - -static void SDL_Convert_F32_to_S8_NEON(Sint8 *dst, const float *src, int num_samples) -{ - LOG_DEBUG_AUDIO_CONVERT("F32", "S8 (using NEON)"); - - CONVERT_16_FWD({ - vst1_lane_s8(&dst[i], vreinterpret_s8_s32(vcvt_n_s32_f32(vld1_dup_f32(&src[i]), 31)), 3); - }, { - float32x4_t floats0 = vld1q_f32(&src[i]); - float32x4_t floats1 = vld1q_f32(&src[i + 4]); - float32x4_t floats2 = vld1q_f32(&src[i + 8]); - float32x4_t floats3 = vld1q_f32(&src[i + 12]); - - int32x4_t ints0 = vcvtq_n_s32_f32(floats0, 31); - int32x4_t ints1 = vcvtq_n_s32_f32(floats1, 31); - int32x4_t ints2 = vcvtq_n_s32_f32(floats2, 31); - int32x4_t ints3 = vcvtq_n_s32_f32(floats3, 31); - - int16x8_t shorts0 = vcombine_s16(vshrn_n_s32(ints0, 16), vshrn_n_s32(ints1, 16)); - int16x8_t shorts1 = vcombine_s16(vshrn_n_s32(ints2, 16), vshrn_n_s32(ints3, 16)); - - int8x16_t bytes = vcombine_s8(vshrn_n_s16(shorts0, 8), vshrn_n_s16(shorts1, 8)); - - vst1q_s8(&dst[i], bytes); - }) -} - -static void SDL_Convert_F32_to_U8_NEON(Uint8 *dst, const float *src, int num_samples) -{ - LOG_DEBUG_AUDIO_CONVERT("F32", "U8 (using NEON)"); - - uint8x16_t flipper = vdupq_n_u8(0x80); - - CONVERT_16_FWD({ - vst1_lane_u8(&dst[i], - veor_u8(vreinterpret_u8_s32(vcvt_n_s32_f32(vld1_dup_f32(&src[i]), 31)), - vget_low_u8(flipper)), 3); - }, { - float32x4_t floats0 = vld1q_f32(&src[i]); - float32x4_t floats1 = vld1q_f32(&src[i + 4]); - float32x4_t floats2 = vld1q_f32(&src[i + 8]); - float32x4_t floats3 = vld1q_f32(&src[i + 12]); - - int32x4_t ints0 = vcvtq_n_s32_f32(floats0, 31); - int32x4_t ints1 = vcvtq_n_s32_f32(floats1, 31); - int32x4_t ints2 = vcvtq_n_s32_f32(floats2, 31); - int32x4_t ints3 = vcvtq_n_s32_f32(floats3, 31); - - int16x8_t shorts0 = vcombine_s16(vshrn_n_s32(ints0, 16), vshrn_n_s32(ints1, 16)); - int16x8_t shorts1 = vcombine_s16(vshrn_n_s32(ints2, 16), vshrn_n_s32(ints3, 16)); - - uint8x16_t bytes = veorq_u8(vreinterpretq_u8_s8( - vcombine_s8(vshrn_n_s16(shorts0, 8), vshrn_n_s16(shorts1, 8))), - flipper); - - vst1q_u8(&dst[i], bytes); - }) -} - -static void SDL_Convert_F32_to_S16_NEON(Sint16 *dst, const float *src, int num_samples) -{ - LOG_DEBUG_AUDIO_CONVERT("F32", "S16 (using NEON)"); - - CONVERT_16_FWD({ - vst1_lane_s16(&dst[i], vreinterpret_s16_s32(vcvt_n_s32_f32(vld1_dup_f32(&src[i]), 31)), 1); - }, { - float32x4_t floats0 = vld1q_f32(&src[i]); - float32x4_t floats1 = vld1q_f32(&src[i + 4]); - float32x4_t floats2 = vld1q_f32(&src[i + 8]); - float32x4_t floats3 = vld1q_f32(&src[i + 12]); - - int32x4_t ints0 = vcvtq_n_s32_f32(floats0, 31); - int32x4_t ints1 = vcvtq_n_s32_f32(floats1, 31); - int32x4_t ints2 = vcvtq_n_s32_f32(floats2, 31); - int32x4_t ints3 = vcvtq_n_s32_f32(floats3, 31); - - int16x8_t shorts0 = vcombine_s16(vshrn_n_s32(ints0, 16), vshrn_n_s32(ints1, 16)); - int16x8_t shorts1 = vcombine_s16(vshrn_n_s32(ints2, 16), vshrn_n_s32(ints3, 16)); - - vst1q_s16(&dst[i], shorts0); - vst1q_s16(&dst[i + 8], shorts1); - }) -} - -static void SDL_Convert_F32_to_S32_NEON(Sint32 *dst, const float *src, int num_samples) -{ - LOG_DEBUG_AUDIO_CONVERT("F32", "S32 (using NEON)"); - - CONVERT_16_FWD({ - vst1_lane_s32(&dst[i], vcvt_n_s32_f32(vld1_dup_f32(&src[i]), 31), 0); - }, { - float32x4_t floats0 = vld1q_f32(&src[i]); - float32x4_t floats1 = vld1q_f32(&src[i + 4]); - float32x4_t floats2 = vld1q_f32(&src[i + 8]); - float32x4_t floats3 = vld1q_f32(&src[i + 12]); - - int32x4_t ints0 = vcvtq_n_s32_f32(floats0, 31); - int32x4_t ints1 = vcvtq_n_s32_f32(floats1, 31); - int32x4_t ints2 = vcvtq_n_s32_f32(floats2, 31); - int32x4_t ints3 = vcvtq_n_s32_f32(floats3, 31); - - vst1q_s32(&dst[i], ints0); - vst1q_s32(&dst[i + 4], ints1); - vst1q_s32(&dst[i + 8], ints2); - vst1q_s32(&dst[i + 12], ints3); - }) -} - -static void SDL_Convert_Swap16_NEON(Uint16* dst, const Uint16* src, int num_samples) -{ - CONVERT_16_FWD({ - dst[i] = SDL_Swap16(src[i]); - }, { - uint8x16_t ints0 = vld1q_u8((const Uint8*)&src[i]); - uint8x16_t ints1 = vld1q_u8((const Uint8*)&src[i + 8]); - - ints0 = vrev16q_u8(ints0); - ints1 = vrev16q_u8(ints1); - - vst1q_u8((Uint8*)&dst[i], ints0); - vst1q_u8((Uint8*)&dst[i + 8], ints1); - }) -} - -static void SDL_Convert_Swap32_NEON(Uint32* dst, const Uint32* src, int num_samples) -{ - CONVERT_16_FWD({ - dst[i] = SDL_Swap32(src[i]); - }, { - uint8x16_t ints0 = vld1q_u8((const Uint8*)&src[i]); - uint8x16_t ints1 = vld1q_u8((const Uint8*)&src[i + 4]); - uint8x16_t ints2 = vld1q_u8((const Uint8*)&src[i + 8]); - uint8x16_t ints3 = vld1q_u8((const Uint8*)&src[i + 12]); - - ints0 = vrev32q_u8(ints0); - ints1 = vrev32q_u8(ints1); - ints2 = vrev32q_u8(ints2); - ints3 = vrev32q_u8(ints3); - - vst1q_u8((Uint8*)&dst[i], ints0); - vst1q_u8((Uint8*)&dst[i + 4], ints1); - vst1q_u8((Uint8*)&dst[i + 8], ints2); - vst1q_u8((Uint8*)&dst[i + 12], ints3); - }) -} -#endif - -#undef CONVERT_16_FWD -#undef CONVERT_16_REV - -// Function pointers set to a CPU-specific implementation. -static void (*SDL_Convert_S8_to_F32)(float *dst, const Sint8 *src, int num_samples) = NULL; -static void (*SDL_Convert_U8_to_F32)(float *dst, const Uint8 *src, int num_samples) = NULL; -static void (*SDL_Convert_S16_to_F32)(float *dst, const Sint16 *src, int num_samples) = NULL; -static void (*SDL_Convert_S32_to_F32)(float *dst, const Sint32 *src, int num_samples) = NULL; -static void (*SDL_Convert_F32_to_S8)(Sint8 *dst, const float *src, int num_samples) = NULL; -static void (*SDL_Convert_F32_to_U8)(Uint8 *dst, const float *src, int num_samples) = NULL; -static void (*SDL_Convert_F32_to_S16)(Sint16 *dst, const float *src, int num_samples) = NULL; -static void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_samples) = NULL; - -static void (*SDL_Convert_Swap16)(Uint16* dst, const Uint16* src, int num_samples) = NULL; -static void (*SDL_Convert_Swap32)(Uint32* dst, const Uint32* src, int num_samples) = NULL; - -void ConvertAudioToFloat(float *dst, const void *src, int num_samples, SDL_AudioFormat src_fmt) -{ - switch (src_fmt) { - case SDL_AUDIO_S8: - SDL_Convert_S8_to_F32(dst, (const Sint8 *) src, num_samples); - break; - - case SDL_AUDIO_U8: - SDL_Convert_U8_to_F32(dst, (const Uint8 *) src, num_samples); - break; - - case SDL_AUDIO_S16: - SDL_Convert_S16_to_F32(dst, (const Sint16 *) src, num_samples); - break; - - case SDL_AUDIO_S16 ^ SDL_AUDIO_MASK_BIG_ENDIAN: - SDL_Convert_Swap16((Uint16*) dst, (const Uint16*) src, num_samples); - SDL_Convert_S16_to_F32(dst, (const Sint16 *) dst, num_samples); - break; - - case SDL_AUDIO_S32: - SDL_Convert_S32_to_F32(dst, (const Sint32 *) src, num_samples); - break; - - case SDL_AUDIO_S32 ^ SDL_AUDIO_MASK_BIG_ENDIAN: - SDL_Convert_Swap32((Uint32*) dst, (const Uint32*) src, num_samples); - SDL_Convert_S32_to_F32(dst, (const Sint32 *) dst, num_samples); - break; - - case SDL_AUDIO_F32 ^ SDL_AUDIO_MASK_BIG_ENDIAN: - SDL_Convert_Swap32((Uint32*) dst, (const Uint32*) src, num_samples); - break; - - default: SDL_assert(!"Unexpected audio format!"); break; - } -} - -void ConvertAudioFromFloat(void *dst, const float *src, int num_samples, SDL_AudioFormat dst_fmt) -{ - switch (dst_fmt) { - case SDL_AUDIO_S8: - SDL_Convert_F32_to_S8((Sint8 *) dst, src, num_samples); - break; - - case SDL_AUDIO_U8: - SDL_Convert_F32_to_U8((Uint8 *) dst, src, num_samples); - break; - - case SDL_AUDIO_S16: - SDL_Convert_F32_to_S16((Sint16 *) dst, src, num_samples); - break; - - case SDL_AUDIO_S16 ^ SDL_AUDIO_MASK_BIG_ENDIAN: - SDL_Convert_F32_to_S16((Sint16 *) dst, src, num_samples); - SDL_Convert_Swap16((Uint16*) dst, (const Uint16*) dst, num_samples); - break; - - case SDL_AUDIO_S32: - SDL_Convert_F32_to_S32((Sint32 *) dst, src, num_samples); - break; - - case SDL_AUDIO_S32 ^ SDL_AUDIO_MASK_BIG_ENDIAN: - SDL_Convert_F32_to_S32((Sint32 *) dst, src, num_samples); - SDL_Convert_Swap32((Uint32*) dst, (const Uint32*) dst, num_samples); - break; - - case SDL_AUDIO_F32 ^ SDL_AUDIO_MASK_BIG_ENDIAN: - SDL_Convert_Swap32((Uint32*) dst, (const Uint32*) src, num_samples); - break; - - default: SDL_assert(!"Unexpected audio format!"); break; - } -} - -void ConvertAudioSwapEndian(void* dst, const void* src, int num_samples, int bitsize) -{ - switch (bitsize) { - case 16: SDL_Convert_Swap16((Uint16*) dst, (const Uint16*) src, num_samples); break; - case 32: SDL_Convert_Swap32((Uint32*) dst, (const Uint32*) src, num_samples); break; - default: SDL_assert(!"Unexpected audio format!"); break; - } -} - -void SDL_ChooseAudioConverters(void) -{ - static bool converters_chosen = false; - if (converters_chosen) { - return; - } - -#define SET_CONVERTER_FUNCS(fntype) \ - SDL_Convert_Swap16 = SDL_Convert_Swap16_##fntype; \ - SDL_Convert_Swap32 = SDL_Convert_Swap32_##fntype; - -#ifdef SDL_SSE4_1_INTRINSICS - if (SDL_HasSSE41()) { - SET_CONVERTER_FUNCS(SSSE3); - } else -#endif -#ifdef SDL_NEON_INTRINSICS - if (SDL_HasNEON()) { - SET_CONVERTER_FUNCS(NEON); - } else -#endif - { - SET_CONVERTER_FUNCS(Scalar); - } - -#undef SET_CONVERTER_FUNCS - -#define SET_CONVERTER_FUNCS(fntype) \ - SDL_Convert_S8_to_F32 = SDL_Convert_S8_to_F32_##fntype; \ - SDL_Convert_U8_to_F32 = SDL_Convert_U8_to_F32_##fntype; \ - SDL_Convert_S16_to_F32 = SDL_Convert_S16_to_F32_##fntype; \ - SDL_Convert_S32_to_F32 = SDL_Convert_S32_to_F32_##fntype; \ - SDL_Convert_F32_to_S8 = SDL_Convert_F32_to_S8_##fntype; \ - SDL_Convert_F32_to_U8 = SDL_Convert_F32_to_U8_##fntype; \ - SDL_Convert_F32_to_S16 = SDL_Convert_F32_to_S16_##fntype; \ - SDL_Convert_F32_to_S32 = SDL_Convert_F32_to_S32_##fntype; \ - -#ifdef SDL_SSE2_INTRINSICS - if (SDL_HasSSE2()) { - SET_CONVERTER_FUNCS(SSE2); - } else -#endif -#ifdef SDL_NEON_INTRINSICS - if (SDL_HasNEON()) { - SET_CONVERTER_FUNCS(NEON); - } else -#endif - { - SET_CONVERTER_FUNCS(Scalar); - } - -#undef SET_CONVERTER_FUNCS - - converters_chosen = true; -} diff --git a/contrib/SDL-3.2.8/src/audio/SDL_mixer.c b/contrib/SDL-3.2.8/src/audio/SDL_mixer.c deleted file mode 100644 index 047c6fd..0000000 --- a/contrib/SDL-3.2.8/src/audio/SDL_mixer.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -// This provides the default mixing callback for the SDL audio routines - -#include "SDL_sysaudio.h" - -/* This table is used to add two sound values together and pin - * the value to avoid overflow. (used with permission from ARDI) - */ -static const Uint8 mix8[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, - 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, - 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, - 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, - 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, - 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, - 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, - 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, - 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, - 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, - 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, - 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, - 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, - 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, - 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, - 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, - 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, - 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, - 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF -}; - -// The volume ranges from 0 - 128 -#define MIX_MAXVOLUME 128 -#define ADJUST_VOLUME(type, s, v) ((s) = (type)(((s) * (v)) / MIX_MAXVOLUME)) -#define ADJUST_VOLUME_U8(s, v) ((s) = (Uint8)(((((s) - 128) * (v)) / MIX_MAXVOLUME) + 128)) - -// !!! FIXME: This needs some SIMD magic. -// !!! FIXME: Add fast-path for volume = 1 -// !!! FIXME: Use larger scales for 16-bit/32-bit integers - -bool SDL_MixAudio(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format, Uint32 len, float fvolume) -{ - int volume = (int)SDL_roundf(fvolume * MIX_MAXVOLUME); - - if (volume == 0) { - return true; - } - - switch (format) { - - case SDL_AUDIO_U8: - { - Uint8 src_sample; - - while (len--) { - src_sample = *src; - ADJUST_VOLUME_U8(src_sample, volume); - *dst = mix8[*dst + src_sample]; - ++dst; - ++src; - } - } break; - - case SDL_AUDIO_S8: - { - Sint8 *dst8, *src8; - Sint8 src_sample; - int dst_sample; - const int max_audioval = SDL_MAX_SINT8; - const int min_audioval = SDL_MIN_SINT8; - - src8 = (Sint8 *)src; - dst8 = (Sint8 *)dst; - while (len--) { - src_sample = *src8; - ADJUST_VOLUME(Sint8, src_sample, volume); - dst_sample = *dst8 + src_sample; - if (dst_sample > max_audioval) { - dst_sample = max_audioval; - } else if (dst_sample < min_audioval) { - dst_sample = min_audioval; - } - *dst8 = (Sint8)dst_sample; - ++dst8; - ++src8; - } - } break; - - case SDL_AUDIO_S16LE: - { - Sint16 src1, src2; - int dst_sample; - const int max_audioval = SDL_MAX_SINT16; - const int min_audioval = SDL_MIN_SINT16; - - len /= 2; - while (len--) { - src1 = SDL_Swap16LE(*(Sint16 *)src); - ADJUST_VOLUME(Sint16, src1, volume); - src2 = SDL_Swap16LE(*(Sint16 *)dst); - src += 2; - dst_sample = src1 + src2; - if (dst_sample > max_audioval) { - dst_sample = max_audioval; - } else if (dst_sample < min_audioval) { - dst_sample = min_audioval; - } - *(Sint16 *)dst = SDL_Swap16LE((Sint16)dst_sample); - dst += 2; - } - } break; - - case SDL_AUDIO_S16BE: - { - Sint16 src1, src2; - int dst_sample; - const int max_audioval = SDL_MAX_SINT16; - const int min_audioval = SDL_MIN_SINT16; - - len /= 2; - while (len--) { - src1 = SDL_Swap16BE(*(Sint16 *)src); - ADJUST_VOLUME(Sint16, src1, volume); - src2 = SDL_Swap16BE(*(Sint16 *)dst); - src += 2; - dst_sample = src1 + src2; - if (dst_sample > max_audioval) { - dst_sample = max_audioval; - } else if (dst_sample < min_audioval) { - dst_sample = min_audioval; - } - *(Sint16 *)dst = SDL_Swap16BE((Sint16)dst_sample); - dst += 2; - } - } break; - - case SDL_AUDIO_S32LE: - { - const Uint32 *src32 = (Uint32 *)src; - Uint32 *dst32 = (Uint32 *)dst; - Sint64 src1, src2; - Sint64 dst_sample; - const Sint64 max_audioval = SDL_MAX_SINT32; - const Sint64 min_audioval = SDL_MIN_SINT32; - - len /= 4; - while (len--) { - src1 = (Sint64)((Sint32)SDL_Swap32LE(*src32)); - src32++; - ADJUST_VOLUME(Sint64, src1, volume); - src2 = (Sint64)((Sint32)SDL_Swap32LE(*dst32)); - dst_sample = src1 + src2; - if (dst_sample > max_audioval) { - dst_sample = max_audioval; - } else if (dst_sample < min_audioval) { - dst_sample = min_audioval; - } - *(dst32++) = SDL_Swap32LE((Uint32)((Sint32)dst_sample)); - } - } break; - - case SDL_AUDIO_S32BE: - { - const Uint32 *src32 = (Uint32 *)src; - Uint32 *dst32 = (Uint32 *)dst; - Sint64 src1, src2; - Sint64 dst_sample; - const Sint64 max_audioval = SDL_MAX_SINT32; - const Sint64 min_audioval = SDL_MIN_SINT32; - - len /= 4; - while (len--) { - src1 = (Sint64)((Sint32)SDL_Swap32BE(*src32)); - src32++; - ADJUST_VOLUME(Sint64, src1, volume); - src2 = (Sint64)((Sint32)SDL_Swap32BE(*dst32)); - dst_sample = src1 + src2; - if (dst_sample > max_audioval) { - dst_sample = max_audioval; - } else if (dst_sample < min_audioval) { - dst_sample = min_audioval; - } - *(dst32++) = SDL_Swap32BE((Uint32)((Sint32)dst_sample)); - } - } break; - - case SDL_AUDIO_F32LE: - { - const float *src32 = (float *)src; - float *dst32 = (float *)dst; - float src1, src2; - float dst_sample; - const float max_audioval = 1.0f; - const float min_audioval = -1.0f; - - len /= 4; - while (len--) { - src1 = SDL_SwapFloatLE(*src32) * fvolume; - src2 = SDL_SwapFloatLE(*dst32); - src32++; - - dst_sample = src1 + src2; - if (dst_sample > max_audioval) { - dst_sample = max_audioval; - } else if (dst_sample < min_audioval) { - dst_sample = min_audioval; - } - *(dst32++) = SDL_SwapFloatLE(dst_sample); - } - } break; - - case SDL_AUDIO_F32BE: - { - const float *src32 = (float *)src; - float *dst32 = (float *)dst; - float src1, src2; - float dst_sample; - const float max_audioval = 1.0f; - const float min_audioval = -1.0f; - - len /= 4; - while (len--) { - src1 = SDL_SwapFloatBE(*src32) * fvolume; - src2 = SDL_SwapFloatBE(*dst32); - src32++; - - dst_sample = src1 + src2; - if (dst_sample > max_audioval) { - dst_sample = max_audioval; - } else if (dst_sample < min_audioval) { - dst_sample = min_audioval; - } - *(dst32++) = SDL_SwapFloatBE(dst_sample); - } - } break; - - default: // If this happens... FIXME! - return SDL_SetError("SDL_MixAudio(): unknown audio format"); - } - - return true; -} diff --git a/contrib/SDL-3.2.8/src/audio/SDL_sysaudio.h b/contrib/SDL-3.2.8/src/audio/SDL_sysaudio.h deleted file mode 100644 index 4a88bd2..0000000 --- a/contrib/SDL-3.2.8/src/audio/SDL_sysaudio.h +++ /dev/null @@ -1,392 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include "SDL_internal.h" - -#ifndef SDL_sysaudio_h_ -#define SDL_sysaudio_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.", from, to); -#else -#define LOG_DEBUG_AUDIO_CONVERT(from, to) -#endif - -// !!! FIXME: These are wordy and unlocalized... -#define DEFAULT_PLAYBACK_DEVNAME "System audio playback device" -#define DEFAULT_RECORDING_DEVNAME "System audio recording device" - -// these are used when no better specifics are known. We default to CD audio quality. -#define DEFAULT_AUDIO_PLAYBACK_FORMAT SDL_AUDIO_S16 -#define DEFAULT_AUDIO_PLAYBACK_CHANNELS 2 -#define DEFAULT_AUDIO_PLAYBACK_FREQUENCY 44100 - -#define DEFAULT_AUDIO_RECORDING_FORMAT SDL_AUDIO_S16 -#define DEFAULT_AUDIO_RECORDING_CHANNELS 1 -#define DEFAULT_AUDIO_RECORDING_FREQUENCY 44100 - -#define SDL_MAX_CHANNELMAP_CHANNELS 8 // !!! FIXME: if SDL ever supports more channels, clean this out and make those parts dynamic. - -typedef struct SDL_AudioDevice SDL_AudioDevice; -typedef struct SDL_LogicalAudioDevice SDL_LogicalAudioDevice; - -// Used by src/SDL.c to initialize a particular audio driver. -extern bool 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. -extern void SDL_ChooseAudioConverters(void); -extern void SDL_SetupAudioResampler(void); - -/* Backends 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 SDL_AudioDevice *SDL_AddAudioDevice(bool recording, const char *name, const SDL_AudioSpec *spec, void *handle); - -/* Backends 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); - -// Backends should call this if the system default device changes. -extern void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device); - -// Backends should call this if a device's format is changing (opened or not); SDL will update state and carry on with the new format. -extern bool SDL_AudioDeviceFormatChanged(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames); - -// Same as above, but assume the device is already locked. -extern bool SDL_AudioDeviceFormatChangedAlreadyLocked(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames); - -// Find the SDL_AudioDevice associated with the handle supplied to SDL_AddAudioDevice. NULL if not found. DOES NOT LOCK THE DEVICE. -extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByHandle(void *handle); - -// Find an SDL_AudioDevice, selected by a callback. NULL if not found. DOES NOT LOCK THE DEVICE. -extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByCallback(bool (*callback)(SDL_AudioDevice *device, void *userdata), void *userdata); - -// 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); - -// Backends can call this to get a reasonable default sample frame count for a device's sample rate. -int SDL_GetDefaultSampleFramesFromFreq(const int freq); - -// Backends can call this to get a standardized name for a thread to power a specific audio device. -extern char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen); - -// Backends can call these to change a device's refcount. -extern void RefPhysicalAudioDevice(SDL_AudioDevice *device); -extern void UnrefPhysicalAudioDevice(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_PlaybackAudioThreadSetup(SDL_AudioDevice *device); -extern bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device); -extern void SDL_PlaybackAudioThreadShutdown(SDL_AudioDevice *device); -extern void SDL_RecordingAudioThreadSetup(SDL_AudioDevice *device); -extern bool SDL_RecordingAudioThreadIterate(SDL_AudioDevice *device); -extern void SDL_RecordingAudioThreadShutdown(SDL_AudioDevice *device); -extern void SDL_AudioThreadFinalize(SDL_AudioDevice *device); - -extern void ConvertAudioToFloat(float *dst, const void *src, int num_samples, SDL_AudioFormat src_fmt); -extern void ConvertAudioFromFloat(void *dst, const float *src, int num_samples, SDL_AudioFormat dst_fmt); -extern void ConvertAudioSwapEndian(void* dst, const void* src, int num_samples, int bitsize); - -extern bool SDL_ChannelMapIsDefault(const int *map, int channels); -extern bool SDL_ChannelMapIsBogus(const int *map, int channels); - -// this gets used from the audio device threads. It has rules, don't use this if you don't know how to use it! -extern void ConvertAudio(int num_frames, - const void *src, SDL_AudioFormat src_format, int src_channels, const int *src_map, - void *dst, SDL_AudioFormat dst_format, int dst_channels, const int *dst_map, - void* scratch, float gain); - -// Compare two SDL_AudioSpecs, return true if they match exactly. -// Using SDL_memcmp directly isn't safe, since potential padding might not be initialized. -// either channel map can be NULL for the default (and both should be if you don't care about them). -extern bool SDL_AudioSpecsEqual(const SDL_AudioSpec *a, const SDL_AudioSpec *b, const int *channel_map_a, const int *channel_map_b); - -// See if two channel maps match -// either channel map can be NULL for the default (and both should be if you don't care about them). -extern bool SDL_AudioChannelMapsEqual(int channels, const int *channel_map_a, const int *channel_map_b); - -// allocate+copy a channel map. -extern int *SDL_ChannelMapDup(const int *origchmap, int channels); - -// Special case to let something in SDL_audiocvt.c access something in SDL_audio.c. Don't use this. -extern void OnAudioStreamCreated(SDL_AudioStream *stream); -extern void OnAudioStreamDestroy(SDL_AudioStream *stream); - -// This just lets audio playback apply logical device gain at the same time as audiostream gain, so it's one multiplication instead of thousands. -extern int SDL_GetAudioStreamDataAdjustGain(SDL_AudioStream *stream, void *voidbuf, int len, float extra_gain); - -// This is the bulk of `SDL_SetAudioStream*putChannelMap`'s work, but it lets you skip the check about changing the device end of a stream if isinput==-1. -extern bool SetAudioStreamChannelMap(SDL_AudioStream *stream, const SDL_AudioSpec *spec, int **stream_chmap, const int *chmap, int channels, int isinput); - - -typedef struct SDL_AudioDriverImpl -{ - void (*DetectDevices)(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording); - bool (*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 - bool (*WaitDevice)(SDL_AudioDevice *device); - bool (*PlayDevice)(SDL_AudioDevice *device, const Uint8 *buffer, int buflen); // buffer and buflen are always from GetDeviceBuf, passed here for convenience. - Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *device, int *buffer_size); - bool (*WaitRecordingDevice)(SDL_AudioDevice *device); - int (*RecordDevice)(SDL_AudioDevice *device, void *buffer, int buflen); - void (*FlushRecording)(SDL_AudioDevice *device); - void (*CloseDevice)(SDL_AudioDevice *device); - void (*FreeDeviceHandle)(SDL_AudioDevice *device); // SDL is done with this device; free the handle from SDL_AddAudioDevice() - void (*DeinitializeStart)(void); // SDL calls this, then starts destroying objects, then calls Deinitialize. This is a good place to stop hotplug detection. - void (*Deinitialize)(void); - - // Some flags to push duplicate code into the core and reduce #ifdefs. - bool ProvidesOwnCallbackThread; // !!! FIXME: rename this, it's not a callback thread anymore. - bool HasRecordingSupport; - bool OnlyHasDefaultPlaybackDevice; - bool OnlyHasDefaultRecordingDevice; // !!! FIXME: is there ever a time where you'd have a default playback and not a default recording (or vice versa)? -} SDL_AudioDriverImpl; - - -typedef struct SDL_PendingAudioDeviceEvent -{ - Uint32 type; - SDL_AudioDeviceID devid; - struct SDL_PendingAudioDeviceEvent *next; -} SDL_PendingAudioDeviceEvent; - -typedef struct SDL_AudioDriver -{ - 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_hash_lock; // A rwlock that protects `device_hash` - SDL_HashTable *device_hash; // the collection of currently-available audio devices (recording, playback, logical and physical!) - SDL_AudioStream *existing_streams; // a list of all existing SDL_AudioStreams. - SDL_AudioDeviceID default_playback_device_id; - SDL_AudioDeviceID default_recording_device_id; - SDL_PendingAudioDeviceEvent pending_events; - SDL_PendingAudioDeviceEvent *pending_events_tail; - - // !!! FIXME: most (all?) of these don't have to be atomic. - SDL_AtomicInt playback_device_count; - SDL_AtomicInt recording_device_count; - SDL_AtomicInt shutting_down; // non-zero during SDL_Quit, so we known not to accept any last-minute device hotplugs. -} SDL_AudioDriver; - -struct SDL_AudioQueue; // forward decl. - -struct SDL_AudioStream -{ - SDL_Mutex* lock; - - SDL_PropertiesID props; - - SDL_AudioStreamCallback get_callback; - void *get_callback_userdata; - SDL_AudioStreamCallback put_callback; - void *put_callback_userdata; - - SDL_AudioSpec src_spec; - SDL_AudioSpec dst_spec; - int *src_chmap; - int *dst_chmap; - float freq_ratio; - float gain; - - struct SDL_AudioQueue* queue; - - SDL_AudioSpec input_spec; // The spec of input data currently being processed - int *input_chmap; - int input_chmap_storage[SDL_MAX_CHANNELMAP_CHANNELS]; // !!! FIXME: this needs to grow if SDL ever supports more channels. But if it grows, we should probably be more clever about allocations. - Sint64 resample_offset; - - Uint8 *work_buffer; // used for scratch space during data conversion/resampling. - size_t work_buffer_allocation; - - bool simplified; // true if created via SDL_OpenAudioDeviceStream - - SDL_LogicalAudioDevice *bound_device; - SDL_AudioStream *next_binding; - SDL_AudioStream *prev_binding; - - SDL_AudioStream *prev; // linked list of all existing streams (so we can free them on shutdown). - SDL_AudioStream *next; // linked list of all existing streams (so we can free them on shutdown). -}; - -/* Logical devices are an abstraction in SDL3; you can open the same physical - device multiple times, and each will result in an object with its own set - of bound audio streams, etc, even though internally these are all processed - as a group when mixing the final output for the physical device. */ -struct SDL_LogicalAudioDevice -{ - // the unique instance ID of this device. - SDL_AudioDeviceID instance_id; - - // The physical device associated with this opened device. - SDL_AudioDevice *physical_device; - - // If whole logical device is paused (process no streams bound to this device). - SDL_AtomicInt paused; - - // Volume of the device output. - float gain; - - // double-linked list of all audio streams currently bound to this opened device. - SDL_AudioStream *bound_streams; - - // true if this was opened as a default device. - bool opened_as_default; - - // true if device was opened with SDL_OpenAudioDeviceStream (so it forbids binding changes, etc). - bool simplified; - - // If non-NULL, callback into the app that lets them access the final postmix buffer. - SDL_AudioPostmixCallback postmix; - - // App-supplied pointer for postmix callback. - void *postmix_userdata; - - // double-linked list of opened devices on the same physical device. - SDL_LogicalAudioDevice *next; - SDL_LogicalAudioDevice *prev; -}; - -struct SDL_AudioDevice -{ - // A mutex for locking access to this struct - SDL_Mutex *lock; - - // A condition variable to protect device close, where we can't hold the device lock forever. - SDL_Condition *close_cond; - - // Reference count of the device; logical devices, device threads, etc, add to this. - SDL_AtomicInt refcount; - - // These are, initially, set from current_audio, but we might swap them out with Zombie versions on disconnect/failure. - bool (*WaitDevice)(SDL_AudioDevice *device); - bool (*PlayDevice)(SDL_AudioDevice *device, const Uint8 *buffer, int buflen); - Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *device, int *buffer_size); - bool (*WaitRecordingDevice)(SDL_AudioDevice *device); - int (*RecordDevice)(SDL_AudioDevice *device, void *buffer, int buflen); - void (*FlushRecording)(SDL_AudioDevice *device); - - // 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; - - // The size, in bytes, of the device's playback/recording buffer. - int buffer_size; - - // The device's channel map, or NULL for SDL default layout. - int *chmap; - - // The device's default audio specification - SDL_AudioSpec default_spec; - - // Number of sample frames the devices wants per-buffer. - int sample_frames; - - // Value to use for SDL_memset to silence a buffer in this device's format - int silence_value; - - // non-zero if we are signaling the audio thread to end. - SDL_AtomicInt shutdown; - - // non-zero if this was a disconnected device and we're waiting for it to be decommissioned. - SDL_AtomicInt zombie; - - // true if this is a recording device instead of an playback device - bool recording; - - // true if audio thread can skip silence/mix/convert stages and just do a basic memcpy. - bool simple_copy; - - // Scratch buffers used for mixing. - Uint8 *work_buffer; - Uint8 *mix_buffer; - float *postmix_buffer; - - // Size of work_buffer (and mix_buffer) in bytes. - int work_buffer_size; - - // A thread to feed the audio device - SDL_Thread *thread; - - // true if this physical device is currently opened by the backend. - bool currently_opened; - - // Data private to this driver - struct SDL_PrivateAudioData *hidden; - - // All logical devices associated with this physical device. - SDL_LogicalAudioDevice *logical_devices; -}; - -typedef struct AudioBootStrap -{ - const char *name; - const char *desc; - bool (*init)(SDL_AudioDriverImpl *impl); - bool demand_only; // if true: request explicitly, or it won't be available. - bool is_preferred; -} AudioBootStrap; - -// Not all of these are available in a given build. Use #ifdefs, etc. -extern AudioBootStrap PRIVATEAUDIO_bootstrap; -extern AudioBootStrap PIPEWIRE_PREFERRED_bootstrap; -extern AudioBootStrap PIPEWIRE_bootstrap; -extern AudioBootStrap PULSEAUDIO_bootstrap; -extern AudioBootStrap ALSA_bootstrap; -extern AudioBootStrap JACK_bootstrap; -extern AudioBootStrap SNDIO_bootstrap; -extern AudioBootStrap NETBSDAUDIO_bootstrap; -extern AudioBootStrap DSP_bootstrap; -extern AudioBootStrap WASAPI_bootstrap; -extern AudioBootStrap DSOUND_bootstrap; -extern AudioBootStrap WINMM_bootstrap; -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 PS2AUDIO_bootstrap; -extern AudioBootStrap PSPAUDIO_bootstrap; -extern AudioBootStrap VITAAUD_bootstrap; -extern AudioBootStrap N3DSAUDIO_bootstrap; -extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap; -extern AudioBootStrap QSAAUDIO_bootstrap; - -#endif // SDL_sysaudio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/SDL_wave.c b/contrib/SDL-3.2.8/src/audio/SDL_wave.c deleted file mode 100644 index 1d53e79..0000000 --- a/contrib/SDL-3.2.8/src/audio/SDL_wave.c +++ /dev/null @@ -1,2151 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef HAVE_LIMITS_H -#include -#endif -#ifndef INT_MAX -SDL_COMPILE_TIME_ASSERT(int_size, sizeof(int) == sizeof(Sint32)); -#define INT_MAX SDL_MAX_SINT32 -#endif -#ifndef SIZE_MAX -#define SIZE_MAX ((size_t)-1) -#endif - -// Microsoft WAVE file loading routines - -#include "SDL_wave.h" -#include "SDL_sysaudio.h" - -/* Reads the value stored at the location of the f1 pointer, multiplies it - * with the second argument and then stores the result to f1. - * Returns 0 on success, or -1 if the multiplication overflows, in which case f1 - * does not get modified. - */ -static int SafeMult(size_t *f1, size_t f2) -{ - if (*f1 > 0 && SIZE_MAX / *f1 <= f2) { - return -1; - } - *f1 *= f2; - return 0; -} - -typedef struct ADPCM_DecoderState -{ - Uint32 channels; // Number of channels. - size_t blocksize; // Size of an ADPCM block in bytes. - size_t blockheadersize; // Size of an ADPCM block header in bytes. - size_t samplesperblock; // Number of samples per channel in an ADPCM block. - size_t framesize; // Size of a sample frame (16-bit PCM) in bytes. - Sint64 framestotal; // Total number of sample frames. - Sint64 framesleft; // Number of sample frames still to be decoded. - void *ddata; // Decoder data from initialization. - void *cstate; // Decoding state for each channel. - - // ADPCM data. - struct - { - Uint8 *data; - size_t size; - size_t pos; - } input; - - // Current ADPCM block in the ADPCM data above. - struct - { - Uint8 *data; - size_t size; - size_t pos; - } block; - - // Decoded 16-bit PCM data. - struct - { - Sint16 *data; - size_t size; - size_t pos; - } output; -} ADPCM_DecoderState; - -typedef struct MS_ADPCM_CoeffData -{ - Uint16 coeffcount; - Sint16 *coeff; - Sint16 aligndummy; // Has to be last member. -} MS_ADPCM_CoeffData; - -typedef struct MS_ADPCM_ChannelState -{ - Uint16 delta; - Sint16 coeff1; - Sint16 coeff2; -} MS_ADPCM_ChannelState; - -#ifdef SDL_WAVE_DEBUG_LOG_FORMAT -static void WaveDebugLogFormat(WaveFile *file) -{ - WaveFormat *format = &file->format; - const char *fmtstr = "WAVE file: %s, %u Hz, %s, %u bits, %u %s/s"; - const char *waveformat, *wavechannel, *wavebpsunit = "B"; - Uint32 wavebps = format->byterate; - char channelstr[64]; - - SDL_zeroa(channelstr); - - switch (format->encoding) { - case PCM_CODE: - waveformat = "PCM"; - break; - case IEEE_FLOAT_CODE: - waveformat = "IEEE Float"; - break; - case ALAW_CODE: - waveformat = "A-law"; - break; - case MULAW_CODE: - waveformat = "\xc2\xb5-law"; - break; - case MS_ADPCM_CODE: - waveformat = "MS ADPCM"; - break; - case IMA_ADPCM_CODE: - waveformat = "IMA ADPCM"; - break; - default: - waveformat = "Unknown"; - break; - } - -#define SDL_WAVE_DEBUG_CHANNELCFG(STR, CODE) \ - case CODE: \ - wavechannel = STR; \ - break; -#define SDL_WAVE_DEBUG_CHANNELSTR(STR, CODE) \ - if (format->channelmask & CODE) { \ - SDL_strlcat(channelstr, channelstr[0] ? "-" STR : STR, sizeof(channelstr)); \ - } - - if (format->formattag == EXTENSIBLE_CODE && format->channelmask > 0) { - switch (format->channelmask) { - SDL_WAVE_DEBUG_CHANNELCFG("1.0 Mono", 0x4) - SDL_WAVE_DEBUG_CHANNELCFG("1.1 Mono", 0xc) - SDL_WAVE_DEBUG_CHANNELCFG("2.0 Stereo", 0x3) - SDL_WAVE_DEBUG_CHANNELCFG("2.1 Stereo", 0xb) - SDL_WAVE_DEBUG_CHANNELCFG("3.0 Stereo", 0x7) - SDL_WAVE_DEBUG_CHANNELCFG("3.1 Stereo", 0xf) - SDL_WAVE_DEBUG_CHANNELCFG("3.0 Surround", 0x103) - SDL_WAVE_DEBUG_CHANNELCFG("3.1 Surround", 0x10b) - SDL_WAVE_DEBUG_CHANNELCFG("4.0 Quad", 0x33) - SDL_WAVE_DEBUG_CHANNELCFG("4.1 Quad", 0x3b) - SDL_WAVE_DEBUG_CHANNELCFG("4.0 Surround", 0x107) - SDL_WAVE_DEBUG_CHANNELCFG("4.1 Surround", 0x10f) - SDL_WAVE_DEBUG_CHANNELCFG("5.0", 0x37) - SDL_WAVE_DEBUG_CHANNELCFG("5.1", 0x3f) - SDL_WAVE_DEBUG_CHANNELCFG("5.0 Side", 0x607) - SDL_WAVE_DEBUG_CHANNELCFG("5.1 Side", 0x60f) - SDL_WAVE_DEBUG_CHANNELCFG("6.0", 0x137) - SDL_WAVE_DEBUG_CHANNELCFG("6.1", 0x13f) - SDL_WAVE_DEBUG_CHANNELCFG("6.0 Side", 0x707) - SDL_WAVE_DEBUG_CHANNELCFG("6.1 Side", 0x70f) - SDL_WAVE_DEBUG_CHANNELCFG("7.0", 0xf7) - SDL_WAVE_DEBUG_CHANNELCFG("7.1", 0xff) - SDL_WAVE_DEBUG_CHANNELCFG("7.0 Side", 0x6c7) - SDL_WAVE_DEBUG_CHANNELCFG("7.1 Side", 0x6cf) - SDL_WAVE_DEBUG_CHANNELCFG("7.0 Surround", 0x637) - SDL_WAVE_DEBUG_CHANNELCFG("7.1 Surround", 0x63f) - SDL_WAVE_DEBUG_CHANNELCFG("9.0 Surround", 0x5637) - SDL_WAVE_DEBUG_CHANNELCFG("9.1 Surround", 0x563f) - SDL_WAVE_DEBUG_CHANNELCFG("11.0 Surround", 0x56f7) - SDL_WAVE_DEBUG_CHANNELCFG("11.1 Surround", 0x56ff) - default: - SDL_WAVE_DEBUG_CHANNELSTR("FL", 0x1) - SDL_WAVE_DEBUG_CHANNELSTR("FR", 0x2) - SDL_WAVE_DEBUG_CHANNELSTR("FC", 0x4) - SDL_WAVE_DEBUG_CHANNELSTR("LF", 0x8) - SDL_WAVE_DEBUG_CHANNELSTR("BL", 0x10) - SDL_WAVE_DEBUG_CHANNELSTR("BR", 0x20) - SDL_WAVE_DEBUG_CHANNELSTR("FLC", 0x40) - SDL_WAVE_DEBUG_CHANNELSTR("FRC", 0x80) - SDL_WAVE_DEBUG_CHANNELSTR("BC", 0x100) - SDL_WAVE_DEBUG_CHANNELSTR("SL", 0x200) - SDL_WAVE_DEBUG_CHANNELSTR("SR", 0x400) - SDL_WAVE_DEBUG_CHANNELSTR("TC", 0x800) - SDL_WAVE_DEBUG_CHANNELSTR("TFL", 0x1000) - SDL_WAVE_DEBUG_CHANNELSTR("TFC", 0x2000) - SDL_WAVE_DEBUG_CHANNELSTR("TFR", 0x4000) - SDL_WAVE_DEBUG_CHANNELSTR("TBL", 0x8000) - SDL_WAVE_DEBUG_CHANNELSTR("TBC", 0x10000) - SDL_WAVE_DEBUG_CHANNELSTR("TBR", 0x20000) - break; - } - } else { - switch (format->channels) { - default: - if (SDL_snprintf(channelstr, sizeof(channelstr), "%u channels", format->channels) >= 0) { - wavechannel = channelstr; - break; - } - case 0: - wavechannel = "Unknown"; - break; - case 1: - wavechannel = "Mono"; - break; - case 2: - wavechannel = "Setero"; - break; - } - } - -#undef SDL_WAVE_DEBUG_CHANNELCFG -#undef SDL_WAVE_DEBUG_CHANNELSTR - - if (wavebps >= 1024) { - wavebpsunit = "KiB"; - wavebps = wavebps / 1024 + (wavebps & 0x3ff ? 1 : 0); - } - - SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, fmtstr, waveformat, format->frequency, wavechannel, format->bitspersample, wavebps, wavebpsunit); -} -#endif - -#ifdef SDL_WAVE_DEBUG_DUMP_FORMAT -static void WaveDebugDumpFormat(WaveFile *file, Uint32 rifflen, Uint32 fmtlen, Uint32 datalen) -{ - WaveFormat *format = &file->format; - const char *fmtstr1 = "WAVE chunk dump:\n" - "-------------------------------------------\n" - "RIFF %11u\n" - "-------------------------------------------\n" - " fmt %11u\n" - " wFormatTag 0x%04x\n" - " nChannels %11u\n" - " nSamplesPerSec %11u\n" - " nAvgBytesPerSec %11u\n" - " nBlockAlign %11u\n"; - const char *fmtstr2 = " wBitsPerSample %11u\n"; - const char *fmtstr3 = " cbSize %11u\n"; - const char *fmtstr4a = " wValidBitsPerSample %11u\n"; - const char *fmtstr4b = " wSamplesPerBlock %11u\n"; - const char *fmtstr5 = " dwChannelMask 0x%08x\n" - " SubFormat\n" - " %08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x\n"; - const char *fmtstr6 = "-------------------------------------------\n" - " fact\n" - " dwSampleLength %11u\n"; - const char *fmtstr7 = "-------------------------------------------\n" - " data %11u\n" - "-------------------------------------------\n"; - char *dumpstr; - size_t dumppos = 0; - const size_t bufsize = 1024; - int res; - - dumpstr = SDL_malloc(bufsize); - if (!dumpstr) { - return; - } - dumpstr[0] = 0; - - res = SDL_snprintf(dumpstr, bufsize, fmtstr1, rifflen, fmtlen, format->formattag, format->channels, format->frequency, format->byterate, format->blockalign); - dumppos += res > 0 ? res : 0; - if (fmtlen >= 16) { - res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr2, format->bitspersample); - dumppos += res > 0 ? res : 0; - } - if (fmtlen >= 18) { - res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr3, format->extsize); - dumppos += res > 0 ? res : 0; - } - if (format->formattag == EXTENSIBLE_CODE && fmtlen >= 40 && format->extsize >= 22) { - const Uint8 *g = format->subformat; - const Uint32 g1 = g[0] | ((Uint32)g[1] << 8) | ((Uint32)g[2] << 16) | ((Uint32)g[3] << 24); - const Uint32 g2 = g[4] | ((Uint32)g[5] << 8); - const Uint32 g3 = g[6] | ((Uint32)g[7] << 8); - - switch (format->encoding) { - default: - res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr4a, format->validsamplebits); - dumppos += res > 0 ? res : 0; - break; - case MS_ADPCM_CODE: - case IMA_ADPCM_CODE: - res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr4b, format->samplesperblock); - dumppos += res > 0 ? res : 0; - break; - } - res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr5, format->channelmask, g1, g2, g3, g[8], g[9], g[10], g[11], g[12], g[13], g[14], g[15]); - dumppos += res > 0 ? res : 0; - } else { - switch (format->encoding) { - case MS_ADPCM_CODE: - case IMA_ADPCM_CODE: - if (fmtlen >= 20 && format->extsize >= 2) { - res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr4b, format->samplesperblock); - dumppos += res > 0 ? res : 0; - } - break; - } - } - if (file->fact.status >= 1) { - res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr6, file->fact.samplelength); - dumppos += res > 0 ? res : 0; - } - res = SDL_snprintf(dumpstr + dumppos, bufsize - dumppos, fmtstr7, datalen); - dumppos += res > 0 ? res : 0; - - SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "%s", dumpstr); - - SDL_free(dumpstr); -} -#endif - -static Sint64 WaveAdjustToFactValue(WaveFile *file, Sint64 sampleframes) -{ - if (file->fact.status == 2) { - if (file->facthint == FactStrict && sampleframes < file->fact.samplelength) { - SDL_SetError("Invalid number of sample frames in WAVE fact chunk (too many)"); - return -1; - } else if (sampleframes > file->fact.samplelength) { - return file->fact.samplelength; - } - } - - return sampleframes; -} - -static bool MS_ADPCM_CalculateSampleFrames(WaveFile *file, size_t datalength) -{ - WaveFormat *format = &file->format; - const size_t blockheadersize = (size_t)file->format.channels * 7; - const size_t availableblocks = datalength / file->format.blockalign; - const size_t blockframebitsize = (size_t)file->format.bitspersample * file->format.channels; - const size_t trailingdata = datalength % file->format.blockalign; - - if (file->trunchint == TruncVeryStrict || file->trunchint == TruncStrict) { - // The size of the data chunk must be a multiple of the block size. - if (datalength < blockheadersize || trailingdata > 0) { - return SDL_SetError("Truncated MS ADPCM block"); - } - } - - // Calculate number of sample frames that will be decoded. - file->sampleframes = (Sint64)availableblocks * format->samplesperblock; - if (trailingdata > 0) { - // The last block is truncated. Check if we can get any samples out of it. - if (file->trunchint == TruncDropFrame) { - // Drop incomplete sample frame. - if (trailingdata >= blockheadersize) { - size_t trailingsamples = 2 + (trailingdata - blockheadersize) * 8 / blockframebitsize; - if (trailingsamples > format->samplesperblock) { - trailingsamples = format->samplesperblock; - } - file->sampleframes += trailingsamples; - } - } - } - - file->sampleframes = WaveAdjustToFactValue(file, file->sampleframes); - if (file->sampleframes < 0) { - return false; - } - - return true; -} - -static bool MS_ADPCM_Init(WaveFile *file, size_t datalength) -{ - WaveFormat *format = &file->format; - WaveChunk *chunk = &file->chunk; - const size_t blockheadersize = (size_t)format->channels * 7; - const size_t blockdatasize = (size_t)format->blockalign - blockheadersize; - const size_t blockframebitsize = (size_t)format->bitspersample * format->channels; - const size_t blockdatasamples = (blockdatasize * 8) / blockframebitsize; - const Sint16 presetcoeffs[14] = { 256, 0, 512, -256, 0, 0, 192, 64, 240, 0, 460, -208, 392, -232 }; - size_t i, coeffcount; - MS_ADPCM_CoeffData *coeffdata; - - // Sanity checks. - - /* While it's clear how IMA ADPCM handles more than two channels, the nibble - * order of MS ADPCM makes it awkward. The Standards Update does not talk - * about supporting more than stereo anyway. - */ - if (format->channels > 2) { - return SDL_SetError("Invalid number of channels"); - } - - if (format->bitspersample != 4) { - return SDL_SetError("Invalid MS ADPCM bits per sample of %u", (unsigned int)format->bitspersample); - } - - // The block size must be big enough to contain the block header. - if (format->blockalign < blockheadersize) { - return SDL_SetError("Invalid MS ADPCM block size (nBlockAlign)"); - } - - if (format->formattag == EXTENSIBLE_CODE) { - /* Does have a GUID (like all format tags), but there's no specification - * for how the data is packed into the extensible header. Making - * assumptions here could lead to new formats nobody wants to support. - */ - return SDL_SetError("MS ADPCM with the extensible header is not supported"); - } - - /* There are wSamplesPerBlock, wNumCoef, and at least 7 coefficient pairs in - * the extended part of the header. - */ - if (chunk->size < 22) { - return SDL_SetError("Could not read MS ADPCM format header"); - } - - format->samplesperblock = chunk->data[18] | ((Uint16)chunk->data[19] << 8); - // Number of coefficient pairs. A pair has two 16-bit integers. - coeffcount = chunk->data[20] | ((size_t)chunk->data[21] << 8); - /* bPredictor, the integer offset into the coefficients array, is only - * 8 bits. It can only address the first 256 coefficients. Let's limit - * the count number here. - */ - if (coeffcount > 256) { - coeffcount = 256; - } - - if (chunk->size < 22 + coeffcount * 4) { - return SDL_SetError("Could not read custom coefficients in MS ADPCM format header"); - } else if (format->extsize < 4 + coeffcount * 4) { - return SDL_SetError("Invalid MS ADPCM format header (too small)"); - } else if (coeffcount < 7) { - return SDL_SetError("Missing required coefficients in MS ADPCM format header"); - } - - coeffdata = (MS_ADPCM_CoeffData *)SDL_malloc(sizeof(MS_ADPCM_CoeffData) + coeffcount * 4); - file->decoderdata = coeffdata; // Freed in cleanup. - if (!coeffdata) { - return false; - } - coeffdata->coeff = &coeffdata->aligndummy; - coeffdata->coeffcount = (Uint16)coeffcount; - - // Copy the 16-bit pairs. - for (i = 0; i < coeffcount * 2; i++) { - Sint32 c = chunk->data[22 + i * 2] | ((Sint32)chunk->data[23 + i * 2] << 8); - if (c >= 0x8000) { - c -= 0x10000; - } - if (i < 14 && c != presetcoeffs[i]) { - return SDL_SetError("Wrong preset coefficients in MS ADPCM format header"); - } - coeffdata->coeff[i] = (Sint16)c; - } - - /* Technically, wSamplesPerBlock is required, but we have all the - * information in the other fields to calculate it, if it's zero. - */ - if (format->samplesperblock == 0) { - /* Let's be nice to the encoders that didn't know how to fill this. - * The Standards Update calculates it this way: - * - * x = Block size (in bits) minus header size (in bits) - * y = Bit depth multiplied by channel count - * z = Number of samples per channel in block header - * wSamplesPerBlock = x / y + z - */ - format->samplesperblock = (Uint32)blockdatasamples + 2; - } - - /* nBlockAlign can be in conflict with wSamplesPerBlock. For example, if - * the number of samples doesn't fit into the block. The Standards Update - * also describes wSamplesPerBlock with a formula that makes it necessary to - * always fill the block with the maximum amount of samples, but this is not - * enforced here as there are no compatibility issues. - * A truncated block header with just one sample is not supported. - */ - if (format->samplesperblock == 1 || blockdatasamples < format->samplesperblock - 2) { - return SDL_SetError("Invalid number of samples per MS ADPCM block (wSamplesPerBlock)"); - } - - if (!MS_ADPCM_CalculateSampleFrames(file, datalength)) { - return false; - } - - return true; -} - -static Sint16 MS_ADPCM_ProcessNibble(MS_ADPCM_ChannelState *cstate, Sint32 sample1, Sint32 sample2, Uint8 nybble) -{ - const Sint32 max_audioval = 32767; - const Sint32 min_audioval = -32768; - const Uint16 max_deltaval = 65535; - const Uint16 adaptive[] = { - 230, 230, 230, 230, 307, 409, 512, 614, - 768, 614, 512, 409, 307, 230, 230, 230 - }; - Sint32 new_sample; - Sint32 errordelta; - Uint32 delta = cstate->delta; - - new_sample = (sample1 * cstate->coeff1 + sample2 * cstate->coeff2) / 256; - // The nibble is a signed 4-bit error delta. - errordelta = (Sint32)nybble - (nybble >= 0x08 ? 0x10 : 0); - new_sample += (Sint32)delta * errordelta; - if (new_sample < min_audioval) { - new_sample = min_audioval; - } else if (new_sample > max_audioval) { - new_sample = max_audioval; - } - delta = (delta * adaptive[nybble]) / 256; - if (delta < 16) { - delta = 16; - } else if (delta > max_deltaval) { - /* This issue is not described in the Standards Update and therefore - * undefined. It seems sensible to prevent overflows with a limit. - */ - delta = max_deltaval; - } - - cstate->delta = (Uint16)delta; - return (Sint16)new_sample; -} - -static bool MS_ADPCM_DecodeBlockHeader(ADPCM_DecoderState *state) -{ - Uint8 coeffindex; - const Uint32 channels = state->channels; - Sint32 sample; - Uint32 c; - MS_ADPCM_ChannelState *cstate = (MS_ADPCM_ChannelState *)state->cstate; - MS_ADPCM_CoeffData *ddata = (MS_ADPCM_CoeffData *)state->ddata; - - for (c = 0; c < channels; c++) { - size_t o = c; - - // Load the coefficient pair into the channel state. - coeffindex = state->block.data[o]; - if (coeffindex > ddata->coeffcount) { - return SDL_SetError("Invalid MS ADPCM coefficient index in block header"); - } - cstate[c].coeff1 = ddata->coeff[coeffindex * 2]; - cstate[c].coeff2 = ddata->coeff[coeffindex * 2 + 1]; - - // Initial delta value. - o = (size_t)channels + c * 2; - cstate[c].delta = state->block.data[o] | ((Uint16)state->block.data[o + 1] << 8); - - /* Load the samples from the header. Interestingly, the sample later in - * the output stream comes first. - */ - o = (size_t)channels * 3 + c * 2; - sample = state->block.data[o] | ((Sint32)state->block.data[o + 1] << 8); - if (sample >= 0x8000) { - sample -= 0x10000; - } - state->output.data[state->output.pos + channels] = (Sint16)sample; - - o = (size_t)channels * 5 + c * 2; - sample = state->block.data[o] | ((Sint32)state->block.data[o + 1] << 8); - if (sample >= 0x8000) { - sample -= 0x10000; - } - state->output.data[state->output.pos] = (Sint16)sample; - - state->output.pos++; - } - - state->block.pos += state->blockheadersize; - - // Skip second sample frame that came from the header. - state->output.pos += state->channels; - - // Header provided two sample frames. - state->framesleft -= 2; - - return true; -} - -/* Decodes the data of the MS ADPCM block. Decoding will stop if a block is too - * short, returning with none or partially decoded data. The partial data - * will always contain full sample frames (same sample count for each channel). - * Incomplete sample frames are discarded. - */ -static bool MS_ADPCM_DecodeBlockData(ADPCM_DecoderState *state) -{ - Uint16 nybble = 0; - Sint16 sample1, sample2; - const Uint32 channels = state->channels; - Uint32 c; - MS_ADPCM_ChannelState *cstate = (MS_ADPCM_ChannelState *)state->cstate; - - size_t blockpos = state->block.pos; - size_t blocksize = state->block.size; - - size_t outpos = state->output.pos; - - Sint64 blockframesleft = state->samplesperblock - 2; - if (blockframesleft > state->framesleft) { - blockframesleft = state->framesleft; - } - - while (blockframesleft > 0) { - for (c = 0; c < channels; c++) { - if (nybble & 0x4000) { - nybble <<= 4; - } else if (blockpos < blocksize) { - nybble = state->block.data[blockpos++] | 0x4000; - } else { - // Out of input data. Drop the incomplete frame and return. - state->output.pos = outpos - c; - return false; - } - - // Load previous samples which may come from the block header. - sample1 = state->output.data[outpos - channels]; - sample2 = state->output.data[outpos - channels * 2]; - - sample1 = MS_ADPCM_ProcessNibble(cstate + c, sample1, sample2, (nybble >> 4) & 0x0f); - state->output.data[outpos++] = sample1; - } - - state->framesleft--; - blockframesleft--; - } - - state->output.pos = outpos; - - return true; -} - -static bool MS_ADPCM_Decode(WaveFile *file, Uint8 **audio_buf, Uint32 *audio_len) -{ - bool result; - size_t bytesleft, outputsize; - WaveChunk *chunk = &file->chunk; - ADPCM_DecoderState state; - MS_ADPCM_ChannelState cstate[2]; - - SDL_zero(state); - SDL_zeroa(cstate); - - if (chunk->size != chunk->length) { - // Could not read everything. Recalculate number of sample frames. - if (!MS_ADPCM_CalculateSampleFrames(file, chunk->size)) { - return false; - } - } - - // Nothing to decode, nothing to return. - if (file->sampleframes == 0) { - *audio_buf = NULL; - *audio_len = 0; - return true; - } - - state.blocksize = file->format.blockalign; - state.channels = file->format.channels; - state.blockheadersize = (size_t)state.channels * 7; - state.samplesperblock = file->format.samplesperblock; - state.framesize = state.channels * sizeof(Sint16); - state.ddata = file->decoderdata; - state.framestotal = file->sampleframes; - state.framesleft = state.framestotal; - - state.input.data = chunk->data; - state.input.size = chunk->size; - state.input.pos = 0; - - // The output size in bytes. May get modified if data is truncated. - outputsize = (size_t)state.framestotal; - if (SafeMult(&outputsize, state.framesize)) { - return SDL_SetError("WAVE file too big"); - } else if (outputsize > SDL_MAX_UINT32 || state.framestotal > SIZE_MAX) { - return SDL_SetError("WAVE file too big"); - } - - state.output.pos = 0; - state.output.size = outputsize / sizeof(Sint16); - state.output.data = (Sint16 *)SDL_calloc(1, outputsize); - if (!state.output.data) { - return false; - } - - state.cstate = cstate; - - // Decode block by block. A truncated block will stop the decoding. - bytesleft = state.input.size - state.input.pos; - while (state.framesleft > 0 && bytesleft >= state.blockheadersize) { - state.block.data = state.input.data + state.input.pos; - state.block.size = bytesleft < state.blocksize ? bytesleft : state.blocksize; - state.block.pos = 0; - - if (state.output.size - state.output.pos < (Uint64)state.framesleft * state.channels) { - // Somehow didn't allocate enough space for the output. - SDL_free(state.output.data); - return SDL_SetError("Unexpected overflow in MS ADPCM decoder"); - } - - // Initialize decoder with the values from the block header. - result = MS_ADPCM_DecodeBlockHeader(&state); - if (!result) { - SDL_free(state.output.data); - return false; - } - - // Decode the block data. It stores the samples directly in the output. - result = MS_ADPCM_DecodeBlockData(&state); - if (!result) { - // Unexpected end. Stop decoding and return partial data if necessary. - if (file->trunchint == TruncVeryStrict || file->trunchint == TruncStrict) { - SDL_free(state.output.data); - return SDL_SetError("Truncated data chunk"); - } else if (file->trunchint != TruncDropFrame) { - state.output.pos -= state.output.pos % (state.samplesperblock * state.channels); - } - outputsize = state.output.pos * sizeof(Sint16); // Can't overflow, is always smaller. - break; - } - - state.input.pos += state.block.size; - bytesleft = state.input.size - state.input.pos; - } - - *audio_buf = (Uint8 *)state.output.data; - *audio_len = (Uint32)outputsize; - - return true; -} - -static bool IMA_ADPCM_CalculateSampleFrames(WaveFile *file, size_t datalength) -{ - WaveFormat *format = &file->format; - const size_t blockheadersize = (size_t)format->channels * 4; - const size_t subblockframesize = (size_t)format->channels * 4; - const size_t availableblocks = datalength / format->blockalign; - const size_t trailingdata = datalength % format->blockalign; - - if (file->trunchint == TruncVeryStrict || file->trunchint == TruncStrict) { - // The size of the data chunk must be a multiple of the block size. - if (datalength < blockheadersize || trailingdata > 0) { - return SDL_SetError("Truncated IMA ADPCM block"); - } - } - - // Calculate number of sample frames that will be decoded. - file->sampleframes = (Uint64)availableblocks * format->samplesperblock; - if (trailingdata > 0) { - // The last block is truncated. Check if we can get any samples out of it. - if (file->trunchint == TruncDropFrame && trailingdata > blockheadersize - 2) { - /* The sample frame in the header of the truncated block is present. - * Drop incomplete sample frames. - */ - size_t trailingsamples = 1; - - if (trailingdata > blockheadersize) { - // More data following after the header. - const size_t trailingblockdata = trailingdata - blockheadersize; - const size_t trailingsubblockdata = trailingblockdata % subblockframesize; - trailingsamples += (trailingblockdata / subblockframesize) * 8; - /* Due to the interleaved sub-blocks, the last 4 bytes determine - * how many samples of the truncated sub-block are lost. - */ - if (trailingsubblockdata > subblockframesize - 4) { - trailingsamples += (trailingsubblockdata % 4) * 2; - } - } - - if (trailingsamples > format->samplesperblock) { - trailingsamples = format->samplesperblock; - } - file->sampleframes += trailingsamples; - } - } - - file->sampleframes = WaveAdjustToFactValue(file, file->sampleframes); - if (file->sampleframes < 0) { - return false; - } - - return true; -} - -static bool IMA_ADPCM_Init(WaveFile *file, size_t datalength) -{ - WaveFormat *format = &file->format; - WaveChunk *chunk = &file->chunk; - const size_t blockheadersize = (size_t)format->channels * 4; - const size_t blockdatasize = (size_t)format->blockalign - blockheadersize; - const size_t blockframebitsize = (size_t)format->bitspersample * format->channels; - const size_t blockdatasamples = (blockdatasize * 8) / blockframebitsize; - - // Sanity checks. - - // IMA ADPCM can also have 3-bit samples, but it's not supported by SDL at this time. - if (format->bitspersample == 3) { - return SDL_SetError("3-bit IMA ADPCM currently not supported"); - } else if (format->bitspersample != 4) { - return SDL_SetError("Invalid IMA ADPCM bits per sample of %u", (unsigned int)format->bitspersample); - } - - /* The block size is required to be a multiple of 4 and it must be able to - * hold a block header. - */ - if (format->blockalign < blockheadersize || format->blockalign % 4) { - return SDL_SetError("Invalid IMA ADPCM block size (nBlockAlign)"); - } - - if (format->formattag == EXTENSIBLE_CODE) { - /* There's no specification for this, but it's basically the same - * format because the extensible header has wSampePerBlocks too. - */ - } else { - // The Standards Update says there 'should' be 2 bytes for wSamplesPerBlock. - if (chunk->size >= 20 && format->extsize >= 2) { - format->samplesperblock = chunk->data[18] | ((Uint16)chunk->data[19] << 8); - } - } - - if (format->samplesperblock == 0) { - /* Field zero? No problem. We just assume the encoder packed the block. - * The specification calculates it this way: - * - * x = Block size (in bits) minus header size (in bits) - * y = Bit depth multiplied by channel count - * z = Number of samples per channel in header - * wSamplesPerBlock = x / y + z - */ - format->samplesperblock = (Uint32)blockdatasamples + 1; - } - - /* nBlockAlign can be in conflict with wSamplesPerBlock. For example, if - * the number of samples doesn't fit into the block. The Standards Update - * also describes wSamplesPerBlock with a formula that makes it necessary - * to always fill the block with the maximum amount of samples, but this is - * not enforced here as there are no compatibility issues. - */ - if (blockdatasamples < format->samplesperblock - 1) { - return SDL_SetError("Invalid number of samples per IMA ADPCM block (wSamplesPerBlock)"); - } - - if (!IMA_ADPCM_CalculateSampleFrames(file, datalength)) { - return false; - } - - return true; -} - -static Sint16 IMA_ADPCM_ProcessNibble(Sint8 *cindex, Sint16 lastsample, Uint8 nybble) -{ - const Sint32 max_audioval = 32767; - const Sint32 min_audioval = -32768; - const Sint8 index_table_4b[16] = { - -1, -1, -1, -1, - 2, 4, 6, 8, - -1, -1, -1, -1, - 2, 4, 6, 8 - }; - const Uint16 step_table[89] = { - 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, - 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, - 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, - 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, - 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, - 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, - 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, - 22385, 24623, 27086, 29794, 32767 - }; - Uint32 step; - Sint32 sample, delta; - Sint8 index = *cindex; - - // Clamp index into valid range. - if (index > 88) { - index = 88; - } else if (index < 0) { - index = 0; - } - - // explicit cast to avoid gcc warning about using 'char' as array index - step = step_table[(size_t)index]; - - // Update index value - *cindex = index + index_table_4b[nybble]; - - /* This calculation uses shifts and additions because multiplications were - * much slower back then. Sadly, this can't just be replaced with an actual - * multiplication now as the old algorithm drops some bits. The closest - * approximation I could find is something like this: - * (nybble & 0x8 ? -1 : 1) * ((nybble & 0x7) * step / 4 + step / 8) - */ - delta = step >> 3; - if (nybble & 0x04) { - delta += step; - } - if (nybble & 0x02) { - delta += step >> 1; - } - if (nybble & 0x01) { - delta += step >> 2; - } - if (nybble & 0x08) { - delta = -delta; - } - - sample = lastsample + delta; - - // Clamp output sample - if (sample > max_audioval) { - sample = max_audioval; - } else if (sample < min_audioval) { - sample = min_audioval; - } - - return (Sint16)sample; -} - -static bool IMA_ADPCM_DecodeBlockHeader(ADPCM_DecoderState *state) -{ - Sint16 step; - Uint32 c; - Uint8 *cstate = (Uint8 *)state->cstate; - - for (c = 0; c < state->channels; c++) { - size_t o = state->block.pos + c * 4; - - // Extract the sample from the header. - Sint32 sample = state->block.data[o] | ((Sint32)state->block.data[o + 1] << 8); - if (sample >= 0x8000) { - sample -= 0x10000; - } - state->output.data[state->output.pos++] = (Sint16)sample; - - // Channel step index. - step = (Sint16)state->block.data[o + 2]; - cstate[c] = (Sint8)(step > 0x80 ? step - 0x100 : step); - - // Reserved byte in block header, should be 0. - if (state->block.data[o + 3] != 0) { - /* Uh oh, corrupt data? Buggy code? */; - } - } - - state->block.pos += state->blockheadersize; - - // Header provided one sample frame. - state->framesleft--; - - return true; -} - -/* Decodes the data of the IMA ADPCM block. Decoding will stop if a block is too - * short, returning with none or partially decoded data. The partial data always - * contains full sample frames (same sample count for each channel). - * Incomplete sample frames are discarded. - */ -static bool IMA_ADPCM_DecodeBlockData(ADPCM_DecoderState *state) -{ - size_t i; - const Uint32 channels = state->channels; - const size_t subblockframesize = (size_t)channels * 4; - Uint64 bytesrequired; - Uint32 c; - bool result = true; - - size_t blockpos = state->block.pos; - size_t blocksize = state->block.size; - size_t blockleft = blocksize - blockpos; - - size_t outpos = state->output.pos; - - Sint64 blockframesleft = state->samplesperblock - 1; - if (blockframesleft > state->framesleft) { - blockframesleft = state->framesleft; - } - - bytesrequired = (blockframesleft + 7) / 8 * subblockframesize; - if (blockleft < bytesrequired) { - // Data truncated. Calculate how many samples we can get out if it. - const size_t guaranteedframes = blockleft / subblockframesize; - const size_t remainingbytes = blockleft % subblockframesize; - blockframesleft = guaranteedframes; - if (remainingbytes > subblockframesize - 4) { - blockframesleft += (Sint64)(remainingbytes % 4) * 2; - } - // Signal the truncation. - result = false; - } - - /* Each channel has their nibbles packed into 32-bit blocks. These blocks - * are interleaved and make up the data part of the ADPCM block. This loop - * decodes the samples as they come from the input data and puts them at - * the appropriate places in the output data. - */ - while (blockframesleft > 0) { - const size_t subblocksamples = blockframesleft < 8 ? (size_t)blockframesleft : 8; - - for (c = 0; c < channels; c++) { - Uint8 nybble = 0; - // Load previous sample which may come from the block header. - Sint16 sample = state->output.data[outpos + c - channels]; - - for (i = 0; i < subblocksamples; i++) { - if (i & 1) { - nybble >>= 4; - } else { - nybble = state->block.data[blockpos++]; - } - - sample = IMA_ADPCM_ProcessNibble((Sint8 *)state->cstate + c, sample, nybble & 0x0f); - state->output.data[outpos + c + i * channels] = sample; - } - } - - outpos += channels * subblocksamples; - state->framesleft -= subblocksamples; - blockframesleft -= subblocksamples; - } - - state->block.pos = blockpos; - state->output.pos = outpos; - - return result; -} - -static bool IMA_ADPCM_Decode(WaveFile *file, Uint8 **audio_buf, Uint32 *audio_len) -{ - bool result; - size_t bytesleft, outputsize; - WaveChunk *chunk = &file->chunk; - ADPCM_DecoderState state; - Sint8 *cstate; - - if (chunk->size != chunk->length) { - // Could not read everything. Recalculate number of sample frames. - if (!IMA_ADPCM_CalculateSampleFrames(file, chunk->size)) { - return false; - } - } - - // Nothing to decode, nothing to return. - if (file->sampleframes == 0) { - *audio_buf = NULL; - *audio_len = 0; - return true; - } - - SDL_zero(state); - state.channels = file->format.channels; - state.blocksize = file->format.blockalign; - state.blockheadersize = (size_t)state.channels * 4; - state.samplesperblock = file->format.samplesperblock; - state.framesize = state.channels * sizeof(Sint16); - state.framestotal = file->sampleframes; - state.framesleft = state.framestotal; - - state.input.data = chunk->data; - state.input.size = chunk->size; - state.input.pos = 0; - - // The output size in bytes. May get modified if data is truncated. - outputsize = (size_t)state.framestotal; - if (SafeMult(&outputsize, state.framesize)) { - return SDL_SetError("WAVE file too big"); - } else if (outputsize > SDL_MAX_UINT32 || state.framestotal > SIZE_MAX) { - return SDL_SetError("WAVE file too big"); - } - - state.output.pos = 0; - state.output.size = outputsize / sizeof(Sint16); - state.output.data = (Sint16 *)SDL_malloc(outputsize); - if (!state.output.data) { - return false; - } - - cstate = (Sint8 *)SDL_calloc(state.channels, sizeof(Sint8)); - if (!cstate) { - SDL_free(state.output.data); - return false; - } - state.cstate = cstate; - - // Decode block by block. A truncated block will stop the decoding. - bytesleft = state.input.size - state.input.pos; - while (state.framesleft > 0 && bytesleft >= state.blockheadersize) { - state.block.data = state.input.data + state.input.pos; - state.block.size = bytesleft < state.blocksize ? bytesleft : state.blocksize; - state.block.pos = 0; - - if (state.output.size - state.output.pos < (Uint64)state.framesleft * state.channels) { - // Somehow didn't allocate enough space for the output. - SDL_free(state.output.data); - SDL_free(cstate); - return SDL_SetError("Unexpected overflow in IMA ADPCM decoder"); - } - - // Initialize decoder with the values from the block header. - result = IMA_ADPCM_DecodeBlockHeader(&state); - if (result) { - // Decode the block data. It stores the samples directly in the output. - result = IMA_ADPCM_DecodeBlockData(&state); - } - - if (!result) { - // Unexpected end. Stop decoding and return partial data if necessary. - if (file->trunchint == TruncVeryStrict || file->trunchint == TruncStrict) { - SDL_free(state.output.data); - SDL_free(cstate); - return SDL_SetError("Truncated data chunk"); - } else if (file->trunchint != TruncDropFrame) { - state.output.pos -= state.output.pos % (state.samplesperblock * state.channels); - } - outputsize = state.output.pos * sizeof(Sint16); // Can't overflow, is always smaller. - break; - } - - state.input.pos += state.block.size; - bytesleft = state.input.size - state.input.pos; - } - - *audio_buf = (Uint8 *)state.output.data; - *audio_len = (Uint32)outputsize; - - SDL_free(cstate); - - return true; -} - -static bool LAW_Init(WaveFile *file, size_t datalength) -{ - WaveFormat *format = &file->format; - - // Standards Update requires this to be 8. - if (format->bitspersample != 8) { - return SDL_SetError("Invalid companded bits per sample of %u", (unsigned int)format->bitspersample); - } - - // Not going to bother with weird padding. - if (format->blockalign != format->channels) { - return SDL_SetError("Unsupported block alignment"); - } - - if ((file->trunchint == TruncVeryStrict || file->trunchint == TruncStrict)) { - if (format->blockalign > 1 && datalength % format->blockalign) { - return SDL_SetError("Truncated data chunk in WAVE file"); - } - } - - file->sampleframes = WaveAdjustToFactValue(file, datalength / format->blockalign); - if (file->sampleframes < 0) { - return false; - } - - return true; -} - -static bool LAW_Decode(WaveFile *file, Uint8 **audio_buf, Uint32 *audio_len) -{ -#ifdef SDL_WAVE_LAW_LUT - const Sint16 alaw_lut[256] = { - -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, -2752, - -2624, -3008, -2880, -2240, -2112, -2496, -2368, -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, -22016, - -20992, -24064, -23040, -17920, -16896, -19968, -18944, -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136, -11008, - -10496, -12032, -11520, -8960, -8448, -9984, -9472, -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568, -344, - -328, -376, -360, -280, -264, -312, -296, -472, -456, -504, -488, -408, -392, -440, -424, -88, - -72, -120, -104, -24, -8, -56, -40, -216, -200, -248, -232, -152, -136, -184, -168, -1376, - -1312, -1504, -1440, -1120, -1056, -1248, -1184, -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, -688, - -656, -752, -720, -560, -528, -624, -592, -944, -912, -1008, -976, -816, -784, -880, -848, 5504, - 5248, 6016, 5760, 4480, 4224, 4992, 4736, 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, 2752, - 2624, 3008, 2880, 2240, 2112, 2496, 2368, 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, 22016, - 20992, 24064, 23040, 17920, 16896, 19968, 18944, 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, 11008, - 10496, 12032, 11520, 8960, 8448, 9984, 9472, 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, 344, - 328, 376, 360, 280, 264, 312, 296, 472, 456, 504, 488, 408, 392, 440, 424, 88, - 72, 120, 104, 24, 8, 56, 40, 216, 200, 248, 232, 152, 136, 184, 168, 1376, - 1312, 1504, 1440, 1120, 1056, 1248, 1184, 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, 688, - 656, 752, 720, 560, 528, 624, 592, 944, 912, 1008, 976, 816, 784, 880, 848 - }; - const Sint16 mulaw_lut[256] = { - -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, -15996, - -15484, -14972, -14460, -13948, -13436, -12924, -12412, -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, -7932, - -7676, -7420, -7164, -6908, -6652, -6396, -6140, -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, -3900, - -3772, -3644, -3516, -3388, -3260, -3132, -3004, -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, -1884, - -1820, -1756, -1692, -1628, -1564, -1500, -1436, -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, -876, - -844, -812, -780, -748, -716, -684, -652, -620, -588, -556, -524, -492, -460, -428, -396, -372, - -356, -340, -324, -308, -292, -276, -260, -244, -228, -212, -196, -180, -164, -148, -132, -120, - -112, -104, -96, -88, -80, -72, -64, -56, -48, -40, -32, -24, -16, -8, 0, 32124, - 31100, 30076, 29052, 28028, 27004, 25980, 24956, 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, 15996, - 15484, 14972, 14460, 13948, 13436, 12924, 12412, 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, 7932, - 7676, 7420, 7164, 6908, 6652, 6396, 6140, 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, 3900, - 3772, 3644, 3516, 3388, 3260, 3132, 3004, 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, 1884, - 1820, 1756, 1692, 1628, 1564, 1500, 1436, 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, 876, - 844, 812, 780, 748, 716, 684, 652, 620, 588, 556, 524, 492, 460, 428, 396, 372, - 356, 340, 324, 308, 292, 276, 260, 244, 228, 212, 196, 180, 164, 148, 132, 120, - 112, 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0 - }; -#endif - - WaveFormat *format = &file->format; - WaveChunk *chunk = &file->chunk; - size_t i, sample_count, expanded_len; - Uint8 *src; - Sint16 *dst; - - if (chunk->length != chunk->size) { - file->sampleframes = WaveAdjustToFactValue(file, chunk->size / format->blockalign); - if (file->sampleframes < 0) { - return false; - } - } - - // Nothing to decode, nothing to return. - if (file->sampleframes == 0) { - *audio_buf = NULL; - *audio_len = 0; - return true; - } - - sample_count = (size_t)file->sampleframes; - if (SafeMult(&sample_count, format->channels)) { - return SDL_SetError("WAVE file too big"); - } - - expanded_len = sample_count; - if (SafeMult(&expanded_len, sizeof(Sint16))) { - return SDL_SetError("WAVE file too big"); - } else if (expanded_len > SDL_MAX_UINT32 || file->sampleframes > SIZE_MAX) { - return SDL_SetError("WAVE file too big"); - } - - // 1 to avoid allocating zero bytes, to keep static analysis happy. - src = (Uint8 *)SDL_realloc(chunk->data, expanded_len ? expanded_len : 1); - if (!src) { - return false; - } - chunk->data = NULL; - chunk->size = 0; - - dst = (Sint16 *)src; - - /* Work backwards, since we're expanding in-place. `format` will - * inform the caller about the byte order. - */ - i = sample_count; - switch (file->format.encoding) { -#ifdef SDL_WAVE_LAW_LUT - case ALAW_CODE: - while (i--) { - dst[i] = alaw_lut[src[i]]; - } - break; - case MULAW_CODE: - while (i--) { - dst[i] = mulaw_lut[src[i]]; - } - break; -#else - case ALAW_CODE: - while (i--) { - Uint8 nibble = src[i]; - Uint8 exponent = (nibble & 0x7f) ^ 0x55; - Sint16 mantissa = exponent & 0xf; - - exponent >>= 4; - if (exponent > 0) { - mantissa |= 0x10; - } - mantissa = (mantissa << 4) | 0x8; - if (exponent > 1) { - mantissa <<= exponent - 1; - } - - dst[i] = nibble & 0x80 ? mantissa : -mantissa; - } - break; - case MULAW_CODE: - while (i--) { - Uint8 nibble = ~src[i]; - Sint16 mantissa = nibble & 0xf; - Uint8 exponent = (nibble >> 4) & 0x7; - Sint16 step = 4 << (exponent + 1); - - mantissa = (0x80 << exponent) + step * mantissa + step / 2 - 132; - - dst[i] = nibble & 0x80 ? -mantissa : mantissa; - } - break; -#endif - default: - SDL_free(src); - return SDL_SetError("Unknown companded encoding"); - } - - *audio_buf = src; - *audio_len = (Uint32)expanded_len; - - return true; -} - -static bool PCM_Init(WaveFile *file, size_t datalength) -{ - WaveFormat *format = &file->format; - - if (format->encoding == PCM_CODE) { - switch (format->bitspersample) { - case 8: - case 16: - case 24: - case 32: - // These are supported. - break; - default: - return SDL_SetError("%u-bit PCM format not supported", (unsigned int)format->bitspersample); - } - } else if (format->encoding == IEEE_FLOAT_CODE) { - if (format->bitspersample != 32) { - return SDL_SetError("%u-bit IEEE floating-point format not supported", (unsigned int)format->bitspersample); - } - } - - /* It wouldn't be that hard to support more exotic block sizes, but - * the most common formats should do for now. - */ - // Make sure we're a multiple of the blockalign, at least. - if ((format->channels * format->bitspersample) % (format->blockalign * 8)) { - return SDL_SetError("Unsupported block alignment"); - } - - if ((file->trunchint == TruncVeryStrict || file->trunchint == TruncStrict)) { - if (format->blockalign > 1 && datalength % format->blockalign) { - return SDL_SetError("Truncated data chunk in WAVE file"); - } - } - - file->sampleframes = WaveAdjustToFactValue(file, datalength / format->blockalign); - if (file->sampleframes < 0) { - return false; - } - - return true; -} - -static bool PCM_ConvertSint24ToSint32(WaveFile *file, Uint8 **audio_buf, Uint32 *audio_len) -{ - WaveFormat *format = &file->format; - WaveChunk *chunk = &file->chunk; - size_t i, expanded_len, sample_count; - Uint8 *ptr; - - sample_count = (size_t)file->sampleframes; - if (SafeMult(&sample_count, format->channels)) { - return SDL_SetError("WAVE file too big"); - } - - expanded_len = sample_count; - if (SafeMult(&expanded_len, sizeof(Sint32))) { - return SDL_SetError("WAVE file too big"); - } else if (expanded_len > SDL_MAX_UINT32 || file->sampleframes > SIZE_MAX) { - return SDL_SetError("WAVE file too big"); - } - - // 1 to avoid allocating zero bytes, to keep static analysis happy. - ptr = (Uint8 *)SDL_realloc(chunk->data, expanded_len ? expanded_len : 1); - if (!ptr) { - return false; - } - - // This pointer is now invalid. - chunk->data = NULL; - chunk->size = 0; - - *audio_buf = ptr; - *audio_len = (Uint32)expanded_len; - - // work from end to start, since we're expanding in-place. - for (i = sample_count; i > 0; i--) { - const size_t o = i - 1; - uint8_t b[4]; - - b[0] = 0; - b[1] = ptr[o * 3]; - b[2] = ptr[o * 3 + 1]; - b[3] = ptr[o * 3 + 2]; - - ptr[o * 4 + 0] = b[0]; - ptr[o * 4 + 1] = b[1]; - ptr[o * 4 + 2] = b[2]; - ptr[o * 4 + 3] = b[3]; - } - - return true; -} - -static bool PCM_Decode(WaveFile *file, Uint8 **audio_buf, Uint32 *audio_len) -{ - WaveFormat *format = &file->format; - WaveChunk *chunk = &file->chunk; - size_t outputsize; - - if (chunk->length != chunk->size) { - file->sampleframes = WaveAdjustToFactValue(file, chunk->size / format->blockalign); - if (file->sampleframes < 0) { - return false; - } - } - - // Nothing to decode, nothing to return. - if (file->sampleframes == 0) { - *audio_buf = NULL; - *audio_len = 0; - return true; - } - - // 24-bit samples get shifted to 32 bits. - if (format->encoding == PCM_CODE && format->bitspersample == 24) { - return PCM_ConvertSint24ToSint32(file, audio_buf, audio_len); - } - - outputsize = (size_t)file->sampleframes; - if (SafeMult(&outputsize, format->blockalign)) { - return SDL_SetError("WAVE file too big"); - } else if (outputsize > SDL_MAX_UINT32 || file->sampleframes > SIZE_MAX) { - return SDL_SetError("WAVE file too big"); - } - - *audio_buf = chunk->data; - *audio_len = (Uint32)outputsize; - - // This pointer is going to be returned to the caller. Prevent free in cleanup. - chunk->data = NULL; - chunk->size = 0; - - return true; -} - -static WaveRiffSizeHint WaveGetRiffSizeHint(void) -{ - const char *hint = SDL_GetHint(SDL_HINT_WAVE_RIFF_CHUNK_SIZE); - - if (hint) { - if (SDL_strcmp(hint, "force") == 0) { - return RiffSizeForce; - } else if (SDL_strcmp(hint, "ignore") == 0) { - return RiffSizeIgnore; - } else if (SDL_strcmp(hint, "ignorezero") == 0) { - return RiffSizeIgnoreZero; - } else if (SDL_strcmp(hint, "maximum") == 0) { - return RiffSizeMaximum; - } - } - - return RiffSizeNoHint; -} - -static WaveTruncationHint WaveGetTruncationHint(void) -{ - const char *hint = SDL_GetHint(SDL_HINT_WAVE_TRUNCATION); - - if (hint) { - if (SDL_strcmp(hint, "verystrict") == 0) { - return TruncVeryStrict; - } else if (SDL_strcmp(hint, "strict") == 0) { - return TruncStrict; - } else if (SDL_strcmp(hint, "dropframe") == 0) { - return TruncDropFrame; - } else if (SDL_strcmp(hint, "dropblock") == 0) { - return TruncDropBlock; - } - } - - return TruncNoHint; -} - -static WaveFactChunkHint WaveGetFactChunkHint(void) -{ - const char *hint = SDL_GetHint(SDL_HINT_WAVE_FACT_CHUNK); - - if (hint) { - if (SDL_strcmp(hint, "truncate") == 0) { - return FactTruncate; - } else if (SDL_strcmp(hint, "strict") == 0) { - return FactStrict; - } else if (SDL_strcmp(hint, "ignorezero") == 0) { - return FactIgnoreZero; - } else if (SDL_strcmp(hint, "ignore") == 0) { - return FactIgnore; - } - } - - return FactNoHint; -} - -static void WaveFreeChunkData(WaveChunk *chunk) -{ - if (chunk->data) { - SDL_free(chunk->data); - chunk->data = NULL; - } - chunk->size = 0; -} - -static int WaveNextChunk(SDL_IOStream *src, WaveChunk *chunk) -{ - Uint32 chunkheader[2]; - Sint64 nextposition = chunk->position + chunk->length; - - // Data is no longer valid after this function returns. - WaveFreeChunkData(chunk); - - // Error on overflows. - if (SDL_MAX_SINT64 - chunk->length < chunk->position || SDL_MAX_SINT64 - 8 < nextposition) { - return -1; - } - - // RIFF chunks have a 2-byte alignment. Skip padding byte. - if (chunk->length & 1) { - nextposition++; - } - - if (SDL_SeekIO(src, nextposition, SDL_IO_SEEK_SET) != nextposition) { - // Not sure how we ended up here. Just abort. - return -2; - } else if (SDL_ReadIO(src, chunkheader, sizeof(Uint32) * 2) != (sizeof(Uint32) * 2)) { - return -1; - } - - chunk->fourcc = SDL_Swap32LE(chunkheader[0]); - chunk->length = SDL_Swap32LE(chunkheader[1]); - chunk->position = nextposition + 8; - - return 0; -} - -static int WaveReadPartialChunkData(SDL_IOStream *src, WaveChunk *chunk, size_t length) -{ - WaveFreeChunkData(chunk); - - if (length > chunk->length) { - length = chunk->length; - } - - if (length > 0) { - chunk->data = (Uint8 *)SDL_malloc(length); - if (!chunk->data) { - return -1; - } - - if (SDL_SeekIO(src, chunk->position, SDL_IO_SEEK_SET) != chunk->position) { - // Not sure how we ended up here. Just abort. - return -2; - } - - chunk->size = SDL_ReadIO(src, chunk->data, length); - if (chunk->size != length) { - // Expected to be handled by the caller. - } - } - - return 0; -} - -static int WaveReadChunkData(SDL_IOStream *src, WaveChunk *chunk) -{ - return WaveReadPartialChunkData(src, chunk, chunk->length); -} - -typedef struct WaveExtensibleGUID -{ - Uint16 encoding; - Uint8 guid[16]; -} WaveExtensibleGUID; - -// Some of the GUIDs that are used by WAVEFORMATEXTENSIBLE. -#define WAVE_FORMATTAG_GUID(tag) \ - { \ - (tag) & 0xff, (tag) >> 8, 0, 0, 0, 0, 16, 0, 128, 0, 0, 170, 0, 56, 155, 113 \ - } -static WaveExtensibleGUID extensible_guids[] = { - { PCM_CODE, WAVE_FORMATTAG_GUID(PCM_CODE) }, - { MS_ADPCM_CODE, WAVE_FORMATTAG_GUID(MS_ADPCM_CODE) }, - { IEEE_FLOAT_CODE, WAVE_FORMATTAG_GUID(IEEE_FLOAT_CODE) }, - { ALAW_CODE, WAVE_FORMATTAG_GUID(ALAW_CODE) }, - { MULAW_CODE, WAVE_FORMATTAG_GUID(MULAW_CODE) }, - { IMA_ADPCM_CODE, WAVE_FORMATTAG_GUID(IMA_ADPCM_CODE) } -}; - -static Uint16 WaveGetFormatGUIDEncoding(WaveFormat *format) -{ - size_t i; - for (i = 0; i < SDL_arraysize(extensible_guids); i++) { - if (SDL_memcmp(format->subformat, extensible_guids[i].guid, 16) == 0) { - return extensible_guids[i].encoding; - } - } - return UNKNOWN_CODE; -} - -static bool WaveReadFormat(WaveFile *file) -{ - WaveChunk *chunk = &file->chunk; - WaveFormat *format = &file->format; - SDL_IOStream *fmtsrc; - size_t fmtlen = chunk->size; - - if (fmtlen > SDL_MAX_SINT32) { - // Limit given by SDL_IOFromConstMem. - return SDL_SetError("Data of WAVE fmt chunk too big"); - } - fmtsrc = SDL_IOFromConstMem(chunk->data, (int)chunk->size); - if (!fmtsrc) { - return false; - } - - if (!SDL_ReadU16LE(fmtsrc, &format->formattag) || - !SDL_ReadU16LE(fmtsrc, &format->channels) || - !SDL_ReadU32LE(fmtsrc, &format->frequency) || - !SDL_ReadU32LE(fmtsrc, &format->byterate) || - !SDL_ReadU16LE(fmtsrc, &format->blockalign)) { - return false; - } - format->encoding = format->formattag; - - // This is PCM specific in the first version of the specification. - if (fmtlen >= 16) { - if (!SDL_ReadU16LE(fmtsrc, &format->bitspersample)) { - return false; - } - } else if (format->encoding == PCM_CODE) { - SDL_CloseIO(fmtsrc); - return SDL_SetError("Missing wBitsPerSample field in WAVE fmt chunk"); - } - - // The earlier versions also don't have this field. - if (fmtlen >= 18) { - if (!SDL_ReadU16LE(fmtsrc, &format->extsize)) { - return false; - } - } - - if (format->formattag == EXTENSIBLE_CODE) { - /* note that this ignores channel masks, smaller valid bit counts - * inside a larger container, and most subtypes. This is just enough - * to get things that didn't really _need_ WAVE_FORMAT_EXTENSIBLE - * to be useful working when they use this format flag. - */ - - // Extensible header must be at least 22 bytes. - if (fmtlen < 40 || format->extsize < 22) { - SDL_CloseIO(fmtsrc); - return SDL_SetError("Extensible WAVE header too small"); - } - - if (!SDL_ReadU16LE(fmtsrc, &format->validsamplebits) || - !SDL_ReadU32LE(fmtsrc, &format->channelmask) || - SDL_ReadIO(fmtsrc, format->subformat, 16) != 16) { - } - format->samplesperblock = format->validsamplebits; - format->encoding = WaveGetFormatGUIDEncoding(format); - } - - SDL_CloseIO(fmtsrc); - - return true; -} - -static bool WaveCheckFormat(WaveFile *file, size_t datalength) -{ - WaveFormat *format = &file->format; - - // Check for some obvious issues. - - if (format->channels == 0) { - return SDL_SetError("Invalid number of channels"); - } - - if (format->frequency == 0) { - return SDL_SetError("Invalid sample rate"); - } else if (format->frequency > INT_MAX) { - return SDL_SetError("Sample rate exceeds limit of %d", INT_MAX); - } - - // Reject invalid fact chunks in strict mode. - if (file->facthint == FactStrict && file->fact.status == -1) { - return SDL_SetError("Invalid fact chunk in WAVE file"); - } - - /* Check for issues common to all encodings. Some unsupported formats set - * the bits per sample to zero. These fall through to the 'unsupported - * format' error. - */ - switch (format->encoding) { - case IEEE_FLOAT_CODE: - case ALAW_CODE: - case MULAW_CODE: - case MS_ADPCM_CODE: - case IMA_ADPCM_CODE: - // These formats require a fact chunk. - if (file->facthint == FactStrict && file->fact.status <= 0) { - return SDL_SetError("Missing fact chunk in WAVE file"); - } - SDL_FALLTHROUGH; - case PCM_CODE: - // All supported formats require a non-zero bit depth. - if (file->chunk.size < 16) { - return SDL_SetError("Missing wBitsPerSample field in WAVE fmt chunk"); - } else if (format->bitspersample == 0) { - return SDL_SetError("Invalid bits per sample"); - } - - // All supported formats must have a proper block size. - if (format->blockalign == 0) { - format->blockalign = 1; // force it to 1 if it was unset. - } - - /* If the fact chunk is valid and the appropriate hint is set, the - * decoders will use the number of sample frames from the fact chunk. - */ - if (file->fact.status == 1) { - WaveFactChunkHint hint = file->facthint; - Uint32 samples = file->fact.samplelength; - if (hint == FactTruncate || hint == FactStrict || (hint == FactIgnoreZero && samples > 0)) { - file->fact.status = 2; - } - } - } - - // Check the format for encoding specific issues and initialize decoders. - switch (format->encoding) { - case PCM_CODE: - case IEEE_FLOAT_CODE: - if (!PCM_Init(file, datalength)) { - return false; - } - break; - case ALAW_CODE: - case MULAW_CODE: - if (!LAW_Init(file, datalength)) { - return false; - } - break; - case MS_ADPCM_CODE: - if (!MS_ADPCM_Init(file, datalength)) { - return false; - } - break; - case IMA_ADPCM_CODE: - if (!IMA_ADPCM_Init(file, datalength)) { - return false; - } - break; - case MPEG_CODE: - case MPEGLAYER3_CODE: - return SDL_SetError("MPEG formats not supported"); - default: - if (format->formattag == EXTENSIBLE_CODE) { - const char *errstr = "Unknown WAVE format GUID: %08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x"; - const Uint8 *g = format->subformat; - const Uint32 g1 = g[0] | ((Uint32)g[1] << 8) | ((Uint32)g[2] << 16) | ((Uint32)g[3] << 24); - const Uint32 g2 = g[4] | ((Uint32)g[5] << 8); - const Uint32 g3 = g[6] | ((Uint32)g[7] << 8); - return SDL_SetError(errstr, g1, g2, g3, g[8], g[9], g[10], g[11], g[12], g[13], g[14], g[15]); - } - return SDL_SetError("Unknown WAVE format tag: 0x%04x", (unsigned int)format->encoding); - } - - return true; -} - -static bool WaveLoad(SDL_IOStream *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) -{ - int result; - Uint32 chunkcount = 0; - Uint32 chunkcountlimit = 10000; - const char *hint; - Sint64 RIFFstart, RIFFend, lastchunkpos; - bool RIFFlengthknown = false; - WaveFormat *format = &file->format; - WaveChunk *chunk = &file->chunk; - WaveChunk RIFFchunk; - WaveChunk fmtchunk; - WaveChunk datachunk; - - SDL_zero(RIFFchunk); - SDL_zero(fmtchunk); - SDL_zero(datachunk); - - hint = SDL_GetHint(SDL_HINT_WAVE_CHUNK_LIMIT); - if (hint) { - unsigned int count; - if (SDL_sscanf(hint, "%u", &count) == 1) { - chunkcountlimit = count <= SDL_MAX_UINT32 ? count : SDL_MAX_UINT32; - } - } - - RIFFstart = SDL_TellIO(src); - if (RIFFstart < 0) { - return SDL_SetError("Could not seek in file"); - } - - RIFFchunk.position = RIFFstart; - if (WaveNextChunk(src, &RIFFchunk) < 0) { - return SDL_SetError("Could not read RIFF header"); - } - - // Check main WAVE file identifiers. - if (RIFFchunk.fourcc == RIFF) { - Uint32 formtype; - // Read the form type. "WAVE" expected. - if (!SDL_ReadU32LE(src, &formtype)) { - return SDL_SetError("Could not read RIFF form type"); - } else if (formtype != WAVE) { - return SDL_SetError("RIFF form type is not WAVE (not a Waveform file)"); - } - } else if (RIFFchunk.fourcc == WAVE) { - // RIFF chunk missing or skipped. Length unknown. - RIFFchunk.position = 0; - RIFFchunk.length = 0; - } else { - return SDL_SetError("Could not find RIFF or WAVE identifiers (not a Waveform file)"); - } - - // The 4-byte form type is immediately followed by the first chunk. - chunk->position = RIFFchunk.position + 4; - - /* Use the RIFF chunk size to limit the search for the chunks. This is not - * always reliable and the hint can be used to tune the behavior. By - * default, it will never search past 4 GiB. - */ - switch (file->riffhint) { - case RiffSizeIgnore: - RIFFend = RIFFchunk.position + SDL_MAX_UINT32; - break; - default: - case RiffSizeIgnoreZero: - if (RIFFchunk.length == 0) { - RIFFend = RIFFchunk.position + SDL_MAX_UINT32; - break; - } - SDL_FALLTHROUGH; - case RiffSizeForce: - RIFFend = RIFFchunk.position + RIFFchunk.length; - RIFFlengthknown = true; - break; - case RiffSizeMaximum: - RIFFend = SDL_MAX_SINT64; - break; - } - - /* Step through all chunks and save information on the fmt, data, and fact - * chunks. Ignore the chunks we don't know as per specification. This - * currently also ignores cue, list, and slnt chunks. - */ - while ((Uint64)RIFFend > (Uint64)chunk->position + chunk->length + (chunk->length & 1)) { - // Abort after too many chunks or else corrupt files may waste time. - if (chunkcount++ >= chunkcountlimit) { - return SDL_SetError("Chunk count in WAVE file exceeds limit of %" SDL_PRIu32, chunkcountlimit); - } - - result = WaveNextChunk(src, chunk); - if (result < 0) { - // Unexpected EOF. Corrupt file or I/O issues. - if (file->trunchint == TruncVeryStrict) { - return SDL_SetError("Unexpected end of WAVE file"); - } - // Let the checks after this loop sort this issue out. - break; - } else if (result == -2) { - return SDL_SetError("Could not seek to WAVE chunk header"); - } - - if (chunk->fourcc == FMT) { - if (fmtchunk.fourcc == FMT) { - // Multiple fmt chunks. Ignore or error? - } else { - // The fmt chunk must occur before the data chunk. - if (datachunk.fourcc == DATA) { - return SDL_SetError("fmt chunk after data chunk in WAVE file"); - } - fmtchunk = *chunk; - } - } else if (chunk->fourcc == DATA) { - /* Only use the first data chunk. Handling the wavl list madness - * may require a different approach. - */ - if (datachunk.fourcc != DATA) { - datachunk = *chunk; - } - } else if (chunk->fourcc == FACT) { - /* The fact chunk data must be at least 4 bytes for the - * dwSampleLength field. Ignore all fact chunks after the first one. - */ - if (file->fact.status == 0) { - if (chunk->length < 4) { - file->fact.status = -1; - } else { - // Let's use src directly, it's just too convenient. - Sint64 position = SDL_SeekIO(src, chunk->position, SDL_IO_SEEK_SET); - if (position == chunk->position && SDL_ReadU32LE(src, &file->fact.samplelength)) { - file->fact.status = 1; - } else { - file->fact.status = -1; - } - } - } - } - - /* Go through all chunks in verystrict mode or stop the search early if - * all required chunks were found. - */ - if (file->trunchint == TruncVeryStrict) { - if ((Uint64)RIFFend < (Uint64)chunk->position + chunk->length) { - return SDL_SetError("RIFF size truncates chunk"); - } - } else if (fmtchunk.fourcc == FMT && datachunk.fourcc == DATA) { - if (file->fact.status == 1 || file->facthint == FactIgnore || file->facthint == FactNoHint) { - break; - } - } - } - - /* Save the position after the last chunk. This position will be used if the - * RIFF length is unknown. - */ - lastchunkpos = chunk->position + chunk->length; - - // The fmt chunk is mandatory. - if (fmtchunk.fourcc != FMT) { - return SDL_SetError("Missing fmt chunk in WAVE file"); - } - // A data chunk must be present. - if (datachunk.fourcc != DATA) { - return SDL_SetError("Missing data chunk in WAVE file"); - } - // Check if the last chunk has all of its data in verystrict mode. - if (file->trunchint == TruncVeryStrict) { - // data chunk is handled later. - if (chunk->fourcc != DATA && chunk->length > 0) { - Uint8 tmp; - Uint64 position = (Uint64)chunk->position + chunk->length - 1; - if (position > SDL_MAX_SINT64 || SDL_SeekIO(src, (Sint64)position, SDL_IO_SEEK_SET) != (Sint64)position) { - return SDL_SetError("Could not seek to WAVE chunk data"); - } else if (!SDL_ReadU8(src, &tmp)) { - return SDL_SetError("RIFF size truncates chunk"); - } - } - } - - // Process fmt chunk. - *chunk = fmtchunk; - - /* No need to read more than 1046 bytes of the fmt chunk data with the - * formats that are currently supported. (1046 because of MS ADPCM coefficients) - */ - if (WaveReadPartialChunkData(src, chunk, 1046) < 0) { - return SDL_SetError("Could not read data of WAVE fmt chunk"); - } - - /* The fmt chunk data must be at least 14 bytes to include all common fields. - * It usually is 16 and larger depending on the header and encoding. - */ - if (chunk->length < 14) { - return SDL_SetError("Invalid WAVE fmt chunk length (too small)"); - } else if (chunk->size < 14) { - return SDL_SetError("Could not read data of WAVE fmt chunk"); - } else if (!WaveReadFormat(file)) { - return false; - } else if (!WaveCheckFormat(file, (size_t)datachunk.length)) { - return false; - } - -#ifdef SDL_WAVE_DEBUG_LOG_FORMAT - WaveDebugLogFormat(file); -#endif -#ifdef SDL_WAVE_DEBUG_DUMP_FORMAT - WaveDebugDumpFormat(file, RIFFchunk.length, fmtchunk.length, datachunk.length); -#endif - - WaveFreeChunkData(chunk); - - // Process data chunk. - *chunk = datachunk; - - if (chunk->length > 0) { - result = WaveReadChunkData(src, chunk); - if (result < 0) { - return false; - } else if (result == -2) { - return SDL_SetError("Could not seek data of WAVE data chunk"); - } - } - - if (chunk->length != chunk->size) { - // I/O issues or corrupt file. - if (file->trunchint == TruncVeryStrict || file->trunchint == TruncStrict) { - return SDL_SetError("Could not read data of WAVE data chunk"); - } - // The decoders handle this truncation. - } - - // Decode or convert the data if necessary. - switch (format->encoding) { - case PCM_CODE: - case IEEE_FLOAT_CODE: - if (!PCM_Decode(file, audio_buf, audio_len)) { - return false; - } - break; - case ALAW_CODE: - case MULAW_CODE: - if (!LAW_Decode(file, audio_buf, audio_len)) { - return false; - } - break; - case MS_ADPCM_CODE: - if (!MS_ADPCM_Decode(file, audio_buf, audio_len)) { - return false; - } - break; - case IMA_ADPCM_CODE: - if (!IMA_ADPCM_Decode(file, audio_buf, audio_len)) { - return false; - } - break; - } - - /* Setting up the specs. All unsupported formats were filtered out - * by checks earlier in this function. - */ - spec->freq = format->frequency; - spec->channels = (Uint8)format->channels; - spec->format = SDL_AUDIO_UNKNOWN; - - switch (format->encoding) { - case MS_ADPCM_CODE: - case IMA_ADPCM_CODE: - case ALAW_CODE: - case MULAW_CODE: - // These can be easily stored in the byte order of the system. - spec->format = SDL_AUDIO_S16; - break; - case IEEE_FLOAT_CODE: - spec->format = SDL_AUDIO_F32LE; - break; - case PCM_CODE: - switch (format->bitspersample) { - case 8: - spec->format = SDL_AUDIO_U8; - break; - case 16: - spec->format = SDL_AUDIO_S16LE; - break; - case 24: // Has been shifted to 32 bits. - case 32: - spec->format = SDL_AUDIO_S32LE; - break; - default: - // Just in case something unexpected happened in the checks. - return SDL_SetError("Unexpected %u-bit PCM data format", (unsigned int)format->bitspersample); - } - break; - default: - return SDL_SetError("Unexpected data format"); - } - - // Report the end position back to the cleanup code. - if (RIFFlengthknown) { - chunk->position = RIFFend; - } else { - chunk->position = lastchunkpos; - } - - return true; -} - -bool SDL_LoadWAV_IO(SDL_IOStream *src, bool closeio, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) -{ - bool result = false; - WaveFile file; - - if (spec) { - SDL_zerop(spec); - } - if (audio_buf) { - *audio_buf = NULL; - } - if (audio_len) { - *audio_len = 0; - } - - // Make sure we are passed a valid data source - if (!src) { - SDL_InvalidParamError("src"); - goto done; - } else if (!spec) { - SDL_InvalidParamError("spec"); - goto done; - } else if (!audio_buf) { - SDL_InvalidParamError("audio_buf"); - goto done; - } else if (!audio_len) { - SDL_InvalidParamError("audio_len"); - goto done; - } - - SDL_zero(file); - file.riffhint = WaveGetRiffSizeHint(); - file.trunchint = WaveGetTruncationHint(); - file.facthint = WaveGetFactChunkHint(); - - result = WaveLoad(src, &file, spec, audio_buf, audio_len); - if (!result) { - SDL_free(*audio_buf); - audio_buf = NULL; - audio_len = 0; - } - - // Cleanup - if (!closeio) { - SDL_SeekIO(src, file.chunk.position, SDL_IO_SEEK_SET); - } - WaveFreeChunkData(&file.chunk); - SDL_free(file.decoderdata); -done: - if (closeio && src) { - SDL_CloseIO(src); - } - return result; -} - -bool SDL_LoadWAV(const char *path, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) -{ - SDL_IOStream *stream = SDL_IOFromFile(path, "rb"); - if (!stream) { - if (spec) { - SDL_zerop(spec); - } - if (audio_buf) { - *audio_buf = NULL; - } - if (audio_len) { - *audio_len = 0; - } - return false; - } - return SDL_LoadWAV_IO(stream, true, spec, audio_buf, audio_len); -} - diff --git a/contrib/SDL-3.2.8/src/audio/SDL_wave.h b/contrib/SDL-3.2.8/src/audio/SDL_wave.h deleted file mode 100644 index 0ec8965..0000000 --- a/contrib/SDL-3.2.8/src/audio/SDL_wave.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -// RIFF WAVE files are little-endian - -/*******************************************/ -// Define values for Microsoft WAVE format -/*******************************************/ -// FOURCC -#define RIFF 0x46464952 // "RIFF" -#define WAVE 0x45564157 // "WAVE" -#define FACT 0x74636166 // "fact" -#define LIST 0x5453494c // "LIST" -#define BEXT 0x74786562 // "bext" -#define JUNK 0x4B4E554A // "JUNK" -#define FMT 0x20746D66 // "fmt " -#define DATA 0x61746164 // "data" -// Format tags -#define UNKNOWN_CODE 0x0000 -#define PCM_CODE 0x0001 -#define MS_ADPCM_CODE 0x0002 -#define IEEE_FLOAT_CODE 0x0003 -#define ALAW_CODE 0x0006 -#define MULAW_CODE 0x0007 -#define IMA_ADPCM_CODE 0x0011 -#define MPEG_CODE 0x0050 -#define MPEGLAYER3_CODE 0x0055 -#define EXTENSIBLE_CODE 0xFFFE - -// Stores the WAVE format information. -typedef struct WaveFormat -{ - Uint16 formattag; // Raw value of the first field in the fmt chunk data. - Uint16 encoding; // Actual encoding, possibly from the extensible header. - Uint16 channels; // Number of channels. - Uint32 frequency; // Sampling rate in Hz. - Uint32 byterate; // Average bytes per second. - Uint16 blockalign; // Bytes per block. - Uint16 bitspersample; // Currently supported are 8, 16, 24, 32, and 4 for ADPCM. - - /* Extra information size. Number of extra bytes starting at byte 18 in the - * fmt chunk data. This is at least 22 for the extensible header. - */ - Uint16 extsize; - - // Extensible WAVE header fields - Uint16 validsamplebits; - Uint32 samplesperblock; // For compressed formats. Can be zero. Actually 16 bits in the header. - Uint32 channelmask; - Uint8 subformat[16]; // A format GUID. -} WaveFormat; - -// Stores information on the fact chunk. -typedef struct WaveFact -{ - /* Represents the state of the fact chunk in the WAVE file. - * Set to -1 if the fact chunk is invalid. - * Set to 0 if the fact chunk is not present - * Set to 1 if the fact chunk is present and valid. - * Set to 2 if samplelength is going to be used as the number of sample frames. - */ - Sint32 status; - - /* Version 1 of the RIFF specification calls the field in the fact chunk - * dwFileSize. The Standards Update then calls it dwSampleLength and specifies - * that it is 'the length of the data in samples'. WAVE files from Windows - * with this chunk have it set to the samples per channel (sample frames). - * This is useful to truncate compressed audio to a specific sample count - * because a compressed block is usually decoded to a fixed number of - * sample frames. - */ - Uint32 samplelength; // Raw sample length value from the fact chunk. -} WaveFact; - -// Generic struct for the chunks in the WAVE file. -typedef struct WaveChunk -{ - Uint32 fourcc; // FOURCC of the chunk. - Uint32 length; // Size of the chunk data. - Sint64 position; // Position of the data in the stream. - Uint8 *data; // When allocated, this points to the chunk data. length is used for the memory allocation size. - size_t size; // Number of bytes in data that could be read from the stream. Can be smaller than length. -} WaveChunk; - -// Controls how the size of the RIFF chunk affects the loading of a WAVE file. -typedef enum WaveRiffSizeHint -{ - RiffSizeNoHint, - RiffSizeForce, - RiffSizeIgnoreZero, - RiffSizeIgnore, - RiffSizeMaximum -} WaveRiffSizeHint; - -// Controls how a truncated WAVE file is handled. -typedef enum WaveTruncationHint -{ - TruncNoHint, - TruncVeryStrict, - TruncStrict, - TruncDropFrame, - TruncDropBlock -} WaveTruncationHint; - -// Controls how the fact chunk affects the loading of a WAVE file. -typedef enum WaveFactChunkHint -{ - FactNoHint, - FactTruncate, - FactStrict, - FactIgnoreZero, - FactIgnore -} WaveFactChunkHint; - -typedef struct WaveFile -{ - WaveChunk chunk; - WaveFormat format; - WaveFact fact; - - /* Number of sample frames that will be decoded. Calculated either with the - * size of the data chunk or, if the appropriate hint is enabled, with the - * sample length value from the fact chunk. - */ - Sint64 sampleframes; - - void *decoderdata; // Some decoders require extra data for a state. - - WaveRiffSizeHint riffhint; - WaveTruncationHint trunchint; - WaveFactChunkHint facthint; -} WaveFile; diff --git a/contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.c b/contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.c deleted file mode 100644 index 3360bec..0000000 --- a/contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.c +++ /dev/null @@ -1,551 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_AAUDIO - -#include "../SDL_sysaudio.h" -#include "SDL_aaudio.h" - -#include "../../core/android/SDL_android.h" -#include - -#if __ANDROID_API__ < 31 -#define AAUDIO_FORMAT_PCM_I32 4 -#endif - -struct SDL_PrivateAudioData -{ - AAudioStream *stream; - int num_buffers; - Uint8 *mixbuf; // Raw mixing buffer - size_t mixbuf_bytes; // num_buffers * device->buffer_size - size_t callback_bytes; - size_t processed_bytes; - SDL_Semaphore *semaphore; - SDL_AtomicInt error_callback_triggered; -}; - -// Debug -#if 0 -#define LOGI(...) SDL_Log(__VA_ARGS__); -#else -#define LOGI(...) -#endif - -#define LIB_AAUDIO_SO "libaaudio.so" - -typedef struct AAUDIO_Data -{ - SDL_SharedObject *handle; -#define SDL_PROC(ret, func, params) ret (*func) params; -#include "SDL_aaudiofuncs.h" -} AAUDIO_Data; -static AAUDIO_Data ctx; - -static bool AAUDIO_LoadFunctions(AAUDIO_Data *data) -{ -#define SDL_PROC(ret, func, params) \ - do { \ - data->func = (ret (*) params)SDL_LoadFunction(data->handle, #func); \ - if (!data->func) { \ - return SDL_SetError("Couldn't load AAUDIO function %s: %s", #func, SDL_GetError()); \ - } \ - } while (0); -#include "SDL_aaudiofuncs.h" - return true; -} - - -static void AAUDIO_errorCallback(AAudioStream *stream, void *userData, aaudio_result_t error) -{ - LOGI("SDL AAUDIO_errorCallback: %d - %s", error, ctx.AAudio_convertResultToText(error)); - - // You MUST NOT close the audio stream from this callback, so we cannot call SDL_AudioDeviceDisconnected here. - // Just flag the device so we can kill it in PlayDevice instead. - SDL_AudioDevice *device = (SDL_AudioDevice *) userData; - SDL_SetAtomicInt(&device->hidden->error_callback_triggered, (int) error); // AAUDIO_OK is zero, so !triggered means no error. - SDL_SignalSemaphore(device->hidden->semaphore); // in case we're blocking in WaitDevice. -} - -static aaudio_data_callback_result_t AAUDIO_dataCallback(AAudioStream *stream, void *userData, void *audioData, int32_t numFrames) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *) userData; - struct SDL_PrivateAudioData *hidden = device->hidden; - size_t framesize = SDL_AUDIO_FRAMESIZE(device->spec); - size_t callback_bytes = numFrames * framesize; - size_t old_buffer_index = hidden->callback_bytes / device->buffer_size; - - if (device->recording) { - const Uint8 *input = (const Uint8 *)audioData; - size_t available_bytes = hidden->mixbuf_bytes - (hidden->callback_bytes - hidden->processed_bytes); - size_t size = SDL_min(available_bytes, callback_bytes); - size_t offset = hidden->callback_bytes % hidden->mixbuf_bytes; - size_t end = (offset + size) % hidden->mixbuf_bytes; - SDL_assert(size <= hidden->mixbuf_bytes); - -//LOGI("Recorded %zu frames, %zu available, %zu max (%zu written, %zu read)", callback_bytes / framesize, available_bytes / framesize, hidden->mixbuf_bytes / framesize, hidden->callback_bytes / framesize, hidden->processed_bytes / framesize); - - if (offset <= end) { - SDL_memcpy(&hidden->mixbuf[offset], input, size); - } else { - size_t partial = (hidden->mixbuf_bytes - offset); - SDL_memcpy(&hidden->mixbuf[offset], &input[0], partial); - SDL_memcpy(&hidden->mixbuf[0], &input[partial], end); - } - - SDL_MemoryBarrierRelease(); - hidden->callback_bytes += size; - - if (size < callback_bytes) { - LOGI("Audio recording overflow, dropped %zu frames", (callback_bytes - size) / framesize); - } - } else { - Uint8 *output = (Uint8 *)audioData; - size_t available_bytes = (hidden->processed_bytes - hidden->callback_bytes); - size_t size = SDL_min(available_bytes, callback_bytes); - size_t offset = hidden->callback_bytes % hidden->mixbuf_bytes; - size_t end = (offset + size) % hidden->mixbuf_bytes; - SDL_assert(size <= hidden->mixbuf_bytes); - -//LOGI("Playing %zu frames, %zu available, %zu max (%zu written, %zu read)", callback_bytes / framesize, available_bytes / framesize, hidden->mixbuf_bytes / framesize, hidden->processed_bytes / framesize, hidden->callback_bytes / framesize); - - SDL_MemoryBarrierAcquire(); - if (offset <= end) { - SDL_memcpy(output, &hidden->mixbuf[offset], size); - } else { - size_t partial = (hidden->mixbuf_bytes - offset); - SDL_memcpy(&output[0], &hidden->mixbuf[offset], partial); - SDL_memcpy(&output[partial], &hidden->mixbuf[0], end); - } - hidden->callback_bytes += size; - - if (size < callback_bytes) { - LOGI("Audio playback underflow, missed %zu frames", (callback_bytes - size) / framesize); - SDL_memset(&output[size], device->silence_value, (callback_bytes - size)); - } - } - - size_t new_buffer_index = hidden->callback_bytes / device->buffer_size; - while (old_buffer_index < new_buffer_index) { - // Trigger audio processing - SDL_SignalSemaphore(hidden->semaphore); - ++old_buffer_index; - } - - return AAUDIO_CALLBACK_RESULT_CONTINUE; -} - -static Uint8 *AAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize) -{ - struct SDL_PrivateAudioData *hidden = device->hidden; - size_t offset = (hidden->processed_bytes % hidden->mixbuf_bytes); - return &hidden->mixbuf[offset]; -} - -static bool AAUDIO_WaitDevice(SDL_AudioDevice *device) -{ - while (!SDL_GetAtomicInt(&device->shutdown)) { - // this semaphore won't fire when the app is in the background (AAUDIO_PauseDevices was called). - if (SDL_WaitSemaphoreTimeout(device->hidden->semaphore, 100)) { - return true; // semaphore was signaled, let's go! - } - // Still waiting on the semaphore (or the system), check other things then wait again. - } - return true; -} - -static bool BuildAAudioStream(SDL_AudioDevice *device); - -static bool RecoverAAudioDevice(SDL_AudioDevice *device) -{ - struct SDL_PrivateAudioData *hidden = device->hidden; - - // attempt to build a new stream, in case there's a new default device. - ctx.AAudioStream_requestStop(hidden->stream); - ctx.AAudioStream_close(hidden->stream); - hidden->stream = NULL; - - SDL_aligned_free(hidden->mixbuf); - hidden->mixbuf = NULL; - - SDL_DestroySemaphore(hidden->semaphore); - hidden->semaphore = NULL; - - const int prev_sample_frames = device->sample_frames; - SDL_AudioSpec prevspec; - SDL_copyp(&prevspec, &device->spec); - - if (!BuildAAudioStream(device)) { - return false; // oh well, we tried. - } - - // we don't know the new device spec until we open the new device, so we saved off the old one and force it back - // so SDL_AudioDeviceFormatChanged can set up all the important state if necessary and then set it back to the new spec. - const int new_sample_frames = device->sample_frames; - SDL_AudioSpec newspec; - SDL_copyp(&newspec, &device->spec); - - device->sample_frames = prev_sample_frames; - SDL_copyp(&device->spec, &prevspec); - if (!SDL_AudioDeviceFormatChangedAlreadyLocked(device, &newspec, new_sample_frames)) { - return false; // ugh - } - return true; -} - - -static bool AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) -{ - struct SDL_PrivateAudioData *hidden = device->hidden; - - // AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here. - const aaudio_result_t err = (aaudio_result_t) SDL_GetAtomicInt(&hidden->error_callback_triggered); - if (err) { - SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "aaudio: Audio device triggered error %d (%s)", (int) err, ctx.AAudio_convertResultToText(err)); - - if (!RecoverAAudioDevice(device)) { - return false; // oh well, we went down hard. - } - } else { - SDL_MemoryBarrierRelease(); - hidden->processed_bytes += buflen; - } - return true; -} - -static int AAUDIO_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - struct SDL_PrivateAudioData *hidden = device->hidden; - - // AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here. - if (SDL_GetAtomicInt(&hidden->error_callback_triggered)) { - SDL_SetAtomicInt(&hidden->error_callback_triggered, 0); - return -1; - } - - SDL_assert(buflen == device->buffer_size); // If this isn't true, we need to change semaphore trigger logic and account for wrapping copies here - size_t offset = (hidden->processed_bytes % hidden->mixbuf_bytes); - SDL_MemoryBarrierAcquire(); - SDL_memcpy(buffer, &hidden->mixbuf[offset], buflen); - hidden->processed_bytes += buflen; - return buflen; -} - -static void AAUDIO_CloseDevice(SDL_AudioDevice *device) -{ - struct SDL_PrivateAudioData *hidden = device->hidden; - LOGI(__func__); - - if (hidden) { - if (hidden->stream) { - ctx.AAudioStream_requestStop(hidden->stream); - // !!! FIXME: do we have to wait for the state to change to make sure all buffered audio has played, or will close do this (or will the system do this after the close)? - // !!! FIXME: also, will this definitely wait for a running data callback to finish, and then stop the callback from firing again? - ctx.AAudioStream_close(hidden->stream); - } - - if (hidden->semaphore) { - SDL_DestroySemaphore(hidden->semaphore); - } - - SDL_aligned_free(hidden->mixbuf); - SDL_free(hidden); - device->hidden = NULL; - } -} - -static bool BuildAAudioStream(SDL_AudioDevice *device) -{ - struct SDL_PrivateAudioData *hidden = device->hidden; - const bool recording = device->recording; - aaudio_result_t res; - - SDL_SetAtomicInt(&hidden->error_callback_triggered, 0); - - AAudioStreamBuilder *builder = NULL; - res = ctx.AAudio_createStreamBuilder(&builder); - if (res != AAUDIO_OK) { - LOGI("SDL Failed AAudio_createStreamBuilder %d", res); - return SDL_SetError("SDL Failed AAudio_createStreamBuilder %d", res); - } else if (!builder) { - LOGI("SDL Failed AAudio_createStreamBuilder - builder NULL"); - return SDL_SetError("SDL Failed AAudio_createStreamBuilder - builder NULL"); - } - -#if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES - const int aaudio_device_id = (int) ((size_t) device->handle); - LOGI("Opening device id %d", aaudio_device_id); - ctx.AAudioStreamBuilder_setDeviceId(builder, aaudio_device_id); -#endif - - aaudio_format_t format; - if ((device->spec.format == SDL_AUDIO_S32) && (SDL_GetAndroidSDKVersion() >= 31)) { - format = AAUDIO_FORMAT_PCM_I32; - } else if (device->spec.format == SDL_AUDIO_F32) { - format = AAUDIO_FORMAT_PCM_FLOAT; - } else { - format = AAUDIO_FORMAT_PCM_I16; // sint16 is a safe bet for everything else. - } - ctx.AAudioStreamBuilder_setFormat(builder, format); - ctx.AAudioStreamBuilder_setSampleRate(builder, device->spec.freq); - ctx.AAudioStreamBuilder_setChannelCount(builder, device->spec.channels); - - const aaudio_direction_t direction = (recording ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT); - ctx.AAudioStreamBuilder_setDirection(builder, direction); - ctx.AAudioStreamBuilder_setErrorCallback(builder, AAUDIO_errorCallback, device); - ctx.AAudioStreamBuilder_setDataCallback(builder, AAUDIO_dataCallback, device); - // Some devices have flat sounding audio when low latency mode is enabled, but this is a better experience for most people - if (SDL_GetHintBoolean(SDL_HINT_ANDROID_LOW_LATENCY_AUDIO, true)) { - SDL_Log("Low latency audio enabled"); - ctx.AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); - } else { - SDL_Log("Low latency audio disabled"); - } - - LOGI("AAudio Try to open %u hz %s %u channels samples %u", - device->spec.freq, SDL_GetAudioFormatName(device->spec.format), - device->spec.channels, device->sample_frames); - - res = ctx.AAudioStreamBuilder_openStream(builder, &hidden->stream); - if (res != AAUDIO_OK) { - LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res); - ctx.AAudioStreamBuilder_delete(builder); - return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); - } - ctx.AAudioStreamBuilder_delete(builder); - - device->sample_frames = (int)ctx.AAudioStream_getFramesPerDataCallback(hidden->stream); - if (device->sample_frames == AAUDIO_UNSPECIFIED) { - // We'll get variable frames in the callback, make sure we have at least half a buffer available - device->sample_frames = (int)ctx.AAudioStream_getBufferCapacityInFrames(hidden->stream) / 2; - } - - device->spec.freq = ctx.AAudioStream_getSampleRate(hidden->stream); - device->spec.channels = ctx.AAudioStream_getChannelCount(hidden->stream); - - format = ctx.AAudioStream_getFormat(hidden->stream); - if (format == AAUDIO_FORMAT_PCM_I16) { - device->spec.format = SDL_AUDIO_S16; - } else if (format == AAUDIO_FORMAT_PCM_I32) { - device->spec.format = SDL_AUDIO_S32; - } else if (format == AAUDIO_FORMAT_PCM_FLOAT) { - device->spec.format = SDL_AUDIO_F32; - } else { - return SDL_SetError("Got unexpected audio format %d from AAudioStream_getFormat", (int) format); - } - - SDL_UpdatedAudioDeviceFormat(device); - - // Allocate a triple buffered mixing buffer - // Two buffers can be in the process of being filled while the third is being read - hidden->num_buffers = 3; - hidden->mixbuf_bytes = (hidden->num_buffers * device->buffer_size); - hidden->mixbuf = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), hidden->mixbuf_bytes); - if (!hidden->mixbuf) { - return false; - } - hidden->processed_bytes = 0; - hidden->callback_bytes = 0; - - hidden->semaphore = SDL_CreateSemaphore(recording ? 0 : hidden->num_buffers); - if (!hidden->semaphore) { - LOGI("SDL Failed SDL_CreateSemaphore %s recording:%d", SDL_GetError(), recording); - return false; - } - - LOGI("AAudio Actually opened %u hz %s %u channels samples %u, buffers %d", - device->spec.freq, SDL_GetAudioFormatName(device->spec.format), - device->spec.channels, device->sample_frames, hidden->num_buffers); - - res = ctx.AAudioStream_requestStart(hidden->stream); - if (res != AAUDIO_OK) { - LOGI("SDL Failed AAudioStream_requestStart %d recording:%d", res, recording); - return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); - } - - LOGI("SDL AAudioStream_requestStart OK"); - - return true; -} - -// !!! FIXME: make this non-blocking! -static void SDLCALL RequestAndroidPermissionBlockingCallback(void *userdata, const char *permission, bool granted) -{ - SDL_SetAtomicInt((SDL_AtomicInt *) userdata, granted ? 1 : -1); -} - -static bool AAUDIO_OpenDevice(SDL_AudioDevice *device) -{ -#if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES - SDL_assert(device->handle); // AAUDIO_UNSPECIFIED is zero, so legit devices should all be non-zero. -#endif - - LOGI(__func__); - - if (device->recording) { - // !!! FIXME: make this non-blocking! - SDL_AtomicInt permission_response; - SDL_SetAtomicInt(&permission_response, 0); - if (!SDL_RequestAndroidPermission("android.permission.RECORD_AUDIO", RequestAndroidPermissionBlockingCallback, &permission_response)) { - return false; - } - - while (SDL_GetAtomicInt(&permission_response) == 0) { - SDL_Delay(10); - } - - if (SDL_GetAtomicInt(&permission_response) < 0) { - LOGI("This app doesn't have RECORD_AUDIO permission"); - return SDL_SetError("This app doesn't have RECORD_AUDIO permission"); - } - } - - device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } - - return BuildAAudioStream(device); -} - -static bool PauseOneDevice(SDL_AudioDevice *device, void *userdata) -{ - struct SDL_PrivateAudioData *hidden = (struct SDL_PrivateAudioData *)device->hidden; - if (hidden) { - if (hidden->stream) { - aaudio_result_t res; - - if (device->recording) { - // Pause() isn't implemented for recording, use Stop() - res = ctx.AAudioStream_requestStop(hidden->stream); - } else { - res = ctx.AAudioStream_requestPause(hidden->stream); - } - - if (res != AAUDIO_OK) { - LOGI("SDL Failed AAudioStream_requestPause %d", res); - SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); - } - } - } - return false; // keep enumerating. -} - -// Pause (block) all non already paused audio devices by taking their mixer lock -void AAUDIO_PauseDevices(void) -{ - if (ctx.handle) { // AAUDIO driver is used? - (void) SDL_FindPhysicalAudioDeviceByCallback(PauseOneDevice, NULL); - } -} - -// Resume (unblock) all non already paused audio devices by releasing their mixer lock -static bool ResumeOneDevice(SDL_AudioDevice *device, void *userdata) -{ - struct SDL_PrivateAudioData *hidden = device->hidden; - if (hidden) { - if (hidden->stream) { - aaudio_result_t res = ctx.AAudioStream_requestStart(hidden->stream); - if (res != AAUDIO_OK) { - LOGI("SDL Failed AAudioStream_requestStart %d", res); - SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); - } - } - } - return false; // keep enumerating. -} - -void AAUDIO_ResumeDevices(void) -{ - if (ctx.handle) { // AAUDIO driver is used? - (void) SDL_FindPhysicalAudioDeviceByCallback(ResumeOneDevice, NULL); - } -} - -static void AAUDIO_Deinitialize(void) -{ - Android_StopAudioHotplug(); - - LOGI(__func__); - if (ctx.handle) { - SDL_UnloadObject(ctx.handle); - } - SDL_zero(ctx); - LOGI("End AAUDIO %s", SDL_GetError()); -} - - -static bool AAUDIO_Init(SDL_AudioDriverImpl *impl) -{ - LOGI(__func__); - - /* AAudio was introduced in Android 8.0, but has reference counting crash issues in that release, - * so don't use it until 8.1. - * - * See https://github.com/google/oboe/issues/40 for more information. - */ - if (SDL_GetAndroidSDKVersion() < 27) { - return false; - } - - SDL_zero(ctx); - - ctx.handle = SDL_LoadObject(LIB_AAUDIO_SO); - if (!ctx.handle) { - LOGI("SDL couldn't find " LIB_AAUDIO_SO); - return false; - } - - if (!AAUDIO_LoadFunctions(&ctx)) { - SDL_UnloadObject(ctx.handle); - SDL_zero(ctx); - return false; - } - - impl->ThreadInit = Android_AudioThreadInit; - impl->Deinitialize = AAUDIO_Deinitialize; - impl->OpenDevice = AAUDIO_OpenDevice; - impl->CloseDevice = AAUDIO_CloseDevice; - impl->WaitDevice = AAUDIO_WaitDevice; - impl->PlayDevice = AAUDIO_PlayDevice; - impl->GetDeviceBuf = AAUDIO_GetDeviceBuf; - impl->WaitRecordingDevice = AAUDIO_WaitDevice; - impl->RecordDevice = AAUDIO_RecordDevice; - - impl->HasRecordingSupport = true; - -#if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES - impl->DetectDevices = Android_StartAudioHotplug; -#else - impl->OnlyHasDefaultPlaybackDevice = true; - impl->OnlyHasDefaultRecordingDevice = true; -#endif - - LOGI("SDL AAUDIO_Init OK"); - return true; -} - -AudioBootStrap AAUDIO_bootstrap = { - "AAudio", "AAudio audio driver", AAUDIO_Init, false, false -}; - -#endif // SDL_AUDIO_DRIVER_AAUDIO diff --git a/contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.h b/contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.h deleted file mode 100644 index 5326bad..0000000 --- a/contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_aaudio_h_ -#define SDL_aaudio_h_ - -#ifdef SDL_AUDIO_DRIVER_AAUDIO - -extern void AAUDIO_ResumeDevices(void); -extern void AAUDIO_PauseDevices(void); - -#else - -#define AAUDIO_ResumeDevices() -#define AAUDIO_PauseDevices() - -#endif - -#endif // SDL_aaudio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudiofuncs.h b/contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudiofuncs.h deleted file mode 100644 index 0298821..0000000 --- a/contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudiofuncs.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright , (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#define SDL_PROC_UNUSED(ret, func, params) - -SDL_PROC(const char *, AAudio_convertResultToText, (aaudio_result_t returnCode)) -SDL_PROC(const char *, AAudio_convertStreamStateToText, (aaudio_stream_state_t state)) -SDL_PROC(aaudio_result_t, AAudio_createStreamBuilder, (AAudioStreamBuilder * *builder)) -SDL_PROC(void, AAudioStreamBuilder_setDeviceId, (AAudioStreamBuilder * builder, int32_t deviceId)) -SDL_PROC(void, AAudioStreamBuilder_setSampleRate, (AAudioStreamBuilder * builder, int32_t sampleRate)) -SDL_PROC(void, AAudioStreamBuilder_setChannelCount, (AAudioStreamBuilder * builder, int32_t channelCount)) -SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSamplesPerFrame, (AAudioStreamBuilder * builder, int32_t samplesPerFrame)) -SDL_PROC(void, AAudioStreamBuilder_setFormat, (AAudioStreamBuilder * builder, aaudio_format_t format)) -SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSharingMode, (AAudioStreamBuilder * builder, aaudio_sharing_mode_t sharingMode)) -SDL_PROC(void, AAudioStreamBuilder_setDirection, (AAudioStreamBuilder * builder, aaudio_direction_t direction)) -SDL_PROC_UNUSED(void, AAudioStreamBuilder_setBufferCapacityInFrames, (AAudioStreamBuilder * builder, int32_t numFrames)) -SDL_PROC(void, AAudioStreamBuilder_setPerformanceMode, (AAudioStreamBuilder * builder, aaudio_performance_mode_t mode)) -SDL_PROC_UNUSED(void, AAudioStreamBuilder_setUsage, (AAudioStreamBuilder * builder, aaudio_usage_t usage)) // API 28 -SDL_PROC_UNUSED(void, AAudioStreamBuilder_setContentType, (AAudioStreamBuilder * builder, aaudio_content_type_t contentType)) // API 28 -SDL_PROC_UNUSED(void, AAudioStreamBuilder_setInputPreset, (AAudioStreamBuilder * builder, aaudio_input_preset_t inputPreset)) // API 28 -SDL_PROC_UNUSED(void, AAudioStreamBuilder_setAllowedCapturePolicy, (AAudioStreamBuilder * builder, aaudio_allowed_capture_policy_t capturePolicy)) // API 29 -SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSessionId, (AAudioStreamBuilder * builder, aaudio_session_id_t sessionId)) // API 28 -SDL_PROC_UNUSED(void, AAudioStreamBuilder_setPrivacySensitive, (AAudioStreamBuilder * builder, bool privacySensitive)) // API 30 -SDL_PROC(void, AAudioStreamBuilder_setDataCallback, (AAudioStreamBuilder * builder, AAudioStream_dataCallback callback, void *userData)) -SDL_PROC(void, AAudioStreamBuilder_setFramesPerDataCallback, (AAudioStreamBuilder * builder, int32_t numFrames)) -SDL_PROC(void, AAudioStreamBuilder_setErrorCallback, (AAudioStreamBuilder * builder, AAudioStream_errorCallback callback, void *userData)) -SDL_PROC(aaudio_result_t, AAudioStreamBuilder_openStream, (AAudioStreamBuilder * builder, AAudioStream **stream)) -SDL_PROC(aaudio_result_t, AAudioStreamBuilder_delete, (AAudioStreamBuilder * builder)) -SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_release, (AAudioStream * stream)) // API 30 -SDL_PROC(aaudio_result_t, AAudioStream_close, (AAudioStream * stream)) -SDL_PROC(aaudio_result_t, AAudioStream_requestStart, (AAudioStream * stream)) -SDL_PROC(aaudio_result_t, AAudioStream_requestPause, (AAudioStream * stream)) -SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_requestFlush, (AAudioStream * stream)) -SDL_PROC(aaudio_result_t, AAudioStream_requestStop, (AAudioStream * stream)) -SDL_PROC(aaudio_stream_state_t, AAudioStream_getState, (AAudioStream * stream)) -SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_waitForStateChange, (AAudioStream * stream, aaudio_stream_state_t inputState, aaudio_stream_state_t *nextState, int64_t timeoutNanoseconds)) -SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_read, (AAudioStream * stream, void *buffer, int32_t numFrames, int64_t timeoutNanoseconds)) -SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_write, (AAudioStream * stream, const void *buffer, int32_t numFrames, int64_t timeoutNanoseconds)) -SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_setBufferSizeInFrames, (AAudioStream * stream, int32_t numFrames)) -SDL_PROC_UNUSED(int32_t, AAudioStream_getBufferSizeInFrames, (AAudioStream * stream)) -SDL_PROC_UNUSED(int32_t, AAudioStream_getFramesPerBurst, (AAudioStream * stream)) -SDL_PROC(int32_t, AAudioStream_getBufferCapacityInFrames, (AAudioStream * stream)) -SDL_PROC(int32_t, AAudioStream_getFramesPerDataCallback, (AAudioStream * stream)) -SDL_PROC_UNUSED(int32_t, AAudioStream_getXRunCount, (AAudioStream * stream)) -SDL_PROC(int32_t, AAudioStream_getSampleRate, (AAudioStream * stream)) -SDL_PROC(int32_t, AAudioStream_getChannelCount, (AAudioStream * stream)) -SDL_PROC_UNUSED(int32_t, AAudioStream_getSamplesPerFrame, (AAudioStream * stream)) -SDL_PROC_UNUSED(int32_t, AAudioStream_getDeviceId, (AAudioStream * stream)) -SDL_PROC(aaudio_format_t, AAudioStream_getFormat, (AAudioStream * stream)) -SDL_PROC_UNUSED(aaudio_sharing_mode_t, AAudioStream_getSharingMode, (AAudioStream * stream)) -SDL_PROC_UNUSED(aaudio_performance_mode_t, AAudioStream_getPerformanceMode, (AAudioStream * stream)) -SDL_PROC_UNUSED(aaudio_direction_t, AAudioStream_getDirection, (AAudioStream * stream)) -SDL_PROC_UNUSED(int64_t, AAudioStream_getFramesWritten, (AAudioStream * stream)) -SDL_PROC_UNUSED(int64_t, AAudioStream_getFramesRead, (AAudioStream * stream)) -SDL_PROC_UNUSED(aaudio_session_id_t, AAudioStream_getSessionId, (AAudioStream * stream)) // API 28 -SDL_PROC(aaudio_result_t, AAudioStream_getTimestamp, (AAudioStream * stream, clockid_t clockid, int64_t *framePosition, int64_t *timeNanoseconds)) -SDL_PROC_UNUSED(aaudio_usage_t, AAudioStream_getUsage, (AAudioStream * stream)) // API 28 -SDL_PROC_UNUSED(aaudio_content_type_t, AAudioStream_getContentType, (AAudioStream * stream)) // API 28 -SDL_PROC_UNUSED(aaudio_input_preset_t, AAudioStream_getInputPreset, (AAudioStream * stream)) // API 28 -SDL_PROC_UNUSED(aaudio_allowed_capture_policy_t, AAudioStream_getAllowedCapturePolicy, (AAudioStream * stream)) // API 29 -SDL_PROC_UNUSED(bool, AAudioStream_isPrivacySensitive, (AAudioStream * stream)) // API 30 - -#undef SDL_PROC -#undef SDL_PROC_UNUSED diff --git a/contrib/SDL-3.2.8/src/audio/alsa/SDL_alsa_audio.c b/contrib/SDL-3.2.8/src/audio/alsa/SDL_alsa_audio.c deleted file mode 100644 index 308eb23..0000000 --- a/contrib/SDL-3.2.8/src/audio/alsa/SDL_alsa_audio.c +++ /dev/null @@ -1,1519 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_ALSA - -#ifndef SDL_ALSA_NON_BLOCKING -#define SDL_ALSA_NON_BLOCKING 0 -#endif - -// without the thread, you will detect devices on startup, but will not get further hotplug events. But that might be okay. -#ifndef SDL_ALSA_HOTPLUG_THREAD -#define SDL_ALSA_HOTPLUG_THREAD 1 -#endif - -// this turns off debug logging completely (but by default this goes to the bitbucket). -#ifndef SDL_ALSA_DEBUG -#define SDL_ALSA_DEBUG 1 -#endif - -#include "../SDL_sysaudio.h" -#include "SDL_alsa_audio.h" - -#if SDL_ALSA_DEBUG -#define LOGDEBUG(...) SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "ALSA: " __VA_ARGS__) -#else -#define LOGDEBUG(...) -#endif - -//TODO: cleanup once the code settled down - -static int (*ALSA_snd_pcm_open)(snd_pcm_t **, const char *, snd_pcm_stream_t, int); -static int (*ALSA_snd_pcm_close)(snd_pcm_t *pcm); -static int (*ALSA_snd_pcm_start)(snd_pcm_t *pcm); -static snd_pcm_sframes_t (*ALSA_snd_pcm_writei)(snd_pcm_t *, const void *, snd_pcm_uframes_t); -static snd_pcm_sframes_t (*ALSA_snd_pcm_readi)(snd_pcm_t *, void *, snd_pcm_uframes_t); -static int (*ALSA_snd_pcm_recover)(snd_pcm_t *, int, int); -static int (*ALSA_snd_pcm_prepare)(snd_pcm_t *); -static int (*ALSA_snd_pcm_drain)(snd_pcm_t *); -static const char *(*ALSA_snd_strerror)(int); -static size_t (*ALSA_snd_pcm_hw_params_sizeof)(void); -static size_t (*ALSA_snd_pcm_sw_params_sizeof)(void); -static void (*ALSA_snd_pcm_hw_params_copy)(snd_pcm_hw_params_t *, const snd_pcm_hw_params_t *); -static int (*ALSA_snd_pcm_hw_params_any)(snd_pcm_t *, snd_pcm_hw_params_t *); -static int (*ALSA_snd_pcm_hw_params_set_access)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t); -static int (*ALSA_snd_pcm_hw_params_set_format)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t); -static int (*ALSA_snd_pcm_hw_params_set_channels)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int); -static int (*ALSA_snd_pcm_hw_params_get_channels)(const snd_pcm_hw_params_t *, unsigned int *); -static int (*ALSA_snd_pcm_hw_params_set_rate_near)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *); -static int (*ALSA_snd_pcm_hw_params_set_period_size_near)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *); -static int (*ALSA_snd_pcm_hw_params_get_period_size)(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *); -static int (*ALSA_snd_pcm_hw_params_set_periods_min)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *); -static int (*ALSA_snd_pcm_hw_params_set_periods_first)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *); -static int (*ALSA_snd_pcm_hw_params_get_periods)(const snd_pcm_hw_params_t *, unsigned int *, int *); -static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near)(snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *); -static int (*ALSA_snd_pcm_hw_params_get_buffer_size)(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *); -static int (*ALSA_snd_pcm_hw_params)(snd_pcm_t *, snd_pcm_hw_params_t *); -static int (*ALSA_snd_pcm_sw_params_current)(snd_pcm_t *, - snd_pcm_sw_params_t *); -static int (*ALSA_snd_pcm_sw_params_set_start_threshold)(snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t); -static int (*ALSA_snd_pcm_sw_params)(snd_pcm_t *, snd_pcm_sw_params_t *); -static int (*ALSA_snd_pcm_nonblock)(snd_pcm_t *, int); -static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int); -static int (*ALSA_snd_pcm_sw_params_set_avail_min)(snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t); -static int (*ALSA_snd_pcm_reset)(snd_pcm_t *); -static int (*ALSA_snd_device_name_hint)(int, const char *, void ***); -static char *(*ALSA_snd_device_name_get_hint)(const void *, const char *); -static int (*ALSA_snd_device_name_free_hint)(void **); -static snd_pcm_sframes_t (*ALSA_snd_pcm_avail)(snd_pcm_t *); -static size_t (*ALSA_snd_ctl_card_info_sizeof)(void); -static size_t (*ALSA_snd_pcm_info_sizeof)(void); -static int (*ALSA_snd_card_next)(int*); -static int (*ALSA_snd_ctl_open)(snd_ctl_t **,const char *,int); -static int (*ALSA_snd_ctl_close)(snd_ctl_t *); -static int (*ALSA_snd_ctl_card_info)(snd_ctl_t *, snd_ctl_card_info_t *); -static int (*ALSA_snd_ctl_pcm_next_device)(snd_ctl_t *, int *); -static unsigned int (*ALSA_snd_pcm_info_get_subdevices_count)(const snd_pcm_info_t *); -static void (*ALSA_snd_pcm_info_set_device)(snd_pcm_info_t *, unsigned int); -static void (*ALSA_snd_pcm_info_set_subdevice)(snd_pcm_info_t *, unsigned int); -static void (*ALSA_snd_pcm_info_set_stream)(snd_pcm_info_t *, snd_pcm_stream_t); -static int (*ALSA_snd_ctl_pcm_info)(snd_ctl_t *, snd_pcm_info_t *); -static unsigned int (*ALSA_snd_pcm_info_get_subdevices_count)(const snd_pcm_info_t *); -static const char *(*ALSA_snd_ctl_card_info_get_id)(const snd_ctl_card_info_t *); -static const char *(*ALSA_snd_pcm_info_get_name)(const snd_pcm_info_t *); -static const char *(*ALSA_snd_pcm_info_get_subdevice_name)(const snd_pcm_info_t *); -static const char *(*ALSA_snd_ctl_card_info_get_name)(const snd_ctl_card_info_t *); -static void (*ALSA_snd_ctl_card_info_clear)(snd_ctl_card_info_t *); -static int (*ALSA_snd_pcm_hw_free)(snd_pcm_t *); -static int (*ALSA_snd_pcm_hw_params_set_channels_near)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *); -static snd_pcm_chmap_query_t **(*ALSA_snd_pcm_query_chmaps)(snd_pcm_t *pcm); -static void (*ALSA_snd_pcm_free_chmaps)(snd_pcm_chmap_query_t **maps); -static int (*ALSA_snd_pcm_set_chmap)(snd_pcm_t *, const snd_pcm_chmap_t *); -static int (*ALSA_snd_pcm_chmap_print)(const snd_pcm_chmap_t *, size_t, char *); - -#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC -#define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof -#define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof - -static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC; -static SDL_SharedObject *alsa_handle = NULL; - -static bool load_alsa_sym(const char *fn, void **addr) -{ - *addr = SDL_LoadFunction(alsa_handle, fn); - if (!*addr) { - // Don't call SDL_SetError(): SDL_LoadFunction already did. - return false; - } - - return true; -} - -// cast funcs to char* first, to please GCC's strict aliasing rules. -#define SDL_ALSA_SYM(x) \ - if (!load_alsa_sym(#x, (void **)(char *)&ALSA_##x)) \ - return false -#else -#define SDL_ALSA_SYM(x) ALSA_##x = x -#endif - -static bool load_alsa_syms(void) -{ - SDL_ALSA_SYM(snd_pcm_open); - SDL_ALSA_SYM(snd_pcm_close); - SDL_ALSA_SYM(snd_pcm_start); - SDL_ALSA_SYM(snd_pcm_writei); - SDL_ALSA_SYM(snd_pcm_readi); - SDL_ALSA_SYM(snd_pcm_recover); - SDL_ALSA_SYM(snd_pcm_prepare); - SDL_ALSA_SYM(snd_pcm_drain); - SDL_ALSA_SYM(snd_strerror); - SDL_ALSA_SYM(snd_pcm_hw_params_sizeof); - SDL_ALSA_SYM(snd_pcm_sw_params_sizeof); - SDL_ALSA_SYM(snd_pcm_hw_params_copy); - SDL_ALSA_SYM(snd_pcm_hw_params_any); - SDL_ALSA_SYM(snd_pcm_hw_params_set_access); - SDL_ALSA_SYM(snd_pcm_hw_params_set_format); - SDL_ALSA_SYM(snd_pcm_hw_params_set_channels); - SDL_ALSA_SYM(snd_pcm_hw_params_get_channels); - SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near); - SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near); - SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size); - SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_min); - SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_first); - SDL_ALSA_SYM(snd_pcm_hw_params_get_periods); - SDL_ALSA_SYM(snd_pcm_hw_params_set_buffer_size_near); - SDL_ALSA_SYM(snd_pcm_hw_params_get_buffer_size); - SDL_ALSA_SYM(snd_pcm_hw_params); - SDL_ALSA_SYM(snd_pcm_sw_params_current); - SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold); - SDL_ALSA_SYM(snd_pcm_sw_params); - SDL_ALSA_SYM(snd_pcm_nonblock); - SDL_ALSA_SYM(snd_pcm_wait); - SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min); - SDL_ALSA_SYM(snd_pcm_reset); - SDL_ALSA_SYM(snd_device_name_hint); - SDL_ALSA_SYM(snd_device_name_get_hint); - SDL_ALSA_SYM(snd_device_name_free_hint); - SDL_ALSA_SYM(snd_pcm_avail); - SDL_ALSA_SYM(snd_ctl_card_info_sizeof); - SDL_ALSA_SYM(snd_pcm_info_sizeof); - SDL_ALSA_SYM(snd_card_next); - SDL_ALSA_SYM(snd_ctl_open); - SDL_ALSA_SYM(snd_ctl_close); - SDL_ALSA_SYM(snd_ctl_card_info); - SDL_ALSA_SYM(snd_ctl_pcm_next_device); - SDL_ALSA_SYM(snd_pcm_info_get_subdevices_count); - SDL_ALSA_SYM(snd_pcm_info_set_device); - SDL_ALSA_SYM(snd_pcm_info_set_subdevice); - SDL_ALSA_SYM(snd_pcm_info_set_stream); - SDL_ALSA_SYM(snd_ctl_pcm_info); - SDL_ALSA_SYM(snd_pcm_info_get_subdevices_count); - SDL_ALSA_SYM(snd_ctl_card_info_get_id); - SDL_ALSA_SYM(snd_pcm_info_get_name); - SDL_ALSA_SYM(snd_pcm_info_get_subdevice_name); - SDL_ALSA_SYM(snd_ctl_card_info_get_name); - SDL_ALSA_SYM(snd_ctl_card_info_clear); - SDL_ALSA_SYM(snd_pcm_hw_free); - SDL_ALSA_SYM(snd_pcm_hw_params_set_channels_near); - SDL_ALSA_SYM(snd_pcm_query_chmaps); - SDL_ALSA_SYM(snd_pcm_free_chmaps); - SDL_ALSA_SYM(snd_pcm_set_chmap); - SDL_ALSA_SYM(snd_pcm_chmap_print); - - return true; -} - -#undef SDL_ALSA_SYM - -#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC - -static void UnloadALSALibrary(void) -{ - if (alsa_handle) { - SDL_UnloadObject(alsa_handle); - alsa_handle = NULL; - } -} - -static bool LoadALSALibrary(void) -{ - bool retval = true; - if (!alsa_handle) { - alsa_handle = SDL_LoadObject(alsa_library); - if (!alsa_handle) { - retval = false; - // Don't call SDL_SetError(): SDL_LoadObject already did. - } else { - retval = load_alsa_syms(); - if (!retval) { - UnloadALSALibrary(); - } - } - } - return retval; -} - -#else - -static void UnloadALSALibrary(void) -{ -} - -static bool LoadALSALibrary(void) -{ - load_alsa_syms(); - return true; -} - -#endif // SDL_AUDIO_DRIVER_ALSA_DYNAMIC - -static const char *ALSA_device_prefix = NULL; -static void ALSA_guess_device_prefix(void) -{ - if (ALSA_device_prefix) { - return; // already calculated. - } - - // Apparently there are several different ways that ALSA lists - // actual hardware. It could be prefixed with "hw:" or "default:" - // or "sysdefault:" and maybe others. Go through the list and see - // if we can find a preferred prefix for the system. - - static const char *const prefixes[] = { - "hw:", "sysdefault:", "default:" - }; - - void **hints = NULL; - if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) { - for (int i = 0; hints[i]; i++) { - char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME"); - if (name) { - for (int j = 0; j < SDL_arraysize(prefixes); j++) { - const char *prefix = prefixes[j]; - const size_t prefixlen = SDL_strlen(prefix); - if (SDL_strncmp(name, prefix, prefixlen) == 0) { - ALSA_device_prefix = prefix; - break; - } - } - free(name); // This should NOT be SDL_free() - - if (ALSA_device_prefix) { - break; - } - } - } - } - - if (!ALSA_device_prefix) { - ALSA_device_prefix = prefixes[0]; // oh well. - } - - LOGDEBUG("device prefix is probably '%s'", ALSA_device_prefix); -} - -typedef struct ALSA_Device -{ - // the unicity key is the couple (id,recording) - char *id; // empty means canonical default - char *name; - bool recording; - struct ALSA_Device *next; -} ALSA_Device; - -static const ALSA_Device default_playback_handle = { - "", - "default", - false, - NULL -}; - -static const ALSA_Device default_recording_handle = { - "", - "default", - true, - NULL -}; - -// TODO: Figure out the "right"(TM) way. For the moment we presume that if a system is using a -// software mixer for application audio sharing which is not the linux native alsa[dmix], for -// instance jack/pulseaudio2[pipewire]/pulseaudio1/esound/etc, we expect the system integrators did -// configure the canonical default to the right alsa PCM plugin for their software mixer. -// -// All the above may be completely wrong. -static char *get_pcm_str(void *handle) -{ - SDL_assert(handle != NULL); // SDL2 used NULL to mean "default" but that's not true in SDL3. - ALSA_Device *dev = (ALSA_Device *)handle; - char *pcm_str = NULL; - - if (SDL_strlen(dev->id) == 0) { - // If the user does not want to go thru the default PCM or the canonical default, the - // the configuration space being _massive_, give the user the ability to specify - // its own PCMs using environment variables. It will have to fit SDL constraints though. - const char *devname = SDL_GetHint(dev->recording ? SDL_HINT_AUDIO_ALSA_DEFAULT_RECORDING_DEVICE : SDL_HINT_AUDIO_ALSA_DEFAULT_PLAYBACK_DEVICE); - if (!devname) { - devname = SDL_GetHint(SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE); - if (!devname) { - devname = "default"; - } - } - pcm_str = SDL_strdup(devname); - } else { - SDL_asprintf(&pcm_str, "%sCARD=%s", ALSA_device_prefix, dev->id); - } - return pcm_str; -} - -// This function waits until it is possible to write a full sound buffer -static bool ALSA_WaitDevice(SDL_AudioDevice *device) -{ - const int fulldelay = (int) ((((Uint64) device->sample_frames) * 1000) / device->spec.freq); - const int delay = SDL_max(fulldelay, 10); - - while (!SDL_GetAtomicInt(&device->shutdown)) { - const int rc = ALSA_snd_pcm_wait(device->hidden->pcm, delay); - if (rc < 0 && (rc != -EAGAIN)) { - const int status = ALSA_snd_pcm_recover(device->hidden->pcm, rc, 0); - if (status < 0) { - // Hmm, not much we can do - abort - SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA: snd_pcm_wait failed (unrecoverable): %s", ALSA_snd_strerror(rc)); - return false; - } - continue; - } - - if (rc > 0) { - break; // ready to go! - } - - // Timed out! Make sure we aren't shutting down and then wait again. - } - - return true; -} - -static bool ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) -{ - SDL_assert(buffer == device->hidden->mixbuf); - Uint8 *sample_buf = (Uint8 *) buffer; // !!! FIXME: deal with this without casting away constness - const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec); - snd_pcm_uframes_t frames_left = (snd_pcm_uframes_t) (buflen / frame_size); - - while ((frames_left > 0) && !SDL_GetAtomicInt(&device->shutdown)) { - const int rc = ALSA_snd_pcm_writei(device->hidden->pcm, sample_buf, frames_left); - //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA PLAYDEVICE: WROTE %d of %d bytes", (rc >= 0) ? ((int) (rc * frame_size)) : rc, (int) (frames_left * frame_size)); - SDL_assert(rc != 0); // assuming this can't happen if we used snd_pcm_wait and queried for available space. - if (rc < 0) { - SDL_assert(rc != -EAGAIN); // assuming this can't happen if we used snd_pcm_wait and queried for available space. snd_pcm_recover won't handle it! - const int status = ALSA_snd_pcm_recover(device->hidden->pcm, rc, 0); - if (status < 0) { - // Hmm, not much we can do - abort - SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA write failed (unrecoverable): %s", ALSA_snd_strerror(rc)); - return false; - } - continue; - } - - sample_buf += rc * frame_size; - frames_left -= rc; - } - - return true; -} - -static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(device->hidden->pcm); - if (rc <= 0) { - // Wait a bit and try again, maybe the hardware isn't quite ready yet? - SDL_Delay(1); - - rc = ALSA_snd_pcm_avail(device->hidden->pcm); - if (rc <= 0) { - // We'll catch it next time - *buffer_size = 0; - return NULL; - } - } - - const int requested_frames = SDL_min(device->sample_frames, rc); - const int requested_bytes = requested_frames * SDL_AUDIO_FRAMESIZE(device->spec); - SDL_assert(requested_bytes <= *buffer_size); - //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA GETDEVICEBUF: NEED %d BYTES", requested_bytes); - *buffer_size = requested_bytes; - return device->hidden->mixbuf; -} - -static int ALSA_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec); - SDL_assert((buflen % frame_size) == 0); - - const snd_pcm_sframes_t total_available = ALSA_snd_pcm_avail(device->hidden->pcm); - const int total_frames = SDL_min(buflen / frame_size, total_available); - - const int rc = ALSA_snd_pcm_readi(device->hidden->pcm, buffer, total_frames); - - SDL_assert(rc != -EAGAIN); // assuming this can't happen if we used snd_pcm_wait and queried for available space. snd_pcm_recover won't handle it! - - if (rc < 0) { - const int status = ALSA_snd_pcm_recover(device->hidden->pcm, rc, 0); - if (status < 0) { - // Hmm, not much we can do - abort - SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA read failed (unrecoverable): %s", ALSA_snd_strerror(rc)); - return -1; - } - return 0; // go back to WaitDevice and try again. - } - - //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA: recorded %d bytes", rc * frame_size); - - return rc * frame_size; -} - -static void ALSA_FlushRecording(SDL_AudioDevice *device) -{ - ALSA_snd_pcm_reset(device->hidden->pcm); -} - -static void ALSA_CloseDevice(SDL_AudioDevice *device) -{ - if (device->hidden) { - if (device->hidden->pcm) { - // Wait for the submitted audio to drain. ALSA_snd_pcm_drop() can hang, so don't use that. - SDL_Delay(((device->sample_frames * 1000) / device->spec.freq) * 2); - ALSA_snd_pcm_close(device->hidden->pcm); - } - SDL_free(device->hidden->mixbuf); - SDL_free(device->hidden); - } -} - - -// To make easier to track parameters during the whole alsa pcm configuration: -struct ALSA_pcm_cfg_ctx { - SDL_AudioDevice *device; - - snd_pcm_hw_params_t *hwparams; - snd_pcm_sw_params_t *swparams; - - SDL_AudioFormat matched_sdl_format; - unsigned int chans_n; - unsigned int target_chans_n; - unsigned int rate; - snd_pcm_uframes_t persize; // alsa period size, SDL audio device sample_frames - snd_pcm_chmap_query_t **chmap_queries; - unsigned int sdl_chmap[SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX]; - unsigned int alsa_chmap_installed[SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX]; - - unsigned int periods; -}; -// The following are SDL channel maps with alsa position values, from 0 channels to 8 channels. -// See SDL3/SDL_audio.h -// Strictly speaking those are "parameters" of channel maps, like alsa hwparams and swparams, they -// have to be "reduced/refined" until an exact channel map. Only the 6 channels map requires such -// "reduction/refine". -static enum snd_pcm_chmap_position sdl_channel_maps[SDL_AUDIO_ALSA__SDL_CHMAPS_N][SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX] = { - // 0 channels - { - 0 - }, - // 1 channel - { - SND_CHMAP_MONO, - }, - // 2 channels - { - SND_CHMAP_FL, - SND_CHMAP_FR, - }, - // 3 channels - { - SND_CHMAP_FL, - SND_CHMAP_FR, - SND_CHMAP_LFE, - }, - // 4 channels - { - SND_CHMAP_FL, - SND_CHMAP_FR, - SND_CHMAP_RL, - SND_CHMAP_RR, - }, - // 5 channels - { - SND_CHMAP_FL, - SND_CHMAP_FR, - SND_CHMAP_LFE, - SND_CHMAP_RL, - SND_CHMAP_RR, - }, - // 6 channels - // XXX: here we encode not a uniq channel map but a set of channel maps. We will reduce it each - // time we are going to work with an alsa 6 channels map. - { - SND_CHMAP_FL, - SND_CHMAP_FR, - SND_CHMAP_FC, - SND_CHMAP_LFE, - // The 2 following channel positions are (SND_CHMAP_SL,SND_CHMAP_SR) or - // (SND_CHMAP_RL,SND_CHMAP_RR) - SND_CHMAP_UNKNOWN, - SND_CHMAP_UNKNOWN, - }, - // 7 channels - { - SND_CHMAP_FL, - SND_CHMAP_FR, - SND_CHMAP_FC, - SND_CHMAP_LFE, - SND_CHMAP_RC, - SND_CHMAP_SL, - SND_CHMAP_SR, - }, - // 8 channels - { - SND_CHMAP_FL, - SND_CHMAP_FR, - SND_CHMAP_FC, - SND_CHMAP_LFE, - SND_CHMAP_RL, - SND_CHMAP_RR, - SND_CHMAP_SL, - SND_CHMAP_SR, - }, -}; - -// Helper for the function right below. -static bool has_pos(const unsigned int *chmap, unsigned int pos) -{ - for (unsigned int chan_idx = 0; ; chan_idx++) { - if (chan_idx == 6) { - return false; - } - if (chmap[chan_idx] == pos) { - return true; - } - } - SDL_assert(!"Shouldn't hit this code."); - return false; -} - -// XXX: Each time we are going to work on an alsa 6 channels map, we must reduce the set of channel -// maps which is encoded in sdl_channel_maps[6] to a uniq one. -#define HAVE_NONE 0 -#define HAVE_REAR 1 -#define HAVE_SIDE 2 -#define HAVE_BOTH 3 -static void sdl_6chans_set_rear_or_side_channels_from_alsa_6chans(unsigned int *sdl_6chans, const unsigned int *alsa_6chans) -{ - // For alsa channel maps with 6 channels and with SND_CHMAP_FL,SND_CHMAP_FR,SND_CHMAP_FC, - // SND_CHMAP_LFE, reduce our 6 channels maps to a uniq one. - if ( !has_pos(alsa_6chans, SND_CHMAP_FL) || - !has_pos(alsa_6chans, SND_CHMAP_FR) || - !has_pos(alsa_6chans, SND_CHMAP_FC) || - !has_pos(alsa_6chans, SND_CHMAP_LFE)) { - sdl_6chans[4] = SND_CHMAP_UNKNOWN; - sdl_6chans[5] = SND_CHMAP_UNKNOWN; - LOGDEBUG("6channels:unsupported channel map"); - return; - } - - unsigned int state = HAVE_NONE; - for (unsigned int chan_idx = 0; chan_idx < 6; chan_idx++) { - if ((alsa_6chans[chan_idx] == SND_CHMAP_SL) || (alsa_6chans[chan_idx] == SND_CHMAP_SR)) { - if (state == HAVE_NONE) { - state = HAVE_SIDE; - } else if (state == HAVE_REAR) { - state = HAVE_BOTH; - break; - } - } else if ((alsa_6chans[chan_idx] == SND_CHMAP_RL) || (alsa_6chans[chan_idx] == SND_CHMAP_RR)) { - if (state == HAVE_NONE) { - state = HAVE_REAR; - } else if (state == HAVE_SIDE) { - state = HAVE_BOTH; - break; - } - } - } - - if ((state == HAVE_BOTH) || (state == HAVE_NONE)) { - sdl_6chans[4] = SND_CHMAP_UNKNOWN; - sdl_6chans[5] = SND_CHMAP_UNKNOWN; - LOGDEBUG("6channels:unsupported channel map"); - } else if (state == HAVE_REAR) { - sdl_6chans[4] = SND_CHMAP_RL; - sdl_6chans[5] = SND_CHMAP_RR; - LOGDEBUG("6channels:sdl map set to rear"); - } else { // state == HAVE_SIDE - sdl_6chans[4] = SND_CHMAP_SL; - sdl_6chans[5] = SND_CHMAP_SR; - LOGDEBUG("6channels:sdl map set to side"); - } -} -#undef HAVE_NONE -#undef HAVE_REAR -#undef HAVE_SIDE -#undef HAVE_BOTH - -static void swizzle_map_compute_alsa_subscan(const struct ALSA_pcm_cfg_ctx *ctx, int *swizzle_map, unsigned int sdl_pos_idx) -{ - swizzle_map[sdl_pos_idx] = -1; - for (unsigned int alsa_pos_idx = 0; ; alsa_pos_idx++) { - SDL_assert(alsa_pos_idx != ctx->chans_n); // no 0 channels or not found matching position should happen here (actually enforce playback/recording symmetry). - if (ctx->alsa_chmap_installed[alsa_pos_idx] == ctx->sdl_chmap[sdl_pos_idx]) { - LOGDEBUG("swizzle SDL %u <-> alsa %u", sdl_pos_idx,alsa_pos_idx); - swizzle_map[sdl_pos_idx] = (int) alsa_pos_idx; - return; - } - } -} - -// XXX: this must stay playback/recording symetric. -static void swizzle_map_compute(const struct ALSA_pcm_cfg_ctx *ctx, int *swizzle_map, bool *needs_swizzle) -{ - *needs_swizzle = false; - for (unsigned int sdl_pos_idx = 0; sdl_pos_idx != ctx->chans_n; sdl_pos_idx++) { - swizzle_map_compute_alsa_subscan(ctx, swizzle_map, sdl_pos_idx); - if (swizzle_map[sdl_pos_idx] != sdl_pos_idx) { - *needs_swizzle = true; - } - } -} - -#define CHMAP_INSTALLED 0 -#define CHANS_N_NEXT 1 -#define CHMAP_NOT_FOUND 2 -// Should always be a queried alsa channel map unless the queried alsa channel map was of type VAR, -// namely we can program the channel positions directly from the SDL channel map. -static int alsa_chmap_install(struct ALSA_pcm_cfg_ctx *ctx, const unsigned int *chmap) -{ - bool isstack; - snd_pcm_chmap_t *chmap_to_install = (snd_pcm_chmap_t*)SDL_small_alloc(unsigned int, 1 + ctx->chans_n, &isstack); - if (!chmap_to_install) { - return -1; - } - - chmap_to_install->channels = ctx->chans_n; - SDL_memcpy(chmap_to_install->pos, chmap, sizeof (unsigned int) * ctx->chans_n); - - #if SDL_ALSA_DEBUG - char logdebug_chmap_str[128]; - ALSA_snd_pcm_chmap_print(chmap_to_install,sizeof(logdebug_chmap_str),logdebug_chmap_str); - LOGDEBUG("channel map to install:%s",logdebug_chmap_str); - #endif - - int status = ALSA_snd_pcm_set_chmap(ctx->device->hidden->pcm, chmap_to_install); - if (status < 0) { - SDL_SetError("ALSA: failed to install channel map: %s", ALSA_snd_strerror(status)); - return -1; - } - SDL_memcpy(ctx->alsa_chmap_installed, chmap, ctx->chans_n * sizeof (unsigned int)); - - SDL_small_free(chmap_to_install, isstack); - return CHMAP_INSTALLED; -} - -// We restrict the alsa channel maps because in the unordered matches we do only simple accounting. -// In the end, this will handle mostly alsa channel maps with more than one SND_CHMAP_NA position fillers. -static bool alsa_chmap_has_duplicate_position(const struct ALSA_pcm_cfg_ctx *ctx, const unsigned int *pos) -{ - if (ctx->chans_n < 2) {// we need at least 2 positions - LOGDEBUG("channel map:no duplicate"); - return false; - } - - for (unsigned int chan_idx = 1; chan_idx != ctx->chans_n; chan_idx++) { - for (unsigned int seen_idx = 0; seen_idx != chan_idx; seen_idx++) { - if (pos[seen_idx] == pos[chan_idx]) { - LOGDEBUG("channel map:have duplicate"); - return true; - } - } - } - - LOGDEBUG("channel map:no duplicate"); - return false; -} - -static int alsa_chmap_cfg_ordered_fixed_or_paired(struct ALSA_pcm_cfg_ctx *ctx) -{ - for (snd_pcm_chmap_query_t **chmap_query = ctx->chmap_queries; *chmap_query; chmap_query++) { - if ( ((*chmap_query)->map.channels != ctx->chans_n) || - (((*chmap_query)->type != SND_CHMAP_TYPE_FIXED) && ((*chmap_query)->type != SND_CHMAP_TYPE_PAIRED)) ) { - continue; - } - - #if SDL_ALSA_DEBUG - char logdebug_chmap_str[128]; - ALSA_snd_pcm_chmap_print(&(*chmap_query)->map,sizeof(logdebug_chmap_str),logdebug_chmap_str); - LOGDEBUG("channel map:ordered:fixed|paired:%s",logdebug_chmap_str); - #endif - - for (int i = 0; i < ctx->chans_n; i++) { - ctx->sdl_chmap[i] = (unsigned int) sdl_channel_maps[ctx->chans_n][i]; - } - - unsigned int *alsa_chmap = (*chmap_query)->map.pos; - if (ctx->chans_n == 6) { - sdl_6chans_set_rear_or_side_channels_from_alsa_6chans(ctx->sdl_chmap, alsa_chmap); - } - if (alsa_chmap_has_duplicate_position(ctx, alsa_chmap)) { - continue; - } - - for (unsigned int chan_idx = 0; ctx->sdl_chmap[chan_idx] == alsa_chmap[chan_idx]; chan_idx++) { - if (chan_idx == ctx->chans_n) { - return alsa_chmap_install(ctx, alsa_chmap); - } - } - } - return CHMAP_NOT_FOUND; -} - -// Here, the alsa channel positions can be programmed in the alsa frame (cf HDMI). -// If the alsa channel map is VAR, we only check we have the unordered set of channel positions we -// are looking for. -static int alsa_chmap_cfg_ordered_var(struct ALSA_pcm_cfg_ctx *ctx) -{ - for (snd_pcm_chmap_query_t **chmap_query = ctx->chmap_queries; *chmap_query; chmap_query++) { - if (((*chmap_query)->map.channels != ctx->chans_n) || ((*chmap_query)->type != SND_CHMAP_TYPE_VAR)) { - continue; - } - - #if SDL_ALSA_DEBUG - char logdebug_chmap_str[128]; - ALSA_snd_pcm_chmap_print(&(*chmap_query)->map,sizeof(logdebug_chmap_str),logdebug_chmap_str); - LOGDEBUG("channel map:ordered:var:%s",logdebug_chmap_str); - #endif - - for (int i = 0; i < ctx->chans_n; i++) { - ctx->sdl_chmap[i] = (unsigned int) sdl_channel_maps[ctx->chans_n][i]; - } - - unsigned int *alsa_chmap = (*chmap_query)->map.pos; - if (ctx->chans_n == 6) { - sdl_6chans_set_rear_or_side_channels_from_alsa_6chans(ctx->sdl_chmap, alsa_chmap); - } - if (alsa_chmap_has_duplicate_position(ctx, alsa_chmap)) { - continue; - } - - unsigned int pos_matches_n = 0; - for (unsigned int chan_idx = 0; chan_idx != ctx->chans_n; chan_idx++) { - for (unsigned int subscan_chan_idx = 0; subscan_chan_idx != ctx->chans_n; subscan_chan_idx++) { - if (ctx->sdl_chmap[chan_idx] == alsa_chmap[subscan_chan_idx]) { - pos_matches_n++; - break; - } - } - } - - if (pos_matches_n == ctx->chans_n) { - return alsa_chmap_install(ctx, ctx->sdl_chmap); // XXX: we program the SDL chmap here - } - } - - return CHMAP_NOT_FOUND; -} - -static int alsa_chmap_cfg_ordered(struct ALSA_pcm_cfg_ctx *ctx) -{ - const int status = alsa_chmap_cfg_ordered_fixed_or_paired(ctx); - return (status != CHMAP_NOT_FOUND) ? status : alsa_chmap_cfg_ordered_var(ctx); -} - -// In the unordered case, we are just interested to get the same unordered set of alsa channel -// positions than in the SDL channel map since we will swizzle (no duplicate channel position). -static int alsa_chmap_cfg_unordered(struct ALSA_pcm_cfg_ctx *ctx) -{ - for (snd_pcm_chmap_query_t **chmap_query = ctx->chmap_queries; *chmap_query; chmap_query++) { - if ( ((*chmap_query)->map.channels != ctx->chans_n) || - (((*chmap_query)->type != SND_CHMAP_TYPE_FIXED) && ((*chmap_query)->type != SND_CHMAP_TYPE_PAIRED)) ) { - continue; - } - - #if SDL_ALSA_DEBUG - char logdebug_chmap_str[128]; - ALSA_snd_pcm_chmap_print(&(*chmap_query)->map,sizeof(logdebug_chmap_str),logdebug_chmap_str); - LOGDEBUG("channel map:unordered:fixed|paired:%s",logdebug_chmap_str); - #endif - - for (int i = 0; i < ctx->chans_n; i++) { - ctx->sdl_chmap[i] = (unsigned int) sdl_channel_maps[ctx->chans_n][i]; - } - - unsigned int *alsa_chmap = (*chmap_query)->map.pos; - if (ctx->chans_n == 6) { - sdl_6chans_set_rear_or_side_channels_from_alsa_6chans(ctx->sdl_chmap, alsa_chmap); - } - - if (alsa_chmap_has_duplicate_position(ctx, alsa_chmap)) { - continue; - } - - unsigned int pos_matches_n = 0; - for (unsigned int chan_idx = 0; chan_idx != ctx->chans_n; chan_idx++) { - for (unsigned int subscan_chan_idx = 0; subscan_chan_idx != ctx->chans_n; subscan_chan_idx++) { - if (ctx->sdl_chmap[chan_idx] == alsa_chmap[subscan_chan_idx]) { - pos_matches_n++; - break; - } - } - } - - if (pos_matches_n == ctx->chans_n) { - return alsa_chmap_install(ctx, alsa_chmap); - } - } - - return CHMAP_NOT_FOUND; -} - -static int alsa_chmap_cfg(struct ALSA_pcm_cfg_ctx *ctx) -{ - int status; - - ctx->chmap_queries = ALSA_snd_pcm_query_chmaps(ctx->device->hidden->pcm); - if (ctx->chmap_queries == NULL) { - // We couldn't query the channel map, assume no swizzle necessary - LOGDEBUG("couldn't query channel map, swizzling off"); - return CHMAP_INSTALLED; - } - - //---------------------------------------------------------------------------------------------- - status = alsa_chmap_cfg_ordered(ctx); // we prefer first channel maps we don't need to swizzle - if (status == CHMAP_INSTALLED) { - LOGDEBUG("swizzling off"); - return status; - } else if (status != CHMAP_NOT_FOUND) { - return status; // < 0 error code - } - - // Fall-thru - //---------------------------------------------------------------------------------------------- - status = alsa_chmap_cfg_unordered(ctx); // those we will have to swizzle - if (status == CHMAP_INSTALLED) { - LOGDEBUG("swizzling on"); - - bool isstack; - int *swizzle_map = SDL_small_alloc(int, ctx->chans_n, &isstack); - if (!swizzle_map) { - status = -1; - } else { - bool needs_swizzle; - swizzle_map_compute(ctx, swizzle_map, &needs_swizzle); // fine grained swizzle configuration - if (needs_swizzle) { - // let SDL's swizzler handle this one. - ctx->device->chmap = SDL_ChannelMapDup(swizzle_map, ctx->chans_n); - if (!ctx->device->chmap) { - status = -1; - } - } - SDL_small_free(swizzle_map, isstack); - } - } - - if (status == CHMAP_NOT_FOUND) { - return CHANS_N_NEXT; - } - - return status; // < 0 error code -} - -#define CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N 0 // target more hardware pressure -#define CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N 1 // target less hardware pressure -#define CHANS_N_CONFIGURED 0 -#define CHANS_N_NOT_CONFIGURED 1 -static int ALSA_pcm_cfg_hw_chans_n_scan(struct ALSA_pcm_cfg_ctx *ctx, unsigned int mode) -{ - unsigned int target_chans_n = ctx->device->spec.channels; // we start at what was specified - if (mode == CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N) { - target_chans_n--; - } - while (true) { - if (mode == CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N) { - if (target_chans_n > SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX) { - return CHANS_N_NOT_CONFIGURED; - } - // else: CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N - } else if (target_chans_n == 0) { - return CHANS_N_NOT_CONFIGURED; - } - - LOGDEBUG("target chans_n is %u", target_chans_n); - - int status = ALSA_snd_pcm_hw_params_any(ctx->device->hidden->pcm, ctx->hwparams); - if (status < 0) { - SDL_SetError("ALSA: Couldn't get hardware config: %s", ALSA_snd_strerror(status)); - return -1; - } - // SDL only uses interleaved sample output - status = ALSA_snd_pcm_hw_params_set_access(ctx->device->hidden->pcm, ctx->hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); - if (status < 0) { - SDL_SetError("ALSA: Couldn't set interleaved access: %s", ALSA_snd_strerror(status)); - return -1; - } - // Try for a closest match on audio format - snd_pcm_format_t alsa_format = 0; - const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(ctx->device->spec.format); - ctx->matched_sdl_format = 0; - while ((ctx->matched_sdl_format = *(closefmts++)) != 0) { - // XXX: we are forcing the same endianness, namely we won't need byte swapping upon - // writing/reading to/from the SDL audio buffer. - switch (ctx->matched_sdl_format) { - case SDL_AUDIO_U8: - alsa_format = SND_PCM_FORMAT_U8; - break; - case SDL_AUDIO_S8: - alsa_format = SND_PCM_FORMAT_S8; - break; - case SDL_AUDIO_S16LE: - alsa_format = SND_PCM_FORMAT_S16_LE; - break; - case SDL_AUDIO_S16BE: - alsa_format = SND_PCM_FORMAT_S16_BE; - break; - case SDL_AUDIO_S32LE: - alsa_format = SND_PCM_FORMAT_S32_LE; - break; - case SDL_AUDIO_S32BE: - alsa_format = SND_PCM_FORMAT_S32_BE; - break; - case SDL_AUDIO_F32LE: - alsa_format = SND_PCM_FORMAT_FLOAT_LE; - break; - case SDL_AUDIO_F32BE: - alsa_format = SND_PCM_FORMAT_FLOAT_BE; - break; - default: - continue; - } - if (ALSA_snd_pcm_hw_params_set_format(ctx->device->hidden->pcm, ctx->hwparams, alsa_format) >= 0) { - break; - } - } - if (ctx->matched_sdl_format == 0) { - SDL_SetError("ALSA: Unsupported audio format: %s", ALSA_snd_strerror(status)); - return -1; - } - // let alsa approximate the number of channels - ctx->chans_n = target_chans_n; - status = ALSA_snd_pcm_hw_params_set_channels_near(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->chans_n)); - if (status < 0) { - SDL_SetError("ALSA: Couldn't set audio channels: %s", ALSA_snd_strerror(status)); - return -1; - } - // let alsa approximate the audio rate - ctx->rate = ctx->device->spec.freq; - status = ALSA_snd_pcm_hw_params_set_rate_near(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->rate), NULL); - if (status < 0) { - SDL_SetError("ALSA: Couldn't set audio frequency: %s", ALSA_snd_strerror(status)); - return -1; - } - // let approximate the period size to the requested buffer size - ctx->persize = ctx->device->sample_frames; - status = ALSA_snd_pcm_hw_params_set_period_size_near(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->persize), NULL); - if (status < 0) { - SDL_SetError("ALSA: Couldn't set the period size: %s", ALSA_snd_strerror(status)); - return -1; - } - // let approximate the minimun number of periods per buffer (we target a double buffer) - ctx->periods = 2; - status = ALSA_snd_pcm_hw_params_set_periods_min(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->periods), NULL); - if (status < 0) { - SDL_SetError("ALSA: Couldn't set the minimum number of periods per buffer: %s", ALSA_snd_strerror(status)); - return -1; - } - // restrict the number of periods per buffer to an approximation of the approximated minimum - // number of periods per buffer done right above - status = ALSA_snd_pcm_hw_params_set_periods_first(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->periods), NULL); - if (status < 0) { - SDL_SetError("ALSA: Couldn't set the number of periods per buffer: %s", ALSA_snd_strerror(status)); - return -1; - } - // install the hw parameters - status = ALSA_snd_pcm_hw_params(ctx->device->hidden->pcm, ctx->hwparams); - if (status < 0) { - SDL_SetError("ALSA: installation of hardware parameter failed: %s", ALSA_snd_strerror(status)); - return -1; - } - //========================================================================================== - // Here the alsa pcm is in SND_PCM_STATE_PREPARED state, let's figure out a good fit for - // SDL channel map, it may request to change the target number of channels though. - status = alsa_chmap_cfg(ctx); - if (status < 0) { - return status; // we forward the SDL error - } else if (status == CHMAP_INSTALLED) { - return CHANS_N_CONFIGURED; // we are finished here - } - - // status == CHANS_N_NEXT - ALSA_snd_pcm_free_chmaps(ctx->chmap_queries); - ALSA_snd_pcm_hw_free(ctx->device->hidden->pcm); // uninstall those hw params - - if (mode == CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N) { - target_chans_n++; - } else { // CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N - target_chans_n--; - } - } - - SDL_assert(!"Shouldn't reach this code."); - return CHANS_N_NOT_CONFIGURED; -} -#undef CHMAP_INSTALLED -#undef CHANS_N_NEXT -#undef CHMAP_NOT_FOUND - -static bool ALSA_pcm_cfg_hw(struct ALSA_pcm_cfg_ctx *ctx) -{ - LOGDEBUG("target chans_n, equal or above requested chans_n mode"); - int status = ALSA_pcm_cfg_hw_chans_n_scan(ctx, CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N); - if (status < 0) { // something went too wrong - return false; - } else if (status == CHANS_N_CONFIGURED) { - return true; - } - - // Here, status == CHANS_N_NOT_CONFIGURED - LOGDEBUG("target chans_n, below requested chans_n mode"); - status = ALSA_pcm_cfg_hw_chans_n_scan(ctx, CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N); - if (status < 0) { // something went too wrong - return false; - } else if (status == CHANS_N_CONFIGURED) { - return true; - } - - // Here, status == CHANS_N_NOT_CONFIGURED - return SDL_SetError("ALSA: Coudn't configure targetting any SDL supported channel number"); -} -#undef CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N -#undef CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N -#undef CHANS_N_CONFIGURED -#undef CHANS_N_NOT_CONFIGURED - - -static bool ALSA_pcm_cfg_sw(struct ALSA_pcm_cfg_ctx *ctx) -{ - int status; - - status = ALSA_snd_pcm_sw_params_current(ctx->device->hidden->pcm, ctx->swparams); - if (status < 0) { - return SDL_SetError("ALSA: Couldn't get software config: %s", ALSA_snd_strerror(status)); - } - - status = ALSA_snd_pcm_sw_params_set_avail_min(ctx->device->hidden->pcm, ctx->swparams, ctx->persize); // will become device->sample_frames if the alsa pcm configuration is successful - if (status < 0) { - return SDL_SetError("Couldn't set minimum available samples: %s", ALSA_snd_strerror(status)); - } - - status = ALSA_snd_pcm_sw_params_set_start_threshold(ctx->device->hidden->pcm, ctx->swparams, 1); - if (status < 0) { - return SDL_SetError("ALSA: Couldn't set start threshold: %s", ALSA_snd_strerror(status)); - } - status = ALSA_snd_pcm_sw_params(ctx->device->hidden->pcm, ctx->swparams); - if (status < 0) { - return SDL_SetError("Couldn't set software audio parameters: %s", ALSA_snd_strerror(status)); - } - return true; -} - - -static bool ALSA_OpenDevice(SDL_AudioDevice *device) -{ - const bool recording = device->recording; - struct ALSA_pcm_cfg_ctx cfg_ctx; // used to track everything here - char *pcm_str; - int status = 0; - - //device->spec.channels = 8; - //SDL_SetLogPriority(SDL_LOG_CATEGORY_AUDIO, SDL_LOG_PRIORITY_VERBOSE); - LOGDEBUG("channels requested %u",device->spec.channels); - // XXX: We do not use the SDL internal swizzler yet. - device->chmap = NULL; - - SDL_zero(cfg_ctx); - cfg_ctx.device = device; - - // Initialize all variables that we clean on shutdown - cfg_ctx.device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*cfg_ctx.device->hidden)); - if (!cfg_ctx.device->hidden) { - return false; - } - - // Open the audio device - pcm_str = get_pcm_str(cfg_ctx.device->handle); - if (pcm_str == NULL) { - goto err_free_device_hidden; - } - LOGDEBUG("PCM open '%s'", pcm_str); - status = ALSA_snd_pcm_open(&cfg_ctx.device->hidden->pcm, - pcm_str, - recording ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, - SND_PCM_NONBLOCK); - SDL_free(pcm_str); - if (status < 0) { - SDL_SetError("ALSA: Couldn't open audio device: %s", ALSA_snd_strerror(status)); - goto err_free_device_hidden; - } - - // Now we need to configure the opened pcm as close as possible from the requested parameters we - // can reasonably deal with (and that could change) - snd_pcm_hw_params_alloca(&(cfg_ctx.hwparams)); - snd_pcm_sw_params_alloca(&(cfg_ctx.swparams)); - - if (!ALSA_pcm_cfg_hw(&cfg_ctx)) { // alsa pcm "hardware" part of the pcm - goto err_close_pcm; - } - - // from here, we get only the alsa chmap queries in cfg_ctx to explicitely clean, hwparams is - // uninstalled upon pcm closing - - // This is useful for debugging - #if SDL_ALSA_DEBUG - snd_pcm_uframes_t bufsize; - ALSA_snd_pcm_hw_params_get_buffer_size(cfg_ctx.hwparams, &bufsize); - SDL_LogError(SDL_LOG_CATEGORY_AUDIO, - "ALSA: period size = %ld, periods = %u, buffer size = %lu", - cfg_ctx.persize, cfg_ctx.periods, bufsize); - #endif - - if (!ALSA_pcm_cfg_sw(&cfg_ctx)) { // alsa pcm "software" part of the pcm - goto err_cleanup_ctx; - } - - // Now we can update the following parameters in the spec: - cfg_ctx.device->spec.format = cfg_ctx.matched_sdl_format; - cfg_ctx.device->spec.channels = cfg_ctx.chans_n; - cfg_ctx.device->spec.freq = cfg_ctx.rate; - cfg_ctx.device->sample_frames = cfg_ctx.persize; - // Calculate the final parameters for this audio specification - SDL_UpdatedAudioDeviceFormat(cfg_ctx.device); - - // Allocate mixing buffer - if (!recording) { - cfg_ctx.device->hidden->mixbuf = (Uint8 *)SDL_malloc(cfg_ctx.device->buffer_size); - if (cfg_ctx.device->hidden->mixbuf == NULL) { - goto err_cleanup_ctx; - } - SDL_memset(cfg_ctx.device->hidden->mixbuf, cfg_ctx.device->silence_value, cfg_ctx.device->buffer_size); - } - -#if !SDL_ALSA_NON_BLOCKING - if (!recording) { - ALSA_snd_pcm_nonblock(cfg_ctx.device->hidden->pcm, 0); - } -#endif - ALSA_snd_pcm_start(cfg_ctx.device->hidden->pcm); - return true; // We're ready to rock and roll. :-) - -err_cleanup_ctx: - ALSA_snd_pcm_free_chmaps(cfg_ctx.chmap_queries); -err_close_pcm: - ALSA_snd_pcm_close(cfg_ctx.device->hidden->pcm); -err_free_device_hidden: - SDL_free(cfg_ctx.device->hidden); - cfg_ctx.device->hidden = NULL; - return false; -} - -static ALSA_Device *hotplug_devices = NULL; - -static int hotplug_device_process(snd_ctl_t *ctl, snd_ctl_card_info_t *ctl_card_info, int dev_idx, - snd_pcm_stream_t direction, ALSA_Device **unseen, ALSA_Device **seen) -{ - unsigned int subdevs_n = 1; // we have at least one subdevice (substream since the direction is a stream in alsa terminology) - unsigned int subdev_idx = 0; - const bool recording = direction == SND_PCM_STREAM_CAPTURE ? true : false; // used for the unicity of the device - bool isstack; - snd_pcm_info_t *pcm_info = (snd_pcm_info_t*)SDL_small_alloc(Uint8, ALSA_snd_pcm_info_sizeof(), &isstack); - SDL_memset(pcm_info, 0, ALSA_snd_pcm_info_sizeof()); - - while (true) { - ALSA_snd_pcm_info_set_stream(pcm_info, direction); - ALSA_snd_pcm_info_set_device(pcm_info, dev_idx); - ALSA_snd_pcm_info_set_subdevice(pcm_info, subdev_idx); // we have at least one subdevice (substream) of index 0 - - const int r = ALSA_snd_ctl_pcm_info(ctl, pcm_info); - if (r < 0) { - SDL_small_free(pcm_info, isstack); - // first call to ALSA_snd_ctl_pcm_info - if (subdev_idx == 0 && r == -ENOENT) { // no such direction/stream for this device - return 0; - } - return -1; - } - - if (subdev_idx == 0) { - subdevs_n = ALSA_snd_pcm_info_get_subdevices_count(pcm_info); - } - - // building the unseen list scanning the list of hotplug devices, if it is already there - // using the id, move it to the seen list. - ALSA_Device *unseen_prev_adev = NULL; - ALSA_Device *adev; - for (adev = *unseen; adev; adev = adev->next) { - // the unicity key is the couple (id,recording) - if ((SDL_strcmp(adev->id, ALSA_snd_ctl_card_info_get_id(ctl_card_info)) == 0) && (adev->recording == recording)) { - // unchain from unseen - if (*unseen == adev) { // head - *unseen = adev->next; - } else { - unseen_prev_adev->next = adev->next; - } - // chain to seen - adev->next = *seen; - *seen = adev; - break; - } - unseen_prev_adev = adev; - } - - if (adev == NULL) { // newly seen device - adev = SDL_calloc(1, sizeof(*adev)); - if (adev == NULL) { - SDL_small_free(pcm_info, isstack); - return -1; - } - - adev->id = SDL_strdup(ALSA_snd_ctl_card_info_get_id(ctl_card_info)); - if (adev->id == NULL) { - SDL_small_free(pcm_info, isstack); - SDL_free(adev); - return -1; - } - - if (SDL_asprintf(&adev->name, "%s:%s", ALSA_snd_ctl_card_info_get_name(ctl_card_info), ALSA_snd_pcm_info_get_name(pcm_info)) == -1) { - SDL_small_free(pcm_info, isstack); - SDL_free(adev->id); - SDL_free(adev); - return -1; - } - - if (direction == SND_PCM_STREAM_CAPTURE) { - adev->recording = true; - } else { - adev->recording = false; - } - - if (SDL_AddAudioDevice(recording, adev->name, NULL, adev) == NULL) { - SDL_small_free(pcm_info, isstack); - SDL_free(adev->id); - SDL_free(adev->name); - SDL_free(adev); - return -1; - } - - adev->next = *seen; - *seen = adev; - } - - subdev_idx++; - if (subdev_idx == subdevs_n) { - SDL_small_free(pcm_info, isstack); - return 0; - } - - SDL_memset(pcm_info, 0, ALSA_snd_pcm_info_sizeof()); - } - - SDL_small_free(pcm_info, isstack); - SDL_assert(!"Shouldn't reach this code"); - return -1; -} - -static void ALSA_HotplugIteration(bool *has_default_output, bool *has_default_recording) -{ - if (has_default_output != NULL) { - *has_default_output = true; - } - - if (has_default_recording != NULL) { - *has_default_recording = true; - } - - bool isstack; - snd_ctl_card_info_t *ctl_card_info = (snd_ctl_card_info_t *) SDL_small_alloc(Uint8, ALSA_snd_ctl_card_info_sizeof(), &isstack); - if (!ctl_card_info) { - return; // oh well. - } - - SDL_memset(ctl_card_info, 0, ALSA_snd_ctl_card_info_sizeof()); - - snd_ctl_t *ctl = NULL; - ALSA_Device *unseen = hotplug_devices; - ALSA_Device *seen = NULL; - int card_idx = -1; - while (true) { - int r = ALSA_snd_card_next(&card_idx); - if (r < 0) { - goto failed; - } else if (card_idx == -1) { - break; - } - - char ctl_name[64]; - SDL_snprintf(ctl_name, sizeof (ctl_name), "%s%d", ALSA_device_prefix, card_idx); // card_idx >= 0 - LOGDEBUG("hotplug ctl_name = '%s'", ctl_name); - - r = ALSA_snd_ctl_open(&ctl, ctl_name, 0); - if (r < 0) { - continue; - } - - r = ALSA_snd_ctl_card_info(ctl, ctl_card_info); - if (r < 0) { - goto failed; - } - - int dev_idx = -1; - while (true) { - r = ALSA_snd_ctl_pcm_next_device(ctl, &dev_idx); - if (r < 0) { - goto failed; - } else if (dev_idx == -1) { - break; - } - - r = hotplug_device_process(ctl, ctl_card_info, dev_idx, SND_PCM_STREAM_PLAYBACK, &unseen, &seen); - if (r < 0) { - goto failed; - } - - r = hotplug_device_process(ctl, ctl_card_info, dev_idx, SND_PCM_STREAM_CAPTURE, &unseen, &seen); - if (r < 0) { - goto failed; - } - } - ALSA_snd_ctl_close(ctl); - ALSA_snd_ctl_card_info_clear(ctl_card_info); - } - - // remove only the unseen devices - while (unseen) { - SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle(unseen)); - SDL_free(unseen->name); - SDL_free(unseen->id); - ALSA_Device *next = unseen->next; - SDL_free(unseen); - unseen = next; - } - - // update hotplug devices to be the seen devices - hotplug_devices = seen; - SDL_small_free(ctl_card_info, isstack); - return; - -failed: - if (ctl) { - ALSA_snd_ctl_close(ctl); - } - - // remove the unseen - while (unseen) { - SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle(unseen)); - SDL_free(unseen->name); - SDL_free(unseen->id); - ALSA_Device *next = unseen->next; - SDL_free(unseen); - unseen = next; - } - - // remove the seen - while (seen) { - SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle(seen)); - SDL_free(seen->name); - SDL_free(seen->id); - ALSA_Device *next = seen->next; - SDL_free(seen); - seen = next; - } - - hotplug_devices = NULL; - SDL_small_free(ctl_card_info, isstack); -} - - -#if SDL_ALSA_HOTPLUG_THREAD -static SDL_AtomicInt ALSA_hotplug_shutdown; -static SDL_Thread *ALSA_hotplug_thread; - -static int SDLCALL ALSA_HotplugThread(void *arg) -{ - SDL_SetCurrentThreadPriority(SDL_THREAD_PRIORITY_LOW); - - while (!SDL_GetAtomicInt(&ALSA_hotplug_shutdown)) { - // Block awhile before checking again, unless we're told to stop. - const Uint64 ticks = SDL_GetTicks() + 5000; - while (!SDL_GetAtomicInt(&ALSA_hotplug_shutdown) && (SDL_GetTicks() < ticks)) { - SDL_Delay(100); - } - - ALSA_HotplugIteration(NULL, NULL); // run the check. - } - - return 0; -} -#endif - -static void ALSA_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) -{ - ALSA_guess_device_prefix(); - - // ALSA doesn't have a concept of a changeable default device, afaik, so we expose a generic default - // device here. It's the best we can do at this level. - bool has_default_playback = false, has_default_recording = false; - ALSA_HotplugIteration(&has_default_playback, &has_default_recording); // run once now before a thread continues to check. - if (has_default_playback) { - *default_playback = SDL_AddAudioDevice(/*recording=*/false, "ALSA default playback device", NULL, (void*)&default_playback_handle); - } - if (has_default_recording) { - *default_recording = SDL_AddAudioDevice(/*recording=*/true, "ALSA default recording device", NULL, (void*)&default_recording_handle); - } - -#if SDL_ALSA_HOTPLUG_THREAD - SDL_SetAtomicInt(&ALSA_hotplug_shutdown, 0); - ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", NULL); - // if the thread doesn't spin, oh well, you just don't get further hotplug events. -#endif -} - -static void ALSA_DeinitializeStart(void) -{ - ALSA_Device *dev; - ALSA_Device *next; - -#if SDL_ALSA_HOTPLUG_THREAD - if (ALSA_hotplug_thread) { - SDL_SetAtomicInt(&ALSA_hotplug_shutdown, 1); - SDL_WaitThread(ALSA_hotplug_thread, NULL); - ALSA_hotplug_thread = NULL; - } -#endif - - // Shutting down! Clean up any data we've gathered. - for (dev = hotplug_devices; dev; dev = next) { - //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA: at shutdown, removing %s device '%s'", dev->recording ? "recording" : "playback", dev->name); - next = dev->next; - SDL_free(dev->name); - SDL_free(dev); - } - hotplug_devices = NULL; -} - -static void ALSA_Deinitialize(void) -{ - UnloadALSALibrary(); -} - -static bool ALSA_Init(SDL_AudioDriverImpl *impl) -{ - if (!LoadALSALibrary()) { - return false; - } - - impl->DetectDevices = ALSA_DetectDevices; - impl->OpenDevice = ALSA_OpenDevice; - impl->WaitDevice = ALSA_WaitDevice; - impl->GetDeviceBuf = ALSA_GetDeviceBuf; - impl->PlayDevice = ALSA_PlayDevice; - impl->CloseDevice = ALSA_CloseDevice; - impl->DeinitializeStart = ALSA_DeinitializeStart; - impl->Deinitialize = ALSA_Deinitialize; - impl->WaitRecordingDevice = ALSA_WaitDevice; - impl->RecordDevice = ALSA_RecordDevice; - impl->FlushRecording = ALSA_FlushRecording; - - impl->HasRecordingSupport = true; - - return true; -} - -AudioBootStrap ALSA_bootstrap = { - "alsa", "ALSA PCM audio", ALSA_Init, false, false -}; - -#endif // SDL_AUDIO_DRIVER_ALSA diff --git a/contrib/SDL-3.2.8/src/audio/alsa/SDL_alsa_audio.h b/contrib/SDL-3.2.8/src/audio/alsa/SDL_alsa_audio.h deleted file mode 100644 index c68dda8..0000000 --- a/contrib/SDL-3.2.8/src/audio/alsa/SDL_alsa_audio.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_ALSA_audio_h_ -#define SDL_ALSA_audio_h_ - -#include - -#include "../SDL_sysaudio.h" - -#define SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX 8 -#define SDL_AUDIO_ALSA__SDL_CHMAPS_N 9 // from 0 channels to 8 channels -struct SDL_PrivateAudioData -{ - // The audio device handle - snd_pcm_t *pcm; - - // Raw mixing buffer - Uint8 *mixbuf; -}; - -#endif // SDL_ALSA_audio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/coreaudio/SDL_coreaudio.h b/contrib/SDL-3.2.8/src/audio/coreaudio/SDL_coreaudio.h deleted file mode 100644 index f117c09..0000000 --- a/contrib/SDL-3.2.8/src/audio/coreaudio/SDL_coreaudio.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_coreaudio_h_ -#define SDL_coreaudio_h_ - -#include "../SDL_sysaudio.h" - -#ifndef SDL_PLATFORM_IOS -#define MACOSX_COREAUDIO -#endif - -#ifdef MACOSX_COREAUDIO -#include -#else -#import -#import -#endif - -#include -#include - -// Things named "Master" were renamed to "Main" in macOS 12.0's SDK. -#ifdef MACOSX_COREAUDIO -#include -#ifndef MAC_OS_VERSION_12_0 -#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster -#endif -#endif - -struct SDL_PrivateAudioData -{ - SDL_Thread *thread; - AudioQueueRef audioQueue; - int numAudioBuffers; - AudioQueueBufferRef *audioBuffer; - AudioQueueBufferRef current_buffer; - AudioStreamBasicDescription strdesc; - SDL_Semaphore *ready_semaphore; - char *thread_error; -#ifdef MACOSX_COREAUDIO - AudioDeviceID deviceID; -#else - bool interrupted; - CFTypeRef interruption_listener; -#endif -}; - -#endif // SDL_coreaudio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/coreaudio/SDL_coreaudio.m b/contrib/SDL-3.2.8/src/audio/coreaudio/SDL_coreaudio.m deleted file mode 100644 index 57b19c7..0000000 --- a/contrib/SDL-3.2.8/src/audio/coreaudio/SDL_coreaudio.m +++ /dev/null @@ -1,1040 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_COREAUDIO - -#include "../SDL_sysaudio.h" -#include "SDL_coreaudio.h" -#include "../../thread/SDL_systhread.h" - -#define DEBUG_COREAUDIO 0 - -#if DEBUG_COREAUDIO - #define CHECK_RESULT(msg) \ - if (result != noErr) { \ - SDL_Log("COREAUDIO: Got error %d from '%s'!", (int)result, msg); \ - return SDL_SetError("CoreAudio error (%s): %d", msg, (int)result); \ - } -#else - #define CHECK_RESULT(msg) \ - if (result != noErr) { \ - return SDL_SetError("CoreAudio error (%s): %d", msg, (int)result); \ - } -#endif - -#ifdef MACOSX_COREAUDIO -// Apparently AudioDeviceID values might not be unique, so we wrap it in an SDL_malloc()'d pointer -// to make it so. Use FindCoreAudioDeviceByHandle to deal with this redirection, if you need to -// map from an AudioDeviceID to a SDL handle. -typedef struct SDLCoreAudioHandle -{ - AudioDeviceID devid; - bool recording; -} SDLCoreAudioHandle; - -static bool TestCoreAudioDeviceHandleCallback(SDL_AudioDevice *device, void *handle) -{ - const SDLCoreAudioHandle *a = (const SDLCoreAudioHandle *) device->handle; - const SDLCoreAudioHandle *b = (const SDLCoreAudioHandle *) handle; - return (a->devid == b->devid) && (!!a->recording == !!b->recording); -} - -static SDL_AudioDevice *FindCoreAudioDeviceByHandle(const AudioDeviceID devid, const bool recording) -{ - SDLCoreAudioHandle handle = { devid, recording }; - return SDL_FindPhysicalAudioDeviceByCallback(TestCoreAudioDeviceHandleCallback, &handle); -} - -static const AudioObjectPropertyAddress devlist_address = { - kAudioHardwarePropertyDevices, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMain -}; - -static const AudioObjectPropertyAddress default_playback_device_address = { - kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMain -}; - -static const AudioObjectPropertyAddress default_recording_device_address = { - kAudioHardwarePropertyDefaultInputDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMain -}; - -static const AudioObjectPropertyAddress alive_address = { - kAudioDevicePropertyDeviceIsAlive, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMain -}; - - -static OSStatus DeviceAliveNotification(AudioObjectID devid, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *)data; - SDL_assert(((const SDLCoreAudioHandle *) device->handle)->devid == devid); - - UInt32 alive = 1; - UInt32 size = sizeof(alive); - const OSStatus error = AudioObjectGetPropertyData(devid, addrs, 0, NULL, &size, &alive); - - bool dead = false; - if (error == kAudioHardwareBadDeviceError) { - dead = true; // device was unplugged. - } else if ((error == kAudioHardwareNoError) && (!alive)) { - dead = true; // device died in some other way. - } - - if (dead) { - #if DEBUG_COREAUDIO - SDL_Log("COREAUDIO: device '%s' is lost!", device->name); - #endif - SDL_AudioDeviceDisconnected(device); - } - - return noErr; -} - -static void COREAUDIO_FreeDeviceHandle(SDL_AudioDevice *device) -{ - SDLCoreAudioHandle *handle = (SDLCoreAudioHandle *) device->handle; - AudioObjectRemovePropertyListener(handle->devid, &alive_address, DeviceAliveNotification, device); - SDL_free(handle); -} - -// This only _adds_ new devices. Removal is handled by devices triggering kAudioDevicePropertyDeviceIsAlive property changes. -static void RefreshPhysicalDevices(void) -{ - UInt32 size = 0; - AudioDeviceID *devs = NULL; - bool isstack; - - if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &devlist_address, 0, NULL, &size) != kAudioHardwareNoError) { - return; - } else if ((devs = (AudioDeviceID *) SDL_small_alloc(Uint8, size, &isstack)) == NULL) { - return; - } else if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &devlist_address, 0, NULL, &size, devs) != kAudioHardwareNoError) { - SDL_small_free(devs, isstack); - return; - } - - const UInt32 total_devices = (UInt32) (size / sizeof(AudioDeviceID)); - for (UInt32 i = 0; i < total_devices; i++) { - if (FindCoreAudioDeviceByHandle(devs[i], true) || FindCoreAudioDeviceByHandle(devs[i], false)) { - devs[i] = 0; // The system and SDL both agree it's already here, don't check it again. - } - } - - // any non-zero items remaining in `devs` are new devices to be added. - for (int recording = 0; recording < 2; recording++) { - const AudioObjectPropertyAddress addr = { - kAudioDevicePropertyStreamConfiguration, - recording ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMain - }; - const AudioObjectPropertyAddress nameaddr = { - kAudioObjectPropertyName, - recording ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMain - }; - const AudioObjectPropertyAddress freqaddr = { - kAudioDevicePropertyNominalSampleRate, - recording ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMain - }; - - for (UInt32 i = 0; i < total_devices; i++) { - const AudioDeviceID dev = devs[i]; - if (!dev) { - continue; // already added. - } - - AudioBufferList *buflist = NULL; - double sampleRate = 0; - - if (AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size) != noErr) { - continue; - } else if ((buflist = (AudioBufferList *)SDL_malloc(size)) == NULL) { - continue; - } - - OSStatus result = AudioObjectGetPropertyData(dev, &addr, 0, NULL, &size, buflist); - - SDL_AudioSpec spec; - SDL_zero(spec); - if (result == noErr) { - for (Uint32 j = 0; j < buflist->mNumberBuffers; j++) { - spec.channels += buflist->mBuffers[j].mNumberChannels; - } - } - - SDL_free(buflist); - - if (spec.channels == 0) { - continue; - } - - size = sizeof(sampleRate); - if (AudioObjectGetPropertyData(dev, &freqaddr, 0, NULL, &size, &sampleRate) == noErr) { - spec.freq = (int)sampleRate; - } - - CFStringRef cfstr = NULL; - size = sizeof(CFStringRef); - if (AudioObjectGetPropertyData(dev, &nameaddr, 0, NULL, &size, &cfstr) != kAudioHardwareNoError) { - continue; - } - - CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr), kCFStringEncodingUTF8); - char *name = (char *)SDL_malloc(len + 1); - bool usable = ((name != NULL) && (CFStringGetCString(cfstr, name, len + 1, kCFStringEncodingUTF8))); - - CFRelease(cfstr); - - if (usable) { - // Some devices have whitespace at the end...trim it. - len = (CFIndex) SDL_strlen(name); - while ((len > 0) && (name[len - 1] == ' ')) { - len--; - } - usable = (len > 0); - } - - if (usable) { - name[len] = '\0'; - - #if DEBUG_COREAUDIO - SDL_Log("COREAUDIO: Found %s device #%d: '%s' (devid %d)", ((recording) ? "recording" : "playback"), (int)i, name, (int)dev); - #endif - SDLCoreAudioHandle *newhandle = (SDLCoreAudioHandle *) SDL_calloc(1, sizeof (*newhandle)); - if (newhandle) { - newhandle->devid = dev; - newhandle->recording = recording ? true : false; - SDL_AudioDevice *device = SDL_AddAudioDevice(newhandle->recording, name, &spec, newhandle); - if (device) { - AudioObjectAddPropertyListener(dev, &alive_address, DeviceAliveNotification, device); - } else { - SDL_free(newhandle); - } - } - } - SDL_free(name); // SDL_AddAudioDevice() would have copied the string. - } - } - - SDL_small_free(devs, isstack); -} - -// this is called when the system's list of available audio devices changes. -static OSStatus DeviceListChangedNotification(AudioObjectID systemObj, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data) -{ - RefreshPhysicalDevices(); - return noErr; -} - -static OSStatus DefaultAudioDeviceChangedNotification(const bool recording, AudioObjectID inObjectID, const AudioObjectPropertyAddress *addr) -{ - AudioDeviceID devid; - UInt32 size = sizeof(devid); - if (AudioObjectGetPropertyData(inObjectID, addr, 0, NULL, &size, &devid) == noErr) { - SDL_DefaultAudioDeviceChanged(FindCoreAudioDeviceByHandle(devid, recording)); - } - return noErr; -} - -static OSStatus DefaultPlaybackDeviceChangedNotification(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inUserData) -{ - #if DEBUG_COREAUDIO - SDL_Log("COREAUDIO: default playback device changed!"); - #endif - SDL_assert(inNumberAddresses == 1); - return DefaultAudioDeviceChangedNotification(false, inObjectID, inAddresses); -} - -static OSStatus DefaultRecordingDeviceChangedNotification(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inUserData) -{ - #if DEBUG_COREAUDIO - SDL_Log("COREAUDIO: default recording device changed!"); - #endif - SDL_assert(inNumberAddresses == 1); - return DefaultAudioDeviceChangedNotification(true, inObjectID, inAddresses); -} - -static void COREAUDIO_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) -{ - RefreshPhysicalDevices(); - - AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, DeviceListChangedNotification, NULL); - - // Get the Device ID - UInt32 size; - AudioDeviceID devid; - - size = sizeof(AudioDeviceID); - if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &default_playback_device_address, 0, NULL, &size, &devid) == noErr) { - SDL_AudioDevice *device = FindCoreAudioDeviceByHandle(devid, false); - if (device) { - *default_playback = device; - } - } - AudioObjectAddPropertyListener(kAudioObjectSystemObject, &default_playback_device_address, DefaultPlaybackDeviceChangedNotification, NULL); - - size = sizeof(AudioDeviceID); - if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &default_recording_device_address, 0, NULL, &size, &devid) == noErr) { - SDL_AudioDevice *device = FindCoreAudioDeviceByHandle(devid, true); - if (device) { - *default_recording = device; - } - } - AudioObjectAddPropertyListener(kAudioObjectSystemObject, &default_recording_device_address, DefaultRecordingDeviceChangedNotification, NULL); -} - -#else // iOS-specific section follows. - -static bool session_active = false; - -static bool PauseOneAudioDevice(SDL_AudioDevice *device, void *userdata) -{ - if (device->hidden && device->hidden->audioQueue && !device->hidden->interrupted) { - AudioQueuePause(device->hidden->audioQueue); - } - return false; // keep enumerating devices until we've paused them all. -} - -static void PauseAudioDevices(void) -{ - (void) SDL_FindPhysicalAudioDeviceByCallback(PauseOneAudioDevice, NULL); -} - -static bool ResumeOneAudioDevice(SDL_AudioDevice *device, void *userdata) -{ - if (device->hidden && device->hidden->audioQueue && !device->hidden->interrupted) { - AudioQueueStart(device->hidden->audioQueue, NULL); - } - return false; // keep enumerating devices until we've resumed them all. -} - -static void ResumeAudioDevices(void) -{ - (void) SDL_FindPhysicalAudioDeviceByCallback(ResumeOneAudioDevice, NULL); -} - -static void InterruptionBegin(SDL_AudioDevice *device) -{ - if (device != NULL && device->hidden->audioQueue != NULL) { - device->hidden->interrupted = true; - AudioQueuePause(device->hidden->audioQueue); - } -} - -static void InterruptionEnd(SDL_AudioDevice *device) -{ - if (device != NULL && device->hidden != NULL && device->hidden->audioQueue != NULL && device->hidden->interrupted && AudioQueueStart(device->hidden->audioQueue, NULL) == AVAudioSessionErrorCodeNone) { - device->hidden->interrupted = false; - } -} - -@interface SDLInterruptionListener : NSObject - -@property(nonatomic, assign) SDL_AudioDevice *device; - -@end - -@implementation SDLInterruptionListener - -- (void)audioSessionInterruption:(NSNotification *)note -{ - @synchronized(self) { - NSNumber *type = note.userInfo[AVAudioSessionInterruptionTypeKey]; - if (type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) { - InterruptionBegin(self.device); - } else { - InterruptionEnd(self.device); - } - } -} - -- (void)applicationBecameActive:(NSNotification *)note -{ - @synchronized(self) { - InterruptionEnd(self.device); - } -} - -@end - -typedef struct -{ - int playback; - int recording; -} CountOpenAudioDevicesData; - -static bool CountOpenAudioDevices(SDL_AudioDevice *device, void *userdata) -{ - CountOpenAudioDevicesData *data = (CountOpenAudioDevicesData *) userdata; - if (device->hidden != NULL) { // assume it's open if hidden != NULL - if (device->recording) { - data->recording++; - } else { - data->playback++; - } - } - return false; // keep enumerating until all devices have been checked. -} - -static bool UpdateAudioSession(SDL_AudioDevice *device, bool open, bool allow_playandrecord) -{ - @autoreleasepool { - AVAudioSession *session = [AVAudioSession sharedInstance]; - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - - NSString *category = AVAudioSessionCategoryPlayback; - NSString *mode = AVAudioSessionModeDefault; - NSUInteger options = AVAudioSessionCategoryOptionMixWithOthers; - NSError *err = nil; - const char *hint; - - CountOpenAudioDevicesData data; - SDL_zero(data); - (void) SDL_FindPhysicalAudioDeviceByCallback(CountOpenAudioDevices, &data); - - hint = SDL_GetHint(SDL_HINT_AUDIO_CATEGORY); - if (hint) { - if (SDL_strcasecmp(hint, "AVAudioSessionCategoryAmbient") == 0) { - category = AVAudioSessionCategoryAmbient; - } else if (SDL_strcasecmp(hint, "AVAudioSessionCategorySoloAmbient") == 0) { - category = AVAudioSessionCategorySoloAmbient; - options &= ~AVAudioSessionCategoryOptionMixWithOthers; - } else if (SDL_strcasecmp(hint, "AVAudioSessionCategoryPlayback") == 0 || - SDL_strcasecmp(hint, "playback") == 0) { - category = AVAudioSessionCategoryPlayback; - options &= ~AVAudioSessionCategoryOptionMixWithOthers; - } else if (SDL_strcasecmp(hint, "AVAudioSessionCategoryPlayAndRecord") == 0 || - SDL_strcasecmp(hint, "playandrecord") == 0) { - if (allow_playandrecord) { - category = AVAudioSessionCategoryPlayAndRecord; - } - } - } else if (data.playback && data.recording) { - if (allow_playandrecord) { - category = AVAudioSessionCategoryPlayAndRecord; - } else { - // We already failed play and record with AVAudioSessionErrorCodeResourceNotAvailable - return false; - } - } else if (data.recording) { - category = AVAudioSessionCategoryRecord; - } - - #ifndef SDL_PLATFORM_TVOS - if (category == AVAudioSessionCategoryPlayAndRecord) { - options |= AVAudioSessionCategoryOptionDefaultToSpeaker; - } - #endif - if (category == AVAudioSessionCategoryRecord || - category == AVAudioSessionCategoryPlayAndRecord) { - /* AVAudioSessionCategoryOptionAllowBluetooth isn't available in the SDK for - Apple TV but is still needed in order to output to Bluetooth devices. - */ - options |= 0x4; // AVAudioSessionCategoryOptionAllowBluetooth; - } - if (category == AVAudioSessionCategoryPlayAndRecord) { - options |= AVAudioSessionCategoryOptionAllowBluetoothA2DP | - AVAudioSessionCategoryOptionAllowAirPlay; - } - if (category == AVAudioSessionCategoryPlayback || - category == AVAudioSessionCategoryPlayAndRecord) { - options |= AVAudioSessionCategoryOptionDuckOthers; - } - - if (![session.category isEqualToString:category] || session.categoryOptions != options) { - // Stop the current session so we don't interrupt other application audio - PauseAudioDevices(); - [session setActive:NO error:nil]; - session_active = false; - - if (![session setCategory:category mode:mode options:options error:&err]) { - NSString *desc = err.description; - SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String); - return false; - } - } - - if ((data.playback || data.recording) && !session_active) { - if (![session setActive:YES error:&err]) { - if ([err code] == AVAudioSessionErrorCodeResourceNotAvailable && - category == AVAudioSessionCategoryPlayAndRecord) { - if (UpdateAudioSession(device, open, false)) { - return true; - } else { - return SDL_SetError("Could not activate Audio Session: Resource not available"); - } - } - - NSString *desc = err.description; - return SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String); - } - session_active = true; - ResumeAudioDevices(); - } else if (!data.playback && !data.recording && session_active) { - PauseAudioDevices(); - [session setActive:NO error:nil]; - session_active = false; - } - - if (open) { - SDLInterruptionListener *listener = [SDLInterruptionListener new]; - listener.device = device; - - [center addObserver:listener - selector:@selector(audioSessionInterruption:) - name:AVAudioSessionInterruptionNotification - object:session]; - - /* An interruption end notification is not guaranteed to be sent if - we were previously interrupted... resuming if needed when the app - becomes active seems to be the way to go. */ - // Note: object: below needs to be nil, as otherwise it filters by the object, and session doesn't send foreground / active notifications. - [center addObserver:listener - selector:@selector(applicationBecameActive:) - name:UIApplicationDidBecomeActiveNotification - object:nil]; - - [center addObserver:listener - selector:@selector(applicationBecameActive:) - name:UIApplicationWillEnterForegroundNotification - object:nil]; - - device->hidden->interruption_listener = CFBridgingRetain(listener); - } else { - SDLInterruptionListener *listener = nil; - listener = (SDLInterruptionListener *)CFBridgingRelease(device->hidden->interruption_listener); - [center removeObserver:listener]; - @synchronized(listener) { - listener.device = NULL; - } - } - } - - return true; -} -#endif - - -static bool COREAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) -{ - AudioQueueBufferRef current_buffer = device->hidden->current_buffer; - SDL_assert(current_buffer != NULL); // should have been called from PlaybackBufferReadyCallback - SDL_assert(buffer == (Uint8 *) current_buffer->mAudioData); - current_buffer->mAudioDataByteSize = current_buffer->mAudioDataBytesCapacity; - device->hidden->current_buffer = NULL; - AudioQueueEnqueueBuffer(device->hidden->audioQueue, current_buffer, 0, NULL); - return true; -} - -static Uint8 *COREAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - AudioQueueBufferRef current_buffer = device->hidden->current_buffer; - SDL_assert(current_buffer != NULL); // should have been called from PlaybackBufferReadyCallback - SDL_assert(current_buffer->mAudioData != NULL); - *buffer_size = (int) current_buffer->mAudioDataBytesCapacity; - return (Uint8 *) current_buffer->mAudioData; -} - -static void PlaybackBufferReadyCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *)inUserData; - SDL_assert(inBuffer != NULL); // ...right? - SDL_assert(device->hidden->current_buffer == NULL); // shouldn't have anything pending - device->hidden->current_buffer = inBuffer; - const bool okay = SDL_PlaybackAudioThreadIterate(device); - SDL_assert((device->hidden->current_buffer == NULL) || !okay); // PlayDevice should have enqueued and cleaned it out, unless we failed or shutdown. - - // buffer is unexpectedly here? We're probably dying, but try to requeue this buffer with silence. - if (device->hidden->current_buffer) { - AudioQueueBufferRef current_buffer = device->hidden->current_buffer; - device->hidden->current_buffer = NULL; - SDL_memset(current_buffer->mAudioData, device->silence_value, (size_t) current_buffer->mAudioDataBytesCapacity); - AudioQueueEnqueueBuffer(device->hidden->audioQueue, current_buffer, 0, NULL); - } -} - -static int COREAUDIO_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - AudioQueueBufferRef current_buffer = device->hidden->current_buffer; - SDL_assert(current_buffer != NULL); // should have been called from RecordingBufferReadyCallback - SDL_assert(current_buffer->mAudioData != NULL); - SDL_assert(buflen >= (int) current_buffer->mAudioDataByteSize); // `cpy` makes sure this won't overflow a buffer, but we _will_ drop samples if this assertion fails! - const int cpy = SDL_min(buflen, (int) current_buffer->mAudioDataByteSize); - SDL_memcpy(buffer, current_buffer->mAudioData, cpy); - device->hidden->current_buffer = NULL; - AudioQueueEnqueueBuffer(device->hidden->audioQueue, current_buffer, 0, NULL); // requeue for capturing more data later. - return cpy; -} - -static void COREAUDIO_FlushRecording(SDL_AudioDevice *device) -{ - AudioQueueBufferRef current_buffer = device->hidden->current_buffer; - if (current_buffer != NULL) { // also gets called at shutdown, when no buffer is available. - // just requeue the current buffer without reading from it, so it can be refilled with new data later. - device->hidden->current_buffer = NULL; - AudioQueueEnqueueBuffer(device->hidden->audioQueue, current_buffer, 0, NULL); - } -} - -static void RecordingBufferReadyCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer, - const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions, - const AudioStreamPacketDescription *inPacketDescs) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *)inUserData; - SDL_assert(inAQ == device->hidden->audioQueue); - SDL_assert(inBuffer != NULL); // ...right? - SDL_assert(device->hidden->current_buffer == NULL); // shouldn't have anything pending - device->hidden->current_buffer = inBuffer; - SDL_RecordingAudioThreadIterate(device); - - // buffer is unexpectedly here? We're probably dying, but try to requeue this buffer anyhow. - if (device->hidden->current_buffer != NULL) { - SDL_assert(SDL_GetAtomicInt(&device->shutdown) != 0); - COREAUDIO_FlushRecording(device); // just flush it manually, which will requeue it. - } -} - -static void COREAUDIO_CloseDevice(SDL_AudioDevice *device) -{ - if (!device->hidden) { - return; - } - - // dispose of the audio queue before waiting on the thread, or it might stall for a long time! - if (device->hidden->audioQueue) { - AudioQueueFlush(device->hidden->audioQueue); - AudioQueueStop(device->hidden->audioQueue, 0); - AudioQueueDispose(device->hidden->audioQueue, 0); - } - - if (device->hidden->thread) { - SDL_assert(SDL_GetAtomicInt(&device->shutdown) != 0); // should have been set by SDL_audio.c - SDL_WaitThread(device->hidden->thread, NULL); - } - - #ifndef MACOSX_COREAUDIO - UpdateAudioSession(device, false, true); - #endif - - if (device->hidden->ready_semaphore) { - SDL_DestroySemaphore(device->hidden->ready_semaphore); - } - - // AudioQueueDispose() frees the actual buffer objects. - SDL_free(device->hidden->audioBuffer); - SDL_free(device->hidden->thread_error); - SDL_free(device->hidden); -} - -#ifdef MACOSX_COREAUDIO -static bool PrepareDevice(SDL_AudioDevice *device) -{ - SDL_assert(device->handle != NULL); // this meant "system default" in SDL2, but doesn't anymore - - const SDLCoreAudioHandle *handle = (const SDLCoreAudioHandle *) device->handle; - const AudioDeviceID devid = handle->devid; - OSStatus result = noErr; - UInt32 size = 0; - - AudioObjectPropertyAddress addr = { - 0, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMain - }; - - UInt32 alive = 0; - size = sizeof(alive); - addr.mSelector = kAudioDevicePropertyDeviceIsAlive; - addr.mScope = device->recording ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &alive); - CHECK_RESULT("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)"); - if (!alive) { - return SDL_SetError("CoreAudio: requested device exists, but isn't alive."); - } - - // some devices don't support this property, so errors are fine here. - pid_t pid = 0; - size = sizeof(pid); - addr.mSelector = kAudioDevicePropertyHogMode; - result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &pid); - if ((result == noErr) && (pid != -1)) { - return SDL_SetError("CoreAudio: requested device is being hogged."); - } - - device->hidden->deviceID = devid; - - return true; -} - -static bool AssignDeviceToAudioQueue(SDL_AudioDevice *device) -{ - const AudioObjectPropertyAddress prop = { - kAudioDevicePropertyDeviceUID, - device->recording ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMain - }; - - OSStatus result; - CFStringRef devuid; - UInt32 devuidsize = sizeof(devuid); - result = AudioObjectGetPropertyData(device->hidden->deviceID, &prop, 0, NULL, &devuidsize, &devuid); - CHECK_RESULT("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID)"); - result = AudioQueueSetProperty(device->hidden->audioQueue, kAudioQueueProperty_CurrentDevice, &devuid, devuidsize); - CFRelease(devuid); // Release devuid; we're done with it and AudioQueueSetProperty should have retained if it wants to keep it. - CHECK_RESULT("AudioQueueSetProperty (kAudioQueueProperty_CurrentDevice)"); - return true; -} -#endif - -static bool PrepareAudioQueue(SDL_AudioDevice *device) -{ - const AudioStreamBasicDescription *strdesc = &device->hidden->strdesc; - const bool recording = device->recording; - OSStatus result; - - SDL_assert(CFRunLoopGetCurrent() != NULL); - - if (recording) { - result = AudioQueueNewInput(strdesc, RecordingBufferReadyCallback, device, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &device->hidden->audioQueue); - CHECK_RESULT("AudioQueueNewInput"); - } else { - result = AudioQueueNewOutput(strdesc, PlaybackBufferReadyCallback, device, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &device->hidden->audioQueue); - CHECK_RESULT("AudioQueueNewOutput"); - } - - #ifdef MACOSX_COREAUDIO - if (!AssignDeviceToAudioQueue(device)) { - return false; - } - #endif - - SDL_UpdatedAudioDeviceFormat(device); // make sure this is correct. - - // Set the channel layout for the audio queue - AudioChannelLayout layout; - SDL_zero(layout); - switch (device->spec.channels) { - case 1: - // a standard mono stream - layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono; - break; - case 2: - // a standard stereo stream (L R) - implied playback - layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; - break; - case 3: - // L R LFE - layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; - break; - case 4: - // front left, front right, back left, back right - layout.mChannelLayoutTag = kAudioChannelLayoutTag_Quadraphonic; - break; - case 5: - // L R LFE Ls Rs - layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_6; - break; - case 6: - // L R C LFE Ls Rs - layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_12; - break; - case 7: - if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { - // L R C LFE Cs Ls Rs - layout.mChannelLayoutTag = kAudioChannelLayoutTag_WAVE_6_1; - } else { - // L R C LFE Ls Rs Cs - layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A; - - // Convert from SDL channel layout to kAudioChannelLayoutTag_MPEG_6_1_A - static const int swizzle_map[7] = { - 0, 1, 2, 3, 6, 4, 5 - }; - device->chmap = SDL_ChannelMapDup(swizzle_map, SDL_arraysize(swizzle_map)); - if (!device->chmap) { - return false; - } - } - break; - case 8: - if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { - // L R C LFE Rls Rrs Ls Rs - layout.mChannelLayoutTag = kAudioChannelLayoutTag_WAVE_7_1; - } else { - // L R C LFE Ls Rs Rls Rrs - layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_C; - - // Convert from SDL channel layout to kAudioChannelLayoutTag_MPEG_7_1_C - static const int swizzle_map[8] = { - 0, 1, 2, 3, 6, 7, 4, 5 - }; - device->chmap = SDL_ChannelMapDup(swizzle_map, SDL_arraysize(swizzle_map)); - if (!device->chmap) { - return false; - } - } - break; - default: - return SDL_SetError("Unsupported audio channels"); - } - if (layout.mChannelLayoutTag != 0) { - result = AudioQueueSetProperty(device->hidden->audioQueue, kAudioQueueProperty_ChannelLayout, &layout, sizeof(layout)); - CHECK_RESULT("AudioQueueSetProperty(kAudioQueueProperty_ChannelLayout)"); - } - - // Make sure we can feed the device a minimum amount of time - double MINIMUM_AUDIO_BUFFER_TIME_MS = 15.0; - #ifdef SDL_PLATFORM_IOS - if (SDL_floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) { - // Older iOS hardware, use 40 ms as a minimum time - MINIMUM_AUDIO_BUFFER_TIME_MS = 40.0; - } - #endif - - // we use THREE audio buffers by default, unlike most things that would - // choose two alternating buffers, because it helps with issues on - // Bluetooth headsets when recording and playing at the same time. - // See conversation in #8192 for details. - int numAudioBuffers = 3; - const double msecs = (device->sample_frames / ((double)device->spec.freq)) * 1000.0; - if (msecs < MINIMUM_AUDIO_BUFFER_TIME_MS) { // use more buffers if we have a VERY small sample set. - numAudioBuffers = ((int)SDL_ceil(MINIMUM_AUDIO_BUFFER_TIME_MS / msecs) * 2); - } - - device->hidden->numAudioBuffers = numAudioBuffers; - device->hidden->audioBuffer = SDL_calloc(numAudioBuffers, sizeof(AudioQueueBufferRef)); - if (device->hidden->audioBuffer == NULL) { - return false; - } - - #if DEBUG_COREAUDIO - SDL_Log("COREAUDIO: numAudioBuffers == %d", numAudioBuffers); - #endif - - for (int i = 0; i < numAudioBuffers; i++) { - result = AudioQueueAllocateBuffer(device->hidden->audioQueue, device->buffer_size, &device->hidden->audioBuffer[i]); - CHECK_RESULT("AudioQueueAllocateBuffer"); - SDL_memset(device->hidden->audioBuffer[i]->mAudioData, device->silence_value, device->hidden->audioBuffer[i]->mAudioDataBytesCapacity); - device->hidden->audioBuffer[i]->mAudioDataByteSize = device->hidden->audioBuffer[i]->mAudioDataBytesCapacity; - // !!! FIXME: should we use AudioQueueEnqueueBufferWithParameters and specify all frames be "trimmed" so these are immediately ready to refill with SDL callback data? - result = AudioQueueEnqueueBuffer(device->hidden->audioQueue, device->hidden->audioBuffer[i], 0, NULL); - CHECK_RESULT("AudioQueueEnqueueBuffer"); - } - - result = AudioQueueStart(device->hidden->audioQueue, NULL); - CHECK_RESULT("AudioQueueStart"); - - return true; // We're running! -} - -static int AudioQueueThreadEntry(void *arg) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *)arg; - - if (device->recording) { - SDL_RecordingAudioThreadSetup(device); - } else { - SDL_PlaybackAudioThreadSetup(device); - } - - if (!PrepareAudioQueue(device)) { - device->hidden->thread_error = SDL_strdup(SDL_GetError()); - SDL_SignalSemaphore(device->hidden->ready_semaphore); - return 0; - } - - // init was successful, alert parent thread and start running... - SDL_SignalSemaphore(device->hidden->ready_semaphore); - - // This would be WaitDevice/WaitRecordingDevice in the normal SDL audio thread, but we get *BufferReadyCallback calls here to know when to iterate. - while (!SDL_GetAtomicInt(&device->shutdown)) { - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1); - } - - if (device->recording) { - SDL_RecordingAudioThreadShutdown(device); - } else { - // Drain off any pending playback. - const CFTimeInterval secs = (((CFTimeInterval)device->sample_frames) / ((CFTimeInterval)device->spec.freq)) * 2.0; - CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 0); - SDL_PlaybackAudioThreadShutdown(device); - } - - return 0; -} - -static bool COREAUDIO_OpenDevice(SDL_AudioDevice *device) -{ - // Initialize all variables that we clean on shutdown - device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden)); - if (device->hidden == NULL) { - return false; - } - - #ifndef MACOSX_COREAUDIO - if (!UpdateAudioSession(device, true, true)) { - return false; - } - - // Stop CoreAudio from doing expensive audio rate conversion - @autoreleasepool { - AVAudioSession *session = [AVAudioSession sharedInstance]; - [session setPreferredSampleRate:device->spec.freq error:nil]; - device->spec.freq = (int)session.sampleRate; - #ifdef SDL_PLATFORM_TVOS - if (device->recording) { - [session setPreferredInputNumberOfChannels:device->spec.channels error:nil]; - device->spec.channels = (int)session.preferredInputNumberOfChannels; - } else { - [session setPreferredOutputNumberOfChannels:device->spec.channels error:nil]; - device->spec.channels = (int)session.preferredOutputNumberOfChannels; - } - #else - // Calling setPreferredOutputNumberOfChannels seems to break audio output on iOS - #endif // SDL_PLATFORM_TVOS - } - #endif - - // Setup a AudioStreamBasicDescription with the requested format - AudioStreamBasicDescription *strdesc = &device->hidden->strdesc; - strdesc->mFormatID = kAudioFormatLinearPCM; - strdesc->mFormatFlags = kLinearPCMFormatFlagIsPacked; - strdesc->mChannelsPerFrame = device->spec.channels; - strdesc->mSampleRate = device->spec.freq; - strdesc->mFramesPerPacket = 1; - - const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); - SDL_AudioFormat test_format; - while ((test_format = *(closefmts++)) != 0) { - // CoreAudio handles most of SDL's formats natively. - switch (test_format) { - case SDL_AUDIO_U8: - case SDL_AUDIO_S8: - case SDL_AUDIO_S16LE: - case SDL_AUDIO_S16BE: - case SDL_AUDIO_S32LE: - case SDL_AUDIO_S32BE: - case SDL_AUDIO_F32LE: - case SDL_AUDIO_F32BE: - break; - - default: - continue; - } - break; - } - - if (!test_format) { // shouldn't happen, but just in case... - return SDL_SetError("%s: Unsupported audio format", "coreaudio"); - } - device->spec.format = test_format; - strdesc->mBitsPerChannel = SDL_AUDIO_BITSIZE(test_format); - if (SDL_AUDIO_ISBIGENDIAN(test_format)) { - strdesc->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; - } - - if (SDL_AUDIO_ISFLOAT(test_format)) { - strdesc->mFormatFlags |= kLinearPCMFormatFlagIsFloat; - } else if (SDL_AUDIO_ISSIGNED(test_format)) { - strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; - } - - strdesc->mBytesPerFrame = strdesc->mChannelsPerFrame * strdesc->mBitsPerChannel / 8; - strdesc->mBytesPerPacket = strdesc->mBytesPerFrame * strdesc->mFramesPerPacket; - -#ifdef MACOSX_COREAUDIO - if (!PrepareDevice(device)) { - return false; - } -#endif - - // This has to init in a new thread so it can get its own CFRunLoop. :/ - device->hidden->ready_semaphore = SDL_CreateSemaphore(0); - if (!device->hidden->ready_semaphore) { - return false; // oh well. - } - - char threadname[64]; - SDL_GetAudioThreadName(device, threadname, sizeof(threadname)); - device->hidden->thread = SDL_CreateThread(AudioQueueThreadEntry, threadname, device); - if (!device->hidden->thread) { - return false; - } - - SDL_WaitSemaphore(device->hidden->ready_semaphore); - SDL_DestroySemaphore(device->hidden->ready_semaphore); - device->hidden->ready_semaphore = NULL; - - if ((device->hidden->thread != NULL) && (device->hidden->thread_error != NULL)) { - SDL_WaitThread(device->hidden->thread, NULL); - device->hidden->thread = NULL; - return SDL_SetError("%s", device->hidden->thread_error); - } - - return (device->hidden->thread != NULL); -} - -static void COREAUDIO_DeinitializeStart(void) -{ -#ifdef MACOSX_COREAUDIO - AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, DeviceListChangedNotification, NULL); - AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &default_playback_device_address, DefaultPlaybackDeviceChangedNotification, NULL); - AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &default_recording_device_address, DefaultRecordingDeviceChangedNotification, NULL); -#endif -} - -static bool COREAUDIO_Init(SDL_AudioDriverImpl *impl) -{ - impl->OpenDevice = COREAUDIO_OpenDevice; - impl->PlayDevice = COREAUDIO_PlayDevice; - impl->GetDeviceBuf = COREAUDIO_GetDeviceBuf; - impl->RecordDevice = COREAUDIO_RecordDevice; - impl->FlushRecording = COREAUDIO_FlushRecording; - impl->CloseDevice = COREAUDIO_CloseDevice; - impl->DeinitializeStart = COREAUDIO_DeinitializeStart; - -#ifdef MACOSX_COREAUDIO - impl->DetectDevices = COREAUDIO_DetectDevices; - impl->FreeDeviceHandle = COREAUDIO_FreeDeviceHandle; -#else - impl->OnlyHasDefaultPlaybackDevice = true; - impl->OnlyHasDefaultRecordingDevice = true; -#endif - - impl->ProvidesOwnCallbackThread = true; - impl->HasRecordingSupport = true; - - return true; -} - -AudioBootStrap COREAUDIO_bootstrap = { - "coreaudio", "CoreAudio", COREAUDIO_Init, false, false -}; - -#endif // SDL_AUDIO_DRIVER_COREAUDIO diff --git a/contrib/SDL-3.2.8/src/audio/directsound/SDL_directsound.c b/contrib/SDL-3.2.8/src/audio/directsound/SDL_directsound.c deleted file mode 100644 index 7b5cb11..0000000 --- a/contrib/SDL-3.2.8/src/audio/directsound/SDL_directsound.c +++ /dev/null @@ -1,680 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_DSOUND - -#include "../SDL_sysaudio.h" -#include "SDL_directsound.h" -#include -#ifdef HAVE_MMDEVICEAPI_H -#include "../../core/windows/SDL_immdevice.h" -#endif - -#ifndef WAVE_FORMAT_IEEE_FLOAT -#define WAVE_FORMAT_IEEE_FLOAT 0x0003 -#endif - -// For Vista+, we can enumerate DSound devices with IMMDevice -#ifdef HAVE_MMDEVICEAPI_H -static bool SupportsIMMDevice = false; -#endif - -// DirectX function pointers for audio -static SDL_SharedObject *DSoundDLL = NULL; -typedef HRESULT(WINAPI *fnDirectSoundCreate8)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN); -typedef HRESULT(WINAPI *fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID); -typedef HRESULT(WINAPI *fnDirectSoundCaptureCreate8)(LPCGUID, LPDIRECTSOUNDCAPTURE8 *, LPUNKNOWN); -typedef HRESULT(WINAPI *fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID); -typedef HRESULT(WINAPI *fnGetDeviceID)(LPCGUID, LPGUID); -static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL; -static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL; -static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = NULL; -static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = NULL; -static fnGetDeviceID pGetDeviceID = NULL; - -#include -DEFINE_GUID(SDL_DSDEVID_DefaultPlayback, 0xdef00000, 0x9c6d, 0x47ed, 0xaa, 0xf1, 0x4d, 0xda, 0x8f, 0x2b, 0x5c, 0x03); -DEFINE_GUID(SDL_DSDEVID_DefaultCapture, 0xdef00001, 0x9c6d, 0x47ed, 0xaa, 0xf1, 0x4d, 0xda, 0x8f, 0x2b, 0x5c, 0x03); - -static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; -static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; - -static void DSOUND_Unload(void) -{ - pDirectSoundCreate8 = NULL; - pDirectSoundEnumerateW = NULL; - pDirectSoundCaptureCreate8 = NULL; - pDirectSoundCaptureEnumerateW = NULL; - pGetDeviceID = NULL; - - if (DSoundDLL) { - SDL_UnloadObject(DSoundDLL); - DSoundDLL = NULL; - } -} - -static bool DSOUND_Load(void) -{ - bool loaded = false; - - DSOUND_Unload(); - - DSoundDLL = SDL_LoadObject("DSOUND.DLL"); - if (!DSoundDLL) { - SDL_SetError("DirectSound: failed to load DSOUND.DLL"); - } else { -// Now make sure we have DirectX 8 or better... -#define DSOUNDLOAD(f) \ - { \ - p##f = (fn##f)SDL_LoadFunction(DSoundDLL, #f); \ - if (!p##f) \ - loaded = false; \ - } - loaded = true; // will reset if necessary. - DSOUNDLOAD(DirectSoundCreate8); - DSOUNDLOAD(DirectSoundEnumerateW); - DSOUNDLOAD(DirectSoundCaptureCreate8); - DSOUNDLOAD(DirectSoundCaptureEnumerateW); - DSOUNDLOAD(GetDeviceID); -#undef DSOUNDLOAD - - if (!loaded) { - SDL_SetError("DirectSound: System doesn't appear to have DX8."); - } - } - - if (!loaded) { - DSOUND_Unload(); - } - - return loaded; -} - -static bool SetDSerror(const char *function, int code) -{ - const char *error; - - switch (code) { - case E_NOINTERFACE: - error = "Unsupported interface -- Is DirectX 8.0 or later installed?"; - break; - case DSERR_ALLOCATED: - error = "Audio device in use"; - break; - case DSERR_BADFORMAT: - error = "Unsupported audio format"; - break; - case DSERR_BUFFERLOST: - error = "Mixing buffer was lost"; - break; - case DSERR_CONTROLUNAVAIL: - error = "Control requested is not available"; - break; - case DSERR_INVALIDCALL: - error = "Invalid call for the current state"; - break; - case DSERR_INVALIDPARAM: - error = "Invalid parameter"; - break; - case DSERR_NODRIVER: - error = "No audio device found"; - break; - case DSERR_OUTOFMEMORY: - error = "Out of memory"; - break; - case DSERR_PRIOLEVELNEEDED: - error = "Caller doesn't have priority"; - break; - case DSERR_UNSUPPORTED: - error = "Function not supported"; - break; - default: - error = "Unknown DirectSound error"; - break; - } - - return SDL_SetError("%s: %s (0x%x)", function, error, code); -} - -static void DSOUND_FreeDeviceHandle(SDL_AudioDevice *device) -{ -#ifdef HAVE_MMDEVICEAPI_H - if (SupportsIMMDevice) { - SDL_IMMDevice_FreeDeviceHandle(device); - } else -#endif - { - SDL_free(device->handle); - } -} - -// FindAllDevs is presumably only used on WinXP; Vista and later can use IMMDevice for better results. -typedef struct FindAllDevsData -{ - bool recording; - SDL_AudioDevice **default_device; - LPCGUID default_device_guid; -} FindAllDevsData; - -static BOOL CALLBACK FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID userdata) -{ - FindAllDevsData *data = (FindAllDevsData *) userdata; - if (guid != NULL) { // skip default device - char *str = WIN_LookupAudioDeviceName(desc, guid); - if (str) { - LPGUID cpyguid = (LPGUID)SDL_malloc(sizeof(GUID)); - if (cpyguid) { - SDL_copyp(cpyguid, guid); - - /* Note that spec is NULL, because we are required to connect to the - * device before getting the channel mask and output format, making - * this information inaccessible at enumeration time - */ - SDL_AudioDevice *device = SDL_AddAudioDevice(data->recording, str, NULL, cpyguid); - if (device && data->default_device && data->default_device_guid) { - if (SDL_memcmp(cpyguid, data->default_device_guid, sizeof (GUID)) == 0) { - *data->default_device = device; - } - } - } - SDL_free(str); // SDL_AddAudioDevice() makes a copy of this string. - } - } - return TRUE; // keep enumerating. -} - -static void DSOUND_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) -{ -#ifdef HAVE_MMDEVICEAPI_H - if (SupportsIMMDevice) { - SDL_IMMDevice_EnumerateEndpoints(default_playback, default_recording); - } else -#endif - { - // Without IMMDevice, you can enumerate devices and figure out the default devices, - // but you won't get device hotplug or default device change notifications. But this is - // only for WinXP; Windows Vista and later should be using IMMDevice. - FindAllDevsData data; - GUID guid; - - data.recording = true; - data.default_device = default_recording; - data.default_device_guid = (pGetDeviceID(&SDL_DSDEVID_DefaultCapture, &guid) == DS_OK) ? &guid : NULL; - pDirectSoundCaptureEnumerateW(FindAllDevs, &data); - - data.recording = false; - data.default_device = default_playback; - data.default_device_guid = (pGetDeviceID(&SDL_DSDEVID_DefaultPlayback, &guid) == DS_OK) ? &guid : NULL; - pDirectSoundEnumerateW(FindAllDevs, &data); - } - -} - -static bool DSOUND_WaitDevice(SDL_AudioDevice *device) -{ - /* Semi-busy wait, since we have no way of getting play notification - on a primary mixing buffer located in hardware (DirectX 5.0) - */ - while (!SDL_GetAtomicInt(&device->shutdown)) { - DWORD status = 0; - DWORD cursor = 0; - DWORD junk = 0; - HRESULT result = DS_OK; - - // Try to restore a lost sound buffer - IDirectSoundBuffer_GetStatus(device->hidden->mixbuf, &status); - if (status & DSBSTATUS_BUFFERLOST) { - IDirectSoundBuffer_Restore(device->hidden->mixbuf); - } else if (!(status & DSBSTATUS_PLAYING)) { - result = IDirectSoundBuffer_Play(device->hidden->mixbuf, 0, 0, DSBPLAY_LOOPING); - } else { - // Find out where we are playing - result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf, &junk, &cursor); - if ((result == DS_OK) && ((cursor / device->buffer_size) != device->hidden->lastchunk)) { - break; // ready for next chunk! - } - } - - if ((result != DS_OK) && (result != DSERR_BUFFERLOST)) { - return false; - } - - SDL_Delay(1); // not ready yet; sleep a bit. - } - - return true; -} - -static bool DSOUND_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) -{ - // Unlock the buffer, allowing it to play - SDL_assert(buflen == device->buffer_size); - if (IDirectSoundBuffer_Unlock(device->hidden->mixbuf, (LPVOID) buffer, buflen, NULL, 0) != DS_OK) { - return false; - } - return true; -} - -static Uint8 *DSOUND_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - DWORD cursor = 0; - DWORD junk = 0; - HRESULT result = DS_OK; - - SDL_assert(*buffer_size == device->buffer_size); - - // Figure out which blocks to fill next - device->hidden->locked_buf = NULL; - result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf, - &junk, &cursor); - if (result == DSERR_BUFFERLOST) { - IDirectSoundBuffer_Restore(device->hidden->mixbuf); - result = IDirectSoundBuffer_GetCurrentPosition(device->hidden->mixbuf, - &junk, &cursor); - } - if (result != DS_OK) { - SetDSerror("DirectSound GetCurrentPosition", result); - return NULL; - } - cursor /= device->buffer_size; -#ifdef DEBUG_SOUND - // Detect audio dropouts - { - DWORD spot = cursor; - if (spot < device->hidden->lastchunk) { - spot += device->hidden->num_buffers; - } - if (spot > device->hidden->lastchunk + 1) { - fprintf(stderr, "Audio dropout, missed %d fragments\n", - (spot - (device->hidden->lastchunk + 1))); - } - } -#endif - device->hidden->lastchunk = cursor; - cursor = (cursor + 1) % device->hidden->num_buffers; - cursor *= device->buffer_size; - - // Lock the audio buffer - DWORD rawlen = 0; - result = IDirectSoundBuffer_Lock(device->hidden->mixbuf, cursor, - device->buffer_size, - (LPVOID *)&device->hidden->locked_buf, - &rawlen, NULL, &junk, 0); - if (result == DSERR_BUFFERLOST) { - IDirectSoundBuffer_Restore(device->hidden->mixbuf); - result = IDirectSoundBuffer_Lock(device->hidden->mixbuf, cursor, - device->buffer_size, - (LPVOID *)&device->hidden->locked_buf, &rawlen, NULL, - &junk, 0); - } - if (result != DS_OK) { - SetDSerror("DirectSound Lock", result); - return NULL; - } - return device->hidden->locked_buf; -} - -static bool DSOUND_WaitRecordingDevice(SDL_AudioDevice *device) -{ - struct SDL_PrivateAudioData *h = device->hidden; - while (!SDL_GetAtomicInt(&device->shutdown)) { - DWORD junk, cursor; - if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) != DS_OK) { - return false; - } else if ((cursor / device->buffer_size) != h->lastchunk) { - break; - } - SDL_Delay(1); - } - - return true; -} - -static int DSOUND_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - struct SDL_PrivateAudioData *h = device->hidden; - DWORD ptr1len, ptr2len; - VOID *ptr1, *ptr2; - - SDL_assert(buflen == device->buffer_size); - - if (IDirectSoundCaptureBuffer_Lock(h->capturebuf, h->lastchunk * buflen, buflen, &ptr1, &ptr1len, &ptr2, &ptr2len, 0) != DS_OK) { - return -1; - } - - SDL_assert(ptr1len == (DWORD)buflen); - SDL_assert(ptr2 == NULL); - SDL_assert(ptr2len == 0); - - SDL_memcpy(buffer, ptr1, ptr1len); - - if (IDirectSoundCaptureBuffer_Unlock(h->capturebuf, ptr1, ptr1len, ptr2, ptr2len) != DS_OK) { - return -1; - } - - h->lastchunk = (h->lastchunk + 1) % h->num_buffers; - - return (int) ptr1len; -} - -static void DSOUND_FlushRecording(SDL_AudioDevice *device) -{ - struct SDL_PrivateAudioData *h = device->hidden; - DWORD junk, cursor; - if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) == DS_OK) { - h->lastchunk = cursor / device->buffer_size; - } -} - -static void DSOUND_CloseDevice(SDL_AudioDevice *device) -{ - if (device->hidden) { - if (device->hidden->mixbuf) { - IDirectSoundBuffer_Stop(device->hidden->mixbuf); - IDirectSoundBuffer_Release(device->hidden->mixbuf); - } - if (device->hidden->sound) { - IDirectSound_Release(device->hidden->sound); - } - if (device->hidden->capturebuf) { - IDirectSoundCaptureBuffer_Stop(device->hidden->capturebuf); - IDirectSoundCaptureBuffer_Release(device->hidden->capturebuf); - } - if (device->hidden->capture) { - IDirectSoundCapture_Release(device->hidden->capture); - } - SDL_free(device->hidden); - device->hidden = NULL; - } -} - -/* This function tries to create a secondary audio buffer, and returns the - number of audio chunks available in the created buffer. This is for - playback devices, not recording. -*/ -static bool CreateSecondary(SDL_AudioDevice *device, const DWORD bufsize, WAVEFORMATEX *wfmt) -{ - LPDIRECTSOUND sndObj = device->hidden->sound; - LPDIRECTSOUNDBUFFER *sndbuf = &device->hidden->mixbuf; - HRESULT result = DS_OK; - DSBUFFERDESC format; - LPVOID pvAudioPtr1, pvAudioPtr2; - DWORD dwAudioBytes1, dwAudioBytes2; - - // Try to create the secondary buffer - SDL_zero(format); - format.dwSize = sizeof(format); - format.dwFlags = DSBCAPS_GETCURRENTPOSITION2; - format.dwFlags |= DSBCAPS_GLOBALFOCUS; - format.dwBufferBytes = bufsize; - format.lpwfxFormat = wfmt; - result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL); - if (result != DS_OK) { - return SetDSerror("DirectSound CreateSoundBuffer", result); - } - IDirectSoundBuffer_SetFormat(*sndbuf, wfmt); - - // Silence the initial audio buffer - result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes, - (LPVOID *)&pvAudioPtr1, &dwAudioBytes1, - (LPVOID *)&pvAudioPtr2, &dwAudioBytes2, - DSBLOCK_ENTIREBUFFER); - if (result == DS_OK) { - SDL_memset(pvAudioPtr1, device->silence_value, dwAudioBytes1); - IDirectSoundBuffer_Unlock(*sndbuf, - (LPVOID)pvAudioPtr1, dwAudioBytes1, - (LPVOID)pvAudioPtr2, dwAudioBytes2); - } - - return true; // We're ready to go -} - -/* This function tries to create a capture buffer, and returns the - number of audio chunks available in the created buffer. This is for - recording devices, not playback. -*/ -static bool CreateCaptureBuffer(SDL_AudioDevice *device, const DWORD bufsize, WAVEFORMATEX *wfmt) -{ - LPDIRECTSOUNDCAPTURE capture = device->hidden->capture; - LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &device->hidden->capturebuf; - DSCBUFFERDESC format; - HRESULT result; - - SDL_zero(format); - format.dwSize = sizeof(format); - format.dwFlags = DSCBCAPS_WAVEMAPPED; - format.dwBufferBytes = bufsize; - format.lpwfxFormat = wfmt; - - result = IDirectSoundCapture_CreateCaptureBuffer(capture, &format, capturebuf, NULL); - if (result != DS_OK) { - return SetDSerror("DirectSound CreateCaptureBuffer", result); - } - - result = IDirectSoundCaptureBuffer_Start(*capturebuf, DSCBSTART_LOOPING); - if (result != DS_OK) { - IDirectSoundCaptureBuffer_Release(*capturebuf); - return SetDSerror("DirectSound Start", result); - } - -#if 0 - // presumably this starts at zero, but just in case... - result = IDirectSoundCaptureBuffer_GetCurrentPosition(*capturebuf, &junk, &cursor); - if (result != DS_OK) { - IDirectSoundCaptureBuffer_Stop(*capturebuf); - IDirectSoundCaptureBuffer_Release(*capturebuf); - return SetDSerror("DirectSound GetCurrentPosition", result); - } - - device->hidden->lastchunk = cursor / device->buffer_size; -#endif - - return true; -} - -static bool DSOUND_OpenDevice(SDL_AudioDevice *device) -{ - // Initialize all variables that we clean on shutdown - device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } - - // Open the audio device - LPGUID guid; -#ifdef HAVE_MMDEVICEAPI_H - if (SupportsIMMDevice) { - guid = SDL_IMMDevice_GetDirectSoundGUID(device); - } else -#endif - { - guid = (LPGUID) device->handle; - } - - SDL_assert(guid != NULL); - - HRESULT result; - if (device->recording) { - result = pDirectSoundCaptureCreate8(guid, &device->hidden->capture, NULL); - if (result != DS_OK) { - return SetDSerror("DirectSoundCaptureCreate8", result); - } - } else { - result = pDirectSoundCreate8(guid, &device->hidden->sound, NULL); - if (result != DS_OK) { - return SetDSerror("DirectSoundCreate8", result); - } - result = IDirectSound_SetCooperativeLevel(device->hidden->sound, - GetDesktopWindow(), - DSSCL_NORMAL); - if (result != DS_OK) { - return SetDSerror("DirectSound SetCooperativeLevel", result); - } - } - - const DWORD numchunks = 8; - DWORD bufsize; - bool tried_format = false; - SDL_AudioFormat test_format; - const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); - while ((test_format = *(closefmts++)) != 0) { - switch (test_format) { - case SDL_AUDIO_U8: - case SDL_AUDIO_S16: - case SDL_AUDIO_S32: - case SDL_AUDIO_F32: - tried_format = true; - - device->spec.format = test_format; - - // Update the fragment size as size in bytes - SDL_UpdatedAudioDeviceFormat(device); - - bufsize = numchunks * device->buffer_size; - if ((bufsize < DSBSIZE_MIN) || (bufsize > DSBSIZE_MAX)) { - SDL_SetError("Sound buffer size must be between %d and %d", - (int)((DSBSIZE_MIN < numchunks) ? 1 : DSBSIZE_MIN / numchunks), - (int)(DSBSIZE_MAX / numchunks)); - } else { - WAVEFORMATEXTENSIBLE wfmt; - SDL_zero(wfmt); - if (device->spec.channels > 2) { - wfmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wfmt.Format.cbSize = sizeof(wfmt) - sizeof(WAVEFORMATEX); - - if (SDL_AUDIO_ISFLOAT(device->spec.format)) { - SDL_memcpy(&wfmt.SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)); - } else { - SDL_memcpy(&wfmt.SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)); - } - wfmt.Samples.wValidBitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format); - - switch (device->spec.channels) { - case 3: // 3.0 (or 2.1) - wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER; - break; - case 4: // 4.0 - wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; - break; - case 5: // 5.0 (or 4.1) - wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; - break; - case 6: // 5.1 - wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; - break; - case 7: // 6.1 - wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_BACK_CENTER; - break; - case 8: // 7.1 - wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; - break; - default: - SDL_assert(!"Unsupported channel count!"); - break; - } - } else if (SDL_AUDIO_ISFLOAT(device->spec.format)) { - wfmt.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - } else { - wfmt.Format.wFormatTag = WAVE_FORMAT_PCM; - } - - wfmt.Format.wBitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format); - wfmt.Format.nChannels = (WORD)device->spec.channels; - wfmt.Format.nSamplesPerSec = device->spec.freq; - wfmt.Format.nBlockAlign = wfmt.Format.nChannels * (wfmt.Format.wBitsPerSample / 8); - wfmt.Format.nAvgBytesPerSec = wfmt.Format.nSamplesPerSec * wfmt.Format.nBlockAlign; - - const bool rc = device->recording ? CreateCaptureBuffer(device, bufsize, (WAVEFORMATEX *)&wfmt) : CreateSecondary(device, bufsize, (WAVEFORMATEX *)&wfmt); - if (rc) { - device->hidden->num_buffers = numchunks; - break; - } - } - continue; - default: - continue; - } - break; - } - - if (!test_format) { - if (tried_format) { - return false; // CreateSecondary() should have called SDL_SetError(). - } - return SDL_SetError("%s: Unsupported audio format", "directsound"); - } - - // Playback buffers will auto-start playing in DSOUND_WaitDevice() - - return true; // good to go. -} - -static void DSOUND_DeinitializeStart(void) -{ -#ifdef HAVE_MMDEVICEAPI_H - if (SupportsIMMDevice) { - SDL_IMMDevice_Quit(); - } -#endif -} - -static void DSOUND_Deinitialize(void) -{ - DSOUND_Unload(); -#ifdef HAVE_MMDEVICEAPI_H - SupportsIMMDevice = false; -#endif -} - -static bool DSOUND_Init(SDL_AudioDriverImpl *impl) -{ - if (!DSOUND_Load()) { - return false; - } - -#ifdef HAVE_MMDEVICEAPI_H - SupportsIMMDevice = SDL_IMMDevice_Init(NULL); -#endif - - impl->DetectDevices = DSOUND_DetectDevices; - impl->OpenDevice = DSOUND_OpenDevice; - impl->PlayDevice = DSOUND_PlayDevice; - impl->WaitDevice = DSOUND_WaitDevice; - impl->GetDeviceBuf = DSOUND_GetDeviceBuf; - impl->WaitRecordingDevice = DSOUND_WaitRecordingDevice; - impl->RecordDevice = DSOUND_RecordDevice; - impl->FlushRecording = DSOUND_FlushRecording; - impl->CloseDevice = DSOUND_CloseDevice; - impl->FreeDeviceHandle = DSOUND_FreeDeviceHandle; - impl->DeinitializeStart = DSOUND_DeinitializeStart; - impl->Deinitialize = DSOUND_Deinitialize; - - impl->HasRecordingSupport = true; - - return true; -} - -AudioBootStrap DSOUND_bootstrap = { - "directsound", "DirectSound", DSOUND_Init, false, false -}; - -#endif // SDL_AUDIO_DRIVER_DSOUND diff --git a/contrib/SDL-3.2.8/src/audio/directsound/SDL_directsound.h b/contrib/SDL-3.2.8/src/audio/directsound/SDL_directsound.h deleted file mode 100644 index a4fa2fa..0000000 --- a/contrib/SDL-3.2.8/src/audio/directsound/SDL_directsound.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_directsound_h_ -#define SDL_directsound_h_ - -#include "../../core/windows/SDL_directx.h" - -#include "../SDL_sysaudio.h" - -// The DirectSound objects -struct SDL_PrivateAudioData -{ - // !!! FIXME: make this a union with capture/playback sections? - LPDIRECTSOUND sound; - LPDIRECTSOUNDBUFFER mixbuf; - LPDIRECTSOUNDCAPTURE capture; - LPDIRECTSOUNDCAPTUREBUFFER capturebuf; - int num_buffers; - DWORD lastchunk; - Uint8 *locked_buf; -}; - -#endif // SDL_directsound_h_ diff --git a/contrib/SDL-3.2.8/src/audio/disk/SDL_diskaudio.c b/contrib/SDL-3.2.8/src/audio/disk/SDL_diskaudio.c deleted file mode 100644 index 9e05478..0000000 --- a/contrib/SDL-3.2.8/src/audio/disk/SDL_diskaudio.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_DISK - -// Output raw audio data to a file. - -#include "../SDL_sysaudio.h" -#include "SDL_diskaudio.h" - -#define DISKDEFAULT_OUTFILE "sdlaudio.raw" -#define DISKDEFAULT_INFILE "sdlaudio-in.raw" - -static bool DISKAUDIO_WaitDevice(SDL_AudioDevice *device) -{ - SDL_Delay(device->hidden->io_delay); - return true; -} - -static bool DISKAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) -{ - const int written = (int)SDL_WriteIO(device->hidden->io, buffer, (size_t)buffer_size); - if (written != buffer_size) { // If we couldn't write, assume fatal error for now - return false; - } -#ifdef DEBUG_AUDIO - SDL_Log("DISKAUDIO: Wrote %d bytes of audio data", (int) written); -#endif - return true; -} - -static Uint8 *DISKAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - return device->hidden->mixbuf; -} - -static int DISKAUDIO_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - struct SDL_PrivateAudioData *h = device->hidden; - const int origbuflen = buflen; - - if (h->io) { - const int br = (int)SDL_ReadIO(h->io, buffer, (size_t)buflen); - buflen -= br; - buffer = ((Uint8 *)buffer) + br; - if (buflen > 0) { // EOF (or error, but whatever). - SDL_CloseIO(h->io); - h->io = NULL; - } - } - - // if we ran out of file, just write silence. - SDL_memset(buffer, device->silence_value, buflen); - - return origbuflen; -} - -static void DISKAUDIO_FlushRecording(SDL_AudioDevice *device) -{ - // no op...we don't advance the file pointer or anything. -} - -static void DISKAUDIO_CloseDevice(SDL_AudioDevice *device) -{ - if (device->hidden) { - if (device->hidden->io) { - SDL_CloseIO(device->hidden->io); - } - SDL_free(device->hidden->mixbuf); - SDL_free(device->hidden); - device->hidden = NULL; - } -} - -static const char *get_filename(const bool recording) -{ - const char *devname = SDL_GetHint(recording ? SDL_HINT_AUDIO_DISK_INPUT_FILE : SDL_HINT_AUDIO_DISK_OUTPUT_FILE); - if (!devname) { - devname = recording ? DISKDEFAULT_INFILE : DISKDEFAULT_OUTFILE; - } - return devname; -} - -static bool DISKAUDIO_OpenDevice(SDL_AudioDevice *device) -{ - bool recording = device->recording; - const char *fname = get_filename(recording); - - device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } - - device->hidden->io_delay = ((device->sample_frames * 1000) / device->spec.freq); - - const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DISK_TIMESCALE); - if (hint) { - double scale = SDL_atof(hint); - if (scale >= 0.0) { - device->hidden->io_delay = (Uint32)SDL_round(device->hidden->io_delay * scale); - } - } - - // Open the "audio device" - device->hidden->io = SDL_IOFromFile(fname, recording ? "rb" : "wb"); - if (!device->hidden->io) { - return false; - } - - // Allocate mixing buffer - if (!recording) { - device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size); - if (!device->hidden->mixbuf) { - return false; - } - SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size); - } - - SDL_LogCritical(SDL_LOG_CATEGORY_AUDIO, "You are using the SDL disk i/o audio driver!"); - SDL_LogCritical(SDL_LOG_CATEGORY_AUDIO, " %s file [%s].", recording ? "Reading from" : "Writing to", fname); - - return true; // We're ready to rock and roll. :-) -} - -static void DISKAUDIO_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) -{ - *default_playback = SDL_AddAudioDevice(false, DEFAULT_PLAYBACK_DEVNAME, NULL, (void *)0x1); - *default_recording = SDL_AddAudioDevice(true, DEFAULT_RECORDING_DEVNAME, NULL, (void *)0x2); -} - -static bool DISKAUDIO_Init(SDL_AudioDriverImpl *impl) -{ - impl->OpenDevice = DISKAUDIO_OpenDevice; - impl->WaitDevice = DISKAUDIO_WaitDevice; - impl->WaitRecordingDevice = DISKAUDIO_WaitDevice; - impl->PlayDevice = DISKAUDIO_PlayDevice; - impl->GetDeviceBuf = DISKAUDIO_GetDeviceBuf; - impl->RecordDevice = DISKAUDIO_RecordDevice; - impl->FlushRecording = DISKAUDIO_FlushRecording; - impl->CloseDevice = DISKAUDIO_CloseDevice; - impl->DetectDevices = DISKAUDIO_DetectDevices; - - impl->HasRecordingSupport = true; - - return true; -} - -AudioBootStrap DISKAUDIO_bootstrap = { - "disk", "direct-to-disk audio", DISKAUDIO_Init, true, false -}; - -#endif // SDL_AUDIO_DRIVER_DISK diff --git a/contrib/SDL-3.2.8/src/audio/disk/SDL_diskaudio.h b/contrib/SDL-3.2.8/src/audio/disk/SDL_diskaudio.h deleted file mode 100644 index d8d9980..0000000 --- a/contrib/SDL-3.2.8/src/audio/disk/SDL_diskaudio.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_diskaudio_h_ -#define SDL_diskaudio_h_ - -#include "../SDL_sysaudio.h" - -struct SDL_PrivateAudioData -{ - // The file descriptor for the audio device - SDL_IOStream *io; - Uint32 io_delay; - Uint8 *mixbuf; -}; - -#endif // SDL_diskaudio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.c b/contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.c deleted file mode 100644 index 62b8990..0000000 --- a/contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -// !!! FIXME: clean out perror and fprintf calls in here. - -#ifdef SDL_AUDIO_DRIVER_OSS - -#include // For perror() -#include // For strerror() -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "../SDL_audiodev_c.h" -#include "SDL_dspaudio.h" - -static void DSP_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) -{ - SDL_EnumUnixAudioDevices(false, NULL); -} - -static void DSP_CloseDevice(SDL_AudioDevice *device) -{ - if (device->hidden) { - if (device->hidden->audio_fd >= 0) { - close(device->hidden->audio_fd); - } - SDL_free(device->hidden->mixbuf); - SDL_free(device->hidden); - } -} - -static bool DSP_OpenDevice(SDL_AudioDevice *device) -{ - // Make sure fragment size stays a power of 2, or OSS fails. - // (I don't know which of these are actually legal values, though...) - if (device->spec.channels > 8) { - device->spec.channels = 8; - } else if (device->spec.channels > 4) { - device->spec.channels = 4; - } else if (device->spec.channels > 2) { - device->spec.channels = 2; - } - - // Initialize all variables that we clean on shutdown - device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } - - // Open the audio device; we hardcode the device path in `device->name` for lack of better info, so use that. - const int flags = ((device->recording) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT); - device->hidden->audio_fd = open(device->name, flags | O_CLOEXEC, 0); - if (device->hidden->audio_fd < 0) { - return SDL_SetError("Couldn't open %s: %s", device->name, strerror(errno)); - } - - // Make the file descriptor use blocking i/o with fcntl() - { - const long ctlflags = fcntl(device->hidden->audio_fd, F_GETFL) & ~O_NONBLOCK; - if (fcntl(device->hidden->audio_fd, F_SETFL, ctlflags) < 0) { - return SDL_SetError("Couldn't set audio blocking mode"); - } - } - - // Get a list of supported hardware formats - int value; - if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) { - perror("SNDCTL_DSP_GETFMTS"); - return SDL_SetError("Couldn't get audio format list"); - } - - // Try for a closest match on audio format - int format = 0; - SDL_AudioFormat test_format; - const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); - while ((test_format = *(closefmts++)) != 0) { -#ifdef DEBUG_AUDIO - fprintf(stderr, "Trying format 0x%4.4x\n", test_format); -#endif - switch (test_format) { - case SDL_AUDIO_U8: - if (value & AFMT_U8) { - format = AFMT_U8; - } - break; - case SDL_AUDIO_S16LE: - if (value & AFMT_S16_LE) { - format = AFMT_S16_LE; - } - break; - case SDL_AUDIO_S16BE: - if (value & AFMT_S16_BE) { - format = AFMT_S16_BE; - } - break; - - default: - continue; - } - break; - } - if (format == 0) { - return SDL_SetError("Couldn't find any hardware audio formats"); - } - device->spec.format = test_format; - - // Set the audio format - value = format; - if ((ioctl(device->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || - (value != format)) { - perror("SNDCTL_DSP_SETFMT"); - return SDL_SetError("Couldn't set audio format"); - } - - // Set the number of channels of output - value = device->spec.channels; - if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) { - perror("SNDCTL_DSP_CHANNELS"); - return SDL_SetError("Cannot set the number of channels"); - } - device->spec.channels = value; - - // Set the DSP frequency - value = device->spec.freq; - if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) { - perror("SNDCTL_DSP_SPEED"); - return SDL_SetError("Couldn't set audio frequency"); - } - device->spec.freq = value; - - // Calculate the final parameters for this audio specification - SDL_UpdatedAudioDeviceFormat(device); - - /* Determine the power of two of the fragment size - Since apps don't control this in SDL3, and this driver only accepts 8, 16 - bit formats and 1, 2, 4, 8 channels, this should always be a power of 2 already. */ - SDL_assert(SDL_powerof2(device->buffer_size) == device->buffer_size); - - int frag_spec = 0; - while ((0x01U << frag_spec) < device->buffer_size) { - frag_spec++; - } - frag_spec |= 0x00020000; // two fragments, for low latency - - // Set the audio buffering parameters -#ifdef DEBUG_AUDIO - fprintf(stderr, "Requesting %d fragments of size %d\n", - (frag_spec >> 16), 1 << (frag_spec & 0xFFFF)); -#endif - if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) { - perror("SNDCTL_DSP_SETFRAGMENT"); - } -#ifdef DEBUG_AUDIO - { - audio_buf_info info; - ioctl(device->hidden->audio_fd, SNDCTL_DSP_GETOSPACE, &info); - fprintf(stderr, "fragments = %d\n", info.fragments); - fprintf(stderr, "fragstotal = %d\n", info.fragstotal); - fprintf(stderr, "fragsize = %d\n", info.fragsize); - fprintf(stderr, "bytes = %d\n", info.bytes); - } -#endif - - // Allocate mixing buffer - if (!device->recording) { - device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size); - if (!device->hidden->mixbuf) { - return false; - } - SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size); - } - - return true; // We're ready to rock and roll. :-) -} - -static bool DSP_WaitDevice(SDL_AudioDevice *device) -{ - const unsigned long ioctlreq = device->recording ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE; - struct SDL_PrivateAudioData *h = device->hidden; - - while (!SDL_GetAtomicInt(&device->shutdown)) { - audio_buf_info info; - const int rc = ioctl(h->audio_fd, ioctlreq, &info); - if (rc < 0) { - if (errno == EAGAIN) { - continue; - } - // Hmm, not much we can do - abort - fprintf(stderr, "dsp WaitDevice ioctl failed (unrecoverable): %s\n", strerror(errno)); - return false; - } else if (info.bytes < device->buffer_size) { - SDL_Delay(10); - } else { - break; // ready to go! - } - } - - return true; -} - -static bool DSP_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) -{ - struct SDL_PrivateAudioData *h = device->hidden; - if (write(h->audio_fd, buffer, buflen) == -1) { - perror("Audio write"); - return false; - } -#ifdef DEBUG_AUDIO - fprintf(stderr, "Wrote %d bytes of audio data\n", h->mixlen); -#endif - return true; -} - -static Uint8 *DSP_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - return device->hidden->mixbuf; -} - -static int DSP_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - return (int)read(device->hidden->audio_fd, buffer, buflen); -} - -static void DSP_FlushRecording(SDL_AudioDevice *device) -{ - struct SDL_PrivateAudioData *h = device->hidden; - audio_buf_info info; - if (ioctl(h->audio_fd, SNDCTL_DSP_GETISPACE, &info) == 0) { - while (info.bytes > 0) { - char buf[512]; - const size_t len = SDL_min(sizeof(buf), info.bytes); - const ssize_t br = read(h->audio_fd, buf, len); - if (br <= 0) { - break; - } - info.bytes -= br; - } - } -} - -static bool InitTimeDevicesExist = false; -static bool look_for_devices_test(int fd) -{ - InitTimeDevicesExist = true; // note that _something_ exists. - // Don't add to the device list, we're just seeing if any devices exist. - return false; -} - -static bool DSP_Init(SDL_AudioDriverImpl *impl) -{ - InitTimeDevicesExist = false; - SDL_EnumUnixAudioDevices(false, look_for_devices_test); - if (!InitTimeDevicesExist) { - SDL_SetError("dsp: No such audio device"); - return false; // maybe try a different backend. - } - - impl->DetectDevices = DSP_DetectDevices; - impl->OpenDevice = DSP_OpenDevice; - impl->WaitDevice = DSP_WaitDevice; - impl->PlayDevice = DSP_PlayDevice; - impl->GetDeviceBuf = DSP_GetDeviceBuf; - impl->CloseDevice = DSP_CloseDevice; - impl->WaitRecordingDevice = DSP_WaitDevice; - impl->RecordDevice = DSP_RecordDevice; - impl->FlushRecording = DSP_FlushRecording; - - impl->HasRecordingSupport = true; - - return true; -} - -AudioBootStrap DSP_bootstrap = { - "dsp", "Open Sound System (/dev/dsp)", DSP_Init, false, false -}; - -#endif // SDL_AUDIO_DRIVER_OSS diff --git a/contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.h b/contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.h deleted file mode 100644 index 65dea60..0000000 --- a/contrib/SDL-3.2.8/src/audio/dsp/SDL_dspaudio.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_dspaudio_h_ -#define SDL_dspaudio_h_ - -#include "../SDL_sysaudio.h" - -struct SDL_PrivateAudioData -{ - // The file descriptor for the audio device - int audio_fd; - - // Raw mixing buffer - Uint8 *mixbuf; -}; - -#endif // SDL_dspaudio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/dummy/SDL_dummyaudio.c b/contrib/SDL-3.2.8/src/audio/dummy/SDL_dummyaudio.c deleted file mode 100644 index d0f1a1a..0000000 --- a/contrib/SDL-3.2.8/src/audio/dummy/SDL_dummyaudio.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -// Output audio to nowhere... - -#include "../SDL_sysaudio.h" -#include "SDL_dummyaudio.h" - -#if defined(SDL_PLATFORM_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__) -#include -#endif - -static bool DUMMYAUDIO_WaitDevice(SDL_AudioDevice *device) -{ - SDL_Delay(device->hidden->io_delay); - return true; -} - -static bool DUMMYAUDIO_OpenDevice(SDL_AudioDevice *device) -{ - device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } - - if (!device->recording) { - device->hidden->mixbuf = (Uint8 *) SDL_malloc(device->buffer_size); - if (!device->hidden->mixbuf) { - return false; - } - } - - device->hidden->io_delay = ((device->sample_frames * 1000) / device->spec.freq); - - const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DUMMY_TIMESCALE); - if (hint) { - double scale = SDL_atof(hint); - if (scale >= 0.0) { - device->hidden->io_delay = (Uint32)SDL_round(device->hidden->io_delay * scale); - } - } - - // on Emscripten without threads, we just fire a repeating timer to consume audio. - #if defined(SDL_PLATFORM_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__) - MAIN_THREAD_EM_ASM({ - var a = Module['SDL3'].dummy_audio; - if (a.timers[$0] !== undefined) { clearInterval(a.timers[$0]); } - a.timers[$0] = setInterval(function() { dynCall('vi', $3, [$4]); }, ($1 / $2) * 1000); - }, device->recording ? 1 : 0, device->sample_frames, device->spec.freq, device->recording ? SDL_RecordingAudioThreadIterate : SDL_PlaybackAudioThreadIterate, device); - #endif - - return true; // we're good; don't change reported device format. -} - -static void DUMMYAUDIO_CloseDevice(SDL_AudioDevice *device) -{ - if (device->hidden) { - // on Emscripten without threads, we just fire a repeating timer to consume audio. - #if defined(SDL_PLATFORM_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__) - MAIN_THREAD_EM_ASM({ - var a = Module['SDL3'].dummy_audio; - if (a.timers[$0] !== undefined) { clearInterval(a.timers[$0]); } - a.timers[$0] = undefined; - }, device->recording ? 1 : 0); - #endif - SDL_free(device->hidden->mixbuf); - SDL_free(device->hidden); - device->hidden = NULL; - } -} - -static Uint8 *DUMMYAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - return device->hidden->mixbuf; -} - -static int DUMMYAUDIO_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - // always return a full buffer of silence. - SDL_memset(buffer, device->silence_value, buflen); - return buflen; -} - -static bool DUMMYAUDIO_Init(SDL_AudioDriverImpl *impl) -{ - impl->OpenDevice = DUMMYAUDIO_OpenDevice; - impl->CloseDevice = DUMMYAUDIO_CloseDevice; - impl->WaitDevice = DUMMYAUDIO_WaitDevice; - impl->GetDeviceBuf = DUMMYAUDIO_GetDeviceBuf; - impl->WaitRecordingDevice = DUMMYAUDIO_WaitDevice; - impl->RecordDevice = DUMMYAUDIO_RecordDevice; - - impl->OnlyHasDefaultPlaybackDevice = true; - impl->OnlyHasDefaultRecordingDevice = true; - impl->HasRecordingSupport = true; - - // on Emscripten without threads, we just fire a repeating timer to consume audio. - #if defined(SDL_PLATFORM_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__) - MAIN_THREAD_EM_ASM({ - if (typeof(Module['SDL3']) === 'undefined') { - Module['SDL3'] = {}; - } - Module['SDL3'].dummy_audio = {}; - Module['SDL3'].dummy_audio.timers = []; - Module['SDL3'].dummy_audio.timers[0] = undefined; - Module['SDL3'].dummy_audio.timers[1] = undefined; - }); - impl->ProvidesOwnCallbackThread = true; - #endif - - return true; -} - -AudioBootStrap DUMMYAUDIO_bootstrap = { - "dummy", "SDL dummy audio driver", DUMMYAUDIO_Init, true, false -}; diff --git a/contrib/SDL-3.2.8/src/audio/dummy/SDL_dummyaudio.h b/contrib/SDL-3.2.8/src/audio/dummy/SDL_dummyaudio.h deleted file mode 100644 index d5e75c7..0000000 --- a/contrib/SDL-3.2.8/src/audio/dummy/SDL_dummyaudio.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_dummyaudio_h_ -#define SDL_dummyaudio_h_ - -#include "../SDL_sysaudio.h" - -struct SDL_PrivateAudioData -{ - Uint8 *mixbuf; // The file descriptor for the audio device - Uint32 io_delay; // milliseconds to sleep in WaitDevice. -}; - -#endif // SDL_dummyaudio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/emscripten/SDL_emscriptenaudio.c b/contrib/SDL-3.2.8/src/audio/emscripten/SDL_emscriptenaudio.c deleted file mode 100644 index 55fb5b4..0000000 --- a/contrib/SDL-3.2.8/src/audio/emscripten/SDL_emscriptenaudio.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_EMSCRIPTEN - -#include "../SDL_sysaudio.h" -#include "SDL_emscriptenaudio.h" - -#include - -// just turn off clang-format for this whole file, this INDENT_OFF stuff on -// each EM_ASM section is ugly. -/* *INDENT-OFF* */ // clang-format off - -static Uint8 *EMSCRIPTENAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - return device->hidden->mixbuf; -} - -static bool EMSCRIPTENAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) -{ - const int framelen = SDL_AUDIO_FRAMESIZE(device->spec); - MAIN_THREAD_EM_ASM({ - /* Convert incoming buf pointer to a HEAPF32 offset. */ - #ifdef __wasm64__ - var buf = $0 / 4; - #else - var buf = $0 >>> 2; - #endif - - var SDL3 = Module['SDL3']; - var numChannels = SDL3.audio_playback.currentPlaybackBuffer['numberOfChannels']; - for (var c = 0; c < numChannels; ++c) { - var channelData = SDL3.audio_playback.currentPlaybackBuffer['getChannelData'](c); - if (channelData.length != $1) { - throw 'Web Audio playback buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!'; - } - - for (var j = 0; j < $1; ++j) { - channelData[j] = HEAPF32[buf + (j*numChannels + c)]; - } - } - }, buffer, buffer_size / framelen); - return true; -} - - -static void EMSCRIPTENAUDIO_FlushRecording(SDL_AudioDevice *device) -{ - // Do nothing, the new data will just be dropped. -} - -static int EMSCRIPTENAUDIO_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - MAIN_THREAD_EM_ASM({ - var SDL3 = Module['SDL3']; - var numChannels = SDL3.audio_recording.currentRecordingBuffer.numberOfChannels; - for (var c = 0; c < numChannels; ++c) { - var channelData = SDL3.audio_recording.currentRecordingBuffer.getChannelData(c); - if (channelData.length != $1) { - throw 'Web Audio recording buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!'; - } - - if (numChannels == 1) { // fastpath this a little for the common (mono) case. - for (var j = 0; j < $1; ++j) { - setValue($0 + (j * 4), channelData[j], 'float'); - } - } else { - for (var j = 0; j < $1; ++j) { - setValue($0 + (((j * numChannels) + c) * 4), channelData[j], 'float'); - } - } - } - }, buffer, (buflen / sizeof(float)) / device->spec.channels); - - return buflen; -} - -static void EMSCRIPTENAUDIO_CloseDevice(SDL_AudioDevice *device) -{ - if (!device->hidden) { - return; - } - - MAIN_THREAD_EM_ASM({ - var SDL3 = Module['SDL3']; - if ($0) { - if (SDL3.audio_recording.silenceTimer !== undefined) { - clearInterval(SDL3.audio_recording.silenceTimer); - } - if (SDL3.audio_recording.stream !== undefined) { - var tracks = SDL3.audio_recording.stream.getAudioTracks(); - for (var i = 0; i < tracks.length; i++) { - SDL3.audio_recording.stream.removeTrack(tracks[i]); - } - } - if (SDL3.audio_recording.scriptProcessorNode !== undefined) { - SDL3.audio_recording.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) {}; - SDL3.audio_recording.scriptProcessorNode.disconnect(); - } - if (SDL3.audio_recording.mediaStreamNode !== undefined) { - SDL3.audio_recording.mediaStreamNode.disconnect(); - } - SDL3.audio_recording = undefined; - } else { - if (SDL3.audio_playback.scriptProcessorNode != undefined) { - SDL3.audio_playback.scriptProcessorNode.disconnect(); - } - if (SDL3.audio_playback.silenceTimer !== undefined) { - clearInterval(SDL3.audio_playback.silenceTimer); - } - SDL3.audio_playback = undefined; - } - if ((SDL3.audioContext !== undefined) && (SDL3.audio_playback === undefined) && (SDL3.audio_recording === undefined)) { - SDL3.audioContext.close(); - SDL3.audioContext = undefined; - } - }, device->recording); - - SDL_free(device->hidden->mixbuf); - SDL_free(device->hidden); - device->hidden = NULL; - - SDL_AudioThreadFinalize(device); -} - -EM_JS_DEPS(sdlaudio, "$autoResumeAudioContext,$dynCall"); - -static bool EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device) -{ - // based on parts of library_sdl.js - - // create context - const bool result = MAIN_THREAD_EM_ASM_INT({ - if (typeof(Module['SDL3']) === 'undefined') { - Module['SDL3'] = {}; - } - var SDL3 = Module['SDL3']; - if (!$0) { - SDL3.audio_playback = {}; - } else { - SDL3.audio_recording = {}; - } - - if (!SDL3.audioContext) { - if (typeof(AudioContext) !== 'undefined') { - SDL3.audioContext = new AudioContext(); - } else if (typeof(webkitAudioContext) !== 'undefined') { - SDL3.audioContext = new webkitAudioContext(); - } - if (SDL3.audioContext) { - if ((typeof navigator.userActivation) === 'undefined') { - autoResumeAudioContext(SDL3.audioContext); - } - } - } - return (SDL3.audioContext !== undefined); - }, device->recording); - - if (!result) { - return SDL_SetError("Web Audio API is not available!"); - } - - device->spec.format = SDL_AUDIO_F32; // web audio only supports floats - - // Initialize all variables that we clean on shutdown - device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } - - // limit to native freq - device->spec.freq = EM_ASM_INT({ return Module['SDL3'].audioContext.sampleRate; }); - device->sample_frames = SDL_GetDefaultSampleFramesFromFreq(device->spec.freq) * 2; // double the buffer size, some browsers need more, and we'll just have to live with the latency. - - SDL_UpdatedAudioDeviceFormat(device); - - if (!device->recording) { - device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size); - if (!device->hidden->mixbuf) { - return false; - } - SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size); - } - - if (device->recording) { - /* The idea is to take the recording media stream, hook it up to an - audio graph where we can pass it through a ScriptProcessorNode - to access the raw PCM samples and push them to the SDL app's - callback. From there, we "process" the audio data into silence - and forget about it. - - This should, strictly speaking, use MediaRecorder for recording, but - this API is cleaner to use and better supported, and fires a - callback whenever there's enough data to fire down into the app. - The downside is that we are spending CPU time silencing a buffer - that the audiocontext uselessly mixes into any playback. On the - upside, both of those things are not only run in native code in - the browser, they're probably SIMD code, too. MediaRecorder - feels like it's a pretty inefficient tapdance in similar ways, - to be honest. */ - - MAIN_THREAD_EM_ASM({ - var SDL3 = Module['SDL3']; - var have_microphone = function(stream) { - //console.log('SDL audio recording: we have a microphone! Replacing silence callback.'); - if (SDL3.audio_recording.silenceTimer !== undefined) { - clearInterval(SDL3.audio_recording.silenceTimer); - SDL3.audio_recording.silenceTimer = undefined; - SDL3.audio_recording.silenceBuffer = undefined - } - SDL3.audio_recording.mediaStreamNode = SDL3.audioContext.createMediaStreamSource(stream); - SDL3.audio_recording.scriptProcessorNode = SDL3.audioContext.createScriptProcessor($1, $0, 1); - SDL3.audio_recording.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) { - if ((SDL3 === undefined) || (SDL3.audio_recording === undefined)) { return; } - audioProcessingEvent.outputBuffer.getChannelData(0).fill(0.0); - SDL3.audio_recording.currentRecordingBuffer = audioProcessingEvent.inputBuffer; - dynCall('ip', $2, [$3]); - }; - SDL3.audio_recording.mediaStreamNode.connect(SDL3.audio_recording.scriptProcessorNode); - SDL3.audio_recording.scriptProcessorNode.connect(SDL3.audioContext.destination); - SDL3.audio_recording.stream = stream; - }; - - var no_microphone = function(error) { - //console.log('SDL audio recording: we DO NOT have a microphone! (' + error.name + ')...leaving silence callback running.'); - }; - - // we write silence to the audio callback until the microphone is available (user approves use, etc). - SDL3.audio_recording.silenceBuffer = SDL3.audioContext.createBuffer($0, $1, SDL3.audioContext.sampleRate); - SDL3.audio_recording.silenceBuffer.getChannelData(0).fill(0.0); - var silence_callback = function() { - SDL3.audio_recording.currentRecordingBuffer = SDL3.audio_recording.silenceBuffer; - dynCall('ip', $2, [$3]); - }; - - SDL3.audio_recording.silenceTimer = setInterval(silence_callback, ($1 / SDL3.audioContext.sampleRate) * 1000); - - if ((navigator.mediaDevices !== undefined) && (navigator.mediaDevices.getUserMedia !== undefined)) { - navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(have_microphone).catch(no_microphone); - } else if (navigator.webkitGetUserMedia !== undefined) { - navigator.webkitGetUserMedia({ audio: true, video: false }, have_microphone, no_microphone); - } - }, device->spec.channels, device->sample_frames, SDL_RecordingAudioThreadIterate, device); - } else { - // setup a ScriptProcessorNode - MAIN_THREAD_EM_ASM({ - var SDL3 = Module['SDL3']; - SDL3.audio_playback.scriptProcessorNode = SDL3.audioContext['createScriptProcessor']($1, 0, $0); - SDL3.audio_playback.scriptProcessorNode['onaudioprocess'] = function (e) { - if ((SDL3 === undefined) || (SDL3.audio_playback === undefined)) { return; } - // if we're actually running the node, we don't need the fake callback anymore, so kill it. - if (SDL3.audio_playback.silenceTimer !== undefined) { - clearInterval(SDL3.audio_playback.silenceTimer); - SDL3.audio_playback.silenceTimer = undefined; - SDL3.audio_playback.silenceBuffer = undefined; - } - SDL3.audio_playback.currentPlaybackBuffer = e['outputBuffer']; - dynCall('ip', $2, [$3]); - }; - - SDL3.audio_playback.scriptProcessorNode['connect'](SDL3.audioContext['destination']); - - if (SDL3.audioContext.state === 'suspended') { // uhoh, autoplay is blocked. - SDL3.audio_playback.silenceBuffer = SDL3.audioContext.createBuffer($0, $1, SDL3.audioContext.sampleRate); - SDL3.audio_playback.silenceBuffer.getChannelData(0).fill(0.0); - var silence_callback = function() { - if ((typeof navigator.userActivation) !== 'undefined') { - if (navigator.userActivation.hasBeenActive) { - SDL3.audioContext.resume(); - } - } - - // the buffer that gets filled here just gets ignored, so the app can make progress - // and/or avoid flooding audio queues until we can actually play audio. - SDL3.audio_playback.currentPlaybackBuffer = SDL3.audio_playback.silenceBuffer; - dynCall('ip', $2, [$3]); - SDL3.audio_playback.currentPlaybackBuffer = undefined; - }; - - SDL3.audio_playback.silenceTimer = setInterval(silence_callback, ($1 / SDL3.audioContext.sampleRate) * 1000); - } - }, device->spec.channels, device->sample_frames, SDL_PlaybackAudioThreadIterate, device); - } - - return true; -} - -static bool EMSCRIPTENAUDIO_Init(SDL_AudioDriverImpl *impl) -{ - bool available, recording_available; - - impl->OpenDevice = EMSCRIPTENAUDIO_OpenDevice; - impl->CloseDevice = EMSCRIPTENAUDIO_CloseDevice; - impl->GetDeviceBuf = EMSCRIPTENAUDIO_GetDeviceBuf; - impl->PlayDevice = EMSCRIPTENAUDIO_PlayDevice; - impl->FlushRecording = EMSCRIPTENAUDIO_FlushRecording; - impl->RecordDevice = EMSCRIPTENAUDIO_RecordDevice; - - impl->OnlyHasDefaultPlaybackDevice = true; - - // technically, this is just runs in idle time in the main thread, but it's close enough to a "thread" for our purposes. - impl->ProvidesOwnCallbackThread = true; - - // check availability - available = MAIN_THREAD_EM_ASM_INT({ - if (typeof(AudioContext) !== 'undefined') { - return true; - } else if (typeof(webkitAudioContext) !== 'undefined') { - return true; - } - return false; - }); - - if (!available) { - SDL_SetError("No audio context available"); - } - - recording_available = available && MAIN_THREAD_EM_ASM_INT({ - if ((typeof(navigator.mediaDevices) !== 'undefined') && (typeof(navigator.mediaDevices.getUserMedia) !== 'undefined')) { - return true; - } else if (typeof(navigator.webkitGetUserMedia) !== 'undefined') { - return true; - } - return false; - }); - - impl->HasRecordingSupport = recording_available; - impl->OnlyHasDefaultRecordingDevice = recording_available; - - return available; -} - -AudioBootStrap EMSCRIPTENAUDIO_bootstrap = { - "emscripten", "SDL emscripten audio driver", EMSCRIPTENAUDIO_Init, false, false -}; - -/* *INDENT-ON* */ // clang-format on - -#endif // SDL_AUDIO_DRIVER_EMSCRIPTEN diff --git a/contrib/SDL-3.2.8/src/audio/emscripten/SDL_emscriptenaudio.h b/contrib/SDL-3.2.8/src/audio/emscripten/SDL_emscriptenaudio.h deleted file mode 100644 index aaf761b..0000000 --- a/contrib/SDL-3.2.8/src/audio/emscripten/SDL_emscriptenaudio.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_emscriptenaudio_h_ -#define SDL_emscriptenaudio_h_ - -#include "../SDL_sysaudio.h" - -struct SDL_PrivateAudioData -{ - Uint8 *mixbuf; -}; - -#endif // SDL_emscriptenaudio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/haiku/SDL_haikuaudio.cc b/contrib/SDL-3.2.8/src/audio/haiku/SDL_haikuaudio.cc deleted file mode 100644 index 730b107..0000000 --- a/contrib/SDL-3.2.8/src/audio/haiku/SDL_haikuaudio.cc +++ /dev/null @@ -1,222 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_HAIKU - -// Allow access to the audio stream on Haiku - -#include -#include - -#include "../../core/haiku/SDL_BeApp.h" - -extern "C" -{ - -#include "../SDL_sysaudio.h" -#include "SDL_haikuaudio.h" - -} - -static Uint8 *HAIKUAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - SDL_assert(device->hidden->current_buffer != NULL); - SDL_assert(device->hidden->current_buffer_len > 0); - *buffer_size = device->hidden->current_buffer_len; - return device->hidden->current_buffer; -} - -static bool HAIKUAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) -{ - // We already wrote our output right into the BSoundPlayer's callback's stream. Just clean up our stuff. - SDL_assert(device->hidden->current_buffer != NULL); - SDL_assert(device->hidden->current_buffer_len > 0); - device->hidden->current_buffer = NULL; - device->hidden->current_buffer_len = 0; - return true; -} - -// The Haiku callback for handling the audio buffer -static void FillSound(void *data, void *stream, size_t len, const media_raw_audio_format & format) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *)data; - SDL_assert(device->hidden->current_buffer == NULL); - SDL_assert(device->hidden->current_buffer_len == 0); - device->hidden->current_buffer = (Uint8 *) stream; - device->hidden->current_buffer_len = (int) len; - SDL_PlaybackAudioThreadIterate(device); -} - -static void HAIKUAUDIO_CloseDevice(SDL_AudioDevice *device) -{ - if (device->hidden) { - if (device->hidden->audio_obj) { - device->hidden->audio_obj->Stop(); - delete device->hidden->audio_obj; - } - delete device->hidden; - device->hidden = NULL; - SDL_AudioThreadFinalize(device); - } -} - - -static const int sig_list[] = { - SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM, SIGTERM, SIGWINCH, 0 -}; - -static inline void MaskSignals(sigset_t * omask) -{ - sigset_t mask; - int i; - - sigemptyset(&mask); - for (i = 0; sig_list[i]; ++i) { - sigaddset(&mask, sig_list[i]); - } - sigprocmask(SIG_BLOCK, &mask, omask); -} - -static inline void UnmaskSignals(sigset_t * omask) -{ - sigprocmask(SIG_SETMASK, omask, NULL); -} - - -static bool HAIKUAUDIO_OpenDevice(SDL_AudioDevice *device) -{ - // Initialize all variables that we clean on shutdown - device->hidden = new SDL_PrivateAudioData; - if (!device->hidden) { - return false; - } - SDL_zerop(device->hidden); - - // Parse the audio format and fill the Be raw audio format - media_raw_audio_format format; - SDL_zero(format); - format.byte_order = B_MEDIA_LITTLE_ENDIAN; - format.frame_rate = (float) device->spec.freq; - format.channel_count = device->spec.channels; // !!! FIXME: support > 2? - - SDL_AudioFormat test_format; - const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); - while ((test_format = *(closefmts++)) != 0) { - switch (test_format) { - case SDL_AUDIO_S8: - format.format = media_raw_audio_format::B_AUDIO_CHAR; - break; - - case SDL_AUDIO_U8: - format.format = media_raw_audio_format::B_AUDIO_UCHAR; - break; - - case SDL_AUDIO_S16LE: - format.format = media_raw_audio_format::B_AUDIO_SHORT; - break; - - case SDL_AUDIO_S16BE: - format.format = media_raw_audio_format::B_AUDIO_SHORT; - format.byte_order = B_MEDIA_BIG_ENDIAN; - break; - - case SDL_AUDIO_S32LE: - format.format = media_raw_audio_format::B_AUDIO_INT; - break; - - case SDL_AUDIO_S32BE: - format.format = media_raw_audio_format::B_AUDIO_INT; - format.byte_order = B_MEDIA_BIG_ENDIAN; - break; - - case SDL_AUDIO_F32LE: - format.format = media_raw_audio_format::B_AUDIO_FLOAT; - break; - - case SDL_AUDIO_F32BE: - format.format = media_raw_audio_format::B_AUDIO_FLOAT; - format.byte_order = B_MEDIA_BIG_ENDIAN; - break; - - default: - continue; - } - break; - } - - if (!test_format) { // shouldn't happen, but just in case... - return SDL_SetError("HAIKU: Unsupported audio format"); - } - device->spec.format = test_format; - - // Calculate the final parameters for this audio specification - SDL_UpdatedAudioDeviceFormat(device); - - format.buffer_size = device->buffer_size; - - // Subscribe to the audio stream (creates a new thread) - sigset_t omask; - MaskSignals(&omask); - device->hidden->audio_obj = new BSoundPlayer(&format, "SDL Audio", - FillSound, NULL, device); - UnmaskSignals(&omask); - - if (device->hidden->audio_obj->Start() == B_NO_ERROR) { - device->hidden->audio_obj->SetHasData(true); - } else { - return SDL_SetError("Unable to start Haiku audio"); - } - - return true; // We're running! -} - -static void HAIKUAUDIO_Deinitialize(void) -{ - SDL_QuitBeApp(); -} - -static bool HAIKUAUDIO_Init(SDL_AudioDriverImpl *impl) -{ - if (!SDL_InitBeApp()) { - return false; - } - - // Set the function pointers - impl->OpenDevice = HAIKUAUDIO_OpenDevice; - impl->GetDeviceBuf = HAIKUAUDIO_GetDeviceBuf; - impl->PlayDevice = HAIKUAUDIO_PlayDevice; - impl->CloseDevice = HAIKUAUDIO_CloseDevice; - impl->Deinitialize = HAIKUAUDIO_Deinitialize; - impl->ProvidesOwnCallbackThread = true; - impl->OnlyHasDefaultPlaybackDevice = true; - - return true; -} - - -extern "C" { extern AudioBootStrap HAIKUAUDIO_bootstrap; } - -AudioBootStrap HAIKUAUDIO_bootstrap = { - "haiku", "Haiku BSoundPlayer", HAIKUAUDIO_Init, false, false -}; - -#endif // SDL_AUDIO_DRIVER_HAIKU diff --git a/contrib/SDL-3.2.8/src/audio/haiku/SDL_haikuaudio.h b/contrib/SDL-3.2.8/src/audio/haiku/SDL_haikuaudio.h deleted file mode 100644 index 9547546..0000000 --- a/contrib/SDL-3.2.8/src/audio/haiku/SDL_haikuaudio.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_haikuaudio_h_ -#define SDL_haikuaudio_h_ - -#include "../SDL_sysaudio.h" - -struct SDL_PrivateAudioData -{ - BSoundPlayer *audio_obj; - Uint8 *current_buffer; - int current_buffer_len; -}; - -#endif // SDL_haikuaudio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/jack/SDL_jackaudio.c b/contrib/SDL-3.2.8/src/audio/jack/SDL_jackaudio.c deleted file mode 100644 index 3ae5137..0000000 --- a/contrib/SDL-3.2.8/src/audio/jack/SDL_jackaudio.c +++ /dev/null @@ -1,435 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_JACK - -#include "../SDL_sysaudio.h" -#include "SDL_jackaudio.h" -#include "../../thread/SDL_systhread.h" - -static jack_client_t *(*JACK_jack_client_open)(const char *, jack_options_t, jack_status_t *, ...); -static int (*JACK_jack_client_close)(jack_client_t *); -static void (*JACK_jack_on_shutdown)(jack_client_t *, JackShutdownCallback, void *); -static int (*JACK_jack_activate)(jack_client_t *); -static int (*JACK_jack_deactivate)(jack_client_t *); -static void *(*JACK_jack_port_get_buffer)(jack_port_t *, jack_nframes_t); -static int (*JACK_jack_port_unregister)(jack_client_t *, jack_port_t *); -static void (*JACK_jack_free)(void *); -static const char **(*JACK_jack_get_ports)(jack_client_t *, const char *, const char *, unsigned long); -static jack_nframes_t (*JACK_jack_get_sample_rate)(jack_client_t *); -static jack_nframes_t (*JACK_jack_get_buffer_size)(jack_client_t *); -static jack_port_t *(*JACK_jack_port_register)(jack_client_t *, const char *, const char *, unsigned long, unsigned long); -static jack_port_t *(*JACK_jack_port_by_name)(jack_client_t *, const char *); -static const char *(*JACK_jack_port_name)(const jack_port_t *); -static const char *(*JACK_jack_port_type)(const jack_port_t *); -static int (*JACK_jack_connect)(jack_client_t *, const char *, const char *); -static int (*JACK_jack_set_process_callback)(jack_client_t *, JackProcessCallback, void *); -static int (*JACK_jack_set_sample_rate_callback)(jack_client_t *, JackSampleRateCallback, void *); -static int (*JACK_jack_set_buffer_size_callback)(jack_client_t *, JackBufferSizeCallback, void *); - -static bool load_jack_syms(void); - -#ifdef SDL_AUDIO_DRIVER_JACK_DYNAMIC - -static const char *jack_library = SDL_AUDIO_DRIVER_JACK_DYNAMIC; -static SDL_SharedObject *jack_handle = NULL; - -// !!! FIXME: this is copy/pasted in several places now -static bool load_jack_sym(const char *fn, void **addr) -{ - *addr = SDL_LoadFunction(jack_handle, fn); - if (!*addr) { - // Don't call SDL_SetError(): SDL_LoadFunction already did. - return false; - } - - return true; -} - -// cast funcs to char* first, to please GCC's strict aliasing rules. -#define SDL_JACK_SYM(x) \ - if (!load_jack_sym(#x, (void **)(char *)&JACK_##x)) \ - return false - -static void UnloadJackLibrary(void) -{ - if (jack_handle) { - SDL_UnloadObject(jack_handle); - jack_handle = NULL; - } -} - -static bool LoadJackLibrary(void) -{ - bool result = true; - if (!jack_handle) { - jack_handle = SDL_LoadObject(jack_library); - if (!jack_handle) { - result = false; - // Don't call SDL_SetError(): SDL_LoadObject already did. - } else { - result = load_jack_syms(); - if (!result) { - UnloadJackLibrary(); - } - } - } - return result; -} - -#else - -#define SDL_JACK_SYM(x) JACK_##x = x - -static void UnloadJackLibrary(void) -{ -} - -static bool LoadJackLibrary(void) -{ - load_jack_syms(); - return true; -} - -#endif // SDL_AUDIO_DRIVER_JACK_DYNAMIC - -static bool load_jack_syms(void) -{ - SDL_JACK_SYM(jack_client_open); - SDL_JACK_SYM(jack_client_close); - SDL_JACK_SYM(jack_on_shutdown); - SDL_JACK_SYM(jack_activate); - SDL_JACK_SYM(jack_deactivate); - SDL_JACK_SYM(jack_port_get_buffer); - SDL_JACK_SYM(jack_port_unregister); - SDL_JACK_SYM(jack_free); - SDL_JACK_SYM(jack_get_ports); - SDL_JACK_SYM(jack_get_sample_rate); - SDL_JACK_SYM(jack_get_buffer_size); - SDL_JACK_SYM(jack_port_register); - SDL_JACK_SYM(jack_port_by_name); - SDL_JACK_SYM(jack_port_name); - SDL_JACK_SYM(jack_port_type); - SDL_JACK_SYM(jack_connect); - SDL_JACK_SYM(jack_set_process_callback); - SDL_JACK_SYM(jack_set_sample_rate_callback); - SDL_JACK_SYM(jack_set_buffer_size_callback); - - return true; -} - -static void jackShutdownCallback(void *arg) // JACK went away; device is lost. -{ - SDL_AudioDeviceDisconnected((SDL_AudioDevice *)arg); -} - -static int jackSampleRateCallback(jack_nframes_t nframes, void *arg) -{ - //SDL_Log("JACK Sample Rate Callback! %d", (int) nframes); - SDL_AudioDevice *device = (SDL_AudioDevice *) arg; - SDL_AudioSpec newspec; - SDL_copyp(&newspec, &device->spec); - newspec.freq = (int) nframes; - if (!SDL_AudioDeviceFormatChanged(device, &newspec, device->sample_frames)) { - SDL_AudioDeviceDisconnected(device); - } - return 0; -} - -static int jackBufferSizeCallback(jack_nframes_t nframes, void *arg) -{ - //SDL_Log("JACK Buffer Size Callback! %d", (int) nframes); - SDL_AudioDevice *device = (SDL_AudioDevice *) arg; - SDL_AudioSpec newspec; - SDL_copyp(&newspec, &device->spec); - if (!SDL_AudioDeviceFormatChanged(device, &newspec, (int) nframes)) { - SDL_AudioDeviceDisconnected(device); - } - return 0; -} - -static int jackProcessPlaybackCallback(jack_nframes_t nframes, void *arg) -{ - SDL_assert(nframes == ((SDL_AudioDevice *)arg)->sample_frames); - SDL_PlaybackAudioThreadIterate((SDL_AudioDevice *)arg); - return 0; -} - -static bool JACK_PlayDevice(SDL_AudioDevice *device, const Uint8 *ui8buffer, int buflen) -{ - const float *buffer = (float *) ui8buffer; - jack_port_t **ports = device->hidden->sdlports; - const int total_channels = device->spec.channels; - const int total_frames = device->sample_frames; - const jack_nframes_t nframes = (jack_nframes_t) device->sample_frames; - - for (int channelsi = 0; channelsi < total_channels; channelsi++) { - float *dst = (float *)JACK_jack_port_get_buffer(ports[channelsi], nframes); - if (dst) { - const float *src = buffer + channelsi; - for (int framesi = 0; framesi < total_frames; framesi++) { - *(dst++) = *src; - src += total_channels; - } - } - } - - return true; -} - -static Uint8 *JACK_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - return (Uint8 *)device->hidden->iobuffer; -} - -static int jackProcessRecordingCallback(jack_nframes_t nframes, void *arg) -{ - SDL_assert(nframes == ((SDL_AudioDevice *)arg)->sample_frames); - SDL_RecordingAudioThreadIterate((SDL_AudioDevice *)arg); - return 0; -} - -static int JACK_RecordDevice(SDL_AudioDevice *device, void *vbuffer, int buflen) -{ - float *buffer = (float *) vbuffer; - jack_port_t **ports = device->hidden->sdlports; - const int total_channels = device->spec.channels; - const int total_frames = device->sample_frames; - const jack_nframes_t nframes = (jack_nframes_t) device->sample_frames; - - for (int channelsi = 0; channelsi < total_channels; channelsi++) { - const float *src = (const float *)JACK_jack_port_get_buffer(ports[channelsi], nframes); - if (src) { - float *dst = buffer + channelsi; - for (int framesi = 0; framesi < total_frames; framesi++) { - *dst = *(src++); - dst += total_channels; - } - } - } - - return buflen; -} - -static void JACK_FlushRecording(SDL_AudioDevice *device) -{ - // do nothing, the data will just be replaced next callback. -} - -static void JACK_CloseDevice(SDL_AudioDevice *device) -{ - if (device->hidden) { - if (device->hidden->client) { - JACK_jack_deactivate(device->hidden->client); - - if (device->hidden->sdlports) { - const int channels = device->spec.channels; - int i; - for (i = 0; i < channels; i++) { - JACK_jack_port_unregister(device->hidden->client, device->hidden->sdlports[i]); - } - SDL_free(device->hidden->sdlports); - } - - JACK_jack_client_close(device->hidden->client); - } - - SDL_free(device->hidden->iobuffer); - SDL_free(device->hidden); - device->hidden = NULL; - - SDL_AudioThreadFinalize(device); - } -} - -// !!! FIXME: unify this (PulseAudio has a getAppName, Pipewire has a thing, etc) -static const char *GetJackAppName(void) -{ - return SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING); -} - -static bool JACK_OpenDevice(SDL_AudioDevice *device) -{ - /* Note that JACK uses "output" for recording devices (they output audio - data to us) and "input" for playback (we input audio data to them). - Likewise, SDL's playback port will be "output" (we write data out) - and recording will be "input" (we read data in). */ - const bool recording = device->recording; - const unsigned long sysportflags = recording ? JackPortIsOutput : JackPortIsInput; - const unsigned long sdlportflags = recording ? JackPortIsInput : JackPortIsOutput; - const JackProcessCallback callback = recording ? jackProcessRecordingCallback : jackProcessPlaybackCallback; - const char *sdlportstr = recording ? "input" : "output"; - const char **devports = NULL; - int *audio_ports; - jack_client_t *client = NULL; - jack_status_t status; - int channels = 0; - int ports = 0; - int i; - - // Initialize all variables that we clean on shutdown - device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } - - client = JACK_jack_client_open(GetJackAppName(), JackNoStartServer, &status, NULL); - device->hidden->client = client; - if (!client) { - return SDL_SetError("Can't open JACK client"); - } - - devports = JACK_jack_get_ports(client, NULL, NULL, JackPortIsPhysical | sysportflags); - if (!devports || !devports[0]) { - return SDL_SetError("No physical JACK ports available"); - } - - while (devports[++ports]) { - // spin to count devports - } - - // Filter out non-audio ports - audio_ports = SDL_calloc(ports, sizeof(*audio_ports)); - for (i = 0; i < ports; i++) { - const jack_port_t *dport = JACK_jack_port_by_name(client, devports[i]); - const char *type = JACK_jack_port_type(dport); - const int len = SDL_strlen(type); - // See if type ends with "audio" - if (len >= 5 && !SDL_memcmp(type + len - 5, "audio", 5)) { - audio_ports[channels++] = i; - } - } - if (channels == 0) { - SDL_free(audio_ports); - return SDL_SetError("No physical JACK ports available"); - } - - // Jack pretty much demands what it wants. - device->spec.format = SDL_AUDIO_F32; - device->spec.freq = JACK_jack_get_sample_rate(client); - device->spec.channels = channels; - device->sample_frames = JACK_jack_get_buffer_size(client); - - SDL_UpdatedAudioDeviceFormat(device); - - if (!recording) { - device->hidden->iobuffer = (float *)SDL_calloc(1, device->buffer_size); - if (!device->hidden->iobuffer) { - SDL_free(audio_ports); - return false; - } - } - - // Build SDL's ports, which we will connect to the device ports. - device->hidden->sdlports = (jack_port_t **)SDL_calloc(channels, sizeof(jack_port_t *)); - if (!device->hidden->sdlports) { - SDL_free(audio_ports); - return false; - } - - for (i = 0; i < channels; i++) { - char portname[32]; - (void)SDL_snprintf(portname, sizeof(portname), "sdl_jack_%s_%d", sdlportstr, i); - device->hidden->sdlports[i] = JACK_jack_port_register(client, portname, JACK_DEFAULT_AUDIO_TYPE, sdlportflags, 0); - if (device->hidden->sdlports[i] == NULL) { - SDL_free(audio_ports); - return SDL_SetError("jack_port_register failed"); - } - } - - if (JACK_jack_set_buffer_size_callback(client, jackBufferSizeCallback, device) != 0) { - SDL_free(audio_ports); - return SDL_SetError("JACK: Couldn't set buffer size callback"); - } else if (JACK_jack_set_sample_rate_callback(client, jackSampleRateCallback, device) != 0) { - SDL_free(audio_ports); - return SDL_SetError("JACK: Couldn't set sample rate callback"); - } else if (JACK_jack_set_process_callback(client, callback, device) != 0) { - SDL_free(audio_ports); - return SDL_SetError("JACK: Couldn't set process callback"); - } - - JACK_jack_on_shutdown(client, jackShutdownCallback, device); - - if (JACK_jack_activate(client) != 0) { - SDL_free(audio_ports); - return SDL_SetError("Failed to activate JACK client"); - } - - // once activated, we can connect all the ports. - for (i = 0; i < channels; i++) { - const char *sdlport = JACK_jack_port_name(device->hidden->sdlports[i]); - const char *srcport = recording ? devports[audio_ports[i]] : sdlport; - const char *dstport = recording ? sdlport : devports[audio_ports[i]]; - if (JACK_jack_connect(client, srcport, dstport) != 0) { - SDL_free(audio_ports); - return SDL_SetError("Couldn't connect JACK ports: %s => %s", srcport, dstport); - } - } - - // don't need these anymore. - JACK_jack_free(devports); - SDL_free(audio_ports); - - // We're ready to rock and roll. :-) - return true; -} - -static void JACK_Deinitialize(void) -{ - UnloadJackLibrary(); -} - -static bool JACK_Init(SDL_AudioDriverImpl *impl) -{ - if (!LoadJackLibrary()) { - return false; - } else { - // Make sure a JACK server is running and available. - jack_status_t status; - jack_client_t *client = JACK_jack_client_open("SDL", JackNoStartServer, &status, NULL); - if (!client) { - UnloadJackLibrary(); - return SDL_SetError("Can't open JACK client"); - } - JACK_jack_client_close(client); - } - - impl->OpenDevice = JACK_OpenDevice; - impl->GetDeviceBuf = JACK_GetDeviceBuf; - impl->PlayDevice = JACK_PlayDevice; - impl->CloseDevice = JACK_CloseDevice; - impl->Deinitialize = JACK_Deinitialize; - impl->RecordDevice = JACK_RecordDevice; - impl->FlushRecording = JACK_FlushRecording; - impl->OnlyHasDefaultPlaybackDevice = true; - impl->OnlyHasDefaultRecordingDevice = true; - impl->HasRecordingSupport = true; - impl->ProvidesOwnCallbackThread = true; - - return true; -} - -AudioBootStrap JACK_bootstrap = { - "jack", "JACK Audio Connection Kit", JACK_Init, false, false -}; - -#endif // SDL_AUDIO_DRIVER_JACK diff --git a/contrib/SDL-3.2.8/src/audio/jack/SDL_jackaudio.h b/contrib/SDL-3.2.8/src/audio/jack/SDL_jackaudio.h deleted file mode 100644 index 4f972d5..0000000 --- a/contrib/SDL-3.2.8/src/audio/jack/SDL_jackaudio.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#ifndef SDL_jackaudio_h_ -#define SDL_jackaudio_h_ - -#include - -#include "../SDL_sysaudio.h" - -struct SDL_PrivateAudioData -{ - jack_client_t *client; - jack_port_t **sdlports; - float *iobuffer; -}; - -#endif // SDL_jackaudio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/n3ds/SDL_n3dsaudio.c b/contrib/SDL-3.2.8/src/audio/n3ds/SDL_n3dsaudio.c deleted file mode 100644 index 780b06c..0000000 --- a/contrib/SDL-3.2.8/src/audio/n3ds/SDL_n3dsaudio.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_N3DS - -// N3DS Audio driver - -#include "../SDL_sysaudio.h" -#include "SDL_n3dsaudio.h" - -#define N3DSAUDIO_DRIVER_NAME "n3ds" - -static dspHookCookie dsp_hook; -static SDL_AudioDevice *audio_device; - -// fully local functions related to the wavebufs / DSP, not the same as the `device->lock` SDL_Mutex! -static SDL_INLINE void contextLock(SDL_AudioDevice *device) -{ - LightLock_Lock(&device->hidden->lock); -} - -static SDL_INLINE void contextUnlock(SDL_AudioDevice *device) -{ - LightLock_Unlock(&device->hidden->lock); -} - -static void N3DSAUD_DspHook(DSP_HookType hook) -{ - if (hook == DSPHOOK_ONCANCEL) { - contextLock(audio_device); - audio_device->hidden->isCancelled = true; - SDL_AudioDeviceDisconnected(audio_device); - CondVar_Broadcast(&audio_device->hidden->cv); - contextUnlock(audio_device); - } -} - -static void AudioFrameFinished(void *vdevice) -{ - bool shouldBroadcast = false; - unsigned i; - SDL_AudioDevice *device = (SDL_AudioDevice *)vdevice; - - contextLock(device); - - for (i = 0; i < NUM_BUFFERS; i++) { - if (device->hidden->waveBuf[i].status == NDSP_WBUF_DONE) { - device->hidden->waveBuf[i].status = NDSP_WBUF_FREE; - shouldBroadcast = true; - } - } - - if (shouldBroadcast) { - CondVar_Broadcast(&device->hidden->cv); - } - - contextUnlock(device); -} - -static bool N3DSAUDIO_OpenDevice(SDL_AudioDevice *device) -{ - Result ndsp_init_res; - Uint8 *data_vaddr; - float mix[12]; - - device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } - - // Initialise the DSP service - ndsp_init_res = ndspInit(); - if (R_FAILED(ndsp_init_res)) { - if ((R_SUMMARY(ndsp_init_res) == RS_NOTFOUND) && (R_MODULE(ndsp_init_res) == RM_DSP)) { - return SDL_SetError("DSP init failed: dspfirm.cdc missing!"); - } else { - return SDL_SetError("DSP init failed. Error code: 0x%lX", ndsp_init_res); - } - } - - // Initialise internal state - LightLock_Init(&device->hidden->lock); - CondVar_Init(&device->hidden->cv); - - if (device->spec.channels > 2) { - device->spec.channels = 2; - } - - Uint32 format = 0; - SDL_AudioFormat test_format; - const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); - while ((test_format = *(closefmts++)) != 0) { - if (test_format == SDL_AUDIO_S8) { // Signed 8-bit audio supported - format = (device->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM8 : NDSP_FORMAT_MONO_PCM8; - break; - } else if (test_format == SDL_AUDIO_S16) { // Signed 16-bit audio supported - format = (device->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM16 : NDSP_FORMAT_MONO_PCM16; - break; - } - } - - if (!test_format) { // shouldn't happen, but just in case... - return SDL_SetError("No supported audio format found."); - } - - device->spec.format = test_format; - - // Update the fragment size as size in bytes - SDL_UpdatedAudioDeviceFormat(device); - - // Allocate mixing buffer - if (device->buffer_size >= SDL_MAX_UINT32 / 2) { - return SDL_SetError("Mixing buffer is too large."); - } - - device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size); - if (!device->hidden->mixbuf) { - return false; - } - - SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size); - - data_vaddr = (Uint8 *)linearAlloc(device->buffer_size * NUM_BUFFERS); - if (!data_vaddr) { - return SDL_OutOfMemory(); - } - - SDL_memset(data_vaddr, 0, device->buffer_size * NUM_BUFFERS); - DSP_FlushDataCache(data_vaddr, device->buffer_size * NUM_BUFFERS); - - device->hidden->nextbuf = 0; - - ndspChnReset(0); - - ndspChnSetInterp(0, NDSP_INTERP_LINEAR); - ndspChnSetRate(0, device->spec.freq); - ndspChnSetFormat(0, format); - - SDL_zeroa(mix); - mix[0] = mix[1] = 1.0f; - ndspChnSetMix(0, mix); - - SDL_memset(device->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS); - - const int sample_frame_size = SDL_AUDIO_FRAMESIZE(device->spec); - for (unsigned i = 0; i < NUM_BUFFERS; i++) { - device->hidden->waveBuf[i].data_vaddr = data_vaddr; - device->hidden->waveBuf[i].nsamples = device->buffer_size / sample_frame_size; - data_vaddr += device->buffer_size; - } - - // Setup callback - audio_device = device; - ndspSetCallback(AudioFrameFinished, device); - dspHook(&dsp_hook, N3DSAUD_DspHook); - - return true; -} - -static bool N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) -{ - contextLock(device); - - const size_t nextbuf = device->hidden->nextbuf; - - if (device->hidden->isCancelled || - device->hidden->waveBuf[nextbuf].status != NDSP_WBUF_FREE) { - contextUnlock(device); - return true; // !!! FIXME: is this a fatal error? If so, this should return false. - } - - device->hidden->nextbuf = (nextbuf + 1) % NUM_BUFFERS; - - contextUnlock(device); - - SDL_memcpy((void *)device->hidden->waveBuf[nextbuf].data_vaddr, buffer, buflen); - DSP_FlushDataCache(device->hidden->waveBuf[nextbuf].data_vaddr, buflen); - - ndspChnWaveBufAdd(0, &device->hidden->waveBuf[nextbuf]); - - return true; -} - -static bool N3DSAUDIO_WaitDevice(SDL_AudioDevice *device) -{ - contextLock(device); - while (!device->hidden->isCancelled && !SDL_GetAtomicInt(&device->shutdown) && - device->hidden->waveBuf[device->hidden->nextbuf].status != NDSP_WBUF_FREE) { - CondVar_Wait(&device->hidden->cv, &device->hidden->lock); - } - contextUnlock(device); - return true; -} - -static Uint8 *N3DSAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - return device->hidden->mixbuf; -} - -static void N3DSAUDIO_CloseDevice(SDL_AudioDevice *device) -{ - if (!device->hidden) { - return; - } - - contextLock(device); - - dspUnhook(&dsp_hook); - ndspSetCallback(NULL, NULL); - - if (!device->hidden->isCancelled) { - ndspChnReset(0); - SDL_memset(device->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS); - CondVar_Broadcast(&device->hidden->cv); - } - - contextUnlock(device); - - ndspExit(); - - if (device->hidden->waveBuf[0].data_vaddr) { - linearFree((void *)device->hidden->waveBuf[0].data_vaddr); - } - - if (device->hidden->mixbuf) { - SDL_free(device->hidden->mixbuf); - device->hidden->mixbuf = NULL; - } - - SDL_free(device->hidden); - device->hidden = NULL; -} - -static void N3DSAUDIO_ThreadInit(SDL_AudioDevice *device) -{ - s32 current_priority = 0x30; - svcGetThreadPriority(¤t_priority, CUR_THREAD_HANDLE); - current_priority--; - // 0x18 is reserved for video, 0x30 is the default for main thread - current_priority = SDL_clamp(current_priority, 0x19, 0x2F); - svcSetThreadPriority(CUR_THREAD_HANDLE, current_priority); -} - -static bool N3DSAUDIO_Init(SDL_AudioDriverImpl *impl) -{ - impl->OpenDevice = N3DSAUDIO_OpenDevice; - impl->PlayDevice = N3DSAUDIO_PlayDevice; - impl->WaitDevice = N3DSAUDIO_WaitDevice; - impl->GetDeviceBuf = N3DSAUDIO_GetDeviceBuf; - impl->CloseDevice = N3DSAUDIO_CloseDevice; - impl->ThreadInit = N3DSAUDIO_ThreadInit; - impl->OnlyHasDefaultPlaybackDevice = true; - - // Should be possible, but micInit would fail - impl->HasRecordingSupport = false; - - return true; -} - -AudioBootStrap N3DSAUDIO_bootstrap = { - N3DSAUDIO_DRIVER_NAME, - "SDL N3DS audio driver", - N3DSAUDIO_Init, - false, - false -}; - -#endif // SDL_AUDIO_DRIVER_N3DS diff --git a/contrib/SDL-3.2.8/src/audio/n3ds/SDL_n3dsaudio.h b/contrib/SDL-3.2.8/src/audio/n3ds/SDL_n3dsaudio.h deleted file mode 100644 index c9ae4f8..0000000 --- a/contrib/SDL-3.2.8/src/audio/n3ds/SDL_n3dsaudio.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef SDL_n3dsaudio_h -#define SDL_n3dsaudio_h - -#include <3ds.h> - -#define NUM_BUFFERS 3 // -- Minimum 2! - -struct SDL_PrivateAudioData -{ - // Speaker data - Uint8 *mixbuf; - Uint32 nextbuf; - ndspWaveBuf waveBuf[NUM_BUFFERS]; - LightLock lock; - CondVar cv; - bool isCancelled; -}; - -#endif // SDL_n3dsaudio_h diff --git a/contrib/SDL-3.2.8/src/audio/netbsd/SDL_netbsdaudio.c b/contrib/SDL-3.2.8/src/audio/netbsd/SDL_netbsdaudio.c deleted file mode 100644 index 26060d3..0000000 --- a/contrib/SDL-3.2.8/src/audio/netbsd/SDL_netbsdaudio.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_NETBSD - -// Driver for native NetBSD audio(4). - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../core/unix/SDL_poll.h" -#include "../SDL_audiodev_c.h" -#include "SDL_netbsdaudio.h" - -//#define DEBUG_AUDIO - -static void NETBSDAUDIO_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) -{ - SDL_EnumUnixAudioDevices(false, NULL); -} - -static void NETBSDAUDIO_Status(SDL_AudioDevice *device) -{ -#ifdef DEBUG_AUDIO - /* *INDENT-OFF* */ // clang-format off - audio_info_t info; - const struct audio_prinfo *prinfo; - - if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) { - fprintf(stderr, "AUDIO_GETINFO failed.\n"); - return; - } - - prinfo = device->recording ? &info.record : &info.play; - - fprintf(stderr, "\n" - "[%s info]\n" - "buffer size : %d bytes\n" - "sample rate : %i Hz\n" - "channels : %i\n" - "precision : %i-bit\n" - "encoding : 0x%x\n" - "seek : %i\n" - "sample count : %i\n" - "EOF count : %i\n" - "paused : %s\n" - "error occurred : %s\n" - "waiting : %s\n" - "active : %s\n" - "", - device->recording ? "record" : "play", - prinfo->buffer_size, - prinfo->sample_rate, - prinfo->channels, - prinfo->precision, - prinfo->encoding, - prinfo->seek, - prinfo->samples, - prinfo->eof, - prinfo->pause ? "yes" : "no", - prinfo->error ? "yes" : "no", - prinfo->waiting ? "yes" : "no", - prinfo->active ? "yes" : "no"); - - fprintf(stderr, "\n" - "[audio info]\n" - "monitor_gain : %i\n" - "hw block size : %d bytes\n" - "hi watermark : %i\n" - "lo watermark : %i\n" - "audio mode : %s\n" - "", - info.monitor_gain, - info.blocksize, - info.hiwat, info.lowat, - (info.mode == AUMODE_PLAY) ? "PLAY" - : (info.mode == AUMODE_RECORD) ? "RECORD" - : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?")); - - fprintf(stderr, "\n" - "[audio spec]\n" - "format : 0x%x\n" - "size : %u\n" - "", - device->spec.format, - device->buffer_size); - /* *INDENT-ON* */ // clang-format on - -#endif // DEBUG_AUDIO -} - -static bool NETBSDAUDIO_WaitDevice(SDL_AudioDevice *device) -{ - const bool recording = device->recording; - while (!SDL_GetAtomicInt(&device->shutdown)) { - audio_info_t info; - const int rc = ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info); - if (rc < 0) { - if (errno == EAGAIN) { - continue; - } - // Hmm, not much we can do - abort - fprintf(stderr, "netbsdaudio WaitDevice ioctl failed (unrecoverable): %s\n", strerror(errno)); - return false; - } - const size_t remain = (size_t)((recording ? info.record.seek : info.play.seek) * SDL_AUDIO_BYTESIZE(device->spec.format)); - if (!recording && (remain >= device->buffer_size)) { - SDL_Delay(10); - } else if (recording && (remain < device->buffer_size)) { - SDL_Delay(10); - } else { - break; // ready to go! - } - } - - return true; -} - -static bool NETBSDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) -{ - struct SDL_PrivateAudioData *h = device->hidden; - const int written = write(h->audio_fd, buffer, buflen); - if (written != buflen) { // Treat even partial writes as fatal errors. - return false; - } - -#ifdef DEBUG_AUDIO - fprintf(stderr, "Wrote %d bytes of audio data\n", written); -#endif - return true; -} - -static Uint8 *NETBSDAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - return device->hidden->mixbuf; -} - -static int NETBSDAUDIO_RecordDevice(SDL_AudioDevice *device, void *vbuffer, int buflen) -{ - Uint8 *buffer = (Uint8 *)vbuffer; - const int br = read(device->hidden->audio_fd, buffer, buflen); - if (br == -1) { - // Non recoverable error has occurred. It should be reported!!! - perror("audio"); - return -1; - } - -#ifdef DEBUG_AUDIO - fprintf(stderr, "Recorded %d bytes of audio data\n", br); -#endif - return br; -} - -static void NETBSDAUDIO_FlushRecording(SDL_AudioDevice *device) -{ - struct SDL_PrivateAudioData *h = device->hidden; - audio_info_t info; - if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) == 0) { - size_t remain = (size_t)(info.record.seek * SDL_AUDIO_BYTESIZE(device->spec.format)); - while (remain > 0) { - char buf[512]; - const size_t len = SDL_min(sizeof(buf), remain); - const ssize_t br = read(h->audio_fd, buf, len); - if (br <= 0) { - break; - } - remain -= br; - } - } -} - -static void NETBSDAUDIO_CloseDevice(SDL_AudioDevice *device) -{ - if (device->hidden) { - if (device->hidden->audio_fd >= 0) { - close(device->hidden->audio_fd); - } - SDL_free(device->hidden->mixbuf); - SDL_free(device->hidden); - device->hidden = NULL; - } -} - -static bool NETBSDAUDIO_OpenDevice(SDL_AudioDevice *device) -{ - const bool recording = device->recording; - int encoding = AUDIO_ENCODING_NONE; - audio_info_t info, hwinfo; - struct audio_prinfo *prinfo = recording ? &info.record : &info.play; - - // Initialize all variables that we clean on shutdown - device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } - - // Open the audio device; we hardcode the device path in `device->name` for lack of better info, so use that. - const int flags = ((device->recording) ? O_RDONLY : O_WRONLY); - device->hidden->audio_fd = open(device->name, flags | O_CLOEXEC); - if (device->hidden->audio_fd < 0) { - return SDL_SetError("Couldn't open %s: %s", device->name, strerror(errno)); - } - - AUDIO_INITINFO(&info); - -#ifdef AUDIO_GETFORMAT // Introduced in NetBSD 9.0 - if (ioctl(device->hidden->audio_fd, AUDIO_GETFORMAT, &hwinfo) != -1) { - // Use the device's native sample rate so the kernel doesn't have to resample. - device->spec.freq = recording ? hwinfo.record.sample_rate : hwinfo.play.sample_rate; - } -#endif - - prinfo->sample_rate = device->spec.freq; - prinfo->channels = device->spec.channels; - - SDL_AudioFormat test_format; - const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); - while ((test_format = *(closefmts++)) != 0) { - switch (test_format) { - case SDL_AUDIO_U8: - encoding = AUDIO_ENCODING_ULINEAR; - break; - case SDL_AUDIO_S8: - encoding = AUDIO_ENCODING_SLINEAR; - break; - case SDL_AUDIO_S16LE: - encoding = AUDIO_ENCODING_SLINEAR_LE; - break; - case SDL_AUDIO_S16BE: - encoding = AUDIO_ENCODING_SLINEAR_BE; - break; - case SDL_AUDIO_S32LE: - encoding = AUDIO_ENCODING_SLINEAR_LE; - break; - case SDL_AUDIO_S32BE: - encoding = AUDIO_ENCODING_SLINEAR_BE; - break; - default: - continue; - } - break; - } - - if (!test_format) { - return SDL_SetError("%s: Unsupported audio format", "netbsd"); - } - prinfo->encoding = encoding; - prinfo->precision = SDL_AUDIO_BITSIZE(test_format); - - info.hiwat = 5; - info.lowat = 3; - if (ioctl(device->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) { - return SDL_SetError("AUDIO_SETINFO failed for %s: %s", device->name, strerror(errno)); - } - - if (ioctl(device->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) { - return SDL_SetError("AUDIO_GETINFO failed for %s: %s", device->name, strerror(errno)); - } - - // Final spec used for the device. - device->spec.format = test_format; - device->spec.freq = prinfo->sample_rate; - device->spec.channels = prinfo->channels; - - SDL_UpdatedAudioDeviceFormat(device); - - if (!recording) { - // Allocate mixing buffer - device->hidden->mixlen = device->buffer_size; - device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->hidden->mixlen); - if (!device->hidden->mixbuf) { - return false; - } - SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size); - } - - NETBSDAUDIO_Status(device); - - return true; // We're ready to rock and roll. :-) -} - -static bool NETBSDAUDIO_Init(SDL_AudioDriverImpl *impl) -{ - impl->DetectDevices = NETBSDAUDIO_DetectDevices; - impl->OpenDevice = NETBSDAUDIO_OpenDevice; - impl->WaitDevice = NETBSDAUDIO_WaitDevice; - impl->PlayDevice = NETBSDAUDIO_PlayDevice; - impl->GetDeviceBuf = NETBSDAUDIO_GetDeviceBuf; - impl->CloseDevice = NETBSDAUDIO_CloseDevice; - impl->WaitRecordingDevice = NETBSDAUDIO_WaitDevice; - impl->RecordDevice = NETBSDAUDIO_RecordDevice; - impl->FlushRecording = NETBSDAUDIO_FlushRecording; - - impl->HasRecordingSupport = true; - - return true; -} - -AudioBootStrap NETBSDAUDIO_bootstrap = { - "netbsd", "NetBSD audio", NETBSDAUDIO_Init, false, false -}; - -#endif // SDL_AUDIO_DRIVER_NETBSD diff --git a/contrib/SDL-3.2.8/src/audio/netbsd/SDL_netbsdaudio.h b/contrib/SDL-3.2.8/src/audio/netbsd/SDL_netbsdaudio.h deleted file mode 100644 index dcdc6f4..0000000 --- a/contrib/SDL-3.2.8/src/audio/netbsd/SDL_netbsdaudio.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_netbsdaudio_h_ -#define SDL_netbsdaudio_h_ - -#include "../SDL_sysaudio.h" - -struct SDL_PrivateAudioData -{ - // The file descriptor for the audio device - int audio_fd; - - // Raw mixing buffer - Uint8 *mixbuf; - int mixlen; - - // Support for audio timing using a timer, in addition to SDL_IOReady() - float frame_ticks; - float next_frame; -}; - -#define FUDGE_TICKS 10 // The scheduler overhead ticks per frame - -#endif // SDL_netbsdaudio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.c b/contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.c deleted file mode 100644 index 4d5b3bd..0000000 --- a/contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.c +++ /dev/null @@ -1,807 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_OPENSLES - -// For more discussion of low latency audio on Android, see this: -// https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html - -#include "../SDL_sysaudio.h" -#include "SDL_openslES.h" - -#include "../../core/android/SDL_android.h" -#include -#include -#include - - -#define NUM_BUFFERS 2 // -- Don't lower this! - -struct SDL_PrivateAudioData -{ - Uint8 *mixbuff; - int next_buffer; - Uint8 *pmixbuff[NUM_BUFFERS]; - SDL_Semaphore *playsem; -}; - -#if 0 -#define LOG_TAG "SDL_openslES" -#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) -#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) -//#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__) -#define LOGV(...) -#else -#define LOGE(...) -#define LOGI(...) -#define LOGV(...) -#endif - -/* -#define SL_SPEAKER_FRONT_LEFT ((SLuint32) 0x00000001) -#define SL_SPEAKER_FRONT_RIGHT ((SLuint32) 0x00000002) -#define SL_SPEAKER_FRONT_CENTER ((SLuint32) 0x00000004) -#define SL_SPEAKER_LOW_FREQUENCY ((SLuint32) 0x00000008) -#define SL_SPEAKER_BACK_LEFT ((SLuint32) 0x00000010) -#define SL_SPEAKER_BACK_RIGHT ((SLuint32) 0x00000020) -#define SL_SPEAKER_FRONT_LEFT_OF_CENTER ((SLuint32) 0x00000040) -#define SL_SPEAKER_FRONT_RIGHT_OF_CENTER ((SLuint32) 0x00000080) -#define SL_SPEAKER_BACK_CENTER ((SLuint32) 0x00000100) -#define SL_SPEAKER_SIDE_LEFT ((SLuint32) 0x00000200) -#define SL_SPEAKER_SIDE_RIGHT ((SLuint32) 0x00000400) -#define SL_SPEAKER_TOP_CENTER ((SLuint32) 0x00000800) -#define SL_SPEAKER_TOP_FRONT_LEFT ((SLuint32) 0x00001000) -#define SL_SPEAKER_TOP_FRONT_CENTER ((SLuint32) 0x00002000) -#define SL_SPEAKER_TOP_FRONT_RIGHT ((SLuint32) 0x00004000) -#define SL_SPEAKER_TOP_BACK_LEFT ((SLuint32) 0x00008000) -#define SL_SPEAKER_TOP_BACK_CENTER ((SLuint32) 0x00010000) -#define SL_SPEAKER_TOP_BACK_RIGHT ((SLuint32) 0x00020000) -*/ -#define SL_ANDROID_SPEAKER_STEREO (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) -#define SL_ANDROID_SPEAKER_QUAD (SL_ANDROID_SPEAKER_STEREO | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT) -#define SL_ANDROID_SPEAKER_5DOT1 (SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY) -#define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT) - -// engine interfaces -static SLObjectItf engineObject = NULL; -static SLEngineItf engineEngine = NULL; - -// output mix interfaces -static SLObjectItf outputMixObject = NULL; - -// buffer queue player interfaces -static SLObjectItf bqPlayerObject = NULL; -static SLPlayItf bqPlayerPlay = NULL; -static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL; -#if 0 -static SLVolumeItf bqPlayerVolume; -#endif - -// recorder interfaces -static SLObjectItf recorderObject = NULL; -static SLRecordItf recorderRecord = NULL; -static SLAndroidSimpleBufferQueueItf recorderBufferQueue = NULL; - -#if 0 -static const char *sldevaudiorecorderstr = "SLES Audio Recorder"; -static const char *sldevaudioplayerstr = "SLES Audio Player"; - -#define SLES_DEV_AUDIO_RECORDER sldevaudiorecorderstr -#define SLES_DEV_AUDIO_PLAYER sldevaudioplayerstr -static void OPENSLES_DetectDevices( int recording ) -{ - LOGI( "openSLES_DetectDevices()" ); - if ( recording ) - addfn( SLES_DEV_AUDIO_RECORDER ); - else - addfn( SLES_DEV_AUDIO_PLAYER ); -} -#endif - -static void OPENSLES_DestroyEngine(void) -{ - LOGI("OPENSLES_DestroyEngine()"); - - // destroy output mix object, and invalidate all associated interfaces - if (outputMixObject != NULL) { - (*outputMixObject)->Destroy(outputMixObject); - outputMixObject = NULL; - } - - // destroy engine object, and invalidate all associated interfaces - if (engineObject != NULL) { - (*engineObject)->Destroy(engineObject); - engineObject = NULL; - engineEngine = NULL; - } -} - -static bool OPENSLES_CreateEngine(void) -{ - const SLInterfaceID ids[1] = { SL_IID_VOLUME }; - const SLboolean req[1] = { SL_BOOLEAN_FALSE }; - SLresult result; - - LOGI("openSLES_CreateEngine()"); - - // create engine - result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); - if (SL_RESULT_SUCCESS != result) { - LOGE("slCreateEngine failed: %d", result); - goto error; - } - LOGI("slCreateEngine OK"); - - // realize the engine - result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); - if (SL_RESULT_SUCCESS != result) { - LOGE("RealizeEngine failed: %d", result); - goto error; - } - LOGI("RealizeEngine OK"); - - // get the engine interface, which is needed in order to create other objects - result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); - if (SL_RESULT_SUCCESS != result) { - LOGE("EngineGetInterface failed: %d", result); - goto error; - } - LOGI("EngineGetInterface OK"); - - // create output mix - result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req); - if (SL_RESULT_SUCCESS != result) { - LOGE("CreateOutputMix failed: %d", result); - goto error; - } - LOGI("CreateOutputMix OK"); - - // realize the output mix - result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); - if (SL_RESULT_SUCCESS != result) { - LOGE("RealizeOutputMix failed: %d", result); - goto error; - } - return true; - -error: - OPENSLES_DestroyEngine(); - return false; -} - -// this callback handler is called every time a buffer finishes recording -static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context) -{ - struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context; - - LOGV("SLES: Recording Callback"); - SDL_SignalSemaphore(audiodata->playsem); -} - -static void OPENSLES_DestroyPCMRecorder(SDL_AudioDevice *device) -{ - struct SDL_PrivateAudioData *audiodata = device->hidden; - SLresult result; - - // stop recording - if (recorderRecord != NULL) { - result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED); - if (SL_RESULT_SUCCESS != result) { - LOGE("SetRecordState stopped: %d", result); - } - } - - // destroy audio recorder object, and invalidate all associated interfaces - if (recorderObject != NULL) { - (*recorderObject)->Destroy(recorderObject); - recorderObject = NULL; - recorderRecord = NULL; - recorderBufferQueue = NULL; - } - - if (audiodata->playsem) { - SDL_DestroySemaphore(audiodata->playsem); - audiodata->playsem = NULL; - } - - if (audiodata->mixbuff) { - SDL_free(audiodata->mixbuff); - } -} - -// !!! FIXME: make this non-blocking! -static void SDLCALL RequestAndroidPermissionBlockingCallback(void *userdata, const char *permission, bool granted) -{ - SDL_SetAtomicInt((SDL_AtomicInt *) userdata, granted ? 1 : -1); -} - -static bool OPENSLES_CreatePCMRecorder(SDL_AudioDevice *device) -{ - struct SDL_PrivateAudioData *audiodata = device->hidden; - SLDataFormat_PCM format_pcm; - SLDataLocator_AndroidSimpleBufferQueue loc_bufq; - SLDataSink audioSnk; - SLDataLocator_IODevice loc_dev; - SLDataSource audioSrc; - const SLInterfaceID ids[1] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; - const SLboolean req[1] = { SL_BOOLEAN_TRUE }; - SLresult result; - int i; - - // !!! FIXME: make this non-blocking! - { - SDL_AtomicInt permission_response; - SDL_SetAtomicInt(&permission_response, 0); - if (!SDL_RequestAndroidPermission("android.permission.RECORD_AUDIO", RequestAndroidPermissionBlockingCallback, &permission_response)) { - return false; - } - - while (SDL_GetAtomicInt(&permission_response) == 0) { - SDL_Delay(10); - } - - if (SDL_GetAtomicInt(&permission_response) < 0) { - LOGE("This app doesn't have RECORD_AUDIO permission"); - return SDL_SetError("This app doesn't have RECORD_AUDIO permission"); - } - } - - // Just go with signed 16-bit audio as it's the most compatible - device->spec.format = SDL_AUDIO_S16; - device->spec.channels = 1; - //device->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/ - - // Update the fragment size as size in bytes - SDL_UpdatedAudioDeviceFormat(device); - - LOGI("Try to open %u hz %u bit %u channels %s samples %u", - device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format), - device->spec.channels, (device->spec.format & 0x1000) ? "BE" : "LE", device->sample_frames); - - // configure audio source - loc_dev.locatorType = SL_DATALOCATOR_IODEVICE; - loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT; - loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; - loc_dev.device = NULL; - audioSrc.pLocator = &loc_dev; - audioSrc.pFormat = NULL; - - // configure audio sink - loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - loc_bufq.numBuffers = NUM_BUFFERS; - - format_pcm.formatType = SL_DATAFORMAT_PCM; - format_pcm.numChannels = device->spec.channels; - format_pcm.samplesPerSec = device->spec.freq * 1000; // / kilo Hz to milli Hz - format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format); - format_pcm.containerSize = SDL_AUDIO_BITSIZE(device->spec.format); - format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; - format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER; - - audioSnk.pLocator = &loc_bufq; - audioSnk.pFormat = &format_pcm; - - // create audio recorder - // (requires the RECORD_AUDIO permission) - result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk, 1, ids, req); - if (SL_RESULT_SUCCESS != result) { - LOGE("CreateAudioRecorder failed: %d", result); - goto failed; - } - - // realize the recorder - result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE); - if (SL_RESULT_SUCCESS != result) { - LOGE("RealizeAudioPlayer failed: %d", result); - goto failed; - } - - // get the record interface - result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord); - if (SL_RESULT_SUCCESS != result) { - LOGE("SL_IID_RECORD interface get failed: %d", result); - goto failed; - } - - // get the buffer queue interface - result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue); - if (SL_RESULT_SUCCESS != result) { - LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result); - goto failed; - } - - // register callback on the buffer queue - // context is '(SDL_PrivateAudioData *)device->hidden' - result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback, device->hidden); - if (SL_RESULT_SUCCESS != result) { - LOGE("RegisterCallback failed: %d", result); - goto failed; - } - - // Create the audio buffer semaphore - audiodata->playsem = SDL_CreateSemaphore(0); - if (!audiodata->playsem) { - LOGE("cannot create Semaphore!"); - goto failed; - } - - // Create the sound buffers - audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * device->buffer_size); - if (!audiodata->mixbuff) { - LOGE("mixbuffer allocate - out of memory"); - goto failed; - } - - for (i = 0; i < NUM_BUFFERS; i++) { - audiodata->pmixbuff[i] = audiodata->mixbuff + i * device->buffer_size; - } - - // in case already recording, stop recording and clear buffer queue - result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED); - if (SL_RESULT_SUCCESS != result) { - LOGE("Record set state failed: %d", result); - goto failed; - } - - // enqueue empty buffers to be filled by the recorder - for (i = 0; i < NUM_BUFFERS; i++) { - result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[i], device->buffer_size); - if (SL_RESULT_SUCCESS != result) { - LOGE("Record enqueue buffers failed: %d", result); - goto failed; - } - } - - // start recording - result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING); - if (SL_RESULT_SUCCESS != result) { - LOGE("Record set state failed: %d", result); - goto failed; - } - - return true; - -failed: - return SDL_SetError("Open device failed!"); -} - -// this callback handler is called every time a buffer finishes playing -static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) -{ - struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context; - - LOGV("SLES: Playback Callback"); - SDL_SignalSemaphore(audiodata->playsem); -} - -static void OPENSLES_DestroyPCMPlayer(SDL_AudioDevice *device) -{ - struct SDL_PrivateAudioData *audiodata = device->hidden; - - // set the player's state to 'stopped' - if (bqPlayerPlay != NULL) { - const SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED); - if (SL_RESULT_SUCCESS != result) { - LOGE("SetPlayState stopped failed: %d", result); - } - } - - // destroy buffer queue audio player object, and invalidate all associated interfaces - if (bqPlayerObject != NULL) { - (*bqPlayerObject)->Destroy(bqPlayerObject); - - bqPlayerObject = NULL; - bqPlayerPlay = NULL; - bqPlayerBufferQueue = NULL; - } - - if (audiodata->playsem) { - SDL_DestroySemaphore(audiodata->playsem); - audiodata->playsem = NULL; - } - - if (audiodata->mixbuff) { - SDL_free(audiodata->mixbuff); - } -} - -static bool OPENSLES_CreatePCMPlayer(SDL_AudioDevice *device) -{ - /* If we want to add floating point audio support (requires API level 21) - it can be done as described here: - https://developer.android.com/ndk/guides/audio/opensl/android-extensions.html#floating-point - */ - if (SDL_GetAndroidSDKVersion() >= 21) { - const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); - SDL_AudioFormat test_format; - while ((test_format = *(closefmts++)) != 0) { - if (SDL_AUDIO_ISSIGNED(test_format)) { - break; - } - } - - if (!test_format) { - // Didn't find a compatible format : - LOGI("No compatible audio format, using signed 16-bit audio"); - test_format = SDL_AUDIO_S16; - } - device->spec.format = test_format; - } else { - // Just go with signed 16-bit audio as it's the most compatible - device->spec.format = SDL_AUDIO_S16; - } - - // Update the fragment size as size in bytes - SDL_UpdatedAudioDeviceFormat(device); - - LOGI("Try to open %u hz %s %u bit %u channels %s samples %u", - device->spec.freq, SDL_AUDIO_ISFLOAT(device->spec.format) ? "float" : "pcm", SDL_AUDIO_BITSIZE(device->spec.format), - device->spec.channels, (device->spec.format & 0x1000) ? "BE" : "LE", device->sample_frames); - - // configure audio source - SLDataLocator_AndroidSimpleBufferQueue loc_bufq; - loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - loc_bufq.numBuffers = NUM_BUFFERS; - - SLDataFormat_PCM format_pcm; - format_pcm.formatType = SL_DATAFORMAT_PCM; - format_pcm.numChannels = device->spec.channels; - format_pcm.samplesPerSec = device->spec.freq * 1000; // / kilo Hz to milli Hz - format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format); - format_pcm.containerSize = SDL_AUDIO_BITSIZE(device->spec.format); - - if (SDL_AUDIO_ISBIGENDIAN(device->spec.format)) { - format_pcm.endianness = SL_BYTEORDER_BIGENDIAN; - } else { - format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; - } - - switch (device->spec.channels) { - case 1: - format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT; - break; - case 2: - format_pcm.channelMask = SL_ANDROID_SPEAKER_STEREO; - break; - case 3: - format_pcm.channelMask = SL_ANDROID_SPEAKER_STEREO | SL_SPEAKER_FRONT_CENTER; - break; - case 4: - format_pcm.channelMask = SL_ANDROID_SPEAKER_QUAD; - break; - case 5: - format_pcm.channelMask = SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER; - break; - case 6: - format_pcm.channelMask = SL_ANDROID_SPEAKER_5DOT1; - break; - case 7: - format_pcm.channelMask = SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_BACK_CENTER; - break; - case 8: - format_pcm.channelMask = SL_ANDROID_SPEAKER_7DOT1; - break; - default: - // Unknown number of channels, fall back to stereo - device->spec.channels = 2; - format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - break; - } - - SLDataSink audioSnk; - SLDataSource audioSrc; - audioSrc.pFormat = (void *)&format_pcm; - - SLAndroidDataFormat_PCM_EX format_pcm_ex; - if (SDL_AUDIO_ISFLOAT(device->spec.format)) { - // Copy all setup into PCM EX structure - format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX; - format_pcm_ex.endianness = format_pcm.endianness; - format_pcm_ex.channelMask = format_pcm.channelMask; - format_pcm_ex.numChannels = format_pcm.numChannels; - format_pcm_ex.sampleRate = format_pcm.samplesPerSec; - format_pcm_ex.bitsPerSample = format_pcm.bitsPerSample; - format_pcm_ex.containerSize = format_pcm.containerSize; - format_pcm_ex.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; - audioSrc.pFormat = (void *)&format_pcm_ex; - } - - audioSrc.pLocator = &loc_bufq; - - // configure audio sink - SLDataLocator_OutputMix loc_outmix; - loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; - loc_outmix.outputMix = outputMixObject; - audioSnk.pLocator = &loc_outmix; - audioSnk.pFormat = NULL; - - // create audio player - const SLInterfaceID ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME }; - const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE }; - SLresult result; - result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req); - if (SL_RESULT_SUCCESS != result) { - LOGE("CreateAudioPlayer failed: %d", result); - goto failed; - } - - // realize the player - result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE); - if (SL_RESULT_SUCCESS != result) { - LOGE("RealizeAudioPlayer failed: %d", result); - goto failed; - } - - // get the play interface - result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay); - if (SL_RESULT_SUCCESS != result) { - LOGE("SL_IID_PLAY interface get failed: %d", result); - goto failed; - } - - // get the buffer queue interface - result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue); - if (SL_RESULT_SUCCESS != result) { - LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result); - goto failed; - } - - // register callback on the buffer queue - // context is '(SDL_PrivateAudioData *)device->hidden' - result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, device->hidden); - if (SL_RESULT_SUCCESS != result) { - LOGE("RegisterCallback failed: %d", result); - goto failed; - } - -#if 0 - // get the volume interface - result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume); - if (SL_RESULT_SUCCESS != result) { - LOGE("SL_IID_VOLUME interface get failed: %d", result); - // goto failed; - } -#endif - - struct SDL_PrivateAudioData *audiodata = device->hidden; - - // Create the audio buffer semaphore - audiodata->playsem = SDL_CreateSemaphore(NUM_BUFFERS - 1); - if (!audiodata->playsem) { - LOGE("cannot create Semaphore!"); - goto failed; - } - - // Create the sound buffers - audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * device->buffer_size); - if (!audiodata->mixbuff) { - LOGE("mixbuffer allocate - out of memory"); - goto failed; - } - - for (int i = 0; i < NUM_BUFFERS; i++) { - audiodata->pmixbuff[i] = audiodata->mixbuff + i * device->buffer_size; - } - - // set the player's state to playing - result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); - if (SL_RESULT_SUCCESS != result) { - LOGE("Play set state failed: %d", result); - goto failed; - } - - return true; - -failed: - return false; -} - -static bool OPENSLES_OpenDevice(SDL_AudioDevice *device) -{ - device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } - - if (device->recording) { - LOGI("OPENSLES_OpenDevice() for recording"); - return OPENSLES_CreatePCMRecorder(device); - } else { - bool ret; - LOGI("OPENSLES_OpenDevice() for playback"); - ret = OPENSLES_CreatePCMPlayer(device); - if (!ret) { - // Another attempt to open the device with a lower frequency - if (device->spec.freq > 48000) { - OPENSLES_DestroyPCMPlayer(device); - device->spec.freq = 48000; - ret = OPENSLES_CreatePCMPlayer(device); - } - } - - if (!ret) { - return SDL_SetError("Open device failed!"); - } - } - - return true; -} - -static bool OPENSLES_WaitDevice(SDL_AudioDevice *device) -{ - struct SDL_PrivateAudioData *audiodata = device->hidden; - - LOGV("OPENSLES_WaitDevice()"); - - while (!SDL_GetAtomicInt(&device->shutdown)) { - // this semaphore won't fire when the app is in the background (OPENSLES_PauseDevices was called). - if (SDL_WaitSemaphoreTimeout(audiodata->playsem, 100)) { - return true; // semaphore was signaled, let's go! - } - // Still waiting on the semaphore (or the system), check other things then wait again. - } - return true; -} - -static bool OPENSLES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) -{ - struct SDL_PrivateAudioData *audiodata = device->hidden; - - LOGV("======OPENSLES_PlayDevice()======"); - - // Queue it up - const SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer, buflen); - - audiodata->next_buffer++; - if (audiodata->next_buffer >= NUM_BUFFERS) { - audiodata->next_buffer = 0; - } - - // If Enqueue fails, callback won't be called. - // Post the semaphore, not to run out of buffer - if (SL_RESULT_SUCCESS != result) { - SDL_SignalSemaphore(audiodata->playsem); - } - - return true; -} - -/// n playn sem -// getbuf 0 - 1 -// fill buff 0 - 1 -// play 0 - 0 1 -// wait 1 0 0 -// getbuf 1 0 0 -// fill buff 1 0 0 -// play 0 0 0 -// wait -// -// okay.. - -static Uint8 *OPENSLES_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize) -{ - struct SDL_PrivateAudioData *audiodata = device->hidden; - - LOGV("OPENSLES_GetDeviceBuf()"); - return audiodata->pmixbuff[audiodata->next_buffer]; -} - -static int OPENSLES_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - struct SDL_PrivateAudioData *audiodata = device->hidden; - - // Copy it to the output buffer - SDL_assert(buflen == device->buffer_size); - SDL_memcpy(buffer, audiodata->pmixbuff[audiodata->next_buffer], device->buffer_size); - - // Re-enqueue the buffer - const SLresult result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], device->buffer_size); - if (SL_RESULT_SUCCESS != result) { - LOGE("Record enqueue buffers failed: %d", result); - return -1; - } - - audiodata->next_buffer++; - if (audiodata->next_buffer >= NUM_BUFFERS) { - audiodata->next_buffer = 0; - } - - return device->buffer_size; -} - -static void OPENSLES_CloseDevice(SDL_AudioDevice *device) -{ - // struct SDL_PrivateAudioData *audiodata = device->hidden; - if (device->hidden) { - if (device->recording) { - LOGI("OPENSLES_CloseDevice() for recording"); - OPENSLES_DestroyPCMRecorder(device); - } else { - LOGI("OPENSLES_CloseDevice() for playing"); - OPENSLES_DestroyPCMPlayer(device); - } - - SDL_free(device->hidden); - device->hidden = NULL; - } -} - -static bool OPENSLES_Init(SDL_AudioDriverImpl *impl) -{ - LOGI("OPENSLES_Init() called"); - - if (!OPENSLES_CreateEngine()) { - return false; - } - - LOGI("OPENSLES_Init() - set pointers"); - - // Set the function pointers - // impl->DetectDevices = OPENSLES_DetectDevices; - impl->ThreadInit = Android_AudioThreadInit; - impl->OpenDevice = OPENSLES_OpenDevice; - impl->WaitDevice = OPENSLES_WaitDevice; - impl->PlayDevice = OPENSLES_PlayDevice; - impl->GetDeviceBuf = OPENSLES_GetDeviceBuf; - impl->WaitRecordingDevice = OPENSLES_WaitDevice; - impl->RecordDevice = OPENSLES_RecordDevice; - impl->CloseDevice = OPENSLES_CloseDevice; - impl->Deinitialize = OPENSLES_DestroyEngine; - - // and the capabilities - impl->HasRecordingSupport = true; - impl->OnlyHasDefaultPlaybackDevice = true; - impl->OnlyHasDefaultRecordingDevice = true; - - LOGI("OPENSLES_Init() - success"); - - // this audio target is available. - return true; -} - -AudioBootStrap OPENSLES_bootstrap = { - "openslES", "OpenSL ES audio driver", OPENSLES_Init, false, false -}; - -void OPENSLES_ResumeDevices(void) -{ - if (bqPlayerPlay != NULL) { - // set the player's state to 'playing' - SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); - if (SL_RESULT_SUCCESS != result) { - LOGE("OPENSLES_ResumeDevices failed: %d", result); - } - } -} - -void OPENSLES_PauseDevices(void) -{ - if (bqPlayerPlay != NULL) { - // set the player's state to 'paused' - SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED); - if (SL_RESULT_SUCCESS != result) { - LOGE("OPENSLES_PauseDevices failed: %d", result); - } - } -} - -#endif // SDL_AUDIO_DRIVER_OPENSLES diff --git a/contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.h b/contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.h deleted file mode 100644 index 0ae2664..0000000 --- a/contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_openslesaudio_h_ -#define SDL_openslesaudio_h_ - -#ifdef SDL_AUDIO_DRIVER_OPENSLES - -extern void OPENSLES_ResumeDevices(void); -extern void OPENSLES_PauseDevices(void); - -#else - -#define OPENSLES_ResumeDevices() -#define OPENSLES_PauseDevices() - -#endif - -#endif // SDL_openslesaudio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/pipewire/SDL_pipewire.c b/contrib/SDL-3.2.8/src/audio/pipewire/SDL_pipewire.c deleted file mode 100644 index e3f9439..0000000 --- a/contrib/SDL-3.2.8/src/audio/pipewire/SDL_pipewire.c +++ /dev/null @@ -1,1349 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_PIPEWIRE - -#include "SDL_pipewire.h" - -#include -#include -#include - -/* - * This seems to be a sane lower limit as Pipewire - * uses it in several of it's own modules. - */ -#define PW_MIN_SAMPLES 32 // About 0.67ms at 48kHz -#define PW_BASE_CLOCK_RATE 48000 - -#define PW_POD_BUFFER_LENGTH 1024 -#define PW_THREAD_NAME_BUFFER_LENGTH 128 -#define PW_MAX_IDENTIFIER_LENGTH 256 - -enum PW_READY_FLAGS -{ - PW_READY_FLAG_BUFFER_ADDED = 0x1, - PW_READY_FLAG_STREAM_READY = 0x2, - PW_READY_FLAG_ALL_PREOPEN_BITS = 0x3, - PW_READY_FLAG_OPEN_COMPLETE = 0x4, - PW_READY_FLAG_ALL_BITS = 0x7 -}; - -#define PW_ID_TO_HANDLE(x) (void *)((uintptr_t)x) -#define PW_HANDLE_TO_ID(x) (uint32_t)((uintptr_t)x) - -static bool pipewire_initialized = false; - -// Pipewire entry points -static const char *(*PIPEWIRE_pw_get_library_version)(void); -static void (*PIPEWIRE_pw_init)(int *, char ***); -static void (*PIPEWIRE_pw_deinit)(void); -static struct pw_main_loop *(*PIPEWIRE_pw_main_loop_new)(const struct spa_dict *loop); -static struct pw_loop *(*PIPEWIRE_pw_main_loop_get_loop)(struct pw_main_loop *loop); -static int (*PIPEWIRE_pw_main_loop_run)(struct pw_main_loop *loop); -static int (*PIPEWIRE_pw_main_loop_quit)(struct pw_main_loop *loop); -static void(*PIPEWIRE_pw_main_loop_destroy)(struct pw_main_loop *loop); -static struct pw_thread_loop *(*PIPEWIRE_pw_thread_loop_new)(const char *, const struct spa_dict *); -static void (*PIPEWIRE_pw_thread_loop_destroy)(struct pw_thread_loop *); -static void (*PIPEWIRE_pw_thread_loop_stop)(struct pw_thread_loop *); -static struct pw_loop *(*PIPEWIRE_pw_thread_loop_get_loop)(struct pw_thread_loop *); -static void (*PIPEWIRE_pw_thread_loop_lock)(struct pw_thread_loop *); -static void (*PIPEWIRE_pw_thread_loop_unlock)(struct pw_thread_loop *); -static void (*PIPEWIRE_pw_thread_loop_signal)(struct pw_thread_loop *, bool); -static void (*PIPEWIRE_pw_thread_loop_wait)(struct pw_thread_loop *); -static int (*PIPEWIRE_pw_thread_loop_start)(struct pw_thread_loop *); -static struct pw_context *(*PIPEWIRE_pw_context_new)(struct pw_loop *, struct pw_properties *, size_t); -static void (*PIPEWIRE_pw_context_destroy)(struct pw_context *); -static struct pw_core *(*PIPEWIRE_pw_context_connect)(struct pw_context *, struct pw_properties *, size_t); -static void (*PIPEWIRE_pw_proxy_add_object_listener)(struct pw_proxy *, struct spa_hook *, const void *, void *); -static void *(*PIPEWIRE_pw_proxy_get_user_data)(struct pw_proxy *); -static void (*PIPEWIRE_pw_proxy_destroy)(struct pw_proxy *); -static int (*PIPEWIRE_pw_core_disconnect)(struct pw_core *); -static struct pw_stream *(*PIPEWIRE_pw_stream_new_simple)(struct pw_loop *, const char *, struct pw_properties *, - const struct pw_stream_events *, void *); -static void (*PIPEWIRE_pw_stream_destroy)(struct pw_stream *); -static int (*PIPEWIRE_pw_stream_connect)(struct pw_stream *, enum pw_direction, uint32_t, enum pw_stream_flags, - const struct spa_pod **, uint32_t); -static enum pw_stream_state (*PIPEWIRE_pw_stream_get_state)(struct pw_stream *stream, const char **error); -static struct pw_buffer *(*PIPEWIRE_pw_stream_dequeue_buffer)(struct pw_stream *); -static int (*PIPEWIRE_pw_stream_queue_buffer)(struct pw_stream *, struct pw_buffer *); -static struct pw_properties *(*PIPEWIRE_pw_properties_new)(const char *, ...)SPA_SENTINEL; -static int (*PIPEWIRE_pw_properties_set)(struct pw_properties *, const char *, const char *); -static int (*PIPEWIRE_pw_properties_setf)(struct pw_properties *, const char *, const char *, ...) SPA_PRINTF_FUNC(3, 4); - -#ifdef SDL_AUDIO_DRIVER_PIPEWIRE_DYNAMIC - -static const char *pipewire_library = SDL_AUDIO_DRIVER_PIPEWIRE_DYNAMIC; -static SDL_SharedObject *pipewire_handle = NULL; - -static bool pipewire_dlsym(const char *fn, void **addr) -{ - *addr = SDL_LoadFunction(pipewire_handle, fn); - if (!*addr) { - // Don't call SDL_SetError(): SDL_LoadFunction already did. - return false; - } - - return true; -} - -#define SDL_PIPEWIRE_SYM(x) \ - if (!pipewire_dlsym(#x, (void **)(char *)&PIPEWIRE_##x)) \ - return false - -static bool load_pipewire_library(void) -{ - pipewire_handle = SDL_LoadObject(pipewire_library); - return pipewire_handle ? true : false; -} - -static void unload_pipewire_library(void) -{ - if (pipewire_handle) { - SDL_UnloadObject(pipewire_handle); - pipewire_handle = NULL; - } -} - -#else - -#define SDL_PIPEWIRE_SYM(x) PIPEWIRE_##x = x - -static bool load_pipewire_library(void) -{ - return true; -} - -static void unload_pipewire_library(void) -{ - // Nothing to do -} - -#endif // SDL_AUDIO_DRIVER_PIPEWIRE_DYNAMIC - -static bool load_pipewire_syms(void) -{ - SDL_PIPEWIRE_SYM(pw_get_library_version); - SDL_PIPEWIRE_SYM(pw_init); - SDL_PIPEWIRE_SYM(pw_deinit); - SDL_PIPEWIRE_SYM(pw_main_loop_new); - SDL_PIPEWIRE_SYM(pw_main_loop_get_loop); - SDL_PIPEWIRE_SYM(pw_main_loop_run); - SDL_PIPEWIRE_SYM(pw_main_loop_quit); - SDL_PIPEWIRE_SYM(pw_main_loop_destroy); - SDL_PIPEWIRE_SYM(pw_thread_loop_new); - SDL_PIPEWIRE_SYM(pw_thread_loop_destroy); - SDL_PIPEWIRE_SYM(pw_thread_loop_stop); - SDL_PIPEWIRE_SYM(pw_thread_loop_get_loop); - SDL_PIPEWIRE_SYM(pw_thread_loop_lock); - SDL_PIPEWIRE_SYM(pw_thread_loop_unlock); - SDL_PIPEWIRE_SYM(pw_thread_loop_signal); - SDL_PIPEWIRE_SYM(pw_thread_loop_wait); - SDL_PIPEWIRE_SYM(pw_thread_loop_start); - SDL_PIPEWIRE_SYM(pw_context_new); - SDL_PIPEWIRE_SYM(pw_context_destroy); - SDL_PIPEWIRE_SYM(pw_context_connect); - SDL_PIPEWIRE_SYM(pw_proxy_add_object_listener); - SDL_PIPEWIRE_SYM(pw_proxy_get_user_data); - SDL_PIPEWIRE_SYM(pw_proxy_destroy); - SDL_PIPEWIRE_SYM(pw_core_disconnect); - SDL_PIPEWIRE_SYM(pw_stream_new_simple); - SDL_PIPEWIRE_SYM(pw_stream_destroy); - SDL_PIPEWIRE_SYM(pw_stream_connect); - SDL_PIPEWIRE_SYM(pw_stream_get_state); - SDL_PIPEWIRE_SYM(pw_stream_dequeue_buffer); - SDL_PIPEWIRE_SYM(pw_stream_queue_buffer); - SDL_PIPEWIRE_SYM(pw_properties_new); - SDL_PIPEWIRE_SYM(pw_properties_set); - SDL_PIPEWIRE_SYM(pw_properties_setf); - - return true; -} - -static bool init_pipewire_library(void) -{ - if (load_pipewire_library()) { - if (load_pipewire_syms()) { - PIPEWIRE_pw_init(NULL, NULL); - return true; - } - } - - return false; -} - -static void deinit_pipewire_library(void) -{ - PIPEWIRE_pw_deinit(); - unload_pipewire_library(); -} - -// A generic Pipewire node object used for enumeration. -struct node_object -{ - struct spa_list link; - - Uint32 id; - int seq; - bool persist; - - /* - * NOTE: If used, this is *must* be allocated with SDL_malloc() or similar - * as SDL_free() will be called on it when the node_object is destroyed. - * - * If ownership of the referenced memory is transferred, this must be set - * to NULL or the memory will be freed when the node_object is destroyed. - */ - void *userdata; - - struct pw_proxy *proxy; - struct spa_hook node_listener; - struct spa_hook core_listener; -}; - -// A sink/source node used for stream I/O. -struct io_node -{ - struct spa_list link; - - Uint32 id; - bool recording; - SDL_AudioSpec spec; - - const char *name; // Friendly name - const char *path; // OS identifier (i.e. ALSA endpoint) - - char buf[]; // Buffer to hold the name and path strings. -}; - -// The global hotplug thread and associated objects. -static struct pw_thread_loop *hotplug_loop; -static struct pw_core *hotplug_core; -static struct pw_context *hotplug_context; -static struct pw_registry *hotplug_registry; -static struct spa_hook hotplug_registry_listener; -static struct spa_hook hotplug_core_listener; -static struct spa_list hotplug_pending_list; -static struct spa_list hotplug_io_list; -static int hotplug_init_seq_val; -static bool hotplug_init_complete; -static bool hotplug_events_enabled; - -static int pipewire_version_major; -static int pipewire_version_minor; -static int pipewire_version_patch; -static char *pipewire_default_sink_id = NULL; -static char *pipewire_default_source_id = NULL; - -static bool pipewire_core_version_at_least(int major, int minor, int patch) -{ - return (pipewire_version_major >= major) && - (pipewire_version_major > major || pipewire_version_minor >= minor) && - (pipewire_version_major > major || pipewire_version_minor > minor || pipewire_version_patch >= patch); -} - -// The active node list -static bool io_list_check_add(struct io_node *node) -{ - struct io_node *n; - bool ret = true; - - // See if the node is already in the list - spa_list_for_each (n, &hotplug_io_list, link) { - if (n->id == node->id) { - ret = false; - goto dup_found; - } - } - - // Add to the list if the node doesn't already exist - spa_list_append(&hotplug_io_list, &node->link); - - if (hotplug_events_enabled) { - SDL_AddAudioDevice(node->recording, node->name, &node->spec, PW_ID_TO_HANDLE(node->id)); - } - -dup_found: - - return ret; -} - -static void io_list_remove(Uint32 id) -{ - struct io_node *n, *temp; - - // Find and remove the node from the list - spa_list_for_each_safe (n, temp, &hotplug_io_list, link) { - if (n->id == id) { - spa_list_remove(&n->link); - - if (hotplug_events_enabled) { - SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle(PW_ID_TO_HANDLE(id))); - } - - SDL_free(n); - - break; - } - } -} - -static void io_list_clear(void) -{ - struct io_node *n, *temp; - - spa_list_for_each_safe (n, temp, &hotplug_io_list, link) { - spa_list_remove(&n->link); - SDL_free(n); - } -} - -static struct io_node *io_list_get_by_id(Uint32 id) -{ - struct io_node *n, *temp; - spa_list_for_each_safe (n, temp, &hotplug_io_list, link) { - if (n->id == id) { - return n; - } - } - return NULL; -} - -static void node_object_destroy(struct node_object *node) -{ - SDL_assert(node != NULL); - - spa_list_remove(&node->link); - spa_hook_remove(&node->node_listener); - spa_hook_remove(&node->core_listener); - SDL_free(node->userdata); - PIPEWIRE_pw_proxy_destroy(node->proxy); -} - -// The pending node list -static void pending_list_add(struct node_object *node) -{ - SDL_assert(node != NULL); - spa_list_append(&hotplug_pending_list, &node->link); -} - -static void pending_list_remove(Uint32 id) -{ - struct node_object *node, *temp; - - spa_list_for_each_safe (node, temp, &hotplug_pending_list, link) { - if (node->id == id) { - node_object_destroy(node); - } - } -} - -static void pending_list_clear(void) -{ - struct node_object *node, *temp; - - spa_list_for_each_safe (node, temp, &hotplug_pending_list, link) { - node_object_destroy(node); - } -} - -static void *node_object_new(Uint32 id, const char *type, Uint32 version, const void *funcs, const struct pw_core_events *core_events) -{ - struct pw_proxy *proxy; - struct node_object *node; - - // Create the proxy object - proxy = pw_registry_bind(hotplug_registry, id, type, version, sizeof(struct node_object)); - if (!proxy) { - SDL_SetError("Pipewire: Failed to create proxy object (%i)", errno); - return NULL; - } - - node = PIPEWIRE_pw_proxy_get_user_data(proxy); - SDL_zerop(node); - - node->id = id; - node->proxy = proxy; - - // Add the callbacks - pw_core_add_listener(hotplug_core, &node->core_listener, core_events, node); - PIPEWIRE_pw_proxy_add_object_listener(node->proxy, &node->node_listener, funcs, node); - - // Add the node to the active list - pending_list_add(node); - - return node; -} - -// Core sync points -static void core_events_hotplug_init_callback(void *object, uint32_t id, int seq) -{ - if (id == PW_ID_CORE && seq == hotplug_init_seq_val) { - // This core listener is no longer needed. - spa_hook_remove(&hotplug_core_listener); - - // Signal that the initial I/O list is populated - hotplug_init_complete = true; - PIPEWIRE_pw_thread_loop_signal(hotplug_loop, false); - } -} - -static void core_events_hotplug_info_callback(void *data, const struct pw_core_info *info) -{ - if (SDL_sscanf(info->version, "%d.%d.%d", &pipewire_version_major, &pipewire_version_minor, &pipewire_version_patch) < 3) { - pipewire_version_major = 0; - pipewire_version_minor = 0; - pipewire_version_patch = 0; - } -} - -static void core_events_interface_callback(void *object, uint32_t id, int seq) -{ - struct node_object *node = object; - struct io_node *io = node->userdata; - - if (id == PW_ID_CORE && seq == node->seq) { - /* - * Move the I/O node to the connected list. - * On success, the list owns the I/O node object. - */ - if (io_list_check_add(io)) { - node->userdata = NULL; - } - - node_object_destroy(node); - } -} - -static void core_events_metadata_callback(void *object, uint32_t id, int seq) -{ - struct node_object *node = object; - - if (id == PW_ID_CORE && seq == node->seq && !node->persist) { - node_object_destroy(node); - } -} - -static const struct pw_core_events hotplug_init_core_events = { PW_VERSION_CORE_EVENTS, .info = core_events_hotplug_info_callback, .done = core_events_hotplug_init_callback }; -static const struct pw_core_events interface_core_events = { PW_VERSION_CORE_EVENTS, .done = core_events_interface_callback }; -static const struct pw_core_events metadata_core_events = { PW_VERSION_CORE_EVENTS, .done = core_events_metadata_callback }; - -static void hotplug_core_sync(struct node_object *node) -{ - /* - * Node sync events *must* come before the hotplug init sync events or the initial - * I/O list will be incomplete when the main hotplug sync point is hit. - */ - if (node) { - node->seq = pw_core_sync(hotplug_core, PW_ID_CORE, node->seq); - } - - if (!hotplug_init_complete) { - hotplug_init_seq_val = pw_core_sync(hotplug_core, PW_ID_CORE, hotplug_init_seq_val); - } -} - -// Helpers for retrieving values from params -static bool get_range_param(const struct spa_pod *param, Uint32 key, int *def, int *min, int *max) -{ - const struct spa_pod_prop *prop; - struct spa_pod *value; - Uint32 n_values, choice; - - prop = spa_pod_find_prop(param, NULL, key); - - if (prop && prop->value.type == SPA_TYPE_Choice) { - value = spa_pod_get_values(&prop->value, &n_values, &choice); - - if (n_values == 3 && choice == SPA_CHOICE_Range) { - Uint32 *v = SPA_POD_BODY(value); - - if (v) { - if (def) { - *def = (int)v[0]; - } - if (min) { - *min = (int)v[1]; - } - if (max) { - *max = (int)v[2]; - } - - return true; - } - } - } - - return false; -} - -static bool get_int_param(const struct spa_pod *param, Uint32 key, int *val) -{ - const struct spa_pod_prop *prop; - Sint32 v; - - prop = spa_pod_find_prop(param, NULL, key); - - if (prop && spa_pod_get_int(&prop->value, &v) == 0) { - if (val) { - *val = (int)v; - } - - return true; - } - - return false; -} - -static SDL_AudioFormat SPAFormatToSDL(enum spa_audio_format spafmt) -{ - switch (spafmt) { - #define CHECKFMT(spa,sdl) case SPA_AUDIO_FORMAT_##spa: return SDL_AUDIO_##sdl - CHECKFMT(U8, U8); - CHECKFMT(S8, S8); - CHECKFMT(S16_LE, S16LE); - CHECKFMT(S16_BE, S16BE); - CHECKFMT(S32_LE, S32LE); - CHECKFMT(S32_BE, S32BE); - CHECKFMT(F32_LE, F32LE); - CHECKFMT(F32_BE, F32BE); - #undef CHECKFMT - default: break; - } - - return SDL_AUDIO_UNKNOWN; -} - -// Interface node callbacks -static void node_event_info(void *object, const struct pw_node_info *info) -{ - struct node_object *node = object; - struct io_node *io = node->userdata; - const char *prop_val; - Uint32 i; - - if (info) { - prop_val = spa_dict_lookup(info->props, PW_KEY_AUDIO_CHANNELS); - if (prop_val) { - io->spec.channels = (Uint8)SDL_atoi(prop_val); - } - - // Need to parse the parameters to get the sample rate - for (i = 0; i < info->n_params; ++i) { - pw_node_enum_params((struct pw_node*)node->proxy, 0, info->params[i].id, 0, 0, NULL); - } - - hotplug_core_sync(node); - } -} - -static void node_event_param(void *object, int seq, uint32_t id, uint32_t index, uint32_t next, const struct spa_pod *param) -{ - struct node_object *node = object; - struct io_node *io = node->userdata; - - if ((id == SPA_PARAM_Format) && (io->spec.format == SDL_AUDIO_UNKNOWN)) { - struct spa_audio_info_raw info; - SDL_zero(info); - if (spa_format_audio_raw_parse(param, &info) == 0) { - //SDL_Log("Sink Format: %d, Rate: %d Hz, Channels: %d", info.format, info.rate, info.channels); - io->spec.format = SPAFormatToSDL(info.format); - } - } - - // Get the default frequency - if (io->spec.freq == 0) { - get_range_param(param, SPA_FORMAT_AUDIO_rate, &io->spec.freq, NULL, NULL); - } - - /* - * The channel count should have come from the node properties, - * but it is stored here as well. If one failed, try the other. - */ - if (io->spec.channels == 0) { - int channels; - if (get_int_param(param, SPA_FORMAT_AUDIO_channels, &channels)) { - io->spec.channels = (Uint8)channels; - } - } -} - -static const struct pw_node_events interface_node_events = { PW_VERSION_NODE_EVENTS, .info = node_event_info, - .param = node_event_param }; - -static char *get_name_from_json(const char *json) -{ - struct spa_json parser[2]; - char key[7]; // "name" - char value[PW_MAX_IDENTIFIER_LENGTH]; - spa_json_init(&parser[0], json, SDL_strlen(json)); - if (spa_json_enter_object(&parser[0], &parser[1]) <= 0) { - // Not actually JSON - return NULL; - } - if (spa_json_get_string(&parser[1], key, sizeof(key)) <= 0) { - // Not actually a key/value pair - return NULL; - } - if (spa_json_get_string(&parser[1], value, sizeof(value)) <= 0) { - // Somehow had a key with no value? - return NULL; - } - return SDL_strdup(value); -} - -static void change_default_device(const char *path) -{ - if (hotplug_events_enabled) { - struct io_node *n, *temp; - spa_list_for_each_safe (n, temp, &hotplug_io_list, link) { - if (SDL_strcmp(n->path, path) == 0) { - SDL_DefaultAudioDeviceChanged(SDL_FindPhysicalAudioDeviceByHandle(PW_ID_TO_HANDLE(n->id))); - return; // found it, we're done. - } - } - } -} - -// Metadata node callback -static int metadata_property(void *object, Uint32 subject, const char *key, const char *type, const char *value) -{ - struct node_object *node = object; - - if (subject == PW_ID_CORE && key && value) { - if (!SDL_strcmp(key, "default.audio.sink")) { - if (pipewire_default_sink_id) { - SDL_free(pipewire_default_sink_id); - } - pipewire_default_sink_id = get_name_from_json(value); - node->persist = true; - change_default_device(pipewire_default_sink_id); - } else if (!SDL_strcmp(key, "default.audio.source")) { - if (pipewire_default_source_id) { - SDL_free(pipewire_default_source_id); - } - pipewire_default_source_id = get_name_from_json(value); - node->persist = true; - change_default_device(pipewire_default_source_id); - } - } - - return 0; -} - -static const struct pw_metadata_events metadata_node_events = { PW_VERSION_METADATA_EVENTS, .property = metadata_property }; - -// Global registry callbacks -static void registry_event_global_callback(void *object, uint32_t id, uint32_t permissions, const char *type, uint32_t version, - const struct spa_dict *props) -{ - struct node_object *node; - - // We're only interested in interface and metadata nodes. - if (!SDL_strcmp(type, PW_TYPE_INTERFACE_Node)) { - const char *media_class = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS); - - if (media_class) { - const char *node_desc; - const char *node_path; - struct io_node *io; - bool recording; - int desc_buffer_len; - int path_buffer_len; - - // Just want sink and source - if (!SDL_strcasecmp(media_class, "Audio/Sink")) { - recording = false; - } else if (!SDL_strcasecmp(media_class, "Audio/Source")) { - recording = true; - } else { - return; - } - - node_desc = spa_dict_lookup(props, PW_KEY_NODE_DESCRIPTION); - node_path = spa_dict_lookup(props, PW_KEY_NODE_NAME); - - if (node_desc && node_path) { - node = node_object_new(id, type, version, &interface_node_events, &interface_core_events); - if (!node) { - SDL_SetError("Pipewire: Failed to allocate interface node"); - return; - } - - // Allocate and initialize the I/O node information struct - desc_buffer_len = SDL_strlen(node_desc) + 1; - path_buffer_len = SDL_strlen(node_path) + 1; - node->userdata = io = SDL_calloc(1, sizeof(struct io_node) + desc_buffer_len + path_buffer_len); - if (!io) { - node_object_destroy(node); - return; - } - - // Begin setting the node properties - io->id = id; - io->recording = recording; - if (io->spec.format == SDL_AUDIO_UNKNOWN) { - io->spec.format = SDL_AUDIO_S16; // we'll go conservative here if for some reason the format isn't known. - } - io->name = io->buf; - io->path = io->buf + desc_buffer_len; - SDL_strlcpy(io->buf, node_desc, desc_buffer_len); - SDL_strlcpy(io->buf + desc_buffer_len, node_path, path_buffer_len); - - // Update sync points - hotplug_core_sync(node); - } - } - } else if (!SDL_strcmp(type, PW_TYPE_INTERFACE_Metadata)) { - node = node_object_new(id, type, version, &metadata_node_events, &metadata_core_events); - if (!node) { - SDL_SetError("Pipewire: Failed to allocate metadata node"); - return; - } - - // Update sync points - hotplug_core_sync(node); - } -} - -static void registry_event_remove_callback(void *object, uint32_t id) -{ - io_list_remove(id); - pending_list_remove(id); -} - -static const struct pw_registry_events registry_events = { PW_VERSION_REGISTRY_EVENTS, .global = registry_event_global_callback, - .global_remove = registry_event_remove_callback }; - -// The hotplug thread -static bool hotplug_loop_init(void) -{ - int res; - - spa_list_init(&hotplug_pending_list); - spa_list_init(&hotplug_io_list); - - hotplug_loop = PIPEWIRE_pw_thread_loop_new("SDLPwAudioPlug", NULL); - if (!hotplug_loop) { - return SDL_SetError("Pipewire: Failed to create hotplug detection loop (%i)", errno); - } - - hotplug_context = PIPEWIRE_pw_context_new(PIPEWIRE_pw_thread_loop_get_loop(hotplug_loop), NULL, 0); - if (!hotplug_context) { - return SDL_SetError("Pipewire: Failed to create hotplug detection context (%i)", errno); - } - - hotplug_core = PIPEWIRE_pw_context_connect(hotplug_context, NULL, 0); - if (!hotplug_core) { - return SDL_SetError("Pipewire: Failed to connect hotplug detection context (%i)", errno); - } - - hotplug_registry = pw_core_get_registry(hotplug_core, PW_VERSION_REGISTRY, 0); - if (!hotplug_registry) { - return SDL_SetError("Pipewire: Failed to acquire hotplug detection registry (%i)", errno); - } - - spa_zero(hotplug_registry_listener); - pw_registry_add_listener(hotplug_registry, &hotplug_registry_listener, ®istry_events, NULL); - - spa_zero(hotplug_core_listener); - pw_core_add_listener(hotplug_core, &hotplug_core_listener, &hotplug_init_core_events, NULL); - - hotplug_init_seq_val = pw_core_sync(hotplug_core, PW_ID_CORE, 0); - - res = PIPEWIRE_pw_thread_loop_start(hotplug_loop); - if (res != 0) { - return SDL_SetError("Pipewire: Failed to start hotplug detection loop"); - } - - return true; -} - -static void hotplug_loop_destroy(void) -{ - if (hotplug_loop) { - PIPEWIRE_pw_thread_loop_stop(hotplug_loop); - } - - pending_list_clear(); - io_list_clear(); - - hotplug_init_complete = false; - hotplug_events_enabled = false; - - if (pipewire_default_sink_id) { - SDL_free(pipewire_default_sink_id); - pipewire_default_sink_id = NULL; - } - if (pipewire_default_source_id) { - SDL_free(pipewire_default_source_id); - pipewire_default_source_id = NULL; - } - - if (hotplug_registry) { - PIPEWIRE_pw_proxy_destroy((struct pw_proxy *)hotplug_registry); - hotplug_registry = NULL; - } - - if (hotplug_core) { - PIPEWIRE_pw_core_disconnect(hotplug_core); - hotplug_core = NULL; - } - - if (hotplug_context) { - PIPEWIRE_pw_context_destroy(hotplug_context); - hotplug_context = NULL; - } - - if (hotplug_loop) { - PIPEWIRE_pw_thread_loop_destroy(hotplug_loop); - hotplug_loop = NULL; - } -} - -static void PIPEWIRE_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) -{ - struct io_node *io; - - PIPEWIRE_pw_thread_loop_lock(hotplug_loop); - - // Wait until the initial registry enumeration is complete - if (!hotplug_init_complete) { - PIPEWIRE_pw_thread_loop_wait(hotplug_loop); - } - - spa_list_for_each (io, &hotplug_io_list, link) { - SDL_AudioDevice *device = SDL_AddAudioDevice(io->recording, io->name, &io->spec, PW_ID_TO_HANDLE(io->id)); - if (pipewire_default_sink_id && SDL_strcmp(io->path, pipewire_default_sink_id) == 0) { - if (!io->recording) { - *default_playback = device; - } - } else if (pipewire_default_source_id && SDL_strcmp(io->path, pipewire_default_source_id) == 0) { - if (io->recording) { - *default_recording = device; - } - } - } - - hotplug_events_enabled = true; - - PIPEWIRE_pw_thread_loop_unlock(hotplug_loop); -} - -// Channel maps that match the order in SDL_Audio.h -static const enum spa_audio_channel PIPEWIRE_channel_map_1[] = { SPA_AUDIO_CHANNEL_MONO }; -static const enum spa_audio_channel PIPEWIRE_channel_map_2[] = { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR }; -static const enum spa_audio_channel PIPEWIRE_channel_map_3[] = { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_LFE }; -static const enum spa_audio_channel PIPEWIRE_channel_map_4[] = { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_RL, - SPA_AUDIO_CHANNEL_RR }; -static const enum spa_audio_channel PIPEWIRE_channel_map_5[] = { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, - SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR }; -static const enum spa_audio_channel PIPEWIRE_channel_map_6[] = { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, - SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR }; -static const enum spa_audio_channel PIPEWIRE_channel_map_7[] = { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, - SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RC, SPA_AUDIO_CHANNEL_RL, - SPA_AUDIO_CHANNEL_RR }; -static const enum spa_audio_channel PIPEWIRE_channel_map_8[] = { SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, - SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, - SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR }; - -#define COPY_CHANNEL_MAP(c) SDL_memcpy(info->position, PIPEWIRE_channel_map_##c, sizeof(PIPEWIRE_channel_map_##c)) - -static void initialize_spa_info(const SDL_AudioSpec *spec, struct spa_audio_info_raw *info) -{ - info->channels = spec->channels; - info->rate = spec->freq; - - switch (spec->channels) { - case 1: - COPY_CHANNEL_MAP(1); - break; - case 2: - COPY_CHANNEL_MAP(2); - break; - case 3: - COPY_CHANNEL_MAP(3); - break; - case 4: - COPY_CHANNEL_MAP(4); - break; - case 5: - COPY_CHANNEL_MAP(5); - break; - case 6: - COPY_CHANNEL_MAP(6); - break; - case 7: - COPY_CHANNEL_MAP(7); - break; - case 8: - COPY_CHANNEL_MAP(8); - break; - } - - // Pipewire natively supports all of SDL's sample formats - switch (spec->format) { - case SDL_AUDIO_U8: - info->format = SPA_AUDIO_FORMAT_U8; - break; - case SDL_AUDIO_S8: - info->format = SPA_AUDIO_FORMAT_S8; - break; - case SDL_AUDIO_S16LE: - info->format = SPA_AUDIO_FORMAT_S16_LE; - break; - case SDL_AUDIO_S16BE: - info->format = SPA_AUDIO_FORMAT_S16_BE; - break; - case SDL_AUDIO_S32LE: - info->format = SPA_AUDIO_FORMAT_S32_LE; - break; - case SDL_AUDIO_S32BE: - info->format = SPA_AUDIO_FORMAT_S32_BE; - break; - case SDL_AUDIO_F32LE: - info->format = SPA_AUDIO_FORMAT_F32_LE; - break; - case SDL_AUDIO_F32BE: - info->format = SPA_AUDIO_FORMAT_F32_BE; - break; - default: - info->format = SPA_AUDIO_FORMAT_UNKNOWN; - break; - } -} - -static Uint8 *PIPEWIRE_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - // See if a buffer is available. If this returns NULL, SDL_PlaybackAudioThreadIterate will return false, but since we own the thread, it won't kill playback. - // !!! FIXME: It's not clear to me if this ever returns NULL or if this was just defensive coding. - - struct pw_stream *stream = device->hidden->stream; - struct pw_buffer *pw_buf = PIPEWIRE_pw_stream_dequeue_buffer(stream); - if (pw_buf == NULL) { - return NULL; - } - - struct spa_buffer *spa_buf = pw_buf->buffer; - if (spa_buf->datas[0].data == NULL) { - PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf); - return NULL; - } - - device->hidden->pw_buf = pw_buf; - return (Uint8 *) spa_buf->datas[0].data; -} - -static bool PIPEWIRE_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) -{ - struct pw_stream *stream = device->hidden->stream; - struct pw_buffer *pw_buf = device->hidden->pw_buf; - struct spa_buffer *spa_buf = pw_buf->buffer; - spa_buf->datas[0].chunk->offset = 0; - spa_buf->datas[0].chunk->stride = device->hidden->stride; - spa_buf->datas[0].chunk->size = buffer_size; - - PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf); - device->hidden->pw_buf = NULL; - - return true; -} - -static void output_callback(void *data) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *) data; - - // this callback can fire in a background thread during OpenDevice, while we're still blocking - // _with the device lock_ until the stream is ready, causing a deadlock. Write silence in this case. - if (device->hidden->stream_init_status != PW_READY_FLAG_ALL_BITS) { - int bufsize = 0; - Uint8 *buf = PIPEWIRE_GetDeviceBuf(device, &bufsize); - if (buf && bufsize) { - SDL_memset(buf, device->silence_value, bufsize); - } - PIPEWIRE_PlayDevice(device, buf, bufsize); - return; - } - - SDL_PlaybackAudioThreadIterate(device); -} - -static void PIPEWIRE_FlushRecording(SDL_AudioDevice *device) -{ - struct pw_stream *stream = device->hidden->stream; - struct pw_buffer *pw_buf = PIPEWIRE_pw_stream_dequeue_buffer(stream); - if (pw_buf) { // just requeue it without any further thought. - PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf); - } -} - -static int PIPEWIRE_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - struct pw_stream *stream = device->hidden->stream; - struct pw_buffer *pw_buf = PIPEWIRE_pw_stream_dequeue_buffer(stream); - if (!pw_buf) { - return 0; - } - - struct spa_buffer *spa_buf = pw_buf->buffer; - if (!spa_buf) { - PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf); - return 0; - } - - const Uint8 *src = (const Uint8 *)spa_buf->datas[0].data; - const Uint32 offset = SPA_MIN(spa_buf->datas[0].chunk->offset, spa_buf->datas[0].maxsize); - const Uint32 size = SPA_MIN(spa_buf->datas[0].chunk->size, spa_buf->datas[0].maxsize - offset); - const int cpy = SDL_min(buflen, (int) size); - - SDL_assert(size <= buflen); // We'll have to reengineer some stuff if this turns out to not be true. - - SDL_memcpy(buffer, src + offset, cpy); - PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf); - - return cpy; -} - -static void input_callback(void *data) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *) data; - - // this callback can fire in a background thread during OpenDevice, while we're still blocking - // _with the device lock_ until the stream is ready, causing a deadlock. Drop data in this case. - if (device->hidden->stream_init_status != PW_READY_FLAG_ALL_BITS) { - PIPEWIRE_FlushRecording(device); - return; - } - - SDL_RecordingAudioThreadIterate(device); -} - -static void stream_add_buffer_callback(void *data, struct pw_buffer *buffer) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *) data; - - if (device->recording == false) { - /* Clamp the output spec samples and size to the max size of the Pipewire buffer. - If they exceed the maximum size of the Pipewire buffer, double buffering will be used. */ - if (device->buffer_size > buffer->buffer->datas[0].maxsize) { - SDL_LockMutex(device->lock); - device->sample_frames = buffer->buffer->datas[0].maxsize / device->hidden->stride; - device->buffer_size = buffer->buffer->datas[0].maxsize; - SDL_UnlockMutex(device->lock); - } - } - - device->hidden->stream_init_status |= PW_READY_FLAG_BUFFER_ADDED; - PIPEWIRE_pw_thread_loop_signal(device->hidden->loop, false); -} - -static void stream_state_changed_callback(void *data, enum pw_stream_state old, enum pw_stream_state state, const char *error) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *) data; - - if (state == PW_STREAM_STATE_STREAMING) { - device->hidden->stream_init_status |= PW_READY_FLAG_STREAM_READY; - } - - if (state == PW_STREAM_STATE_STREAMING || state == PW_STREAM_STATE_ERROR) { - PIPEWIRE_pw_thread_loop_signal(device->hidden->loop, false); - } -} - -static const struct pw_stream_events stream_output_events = { PW_VERSION_STREAM_EVENTS, - .state_changed = stream_state_changed_callback, - .add_buffer = stream_add_buffer_callback, - .process = output_callback }; -static const struct pw_stream_events stream_input_events = { PW_VERSION_STREAM_EVENTS, - .state_changed = stream_state_changed_callback, - .add_buffer = stream_add_buffer_callback, - .process = input_callback }; - -static bool PIPEWIRE_OpenDevice(SDL_AudioDevice *device) -{ - /* - * NOTE: The PW_STREAM_FLAG_RT_PROCESS flag can be set to call the stream - * processing callback from the realtime thread. However, it comes with some - * caveats: no file IO, allocations, locking or other blocking operations - * must occur in the mixer callback. As this cannot be guaranteed when the - * callback is in the calling application, this flag is omitted. - */ - static const enum pw_stream_flags STREAM_FLAGS = PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS; - - char thread_name[PW_THREAD_NAME_BUFFER_LENGTH]; - Uint8 pod_buffer[PW_POD_BUFFER_LENGTH]; - struct spa_pod_builder b = SPA_POD_BUILDER_INIT(pod_buffer, sizeof(pod_buffer)); - struct spa_audio_info_raw spa_info = { 0 }; - const struct spa_pod *params = NULL; - struct SDL_PrivateAudioData *priv; - struct pw_properties *props; - const char *app_name, *icon_name, *app_id, *stream_name, *stream_role, *error; - Uint32 node_id = !device->handle ? PW_ID_ANY : PW_HANDLE_TO_ID(device->handle); - const bool recording = device->recording; - int res; - - // Clamp the period size to sane values - const int min_period = PW_MIN_SAMPLES * SPA_MAX(device->spec.freq / PW_BASE_CLOCK_RATE, 1); - - // Get the hints for the application name, icon name, stream name and role - app_name = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING); - - icon_name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_APP_ICON_NAME); - if (!icon_name || *icon_name == '\0') { - icon_name = "applications-games"; - } - - // App ID. Default to NULL if not available. - app_id = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING); - - stream_name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME); - if (!stream_name || *stream_name == '\0') { - stream_name = "Audio Stream"; - } - - /* - * 'Music' is the default used internally by Pipewire and it's modules, - * but 'Game' seems more appropriate for the majority of SDL applications. - */ - stream_role = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_STREAM_ROLE); - if (!stream_role || *stream_role == '\0') { - stream_role = "Game"; - } - - // Initialize the Pipewire stream info from the SDL audio spec - initialize_spa_info(&device->spec, &spa_info); - params = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &spa_info); - if (!params) { - return SDL_SetError("Pipewire: Failed to set audio format parameters"); - } - - priv = SDL_calloc(1, sizeof(struct SDL_PrivateAudioData)); - device->hidden = priv; - if (!priv) { - return false; - } - - // Size of a single audio frame in bytes - priv->stride = SDL_AUDIO_FRAMESIZE(device->spec); - - if (device->sample_frames < min_period) { - device->sample_frames = min_period; - } - - SDL_UpdatedAudioDeviceFormat(device); - - SDL_GetAudioThreadName(device, thread_name, sizeof(thread_name)); - priv->loop = PIPEWIRE_pw_thread_loop_new(thread_name, NULL); - if (!priv->loop) { - return SDL_SetError("Pipewire: Failed to create stream loop (%i)", errno); - } - - // Load the realtime module so Pipewire can set the loop thread to the appropriate priority. - props = PIPEWIRE_pw_properties_new(PW_KEY_CONFIG_NAME, "client-rt.conf", NULL); - if (!props) { - return SDL_SetError("Pipewire: Failed to create stream context properties (%i)", errno); - } - - priv->context = PIPEWIRE_pw_context_new(PIPEWIRE_pw_thread_loop_get_loop(priv->loop), props, 0); - if (!priv->context) { - return SDL_SetError("Pipewire: Failed to create stream context (%i)", errno); - } - - props = PIPEWIRE_pw_properties_new(NULL, NULL); - if (!props) { - return SDL_SetError("Pipewire: Failed to create stream properties (%i)", errno); - } - - PIPEWIRE_pw_properties_set(props, PW_KEY_MEDIA_TYPE, "Audio"); - PIPEWIRE_pw_properties_set(props, PW_KEY_MEDIA_CATEGORY, recording ? "Capture" : "Playback"); - PIPEWIRE_pw_properties_set(props, PW_KEY_MEDIA_ROLE, stream_role); - PIPEWIRE_pw_properties_set(props, PW_KEY_APP_NAME, app_name); - PIPEWIRE_pw_properties_set(props, PW_KEY_APP_ICON_NAME, icon_name); - if (app_id) { - PIPEWIRE_pw_properties_set(props, PW_KEY_APP_ID, app_id); - } - PIPEWIRE_pw_properties_set(props, PW_KEY_NODE_NAME, stream_name); - PIPEWIRE_pw_properties_set(props, PW_KEY_NODE_DESCRIPTION, stream_name); - PIPEWIRE_pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%i", device->sample_frames, device->spec.freq); - PIPEWIRE_pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", device->spec.freq); - PIPEWIRE_pw_properties_set(props, PW_KEY_NODE_ALWAYS_PROCESS, "true"); - PIPEWIRE_pw_properties_set(props, PW_KEY_NODE_DONT_RECONNECT, "true"); // Requesting a specific device, don't migrate to new default hardware. - - if (node_id != PW_ID_ANY) { - PIPEWIRE_pw_thread_loop_lock(hotplug_loop); - const struct io_node *node = io_list_get_by_id(node_id); - if (node) { - PIPEWIRE_pw_properties_set(props, PW_KEY_TARGET_OBJECT, node->path); - } - PIPEWIRE_pw_thread_loop_unlock(hotplug_loop); - } - - // Create the new stream - priv->stream = PIPEWIRE_pw_stream_new_simple(PIPEWIRE_pw_thread_loop_get_loop(priv->loop), stream_name, props, - recording ? &stream_input_events : &stream_output_events, device); - if (!priv->stream) { - return SDL_SetError("Pipewire: Failed to create stream (%i)", errno); - } - - // The target node is passed via PW_KEY_TARGET_OBJECT; target_id is a legacy parameter and must be PW_ID_ANY. - res = PIPEWIRE_pw_stream_connect(priv->stream, recording ? PW_DIRECTION_INPUT : PW_DIRECTION_OUTPUT, PW_ID_ANY, STREAM_FLAGS, - ¶ms, 1); - if (res != 0) { - return SDL_SetError("Pipewire: Failed to connect stream"); - } - - res = PIPEWIRE_pw_thread_loop_start(priv->loop); - if (res != 0) { - return SDL_SetError("Pipewire: Failed to start stream loop"); - } - - // Wait until all pre-open init flags are set or the stream has failed. - PIPEWIRE_pw_thread_loop_lock(priv->loop); - while (priv->stream_init_status != PW_READY_FLAG_ALL_PREOPEN_BITS && - PIPEWIRE_pw_stream_get_state(priv->stream, NULL) != PW_STREAM_STATE_ERROR) { - PIPEWIRE_pw_thread_loop_wait(priv->loop); - } - priv->stream_init_status |= PW_READY_FLAG_OPEN_COMPLETE; - PIPEWIRE_pw_thread_loop_unlock(priv->loop); - - if (PIPEWIRE_pw_stream_get_state(priv->stream, &error) == PW_STREAM_STATE_ERROR) { - return SDL_SetError("Pipewire: Stream error: %s", error); - } - - return true; -} - -static void PIPEWIRE_CloseDevice(SDL_AudioDevice *device) -{ - if (!device->hidden) { - return; - } - - if (device->hidden->loop) { - PIPEWIRE_pw_thread_loop_stop(device->hidden->loop); - } - - if (device->hidden->stream) { - PIPEWIRE_pw_stream_destroy(device->hidden->stream); - } - - if (device->hidden->context) { - PIPEWIRE_pw_context_destroy(device->hidden->context); - } - - if (device->hidden->loop) { - PIPEWIRE_pw_thread_loop_destroy(device->hidden->loop); - } - - SDL_free(device->hidden); - device->hidden = NULL; - - SDL_AudioThreadFinalize(device); -} - -static void PIPEWIRE_DeinitializeStart(void) -{ - if (pipewire_initialized) { - hotplug_loop_destroy(); - } -} - -static void PIPEWIRE_Deinitialize(void) -{ - if (pipewire_initialized) { - hotplug_loop_destroy(); - deinit_pipewire_library(); - pipewire_initialized = false; - } -} - -static bool PipewireInitialize(SDL_AudioDriverImpl *impl) -{ - if (!pipewire_initialized) { - if (!init_pipewire_library()) { - return false; - } - - pipewire_initialized = true; - - if (!hotplug_loop_init()) { - PIPEWIRE_Deinitialize(); - return false; - } - } - - impl->DetectDevices = PIPEWIRE_DetectDevices; - impl->OpenDevice = PIPEWIRE_OpenDevice; - impl->DeinitializeStart = PIPEWIRE_DeinitializeStart; - impl->Deinitialize = PIPEWIRE_Deinitialize; - impl->PlayDevice = PIPEWIRE_PlayDevice; - impl->GetDeviceBuf = PIPEWIRE_GetDeviceBuf; - impl->RecordDevice = PIPEWIRE_RecordDevice; - impl->FlushRecording = PIPEWIRE_FlushRecording; - impl->CloseDevice = PIPEWIRE_CloseDevice; - - impl->HasRecordingSupport = true; - impl->ProvidesOwnCallbackThread = true; - - return true; -} - -static bool PIPEWIRE_PREFERRED_Init(SDL_AudioDriverImpl *impl) -{ - if (!PipewireInitialize(impl)) { - return false; - } - - // run device detection but don't add any devices to SDL; we're just waiting to see if PipeWire sees any devices. If not, fall back to the next backend. - PIPEWIRE_pw_thread_loop_lock(hotplug_loop); - - // Wait until the initial registry enumeration is complete - if (!hotplug_init_complete) { - PIPEWIRE_pw_thread_loop_wait(hotplug_loop); - } - - const bool no_devices = spa_list_is_empty(&hotplug_io_list); - - PIPEWIRE_pw_thread_loop_unlock(hotplug_loop); - - if (no_devices || !pipewire_core_version_at_least(1, 0, 0)) { - PIPEWIRE_Deinitialize(); - return false; - } - - return true; // this will move on to PIPEWIRE_DetectDevices and reuse hotplug_io_list. -} - -static bool PIPEWIRE_Init(SDL_AudioDriverImpl *impl) -{ - return PipewireInitialize(impl); -} - -AudioBootStrap PIPEWIRE_PREFERRED_bootstrap = { - "pipewire", "Pipewire", PIPEWIRE_PREFERRED_Init, false, true -}; -AudioBootStrap PIPEWIRE_bootstrap = { - "pipewire", "Pipewire", PIPEWIRE_Init, false, false -}; - -#endif // SDL_AUDIO_DRIVER_PIPEWIRE diff --git a/contrib/SDL-3.2.8/src/audio/pipewire/SDL_pipewire.h b/contrib/SDL-3.2.8/src/audio/pipewire/SDL_pipewire.h deleted file mode 100644 index e609e25..0000000 --- a/contrib/SDL-3.2.8/src/audio/pipewire/SDL_pipewire.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include "SDL_internal.h" - -#ifndef SDL_pipewire_h_ -#define SDL_pipewire_h_ - -#include "../SDL_sysaudio.h" -#include - -struct SDL_PrivateAudioData -{ - struct pw_thread_loop *loop; - struct pw_stream *stream; - struct pw_context *context; - - Sint32 stride; // Bytes-per-frame - int stream_init_status; - - // Set in GetDeviceBuf, filled in AudioThreadIterate, queued in PlayDevice - struct pw_buffer *pw_buf; -}; - -#endif // SDL_pipewire_h_ diff --git a/contrib/SDL-3.2.8/src/audio/ps2/SDL_ps2audio.c b/contrib/SDL-3.2.8/src/audio/ps2/SDL_ps2audio.c deleted file mode 100644 index 6579f1b..0000000 --- a/contrib/SDL-3.2.8/src/audio/ps2/SDL_ps2audio.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#include "../SDL_sysaudio.h" -#include "SDL_ps2audio.h" - -#include -#include -#include - -static bool PS2AUDIO_OpenDevice(SDL_AudioDevice *device) -{ - device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } - - // These are the native supported audio PS2 configs - switch (device->spec.freq) { - case 11025: - case 12000: - case 22050: - case 24000: - case 32000: - case 44100: - case 48000: - break; // acceptable value, keep it - default: - device->spec.freq = 48000; - break; - } - - device->sample_frames = 512; - device->spec.channels = device->spec.channels == 1 ? 1 : 2; - device->spec.format = device->spec.format == SDL_AUDIO_S8 ? SDL_AUDIO_S8 : SDL_AUDIO_S16; - - struct audsrv_fmt_t format; - format.bits = device->spec.format == SDL_AUDIO_S8 ? 8 : 16; - format.freq = device->spec.freq; - format.channels = device->spec.channels; - - device->hidden->channel = audsrv_set_format(&format); - audsrv_set_volume(MAX_VOLUME); - - if (device->hidden->channel < 0) { - return SDL_SetError("Couldn't reserve hardware channel"); - } - - // Update the fragment size as size in bytes. - SDL_UpdatedAudioDeviceFormat(device); - - /* Allocate the mixing buffer. Its size and starting address must - be a multiple of 64 bytes. Our sample count is already a multiple of - 64, so spec->size should be a multiple of 64 as well. */ - const int mixlen = device->buffer_size * NUM_BUFFERS; - device->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen); - if (!device->hidden->rawbuf) { - return SDL_SetError("Couldn't allocate mixing buffer"); - } - - SDL_memset(device->hidden->rawbuf, device->silence_value, mixlen); - for (int i = 0; i < NUM_BUFFERS; i++) { - device->hidden->mixbufs[i] = &device->hidden->rawbuf[i * device->buffer_size]; - } - - return true; -} - -static bool PS2AUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) -{ - // this returns number of bytes accepted or a negative error. We assume anything other than buflen is a fatal error. - return (audsrv_play_audio((char *)buffer, buflen) == buflen); -} - -static bool PS2AUDIO_WaitDevice(SDL_AudioDevice *device) -{ - audsrv_wait_audio(device->buffer_size); - return true; -} - -static Uint8 *PS2AUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - Uint8 *buffer = device->hidden->mixbufs[device->hidden->next_buffer]; - device->hidden->next_buffer = (device->hidden->next_buffer + 1) % NUM_BUFFERS; - return buffer; -} - -static void PS2AUDIO_CloseDevice(SDL_AudioDevice *device) -{ - if (device->hidden) { - if (device->hidden->channel >= 0) { - audsrv_stop_audio(); - device->hidden->channel = -1; - } - - if (device->hidden->rawbuf) { - SDL_aligned_free(device->hidden->rawbuf); - device->hidden->rawbuf = NULL; - } - SDL_free(device->hidden); - device->hidden = NULL; - } -} - -static void PS2AUDIO_ThreadInit(SDL_AudioDevice *device) -{ - /* Increase the priority of this audio thread by 1 to put it - ahead of other SDL threads. */ - const int32_t thid = GetThreadId(); - ee_thread_status_t status; - if (ReferThreadStatus(thid, &status) == 0) { - ChangeThreadPriority(thid, status.current_priority - 1); - } -} - -static void PS2AUDIO_Deinitialize(void) -{ - deinit_audio_driver(); -} - -static bool PS2AUDIO_Init(SDL_AudioDriverImpl *impl) -{ - if (init_audio_driver() < 0) { - return false; - } - - impl->OpenDevice = PS2AUDIO_OpenDevice; - impl->PlayDevice = PS2AUDIO_PlayDevice; - impl->WaitDevice = PS2AUDIO_WaitDevice; - impl->GetDeviceBuf = PS2AUDIO_GetDeviceBuf; - impl->CloseDevice = PS2AUDIO_CloseDevice; - impl->ThreadInit = PS2AUDIO_ThreadInit; - impl->Deinitialize = PS2AUDIO_Deinitialize; - impl->OnlyHasDefaultPlaybackDevice = true; - return true; // this audio target is available. -} - -AudioBootStrap PS2AUDIO_bootstrap = { - "ps2", "PS2 audio driver", PS2AUDIO_Init, false, false -}; diff --git a/contrib/SDL-3.2.8/src/audio/ps2/SDL_ps2audio.h b/contrib/SDL-3.2.8/src/audio/ps2/SDL_ps2audio.h deleted file mode 100644 index 5a0ecea..0000000 --- a/contrib/SDL-3.2.8/src/audio/ps2/SDL_ps2audio.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_ps2audio_h_ -#define SDL_ps2audio_h_ - -#include "../SDL_sysaudio.h" - -#define NUM_BUFFERS 2 - -struct SDL_PrivateAudioData -{ - // The hardware output channel. - int channel; - // The raw allocated mixing buffer. - Uint8 *rawbuf; - // Individual mixing buffers. - Uint8 *mixbufs[NUM_BUFFERS]; - // Index of the next available mixing buffer. - int next_buffer; -}; - -#endif // SDL_ps2audio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/psp/SDL_pspaudio.c b/contrib/SDL-3.2.8/src/audio/psp/SDL_pspaudio.c deleted file mode 100644 index 18546e8..0000000 --- a/contrib/SDL-3.2.8/src/audio/psp/SDL_pspaudio.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_PSP - -#include -#include -#include - -#include "../SDL_audiodev_c.h" -#include "../SDL_sysaudio.h" -#include "SDL_pspaudio.h" - -#include -#include - -static bool isBasicAudioConfig(const SDL_AudioSpec *spec) -{ - return spec->freq == 44100; -} - -static bool PSPAUDIO_OpenDevice(SDL_AudioDevice *device) -{ - device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } - - // device only natively supports S16LSB - device->spec.format = SDL_AUDIO_S16LE; - - /* PSP has some limitations with the Audio. It fully supports 44.1KHz (Mono & Stereo), - however with frequencies different than 44.1KHz, it just supports Stereo, - so a resampler must be done for these scenarios */ - if (isBasicAudioConfig(&device->spec)) { - // The sample count must be a multiple of 64. - device->sample_frames = PSP_AUDIO_SAMPLE_ALIGN(device->sample_frames); - // The number of channels (1 or 2). - device->spec.channels = device->spec.channels == 1 ? 1 : 2; - const int format = (device->spec.channels == 1) ? PSP_AUDIO_FORMAT_MONO : PSP_AUDIO_FORMAT_STEREO; - device->hidden->channel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, device->sample_frames, format); - } else { - // 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11050, 8000 - switch (device->spec.freq) { - case 8000: - case 11025: - case 12000: - case 16000: - case 22050: - case 24000: - case 32000: - case 44100: - case 48000: - break; // acceptable, keep it - default: - device->spec.freq = 48000; - break; - } - // The number of samples to output in one output call (min 17, max 4111). - device->sample_frames = device->sample_frames < 17 ? 17 : (device->sample_frames > 4111 ? 4111 : device->sample_frames); - device->spec.channels = 2; // we're forcing the hardware to stereo. - device->hidden->channel = sceAudioSRCChReserve(device->sample_frames, device->spec.freq, 2); - } - - if (device->hidden->channel < 0) { - return SDL_SetError("Couldn't reserve hardware channel"); - } - - // Update the fragment size as size in bytes. - SDL_UpdatedAudioDeviceFormat(device); - - /* Allocate the mixing buffer. Its size and starting address must - be a multiple of 64 bytes. Our sample count is already a multiple of - 64, so spec->size should be a multiple of 64 as well. */ - const int mixlen = device->buffer_size * NUM_BUFFERS; - device->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen); - if (!device->hidden->rawbuf) { - return SDL_SetError("Couldn't allocate mixing buffer"); - } - - SDL_memset(device->hidden->rawbuf, device->silence_value, mixlen); - for (int i = 0; i < NUM_BUFFERS; i++) { - device->hidden->mixbufs[i] = &device->hidden->rawbuf[i * device->buffer_size]; - } - - return true; -} - -static bool PSPAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) -{ - int rc; - if (!isBasicAudioConfig(&device->spec)) { - SDL_assert(device->spec.channels == 2); - rc = sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, (void *) buffer); - } else { - rc = sceAudioOutputPannedBlocking(device->hidden->channel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, (void *) buffer); - } - return (rc == 0); -} - -static bool PSPAUDIO_WaitDevice(SDL_AudioDevice *device) -{ - return true; // Because we block when sending audio, there's no need for this function to do anything. -} - -static Uint8 *PSPAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - Uint8 *buffer = device->hidden->mixbufs[device->hidden->next_buffer]; - device->hidden->next_buffer = (device->hidden->next_buffer + 1) % NUM_BUFFERS; - return buffer; -} - -static void PSPAUDIO_CloseDevice(SDL_AudioDevice *device) -{ - if (device->hidden) { - if (device->hidden->channel >= 0) { - if (!isBasicAudioConfig(&device->spec)) { - sceAudioSRCChRelease(); - } else { - sceAudioChRelease(device->hidden->channel); - } - device->hidden->channel = -1; - } - - if (device->hidden->rawbuf) { - SDL_aligned_free(device->hidden->rawbuf); - device->hidden->rawbuf = NULL; - } - SDL_free(device->hidden); - device->hidden = NULL; - } -} - -static void PSPAUDIO_ThreadInit(SDL_AudioDevice *device) -{ - /* Increase the priority of this audio thread by 1 to put it - ahead of other SDL threads. */ - const SceUID thid = sceKernelGetThreadId(); - SceKernelThreadInfo status; - status.size = sizeof(SceKernelThreadInfo); - if (sceKernelReferThreadStatus(thid, &status) == 0) { - sceKernelChangeThreadPriority(thid, status.currentPriority - 1); - } -} - -static bool PSPAUDIO_Init(SDL_AudioDriverImpl *impl) -{ - impl->OpenDevice = PSPAUDIO_OpenDevice; - impl->PlayDevice = PSPAUDIO_PlayDevice; - impl->WaitDevice = PSPAUDIO_WaitDevice; - impl->GetDeviceBuf = PSPAUDIO_GetDeviceBuf; - impl->CloseDevice = PSPAUDIO_CloseDevice; - impl->ThreadInit = PSPAUDIO_ThreadInit; - impl->OnlyHasDefaultPlaybackDevice = true; - //impl->HasRecordingSupport = true; - //impl->OnlyHasDefaultRecordingDevice = true; - return true; -} - -AudioBootStrap PSPAUDIO_bootstrap = { - "psp", "PSP audio driver", PSPAUDIO_Init, false, false -}; - -#endif // SDL_AUDIO_DRIVER_PSP diff --git a/contrib/SDL-3.2.8/src/audio/psp/SDL_pspaudio.h b/contrib/SDL-3.2.8/src/audio/psp/SDL_pspaudio.h deleted file mode 100644 index d7b2056..0000000 --- a/contrib/SDL-3.2.8/src/audio/psp/SDL_pspaudio.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef SDL_pspaudio_h_ -#define SDL_pspaudio_h_ - -#include "../SDL_sysaudio.h" - -#define NUM_BUFFERS 2 - -struct SDL_PrivateAudioData -{ - // The hardware output channel. - int channel; - // The raw allocated mixing buffer. - Uint8 *rawbuf; - // Individual mixing buffers. - Uint8 *mixbufs[NUM_BUFFERS]; - // Index of the next available mixing buffer. - int next_buffer; -}; - -#endif // SDL_pspaudio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/pulseaudio/SDL_pulseaudio.c b/contrib/SDL-3.2.8/src/audio/pulseaudio/SDL_pulseaudio.c deleted file mode 100644 index 049a5ec..0000000 --- a/contrib/SDL-3.2.8/src/audio/pulseaudio/SDL_pulseaudio.c +++ /dev/null @@ -1,1037 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO - -// Allow access to a raw mixing buffer - -#ifdef HAVE_SIGNAL_H -#include -#endif -#include -#include - -#include "../SDL_sysaudio.h" -#include "SDL_pulseaudio.h" -#include "../../thread/SDL_systhread.h" - -#if (PA_PROTOCOL_VERSION < 28) -typedef void (*pa_operation_notify_cb_t) (pa_operation *o, void *userdata); -#endif - -typedef struct PulseDeviceHandle -{ - char *device_path; - uint32_t device_index; -} PulseDeviceHandle; - -// should we include monitors in the device list? Set at SDL_Init time -static bool include_monitors = false; - -static pa_threaded_mainloop *pulseaudio_threaded_mainloop = NULL; -static pa_context *pulseaudio_context = NULL; -static SDL_Thread *pulseaudio_hotplug_thread = NULL; -static SDL_AtomicInt pulseaudio_hotplug_thread_active; - -// These are the OS identifiers (i.e. ALSA strings)...these are allocated in a callback -// when the default changes, and noticed by the hotplug thread when it alerts SDL -// to the change. -static char *default_sink_path = NULL; -static char *default_source_path = NULL; -static bool default_sink_changed = false; -static bool default_source_changed = false; - - -static const char *(*PULSEAUDIO_pa_get_library_version)(void); -static pa_channel_map *(*PULSEAUDIO_pa_channel_map_init_auto)( - pa_channel_map *, unsigned, pa_channel_map_def_t); -static const char *(*PULSEAUDIO_pa_strerror)(int); -static pa_proplist *(*PULSEAUDIO_pa_proplist_new)(void); -static void (*PULSEAUDIO_pa_proplist_free)(pa_proplist *); -static int (*PULSEAUDIO_pa_proplist_sets)(pa_proplist *, const char *, const char *); - -static pa_threaded_mainloop *(*PULSEAUDIO_pa_threaded_mainloop_new)(void); -static void (*PULSEAUDIO_pa_threaded_mainloop_set_name)(pa_threaded_mainloop *, const char *); -static pa_mainloop_api *(*PULSEAUDIO_pa_threaded_mainloop_get_api)(pa_threaded_mainloop *); -static int (*PULSEAUDIO_pa_threaded_mainloop_start)(pa_threaded_mainloop *); -static void (*PULSEAUDIO_pa_threaded_mainloop_stop)(pa_threaded_mainloop *); -static void (*PULSEAUDIO_pa_threaded_mainloop_lock)(pa_threaded_mainloop *); -static void (*PULSEAUDIO_pa_threaded_mainloop_unlock)(pa_threaded_mainloop *); -static void (*PULSEAUDIO_pa_threaded_mainloop_wait)(pa_threaded_mainloop *); -static void (*PULSEAUDIO_pa_threaded_mainloop_signal)(pa_threaded_mainloop *, int); -static void (*PULSEAUDIO_pa_threaded_mainloop_free)(pa_threaded_mainloop *); - -static pa_operation_state_t (*PULSEAUDIO_pa_operation_get_state)( - const pa_operation *); -static void (*PULSEAUDIO_pa_operation_set_state_callback)(pa_operation *, pa_operation_notify_cb_t, void *); -static void (*PULSEAUDIO_pa_operation_cancel)(pa_operation *); -static void (*PULSEAUDIO_pa_operation_unref)(pa_operation *); - -static pa_context *(*PULSEAUDIO_pa_context_new_with_proplist)(pa_mainloop_api *, - const char *, - const pa_proplist *); -static void (*PULSEAUDIO_pa_context_set_state_callback)(pa_context *, pa_context_notify_cb_t, void *); -static int (*PULSEAUDIO_pa_context_connect)(pa_context *, const char *, - pa_context_flags_t, const pa_spawn_api *); -static pa_operation *(*PULSEAUDIO_pa_context_get_sink_info_list)(pa_context *, pa_sink_info_cb_t, void *); -static pa_operation *(*PULSEAUDIO_pa_context_get_source_info_list)(pa_context *, pa_source_info_cb_t, void *); -static pa_operation *(*PULSEAUDIO_pa_context_get_sink_info_by_index)(pa_context *, uint32_t, pa_sink_info_cb_t, void *); -static pa_operation *(*PULSEAUDIO_pa_context_get_source_info_by_index)(pa_context *, uint32_t, pa_source_info_cb_t, void *); -static pa_context_state_t (*PULSEAUDIO_pa_context_get_state)(const pa_context *); -static pa_operation *(*PULSEAUDIO_pa_context_subscribe)(pa_context *, pa_subscription_mask_t, pa_context_success_cb_t, void *); -static void (*PULSEAUDIO_pa_context_set_subscribe_callback)(pa_context *, pa_context_subscribe_cb_t, void *); -static void (*PULSEAUDIO_pa_context_disconnect)(pa_context *); -static void (*PULSEAUDIO_pa_context_unref)(pa_context *); - -static pa_stream *(*PULSEAUDIO_pa_stream_new)(pa_context *, const char *, - const pa_sample_spec *, const pa_channel_map *); -static void (*PULSEAUDIO_pa_stream_set_state_callback)(pa_stream *, pa_stream_notify_cb_t, void *); -static int (*PULSEAUDIO_pa_stream_connect_playback)(pa_stream *, const char *, - const pa_buffer_attr *, pa_stream_flags_t, const pa_cvolume *, pa_stream *); -static int (*PULSEAUDIO_pa_stream_connect_record)(pa_stream *, const char *, - const pa_buffer_attr *, pa_stream_flags_t); -static const pa_buffer_attr *(*PULSEAUDIO_pa_stream_get_buffer_attr)(pa_stream *); -static pa_stream_state_t (*PULSEAUDIO_pa_stream_get_state)(const pa_stream *); -static size_t (*PULSEAUDIO_pa_stream_writable_size)(const pa_stream *); -static size_t (*PULSEAUDIO_pa_stream_readable_size)(const pa_stream *); -static int (*PULSEAUDIO_pa_stream_write)(pa_stream *, const void *, size_t, - pa_free_cb_t, int64_t, pa_seek_mode_t); -static int (*PULSEAUDIO_pa_stream_begin_write)(pa_stream *, void **, size_t *); -static pa_operation *(*PULSEAUDIO_pa_stream_drain)(pa_stream *, - pa_stream_success_cb_t, void *); -static int (*PULSEAUDIO_pa_stream_peek)(pa_stream *, const void **, size_t *); -static int (*PULSEAUDIO_pa_stream_drop)(pa_stream *); -static pa_operation *(*PULSEAUDIO_pa_stream_flush)(pa_stream *, - pa_stream_success_cb_t, void *); -static int (*PULSEAUDIO_pa_stream_disconnect)(pa_stream *); -static void (*PULSEAUDIO_pa_stream_unref)(pa_stream *); -static void (*PULSEAUDIO_pa_stream_set_write_callback)(pa_stream *, pa_stream_request_cb_t, void *); -static void (*PULSEAUDIO_pa_stream_set_read_callback)(pa_stream *, pa_stream_request_cb_t, void *); -static pa_operation *(*PULSEAUDIO_pa_context_get_server_info)(pa_context *, pa_server_info_cb_t, void *); - -static bool load_pulseaudio_syms(void); - -#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC - -static const char *pulseaudio_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC; -static SDL_SharedObject *pulseaudio_handle = NULL; - -static bool load_pulseaudio_sym(const char *fn, void **addr) -{ - *addr = SDL_LoadFunction(pulseaudio_handle, fn); - if (!*addr) { - // Don't call SDL_SetError(): SDL_LoadFunction already did. - return false; - } - - return true; -} - -// cast funcs to char* first, to please GCC's strict aliasing rules. -#define SDL_PULSEAUDIO_SYM(x) \ - if (!load_pulseaudio_sym(#x, (void **)(char *)&PULSEAUDIO_##x)) \ - return false - -static void UnloadPulseAudioLibrary(void) -{ - if (pulseaudio_handle) { - SDL_UnloadObject(pulseaudio_handle); - pulseaudio_handle = NULL; - } -} - -static bool LoadPulseAudioLibrary(void) -{ - bool result = true; - if (!pulseaudio_handle) { - pulseaudio_handle = SDL_LoadObject(pulseaudio_library); - if (!pulseaudio_handle) { - result = false; - // Don't call SDL_SetError(): SDL_LoadObject already did. - } else { - result = load_pulseaudio_syms(); - if (!result) { - UnloadPulseAudioLibrary(); - } - } - } - return result; -} - -#else - -#define SDL_PULSEAUDIO_SYM(x) PULSEAUDIO_##x = x - -static void UnloadPulseAudioLibrary(void) -{ -} - -static bool LoadPulseAudioLibrary(void) -{ - load_pulseaudio_syms(); - return true; -} - -#endif // SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC - -static bool load_pulseaudio_syms(void) -{ - SDL_PULSEAUDIO_SYM(pa_get_library_version); - SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_new); - SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_get_api); - SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_start); - SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_stop); - SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_lock); - SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_unlock); - SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_wait); - SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_signal); - SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_free); - SDL_PULSEAUDIO_SYM(pa_operation_get_state); - SDL_PULSEAUDIO_SYM(pa_operation_cancel); - SDL_PULSEAUDIO_SYM(pa_operation_unref); - SDL_PULSEAUDIO_SYM(pa_context_new_with_proplist); - SDL_PULSEAUDIO_SYM(pa_context_set_state_callback); - SDL_PULSEAUDIO_SYM(pa_context_connect); - SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_list); - SDL_PULSEAUDIO_SYM(pa_context_get_source_info_list); - SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_by_index); - SDL_PULSEAUDIO_SYM(pa_context_get_source_info_by_index); - SDL_PULSEAUDIO_SYM(pa_context_get_state); - SDL_PULSEAUDIO_SYM(pa_context_subscribe); - SDL_PULSEAUDIO_SYM(pa_context_set_subscribe_callback); - SDL_PULSEAUDIO_SYM(pa_context_disconnect); - SDL_PULSEAUDIO_SYM(pa_context_unref); - SDL_PULSEAUDIO_SYM(pa_stream_new); - SDL_PULSEAUDIO_SYM(pa_stream_set_state_callback); - SDL_PULSEAUDIO_SYM(pa_stream_connect_playback); - SDL_PULSEAUDIO_SYM(pa_stream_connect_record); - SDL_PULSEAUDIO_SYM(pa_stream_get_buffer_attr); - SDL_PULSEAUDIO_SYM(pa_stream_get_state); - SDL_PULSEAUDIO_SYM(pa_stream_writable_size); - SDL_PULSEAUDIO_SYM(pa_stream_readable_size); - SDL_PULSEAUDIO_SYM(pa_stream_begin_write); - SDL_PULSEAUDIO_SYM(pa_stream_write); - SDL_PULSEAUDIO_SYM(pa_stream_drain); - SDL_PULSEAUDIO_SYM(pa_stream_disconnect); - SDL_PULSEAUDIO_SYM(pa_stream_peek); - SDL_PULSEAUDIO_SYM(pa_stream_drop); - SDL_PULSEAUDIO_SYM(pa_stream_flush); - SDL_PULSEAUDIO_SYM(pa_stream_unref); - SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto); - SDL_PULSEAUDIO_SYM(pa_strerror); - SDL_PULSEAUDIO_SYM(pa_stream_set_write_callback); - SDL_PULSEAUDIO_SYM(pa_stream_set_read_callback); - SDL_PULSEAUDIO_SYM(pa_context_get_server_info); - SDL_PULSEAUDIO_SYM(pa_proplist_new); - SDL_PULSEAUDIO_SYM(pa_proplist_free); - SDL_PULSEAUDIO_SYM(pa_proplist_sets); - - // optional -#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC - load_pulseaudio_sym("pa_operation_set_state_callback", (void **)(char *)&PULSEAUDIO_pa_operation_set_state_callback); // needs pulseaudio 4.0 - load_pulseaudio_sym("pa_threaded_mainloop_set_name", (void **)(char *)&PULSEAUDIO_pa_threaded_mainloop_set_name); // needs pulseaudio 5.0 -#elif (PA_PROTOCOL_VERSION >= 29) - PULSEAUDIO_pa_operation_set_state_callback = pa_operation_set_state_callback; - PULSEAUDIO_pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name; -#elif (PA_PROTOCOL_VERSION >= 28) - PULSEAUDIO_pa_operation_set_state_callback = pa_operation_set_state_callback; - PULSEAUDIO_pa_threaded_mainloop_set_name = NULL; -#else - PULSEAUDIO_pa_operation_set_state_callback = NULL; - PULSEAUDIO_pa_threaded_mainloop_set_name = NULL; -#endif - - return true; -} - -static const char *getAppName(void) -{ - return SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING); -} - -static void OperationStateChangeCallback(pa_operation *o, void *userdata) -{ - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // just signal any waiting code, it can look up the details. -} - -/* This function assume you are holding `mainloop`'s lock. The operation is unref'd in here, assuming - you did the work in the callback and just want to know it's done, though. */ -static void WaitForPulseOperation(pa_operation *o) -{ - // This checks for NO errors currently. Either fix that, check results elsewhere, or do things you don't care about. - SDL_assert(pulseaudio_threaded_mainloop != NULL); - if (o) { - // note that if PULSEAUDIO_pa_operation_set_state_callback == NULL, then `o` must have a callback that will signal pulseaudio_threaded_mainloop. - // If not, on really old (earlier PulseAudio 4.0, from the year 2013!) installs, this call will block forever. - // On more modern installs, we won't ever block forever, and maybe be more efficient, thanks to pa_operation_set_state_callback. - // WARNING: at the time of this writing: the Steam Runtime is still on PulseAudio 1.1! - if (PULSEAUDIO_pa_operation_set_state_callback) { - PULSEAUDIO_pa_operation_set_state_callback(o, OperationStateChangeCallback, NULL); - } - while (PULSEAUDIO_pa_operation_get_state(o) == PA_OPERATION_RUNNING) { - PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); // this releases the lock and blocks on an internal condition variable. - } - PULSEAUDIO_pa_operation_unref(o); - } -} - -static void DisconnectFromPulseServer(void) -{ - if (pulseaudio_threaded_mainloop) { - PULSEAUDIO_pa_threaded_mainloop_stop(pulseaudio_threaded_mainloop); - } - if (pulseaudio_context) { - PULSEAUDIO_pa_context_disconnect(pulseaudio_context); - PULSEAUDIO_pa_context_unref(pulseaudio_context); - pulseaudio_context = NULL; - } - if (pulseaudio_threaded_mainloop) { - PULSEAUDIO_pa_threaded_mainloop_free(pulseaudio_threaded_mainloop); - pulseaudio_threaded_mainloop = NULL; - } -} - -static void PulseContextStateChangeCallback(pa_context *context, void *userdata) -{ - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // just signal any waiting code, it can look up the details. -} - -static bool ConnectToPulseServer(void) -{ - pa_mainloop_api *mainloop_api = NULL; - pa_proplist *proplist = NULL; - const char *icon_name; - int state = 0; - - SDL_assert(pulseaudio_threaded_mainloop == NULL); - SDL_assert(pulseaudio_context == NULL); - - // Set up a new main loop - pulseaudio_threaded_mainloop = PULSEAUDIO_pa_threaded_mainloop_new(); - if (!pulseaudio_threaded_mainloop) { - return SDL_SetError("pa_threaded_mainloop_new() failed"); - } - - if (PULSEAUDIO_pa_threaded_mainloop_set_name) { - PULSEAUDIO_pa_threaded_mainloop_set_name(pulseaudio_threaded_mainloop, "PulseMainloop"); - } - - if (PULSEAUDIO_pa_threaded_mainloop_start(pulseaudio_threaded_mainloop) < 0) { - PULSEAUDIO_pa_threaded_mainloop_free(pulseaudio_threaded_mainloop); - pulseaudio_threaded_mainloop = NULL; - return SDL_SetError("pa_threaded_mainloop_start() failed"); - } - - PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - - mainloop_api = PULSEAUDIO_pa_threaded_mainloop_get_api(pulseaudio_threaded_mainloop); - SDL_assert(mainloop_api != NULL); // this never fails, right? - - proplist = PULSEAUDIO_pa_proplist_new(); - if (!proplist) { - SDL_SetError("pa_proplist_new() failed"); - goto failed; - } - - icon_name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_APP_ICON_NAME); - if (!icon_name || *icon_name == '\0') { - icon_name = "applications-games"; - } - PULSEAUDIO_pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, icon_name); - - pulseaudio_context = PULSEAUDIO_pa_context_new_with_proplist(mainloop_api, getAppName(), proplist); - if (!pulseaudio_context) { - SDL_SetError("pa_context_new_with_proplist() failed"); - goto failed; - } - PULSEAUDIO_pa_proplist_free(proplist); - - PULSEAUDIO_pa_context_set_state_callback(pulseaudio_context, PulseContextStateChangeCallback, NULL); - - // Connect to the PulseAudio server - if (PULSEAUDIO_pa_context_connect(pulseaudio_context, NULL, 0, NULL) < 0) { - SDL_SetError("Could not setup connection to PulseAudio"); - goto failed; - } - - state = PULSEAUDIO_pa_context_get_state(pulseaudio_context); - while (PA_CONTEXT_IS_GOOD(state) && (state != PA_CONTEXT_READY)) { - PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); - state = PULSEAUDIO_pa_context_get_state(pulseaudio_context); - } - - if (state != PA_CONTEXT_READY) { - SDL_SetError("Could not connect to PulseAudio"); - goto failed; - } - - PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); - - return true; // connected and ready! - -failed: - PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); - DisconnectFromPulseServer(); - return false; -} - -static void WriteCallback(pa_stream *p, size_t nbytes, void *userdata) -{ - struct SDL_PrivateAudioData *h = (struct SDL_PrivateAudioData *)userdata; - //SDL_Log("PULSEAUDIO WRITE CALLBACK! nbytes=%u", (unsigned int) nbytes); - h->bytes_requested += nbytes; - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); -} - -// This function waits until it is possible to write a full sound buffer -static bool PULSEAUDIO_WaitDevice(SDL_AudioDevice *device) -{ - struct SDL_PrivateAudioData *h = device->hidden; - bool result = true; - - //SDL_Log("PULSEAUDIO PLAYDEVICE START! mixlen=%d", available); - - PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - - while (!SDL_GetAtomicInt(&device->shutdown) && (h->bytes_requested == 0)) { - //SDL_Log("PULSEAUDIO WAIT IN WAITDEVICE!"); - 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)) { - //SDL_Log("PULSEAUDIO DEVICE FAILURE IN WAITDEVICE!"); - result = false; - break; - } - } - - PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); - - return result; -} - -static bool PULSEAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) -{ - struct SDL_PrivateAudioData *h = device->hidden; - - //SDL_Log("PULSEAUDIO PLAYDEVICE START! mixlen=%d", available); - - SDL_assert(h->bytes_requested >= buffer_size); - - PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - const int rc = PULSEAUDIO_pa_stream_write(h->stream, buffer, buffer_size, NULL, 0LL, PA_SEEK_RELATIVE); - PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); - - if (rc < 0) { - return false; - } - - //SDL_Log("PULSEAUDIO FEED! nbytes=%d", buffer_size); - h->bytes_requested -= buffer_size; - - //SDL_Log("PULSEAUDIO PLAYDEVICE END! written=%d", written); - return true; -} - -static Uint8 *PULSEAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - struct SDL_PrivateAudioData *h = device->hidden; - const size_t reqsize = (size_t) SDL_min(*buffer_size, h->bytes_requested); - size_t nbytes = reqsize; - void *data = NULL; - if (PULSEAUDIO_pa_stream_begin_write(h->stream, &data, &nbytes) == 0) { - *buffer_size = (int) nbytes; - return (Uint8 *) data; - } - - // don't know why this would fail, but we'll fall back just in case. - *buffer_size = (int) reqsize; - return device->hidden->mixbuf; -} - -static void ReadCallback(pa_stream *p, size_t nbytes, void *userdata) -{ - //SDL_Log("PULSEAUDIO READ CALLBACK! nbytes=%u", (unsigned int) nbytes); - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // the recording code queries what it needs, we just need to signal to end any wait -} - -static bool PULSEAUDIO_WaitRecordingDevice(SDL_AudioDevice *device) -{ - struct SDL_PrivateAudioData *h = device->hidden; - - if (h->recordingbuf) { - return true; // there's still data available to read. - } - - bool result = true; - - PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - - while (!SDL_GetAtomicInt(&device->shutdown)) { - 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)) { - //SDL_Log("PULSEAUDIO DEVICE FAILURE IN WAITRECORDINGDEVICE!"); - result = false; - break; - } else if (PULSEAUDIO_pa_stream_readable_size(h->stream) > 0) { - // a new fragment is available! - const void *data = NULL; - size_t nbytes = 0; - PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes); - SDL_assert(nbytes > 0); - if (!data) { // If NULL, then the buffer had a hole, ignore that - PULSEAUDIO_pa_stream_drop(h->stream); // drop this fragment. - } else { - // store this fragment's data for use with RecordDevice - //SDL_Log("PULSEAUDIO: recorded %d new bytes", (int) nbytes); - h->recordingbuf = (const Uint8 *)data; - h->recordinglen = nbytes; - break; - } - } - } - - PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); - - return result; -} - -static int PULSEAUDIO_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - struct SDL_PrivateAudioData *h = device->hidden; - - if (h->recordingbuf) { - const int cpy = SDL_min(buflen, h->recordinglen); - if (cpy > 0) { - //SDL_Log("PULSEAUDIO: fed %d recorded bytes", cpy); - SDL_memcpy(buffer, h->recordingbuf, cpy); - h->recordingbuf += cpy; - h->recordinglen -= cpy; - } - if (h->recordinglen == 0) { - h->recordingbuf = NULL; - PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); // don't know if you _have_ to lock for this, but just in case. - PULSEAUDIO_pa_stream_drop(h->stream); // done with this fragment. - PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); - } - return cpy; // new data, return it. - } - - return 0; -} - -static void PULSEAUDIO_FlushRecording(SDL_AudioDevice *device) -{ - struct SDL_PrivateAudioData *h = device->hidden; - const void *data = NULL; - size_t nbytes = 0, buflen = 0; - - PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - - if (h->recordingbuf) { - PULSEAUDIO_pa_stream_drop(h->stream); - h->recordingbuf = NULL; - h->recordinglen = 0; - } - - buflen = PULSEAUDIO_pa_stream_readable_size(h->stream); - while (!SDL_GetAtomicInt(&device->shutdown) && (buflen > 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)) { - //SDL_Log("PULSEAUDIO DEVICE FAILURE IN FLUSHRECORDING!"); - SDL_AudioDeviceDisconnected(device); - break; - } - - // a fragment of audio present before FlushCapture was call is - // still available! Just drop it. - PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes); - PULSEAUDIO_pa_stream_drop(h->stream); - buflen -= nbytes; - } - - PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); -} - -static void PULSEAUDIO_CloseDevice(SDL_AudioDevice *device) -{ - PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - - if (device->hidden->stream) { - if (device->hidden->recordingbuf) { - PULSEAUDIO_pa_stream_drop(device->hidden->stream); - } - PULSEAUDIO_pa_stream_disconnect(device->hidden->stream); - PULSEAUDIO_pa_stream_unref(device->hidden->stream); - } - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // in case the device thread is waiting somewhere, this will unblock it. - PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); - - SDL_free(device->hidden->mixbuf); - SDL_free(device->hidden); -} - -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 bool PULSEAUDIO_OpenDevice(SDL_AudioDevice *device) -{ - const bool recording = device->recording; - struct SDL_PrivateAudioData *h = NULL; - SDL_AudioFormat test_format; - const SDL_AudioFormat *closefmts; - pa_sample_spec paspec; - pa_buffer_attr paattr; - pa_channel_map pacmap; - pa_stream_flags_t flags = 0; - int format = PA_SAMPLE_INVALID; - bool result = true; - - SDL_assert(pulseaudio_threaded_mainloop != NULL); - SDL_assert(pulseaudio_context != NULL); - - // Initialize all variables that we clean on shutdown - h = device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } - - // Try for a closest match on audio format - closefmts = SDL_ClosestAudioFormats(device->spec.format); - while ((test_format = *(closefmts++)) != 0) { -#ifdef DEBUG_AUDIO - SDL_Log("pulseaudio: Trying format 0x%4.4x", test_format); -#endif - switch (test_format) { - case SDL_AUDIO_U8: - format = PA_SAMPLE_U8; - break; - case SDL_AUDIO_S16LE: - format = PA_SAMPLE_S16LE; - break; - case SDL_AUDIO_S16BE: - format = PA_SAMPLE_S16BE; - break; - case SDL_AUDIO_S32LE: - format = PA_SAMPLE_S32LE; - break; - case SDL_AUDIO_S32BE: - format = PA_SAMPLE_S32BE; - break; - case SDL_AUDIO_F32LE: - format = PA_SAMPLE_FLOAT32LE; - break; - case SDL_AUDIO_F32BE: - format = PA_SAMPLE_FLOAT32BE; - break; - default: - continue; - } - break; - } - if (!test_format) { - return SDL_SetError("pulseaudio: Unsupported audio format"); - } - device->spec.format = test_format; - paspec.format = format; - - // Calculate the final parameters for this audio specification - SDL_UpdatedAudioDeviceFormat(device); - - // Allocate mixing buffer - if (!recording) { - h->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size); - if (!h->mixbuf) { - return false; - } - SDL_memset(h->mixbuf, device->silence_value, device->buffer_size); - } - - paspec.channels = device->spec.channels; - paspec.rate = device->spec.freq; - - // Reduced prebuffering compared to the defaults. - paattr.fragsize = device->buffer_size; // despite the name, this is only used for recording devices, according to PulseAudio docs! - paattr.tlength = device->buffer_size; - paattr.prebuf = -1; - paattr.maxlength = -1; - paattr.minreq = -1; - flags |= PA_STREAM_ADJUST_LATENCY; - - PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - - 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, device->spec.channels, PA_CHANNEL_MAP_WAVEEX); - - h->stream = PULSEAUDIO_pa_stream_new( - pulseaudio_context, - (name && *name) ? name : "Audio Stream", // stream description - &paspec, // sample format spec - &pacmap // channel map - ); - - if (!h->stream) { - result = SDL_SetError("Could not set up PulseAudio stream"); - } else { - int rc; - - PULSEAUDIO_pa_stream_set_state_callback(h->stream, PulseStreamStateChangeCallback, NULL); - - // SDL manages device moves if the default changes, so don't ever let Pulse automatically migrate this stream. - flags |= PA_STREAM_DONT_MOVE; - - const char *device_path = ((PulseDeviceHandle *) device->handle)->device_path; - if (recording) { - PULSEAUDIO_pa_stream_set_read_callback(h->stream, ReadCallback, h); - rc = PULSEAUDIO_pa_stream_connect_record(h->stream, device_path, &paattr, flags); - } else { - PULSEAUDIO_pa_stream_set_write_callback(h->stream, WriteCallback, h); - rc = PULSEAUDIO_pa_stream_connect_playback(h->stream, device_path, &paattr, flags, NULL, NULL); - } - - if (rc < 0) { - result = SDL_SetError("Could not connect PulseAudio stream"); - } else { - int state = PULSEAUDIO_pa_stream_get_state(h->stream); - while (PA_STREAM_IS_GOOD(state) && (state != PA_STREAM_READY)) { - PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); - state = PULSEAUDIO_pa_stream_get_state(h->stream); - } - - if (!PA_STREAM_IS_GOOD(state)) { - result = SDL_SetError("Could not connect PulseAudio stream"); - } else { - const pa_buffer_attr *actual_bufattr = PULSEAUDIO_pa_stream_get_buffer_attr(h->stream); - if (!actual_bufattr) { - result = SDL_SetError("Could not determine connected PulseAudio stream's buffer attributes"); - } else { - device->buffer_size = (int) recording ? actual_bufattr->tlength : actual_bufattr->fragsize; - device->sample_frames = device->buffer_size / SDL_AUDIO_FRAMESIZE(device->spec); - } - } - } - } - - PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); - - // We're (hopefully) ready to rock and roll. :-) - return result; -} - -// device handles are device index + 1, cast to void*, so we never pass a NULL. - -static SDL_AudioFormat PulseFormatToSDLFormat(pa_sample_format_t format) -{ - switch (format) { - case PA_SAMPLE_U8: - return SDL_AUDIO_U8; - case PA_SAMPLE_S16LE: - return SDL_AUDIO_S16LE; - case PA_SAMPLE_S16BE: - return SDL_AUDIO_S16BE; - case PA_SAMPLE_S32LE: - return SDL_AUDIO_S32LE; - case PA_SAMPLE_S32BE: - return SDL_AUDIO_S32BE; - case PA_SAMPLE_FLOAT32LE: - return SDL_AUDIO_F32LE; - case PA_SAMPLE_FLOAT32BE: - return SDL_AUDIO_F32BE; - default: - return 0; - } -} - -static void AddPulseAudioDevice(const bool recording, const char *description, const char *name, const uint32_t index, const pa_sample_spec *sample_spec) -{ - SDL_AudioSpec spec; - SDL_zero(spec); - spec.format = PulseFormatToSDLFormat(sample_spec->format); - spec.channels = sample_spec->channels; - spec.freq = sample_spec->rate; - PulseDeviceHandle *handle = (PulseDeviceHandle *) SDL_malloc(sizeof (PulseDeviceHandle)); - if (handle) { - handle->device_path = SDL_strdup(name); - if (!handle->device_path) { - SDL_free(handle); - } else { - handle->device_index = index; - SDL_AddAudioDevice(recording, description, &spec, handle); - } - } -} - -// This is called when PulseAudio adds an playback ("sink") device. -static void SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data) -{ - if (i) { - AddPulseAudioDevice(false, i->description, i->name, i->index, &i->sample_spec); - } - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); -} - -// This is called when PulseAudio adds a recording ("source") device. -static void SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *data) -{ - // Maybe skip "monitor" sources. These are just output from other sinks. - if (i && (include_monitors || (i->monitor_of_sink == PA_INVALID_INDEX))) { - AddPulseAudioDevice(true, i->description, i->name, i->index, &i->sample_spec); - } - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); -} - -static void ServerInfoCallback(pa_context *c, const pa_server_info *i, void *data) -{ - //SDL_Log("PULSEAUDIO ServerInfoCallback!"); - - if (!default_sink_path || (SDL_strcmp(default_sink_path, i->default_sink_name) != 0)) { - char *str = SDL_strdup(i->default_sink_name); - if (str) { - SDL_free(default_sink_path); - default_sink_path = str; - default_sink_changed = true; - } - } - - if (!default_source_path || (SDL_strcmp(default_source_path, i->default_source_name) != 0)) { - char *str = SDL_strdup(i->default_source_name); - if (str) { - SDL_free(default_source_path); - default_source_path = str; - default_source_changed = true; - } - } - - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); -} - -static bool FindAudioDeviceByIndex(SDL_AudioDevice *device, void *userdata) -{ - const uint32_t idx = (uint32_t) (uintptr_t) userdata; - const PulseDeviceHandle *handle = (const PulseDeviceHandle *) device->handle; - return (handle->device_index == idx); -} - -static bool FindAudioDeviceByPath(SDL_AudioDevice *device, void *userdata) -{ - const char *path = (const char *) userdata; - const PulseDeviceHandle *handle = (const PulseDeviceHandle *) device->handle; - return (SDL_strcmp(handle->device_path, path) == 0); -} - -// This is called when PulseAudio has a device connected/removed/changed. -static void HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *data) -{ - const bool added = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW); - const bool removed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE); - const bool changed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE); - - if (added || removed || changed) { // we only care about add/remove events. - const bool sink = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK); - const bool source = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE); - - if (changed) { - PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_server_info(pulseaudio_context, ServerInfoCallback, NULL)); - } - - /* adds need sink details from the PulseAudio server. Another callback... - (just unref all these operations right away, because we aren't going to wait on them - and their callbacks will handle any work, so they can free as soon as that happens.) */ - if (added && sink) { - PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_sink_info_by_index(pulseaudio_context, idx, SinkInfoCallback, NULL)); - } else if (added && source) { - PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_source_info_by_index(pulseaudio_context, idx, SourceInfoCallback, NULL)); - } else if (removed && (sink || source)) { - // removes we can handle just with the device index. - SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByIndex, (void *)(uintptr_t)idx)); - } - } - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); -} - -static bool CheckDefaultDevice(const bool changed, char *device_path) -{ - if (!changed) { - return false; // nothing's happening, leave the flag marked as unchanged. - } else if (!device_path) { - return true; // check again later, we don't have a device name... - } - - SDL_AudioDevice *device = SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByPath, device_path); - if (device) { // if NULL, we might still be waiting for a SinkInfoCallback or something, we'll try later. - SDL_DefaultAudioDeviceChanged(device); - return false; // changing complete, set flag to unchanged for future tests. - } - return true; // couldn't find the changed device, leave it marked as changed to try again later. -} - -// this runs as a thread while the Pulse target is initialized to catch hotplug events. -static int SDLCALL HotplugThread(void *data) -{ - pa_operation *op; - - SDL_SetCurrentThreadPriority(SDL_THREAD_PRIORITY_LOW); - PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - PULSEAUDIO_pa_context_set_subscribe_callback(pulseaudio_context, HotplugCallback, NULL); - - // don't WaitForPulseOperation on the subscription; when it's done we'll be able to get hotplug events, but waiting doesn't changing anything. - op = PULSEAUDIO_pa_context_subscribe(pulseaudio_context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SERVER, NULL, NULL); - - SDL_SignalSemaphore((SDL_Semaphore *) data); - - while (SDL_GetAtomicInt(&pulseaudio_hotplug_thread_active)) { - PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); - if (op && PULSEAUDIO_pa_operation_get_state(op) != PA_OPERATION_RUNNING) { - PULSEAUDIO_pa_operation_unref(op); - op = NULL; - } - - // Update default devices; don't hold the pulse lock during this, since it could deadlock vs a playing device that we're about to lock here. - bool check_default_sink = default_sink_changed; - bool check_default_source = default_source_changed; - char *current_default_sink = check_default_sink ? SDL_strdup(default_sink_path) : NULL; - char *current_default_source = check_default_source ? SDL_strdup(default_source_path) : NULL; - default_sink_changed = default_source_changed = false; - PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); - check_default_sink = CheckDefaultDevice(check_default_sink, current_default_sink); - check_default_source = CheckDefaultDevice(check_default_source, current_default_source); - PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - - // free our copies (which will be NULL if nothing changed) - SDL_free(current_default_sink); - SDL_free(current_default_source); - - // set these to true if we didn't handle the change OR there was _another_ change while we were working unlocked. - default_sink_changed = (default_sink_changed || check_default_sink); - default_source_changed = (default_source_changed || check_default_source); - } - - if (op) { - PULSEAUDIO_pa_operation_unref(op); - } - - PULSEAUDIO_pa_context_set_subscribe_callback(pulseaudio_context, NULL, NULL); - PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); - return 0; -} - -static void PULSEAUDIO_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) -{ - SDL_Semaphore *ready_sem = SDL_CreateSemaphore(0); - - PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - WaitForPulseOperation(PULSEAUDIO_pa_context_get_server_info(pulseaudio_context, ServerInfoCallback, NULL)); - WaitForPulseOperation(PULSEAUDIO_pa_context_get_sink_info_list(pulseaudio_context, SinkInfoCallback, NULL)); - WaitForPulseOperation(PULSEAUDIO_pa_context_get_source_info_list(pulseaudio_context, SourceInfoCallback, NULL)); - PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); - - if (default_sink_path) { - *default_playback = SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByPath, default_sink_path); - } - - if (default_source_path) { - *default_recording = SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByPath, default_source_path); - } - - // ok, we have a sane list, let's set up hotplug notifications now... - SDL_SetAtomicInt(&pulseaudio_hotplug_thread_active, 1); - pulseaudio_hotplug_thread = SDL_CreateThread(HotplugThread, "PulseHotplug", ready_sem); - if (pulseaudio_hotplug_thread) { - SDL_WaitSemaphore(ready_sem); // wait until the thread hits it's main loop. - } else { - SDL_SetAtomicInt(&pulseaudio_hotplug_thread_active, 0); // thread failed to start, we'll go on without hotplug. - } - - SDL_DestroySemaphore(ready_sem); -} - -static void PULSEAUDIO_FreeDeviceHandle(SDL_AudioDevice *device) -{ - PulseDeviceHandle *handle = (PulseDeviceHandle *) device->handle; - SDL_free(handle->device_path); - SDL_free(handle); -} - -static void PULSEAUDIO_DeinitializeStart(void) -{ - if (pulseaudio_hotplug_thread) { - PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - SDL_SetAtomicInt(&pulseaudio_hotplug_thread_active, 0); - PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); - PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); - SDL_WaitThread(pulseaudio_hotplug_thread, NULL); - pulseaudio_hotplug_thread = NULL; - } -} - -static void PULSEAUDIO_Deinitialize(void) -{ - DisconnectFromPulseServer(); - - SDL_free(default_sink_path); - default_sink_path = NULL; - default_sink_changed = false; - SDL_free(default_source_path); - default_source_path = NULL; - default_source_changed = false; - - UnloadPulseAudioLibrary(); -} - -static bool PULSEAUDIO_Init(SDL_AudioDriverImpl *impl) -{ - if (!LoadPulseAudioLibrary()) { - return false; - } else if (!ConnectToPulseServer()) { - UnloadPulseAudioLibrary(); - return false; - } - - include_monitors = SDL_GetHintBoolean(SDL_HINT_AUDIO_INCLUDE_MONITORS, false); - - impl->DetectDevices = PULSEAUDIO_DetectDevices; - impl->OpenDevice = PULSEAUDIO_OpenDevice; - impl->PlayDevice = PULSEAUDIO_PlayDevice; - impl->WaitDevice = PULSEAUDIO_WaitDevice; - impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf; - impl->CloseDevice = PULSEAUDIO_CloseDevice; - impl->DeinitializeStart = PULSEAUDIO_DeinitializeStart; - impl->Deinitialize = PULSEAUDIO_Deinitialize; - impl->WaitRecordingDevice = PULSEAUDIO_WaitRecordingDevice; - impl->RecordDevice = PULSEAUDIO_RecordDevice; - impl->FlushRecording = PULSEAUDIO_FlushRecording; - impl->FreeDeviceHandle = PULSEAUDIO_FreeDeviceHandle; - - impl->HasRecordingSupport = true; - - return true; -} - -AudioBootStrap PULSEAUDIO_bootstrap = { - "pulseaudio", "PulseAudio", PULSEAUDIO_Init, false, false -}; - -#endif // SDL_AUDIO_DRIVER_PULSEAUDIO diff --git a/contrib/SDL-3.2.8/src/audio/pulseaudio/SDL_pulseaudio.h b/contrib/SDL-3.2.8/src/audio/pulseaudio/SDL_pulseaudio.h deleted file mode 100644 index 9ea44c0..0000000 --- a/contrib/SDL-3.2.8/src/audio/pulseaudio/SDL_pulseaudio.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_pulseaudio_h_ -#define SDL_pulseaudio_h_ - -#include - -#include "../SDL_sysaudio.h" - -struct SDL_PrivateAudioData -{ - // pulseaudio structures - pa_stream *stream; - - // Raw mixing buffer - Uint8 *mixbuf; - - int bytes_requested; // bytes of data the hardware wants _now_. - - const Uint8 *recordingbuf; - int recordinglen; -}; - -#endif // SDL_pulseaudio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/qnx/SDL_qsa_audio.c b/contrib/SDL-3.2.8/src/audio/qnx/SDL_qsa_audio.c deleted file mode 100644 index a31bea4..0000000 --- a/contrib/SDL-3.2.8/src/audio/qnx/SDL_qsa_audio.c +++ /dev/null @@ -1,451 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -// !!! FIXME: can this target support hotplugging? - -#include "../../SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_QNX - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "SDL3/SDL_timer.h" -#include "SDL3/SDL_audio.h" -#include "../../core/unix/SDL_poll.h" -#include "../SDL_sysaudio.h" -#include "SDL_qsa_audio.h" - -// default channel communication parameters -#define DEFAULT_CPARAMS_RATE 44100 -#define DEFAULT_CPARAMS_VOICES 1 - -#define DEFAULT_CPARAMS_FRAG_SIZE 4096 -#define DEFAULT_CPARAMS_FRAGS_MIN 1 -#define DEFAULT_CPARAMS_FRAGS_MAX 1 - -#define QSA_MAX_NAME_LENGTH 81+16 // Hardcoded in QSA, can't be changed - -static bool QSA_SetError(const char *fn, int status) -{ - return SDL_SetError("QSA: %s() failed: %s", fn, snd_strerror(status)); -} - -// !!! FIXME: does this need to be here? Does the SDL version not work? -static void QSA_ThreadInit(SDL_AudioDevice *device) -{ - // Increase default 10 priority to 25 to avoid jerky sound - struct sched_param param; - if (SchedGet(0, 0, ¶m) != -1) { - param.sched_priority = param.sched_curpriority + 15; - SchedSet(0, 0, SCHED_NOCHANGE, ¶m); - } -} - -// PCM channel parameters initialize function -static void QSA_InitAudioParams(snd_pcm_channel_params_t * cpars) -{ - SDL_zerop(cpars); - cpars->channel = SND_PCM_CHANNEL_PLAYBACK; - cpars->mode = SND_PCM_MODE_BLOCK; - cpars->start_mode = SND_PCM_START_DATA; - cpars->stop_mode = SND_PCM_STOP_STOP; - cpars->format.format = SND_PCM_SFMT_S16_LE; - cpars->format.interleave = 1; - cpars->format.rate = DEFAULT_CPARAMS_RATE; - cpars->format.voices = DEFAULT_CPARAMS_VOICES; - cpars->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE; - cpars->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN; - cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX; -} - -// This function waits until it is possible to write a full sound buffer -static bool QSA_WaitDevice(SDL_AudioDevice *device) -{ - // Setup timeout for playing one fragment equal to 2 seconds - // If timeout occurred than something wrong with hardware or driver - // For example, Vortex 8820 audio driver stucks on second DAC because - // it doesn't exist ! - const int result = SDL_IOReady(device->hidden->audio_fd, - device->recording ? SDL_IOR_READ : SDL_IOR_WRITE, - 2 * 1000); - switch (result) { - case -1: - SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "QSA: SDL_IOReady() failed: %s", strerror(errno)); - return false; - case 0: - device->hidden->timeout_on_wait = true; // !!! FIXME: Should we just disconnect the device in this case? - break; - default: - device->hidden->timeout_on_wait = false; - break; - } - - return true; -} - -static bool QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) -{ - if (SDL_GetAtomicInt(&device->shutdown) || !device->hidden) { - return true; - } - - int towrite = buflen; - - // Write the audio data, checking for EAGAIN (buffer full) and underrun - while ((towrite > 0) && !SDL_GetAtomicInt(&device->shutdown)); - const int bw = snd_pcm_plugin_write(device->hidden->audio_handle, buffer, towrite); - if (bw != towrite) { - // Check if samples playback got stuck somewhere in hardware or in the audio device driver - if ((errno == EAGAIN) && (bw == 0)) { - if (device->hidden->timeout_on_wait) { - return true; // oh well, try again next time. !!! FIXME: Should we just disconnect the device in this case? - } - } - - // Check for errors or conditions - if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { - SDL_Delay(1); // Let a little CPU time go by and try to write again - - // if we wrote some data - towrite -= bw; - buffer += bw * device->spec.channels; - continue; - } else if ((errno == EINVAL) || (errno == EIO)) { - snd_pcm_channel_status_t cstatus; - SDL_zero(cstatus); - cstatus.channel = device->recording ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK; - - int status = snd_pcm_plugin_status(device->hidden->audio_handle, &cstatus); - if (status < 0) { - QSA_SetError("snd_pcm_plugin_status", status); - return false; - } else if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY)) { - status = snd_pcm_plugin_prepare(device->hidden->audio_handle, device->recording ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK); - if (status < 0) { - QSA_SetError("snd_pcm_plugin_prepare", status); - return false; - } - } - continue; - } else { - return false; - } - } else { - // we wrote all remaining data - towrite -= bw; - buffer += bw * device->spec.channels; - } - } - - // If we couldn't write, assume fatal error for now - return (towrite == 0); -} - -static Uint8 *QSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - return device->hidden->pcm_buf; -} - -static void QSA_CloseDevice(SDL_AudioDevice *device) -{ - if (device->hidden) { - if (device->hidden->audio_handle) { - #if _NTO_VERSION < 710 - // Finish playing available samples or cancel unread samples during recording - snd_pcm_plugin_flush(device->hidden->audio_handle, device->recording ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK); - #endif - snd_pcm_close(device->hidden->audio_handle); - } - - SDL_free(device->hidden->pcm_buf); - SDL_free(device->hidden); - device->hidden = NULL; - } -} - -static bool QSA_OpenDevice(SDL_AudioDevice *device) -{ - if (device->recording) { - return SDL_SetError("SDL recording support isn't available on QNX atm"); // !!! FIXME: most of this code has support for recording devices, but there's no RecordDevice, etc functions. Fill them in! - } - - SDL_assert(device->handle != NULL); // NULL used to mean "system default device" in SDL2; it does not mean that in SDL3. - const Uint32 sdlhandle = (Uint32) ((size_t) device->handle); - const uint32_t cardno = (uint32_t) (sdlhandle & 0xFFFF); - const uint32_t deviceno = (uint32_t) ((sdlhandle >> 16) & 0xFFFF); - const bool recording = device->recording; - int status = 0; - - // Initialize all variables that we clean on shutdown - device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, (sizeof (struct SDL_PrivateAudioData))); - if (device->hidden == NULL) { - return false; - } - - // Initialize channel transfer parameters to default - snd_pcm_channel_params_t cparams; - QSA_InitAudioParams(&cparams); - - // Open requested audio device - status = snd_pcm_open(&device->hidden->audio_handle, cardno, deviceno, recording ? SND_PCM_OPEN_CAPTURE : SND_PCM_OPEN_PLAYBACK); - if (status < 0) { - device->hidden->audio_handle = NULL; - return QSA_SetError("snd_pcm_open", status); - } - - // Try for a closest match on audio format - SDL_AudioFormat test_format = 0; - const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); - while ((test_format = *(closefmts++)) != 0) { - // if match found set format to equivalent QSA format - switch (test_format) { - #define CHECKFMT(sdlfmt, qsafmt) case SDL_AUDIO_##sdlfmt: cparams.format.format = SND_PCM_SFMT_##qsafmt; break - CHECKFMT(U8, U8); - CHECKFMT(S8, S8); - CHECKFMT(S16LSB, S16_LE); - CHECKFMT(S16MSB, S16_BE); - CHECKFMT(S32LSB, S32_LE); - CHECKFMT(S32MSB, S32_BE); - CHECKFMT(F32LSB, FLOAT_LE); - CHECKFMT(F32MSB, FLOAT_BE); - #undef CHECKFMT - default: continue; - } - break; - } - - // assumes test_format not 0 on success - if (test_format == 0) { - return SDL_SetError("QSA: Couldn't find any hardware audio formats"); - } - - device->spec.format = test_format; - - // Set mono/stereo/4ch/6ch/8ch audio - cparams.format.voices = device->spec.channels; - - // Set rate - cparams.format.rate = device->spec.freq; - - // Setup the transfer parameters according to cparams - status = snd_pcm_plugin_params(device->hidden->audio_handle, &cparams); - if (status < 0) { - return QSA_SetError("snd_pcm_plugin_params", status); - } - - // Make sure channel is setup right one last time - snd_pcm_channel_setup_t csetup; - SDL_zero(csetup); - csetup.channel = recording ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK; - if (snd_pcm_plugin_setup(device->hidden->audio_handle, &csetup) < 0) { - return SDL_SetError("QSA: Unable to setup channel"); - } - - device->sample_frames = csetup.buf.block.frag_size; - - // Calculate the final parameters for this audio specification - SDL_UpdatedAudioDeviceFormat(device); - - device->hidden->pcm_buf = (Uint8 *) SDL_malloc(device->buffer_size); - if (device->hidden->pcm_buf == NULL) { - return false; - } - SDL_memset(device->hidden->pcm_buf, device->silence_value, device->buffer_size); - - // get the file descriptor - device->hidden->audio_fd = snd_pcm_file_descriptor(device->hidden->audio_handle, csetup.channel); - if (device->hidden->audio_fd < 0) { - return QSA_SetError("snd_pcm_file_descriptor", device->hidden->audio_fd); - } - - // Prepare an audio channel - status = snd_pcm_plugin_prepare(device->hidden->audio_handle, csetup.channel) - if (status < 0) { - return QSA_SetError("snd_pcm_plugin_prepare", status); - } - - return true; // We're really ready to rock and roll. :-) -} - -static SDL_AudioFormat QnxFormatToSDLFormat(const int32_t qnxfmt) -{ - switch (qnxfmt) { - #define CHECKFMT(sdlfmt, qsafmt) case SND_PCM_SFMT_##qsafmt: return SDL_AUDIO_##sdlfmt - CHECKFMT(U8, U8); - CHECKFMT(S8, S8); - CHECKFMT(S16LSB, S16_LE); - CHECKFMT(S16MSB, S16_BE); - CHECKFMT(S32LSB, S32_LE); - CHECKFMT(S32MSB, S32_BE); - CHECKFMT(F32LSB, FLOAT_LE); - CHECKFMT(F32MSB, FLOAT_BE); - #undef CHECKFMT - default: break; - } - return SDL_AUDIO_S16; // oh well. -} - -static void QSA_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) -{ - // Detect amount of available devices - // this value can be changed in the runtime - int num_cards = 0; - (void) snd_cards_list(NULL, 0, &alloc_num_cards); - bool isstack = false; - int *cards = SDL_small_alloc(int, num_cards, &isstack); - if (!cards) { - return; // we're in trouble. - } - int overflow_cards = 0; - const int total_num_cards = snd_cards_list(cards, num_cards, &overflow_cards); - // if overflow_cards > 0 or total_num_cards > num_cards, it changed at the last moment; oh well, we lost some. - num_cards = SDL_min(num_cards, total_num_cards); // ...but make sure it didn't _shrink_. - - // If io-audio manager is not running we will get 0 as number of available audio devices - if (num_cards == 0) { // not any available audio devices? - SDL_small_free(cards, isstack); - return; - } - - // Find requested devices by type - for (int it = 0; it < num_cards; it++) { - const int card = cards[it]; - for (uint32_t deviceno = 0; ; deviceno++) { - int32_t status; - char name[QSA_MAX_NAME_LENGTH]; - - status = snd_card_get_longname(card, name, sizeof (name)); - if (status == EOK) { - snd_pcm_t *handle; - - // Add device number to device name - char fullname[QSA_MAX_NAME_LENGTH + 32]; - SDL_snprintf(fullname, sizeof (fullname), "%s d%d", name, (int) deviceno); - - // Check if this device id could play anything - bool recording = false; - status = snd_pcm_open(&handle, card, deviceno, SND_PCM_OPEN_PLAYBACK); - if (status != EOK) { // no? See if it's a recording device instead. - #if 0 // !!! FIXME: most of this code has support for recording devices, but there's no RecordDevice, etc functions. Fill them in! - status = snd_pcm_open(&handle, card, deviceno, SND_PCM_OPEN_CAPTURE); - if (status == EOK) { - recording = true; - } - #endif - } - - if (status == EOK) { - SDL_AudioSpec spec; - SDL_zero(spec); - SDL_AudioSpec *pspec = &spec; - snd_pcm_channel_setup_t csetup; - SDL_zero(csetup); - csetup.channel = recording ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK; - - if (snd_pcm_plugin_setup(device->hidden->audio_handle, &csetup) < 0) { - pspec = NULL; // go on without spec info. - } else { - spec.format = QnxFormatToSDLFormat(csetup.format.format); - spec.channels = csetup.format.channels; - spec.freq = csetup.format.rate; - } - - status = snd_pcm_close(handle); - if (status == EOK) { - // !!! FIXME: I'm assuming each of these values are way less than 0xFFFF. Fix this if not. - SDL_assert(card <= 0xFFFF); - SDL_assert(deviceno <= 0xFFFF); - const Uint32 sdlhandle = ((Uint32) card) | (((Uint32) deviceno) << 16); - SDL_AddAudioDevice(recording, fullname, pspec, (void *) ((size_t) sdlhandle)); - } - } else { - // Check if we got end of devices list - if (status == -ENOENT) { - break; - } - } - } else { - break; - } - } - } - - SDL_small_free(cards, isstack); - - // Try to open the "preferred" devices, which will tell us the card/device pairs for the default devices. - snd_pcm_t handle; - int cardno, deviceno; - if (snd_pcm_open_preferred(&handle, &cardno, &deviceno, SND_PCM_OPEN_PLAYBACK) == 0) { - snd_pcm_close(handle); - // !!! FIXME: I'm assuming each of these values are way less than 0xFFFF. Fix this if not. - SDL_assert(cardno <= 0xFFFF); - SDL_assert(deviceno <= 0xFFFF); - const Uint32 sdlhandle = ((Uint32) card) | (((Uint32) deviceno) << 16); - *default_playback = SDL_FindPhysicalAudioDeviceByHandle((void *) ((size_t) sdlhandle)); - } - - if (snd_pcm_open_preferred(&handle, &cardno, &deviceno, SND_PCM_OPEN_CAPTURE) == 0) { - snd_pcm_close(handle); - // !!! FIXME: I'm assuming each of these values are way less than 0xFFFF. Fix this if not. - SDL_assert(cardno <= 0xFFFF); - SDL_assert(deviceno <= 0xFFFF); - const Uint32 sdlhandle = ((Uint32) card) | (((Uint32) deviceno) << 16); - *default_recording = SDL_FindPhysicalAudioDeviceByHandle((void *) ((size_t) sdlhandle)); - } -} - -static void QSA_Deinitialize(void) -{ - // nothing to do here atm. -} - -static bool QSA_Init(SDL_AudioDriverImpl * impl) -{ - impl->DetectDevices = QSA_DetectDevices; - impl->OpenDevice = QSA_OpenDevice; - impl->ThreadInit = QSA_ThreadInit; - impl->WaitDevice = QSA_WaitDevice; - impl->PlayDevice = QSA_PlayDevice; - impl->GetDeviceBuf = QSA_GetDeviceBuf; - impl->CloseDevice = QSA_CloseDevice; - impl->Deinitialize = QSA_Deinitialize; - - // !!! FIXME: most of this code has support for recording devices, but there's no RecordDevice, etc functions. Fill them in! - //impl->HasRecordingSupport = true; - - return true; -} - -AudioBootStrap QSAAUDIO_bootstrap = { - "qsa", "QNX QSA Audio", QSA_Init, false, false -}; - -#endif // SDL_AUDIO_DRIVER_QNX - diff --git a/contrib/SDL-3.2.8/src/audio/qnx/SDL_qsa_audio.h b/contrib/SDL-3.2.8/src/audio/qnx/SDL_qsa_audio.h deleted file mode 100644 index 902752c..0000000 --- a/contrib/SDL-3.2.8/src/audio/qnx/SDL_qsa_audio.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include "../../SDL_internal.h" - -#ifndef __SDL_QSA_AUDIO_H__ -#define __SDL_QSA_AUDIO_H__ - -#include - -#include "../SDL_sysaudio.h" - -struct SDL_PrivateAudioData -{ - snd_pcm_t *audio_handle; // The audio device handle - int audio_fd; // The audio file descriptor, for selecting on - bool timeout_on_wait; // Select timeout status - Uint8 *pcm_buf; // Raw mixing buffer -}; - -#endif // __SDL_QSA_AUDIO_H__ - diff --git a/contrib/SDL-3.2.8/src/audio/sndio/SDL_sndioaudio.c b/contrib/SDL-3.2.8/src/audio/sndio/SDL_sndioaudio.c deleted file mode 100644 index a0d2020..0000000 --- a/contrib/SDL-3.2.8/src/audio/sndio/SDL_sndioaudio.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_SNDIO - -// OpenBSD sndio target - -#ifdef HAVE_STDIO_H -#include -#endif - -#ifdef HAVE_SIGNAL_H -#include -#endif - -#include -#include - -#include "../SDL_sysaudio.h" -#include "SDL_sndioaudio.h" - -#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC -#endif - -#ifndef INFTIM -#define INFTIM -1 -#endif - -#ifndef SIO_DEVANY -#define SIO_DEVANY "default" -#endif - -static struct sio_hdl *(*SNDIO_sio_open)(const char *, unsigned int, int); -static void (*SNDIO_sio_close)(struct sio_hdl *); -static int (*SNDIO_sio_setpar)(struct sio_hdl *, struct sio_par *); -static int (*SNDIO_sio_getpar)(struct sio_hdl *, struct sio_par *); -static int (*SNDIO_sio_start)(struct sio_hdl *); -static int (*SNDIO_sio_stop)(struct sio_hdl *); -static size_t (*SNDIO_sio_read)(struct sio_hdl *, void *, size_t); -static size_t (*SNDIO_sio_write)(struct sio_hdl *, const void *, size_t); -static int (*SNDIO_sio_nfds)(struct sio_hdl *); -static int (*SNDIO_sio_pollfd)(struct sio_hdl *, struct pollfd *, int); -static int (*SNDIO_sio_revents)(struct sio_hdl *, struct pollfd *); -static int (*SNDIO_sio_eof)(struct sio_hdl *); -static void (*SNDIO_sio_initpar)(struct sio_par *); - -#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC -static const char *sndio_library = SDL_AUDIO_DRIVER_SNDIO_DYNAMIC; -static SDL_SharedObject *sndio_handle = NULL; - -static bool load_sndio_sym(const char *fn, void **addr) -{ - *addr = SDL_LoadFunction(sndio_handle, fn); - if (!*addr) { - return false; // Don't call SDL_SetError(): SDL_LoadFunction already did. - } - - return true; -} - -// cast funcs to char* first, to please GCC's strict aliasing rules. -#define SDL_SNDIO_SYM(x) \ - if (!load_sndio_sym(#x, (void **)(char *)&SNDIO_##x)) \ - return false -#else -#define SDL_SNDIO_SYM(x) SNDIO_##x = x -#endif - -static bool load_sndio_syms(void) -{ - SDL_SNDIO_SYM(sio_open); - SDL_SNDIO_SYM(sio_close); - SDL_SNDIO_SYM(sio_setpar); - SDL_SNDIO_SYM(sio_getpar); - SDL_SNDIO_SYM(sio_start); - SDL_SNDIO_SYM(sio_stop); - SDL_SNDIO_SYM(sio_read); - SDL_SNDIO_SYM(sio_write); - SDL_SNDIO_SYM(sio_nfds); - SDL_SNDIO_SYM(sio_pollfd); - SDL_SNDIO_SYM(sio_revents); - SDL_SNDIO_SYM(sio_eof); - SDL_SNDIO_SYM(sio_initpar); - return true; -} - -#undef SDL_SNDIO_SYM - -#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC - -static void UnloadSNDIOLibrary(void) -{ - if (sndio_handle) { - SDL_UnloadObject(sndio_handle); - sndio_handle = NULL; - } -} - -static bool LoadSNDIOLibrary(void) -{ - bool result = true; - if (!sndio_handle) { - sndio_handle = SDL_LoadObject(sndio_library); - if (!sndio_handle) { - result = false; // Don't call SDL_SetError(): SDL_LoadObject already did. - } else { - result = load_sndio_syms(); - if (!result) { - UnloadSNDIOLibrary(); - } - } - } - return result; -} - -#else - -static void UnloadSNDIOLibrary(void) -{ -} - -static bool LoadSNDIOLibrary(void) -{ - load_sndio_syms(); - return true; -} - -#endif // SDL_AUDIO_DRIVER_SNDIO_DYNAMIC - -static bool SNDIO_WaitDevice(SDL_AudioDevice *device) -{ - const bool recording = device->recording; - - while (!SDL_GetAtomicInt(&device->shutdown)) { - if (SNDIO_sio_eof(device->hidden->dev)) { - return false; - } - - const int nfds = SNDIO_sio_pollfd(device->hidden->dev, device->hidden->pfd, recording ? POLLIN : POLLOUT); - if (nfds <= 0 || poll(device->hidden->pfd, nfds, 10) < 0) { - return false; - } - - const int revents = SNDIO_sio_revents(device->hidden->dev, device->hidden->pfd); - if (recording && (revents & POLLIN)) { - break; - } else if (!recording && (revents & POLLOUT)) { - break; - } else if (revents & POLLHUP) { - return false; - } - } - - return true; -} - -static bool SNDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) -{ - // !!! FIXME: this should be non-blocking so we can check device->shutdown. - // this is set to blocking, because we _have_ to send the entire buffer down, but hopefully WaitDevice took most of the delay time. - if (SNDIO_sio_write(device->hidden->dev, buffer, buflen) != buflen) { - return false; // If we couldn't write, assume fatal error for now - } -#ifdef DEBUG_AUDIO - fprintf(stderr, "Wrote %d bytes of audio data\n", written); -#endif - return true; -} - -static int SNDIO_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - // We set recording devices non-blocking; this can safely return 0 in SDL3, but we'll check for EOF to cause a device disconnect. - const size_t br = SNDIO_sio_read(device->hidden->dev, buffer, buflen); - if ((br == 0) && SNDIO_sio_eof(device->hidden->dev)) { - return -1; - } - return (int) br; -} - -static void SNDIO_FlushRecording(SDL_AudioDevice *device) -{ - char buf[512]; - while (!SDL_GetAtomicInt(&device->shutdown) && (SNDIO_sio_read(device->hidden->dev, buf, sizeof(buf)) > 0)) { - // do nothing - } -} - -static Uint8 *SNDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - return device->hidden->mixbuf; -} - -static void SNDIO_CloseDevice(SDL_AudioDevice *device) -{ - if (device->hidden) { - if (device->hidden->dev) { - SNDIO_sio_stop(device->hidden->dev); - SNDIO_sio_close(device->hidden->dev); - } - SDL_free(device->hidden->pfd); - SDL_free(device->hidden->mixbuf); - SDL_free(device->hidden); - device->hidden = NULL; - } -} - -static bool SNDIO_OpenDevice(SDL_AudioDevice *device) -{ - device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } - - // Recording devices must be non-blocking for SNDIO_FlushRecording - device->hidden->dev = SNDIO_sio_open(SIO_DEVANY, - device->recording ? SIO_REC : SIO_PLAY, device->recording); - if (!device->hidden->dev) { - return SDL_SetError("sio_open() failed"); - } - - device->hidden->pfd = SDL_malloc(sizeof(struct pollfd) * SNDIO_sio_nfds(device->hidden->dev)); - if (!device->hidden->pfd) { - return false; - } - - struct sio_par par; - SNDIO_sio_initpar(&par); - - par.rate = device->spec.freq; - par.pchan = device->spec.channels; - par.round = device->sample_frames; - par.appbufsz = par.round * 2; - - // Try for a closest match on audio format - SDL_AudioFormat test_format; - const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); - while ((test_format = *(closefmts++)) != 0) { - if (!SDL_AUDIO_ISFLOAT(test_format)) { - par.le = SDL_AUDIO_ISLITTLEENDIAN(test_format) ? 1 : 0; - par.sig = SDL_AUDIO_ISSIGNED(test_format) ? 1 : 0; - par.bits = SDL_AUDIO_BITSIZE(test_format); - - if (SNDIO_sio_setpar(device->hidden->dev, &par) == 0) { - continue; - } - if (SNDIO_sio_getpar(device->hidden->dev, &par) == 0) { - return SDL_SetError("sio_getpar() failed"); - } - if (par.bps != SIO_BPS(par.bits)) { - continue; - } - if ((par.bits == 8 * par.bps) || (par.msb)) { - break; - } - } - } - - if (!test_format) { - return SDL_SetError("sndio: Unsupported audio format"); - } - - if ((par.bps == 4) && (par.sig) && (par.le)) { - device->spec.format = SDL_AUDIO_S32LE; - } else if ((par.bps == 4) && (par.sig) && (!par.le)) { - device->spec.format = SDL_AUDIO_S32BE; - } else if ((par.bps == 2) && (par.sig) && (par.le)) { - device->spec.format = SDL_AUDIO_S16LE; - } else if ((par.bps == 2) && (par.sig) && (!par.le)) { - device->spec.format = SDL_AUDIO_S16BE; - } else if ((par.bps == 1) && (par.sig)) { - device->spec.format = SDL_AUDIO_S8; - } else if ((par.bps == 1) && (!par.sig)) { - device->spec.format = SDL_AUDIO_U8; - } else { - return SDL_SetError("sndio: Got unsupported hardware audio format."); - } - - device->spec.freq = par.rate; - device->spec.channels = par.pchan; - device->sample_frames = par.round; - - // Calculate the final parameters for this audio specification - SDL_UpdatedAudioDeviceFormat(device); - - // Allocate mixing buffer - device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size); - if (!device->hidden->mixbuf) { - return false; - } - SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size); - - if (!SNDIO_sio_start(device->hidden->dev)) { - return SDL_SetError("sio_start() failed"); - } - - return true; // We're ready to rock and roll. :-) -} - -static void SNDIO_Deinitialize(void) -{ - UnloadSNDIOLibrary(); -} - -static void SNDIO_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) -{ - *default_playback = SDL_AddAudioDevice(false, DEFAULT_PLAYBACK_DEVNAME, NULL, (void *)0x1); - *default_recording = SDL_AddAudioDevice(true, DEFAULT_RECORDING_DEVNAME, NULL, (void *)0x2); -} - -static bool SNDIO_Init(SDL_AudioDriverImpl *impl) -{ - if (!LoadSNDIOLibrary()) { - return false; - } - - impl->OpenDevice = SNDIO_OpenDevice; - impl->WaitDevice = SNDIO_WaitDevice; - impl->PlayDevice = SNDIO_PlayDevice; - impl->GetDeviceBuf = SNDIO_GetDeviceBuf; - impl->CloseDevice = SNDIO_CloseDevice; - impl->WaitRecordingDevice = SNDIO_WaitDevice; - impl->RecordDevice = SNDIO_RecordDevice; - impl->FlushRecording = SNDIO_FlushRecording; - impl->Deinitialize = SNDIO_Deinitialize; - impl->DetectDevices = SNDIO_DetectDevices; - - impl->HasRecordingSupport = true; - - return true; -} - -AudioBootStrap SNDIO_bootstrap = { - "sndio", "OpenBSD sndio", SNDIO_Init, false, false -}; - -#endif // SDL_AUDIO_DRIVER_SNDIO diff --git a/contrib/SDL-3.2.8/src/audio/sndio/SDL_sndioaudio.h b/contrib/SDL-3.2.8/src/audio/sndio/SDL_sndioaudio.h deleted file mode 100644 index d4ff725..0000000 --- a/contrib/SDL-3.2.8/src/audio/sndio/SDL_sndioaudio.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_sndioaudio_h_ -#define SDL_sndioaudio_h_ - -#include -#include - -#include "../SDL_sysaudio.h" - -struct SDL_PrivateAudioData -{ - struct sio_hdl *dev; // The audio device handle - Uint8 *mixbuf; // Raw mixing buffer - struct pollfd *pfd; // Polling structures for non-blocking sndio devices -}; - -#endif // SDL_sndioaudio_h_ diff --git a/contrib/SDL-3.2.8/src/audio/vita/SDL_vitaaudio.c b/contrib/SDL-3.2.8/src/audio/vita/SDL_vitaaudio.c deleted file mode 100644 index e194f21..0000000 --- a/contrib/SDL-3.2.8/src/audio/vita/SDL_vitaaudio.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_VITA - -#include -#include -#include - -#include "../SDL_audiodev_c.h" -#include "../SDL_sysaudio.h" -#include "SDL_vitaaudio.h" - -#include -#include -#include - -#define SCE_AUDIO_SAMPLE_ALIGN(s) (((s) + 63) & ~63) -#define SCE_AUDIO_MAX_VOLUME 0x8000 - -static bool VITAAUD_OpenRecordingDevice(SDL_AudioDevice *device) -{ - device->spec.freq = 16000; - device->spec.channels = 1; - device->sample_frames = 512; - - SDL_UpdatedAudioDeviceFormat(device); - - device->hidden->port = sceAudioInOpenPort(SCE_AUDIO_IN_PORT_TYPE_VOICE, 512, 16000, SCE_AUDIO_IN_PARAM_FORMAT_S16_MONO); - - if (device->hidden->port < 0) { - return SDL_SetError("Couldn't open audio in port: %x", device->hidden->port); - } - - return true; -} - -static bool VITAAUD_OpenDevice(SDL_AudioDevice *device) -{ - int format, mixlen, i, port = SCE_AUDIO_OUT_PORT_TYPE_MAIN; - int vols[2] = { SCE_AUDIO_MAX_VOLUME, SCE_AUDIO_MAX_VOLUME }; - SDL_AudioFormat test_format; - const SDL_AudioFormat *closefmts; - - device->hidden = (struct SDL_PrivateAudioData *) - SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } - - closefmts = SDL_ClosestAudioFormats(device->spec.format); - while ((test_format = *(closefmts++)) != 0) { - if (test_format == SDL_AUDIO_S16LE) { - device->spec.format = test_format; - break; - } - } - - if (!test_format) { - return SDL_SetError("Unsupported audio format"); - } - - if (device->recording) { - return VITAAUD_OpenRecordingDevice(device); - } - - // The sample count must be a multiple of 64. - device->sample_frames = SCE_AUDIO_SAMPLE_ALIGN(device->sample_frames); - - // Update the fragment size as size in bytes. - SDL_UpdatedAudioDeviceFormat(device); - - /* Allocate the mixing buffer. Its size and starting address must - be a multiple of 64 bytes. Our sample count is already a multiple of - 64, so spec->size should be a multiple of 64 as well. */ - mixlen = device->buffer_size * NUM_BUFFERS; - device->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen); - if (!device->hidden->rawbuf) { - return SDL_SetError("Couldn't allocate mixing buffer"); - } - - // Setup the hardware channel. - if (device->spec.channels == 1) { - format = SCE_AUDIO_OUT_MODE_MONO; - } else { - format = SCE_AUDIO_OUT_MODE_STEREO; - } - - // the main port requires 48000Hz audio, so this drops to the background music port if necessary - if (device->spec.freq < 48000) { - port = SCE_AUDIO_OUT_PORT_TYPE_BGM; - } - - device->hidden->port = sceAudioOutOpenPort(port, device->sample_frames, device->spec.freq, format); - if (device->hidden->port < 0) { - SDL_aligned_free(device->hidden->rawbuf); - device->hidden->rawbuf = NULL; - return SDL_SetError("Couldn't open audio out port: %x", device->hidden->port); - } - - sceAudioOutSetVolume(device->hidden->port, SCE_AUDIO_VOLUME_FLAG_L_CH | SCE_AUDIO_VOLUME_FLAG_R_CH, vols); - - SDL_memset(device->hidden->rawbuf, 0, mixlen); - for (i = 0; i < NUM_BUFFERS; i++) { - device->hidden->mixbufs[i] = &device->hidden->rawbuf[i * device->buffer_size]; - } - - device->hidden->next_buffer = 0; - return true; -} - -static bool VITAAUD_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) -{ - return (sceAudioOutOutput(device->hidden->port, buffer) == 0); -} - -// This function waits until it is possible to write a full sound buffer -static bool VITAAUD_WaitDevice(SDL_AudioDevice *device) -{ - // !!! FIXME: we might just need to sleep roughly as long as playback buffers take to process, based on sample rate, etc. - while (!SDL_GetAtomicInt(&device->shutdown) && (sceAudioOutGetRestSample(device->hidden->port) >= device->buffer_size)) { - SDL_Delay(1); - } - return true; -} - -static Uint8 *VITAAUD_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - Uint8 *result = device->hidden->mixbufs[device->hidden->next_buffer]; - device->hidden->next_buffer = (device->hidden->next_buffer + 1) % NUM_BUFFERS; - return result; -} - -static void VITAAUD_CloseDevice(SDL_AudioDevice *device) -{ - if (device->hidden) { - if (device->hidden->port >= 0) { - if (device->recording) { - sceAudioInReleasePort(device->hidden->port); - } else { - sceAudioOutReleasePort(device->hidden->port); - } - device->hidden->port = -1; - } - - if (!device->recording && device->hidden->rawbuf) { - SDL_aligned_free(device->hidden->rawbuf); // this uses SDL_aligned_alloc(), not SDL_malloc() - device->hidden->rawbuf = NULL; - } - SDL_free(device->hidden); - device->hidden = NULL; - } -} - -static bool VITAAUD_WaitRecordingDevice(SDL_AudioDevice *device) -{ - // there's only a blocking call to obtain more data, so we'll just sleep as - // long as a buffer would run. - const Uint64 endticks = SDL_GetTicks() + ((device->sample_frames * 1000) / device->spec.freq); - while (!SDL_GetAtomicInt(&device->shutdown) && (SDL_GetTicks() < endticks)) { - SDL_Delay(1); - } - return true; -} - -static int VITAAUD_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - int ret; - SDL_assert(buflen == device->buffer_size); - ret = sceAudioInInput(device->hidden->port, buffer); - if (ret < 0) { - SDL_SetError("Failed to record from device: %x", ret); - return -1; - } - return device->buffer_size; -} - -static void VITAAUD_FlushRecording(SDL_AudioDevice *device) -{ - // just grab the latest and dump it. - sceAudioInInput(device->hidden->port, device->work_buffer); -} - -static void VITAAUD_ThreadInit(SDL_AudioDevice *device) -{ - // Increase the priority of this audio thread by 1 to put it ahead of other SDL threads. - SceUID thid; - SceKernelThreadInfo info; - thid = sceKernelGetThreadId(); - info.size = sizeof(SceKernelThreadInfo); - if (sceKernelGetThreadInfo(thid, &info) == 0) { - sceKernelChangeThreadPriority(thid, info.currentPriority - 1); - } -} - -static bool VITAAUD_Init(SDL_AudioDriverImpl *impl) -{ - impl->OpenDevice = VITAAUD_OpenDevice; - impl->PlayDevice = VITAAUD_PlayDevice; - impl->WaitDevice = VITAAUD_WaitDevice; - impl->GetDeviceBuf = VITAAUD_GetDeviceBuf; - impl->CloseDevice = VITAAUD_CloseDevice; - impl->ThreadInit = VITAAUD_ThreadInit; - impl->WaitRecordingDevice = VITAAUD_WaitRecordingDevice; - impl->FlushRecording = VITAAUD_FlushRecording; - impl->RecordDevice = VITAAUD_RecordDevice; - - impl->HasRecordingSupport = true; - impl->OnlyHasDefaultPlaybackDevice = true; - impl->OnlyHasDefaultRecordingDevice = true; - - return true; -} - -AudioBootStrap VITAAUD_bootstrap = { - "vita", "VITA audio driver", VITAAUD_Init, false, false -}; - -#endif // SDL_AUDIO_DRIVER_VITA diff --git a/contrib/SDL-3.2.8/src/audio/vita/SDL_vitaaudio.h b/contrib/SDL-3.2.8/src/audio/vita/SDL_vitaaudio.h deleted file mode 100644 index 1e97499..0000000 --- a/contrib/SDL-3.2.8/src/audio/vita/SDL_vitaaudio.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef SDL_vitaaudio_h -#define SDL_vitaaudio_h - -#include "../SDL_sysaudio.h" - -#define NUM_BUFFERS 2 - -struct SDL_PrivateAudioData -{ - // The hardware input/output port. - int port; - // The raw allocated mixing buffer. - Uint8 *rawbuf; - // Individual mixing buffers. - Uint8 *mixbufs[NUM_BUFFERS]; - // Index of the next available mixing buffer. - int next_buffer; -}; - -#endif // SDL_vitaaudio_h diff --git a/contrib/SDL-3.2.8/src/audio/wasapi/SDL_wasapi.c b/contrib/SDL-3.2.8/src/audio/wasapi/SDL_wasapi.c deleted file mode 100644 index db0974b..0000000 --- a/contrib/SDL-3.2.8/src/audio/wasapi/SDL_wasapi.c +++ /dev/null @@ -1,963 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifdef SDL_AUDIO_DRIVER_WASAPI - -#include "../../core/windows/SDL_windows.h" -#include "../../core/windows/SDL_immdevice.h" -#include "../../thread/SDL_systhread.h" -#include "../SDL_sysaudio.h" - -#define COBJMACROS -#include - -#include "SDL_wasapi.h" - -// These constants aren't available in older SDKs -#ifndef AUDCLNT_STREAMFLAGS_RATEADJUST -#define AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000 -#endif -#ifndef AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY -#define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 -#endif -#ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM -#define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 -#endif - -// handle to Avrt.dll--Vista and later!--for flagging the callback thread as "Pro Audio" (low latency). -static HMODULE libavrt = NULL; -typedef HANDLE(WINAPI *pfnAvSetMmThreadCharacteristicsW)(LPCWSTR, LPDWORD); -typedef BOOL(WINAPI *pfnAvRevertMmThreadCharacteristics)(HANDLE); -static pfnAvSetMmThreadCharacteristicsW pAvSetMmThreadCharacteristicsW = NULL; -static pfnAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL; - -// Some GUIDs we need to know without linking to libraries that aren't available before Vista. -static const IID SDL_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483, { 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } }; -static const IID SDL_IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0, { 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17 } }; -static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } }; -#ifdef __IAudioClient3_INTERFACE_DEFINED__ -static const IID SDL_IID_IAudioClient3 = { 0x7ed4ee07, 0x8e67, 0x4cd4, { 0x8c, 0x1a, 0x2b, 0x7a, 0x59, 0x87, 0xad, 0x42 } }; -#endif // - -static bool immdevice_initialized = false; - -// WASAPI is _really_ particular about various things happening on the same thread, for COM and such, -// so we proxy various stuff to a single background thread to manage. - -typedef struct ManagementThreadPendingTask -{ - ManagementThreadTask fn; - void *userdata; - bool result; - SDL_Semaphore *task_complete_sem; - char *errorstr; - struct ManagementThreadPendingTask *next; -} ManagementThreadPendingTask; - -static SDL_Thread *ManagementThread = NULL; -static ManagementThreadPendingTask *ManagementThreadPendingTasks = NULL; -static SDL_Mutex *ManagementThreadLock = NULL; -static SDL_Condition *ManagementThreadCondition = NULL; -static SDL_AtomicInt ManagementThreadShutdown; - -static void ManagementThreadMainloop(void) -{ - SDL_LockMutex(ManagementThreadLock); - ManagementThreadPendingTask *task; - while (((task = (ManagementThreadPendingTask *)SDL_GetAtomicPointer((void **)&ManagementThreadPendingTasks)) != NULL) || !SDL_GetAtomicInt(&ManagementThreadShutdown)) { - if (!task) { - SDL_WaitCondition(ManagementThreadCondition, ManagementThreadLock); // block until there's something to do. - } else { - SDL_SetAtomicPointer((void **) &ManagementThreadPendingTasks, task->next); // take task off the pending list. - SDL_UnlockMutex(ManagementThreadLock); // let other things add to the list while we chew on this task. - task->result = task->fn(task->userdata); // run this task. - if (task->task_complete_sem) { // something waiting on result? - task->errorstr = SDL_strdup(SDL_GetError()); - SDL_SignalSemaphore(task->task_complete_sem); - } else { // nothing waiting, we're done, free it. - SDL_free(task); - } - SDL_LockMutex(ManagementThreadLock); // regrab the lock so we can get the next task; if nothing to do, we'll release the lock in SDL_WaitCondition. - } - } - SDL_UnlockMutex(ManagementThreadLock); // told to shut down and out of tasks, let go of the lock and return. -} - -bool WASAPI_ProxyToManagementThread(ManagementThreadTask task, void *userdata, bool *wait_on_result) -{ - // We want to block for a result, but we are already running from the management thread! Just run the task now so we don't deadlock. - if ((wait_on_result) && (SDL_GetCurrentThreadID() == SDL_GetThreadID(ManagementThread))) { - *wait_on_result = task(userdata); - return true; // completed! - } - - if (SDL_GetAtomicInt(&ManagementThreadShutdown)) { - return SDL_SetError("Can't add task, we're shutting down"); - } - - ManagementThreadPendingTask *pending = (ManagementThreadPendingTask *)SDL_calloc(1, sizeof(ManagementThreadPendingTask)); - if (!pending) { - return false; - } - - pending->fn = task; - pending->userdata = userdata; - - if (wait_on_result) { - pending->task_complete_sem = SDL_CreateSemaphore(0); - if (!pending->task_complete_sem) { - SDL_free(pending); - return false; - } - } - - pending->next = NULL; - - SDL_LockMutex(ManagementThreadLock); - - // add to end of task list. - ManagementThreadPendingTask *prev = NULL; - for (ManagementThreadPendingTask *i = (ManagementThreadPendingTask *)SDL_GetAtomicPointer((void **)&ManagementThreadPendingTasks); i; i = i->next) { - prev = i; - } - - if (prev) { - prev->next = pending; - } else { - SDL_SetAtomicPointer((void **) &ManagementThreadPendingTasks, pending); - } - - // task is added to the end of the pending list, let management thread rip! - SDL_SignalCondition(ManagementThreadCondition); - SDL_UnlockMutex(ManagementThreadLock); - - if (wait_on_result) { - SDL_WaitSemaphore(pending->task_complete_sem); - SDL_DestroySemaphore(pending->task_complete_sem); - *wait_on_result = pending->result; - if (pending->errorstr) { - SDL_SetError("%s", pending->errorstr); - SDL_free(pending->errorstr); - } - SDL_free(pending); - } - - return true; // successfully added (and possibly executed)! -} - -static bool mgmtthrtask_AudioDeviceDisconnected(void *userdata) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; - SDL_AudioDeviceDisconnected(device); - UnrefPhysicalAudioDevice(device); // make sure this lived until the task completes. - return true; -} - -static void AudioDeviceDisconnected(SDL_AudioDevice *device) -{ - // don't wait on this, IMMDevice's own thread needs to return or everything will deadlock. - if (device) { - RefPhysicalAudioDevice(device); // make sure this lives until the task completes. - WASAPI_ProxyToManagementThread(mgmtthrtask_AudioDeviceDisconnected, device, NULL); - } -} - -static bool mgmtthrtask_DefaultAudioDeviceChanged(void *userdata) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; - SDL_DefaultAudioDeviceChanged(device); - UnrefPhysicalAudioDevice(device); // make sure this lived until the task completes. - return true; -} - -static void DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device) -{ - // don't wait on this, IMMDevice's own thread needs to return or everything will deadlock. - if (new_default_device) { - RefPhysicalAudioDevice(new_default_device); // make sure this lives until the task completes. - WASAPI_ProxyToManagementThread(mgmtthrtask_DefaultAudioDeviceChanged, new_default_device, NULL); - } -} - -static void StopWasapiHotplug(void) -{ - if (immdevice_initialized) { - SDL_IMMDevice_Quit(); - immdevice_initialized = false; - } -} - -static void Deinit(void) -{ - if (libavrt) { - FreeLibrary(libavrt); - libavrt = NULL; - } - - pAvSetMmThreadCharacteristicsW = NULL; - pAvRevertMmThreadCharacteristics = NULL; - - StopWasapiHotplug(); - - WIN_CoUninitialize(); -} - -static bool ManagementThreadPrepare(void) -{ - const SDL_IMMDevice_callbacks callbacks = { AudioDeviceDisconnected, DefaultAudioDeviceChanged }; - if (FAILED(WIN_CoInitialize())) { - return SDL_SetError("CoInitialize() failed"); - } else if (!SDL_IMMDevice_Init(&callbacks)) { - return false; // Error string is set by SDL_IMMDevice_Init - } - - immdevice_initialized = true; - - libavrt = LoadLibrary(TEXT("avrt.dll")); // this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now! - if (libavrt) { - pAvSetMmThreadCharacteristicsW = (pfnAvSetMmThreadCharacteristicsW)GetProcAddress(libavrt, "AvSetMmThreadCharacteristicsW"); - pAvRevertMmThreadCharacteristics = (pfnAvRevertMmThreadCharacteristics)GetProcAddress(libavrt, "AvRevertMmThreadCharacteristics"); - } - - ManagementThreadLock = SDL_CreateMutex(); - if (!ManagementThreadLock) { - Deinit(); - return false; - } - - ManagementThreadCondition = SDL_CreateCondition(); - if (!ManagementThreadCondition) { - SDL_DestroyMutex(ManagementThreadLock); - ManagementThreadLock = NULL; - Deinit(); - return false; - } - - return true; -} - -typedef struct -{ - char *errorstr; - SDL_Semaphore *ready_sem; -} ManagementThreadEntryData; - -static int ManagementThreadEntry(void *userdata) -{ - ManagementThreadEntryData *data = (ManagementThreadEntryData *)userdata; - - if (!ManagementThreadPrepare()) { - data->errorstr = SDL_strdup(SDL_GetError()); - SDL_SignalSemaphore(data->ready_sem); // unblock calling thread. - return 0; - } - - SDL_SignalSemaphore(data->ready_sem); // unblock calling thread. - ManagementThreadMainloop(); - - Deinit(); - return 0; -} - -static bool InitManagementThread(void) -{ - ManagementThreadEntryData mgmtdata; - SDL_zero(mgmtdata); - mgmtdata.ready_sem = SDL_CreateSemaphore(0); - if (!mgmtdata.ready_sem) { - return false; - } - - SDL_SetAtomicPointer((void **) &ManagementThreadPendingTasks, NULL); - SDL_SetAtomicInt(&ManagementThreadShutdown, 0); - ManagementThread = SDL_CreateThreadWithStackSize(ManagementThreadEntry, "SDLWASAPIMgmt", 256 * 1024, &mgmtdata); // !!! FIXME: maybe even smaller stack size? - if (!ManagementThread) { - return false; - } - - SDL_WaitSemaphore(mgmtdata.ready_sem); - SDL_DestroySemaphore(mgmtdata.ready_sem); - - if (mgmtdata.errorstr) { - SDL_WaitThread(ManagementThread, NULL); - ManagementThread = NULL; - SDL_SetError("%s", mgmtdata.errorstr); - SDL_free(mgmtdata.errorstr); - return false; - } - - return true; -} - -static void DeinitManagementThread(void) -{ - if (ManagementThread) { - SDL_SetAtomicInt(&ManagementThreadShutdown, 1); - SDL_LockMutex(ManagementThreadLock); - SDL_SignalCondition(ManagementThreadCondition); - SDL_UnlockMutex(ManagementThreadLock); - SDL_WaitThread(ManagementThread, NULL); - ManagementThread = NULL; - } - - SDL_assert(SDL_GetAtomicPointer((void **) &ManagementThreadPendingTasks) == NULL); - - SDL_DestroyCondition(ManagementThreadCondition); - SDL_DestroyMutex(ManagementThreadLock); - ManagementThreadCondition = NULL; - ManagementThreadLock = NULL; - SDL_SetAtomicInt(&ManagementThreadShutdown, 0); -} - -typedef struct -{ - SDL_AudioDevice **default_playback; - SDL_AudioDevice **default_recording; -} mgmtthrtask_DetectDevicesData; - -static bool mgmtthrtask_DetectDevices(void *userdata) -{ - mgmtthrtask_DetectDevicesData *data = (mgmtthrtask_DetectDevicesData *)userdata; - SDL_IMMDevice_EnumerateEndpoints(data->default_playback, data->default_recording); - return true; -} - -static void WASAPI_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) -{ - bool rc; - // this blocks because it needs to finish before the audio subsystem inits - mgmtthrtask_DetectDevicesData data; - data.default_playback = default_playback; - data.default_recording = default_recording; - WASAPI_ProxyToManagementThread(mgmtthrtask_DetectDevices, &data, &rc); -} - -static bool mgmtthrtask_DisconnectDevice(void *userdata) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; - SDL_AudioDeviceDisconnected(device); - UnrefPhysicalAudioDevice(device); - return true; -} - -void WASAPI_DisconnectDevice(SDL_AudioDevice *device) -{ - if (SDL_CompareAndSwapAtomicInt(&device->hidden->device_disconnecting, 0, 1)) { - RefPhysicalAudioDevice(device); // will unref when the task ends. - WASAPI_ProxyToManagementThread(mgmtthrtask_DisconnectDevice, device, NULL); - } -} - -static bool WasapiFailed(SDL_AudioDevice *device, const HRESULT err) -{ - if (err == S_OK) { - return false; - } else if (err == AUDCLNT_E_DEVICE_INVALIDATED) { - device->hidden->device_lost = true; - } else { - device->hidden->device_dead = true; - } - - return true; -} - -static bool mgmtthrtask_StopAndReleaseClient(void *userdata) -{ - IAudioClient *client = (IAudioClient *) userdata; - IAudioClient_Stop(client); - IAudioClient_Release(client); - return true; -} - -static bool mgmtthrtask_ReleaseCaptureClient(void *userdata) -{ - IAudioCaptureClient_Release((IAudioCaptureClient *)userdata); - return true; -} - -static bool mgmtthrtask_ReleaseRenderClient(void *userdata) -{ - IAudioRenderClient_Release((IAudioRenderClient *)userdata); - return true; -} - -static bool mgmtthrtask_CoTaskMemFree(void *userdata) -{ - CoTaskMemFree(userdata); - return true; -} - -static bool mgmtthrtask_CloseHandle(void *userdata) -{ - CloseHandle((HANDLE) userdata); - return true; -} - -static void ResetWasapiDevice(SDL_AudioDevice *device) -{ - if (!device || !device->hidden) { - return; - } - - // just queue up all the tasks in the management thread and don't block. - // We don't care when any of these actually get free'd. - - if (device->hidden->client) { - IAudioClient *client = device->hidden->client; - device->hidden->client = NULL; - WASAPI_ProxyToManagementThread(mgmtthrtask_StopAndReleaseClient, client, NULL); - } - - if (device->hidden->render) { - IAudioRenderClient *render = device->hidden->render; - device->hidden->render = NULL; - WASAPI_ProxyToManagementThread(mgmtthrtask_ReleaseRenderClient, render, NULL); - } - - if (device->hidden->capture) { - IAudioCaptureClient *capture = device->hidden->capture; - device->hidden->capture = NULL; - WASAPI_ProxyToManagementThread(mgmtthrtask_ReleaseCaptureClient, capture, NULL); - } - - if (device->hidden->waveformat) { - void *ptr = device->hidden->waveformat; - device->hidden->waveformat = NULL; - WASAPI_ProxyToManagementThread(mgmtthrtask_CoTaskMemFree, ptr, NULL); - } - - if (device->hidden->event) { - HANDLE event = device->hidden->event; - device->hidden->event = NULL; - WASAPI_ProxyToManagementThread(mgmtthrtask_CloseHandle, (void *) event, NULL); - } -} - -static bool mgmtthrtask_ActivateDevice(void *userdata) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; - - IMMDevice *immdevice = NULL; - if (!SDL_IMMDevice_Get(device, &immdevice, device->recording)) { - device->hidden->client = NULL; - return false; // This is already set by SDL_IMMDevice_Get - } - - // this is _not_ async in standard win32, yay! - HRESULT ret = IMMDevice_Activate(immdevice, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&device->hidden->client); - IMMDevice_Release(immdevice); - - if (FAILED(ret)) { - SDL_assert(device->hidden->client == NULL); - return WIN_SetErrorFromHRESULT("WASAPI can't activate audio endpoint", ret); - } - - SDL_assert(device->hidden->client != NULL); - if (!WASAPI_PrepDevice(device)) { // not async, fire it right away. - return false; - } - - return true; // good to go. -} - -static bool ActivateWasapiDevice(SDL_AudioDevice *device) -{ - // this blocks because we're either being notified from a background thread or we're running during device open, - // both of which won't deadlock vs the device thread. - bool rc = false; - return (WASAPI_ProxyToManagementThread(mgmtthrtask_ActivateDevice, device, &rc) && rc); -} - -// do not call when holding the device lock! -static bool RecoverWasapiDevice(SDL_AudioDevice *device) -{ - ResetWasapiDevice(device); // dump the lost device's handles. - - // This handles a non-default device that simply had its format changed in the Windows Control Panel. - if (!ActivateWasapiDevice(device)) { - WASAPI_DisconnectDevice(device); - return false; - } - - device->hidden->device_lost = false; - - return true; // okay, carry on with new device details! -} - -// do not call when holding the device lock! -static bool RecoverWasapiIfLost(SDL_AudioDevice *device) -{ - if (SDL_GetAtomicInt(&device->shutdown)) { - return false; // closing, stop trying. - } else if (SDL_GetAtomicInt(&device->hidden->device_disconnecting)) { - return false; // failing via the WASAPI management thread, stop trying. - } else if (device->hidden->device_dead) { // had a fatal error elsewhere, clean up and quit - IAudioClient_Stop(device->hidden->client); - WASAPI_DisconnectDevice(device); - SDL_assert(SDL_GetAtomicInt(&device->shutdown)); // so we don't come back through here. - return false; // already failed. - } else if (SDL_GetAtomicInt(&device->zombie)) { - return false; // we're already dead, so just leave and let the Zombie implementations take over. - } else if (!device->hidden->client) { - return true; // still waiting for activation. - } - - return device->hidden->device_lost ? RecoverWasapiDevice(device) : true; -} - -static Uint8 *WASAPI_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) -{ - // get an endpoint buffer from WASAPI. - BYTE *buffer = NULL; - - if (device->hidden->render) { - const HRESULT ret = IAudioRenderClient_GetBuffer(device->hidden->render, device->sample_frames, &buffer); - if (ret == AUDCLNT_E_BUFFER_TOO_LARGE) { - SDL_assert(buffer == NULL); - *buffer_size = 0; // just go back to WaitDevice and try again after the hardware has consumed some more data. - } else if (WasapiFailed(device, ret)) { - SDL_assert(buffer == NULL); - if (device->hidden->device_lost) { // just use an available buffer, we won't be playing it anyhow. - *buffer_size = 0; // we'll recover during WaitDevice and try again. - } - } - } - - return (Uint8 *)buffer; -} - -static bool WASAPI_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) -{ - if (device->hidden->render && !SDL_GetAtomicInt(&device->hidden->device_disconnecting)) { // definitely activated? - // WasapiFailed() will mark the device for reacquisition or removal elsewhere. - WasapiFailed(device, IAudioRenderClient_ReleaseBuffer(device->hidden->render, device->sample_frames, 0)); - } - return true; -} - -static bool WASAPI_WaitDevice(SDL_AudioDevice *device) -{ - // WaitDevice does not hold the device lock, so check for recovery/disconnect details here. - while (RecoverWasapiIfLost(device) && device->hidden->client && device->hidden->event) { - if (device->recording) { - // Recording devices should return immediately if there is any data available - UINT32 padding = 0; - if (!WasapiFailed(device, IAudioClient_GetCurrentPadding(device->hidden->client, &padding))) { - //SDL_Log("WASAPI EVENT! padding=%u maxpadding=%u", (unsigned int)padding, (unsigned int)maxpadding); - if (padding > 0) { - break; - } - } - - switch (WaitForSingleObjectEx(device->hidden->event, 200, FALSE)) { - case WAIT_OBJECT_0: - case WAIT_TIMEOUT: - break; - - default: - //SDL_Log("WASAPI FAILED EVENT!"); - IAudioClient_Stop(device->hidden->client); - return false; - } - } else { - DWORD waitResult = WaitForSingleObjectEx(device->hidden->event, 200, FALSE); - if (waitResult == WAIT_OBJECT_0) { - UINT32 padding = 0; - if (!WasapiFailed(device, IAudioClient_GetCurrentPadding(device->hidden->client, &padding))) { - //SDL_Log("WASAPI EVENT! padding=%u maxpadding=%u", (unsigned int)padding, (unsigned int)maxpadding); - if (padding <= (UINT32)device->sample_frames) { - break; - } - } - } else if (waitResult != WAIT_TIMEOUT) { - //SDL_Log("WASAPI FAILED EVENT!");*/ - IAudioClient_Stop(device->hidden->client); - return false; - } - } - } - - return true; -} - -static int WASAPI_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) -{ - BYTE *ptr = NULL; - UINT32 frames = 0; - DWORD flags = 0; - - while (device->hidden->capture) { - const HRESULT ret = IAudioCaptureClient_GetBuffer(device->hidden->capture, &ptr, &frames, &flags, NULL, NULL); - if (ret == AUDCLNT_S_BUFFER_EMPTY) { - return 0; // in theory we should have waited until there was data, but oh well, we'll go back to waiting. Returning 0 is safe in SDL3. - } - - WasapiFailed(device, ret); // mark device lost/failed if necessary. - - if (ret == S_OK) { - const int total = ((int)frames) * device->hidden->framesize; - const int cpy = SDL_min(buflen, total); - const int leftover = total - cpy; - const bool silent = (flags & AUDCLNT_BUFFERFLAGS_SILENT) ? true : false; - - SDL_assert(leftover == 0); // according to MSDN, this isn't everything available, just one "packet" of data per-GetBuffer call. - - if (silent) { - SDL_memset(buffer, device->silence_value, cpy); - } else { - SDL_memcpy(buffer, ptr, cpy); - } - - WasapiFailed(device, IAudioCaptureClient_ReleaseBuffer(device->hidden->capture, frames)); - - return cpy; - } - } - - return -1; // unrecoverable error. -} - -static void WASAPI_FlushRecording(SDL_AudioDevice *device) -{ - BYTE *ptr = NULL; - UINT32 frames = 0; - DWORD flags = 0; - - // just read until we stop getting packets, throwing them away. - while (!SDL_GetAtomicInt(&device->shutdown) && device->hidden->capture) { - const HRESULT ret = IAudioCaptureClient_GetBuffer(device->hidden->capture, &ptr, &frames, &flags, NULL, NULL); - if (ret == AUDCLNT_S_BUFFER_EMPTY) { - break; // no more buffered data; we're done. - } else if (WasapiFailed(device, ret)) { - break; // failed for some other reason, abort. - } else if (WasapiFailed(device, IAudioCaptureClient_ReleaseBuffer(device->hidden->capture, frames))) { - break; // something broke. - } - } -} - -static void WASAPI_CloseDevice(SDL_AudioDevice *device) -{ - if (device->hidden) { - ResetWasapiDevice(device); - SDL_free(device->hidden->devid); - SDL_free(device->hidden); - device->hidden = NULL; - } -} - -static bool mgmtthrtask_PrepDevice(void *userdata) -{ - SDL_AudioDevice *device = (SDL_AudioDevice *)userdata; - - /* !!! FIXME: we could request an exclusive mode stream, which is lower latency; - !!! it will write into the kernel's audio buffer directly instead of - !!! shared memory that a user-mode mixer then writes to the kernel with - !!! everything else. Doing this means any other sound using this device will - !!! stop playing, including the user's MP3 player and system notification - !!! sounds. You'd probably need to release the device when the app isn't in - !!! the foreground, to be a good citizen of the system. It's doable, but it's - !!! more work and causes some annoyances, and I don't know what the latency - !!! wins actually look like. Maybe add a hint to force exclusive mode at - !!! some point. To be sure, defaulting to shared mode is the right thing to - !!! do in any case. */ - const AUDCLNT_SHAREMODE sharemode = AUDCLNT_SHAREMODE_SHARED; - - IAudioClient *client = device->hidden->client; - SDL_assert(client != NULL); - - device->hidden->event = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!device->hidden->event) { - return WIN_SetError("WASAPI can't create an event handle"); - } - - HRESULT ret; - - WAVEFORMATEX *waveformat = NULL; - ret = IAudioClient_GetMixFormat(client, &waveformat); - if (FAILED(ret)) { - return WIN_SetErrorFromHRESULT("WASAPI can't determine mix format", ret); - } - SDL_assert(waveformat != NULL); - device->hidden->waveformat = waveformat; - - SDL_AudioSpec newspec; - newspec.channels = (Uint8)waveformat->nChannels; - - // Make sure we have a valid format that we can convert to whatever WASAPI wants. - const SDL_AudioFormat wasapi_format = SDL_WaveFormatExToSDLFormat(waveformat); - - SDL_AudioFormat test_format; - const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); - while ((test_format = *(closefmts++)) != 0) { - if (test_format == wasapi_format) { - newspec.format = test_format; - break; - } - } - - if (!test_format) { - return SDL_SetError("%s: Unsupported audio format", "wasapi"); - } - - REFERENCE_TIME default_period = 0; - ret = IAudioClient_GetDevicePeriod(client, &default_period, NULL); - if (FAILED(ret)) { - return WIN_SetErrorFromHRESULT("WASAPI can't determine minimum device period", ret); - } - - DWORD streamflags = 0; - - /* we've gotten reports that WASAPI's resampler introduces distortions, but in the short term - it fixes some other WASAPI-specific quirks we haven't quite tracked down. - Refer to bug #6326 for the immediate concern. */ -#if 1 - // favor WASAPI's resampler over our own - if ((DWORD)device->spec.freq != waveformat->nSamplesPerSec) { - streamflags |= (AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY); - waveformat->nSamplesPerSec = device->spec.freq; - waveformat->nAvgBytesPerSec = waveformat->nSamplesPerSec * waveformat->nChannels * (waveformat->wBitsPerSample / 8); - } -#endif - - newspec.freq = waveformat->nSamplesPerSec; - - streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK; - - int new_sample_frames = 0; - bool iaudioclient3_initialized = false; - -#ifdef __IAudioClient3_INTERFACE_DEFINED__ - // Try querying IAudioClient3 if sharemode is AUDCLNT_SHAREMODE_SHARED - if (sharemode == AUDCLNT_SHAREMODE_SHARED) { - IAudioClient3 *client3 = NULL; - ret = IAudioClient_QueryInterface(client, &SDL_IID_IAudioClient3, (void**)&client3); - if (SUCCEEDED(ret)) { - UINT32 default_period_in_frames = 0; - UINT32 fundamental_period_in_frames = 0; - UINT32 min_period_in_frames = 0; - UINT32 max_period_in_frames = 0; - ret = IAudioClient3_GetSharedModeEnginePeriod(client3, waveformat, - &default_period_in_frames, &fundamental_period_in_frames, &min_period_in_frames, &max_period_in_frames); - if (SUCCEEDED(ret)) { - // IAudioClient3_InitializeSharedAudioStream only accepts the integral multiple of fundamental_period_in_frames - UINT32 period_in_frames = fundamental_period_in_frames * (UINT32)SDL_round((double)device->sample_frames / fundamental_period_in_frames); - period_in_frames = SDL_clamp(period_in_frames, min_period_in_frames, max_period_in_frames); - - ret = IAudioClient3_InitializeSharedAudioStream(client3, streamflags, period_in_frames, waveformat, NULL); - if (SUCCEEDED(ret)) { - new_sample_frames = (int)period_in_frames; - iaudioclient3_initialized = true; - } - } - - IAudioClient3_Release(client3); - } - } -#endif - - if (!iaudioclient3_initialized) - ret = IAudioClient_Initialize(client, sharemode, streamflags, 0, 0, waveformat, NULL); - - if (FAILED(ret)) { - return WIN_SetErrorFromHRESULT("WASAPI can't initialize audio client", ret); - } - - ret = IAudioClient_SetEventHandle(client, device->hidden->event); - if (FAILED(ret)) { - return WIN_SetErrorFromHRESULT("WASAPI can't set event handle", ret); - } - - UINT32 bufsize = 0; // this is in sample frames, not samples, not bytes. - ret = IAudioClient_GetBufferSize(client, &bufsize); - if (FAILED(ret)) { - return WIN_SetErrorFromHRESULT("WASAPI can't determine buffer size", ret); - } - - // Match the callback size to the period size to cut down on the number of - // interrupts waited for in each call to WaitDevice - if (new_sample_frames <= 0) { - const float period_millis = default_period / 10000.0f; - const float period_frames = period_millis * newspec.freq / 1000.0f; - new_sample_frames = (int) SDL_ceilf(period_frames); - } - - // regardless of what we calculated for the period size, clamp it to the expected hardware buffer size. - if (new_sample_frames > (int) bufsize) { - new_sample_frames = (int) bufsize; - } - - // Update the fragment size as size in bytes - if (!SDL_AudioDeviceFormatChangedAlreadyLocked(device, &newspec, new_sample_frames)) { - return false; - } - - device->hidden->framesize = SDL_AUDIO_FRAMESIZE(device->spec); - - if (device->recording) { - IAudioCaptureClient *capture = NULL; - ret = IAudioClient_GetService(client, &SDL_IID_IAudioCaptureClient, (void **)&capture); - if (FAILED(ret)) { - return WIN_SetErrorFromHRESULT("WASAPI can't get capture client service", ret); - } - - SDL_assert(capture != NULL); - device->hidden->capture = capture; - ret = IAudioClient_Start(client); - if (FAILED(ret)) { - return WIN_SetErrorFromHRESULT("WASAPI can't start capture", ret); - } - - WASAPI_FlushRecording(device); // MSDN says you should flush the recording endpoint right after startup. - } else { - IAudioRenderClient *render = NULL; - ret = IAudioClient_GetService(client, &SDL_IID_IAudioRenderClient, (void **)&render); - if (FAILED(ret)) { - return WIN_SetErrorFromHRESULT("WASAPI can't get render client service", ret); - } - - SDL_assert(render != NULL); - device->hidden->render = render; - ret = IAudioClient_Start(client); - if (FAILED(ret)) { - return WIN_SetErrorFromHRESULT("WASAPI can't start playback", ret); - } - } - - return true; // good to go. -} - -// This is called once a device is activated, possibly asynchronously. -bool WASAPI_PrepDevice(SDL_AudioDevice *device) -{ - bool rc = true; - return (WASAPI_ProxyToManagementThread(mgmtthrtask_PrepDevice, device, &rc) && rc); -} - -static bool WASAPI_OpenDevice(SDL_AudioDevice *device) -{ - // Initialize all variables that we clean on shutdown - device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden)); - if (!device->hidden) { - return false; - } else if (!ActivateWasapiDevice(device)) { - return false; // already set error. - } - - /* Ready, but possibly waiting for async device activation. - Until activation is successful, we will report silence from recording - devices and ignore data on playback devices. Upon activation, we'll make - sure any bound audio streams are adjusted for the final device format. */ - - return true; -} - -static void WASAPI_ThreadInit(SDL_AudioDevice *device) -{ - // this thread uses COM. - if (SUCCEEDED(WIN_CoInitialize())) { // can't report errors, hope it worked! - device->hidden->coinitialized = true; - } - - // Set this thread to very high "Pro Audio" priority. - if (pAvSetMmThreadCharacteristicsW) { - DWORD idx = 0; - device->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx); - } else { - SDL_SetCurrentThreadPriority(device->recording ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL); - } -} - -static void WASAPI_ThreadDeinit(SDL_AudioDevice *device) -{ - // Set this thread back to normal priority. - if (device->hidden->task && pAvRevertMmThreadCharacteristics) { - pAvRevertMmThreadCharacteristics(device->hidden->task); - device->hidden->task = NULL; - } - - if (device->hidden->coinitialized) { - WIN_CoUninitialize(); - device->hidden->coinitialized = false; - } -} - -static bool mgmtthrtask_FreeDeviceHandle(void *userdata) -{ - SDL_IMMDevice_FreeDeviceHandle((SDL_AudioDevice *) userdata); - return true; -} - -static void WASAPI_FreeDeviceHandle(SDL_AudioDevice *device) -{ - bool rc; - WASAPI_ProxyToManagementThread(mgmtthrtask_FreeDeviceHandle, device, &rc); -} - -static bool mgmtthrtask_DeinitializeStart(void *userdata) -{ - StopWasapiHotplug(); - return true; -} - -static void WASAPI_DeinitializeStart(void) -{ - bool rc; - WASAPI_ProxyToManagementThread(mgmtthrtask_DeinitializeStart, NULL, &rc); -} - -static void WASAPI_Deinitialize(void) -{ - DeinitManagementThread(); -} - -static bool WASAPI_Init(SDL_AudioDriverImpl *impl) -{ - if (!InitManagementThread()) { - return false; - } - - impl->DetectDevices = WASAPI_DetectDevices; - impl->ThreadInit = WASAPI_ThreadInit; - impl->ThreadDeinit = WASAPI_ThreadDeinit; - impl->OpenDevice = WASAPI_OpenDevice; - impl->PlayDevice = WASAPI_PlayDevice; - impl->WaitDevice = WASAPI_WaitDevice; - impl->GetDeviceBuf = WASAPI_GetDeviceBuf; - impl->WaitRecordingDevice = WASAPI_WaitDevice; - impl->RecordDevice = WASAPI_RecordDevice; - impl->FlushRecording = WASAPI_FlushRecording; - impl->CloseDevice = WASAPI_CloseDevice; - impl->DeinitializeStart = WASAPI_DeinitializeStart; - impl->Deinitialize = WASAPI_Deinitialize; - impl->FreeDeviceHandle = WASAPI_FreeDeviceHandle; - - impl->HasRecordingSupport = true; - - return true; -} - -AudioBootStrap WASAPI_bootstrap = { - "wasapi", "WASAPI", WASAPI_Init, false, false -}; - -#endif // SDL_AUDIO_DRIVER_WASAPI diff --git a/contrib/SDL-3.2.8/src/audio/wasapi/SDL_wasapi.h b/contrib/SDL-3.2.8/src/audio/wasapi/SDL_wasapi.h deleted file mode 100644 index 5e528dc..0000000 --- a/contrib/SDL-3.2.8/src/audio/wasapi/SDL_wasapi.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2025 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" - -#ifndef SDL_wasapi_h_ -#define SDL_wasapi_h_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "../SDL_sysaudio.h" - -struct SDL_PrivateAudioData -{ - WCHAR *devid; - WAVEFORMATEX *waveformat; - IAudioClient *client; - IAudioRenderClient *render; - IAudioCaptureClient *capture; - HANDLE event; - HANDLE task; - bool coinitialized; - int framesize; - SDL_AtomicInt device_disconnecting; - bool device_lost; - bool device_dead; -}; - -// win32 implementation calls into these. -bool WASAPI_PrepDevice(SDL_AudioDevice *device); -void WASAPI_DisconnectDevice(SDL_AudioDevice *device); // don't hold the device lock when calling this! - - -// BE CAREFUL: if you are holding the device lock and proxy to the management thread with wait_until_complete, and grab the lock again, you will deadlock. -typedef bool (*ManagementThreadTask)(void *userdata); -bool WASAPI_ProxyToManagementThread(ManagementThreadTask task, void *userdata, bool *wait_until_complete); - -#ifdef __cplusplus -} -#endif - -#endif // SDL_wasapi_h_ -- cgit v1.2.3