update sdl Merge commit '644725478f4de0f074a6834e8423ac36dce3974f'

This commit is contained in:
2023-09-23 18:53:11 +02:00
172 changed files with 7495 additions and 4062 deletions

View File

@ -116,6 +116,106 @@ const char *SDL_GetCurrentAudioDriver(void)
return current_audio.name;
}
static int GetDefaultSampleFramesFromFreq(const int freq)
{
const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES);
if (hint) {
const int val = SDL_atoi(hint);
if (val > 0) {
return val;
}
}
if (freq <= 22050) {
return 512;
} else if (freq <= 48000) {
return 1024;
} else if (freq <= 96000) {
return 2048;
} else {
return 4096;
}
}
void OnAudioStreamCreated(SDL_AudioStream *stream)
{
SDL_assert(SDL_GetCurrentAudioDriver() != NULL);
SDL_assert(stream != NULL);
// this isn't really part of the "device list" but it's a convenient lock to use here.
SDL_LockRWLockForWriting(current_audio.device_list_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_list_lock);
}
void OnAudioStreamDestroy(SDL_AudioStream *stream)
{
SDL_assert(SDL_GetCurrentAudioDriver() != NULL);
SDL_assert(stream != NULL);
// this isn't really part of the "device list" but it's a convenient lock to use here.
SDL_LockRWLockForWriting(current_audio.device_list_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_list_lock);
}
// should hold logdev's physical device's lock before calling.
static void UpdateAudioStreamFormatsLogical(SDL_LogicalAudioDevice *logdev)
{
const SDL_bool iscapture = logdev->physical_device->iscapture;
SDL_AudioSpec spec;
SDL_copyp(&spec, &logdev->physical_device->spec);
if (logdev->postmix != NULL) {
spec.format = SDL_AUDIO_F32;
}
for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; 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_LockMutex(stream->lock);
SDL_copyp(iscapture ? &stream->src_spec : &stream->dst_spec, &spec);
SDL_UnlockMutex(stream->lock);
}
}
// should hold device->lock before calling.
static void UpdateAudioStreamFormatsPhysical(SDL_AudioDevice *device)
{
for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) {
UpdateAudioStreamFormatsLogical(logdev);
}
}
// device should be locked when calling this.
static SDL_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
!SDL_AtomicGet(&device->logical_devices->paused) && // it isn't paused
device->logical_devices->bound_streams && // there's a bound stream
!device->logical_devices->bound_streams->next_binding // there's only _ONE_ bound stream.
) ? SDL_TRUE : SDL_FALSE;
}
// device management and hotplug...
@ -176,6 +276,8 @@ static void DestroyLogicalAudioDevice(SDL_LogicalAudioDevice *logdev)
SDL_UnlockMutex(stream->lock);
}
logdev->physical_device->simple_copy = AudioDeviceCanUseSimpleCopy(logdev->physical_device);
SDL_free(logdev);
}
@ -208,7 +310,10 @@ static SDL_AudioDevice *CreatePhysicalAudioDevice(const char *name, SDL_bool isc
{
SDL_assert(name != NULL);
if (SDL_AtomicGet(&current_audio.shutting_down)) {
SDL_LockRWLockForReading(current_audio.device_list_lock);
const int shutting_down = SDL_AtomicGet(&current_audio.shutting_down);
SDL_UnlockRWLock(current_audio.device_list_lock);
if (shutting_down) {
return NULL; // we're shutting down, don't add any devices that are hotplugged at the last possible moment.
}
@ -236,8 +341,9 @@ static SDL_AudioDevice *CreatePhysicalAudioDevice(const char *name, SDL_bool isc
SDL_AtomicSet(&device->condemned, 0);
SDL_AtomicSet(&device->zombie, 0);
device->iscapture = iscapture;
SDL_memcpy(&device->spec, spec, sizeof (SDL_AudioSpec));
SDL_memcpy(&device->default_spec, spec, sizeof (SDL_AudioSpec));
SDL_copyp(&device->spec, spec);
SDL_copyp(&device->default_spec, spec);
device->sample_frames = GetDefaultSampleFramesFromFreq(device->spec.freq);
device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format);
device->handle = handle;
device->prev = NULL;
@ -336,7 +442,7 @@ void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device)
SDL_LogicalAudioDevice *next = NULL;
for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = next) {
next = logdev->next;
if (!logdev->is_default) { // if opened as a default, leave it on the zombie device for later migration.
if (!logdev->opened_as_default) { // if opened as a default, leave it on the zombie device for later migration.
DisconnectLogicalAudioDevice(logdev);
}
}
@ -412,7 +518,7 @@ void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device)
static void SDL_AudioThreadDeinit_Default(SDL_AudioDevice *device) { /* no-op. */ }
static void SDL_AudioWaitDevice_Default(SDL_AudioDevice *device) { /* no-op. */ }
static void SDL_AudioPlayDevice_Default(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { /* no-op. */ }
static int SDL_AudioPlayDevice_Default(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size) { return 0; /* no-op. */ }
static void SDL_AudioWaitCaptureDevice_Default(SDL_AudioDevice *device) { /* no-op. */ }
static void SDL_AudioFlushCapture_Default(SDL_AudioDevice *device) { /* no-op. */ }
static void SDL_AudioCloseDevice_Default(SDL_AudioDevice *device) { /* no-op. */ }
@ -493,6 +599,7 @@ int SDL_InitAudio(const char *driver_name)
}
SDL_ChooseAudioConverters();
SDL_SetupAudioResampler();
SDL_RWLock *device_list_lock = SDL_CreateRWLock(); // create this early, so if it fails we don't have to tear down the whole audio subsystem.
if (!device_list_lock) {
@ -614,6 +721,11 @@ void SDL_QuitAudio(void)
return;
}
// Destroy any audio streams that still exist...
while (current_audio.existing_streams != NULL) {
SDL_DestroyAudioStream(current_audio.existing_streams);
}
// merge device lists so we don't have to duplicate work below.
SDL_LockRWLockForWriting(current_audio.device_list_lock);
SDL_AtomicSet(&current_audio.shutting_down, 1);
@ -633,7 +745,7 @@ void SDL_QuitAudio(void)
SDL_AtomicSet(&current_audio.output_device_count, 0);
SDL_AtomicSet(&current_audio.capture_device_count, 0);
SDL_UnlockRWLock(current_audio.device_list_lock);
// mark all devices for shutdown so all threads can begin to terminate.
for (SDL_AudioDevice *i = devices; i != NULL; i = i->next) {
SDL_AtomicSet(&i->shutdown, 1);
@ -676,6 +788,14 @@ void SDL_AudioThreadFinalize(SDL_AudioDevice *device)
SDL_AtomicSet(&device->thread_alive, 0);
}
static void MixFloat32Audio(float *dst, const float *src, const int buffer_size)
{
if (SDL_MixAudioFormat((Uint8 *) dst, (const Uint8 *) src, SDL_AUDIO_F32, buffer_size, SDL_MIX_MAXVOLUME) < 0) {
SDL_assert(!"This shouldn't happen.");
}
}
// Output device thread. This is split into chunks, so backends that need to control this directly can use the pieces they need without duplicating effort.
void SDL_OutputAudioThreadSetup(SDL_AudioDevice *device)
@ -697,41 +817,90 @@ SDL_bool SDL_OutputAudioThreadIterate(SDL_AudioDevice *device)
SDL_bool retval = SDL_TRUE;
int buffer_size = device->buffer_size;
Uint8 *mix_buffer = current_audio.impl.GetDeviceBuf(device, &buffer_size);
if (!mix_buffer) {
Uint8 *device_buffer = current_audio.impl.GetDeviceBuf(device, &buffer_size);
if (!device_buffer) {
retval = SDL_FALSE;
} else {
SDL_assert(buffer_size <= device->buffer_size); // you can ask for less, but not more.
SDL_memset(mix_buffer, device->silence_value, buffer_size); // start with silence.
SDL_assert(AudioDeviceCanUseSimpleCopy(device) == device->simple_copy); // make sure this hasn't gotten out of sync.
for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) {
if (SDL_AtomicGet(&logdev->paused)) {
continue; // paused? Skip this logical device.
// 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(AUDIO_SPECS_EQUAL(stream->dst_spec, device->spec));
const int br = SDL_GetAudioStreamData(stream, device_buffer, buffer_size);
if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow.
retval = SDL_FALSE;
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.
}
} 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;
for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) {
/* this will hold a lock on `stream` while getting. We don't explicitly lock the streams
for iterating here because the binding linked list can only change while the device lock is held.
(we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind
the same stream to different devices at the same time, though.) */
const int br = SDL_GetAudioStreamData(stream, device->work_buffer, buffer_size);
if (br < 0) {
// oh crud, we probably ran out of memory. This is possibly an overreaction to kill the audio device, but it's likely the whole thing is going down in a moment anyhow.
retval = SDL_FALSE;
break;
} else if (br > 0) { // it's okay if we get less than requested, we mix what we have.
// !!! FIXME: this needs to mix to float32 or int32, so we don't clip.
if (SDL_MixAudioFormat(mix_buffer, device->work_buffer, device->spec.format, br, SDL_MIX_MAXVOLUME) < 0) { // !!! FIXME: allow streams to specify gain?
SDL_assert(!"We probably ended up with some totally unexpected audio format here");
retval = SDL_FALSE; // uh...?
SDL_assert(work_buffer_size <= device->work_buffer_size);
outspec.format = SDL_AUDIO_F32;
outspec.channels = device->spec.channels;
outspec.freq = device->spec.freq;
SDL_memset(final_mix_buffer, '\0', work_buffer_size); // start with silence.
for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) {
if (SDL_AtomicGet(&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 != NULL; stream = stream->next_binding) {
// We should have updated this elsewhere if the format changed!
SDL_assert(AUDIO_SPECS_EQUAL(stream->dst_spec, outspec));
/* this will hold a lock on `stream` while getting. We don't explicitly lock the streams
for iterating here because the binding linked list can only change while the device lock is held.
(we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind
the same stream to different devices at the same time, though.) */
const int br = SDL_GetAudioStreamData(stream, device->work_buffer, work_buffer_size);
if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow.
retval = SDL_FALSE;
break;
} else if (br > 0) { // it's okay if we get less than requested, we mix what we have.
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, device_buffer, device->spec.format, device->spec.channels, device->work_buffer);
ConvertAudio(needed_samples / device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, device->work_buffer, device->spec.format, device->spec.channels, NULL);
SDL_memcpy(device_buffer, device->work_buffer, buffer_size);
}
}
// !!! FIXME: have PlayDevice return a value and do disconnects in here with it.
current_audio.impl.PlayDevice(device, mix_buffer, buffer_size); // this SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitDevice!
// PlayDevice SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitDevice instead!
if (current_audio.impl.PlayDevice(device, device_buffer, buffer_size) < 0) {
retval = SDL_FALSE;
}
}
SDL_UnlockMutex(device->lock);
@ -746,9 +915,9 @@ SDL_bool SDL_OutputAudioThreadIterate(SDL_AudioDevice *device)
void SDL_OutputAudioThreadShutdown(SDL_AudioDevice *device)
{
SDL_assert(!device->iscapture);
const int samples = (device->buffer_size / (SDL_AUDIO_BITSIZE(device->spec.format) / 8)) / device->spec.channels;
const int frames = device->buffer_size / SDL_AUDIO_FRAMESIZE(device->spec);
// Wait for the audio to drain. !!! FIXME: don't bother waiting if device is lost.
SDL_Delay(((samples * 1000) / device->spec.freq) * 2);
SDL_Delay(((frames * 1000) / device->spec.freq) * 2);
current_audio.impl.ThreadDeinit(device);
SDL_AudioThreadFinalize(device);
}
@ -791,21 +960,42 @@ SDL_bool SDL_CaptureAudioThreadIterate(SDL_AudioDevice *device)
current_audio.impl.FlushCapture(device); // nothing wants data, dump anything pending.
} else {
// this SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitCaptureDevice!
const int rc = current_audio.impl.CaptureFromDevice(device, device->work_buffer, device->buffer_size);
if (rc < 0) { // uhoh, device failed for some reason!
int br = current_audio.impl.CaptureFromDevice(device, device->work_buffer, device->buffer_size);
if (br < 0) { // uhoh, device failed for some reason!
retval = SDL_FALSE;
} else if (rc > 0) { // queue the new data to each bound stream.
} else if (br > 0) { // queue the new data to each bound stream.
for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) {
if (SDL_AtomicGet(&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 capture device, but we offer it for API consistency.
if (logdev->postmix) {
// move to float format.
SDL_AudioSpec outspec;
outspec.format = SDL_AUDIO_F32;
outspec.channels = device->spec.channels;
outspec.freq = device->spec.freq;
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, device->postmix_buffer, SDL_AUDIO_F32, outspec.channels, NULL);
logdev->postmix(logdev->postmix_userdata, &outspec, device->postmix_buffer, br);
}
for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) {
// We should have updated this elsewhere if the format changed!
SDL_assert(stream->src_spec.format == (logdev->postmix ? SDL_AUDIO_F32 : device->spec.format));
SDL_assert(stream->src_spec.channels == device->spec.channels);
SDL_assert(stream->src_spec.freq == device->spec.freq);
/* this will hold a lock on `stream` while putting. We don't explicitly lock the streams
for iterating here because the binding linked list can only change while the device lock is held.
(we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind
the same stream to different devices at the same time, though.) */
if (SDL_PutAudioStreamData(stream, device->work_buffer, rc) < 0) {
if (SDL_PutAudioStreamData(stream, output_buffer, br) < 0) {
// oh crud, we probably ran out of memory. This is possibly an overreaction to kill the audio device, but it's likely the whole thing is going down in a moment anyhow.
retval = SDL_FALSE;
break;
@ -1036,22 +1226,22 @@ char *SDL_GetAudioDeviceName(SDL_AudioDeviceID devid)
return retval;
}
int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec)
int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec, int *sample_frames)
{
if (!spec) {
return SDL_InvalidParamError("spec");
}
SDL_bool is_default = SDL_FALSE;
SDL_bool wants_default = SDL_FALSE;
if (devid == SDL_AUDIO_DEVICE_DEFAULT_OUTPUT) {
devid = current_audio.default_output_device_id;
is_default = SDL_TRUE;
wants_default = SDL_TRUE;
} else if (devid == SDL_AUDIO_DEVICE_DEFAULT_CAPTURE) {
devid = current_audio.default_capture_device_id;
is_default = SDL_TRUE;
wants_default = SDL_TRUE;
}
if ((devid == 0) && is_default) {
if ((devid == 0) && wants_default) {
return SDL_SetError("No default audio device available");
}
@ -1060,7 +1250,10 @@ int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec)
return -1;
}
SDL_memcpy(spec, &device->spec, sizeof (SDL_AudioSpec));
SDL_copyp(spec, &device->spec);
if (sample_frames) {
*sample_frames = device->sample_frames;
}
SDL_UnlockMutex(device->lock);
return 0;
@ -1080,18 +1273,22 @@ static void ClosePhysicalAudioDevice(SDL_AudioDevice *device)
SDL_AtomicSet(&device->thread_alive, 0);
}
if (device->is_opened) {
if (device->currently_opened) {
current_audio.impl.CloseDevice(device); // if ProvidesOwnCallbackThread, this must join on any existing device thread before returning!
device->is_opened = SDL_FALSE;
device->currently_opened = SDL_FALSE;
device->hidden = NULL; // just in case.
}
if (device->work_buffer) {
SDL_aligned_free(device->work_buffer);
device->work_buffer = NULL;
}
SDL_aligned_free(device->work_buffer);
device->work_buffer = NULL;
SDL_memcpy(&device->spec, &device->default_spec, sizeof (SDL_AudioSpec));
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);
SDL_AtomicSet(&device->shutdown, 0); // ready to go again.
@ -1121,16 +1318,14 @@ static SDL_AudioFormat ParseAudioFormatString(const char *string)
#define CHECK_FMT_STRING(x) if (SDL_strcmp(string, #x) == 0) { return SDL_AUDIO_##x; }
CHECK_FMT_STRING(U8);
CHECK_FMT_STRING(S8);
CHECK_FMT_STRING(S16LSB);
CHECK_FMT_STRING(S16MSB);
CHECK_FMT_STRING(S16LE);
CHECK_FMT_STRING(S16BE);
CHECK_FMT_STRING(S16);
CHECK_FMT_STRING(S32LSB);
CHECK_FMT_STRING(S32MSB);
CHECK_FMT_STRING(S32SYS);
CHECK_FMT_STRING(S32LE);
CHECK_FMT_STRING(S32BE);
CHECK_FMT_STRING(S32);
CHECK_FMT_STRING(F32LSB);
CHECK_FMT_STRING(F32MSB);
CHECK_FMT_STRING(F32SYS);
CHECK_FMT_STRING(F32LE);
CHECK_FMT_STRING(F32BE);
CHECK_FMT_STRING(F32);
#undef CHECK_FMT_STRING
}
@ -1168,15 +1363,12 @@ static void PrepareAudioFormat(SDL_bool iscapture, SDL_AudioSpec *spec)
}
}
static int GetDefaultSampleFramesFromFreq(int freq)
{
return SDL_powerof2((freq / 1000) * 46); // Pick the closest power-of-two to ~46 ms at desired frequency
}
void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device)
{
device->silence_value = SDL_GetSilenceValueForFormat(device->spec.format);
device->buffer_size = device->sample_frames * (SDL_AUDIO_BITSIZE(device->spec.format) / 8) * device->spec.channels;
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)
@ -1189,7 +1381,7 @@ char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen)
// this expects the device lock to be held.
static int OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec *inspec)
{
SDL_assert(!device->is_opened);
SDL_assert(!device->currently_opened);
SDL_assert(device->logical_devices == NULL);
// Just pretend to open a zombie device. It can still collect logical devices on the assumption they will all migrate when the default device is officially changed.
@ -1198,7 +1390,7 @@ static int OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec
}
SDL_AudioSpec spec;
SDL_memcpy(&spec, inspec ? inspec : &device->default_spec, sizeof (SDL_AudioSpec));
SDL_copyp(&spec, inspec ? inspec : &device->default_spec);
PrepareAudioFormat(device->iscapture, &spec);
/* We allow the device format to change if it's better than the current settings (by various definitions of "better"). This prevents
@ -1211,7 +1403,7 @@ static int OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec
device->sample_frames = GetDefaultSampleFramesFromFreq(device->spec.freq);
SDL_UpdatedAudioDeviceFormat(device); // start this off sane.
device->is_opened = SDL_TRUE; // mark this true even if impl.OpenDevice fails, so we know to clean up.
device->currently_opened = SDL_TRUE; // mark this true even if impl.OpenDevice fails, so we know to clean up.
if (current_audio.impl.OpenDevice(device) < 0) {
ClosePhysicalAudioDevice(device); // clean up anything the backend left half-initialized.
return -1;
@ -1220,12 +1412,20 @@ static int OpenPhysicalAudioDevice(SDL_AudioDevice *device, const SDL_AudioSpec
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_SIMDGetAlignment(), device->buffer_size);
device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size);
if (device->work_buffer == NULL) {
ClosePhysicalAudioDevice(device);
return SDL_OutOfMemory();
}
if (device->spec.format != SDL_AUDIO_F32) {
device->mix_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size);
if (device->mix_buffer == NULL) {
ClosePhysicalAudioDevice(device);
return SDL_OutOfMemory();
}
}
// Start the audio thread if necessary
SDL_AtomicSet(&device->thread_alive, 1);
if (!current_audio.impl.ProvidesOwnCallbackThread) {
@ -1251,16 +1451,16 @@ SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSp
return 0;
}
SDL_bool is_default = SDL_FALSE;
SDL_bool wants_default = SDL_FALSE;
if (devid == SDL_AUDIO_DEVICE_DEFAULT_OUTPUT) {
devid = current_audio.default_output_device_id;
is_default = SDL_TRUE;
wants_default = SDL_TRUE;
} else if (devid == SDL_AUDIO_DEVICE_DEFAULT_CAPTURE) {
devid = current_audio.default_capture_device_id;
is_default = SDL_TRUE;
wants_default = SDL_TRUE;
}
if ((devid == 0) && is_default) {
if ((devid == 0) && wants_default) {
SDL_SetError("No default audio device available");
return 0;
}
@ -1273,7 +1473,7 @@ SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSp
} else {
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid); // this locks the physical device, too.
if (logdev) {
is_default = logdev->is_default; // was the original logical device meant to be a default? Make this one, too.
wants_default = logdev->opened_as_default; // was the original logical device meant to be a default? Make this one, too.
device = logdev->physical_device;
}
}
@ -1282,23 +1482,24 @@ SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSp
if (device) {
SDL_LogicalAudioDevice *logdev = NULL;
if (!is_default && SDL_AtomicGet(&device->zombie)) {
if (!wants_default && SDL_AtomicGet(&device->zombie)) {
// uhoh, this device is undead, and just waiting for a new default device to be declared so it can hand off to it. 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_OutOfMemory();
} else if (!device->is_opened && OpenPhysicalAudioDevice(device, spec) == -1) { // first thing using this physical device? Open at the OS level...
} else if (!device->currently_opened && OpenPhysicalAudioDevice(device, spec) == -1) { // first thing using this physical device? Open at the OS level...
SDL_free(logdev);
} else {
SDL_AtomicSet(&logdev->paused, 0);
retval = logdev->instance_id = assign_audio_device_instance_id(device->iscapture, /*islogical=*/SDL_TRUE);
logdev->physical_device = device;
logdev->is_default = is_default;
logdev->opened_as_default = wants_default;
logdev->next = device->logical_devices;
if (device->logical_devices) {
device->logical_devices->prev = logdev;
}
device->logical_devices = logdev;
device->simple_copy = AudioDeviceCanUseSimpleCopy(device);
}
SDL_UnlockMutex(device->lock);
}
@ -1313,6 +1514,7 @@ static int SetLogicalAudioDevicePauseState(SDL_AudioDeviceID devid, int value)
return -1; // ObtainLogicalAudioDevice will have set an error.
}
SDL_AtomicSet(&logdev->paused, value);
logdev->physical_device->simple_copy = AudioDeviceCanUseSimpleCopy(logdev->physical_device);
SDL_UnlockMutex(logdev->physical_device->lock);
return 0;
}
@ -1327,7 +1529,7 @@ int SDLCALL SDL_ResumeAudioDevice(SDL_AudioDeviceID devid)
return SetLogicalAudioDevicePauseState(devid, 0);
}
SDL_bool SDL_IsAudioDevicePaused(SDL_AudioDeviceID devid)
SDL_bool SDL_AudioDevicePaused(SDL_AudioDeviceID devid)
{
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid);
SDL_bool retval = SDL_FALSE;
@ -1340,6 +1542,31 @@ SDL_bool SDL_IsAudioDevicePaused(SDL_AudioDeviceID devid)
return retval;
}
int SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallback callback, void *userdata)
{
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid);
int retval = 0;
if (logdev) {
SDL_AudioDevice *device = logdev->physical_device;
if (callback && !device->postmix_buffer) {
device->postmix_buffer = (float *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size);
if (device->mix_buffer == NULL) {
retval = SDL_OutOfMemory();
}
}
if (retval == 0) {
logdev->postmix = callback;
logdev->postmix_userdata = userdata;
}
UpdateAudioStreamFormatsLogical(logdev);
device->simple_copy = AudioDeviceCanUseSimpleCopy(device);
SDL_UnlockMutex(device->lock);
}
return retval;
}
int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int num_streams)
{
@ -1356,8 +1583,14 @@ int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int
return SDL_SetError("Audio streams are bound to device ids from SDL_OpenAudioDevice, not raw physical devices");
} else if ((logdev = ObtainLogicalAudioDevice(devid)) == NULL) {
return -1; // ObtainLogicalAudioDevice set the error message.
} else if (logdev->simplified) {
SDL_UnlockMutex(logdev->physical_device->lock);
return SDL_SetError("Cannot change stream bindings on device opened with SDL_OpenAudioDeviceStream");
}
// !!! 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));
@ -1388,18 +1621,8 @@ int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int
if (retval == 0) {
// Now that everything is verified, chain everything together.
const SDL_bool iscapture = device->iscapture;
for (int i = 0; i < num_streams; i++) {
SDL_AudioStream *stream = streams[i];
SDL_AudioSpec src_spec, dst_spec;
// set the proper end of the stream to the device's format.
SDL_GetAudioStreamFormat(stream, &src_spec, &dst_spec);
if (iscapture) {
SDL_SetAudioStreamFormat(stream, &device->spec, &dst_spec);
} else {
SDL_SetAudioStreamFormat(stream, &src_spec, &device->spec);
}
stream->bound_device = logdev;
stream->prev_binding = NULL;
@ -1411,8 +1634,12 @@ int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int
SDL_UnlockMutex(stream->lock);
}
UpdateAudioStreamFormatsLogical(logdev);
}
device->simple_copy = AudioDeviceCanUseSimpleCopy(device);
SDL_UnlockMutex(device->lock);
return retval;
@ -1459,7 +1686,8 @@ void SDL_UnbindAudioStreams(SDL_AudioStream **streams, int num_streams)
// everything is locked, start unbinding streams.
for (int i = 0; i < num_streams; i++) {
SDL_AudioStream *stream = streams[i];
if (stream && stream->bound_device) {
// 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 == NULL);
stream->bound_device->bound_streams = stream->next_binding;
@ -1482,6 +1710,7 @@ void SDL_UnbindAudioStreams(SDL_AudioStream **streams, int num_streams)
stream->bound_device = NULL;
SDL_UnlockMutex(stream->lock);
if (logdev) {
logdev->physical_device->simple_copy = AudioDeviceCanUseSimpleCopy(logdev->physical_device);
SDL_UnlockMutex(logdev->physical_device->lock);
}
}
@ -1493,7 +1722,7 @@ void SDL_UnbindAudioStream(SDL_AudioStream *stream)
SDL_UnbindAudioStreams(&stream, 1);
}
SDL_AudioDeviceID SDL_GetAudioStreamBinding(SDL_AudioStream *stream)
SDL_AudioDeviceID SDL_GetAudioStreamDevice(SDL_AudioStream *stream)
{
SDL_AudioDeviceID retval = 0;
if (stream) {
@ -1506,45 +1735,71 @@ SDL_AudioDeviceID SDL_GetAudioStreamBinding(SDL_AudioStream *stream)
return retval;
}
SDL_AudioStream *SDL_CreateAndBindAudioStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec)
SDL_AudioStream *SDL_OpenAudioDeviceStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec, SDL_AudioStreamCallback callback, void *userdata)
{
const SDL_bool islogical = (devid & (1<<1)) ? SDL_FALSE : SDL_TRUE;
if (!islogical) {
SDL_SetError("Audio streams are bound to device ids from SDL_OpenAudioDevice, not raw physical devices");
return NULL;
SDL_AudioDeviceID logdevid = SDL_OpenAudioDevice(devid, spec);
if (!logdevid) {
return NULL; // error string should already be set.
}
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(logdevid);
if (logdev == NULL) { // this shouldn't happen, but just in case.
SDL_CloseAudioDevice(logdevid);
return NULL; // error string should already be set.
}
SDL_AudioDevice *physdevice = logdev->physical_device;
SDL_assert(physdevice != NULL);
SDL_AtomicSet(&logdev->paused, 1); // start the device paused, to match SDL2.
physdevice->simple_copy = AudioDeviceCanUseSimpleCopy(physdevice);
SDL_UnlockMutex(physdevice->lock); // we don't need to hold the lock for any of this.
const SDL_bool iscapture = physdevice->iscapture;
SDL_AudioStream *stream = NULL;
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid);
if (logdev) {
SDL_AudioDevice *device = logdev->physical_device;
if (device->iscapture) {
stream = SDL_CreateAudioStream(&device->spec, spec);
} else {
stream = SDL_CreateAudioStream(spec, &device->spec);
}
if (stream) {
if (SDL_BindAudioStream(devid, stream) == -1) {
SDL_DestroyAudioStream(stream);
stream = NULL;
}
}
SDL_UnlockMutex(device->lock);
if (iscapture) {
stream = SDL_CreateAudioStream(&physdevice->spec, spec);
} else {
stream = SDL_CreateAudioStream(spec, &physdevice->spec);
}
return stream;
if (!stream) {
SDL_CloseAudioDevice(logdevid);
return NULL; // error string should already be set.
}
if (SDL_BindAudioStream(logdevid, stream) == -1) {
SDL_DestroyAudioStream(stream);
SDL_CloseAudioDevice(logdevid);
return NULL; // error string should already be set.
}
logdev->simplified = SDL_TRUE; // forbid further binding changes on this logical device.
stream->simplified = SDL_TRUE; // so we know to close the audio device when this is destroyed.
if (callback) {
int rc;
if (iscapture) {
rc = SDL_SetAudioStreamPutCallback(stream, callback, userdata);
} else {
rc = SDL_SetAudioStreamGetCallback(stream, callback, userdata);
}
SDL_assert(rc == 0); // should only fail if stream==NULL atm.
}
return stream; // ready to rock.
}
#define NUM_FORMATS 8
static const SDL_AudioFormat format_list[NUM_FORMATS][NUM_FORMATS + 1] = {
{ SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, 0 },
{ SDL_AUDIO_S8, SDL_AUDIO_U8, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, 0 },
{ SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
{ SDL_AUDIO_S16MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
{ SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
{ SDL_AUDIO_S32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
{ SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
{ SDL_AUDIO_F32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
{ SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, SDL_AUDIO_F32LE, SDL_AUDIO_F32BE, 0 },
{ SDL_AUDIO_S8, SDL_AUDIO_U8, SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, SDL_AUDIO_F32LE, SDL_AUDIO_F32BE, 0 },
{ SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, SDL_AUDIO_F32LE, SDL_AUDIO_F32BE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
{ SDL_AUDIO_S16BE, SDL_AUDIO_S16LE, SDL_AUDIO_S32BE, SDL_AUDIO_S32LE, SDL_AUDIO_F32BE, SDL_AUDIO_F32LE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
{ SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, SDL_AUDIO_F32LE, SDL_AUDIO_F32BE, SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
{ SDL_AUDIO_S32BE, SDL_AUDIO_S32LE, SDL_AUDIO_F32BE, SDL_AUDIO_F32LE, SDL_AUDIO_S16BE, SDL_AUDIO_S16LE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
{ SDL_AUDIO_F32LE, SDL_AUDIO_F32BE, SDL_AUDIO_S32LE, SDL_AUDIO_S32BE, SDL_AUDIO_S16LE, SDL_AUDIO_S16BE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
{ SDL_AUDIO_F32BE, SDL_AUDIO_F32LE, SDL_AUDIO_S32BE, SDL_AUDIO_S32LE, SDL_AUDIO_S16BE, SDL_AUDIO_S16LE, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
};
const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format)
@ -1597,8 +1852,9 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device)
SDL_AudioSpec spec;
SDL_bool needs_migration = SDL_FALSE;
SDL_zero(spec);
for (SDL_LogicalAudioDevice *logdev = current_default_device->logical_devices; logdev != NULL; logdev = logdev->next) {
if (logdev->is_default) {
if (logdev->opened_as_default) {
needs_migration = SDL_TRUE;
for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) {
const SDL_AudioSpec *streamspec = iscapture ? &stream->dst_spec : &stream->src_spec;
@ -1624,19 +1880,16 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device)
}
if (needs_migration) {
const SDL_bool spec_changed = !AUDIO_SPECS_EQUAL(current_default_device->spec, new_default_device->spec);
const SDL_bool post_fmt_event = (spec_changed && SDL_EventEnabled(SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED)) ? SDL_TRUE : SDL_FALSE;
SDL_LogicalAudioDevice *next = NULL;
for (SDL_LogicalAudioDevice *logdev = current_default_device->logical_devices; logdev != NULL; logdev = next) {
next = logdev->next;
if (!logdev->is_default) {
if (!logdev->opened_as_default) {
continue; // not opened as a default, leave it on the current physical device.
}
// make sure all our streams are targeting the new device's format.
for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) {
SDL_SetAudioStreamFormat(stream, iscapture ? &new_default_device->spec : NULL, iscapture ? NULL : &new_default_device->spec);
}
// now migrate the logical device.
if (logdev->next) {
logdev->next->prev = logdev->prev;
@ -1652,8 +1905,25 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device)
logdev->prev = NULL;
logdev->next = new_default_device->logical_devices;
new_default_device->logical_devices = logdev;
// make sure all our streams are targeting the new device's format.
UpdateAudioStreamFormatsLogical(logdev);
// Post an event for each logical device we moved.
if (post_fmt_event) {
SDL_Event event;
SDL_zero(event);
event.type = SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED;
event.common.timestamp = 0;
event.adevice.iscapture = iscapture ? 1 : 0;
event.adevice.which = logdev->instance_id;
SDL_PushEvent(&event);
}
}
current_default_device->simple_copy = AudioDeviceCanUseSimpleCopy(current_default_device);
new_default_device->simple_copy = AudioDeviceCanUseSimpleCopy(new_default_device);
if (current_default_device->logical_devices == NULL) { // nothing left on the current physical device, close it.
// !!! FIXME: we _need_ to release this lock, but doing so can cause a race condition if someone opens a device while we're closing it.
SDL_UnlockMutex(current_default_device->lock); // can't hold the lock or the audio thread will deadlock while we WaitThread it.
@ -1675,31 +1945,56 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device)
int SDL_AudioDeviceFormatChangedAlreadyLocked(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames)
{
const int orig_work_buffer_size = device->work_buffer_size;
if (AUDIO_SPECS_EQUAL(device->spec, *newspec) && new_sample_frames == device->sample_frames) {
return 0; // we're already in that format.
}
SDL_copyp(&device->spec, newspec);
UpdateAudioStreamFormatsPhysical(device);
SDL_bool kill_device = SDL_FALSE;
const int orig_buffer_size = device->buffer_size;
const SDL_bool iscapture = device->iscapture;
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_SIMDGetAlignment(), device->work_buffer_size);
if (!device->work_buffer) {
kill_device = SDL_TRUE;
}
if ((device->spec.format != newspec->format) || (device->spec.channels != newspec->channels) || (device->spec.freq != newspec->freq)) {
SDL_memcpy(&device->spec, newspec, sizeof (*newspec));
for (SDL_LogicalAudioDevice *logdev = device->logical_devices; !kill_device && (logdev != NULL); logdev = logdev->next) {
for (SDL_AudioStream *stream = logdev->bound_streams; !kill_device && (stream != NULL); stream = stream->next_binding) {
if (SDL_SetAudioStreamFormat(stream, iscapture ? &device->spec : NULL, iscapture ? NULL : &device->spec) == -1) {
kill_device = SDL_TRUE;
}
if (device->postmix_buffer) {
SDL_aligned_free(device->postmix_buffer);
device->postmix_buffer = (float *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->work_buffer_size);
if (!device->postmix_buffer) {
kill_device = SDL_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_SIMDGetAlignment(), device->work_buffer_size);
if (!device->mix_buffer) {
kill_device = SDL_TRUE;
}
}
}
if (!kill_device) {
device->sample_frames = new_sample_frames;
SDL_UpdatedAudioDeviceFormat(device);
if (device->work_buffer && (device->buffer_size > orig_buffer_size)) {
SDL_aligned_free(device->work_buffer);
device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->buffer_size);
if (!device->work_buffer) {
kill_device = SDL_TRUE;
}
// Post an event for the physical device, and each logical device on this physical device.
if (!kill_device && SDL_EventEnabled(SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED)) {
SDL_Event event;
SDL_zero(event);
event.type = SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED;
event.common.timestamp = 0;
event.adevice.iscapture = device->iscapture ? 1 : 0;
event.adevice.which = device->instance_id;
SDL_PushEvent(&event);
for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) {
event.adevice.which = logdev->instance_id;
SDL_PushEvent(&event);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,516 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
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"
#define AUDIO_SPECS_EQUAL(x, y) (((x).format == (y).format) && ((x).channels == (y).channels) && ((x).freq == (y).freq))
struct SDL_AudioTrack
{
SDL_AudioSpec spec;
SDL_bool flushed;
SDL_AudioTrack *next;
size_t (*avail)(void *ctx);
int (*write)(void *ctx, const Uint8 *buf, size_t len);
size_t (*read)(void *ctx, Uint8 *buf, size_t len, SDL_bool advance);
void (*destroy)(void *ctx);
};
struct SDL_AudioQueue
{
SDL_AudioTrack *head;
SDL_AudioTrack *tail;
size_t chunk_size;
};
typedef struct SDL_AudioChunk SDL_AudioChunk;
struct SDL_AudioChunk
{
SDL_AudioChunk *next;
size_t head;
size_t tail;
Uint8 data[SDL_VARIABLE_LENGTH_ARRAY];
};
typedef struct SDL_ChunkedAudioTrack
{
SDL_AudioTrack track;
size_t chunk_size;
SDL_AudioChunk *head;
SDL_AudioChunk *tail;
size_t queued_bytes;
SDL_AudioChunk *free_chunks;
size_t num_free_chunks;
} SDL_ChunkedAudioTrack;
static void DestroyAudioChunk(SDL_AudioChunk *chunk)
{
SDL_free(chunk);
}
static void DestroyAudioChunks(SDL_AudioChunk *chunk)
{
while (chunk) {
SDL_AudioChunk *next = chunk->next;
DestroyAudioChunk(chunk);
chunk = next;
}
}
static void ResetAudioChunk(SDL_AudioChunk *chunk)
{
chunk->next = NULL;
chunk->head = 0;
chunk->tail = 0;
}
static SDL_AudioChunk *CreateAudioChunk(size_t chunk_size)
{
SDL_AudioChunk *chunk = (SDL_AudioChunk *)SDL_malloc(sizeof(*chunk) + chunk_size);
if (chunk == NULL) {
return NULL;
}
ResetAudioChunk(chunk);
return chunk;
}
static void DestroyAudioTrackChunk(SDL_ChunkedAudioTrack *track, SDL_AudioChunk *chunk)
{
// Keeping a list of free chunks reduces memory allocations,
// But also increases the amount of work to perform when freeing the track.
const size_t max_free_bytes = 64 * 1024;
if (track->chunk_size * track->num_free_chunks < max_free_bytes) {
chunk->next = track->free_chunks;
track->free_chunks = chunk;
++track->num_free_chunks;
} else {
DestroyAudioChunk(chunk);
}
}
static SDL_AudioChunk *CreateAudioTrackChunk(SDL_ChunkedAudioTrack *track)
{
if (track->num_free_chunks > 0) {
SDL_AudioChunk *chunk = track->free_chunks;
track->free_chunks = chunk->next;
--track->num_free_chunks;
ResetAudioChunk(chunk);
return chunk;
}
return CreateAudioChunk(track->chunk_size);
}
static size_t AvailChunkedAudioTrack(void *ctx)
{
SDL_ChunkedAudioTrack *track = ctx;
return track->queued_bytes;
}
static int WriteToChunkedAudioTrack(void *ctx, const Uint8 *data, size_t len)
{
SDL_ChunkedAudioTrack *track = ctx;
SDL_AudioChunk *chunk = track->tail;
// Handle the first chunk
if (chunk == NULL) {
chunk = CreateAudioTrackChunk(track);
if (chunk == NULL) {
return SDL_OutOfMemory();
}
SDL_assert((track->head == NULL) && (track->tail == NULL) && (track->queued_bytes == 0));
track->head = chunk;
track->tail = chunk;
}
size_t total = 0;
size_t old_tail = chunk->tail;
size_t chunk_size = track->chunk_size;
while (chunk) {
size_t to_write = chunk_size - chunk->tail;
to_write = SDL_min(to_write, len - total);
SDL_memcpy(&chunk->data[chunk->tail], &data[total], to_write);
total += to_write;
chunk->tail += to_write;
if (total == len) {
break;
}
SDL_AudioChunk *next = CreateAudioTrackChunk(track);
chunk->next = next;
chunk = next;
}
// Roll back the changes if we couldn't write all the data
if (chunk == NULL) {
chunk = track->tail;
SDL_AudioChunk *next = chunk->next;
chunk->next = NULL;
chunk->tail = old_tail;
DestroyAudioChunks(next);
return SDL_OutOfMemory();
}
track->tail = chunk;
track->queued_bytes += total;
return 0;
}
static size_t ReadFromChunkedAudioTrack(void *ctx, Uint8 *data, size_t len, SDL_bool advance)
{
SDL_ChunkedAudioTrack *track = ctx;
SDL_AudioChunk *chunk = track->head;
size_t total = 0;
size_t head = 0;
while (chunk) {
head = chunk->head;
size_t to_read = chunk->tail - head;
to_read = SDL_min(to_read, len - total);
SDL_memcpy(&data[total], &chunk->data[head], to_read);
total += to_read;
SDL_AudioChunk *next = chunk->next;
if (total == len) {
head += to_read;
break;
}
if (advance) {
DestroyAudioTrackChunk(track, chunk);
}
chunk = next;
}
if (advance) {
if (chunk) {
chunk->head = head;
track->head = chunk;
} else {
track->head = NULL;
track->tail = NULL;
}
track->queued_bytes -= total;
}
return total;
}
static void DestroyChunkedAudioTrack(void *ctx)
{
SDL_ChunkedAudioTrack *track = ctx;
DestroyAudioChunks(track->head);
DestroyAudioChunks(track->free_chunks);
SDL_free(track);
}
static SDL_AudioTrack *CreateChunkedAudioTrack(const SDL_AudioSpec *spec, size_t chunk_size)
{
SDL_ChunkedAudioTrack *track = (SDL_ChunkedAudioTrack *)SDL_calloc(1, sizeof(*track));
if (track == NULL) {
SDL_OutOfMemory();
return NULL;
}
SDL_copyp(&track->track.spec, spec);
track->track.avail = AvailChunkedAudioTrack;
track->track.write = WriteToChunkedAudioTrack;
track->track.read = ReadFromChunkedAudioTrack;
track->track.destroy = DestroyChunkedAudioTrack;
track->chunk_size = chunk_size;
return &track->track;
}
SDL_AudioQueue *SDL_CreateAudioQueue(size_t chunk_size)
{
SDL_AudioQueue *queue = (SDL_AudioQueue *)SDL_calloc(1, sizeof(*queue));
if (queue == NULL) {
SDL_OutOfMemory();
return NULL;
}
queue->chunk_size = chunk_size;
return queue;
}
void SDL_DestroyAudioQueue(SDL_AudioQueue *queue)
{
SDL_ClearAudioQueue(queue);
SDL_free(queue);
}
void SDL_ClearAudioQueue(SDL_AudioQueue *queue)
{
SDL_AudioTrack *track = queue->head;
queue->head = NULL;
queue->tail = NULL;
while (track) {
SDL_AudioTrack *next = track->next;
track->destroy(track);
track = next;
}
}
static void SDL_FlushAudioTrack(SDL_AudioTrack *track)
{
track->flushed = SDL_TRUE;
track->write = NULL;
}
void SDL_FlushAudioQueue(SDL_AudioQueue *queue)
{
SDL_AudioTrack *track = queue->tail;
if (track) {
SDL_FlushAudioTrack(track);
}
}
void SDL_PopAudioQueueHead(SDL_AudioQueue *queue)
{
SDL_AudioTrack *track = queue->head;
for (;;) {
SDL_bool flushed = track->flushed;
SDL_AudioTrack *next = track->next;
track->destroy(track);
track = next;
if (flushed) {
break;
}
}
queue->head = track;
if (track == NULL) {
queue->tail = NULL;
}
}
size_t SDL_GetAudioQueueChunkSize(SDL_AudioQueue *queue)
{
return queue->chunk_size;
}
SDL_AudioTrack *SDL_CreateChunkedAudioTrack(const SDL_AudioSpec *spec, const Uint8 *data, size_t len, size_t chunk_size)
{
SDL_AudioTrack *track = CreateChunkedAudioTrack(spec, chunk_size);
if (track == NULL) {
return NULL;
}
if (track->write(track, data, len) != 0) {
track->destroy(track);
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 (!AUDIO_SPECS_EQUAL(tail->spec, track->spec)) {
SDL_FlushAudioTrack(tail);
}
tail->next = track;
} else {
queue->head = track;
}
queue->tail = track;
}
int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const Uint8 *data, size_t len)
{
if (len == 0) {
return 0;
}
SDL_AudioTrack *track = queue->tail;
if ((track != NULL) && !AUDIO_SPECS_EQUAL(track->spec, *spec)) {
SDL_FlushAudioTrack(track);
}
if ((track == NULL) || (track->write == NULL)) {
SDL_AudioTrack *new_track = CreateChunkedAudioTrack(spec, queue->chunk_size);
if (new_track == NULL) {
return SDL_OutOfMemory();
}
if (track) {
track->next = new_track;
} else {
queue->head = new_track;
}
queue->tail = new_track;
track = new_track;
}
return track->write(track, data, len);
}
void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue)
{
return queue->head;
}
size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, SDL_bool *out_flushed)
{
SDL_AudioTrack *iter = *inout_iter;
SDL_assert(iter != NULL);
SDL_copyp(out_spec, &iter->spec);
SDL_bool flushed = SDL_FALSE;
size_t queued_bytes = 0;
while (iter) {
SDL_AudioTrack *track = iter;
iter = iter->next;
size_t avail = track->avail(track);
if (avail >= SDL_SIZE_MAX - queued_bytes) {
queued_bytes = SDL_SIZE_MAX;
flushed = SDL_FALSE;
break;
}
queued_bytes += avail;
flushed = track->flushed;
if (flushed) {
break;
}
}
*inout_iter = iter;
*out_flushed = flushed;
return queued_bytes;
}
int SDL_ReadFromAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len)
{
size_t total = 0;
SDL_AudioTrack *track = queue->head;
for (;;) {
if (track == NULL) {
return SDL_SetError("Reading past end of queue");
}
total += track->read(track, &data[total], len - total, SDL_TRUE);
if (total == len) {
return 0;
}
if (track->flushed) {
return SDL_SetError("Reading past end of flushed track");
}
SDL_AudioTrack *next = track->next;
if (next == NULL) {
return SDL_SetError("Reading past end of incomplete track");
}
queue->head = next;
track->destroy(track);
track = next;
}
}
int SDL_PeekIntoAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len)
{
size_t total = 0;
SDL_AudioTrack *track = queue->head;
for (;;) {
if (track == NULL) {
return SDL_SetError("Peeking past end of queue");
}
total += track->read(track, &data[total], len - total, SDL_FALSE);
if (total == len) {
return 0;
}
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);
return 0;
}
track = track->next;
}
}

View File

@ -0,0 +1,77 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
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 struct SDL_AudioQueue SDL_AudioQueue;
typedef struct SDL_AudioTrack SDL_AudioTrack;
// Create a new audio queue
SDL_AudioQueue *SDL_CreateAudioQueue(size_t chunk_size);
// Destroy an audio queue
void SDL_DestroyAudioQueue(SDL_AudioQueue *queue);
// Completely clear the queue
void SDL_ClearAudioQueue(SDL_AudioQueue *queue);
// Mark the last track as flushed
void SDL_FlushAudioQueue(SDL_AudioQueue *queue);
// Pop the current head track
// REQUIRES: The head track must exist, and must have been flushed
void SDL_PopAudioQueueHead(SDL_AudioQueue *queue);
// Get the chunk size, mostly for use with SDL_CreateChunkedAudioTrack
// This can be called from any thread
size_t SDL_GetAudioQueueChunkSize(SDL_AudioQueue *queue);
// Write data to the end of queue
// REQUIRES: If the spec has changed, the last track must have been flushed
int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const Uint8 *data, size_t len);
// Create a track without needing to hold any locks
SDL_AudioTrack *SDL_CreateChunkedAudioTrack(const SDL_AudioSpec *spec, const Uint8 *data, size_t len, size_t chunk_size);
// Add a track to the end of the queue
// REQUIRES: `track != NULL`
void SDL_AddTrackToAudioQueue(SDL_AudioQueue *queue, SDL_AudioTrack *track);
// Iterate over the tracks in the queue
void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue);
// Query and update the track iterator
// REQUIRES: `*inout_iter != NULL` (a valid iterator)
size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, SDL_bool *out_flushed);
// Read data from the start of the queue
// REQUIRES: There must be enough data in the queue
int SDL_ReadFromAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len);
// Peek into the start of the queue
// REQUIRES: There must be enough data in the queue, unless it has been flushed, in which case missing data is filled with silence.
int SDL_PeekIntoAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len);
#endif // SDL_audioqueue_h_

View File

@ -0,0 +1,335 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
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/ */
#include "SDL_audio_resampler_filter.h"
/* 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)
#define RESAMPLER_FILTER_INTERP_BITS (32 - RESAMPLER_BITS_PER_ZERO_CROSSING)
#define RESAMPLER_FILTER_INTERP_RANGE (1 << RESAMPLER_FILTER_INTERP_BITS)
#define RESAMPLER_SAMPLES_PER_FRAME (RESAMPLER_ZERO_CROSSINGS * 2)
#define RESAMPLER_FULL_FILTER_SIZE (RESAMPLER_SAMPLES_PER_FRAME * (RESAMPLER_SAMPLES_PER_ZERO_CROSSING + 1))
static void ResampleFrame_Scalar(const float *src, float *dst, const float *raw_filter, float interp, int chans)
{
int i, chan;
float filter[RESAMPLER_SAMPLES_PER_FRAME];
// Interpolate between the nearest two filters
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
filter[i] = (raw_filter[i] * (1.0f - interp)) + (raw_filter[i + RESAMPLER_SAMPLES_PER_FRAME] * interp);
}
if (chans == 2) {
float out[2];
out[0] = 0.0f;
out[1] = 0.0f;
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
const float scale = filter[i];
out[0] += src[i * 2 + 0] * scale;
out[1] += src[i * 2 + 1] * scale;
}
dst[0] = out[0];
dst[1] = out[1];
return;
}
if (chans == 1) {
float out = 0.0f;
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
out += src[i] * filter[i];
}
dst[0] = out;
return;
}
for (chan = 0; chan < chans; chan++) {
float f = 0.0f;
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
f += src[i * chans + chan] * filter[i];
}
dst[chan] = f;
}
}
#ifdef SDL_SSE_INTRINSICS
static void SDL_TARGETING("sse") ResampleFrame_SSE(const float *src, float *dst, const float *raw_filter, float interp, int chans)
{
#if RESAMPLER_SAMPLES_PER_FRAME != 10
#error Invalid samples per frame
#endif
// Load the filter
__m128 f0 = _mm_loadu_ps(raw_filter + 0);
__m128 f1 = _mm_loadu_ps(raw_filter + 4);
__m128 f2 = _mm_loadl_pi(_mm_setzero_ps(), (const __m64 *)(raw_filter + 8));
__m128 g0 = _mm_loadu_ps(raw_filter + 10);
__m128 g1 = _mm_loadu_ps(raw_filter + 14);
__m128 g2 = _mm_loadl_pi(_mm_setzero_ps(), (const __m64 *)(raw_filter + 18));
__m128 interp1 = _mm_set1_ps(interp);
__m128 interp2 = _mm_sub_ps(_mm_set1_ps(1.0f), _mm_set1_ps(interp));
// Linear interpolate the filter
f0 = _mm_add_ps(_mm_mul_ps(f0, interp2), _mm_mul_ps(g0, interp1));
f1 = _mm_add_ps(_mm_mul_ps(f1, interp2), _mm_mul_ps(g1, interp1));
f2 = _mm_add_ps(_mm_mul_ps(f2, interp2), _mm_mul_ps(g2, interp1));
if (chans == 2) {
// Duplicate each of the filter elements
g0 = _mm_unpackhi_ps(f0, f0);
f0 = _mm_unpacklo_ps(f0, f0);
g1 = _mm_unpackhi_ps(f1, f1);
f1 = _mm_unpacklo_ps(f1, f1);
f2 = _mm_unpacklo_ps(f2, f2);
// Multiply the filter by the input
f0 = _mm_mul_ps(f0, _mm_loadu_ps(src + 0));
g0 = _mm_mul_ps(g0, _mm_loadu_ps(src + 4));
f1 = _mm_mul_ps(f1, _mm_loadu_ps(src + 8));
g1 = _mm_mul_ps(g1, _mm_loadu_ps(src + 12));
f2 = _mm_mul_ps(f2, _mm_loadu_ps(src + 16));
// Calculate the sum
f0 = _mm_add_ps(_mm_add_ps(_mm_add_ps(f0, g0), _mm_add_ps(f1, g1)), f2);
f0 = _mm_add_ps(f0, _mm_movehl_ps(f0, f0));
// Store the result
_mm_storel_pi((__m64 *)dst, f0);
return;
}
if (chans == 1) {
// Multiply the filter by the input
f0 = _mm_mul_ps(f0, _mm_loadu_ps(src + 0));
f1 = _mm_mul_ps(f1, _mm_loadu_ps(src + 4));
f2 = _mm_mul_ps(f2, _mm_loadl_pi(_mm_setzero_ps(), (const __m64 *)(src + 8)));
// Calculate the sum
f0 = _mm_add_ps(f0, f1);
f0 = _mm_add_ps(_mm_add_ps(f0, f2), _mm_movehl_ps(f0, f0));
f0 = _mm_add_ss(f0, _mm_shuffle_ps(f0, f0, _MM_SHUFFLE(1, 1, 1, 1)));
// Store the result
_mm_store_ss(dst, f0);
return;
}
float filter[RESAMPLER_SAMPLES_PER_FRAME];
_mm_storeu_ps(filter + 0, f0);
_mm_storeu_ps(filter + 4, f1);
_mm_storel_pi((__m64 *)(filter + 8), f2);
int i, chan = 0;
for (; chan + 4 <= chans; chan += 4) {
f0 = _mm_setzero_ps();
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
f0 = _mm_add_ps(f0, _mm_mul_ps(_mm_loadu_ps(&src[i * chans + chan]), _mm_load1_ps(&filter[i])));
}
_mm_storeu_ps(&dst[chan], f0);
}
for (; chan < chans; chan++) {
f0 = _mm_setzero_ps();
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
f0 = _mm_add_ss(f0, _mm_mul_ss(_mm_load_ss(&src[i * chans + chan]), _mm_load_ss(&filter[i])));
}
_mm_store_ss(&dst[chan], f0);
}
}
#endif
static void (*ResampleFrame)(const float *src, float *dst, const float *raw_filter, float interp, int chans);
static float FullResamplerFilter[RESAMPLER_FULL_FILTER_SIZE];
void SDL_SetupAudioResampler(void)
{
static SDL_bool setup = SDL_FALSE;
if (setup) {
return;
}
// Build a table combining the left and right wings, for faster access
int i, j;
for (i = 0; i < RESAMPLER_SAMPLES_PER_ZERO_CROSSING; ++i) {
for (j = 0; j < RESAMPLER_ZERO_CROSSINGS; j++) {
int lwing = (i * RESAMPLER_SAMPLES_PER_FRAME) + (RESAMPLER_ZERO_CROSSINGS - 1) - j;
int rwing = (RESAMPLER_FULL_FILTER_SIZE - 1) - lwing;
float value = ResamplerFilter[(i * RESAMPLER_ZERO_CROSSINGS) + j];
FullResamplerFilter[lwing] = value;
FullResamplerFilter[rwing] = value;
}
}
for (i = 0; i < RESAMPLER_ZERO_CROSSINGS; ++i) {
int rwing = i + RESAMPLER_ZERO_CROSSINGS;
int lwing = (RESAMPLER_FULL_FILTER_SIZE - 1) - rwing;
FullResamplerFilter[lwing] = 0.0f;
FullResamplerFilter[rwing] = 0.0f;
}
ResampleFrame = ResampleFrame_Scalar;
#ifdef SDL_SSE_INTRINSICS
if (SDL_HasSSE()) {
ResampleFrame = ResampleFrame_SSE;
}
#endif
setup = SDL_TRUE;
}
Sint64 SDL_GetResampleRate(int src_rate, int dst_rate)
{
SDL_assert(src_rate > 0);
SDL_assert(dst_rate > 0);
Sint64 sample_rate = ((Sint64)src_rate << 32) / (Sint64)dst_rate;
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 Sint64 ResamplerAdd(Sint64 a, Sint64 b, Sint64 *ret)
{
if ((b > 0) && (a > SDL_MAX_SINT64 - b)) {
return -1;
}
*ret = a + b;
return 0;
}
SDL_FORCE_INLINE Sint64 ResamplerMul(Sint64 a, Sint64 b, Sint64 *ret)
{
if ((b > 0) && (a > SDL_MAX_SINT64 / b)) {
return -1;
}
*ret = a * b;
return 0;
}
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;
SDL_assert(resample_rate > 0);
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 float *filter = &FullResamplerFilter[(srcfraction >> RESAMPLER_FILTER_INTERP_BITS) * RESAMPLER_SAMPLES_PER_FRAME];
const float interp = (float)(srcfraction & (RESAMPLER_FILTER_INTERP_RANGE - 1)) * (1.0f / RESAMPLER_FILTER_INTERP_RANGE);
const float *frame = &src[(srcindex - (RESAMPLER_ZERO_CROSSINGS - 1)) * chans];
ResampleFrame(frame, dst, filter, interp, chans);
dst += chans;
}
*inout_resample_offset = srcpos - ((Sint64)inframes << 32);
}

View File

@ -0,0 +1,43 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
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(const int src_rate, const 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_

View File

@ -39,482 +39,510 @@
#define NEED_SCALAR_CONVERTER_FALLBACKS 1
#endif
#define DIVBY128 0.0078125f
#define DIVBY32768 0.000030517578125f
#define DIVBY8388607 0.00000011920930376163766f
#define DIVBY2147483648 0.0000000004656612873077392578125f /* 0x1p-31f */
#if NEED_SCALAR_CONVERTER_FALLBACKS
/* these all convert backwards because (currently) float32 is >= to the size of anything it converts to, so it lets us safely convert in-place. */
#define AUDIOCVT_TOFLOAT_SCALAR(from, fromtype, equation) \
static void SDL_Convert_##from##_to_F32_Scalar(float *dst, const fromtype *src, int num_samples) { \
int i; \
LOG_DEBUG_AUDIO_CONVERT(#from, "F32"); \
for (i = num_samples - 1; i >= 0; --i) { \
dst[i] = equation; \
} \
/* 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;
}
}
AUDIOCVT_TOFLOAT_SCALAR(S8, Sint8, ((float)src[i]) * DIVBY128)
AUDIOCVT_TOFLOAT_SCALAR(U8, Uint8, (((float)src[i]) * DIVBY128) - 1.0f)
AUDIOCVT_TOFLOAT_SCALAR(S16, Sint16, ((float)src[i]) * DIVBY32768)
AUDIOCVT_TOFLOAT_SCALAR(S32, Sint32, ((float)(src[i] >> 8)) * DIVBY8388607)
#undef AUDIOCVT_FROMFLOAT_SCALAR
static void SDL_Convert_U8_to_F32_Scalar(float *dst, const Uint8 *src, int num_samples)
{
int i;
/* these all convert forwards because (currently) float32 is >= to the size of anything it converts from, so it lets us safely convert in-place. */
#define AUDIOCVT_FROMFLOAT_SCALAR(to, totype, clampmin, clampmax, equation) \
static void SDL_Convert_F32_to_##to##_Scalar(totype *dst, const float *src, int num_samples) { \
int i; \
LOG_DEBUG_AUDIO_CONVERT("F32", #to); \
for (i = 0; i < num_samples; i++) { \
const float sample = src[i]; \
if (sample >= 1.0f) { \
dst[i] = (totype) (clampmax); \
} else if (sample <= -1.0f) { \
dst[i] = (totype) (clampmin); \
} else { \
dst[i] = (totype) (equation); \
} \
} \
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;
}
}
AUDIOCVT_FROMFLOAT_SCALAR(S8, Sint8, -128, 127, sample * 127.0f);
AUDIOCVT_FROMFLOAT_SCALAR(U8, Uint8, 0, 255, (sample + 1.0f) * 127.0f);
AUDIOCVT_FROMFLOAT_SCALAR(S16, Sint16, -32768, 32767, sample * 32767.0f);
AUDIOCVT_FROMFLOAT_SCALAR(S32, Sint32, -2147483648LL, 2147483647, ((Sint32)(sample * 8388607.0f)) << 8);
#undef AUDIOCVT_FROMFLOAT_SCALAR
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
#endif /* NEED_SCALAR_CONVERTER_FALLBACKS */
#ifdef SDL_SSE2_INTRINSICS
static void SDL_TARGETING("sse2") SDL_Convert_S8_to_F32_SSE2(float *dst, const Sint8 *src, int num_samples)
{
int i;
int i = 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)");
src += num_samples - 1;
dst += num_samples - 1;
while (i >= 16) {
i -= 16;
/* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
for (i = num_samples; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
*dst = ((float)*src) * DIVBY128;
const __m128i bytes = _mm_xor_si128(_mm_loadu_si128((const __m128i *)&src[i]), flipper);
const __m128i shorts1 = _mm_unpacklo_epi8(bytes, zero);
const __m128i shorts2 = _mm_unpackhi_epi8(bytes, zero);
const __m128 floats1 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts1, caster)), offset);
const __m128 floats2 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts1, caster)), offset);
const __m128 floats3 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts2, caster)), offset);
const __m128 floats4 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts2, caster)), offset);
_mm_storeu_ps(&dst[i], floats1);
_mm_storeu_ps(&dst[i + 4], floats2);
_mm_storeu_ps(&dst[i + 8], floats3);
_mm_storeu_ps(&dst[i + 12], floats4);
}
src -= 15;
dst -= 15; /* adjust to read SSE blocks from the start. */
SDL_assert(!i || !(((size_t)dst) & 15));
/* Make sure src is aligned too. */
if (!(((size_t)src) & 15)) {
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
const __m128i *mmsrc = (const __m128i *)src;
const __m128i zero = _mm_setzero_si128();
const __m128 divby128 = _mm_set1_ps(DIVBY128);
while (i >= 16) { /* 16 * 8-bit */
const __m128i bytes = _mm_load_si128(mmsrc); /* get 16 sint8 into an XMM register. */
/* treat as int16, shift left to clear every other sint16, then back right with sign-extend. Now sint16. */
const __m128i shorts1 = _mm_srai_epi16(_mm_slli_epi16(bytes, 8), 8);
/* right-shift-sign-extend gets us sint16 with the other set of values. */
const __m128i shorts2 = _mm_srai_epi16(bytes, 8);
/* unpack against zero to make these int32, shift to make them sign-extend, convert to float, multiply. Whew! */
const __m128 floats1 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpacklo_epi16(shorts1, zero), 16), 16)), divby128);
const __m128 floats2 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpacklo_epi16(shorts2, zero), 16), 16)), divby128);
const __m128 floats3 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpackhi_epi16(shorts1, zero), 16), 16)), divby128);
const __m128 floats4 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpackhi_epi16(shorts2, zero), 16), 16)), divby128);
/* Interleave back into correct order, store. */
_mm_store_ps(dst, _mm_unpacklo_ps(floats1, floats2));
_mm_store_ps(dst + 4, _mm_unpackhi_ps(floats1, floats2));
_mm_store_ps(dst + 8, _mm_unpacklo_ps(floats3, floats4));
_mm_store_ps(dst + 12, _mm_unpackhi_ps(floats3, floats4));
i -= 16;
mmsrc--;
dst -= 16;
}
src = (const Sint8 *)mmsrc;
}
src += 15;
dst += 15; /* adjust for any scalar finishing. */
/* Finish off any leftovers with scalar operations. */
while (i) {
*dst = ((float)*src) * DIVBY128;
i--;
src--;
dst--;
--i;
_mm_store_ss(&dst[i], _mm_add_ss(_mm_castsi128_ps(_mm_cvtsi32_si128((Uint8)src[i] ^ 0x47800080u)), offset));
}
}
static void SDL_TARGETING("sse2") SDL_Convert_U8_to_F32_SSE2(float *dst, const Uint8 *src, int num_samples)
{
int i;
int i = 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)");
src += num_samples - 1;
dst += num_samples - 1;
while (i >= 16) {
i -= 16;
/* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
for (i = num_samples; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
*dst = (((float)*src) * DIVBY128) - 1.0f;
const __m128i bytes = _mm_loadu_si128((const __m128i *)&src[i]);
const __m128i shorts1 = _mm_unpacklo_epi8(bytes, zero);
const __m128i shorts2 = _mm_unpackhi_epi8(bytes, zero);
const __m128 floats1 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts1, caster)), offset);
const __m128 floats2 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts1, caster)), offset);
const __m128 floats3 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts2, caster)), offset);
const __m128 floats4 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts2, caster)), offset);
_mm_storeu_ps(&dst[i], floats1);
_mm_storeu_ps(&dst[i + 4], floats2);
_mm_storeu_ps(&dst[i + 8], floats3);
_mm_storeu_ps(&dst[i + 12], floats4);
}
src -= 15;
dst -= 15; /* adjust to read SSE blocks from the start. */
SDL_assert(!i || !(((size_t)dst) & 15));
/* Make sure src is aligned too. */
if (!(((size_t)src) & 15)) {
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
const __m128i *mmsrc = (const __m128i *)src;
const __m128i zero = _mm_setzero_si128();
const __m128 divby128 = _mm_set1_ps(DIVBY128);
const __m128 minus1 = _mm_set1_ps(-1.0f);
while (i >= 16) { /* 16 * 8-bit */
const __m128i bytes = _mm_load_si128(mmsrc); /* get 16 uint8 into an XMM register. */
/* treat as int16, shift left to clear every other sint16, then back right with zero-extend. Now uint16. */
const __m128i shorts1 = _mm_srli_epi16(_mm_slli_epi16(bytes, 8), 8);
/* right-shift-zero-extend gets us uint16 with the other set of values. */
const __m128i shorts2 = _mm_srli_epi16(bytes, 8);
/* unpack against zero to make these int32, convert to float, multiply, add. Whew! */
/* Note that AVX2 can do floating point multiply+add in one instruction, fwiw. SSE2 cannot. */
const __m128 floats1 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(shorts1, zero)), divby128), minus1);
const __m128 floats2 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(shorts2, zero)), divby128), minus1);
const __m128 floats3 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(shorts1, zero)), divby128), minus1);
const __m128 floats4 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(shorts2, zero)), divby128), minus1);
/* Interleave back into correct order, store. */
_mm_store_ps(dst, _mm_unpacklo_ps(floats1, floats2));
_mm_store_ps(dst + 4, _mm_unpackhi_ps(floats1, floats2));
_mm_store_ps(dst + 8, _mm_unpacklo_ps(floats3, floats4));
_mm_store_ps(dst + 12, _mm_unpackhi_ps(floats3, floats4));
i -= 16;
mmsrc--;
dst -= 16;
}
src = (const Uint8 *)mmsrc;
}
src += 15;
dst += 15; /* adjust for any scalar finishing. */
/* Finish off any leftovers with scalar operations. */
while (i) {
*dst = (((float)*src) * DIVBY128) - 1.0f;
i--;
src--;
dst--;
--i;
_mm_store_ss(&dst[i], _mm_add_ss(_mm_castsi128_ps(_mm_cvtsi32_si128((Uint8)src[i] ^ 0x47800000u)), offset));
}
}
static void SDL_TARGETING("sse2") SDL_Convert_S16_to_F32_SSE2(float *dst, const Sint16 *src, int num_samples)
{
int i;
int i = 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)");
src += num_samples - 1;
dst += num_samples - 1;
while (i >= 16) {
i -= 16;
/* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
for (i = num_samples; i && (((size_t)(dst - 7)) & 15); --i, --src, --dst) {
*dst = ((float)*src) * DIVBY32768;
const __m128i shorts1 = _mm_xor_si128(_mm_loadu_si128((const __m128i *)&src[i]), flipper);
const __m128i shorts2 = _mm_xor_si128(_mm_loadu_si128((const __m128i *)&src[i + 8]), flipper);
const __m128 floats1 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts1, caster)), offset);
const __m128 floats2 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts1, caster)), offset);
const __m128 floats3 = _mm_add_ps(_mm_castsi128_ps(_mm_unpacklo_epi16(shorts2, caster)), offset);
const __m128 floats4 = _mm_add_ps(_mm_castsi128_ps(_mm_unpackhi_epi16(shorts2, caster)), offset);
_mm_storeu_ps(&dst[i], floats1);
_mm_storeu_ps(&dst[i + 4], floats2);
_mm_storeu_ps(&dst[i + 8], floats3);
_mm_storeu_ps(&dst[i + 12], floats4);
}
src -= 7;
dst -= 7; /* adjust to read SSE blocks from the start. */
SDL_assert(!i || !(((size_t)dst) & 15));
/* Make sure src is aligned too. */
if (!(((size_t)src) & 15)) {
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
const __m128 divby32768 = _mm_set1_ps(DIVBY32768);
while (i >= 8) { /* 8 * 16-bit */
const __m128i ints = _mm_load_si128((__m128i const *)src); /* get 8 sint16 into an XMM register. */
/* treat as int32, shift left to clear every other sint16, then back right with sign-extend. Now sint32. */
const __m128i a = _mm_srai_epi32(_mm_slli_epi32(ints, 16), 16);
/* right-shift-sign-extend gets us sint32 with the other set of values. */
const __m128i b = _mm_srai_epi32(ints, 16);
/* Interleave these back into the right order, convert to float, multiply, store. */
_mm_store_ps(dst, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi32(a, b)), divby32768));
_mm_store_ps(dst + 4, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi32(a, b)), divby32768));
i -= 8;
src -= 8;
dst -= 8;
}
}
src += 7;
dst += 7; /* adjust for any scalar finishing. */
/* Finish off any leftovers with scalar operations. */
while (i) {
*dst = ((float)*src) * DIVBY32768;
i--;
src--;
dst--;
--i;
_mm_store_ss(&dst[i], _mm_add_ss(_mm_castsi128_ps(_mm_cvtsi32_si128((Uint16)src[i] ^ 0x43808000u)), offset));
}
}
static void SDL_TARGETING("sse2") SDL_Convert_S32_to_F32_SSE2(float *dst, const Sint32 *src, int num_samples)
{
int i;
int i = num_samples;
/* dst[i] = f32(src[i]) / f32(0x80000000) */
const __m128 scaler = _mm_set1_ps(DIVBY2147483648);
LOG_DEBUG_AUDIO_CONVERT("S32", "F32 (using SSE2)");
/* Get dst aligned to 16 bytes */
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
*dst = ((float)(*src >> 8)) * DIVBY8388607;
while (i >= 16) {
i -= 16;
const __m128i ints1 = _mm_loadu_si128((const __m128i *)&src[i]);
const __m128i ints2 = _mm_loadu_si128((const __m128i *)&src[i + 4]);
const __m128i ints3 = _mm_loadu_si128((const __m128i *)&src[i + 8]);
const __m128i ints4 = _mm_loadu_si128((const __m128i *)&src[i + 12]);
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);
const __m128 floats4 = _mm_mul_ps(_mm_cvtepi32_ps(ints4), scaler);
_mm_storeu_ps(&dst[i], floats1);
_mm_storeu_ps(&dst[i + 4], floats2);
_mm_storeu_ps(&dst[i + 8], floats3);
_mm_storeu_ps(&dst[i + 12], floats4);
}
SDL_assert(!i || !(((size_t)dst) & 15));
/* Make sure src is aligned too. */
if (!(((size_t)src) & 15)) {
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
const __m128 divby8388607 = _mm_set1_ps(DIVBY8388607);
const __m128i *mmsrc = (const __m128i *)src;
while (i >= 4) { /* 4 * sint32 */
/* shift out lowest bits so int fits in a float32. Small precision loss, but much faster. */
_mm_store_ps(dst, _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_load_si128(mmsrc), 8)), divby8388607));
i -= 4;
mmsrc++;
dst += 4;
}
src = (const Sint32 *)mmsrc;
}
/* Finish off any leftovers with scalar operations. */
while (i) {
*dst = ((float)(*src >> 8)) * DIVBY8388607;
i--;
src++;
dst++;
--i;
_mm_store_ss(&dst[i], _mm_mul_ss(_mm_cvt_si2ss(_mm_setzero_ps(), src[i]), scaler));
}
}
static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S8_SSE2(Sint8 *dst, const float *src, int num_samples)
{
int i;
int i = 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)");
/* Get dst aligned to 16 bytes */
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
const float sample = *src;
if (sample >= 1.0f) {
*dst = 127;
} else if (sample <= -1.0f) {
*dst = -128;
} else {
*dst = (Sint8)(sample * 127.0f);
}
while (i >= 16) {
const __m128 floats1 = _mm_loadu_ps(&src[0]);
const __m128 floats2 = _mm_loadu_ps(&src[4]);
const __m128 floats3 = _mm_loadu_ps(&src[8]);
const __m128 floats4 = _mm_loadu_ps(&src[12]);
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 ints4 = _mm_castps_si128(_mm_add_ps(floats4, offset));
const __m128i shorts1 = _mm_and_si128(_mm_packs_epi16(ints1, ints2), mask);
const __m128i shorts2 = _mm_and_si128(_mm_packs_epi16(ints3, ints4), mask);
const __m128i bytes = _mm_packus_epi16(shorts1, shorts2);
_mm_storeu_si128((__m128i*)dst, bytes);
i -= 16;
src += 16;
dst += 16;
}
SDL_assert(!i || !(((size_t)dst) & 15));
/* Make sure src is aligned too. */
if (!(((size_t)src) & 15)) {
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
const __m128 one = _mm_set1_ps(1.0f);
const __m128 negone = _mm_set1_ps(-1.0f);
const __m128 mulby127 = _mm_set1_ps(127.0f);
__m128i *mmdst = (__m128i *)dst;
while (i >= 16) { /* 16 * float32 */
const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 4)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 8)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 12)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
_mm_store_si128(mmdst, _mm_packs_epi16(_mm_packs_epi32(ints1, ints2), _mm_packs_epi32(ints3, ints4))); /* pack down, store out. */
i -= 16;
src += 16;
mmdst++;
}
dst = (Sint8 *)mmdst;
}
/* Finish off any leftovers with scalar operations. */
while (i) {
const float sample = *src;
if (sample >= 1.0f) {
*dst = 127;
} else if (sample <= -1.0f) {
*dst = -128;
} else {
*dst = (Sint8)(sample * 127.0f);
}
i--;
src++;
dst++;
const __m128i ints = _mm_castps_si128(_mm_add_ss(_mm_load_ss(src), offset));
*dst = (Sint8)(_mm_cvtsi128_si32(_mm_packs_epi16(ints, ints)) & 0xFF);
--i;
++src;
++dst;
}
}
static void SDL_TARGETING("sse2") SDL_Convert_F32_to_U8_SSE2(Uint8 *dst, const float *src, int num_samples)
{
int i;
int i = 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)");
/* Get dst aligned to 16 bytes */
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
const float sample = *src;
if (sample >= 1.0f) {
*dst = 255;
} else if (sample <= -1.0f) {
*dst = 0;
} else {
*dst = (Uint8)((sample + 1.0f) * 127.0f);
}
while (i >= 16) {
const __m128 floats1 = _mm_loadu_ps(&src[0]);
const __m128 floats2 = _mm_loadu_ps(&src[4]);
const __m128 floats3 = _mm_loadu_ps(&src[8]);
const __m128 floats4 = _mm_loadu_ps(&src[12]);
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 ints4 = _mm_castps_si128(_mm_add_ps(floats4, offset));
const __m128i shorts1 = _mm_and_si128(_mm_packus_epi16(ints1, ints2), mask);
const __m128i shorts2 = _mm_and_si128(_mm_packus_epi16(ints3, ints4), mask);
const __m128i bytes = _mm_packus_epi16(shorts1, shorts2);
_mm_storeu_si128((__m128i*)dst, bytes);
i -= 16;
src += 16;
dst += 16;
}
SDL_assert(!i || !(((size_t)dst) & 15));
/* Make sure src is aligned too. */
if (!(((size_t)src) & 15)) {
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
const __m128 one = _mm_set1_ps(1.0f);
const __m128 negone = _mm_set1_ps(-1.0f);
const __m128 mulby127 = _mm_set1_ps(127.0f);
__m128i *mmdst = (__m128i *)dst;
while (i >= 16) { /* 16 * float32 */
const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 4)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 8)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 12)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
_mm_store_si128(mmdst, _mm_packus_epi16(_mm_packs_epi32(ints1, ints2), _mm_packs_epi32(ints3, ints4))); /* pack down, store out. */
i -= 16;
src += 16;
mmdst++;
}
dst = (Uint8 *)mmdst;
}
/* Finish off any leftovers with scalar operations. */
while (i) {
const float sample = *src;
if (sample >= 1.0f) {
*dst = 255;
} else if (sample <= -1.0f) {
*dst = 0;
} else {
*dst = (Uint8)((sample + 1.0f) * 127.0f);
}
i--;
src++;
dst++;
const __m128i ints = _mm_castps_si128(_mm_add_ss(_mm_load_ss(src), offset));
*dst = (Uint8)(_mm_cvtsi128_si32(_mm_packus_epi16(ints, ints)) & 0xFF);
--i;
++src;
++dst;
}
}
static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S16_SSE2(Sint16 *dst, const float *src, int num_samples)
{
int i;
int i = 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)");
/* Get dst aligned to 16 bytes */
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
const float sample = *src;
if (sample >= 1.0f) {
*dst = 32767;
} else if (sample <= -1.0f) {
*dst = -32768;
} else {
*dst = (Sint16)(sample * 32767.0f);
}
while (i >= 16) {
const __m128 floats1 = _mm_loadu_ps(&src[0]);
const __m128 floats2 = _mm_loadu_ps(&src[4]);
const __m128 floats3 = _mm_loadu_ps(&src[8]);
const __m128 floats4 = _mm_loadu_ps(&src[12]);
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 ints4 = _mm_sub_epi32(_mm_castps_si128(_mm_add_ps(floats4, offset)), _mm_castps_si128(offset));
const __m128i shorts1 = _mm_packs_epi32(ints1, ints2);
const __m128i shorts2 = _mm_packs_epi32(ints3, ints4);
_mm_storeu_si128((__m128i*)&dst[0], shorts1);
_mm_storeu_si128((__m128i*)&dst[8], shorts2);
i -= 16;
src += 16;
dst += 16;
}
SDL_assert(!i || !(((size_t)dst) & 15));
/* Make sure src is aligned too. */
if (!(((size_t)src) & 15)) {
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
const __m128 one = _mm_set1_ps(1.0f);
const __m128 negone = _mm_set1_ps(-1.0f);
const __m128 mulby32767 = _mm_set1_ps(32767.0f);
__m128i *mmdst = (__m128i *)dst;
while (i >= 8) { /* 8 * float32 */
const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby32767)); /* load 4 floats, clamp, convert to sint32 */
const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 4)), one), mulby32767)); /* load 4 floats, clamp, convert to sint32 */
_mm_store_si128(mmdst, _mm_packs_epi32(ints1, ints2)); /* pack to sint16, store out. */
i -= 8;
src += 8;
mmdst++;
}
dst = (Sint16 *)mmdst;
}
/* Finish off any leftovers with scalar operations. */
while (i) {
const float sample = *src;
if (sample >= 1.0f) {
*dst = 32767;
} else if (sample <= -1.0f) {
*dst = -32768;
} else {
*dst = (Sint16)(sample * 32767.0f);
}
i--;
src++;
dst++;
const __m128i ints = _mm_sub_epi32(_mm_castps_si128(_mm_add_ss(_mm_load_ss(src), offset)), _mm_castps_si128(offset));
*dst = (Sint16)(_mm_cvtsi128_si32(_mm_packs_epi32(ints, ints)) & 0xFFFF);
--i;
++src;
++dst;
}
}
static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S32_SSE2(Sint32 *dst, const float *src, int num_samples)
{
int i;
int i = 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)");
/* Get dst aligned to 16 bytes */
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
const float sample = *src;
if (sample >= 1.0f) {
*dst = 2147483647;
} else if (sample <= -1.0f) {
*dst = (Sint32)-2147483648LL;
} else {
*dst = ((Sint32)(sample * 8388607.0f)) << 8;
}
while (i >= 16) {
const __m128 floats1 = _mm_loadu_ps(&src[0]);
const __m128 floats2 = _mm_loadu_ps(&src[4]);
const __m128 floats3 = _mm_loadu_ps(&src[8]);
const __m128 floats4 = _mm_loadu_ps(&src[12]);
const __m128 values1 = _mm_mul_ps(floats1, limit);
const __m128 values2 = _mm_mul_ps(floats2, limit);
const __m128 values3 = _mm_mul_ps(floats3, limit);
const __m128 values4 = _mm_mul_ps(floats4, limit);
const __m128i ints1 = _mm_xor_si128(_mm_cvttps_epi32(values1), _mm_castps_si128(_mm_cmpge_ps(values1, limit)));
const __m128i ints2 = _mm_xor_si128(_mm_cvttps_epi32(values2), _mm_castps_si128(_mm_cmpge_ps(values2, limit)));
const __m128i ints3 = _mm_xor_si128(_mm_cvttps_epi32(values3), _mm_castps_si128(_mm_cmpge_ps(values3, limit)));
const __m128i ints4 = _mm_xor_si128(_mm_cvttps_epi32(values4), _mm_castps_si128(_mm_cmpge_ps(values4, limit)));
_mm_storeu_si128((__m128i*)&dst[0], ints1);
_mm_storeu_si128((__m128i*)&dst[4], ints2);
_mm_storeu_si128((__m128i*)&dst[8], ints3);
_mm_storeu_si128((__m128i*)&dst[12], ints4);
i -= 16;
src += 16;
dst += 16;
}
SDL_assert(!i || !(((size_t)dst) & 15));
SDL_assert(!i || !(((size_t)src) & 15));
{
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
const __m128 one = _mm_set1_ps(1.0f);
const __m128 negone = _mm_set1_ps(-1.0f);
const __m128 mulby8388607 = _mm_set1_ps(8388607.0f);
__m128i *mmdst = (__m128i *)dst;
while (i >= 4) { /* 4 * float32 */
_mm_store_si128(mmdst, _mm_slli_epi32(_mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby8388607)), 8)); /* load 4 floats, clamp, convert to sint32 */
i -= 4;
src += 4;
mmdst++;
}
dst = (Sint32 *)mmdst;
}
/* Finish off any leftovers with scalar operations. */
while (i) {
const float sample = *src;
if (sample >= 1.0f) {
*dst = 2147483647;
} else if (sample <= -1.0f) {
*dst = (Sint32)-2147483648LL;
} else {
*dst = ((Sint32)(sample * 8388607.0f)) << 8;
}
i--;
src++;
dst++;
const __m128 floats = _mm_load_ss(src);
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 = (Sint32)_mm_cvtsi128_si32(ints);
--i;
++src;
++dst;
}
}
#endif
#ifdef SDL_NEON_INTRINSICS
#define DIVBY128 0.0078125f /* 0x1p-7f */
#define DIVBY32768 0.000030517578125f /* 0x1p-15f */
#define DIVBY8388607 0.00000011920930376163766f /* 0x1.000002p-23f */
static void SDL_Convert_S8_to_F32_NEON(float *dst, const Sint8 *src, int num_samples)
{
int i;

View File

@ -131,7 +131,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
}
} break;
case SDL_AUDIO_S16LSB:
case SDL_AUDIO_S16LE:
{
Sint16 src1, src2;
int dst_sample;
@ -155,7 +155,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
}
} break;
case SDL_AUDIO_S16MSB:
case SDL_AUDIO_S16BE:
{
Sint16 src1, src2;
int dst_sample;
@ -179,7 +179,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
}
} break;
case SDL_AUDIO_S32LSB:
case SDL_AUDIO_S32LE:
{
const Uint32 *src32 = (Uint32 *)src;
Uint32 *dst32 = (Uint32 *)dst;
@ -204,7 +204,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
}
} break;
case SDL_AUDIO_S32MSB:
case SDL_AUDIO_S32BE:
{
const Uint32 *src32 = (Uint32 *)src;
Uint32 *dst32 = (Uint32 *)dst;
@ -229,7 +229,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
}
} break;
case SDL_AUDIO_F32LSB:
case SDL_AUDIO_F32LE:
{
const float fmaxvolume = 1.0f / ((float)SDL_MIX_MAXVOLUME);
const float fvolume = (float)volume;
@ -257,7 +257,7 @@ int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
}
} break;
case SDL_AUDIO_F32MSB:
case SDL_AUDIO_F32BE:
{
const float fmaxvolume = 1.0f / ((float)SDL_MIX_MAXVOLUME);
const float fvolume = (float)volume;

View File

@ -24,8 +24,6 @@
#ifndef SDL_sysaudio_h_
#define SDL_sysaudio_h_
#include "../SDL_dataqueue.h"
#define DEBUG_AUDIOSTREAM 0
#define DEBUG_AUDIO_CONVERT 0
@ -58,6 +56,8 @@ extern void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_sam
#define DEFAULT_AUDIO_CAPTURE_CHANNELS 1
#define DEFAULT_AUDIO_CAPTURE_FREQUENCY 44100
#define AUDIO_SPECS_EQUAL(x, y) (((x).format == (y).format) && ((x).channels == (y).channels) && ((x).freq == (y).freq))
typedef struct SDL_AudioDevice SDL_AudioDevice;
typedef struct SDL_LogicalAudioDevice SDL_LogicalAudioDevice;
@ -70,8 +70,9 @@ extern void SDL_QuitAudio(void);
// Function to get a list of audio formats, ordered most similar to `format` to least, 0-terminated. Don't free results.
const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format);
// Must be called at least once before using converters (SDL_CreateAudioStream will call it !!! FIXME but probably shouldn't).
// 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
@ -101,7 +102,7 @@ extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByCallback(SDL_bool (*callbac
extern void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device);
// Backends can call this to get a standardized name for a thread to power a specific audio device.
char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen);
extern char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen);
// These functions are the heart of the audio threads. Backends can call them directly if they aren't using the SDL-provided thread.
@ -113,6 +114,14 @@ extern SDL_bool SDL_CaptureAudioThreadIterate(SDL_AudioDevice *device);
extern void SDL_CaptureAudioThreadShutdown(SDL_AudioDevice *device);
extern void SDL_AudioThreadFinalize(SDL_AudioDevice *device);
// 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,
void *dst, SDL_AudioFormat dst_format, int dst_channels, void* scratch);
// 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);
typedef struct SDL_AudioDriverImpl
{
void (*DetectDevices)(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
@ -120,7 +129,7 @@ typedef struct SDL_AudioDriverImpl
void (*ThreadInit)(SDL_AudioDevice *device); // Called by audio thread at start
void (*ThreadDeinit)(SDL_AudioDevice *device); // Called by audio thread at end
void (*WaitDevice)(SDL_AudioDevice *device);
void (*PlayDevice)(SDL_AudioDevice *device, const Uint8 *buffer, int buflen); // buffer and buflen are always from GetDeviceBuf, passed here for convenience.
int (*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);
void (*WaitCaptureDevice)(SDL_AudioDevice *device);
int (*CaptureFromDevice)(SDL_AudioDevice *device, void *buffer, int buflen);
@ -145,6 +154,7 @@ typedef struct SDL_AudioDriver
SDL_RWLock *device_list_lock; // A mutex for device detection
SDL_AudioDevice *output_devices; // the list of currently-available audio output devices.
SDL_AudioDevice *capture_devices; // the list of currently-available audio capture devices.
SDL_AudioStream *existing_streams; // a list of all existing SDL_AudioStreams.
SDL_AudioDeviceID default_output_device_id;
SDL_AudioDeviceID default_capture_device_id;
SDL_AtomicInt output_device_count;
@ -153,46 +163,41 @@ typedef struct SDL_AudioDriver
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_DataQueue *queue;
SDL_Mutex *lock; // this is just a copy of `queue`'s mutex. We share a lock.
SDL_Mutex* lock;
SDL_AudioStreamRequestCallback get_callback;
SDL_AudioStreamCallback get_callback;
void *get_callback_userdata;
SDL_AudioStreamRequestCallback put_callback;
SDL_AudioStreamCallback put_callback;
void *put_callback_userdata;
Uint8 *work_buffer; // used for scratch space during data conversion/resampling.
Uint8 *history_buffer; // history for left padding and future sample rate changes.
Uint8 *future_buffer; // stuff that left the queue for the right padding and will be next read's data.
float *left_padding; // left padding for resampling.
float *right_padding; // right padding for resampling.
SDL_bool flushed;
size_t work_buffer_allocation;
size_t history_buffer_allocation;
size_t future_buffer_allocation;
size_t resampler_padding_allocation;
int resampler_padding_frames;
int history_buffer_frames;
int future_buffer_filled_frames;
SDL_AudioSpec src_spec;
SDL_AudioSpec dst_spec;
float freq_ratio;
int src_sample_frame_size;
int dst_sample_frame_size;
int max_sample_frame_size;
struct SDL_AudioQueue* queue;
Uint64 total_bytes_queued;
int pre_resample_channels;
int packetlen;
SDL_AudioSpec input_spec; // The spec of input data currently being processed
Sint64 resample_offset;
Uint8 *work_buffer; // used for scratch space during data conversion/resampling.
size_t work_buffer_allocation;
Uint8 *history_buffer; // history for left padding and future sample rate changes.
size_t history_buffer_allocation;
SDL_bool simplified; // SDL_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
@ -214,7 +219,16 @@ struct SDL_LogicalAudioDevice
SDL_AudioStream *bound_streams;
// SDL_TRUE if this was opened as a default device.
SDL_bool is_default;
SDL_bool opened_as_default;
// SDL_TRUE if device was opened with SDL_OpenAudioDeviceStream (so it forbids binding changes, etc).
SDL_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;
@ -263,14 +277,22 @@ struct SDL_AudioDevice
// SDL_TRUE if this is a capture device instead of an output device
SDL_bool iscapture;
// Scratch buffer used for mixing.
// SDL_TRUE if audio thread can skip silence/mix/convert stages and just do a basic memcpy.
SDL_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;
// SDL_TRUE if this physical device is currently opened by the backend.
SDL_bool is_opened;
SDL_bool currently_opened;
// Data private to this driver
struct SDL_PrivateAudioData *hidden;
@ -316,7 +338,4 @@ extern AudioBootStrap N3DSAUDIO_bootstrap;
extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap;
extern AudioBootStrap QSAAUDIO_bootstrap;
extern SDL_AudioDevice *get_audio_dev(SDL_AudioDeviceID id);
extern int get_max_num_audio_dev(void);
#endif // SDL_sysaudio_h_

View File

@ -2039,10 +2039,10 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 *
case ALAW_CODE:
case MULAW_CODE:
/* These can be easily stored in the byte order of the system. */
spec->format = SDL_AUDIO_S16SYS;
spec->format = SDL_AUDIO_S16;
break;
case IEEE_FLOAT_CODE:
spec->format = SDL_AUDIO_F32LSB;
spec->format = SDL_AUDIO_F32LE;
break;
case PCM_CODE:
switch (format->bitspersample) {
@ -2050,11 +2050,11 @@ static int WaveLoad(SDL_RWops *src, WaveFile *file, SDL_AudioSpec *spec, Uint8 *
spec->format = SDL_AUDIO_U8;
break;
case 16:
spec->format = SDL_AUDIO_S16LSB;
spec->format = SDL_AUDIO_S16LE;
break;
case 24: /* Has been shifted to 32 bits. */
case 32:
spec->format = SDL_AUDIO_S32LSB;
spec->format = SDL_AUDIO_S32LE;
break;
default:
/* Just in case something unexpected happened in the checks. */

View File

@ -111,13 +111,14 @@ static void AAUDIO_WaitDevice(SDL_AudioDevice *device)
SDL_WaitSemaphore(device->hidden->semaphore);
}
static void AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
static int AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{
// AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here.
if (SDL_AtomicGet(&device->hidden->error_callback_triggered)) {
SDL_AtomicSet(&device->hidden->error_callback_triggered, 0);
SDL_AudioDeviceDisconnected(device);
return -1;
}
return 0;
}
// no need for a FlushCapture implementation, just don't read mixbuf until the next iteration.
@ -197,9 +198,9 @@ static int AAUDIO_OpenDevice(SDL_AudioDevice *device)
const aaudio_direction_t direction = (iscapture ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT);
ctx.AAudioStreamBuilder_setDirection(builder, direction);
aaudio_format_t format;
if ((device->spec.format == SDL_AUDIO_S32SYS) && (SDL_GetAndroidSDKVersion() >= 31)) {
if ((device->spec.format == SDL_AUDIO_S32) && (SDL_GetAndroidSDKVersion() >= 31)) {
format = AAUDIO_FORMAT_PCM_I32;
} else if (device->spec.format == SDL_AUDIO_F32SYS) {
} 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.
@ -244,11 +245,11 @@ static int AAUDIO_OpenDevice(SDL_AudioDevice *device)
format = ctx.AAudioStream_getFormat(hidden->stream);
if (format == AAUDIO_FORMAT_PCM_I16) {
device->spec.format = SDL_AUDIO_S16SYS;
device->spec.format = SDL_AUDIO_S16;
} else if (format == AAUDIO_FORMAT_PCM_I32) {
device->spec.format = SDL_AUDIO_S32SYS;
device->spec.format = SDL_AUDIO_S32;
} else if (format == AAUDIO_FORMAT_PCM_FLOAT) {
device->spec.format = SDL_AUDIO_F32SYS;
device->spec.format = SDL_AUDIO_F32;
} else {
return SDL_SetError("Got unexpected audio format %d from AAudioStream_getFormat", (int) format);
}

View File

@ -351,12 +351,11 @@ static void ALSA_WaitDevice(SDL_AudioDevice *device)
}
}
static void ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
static int ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{
SDL_assert(buffer == device->hidden->mixbuf);
Uint8 *sample_buf = device->hidden->mixbuf;
const int frame_size = ((SDL_AUDIO_BITSIZE(device->spec.format)) / 8) *
device->spec.channels;
const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec);
snd_pcm_uframes_t frames_left = (snd_pcm_uframes_t) (buflen / frame_size);
device->hidden->swizzle_func(device, sample_buf, frames_left);
@ -378,8 +377,7 @@ static void ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int bu
SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
"ALSA write failed (unrecoverable): %s",
ALSA_snd_strerror(status));
SDL_AudioDeviceDisconnected(device);
return;
return -1;
}
continue;
} else if (status == 0) {
@ -391,6 +389,8 @@ static void ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int bu
sample_buf += status * frame_size;
frames_left -= status;
}
return 0;
}
static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
@ -401,8 +401,7 @@ static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
static int ALSA_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
{
Uint8 *sample_buf = (Uint8 *)buffer;
const int frame_size = ((SDL_AUDIO_BITSIZE(device->spec.format)) / 8) *
device->spec.channels;
const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec);
const int total_frames = buflen / frame_size;
snd_pcm_uframes_t frames_left = total_frames;
@ -564,22 +563,22 @@ static int ALSA_OpenDevice(SDL_AudioDevice *device)
case SDL_AUDIO_S8:
format = SND_PCM_FORMAT_S8;
break;
case SDL_AUDIO_S16LSB:
case SDL_AUDIO_S16LE:
format = SND_PCM_FORMAT_S16_LE;
break;
case SDL_AUDIO_S16MSB:
case SDL_AUDIO_S16BE:
format = SND_PCM_FORMAT_S16_BE;
break;
case SDL_AUDIO_S32LSB:
case SDL_AUDIO_S32LE:
format = SND_PCM_FORMAT_S32_LE;
break;
case SDL_AUDIO_S32MSB:
case SDL_AUDIO_S32BE:
format = SND_PCM_FORMAT_S32_BE;
break;
case SDL_AUDIO_F32LSB:
case SDL_AUDIO_F32LE:
format = SND_PCM_FORMAT_FLOAT_LE;
break;
case SDL_AUDIO_F32MSB:
case SDL_AUDIO_F32BE:
format = SND_PCM_FORMAT_FLOAT_BE;
break;
default:

View File

@ -87,9 +87,10 @@ static int ANDROIDAUDIO_OpenDevice(SDL_AudioDevice *device)
// !!! FIXME: this needs a WaitDevice implementation.
static void ANDROIDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
static int ANDROIDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{
Android_JNI_WriteAudioBuffer();
return 0;
}
static Uint8 *ANDROIDAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)

View File

@ -27,7 +27,7 @@
#include "SDL_coreaudio.h"
#include "../../thread/SDL_systhread.h"
#define DEBUG_COREAUDIO 1
#define DEBUG_COREAUDIO 0
#if DEBUG_COREAUDIO
#define CHECK_RESULT(msg) \
@ -525,7 +525,7 @@ static SDL_bool UpdateAudioSession(SDL_AudioDevice *device, SDL_bool open, SDL_b
#endif
static void COREAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
static int 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 OutputBufferReadyCallback
@ -533,6 +533,7 @@ static void COREAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, i
current_buffer->mAudioDataByteSize = current_buffer->mAudioDataBytesCapacity;
device->hidden->current_buffer = NULL;
AudioQueueEnqueueBuffer(device->hidden->audioQueue, current_buffer, 0, NULL);
return 0;
}
static Uint8 *COREAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
@ -875,12 +876,12 @@ static int COREAUDIO_OpenDevice(SDL_AudioDevice *device)
switch (test_format) {
case SDL_AUDIO_U8:
case SDL_AUDIO_S8:
case SDL_AUDIO_S16LSB:
case SDL_AUDIO_S16MSB:
case SDL_AUDIO_S32LSB:
case SDL_AUDIO_S32MSB:
case SDL_AUDIO_F32LSB:
case SDL_AUDIO_F32MSB:
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:

View File

@ -179,7 +179,7 @@ static BOOL CALLBACK FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVO
if (str != NULL) {
LPGUID cpyguid = (LPGUID)SDL_malloc(sizeof(GUID));
if (cpyguid) {
SDL_memcpy(cpyguid, guid, sizeof(GUID));
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
@ -285,11 +285,14 @@ static void DSOUND_WaitDevice(SDL_AudioDevice *device)
}
}
static void DSOUND_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
static int DSOUND_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{
// Unlock the buffer, allowing it to play
SDL_assert(buflen == device->buffer_size);
IDirectSoundBuffer_Unlock(device->hidden->mixbuf, (LPVOID) buffer, buflen, NULL, 0);
if (IDirectSoundBuffer_Unlock(device->hidden->mixbuf, (LPVOID) buffer, buflen, NULL, 0) != DS_OK) {
return -1;
}
return 0;
}
static Uint8 *DSOUND_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
@ -378,7 +381,7 @@ static int DSOUND_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int b
return -1;
}
SDL_assert(ptr1len == buflen);
SDL_assert(ptr1len == (DWORD)buflen);
SDL_assert(ptr2 == NULL);
SDL_assert(ptr2len == 0);
@ -614,7 +617,7 @@ static int DSOUND_OpenDevice(SDL_AudioDevice *device)
}
wfmt.Format.wBitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
wfmt.Format.nChannels = device->spec.channels;
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;

View File

@ -40,15 +40,16 @@ static void DISKAUDIO_WaitDevice(SDL_AudioDevice *device)
SDL_Delay(device->hidden->io_delay);
}
static void DISKAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
static int DISKAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
{
const int written = (int)SDL_RWwrite(device->hidden->io, buffer, (size_t)buffer_size);
if (written != buffer_size) { // If we couldn't write, assume fatal error for now
SDL_AudioDeviceDisconnected(device);
return -1;
}
#ifdef DEBUG_AUDIO
SDL_Log("DISKAUDIO: Wrote %d bytes of audio data", (int) written);
#endif
return 0;
}
static Uint8 *DISKAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)

View File

@ -111,12 +111,12 @@ static int DSP_OpenDevice(SDL_AudioDevice *device)
format = AFMT_U8;
}
break;
case SDL_AUDIO_S16LSB:
case SDL_AUDIO_S16LE:
if (value & AFMT_S16_LE) {
format = AFMT_S16_LE;
}
break;
case SDL_AUDIO_S16MSB:
case SDL_AUDIO_S16BE:
if (value & AFMT_S16_BE) {
format = AFMT_S16_BE;
}
@ -225,17 +225,17 @@ static void DSP_WaitDevice(SDL_AudioDevice *device)
}
}
static void DSP_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
static int 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");
SDL_AudioDeviceDisconnected(device);
return;
return -1;
}
#ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", h->mixlen);
#endif
return 0;
}
static Uint8 *DSP_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)

View File

@ -36,9 +36,9 @@ static Uint8 *EMSCRIPTENAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_
return device->hidden->mixbuf;
}
static void EMSCRIPTENAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
static int EMSCRIPTENAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
{
const int framelen = (SDL_AUDIO_BITSIZE(device->spec.format) / 8) * device->spec.channels;
const int framelen = SDL_AUDIO_FRAMESIZE(device->spec);
MAIN_THREAD_EM_ASM({
var SDL3 = Module['SDL3'];
var numChannels = SDL3.audio.currentOutputBuffer['numberOfChannels'];
@ -53,11 +53,7 @@ static void EMSCRIPTENAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buf
}
}
}, buffer, buffer_size / framelen);
}
static void HandleAudioProcess(SDL_AudioDevice *device) // this fires when the main thread is idle.
{
SDL_OutputAudioThreadIterate(device);
return 0;
}
@ -92,11 +88,6 @@ static int EMSCRIPTENAUDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buff
return buflen;
}
static void HandleCaptureProcess(SDL_AudioDevice *device) // this fires when the main thread is idle.
{
SDL_CaptureAudioThreadIterate(device);
}
static void EMSCRIPTENAUDIO_CloseDevice(SDL_AudioDevice *device)
{
if (!device->hidden) {
@ -107,32 +98,28 @@ static void EMSCRIPTENAUDIO_CloseDevice(SDL_AudioDevice *device)
var SDL3 = Module['SDL3'];
if ($0) {
if (SDL3.capture.silenceTimer !== undefined) {
clearTimeout(SDL3.capture.silenceTimer);
clearInterval(SDL3.capture.silenceTimer);
}
if (SDL3.capture.stream !== undefined) {
var tracks = SDL3.capture.stream.getAudioTracks();
for (var i = 0; i < tracks.length; i++) {
SDL3.capture.stream.removeTrack(tracks[i]);
}
SDL3.capture.stream = undefined;
}
if (SDL3.capture.scriptProcessorNode !== undefined) {
SDL3.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) {};
SDL3.capture.scriptProcessorNode.disconnect();
SDL3.capture.scriptProcessorNode = undefined;
}
if (SDL3.capture.mediaStreamNode !== undefined) {
SDL3.capture.mediaStreamNode.disconnect();
SDL3.capture.mediaStreamNode = undefined;
}
if (SDL3.capture.silenceBuffer !== undefined) {
SDL3.capture.silenceBuffer = undefined
}
SDL3.capture = undefined;
} else {
if (SDL3.audio.scriptProcessorNode != undefined) {
SDL3.audio.scriptProcessorNode.disconnect();
SDL3.audio.scriptProcessorNode = undefined;
}
if (SDL3.audio.silenceTimer !== undefined) {
clearInterval(SDL3.audio.silenceTimer);
}
SDL3.audio = undefined;
}
@ -174,7 +161,9 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device)
SDL3.audioContext = new webkitAudioContext();
}
if (SDL3.audioContext) {
autoResumeAudioContext(SDL3.audioContext);
if ((typeof navigator.userActivation) === 'undefined') { // Firefox doesn't have this (as of August 2023), use autoResumeAudioContext instead.
autoResumeAudioContext(SDL3.audioContext);
}
}
}
return SDL3.audioContext === undefined ? -1 : 0;
@ -227,8 +216,9 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device)
var have_microphone = function(stream) {
//console.log('SDL audio capture: we have a microphone! Replacing silence callback.');
if (SDL3.capture.silenceTimer !== undefined) {
clearTimeout(SDL3.capture.silenceTimer);
clearInterval(SDL3.capture.silenceTimer);
SDL3.capture.silenceTimer = undefined;
SDL3.capture.silenceBuffer = undefined
}
SDL3.capture.mediaStreamNode = SDL3.audioContext.createMediaStreamSource(stream);
SDL3.capture.scriptProcessorNode = SDL3.audioContext.createScriptProcessor($1, $0, 1);
@ -255,14 +245,14 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device)
dynCall('vi', $2, [$3]);
};
SDL3.capture.silenceTimer = setTimeout(silence_callback, ($1 / SDL3.audioContext.sampleRate) * 1000);
SDL3.capture.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, HandleCaptureProcess, device);
}, device->spec.channels, device->sample_frames, SDL_CaptureAudioThreadIterate, device);
} else {
// setup a ScriptProcessorNode
MAIN_THREAD_EM_ASM({
@ -270,11 +260,38 @@ static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *device)
SDL3.audio.scriptProcessorNode = SDL3.audioContext['createScriptProcessor']($1, 0, $0);
SDL3.audio.scriptProcessorNode['onaudioprocess'] = function (e) {
if ((SDL3 === undefined) || (SDL3.audio === undefined)) { return; }
// if we're actually running the node, we don't need the fake callback anymore, so kill it.
if (SDL3.audio.silenceTimer !== undefined) {
clearInterval(SDL3.audio.silenceTimer);
SDL3.audio.silenceTimer = undefined;
SDL3.audio.silenceBuffer = undefined;
}
SDL3.audio.currentOutputBuffer = e['outputBuffer'];
dynCall('vi', $2, [$3]);
};
SDL3.audio.scriptProcessorNode['connect'](SDL3.audioContext['destination']);
}, device->spec.channels, device->sample_frames, HandleAudioProcess, device);
if (SDL3.audioContext.state === 'suspended') { // uhoh, autoplay is blocked.
SDL3.audio.silenceBuffer = SDL3.audioContext.createBuffer($0, $1, SDL3.audioContext.sampleRate);
SDL3.audio.silenceBuffer.getChannelData(0).fill(0.0);
var silence_callback = function() {
if ((typeof navigator.userActivation) !== 'undefined') { // Almost everything modern except Firefox (as of August 2023)
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.currentOutputBuffer = SDL3.audio.silenceBuffer;
dynCall('vi', $2, [$3]);
SDL3.audio.currentOutputBuffer = undefined;
};
SDL3.audio.silenceTimer = setInterval(silence_callback, ($1 / SDL3.audioContext.sampleRate) * 1000);
}
}, device->spec.channels, device->sample_frames, SDL_OutputAudioThreadIterate, device);
}
return 0;

View File

@ -46,13 +46,14 @@ static Uint8 *HAIKUAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
return device->hidden->current_buffer;
}
static void HAIKUAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
static int 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 0;
}
// The Haiku callback for handling the audio buffer
@ -130,29 +131,29 @@ static int HAIKUAUDIO_OpenDevice(SDL_AudioDevice *device)
format.format = media_raw_audio_format::B_AUDIO_UCHAR;
break;
case SDL_AUDIO_S16LSB:
case SDL_AUDIO_S16LE:
format.format = media_raw_audio_format::B_AUDIO_SHORT;
break;
case SDL_AUDIO_S16MSB:
case SDL_AUDIO_S16BE:
format.format = media_raw_audio_format::B_AUDIO_SHORT;
format.byte_order = B_MEDIA_BIG_ENDIAN;
break;
case SDL_AUDIO_S32LSB:
case SDL_AUDIO_S32LE:
format.format = media_raw_audio_format::B_AUDIO_INT;
break;
case SDL_AUDIO_S32MSB:
case SDL_AUDIO_S32BE:
format.format = media_raw_audio_format::B_AUDIO_INT;
format.byte_order = B_MEDIA_BIG_ENDIAN;
break;
case SDL_AUDIO_F32LSB:
case SDL_AUDIO_F32LE:
format.format = media_raw_audio_format::B_AUDIO_FLOAT;
break;
case SDL_AUDIO_F32MSB:
case SDL_AUDIO_F32BE:
format.format = media_raw_audio_format::B_AUDIO_FLOAT;
format.byte_order = B_MEDIA_BIG_ENDIAN;
break;

View File

@ -149,7 +149,7 @@ static int jackProcessPlaybackCallback(jack_nframes_t nframes, void *arg)
return 0;
}
static void JACK_PlayDevice(SDL_AudioDevice *device, const Uint8 *ui8buffer, int buflen)
static int JACK_PlayDevice(SDL_AudioDevice *device, const Uint8 *ui8buffer, int buflen)
{
const float *buffer = (float *) ui8buffer;
jack_port_t **ports = device->hidden->sdlports;
@ -167,6 +167,8 @@ static void JACK_PlayDevice(SDL_AudioDevice *device, const Uint8 *ui8buffer, int
}
}
}
return 0;
}
static Uint8 *JACK_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
@ -307,7 +309,7 @@ static int JACK_OpenDevice(SDL_AudioDevice *device)
/* !!! FIXME: docs say about buffer size: "This size may change, clients that depend on it must register a bufsize_callback so they will be notified if it does." */
/* Jack pretty much demands what it wants. */
device->spec.format = SDL_AUDIO_F32SYS;
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);

View File

@ -161,7 +161,7 @@ static int N3DSAUDIO_OpenDevice(SDL_AudioDevice *device)
SDL_memset(device->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS);
const int sample_frame_size = device->spec.channels * (SDL_AUDIO_BITSIZE(device->spec.format) / 8);
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;
@ -176,7 +176,7 @@ static int N3DSAUDIO_OpenDevice(SDL_AudioDevice *device)
return 0;
}
static void N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
static int N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{
contextLock(device);
@ -185,7 +185,7 @@ static void N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, i
if (device->hidden->isCancelled ||
device->hidden->waveBuf[nextbuf].status != NDSP_WBUF_FREE) {
contextUnlock(device);
return;
return 0; // !!! FIXME: is this a fatal error? If so, this should return -1.
}
device->hidden->nextbuf = (nextbuf + 1) % NUM_BUFFERS;
@ -196,6 +196,8 @@ static void N3DSAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, i
DSP_FlushDataCache(device->hidden->waveBuf[nextbuf].data_vaddr, buflen);
ndspChnWaveBufAdd(0, &device->hidden->waveBuf[nextbuf]);
return 0;
}
static void N3DSAUDIO_WaitDevice(SDL_AudioDevice *device)

View File

@ -130,7 +130,7 @@ static void NETBSDAUDIO_WaitDevice(SDL_AudioDevice *device)
SDL_AudioDeviceDisconnected(device);
return;
}
const size_t remain = (size_t)((iscapture ? info.record.seek : info.play.seek) * (SDL_AUDIO_BITSIZE(device->spec.format) / 8));
const size_t remain = (size_t)((iscapture ? info.record.seek : info.play.seek) * SDL_AUDIO_BYTESIZE(device->spec.format));
if (!iscapture && (remain >= device->buffer_size)) {
SDL_Delay(10);
} else if (iscapture && (remain < device->buffer_size)) {
@ -141,20 +141,18 @@ static void NETBSDAUDIO_WaitDevice(SDL_AudioDevice *device)
}
}
static void NETBSDAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
static int 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 == -1) {
// Non recoverable error has occurred. It should be reported!!!
SDL_AudioDeviceDisconnected(device);
perror("audio");
return;
if (written != buflen) { // Treat even partial writes as fatal errors.
return -1;
}
#ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
#endif
return 0;
}
static Uint8 *NETBSDAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
@ -183,7 +181,7 @@ static void NETBSDAUDIO_FlushCapture(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_BITSIZE(device->spec.format) / 8));
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);
@ -250,16 +248,16 @@ static int NETBSDAUDIO_OpenDevice(SDL_AudioDevice *device)
case SDL_AUDIO_S8:
encoding = AUDIO_ENCODING_SLINEAR;
break;
case SDL_AUDIO_S16LSB:
case SDL_AUDIO_S16LE:
encoding = AUDIO_ENCODING_SLINEAR_LE;
break;
case SDL_AUDIO_S16MSB:
case SDL_AUDIO_S16BE:
encoding = AUDIO_ENCODING_SLINEAR_BE;
break;
case SDL_AUDIO_S32LSB:
case SDL_AUDIO_S32LE:
encoding = AUDIO_ENCODING_SLINEAR_LE;
break;
case SDL_AUDIO_S32MSB:
case SDL_AUDIO_S32BE:
encoding = AUDIO_ENCODING_SLINEAR_BE;
break;
default:

View File

@ -248,7 +248,7 @@ static int openslES_CreatePCMRecorder(SDL_AudioDevice *device)
}
// Just go with signed 16-bit audio as it's the most compatible
device->spec.format = SDL_AUDIO_S16SYS;
device->spec.format = SDL_AUDIO_S16;
device->spec.channels = 1;
//device->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/
@ -424,12 +424,12 @@ static int openslES_CreatePCMPlayer(SDL_AudioDevice *device)
if (!test_format) {
// Didn't find a compatible format :
LOGI("No compatible audio format, using signed 16-bit audio");
test_format = SDL_AUDIO_S16SYS;
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_S16SYS;
device->spec.format = SDL_AUDIO_S16;
}
// Update the fragment size as size in bytes
@ -638,7 +638,7 @@ static void openslES_WaitDevice(SDL_AudioDevice *device)
SDL_WaitSemaphore(audiodata->playsem);
}
static void openslES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
static int openslES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{
struct SDL_PrivateAudioData *audiodata = device->hidden;
@ -657,6 +657,8 @@ static void openslES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, in
if (SL_RESULT_SUCCESS != result) {
SDL_PostSemaphore(audiodata->playsem);
}
return 0;
}
/// n playn sem

View File

@ -898,22 +898,22 @@ static void initialize_spa_info(const SDL_AudioSpec *spec, struct spa_audio_info
case SDL_AUDIO_S8:
info->format = SPA_AUDIO_FORMAT_S8;
break;
case SDL_AUDIO_S16LSB:
case SDL_AUDIO_S16LE:
info->format = SPA_AUDIO_FORMAT_S16_LE;
break;
case SDL_AUDIO_S16MSB:
case SDL_AUDIO_S16BE:
info->format = SPA_AUDIO_FORMAT_S16_BE;
break;
case SDL_AUDIO_S32LSB:
case SDL_AUDIO_S32LE:
info->format = SPA_AUDIO_FORMAT_S32_LE;
break;
case SDL_AUDIO_S32MSB:
case SDL_AUDIO_S32BE:
info->format = SPA_AUDIO_FORMAT_S32_BE;
break;
case SDL_AUDIO_F32LSB:
case SDL_AUDIO_F32LE:
info->format = SPA_AUDIO_FORMAT_F32_LE;
break;
case SDL_AUDIO_F32MSB:
case SDL_AUDIO_F32BE:
info->format = SPA_AUDIO_FORMAT_F32_BE;
break;
}
@ -940,7 +940,7 @@ static Uint8 *PIPEWIRE_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
return (Uint8 *) spa_buf->datas[0].data;
}
static void PIPEWIRE_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
static int 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;
@ -951,6 +951,8 @@ static void PIPEWIRE_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, in
PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf);
device->hidden->pw_buf = NULL;
return 0;
}
static void output_callback(void *data)
@ -1106,7 +1108,7 @@ static int PIPEWIRE_OpenDevice(SDL_AudioDevice *device)
}
/* Size of a single audio frame in bytes */
priv->stride = (SDL_AUDIO_BITSIZE(device->spec.format) / 8) * device->spec.channels;
priv->stride = SDL_AUDIO_FRAMESIZE(device->spec);
if (device->sample_frames < min_period) {
device->sample_frames = min_period;

View File

@ -85,9 +85,10 @@ static int PS2AUDIO_OpenDevice(SDL_AudioDevice *device)
return 0;
}
static void PS2AUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
static int PS2AUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{
audsrv_play_audio((char *)buffer, 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) ? -1 : 0;
}
static void PS2AUDIO_WaitDevice(SDL_AudioDevice *device)

View File

@ -47,7 +47,7 @@ static int PSPAUDIO_OpenDevice(SDL_AudioDevice *device)
}
// device only natively supports S16LSB
device->spec.format = SDL_AUDIO_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,
@ -106,14 +106,16 @@ static int PSPAUDIO_OpenDevice(SDL_AudioDevice *device)
return 0;
}
static void PSPAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
static int PSPAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{
int rc;
if (!isBasicAudioConfig(&device->spec)) {
SDL_assert(device->spec.channels == 2);
sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, (void *) buffer);
rc = sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, (void *) buffer);
} else {
sceAudioOutputPannedBlocking(device->hidden->channel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, (void *) buffer);
rc = sceAudioOutputPannedBlocking(device->hidden->channel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, (void *) buffer);
}
return (rc == 0) ? 0 : -1;
}
static void PSPAUDIO_WaitDevice(SDL_AudioDevice *device)

View File

@ -388,7 +388,7 @@ static void PULSEAUDIO_WaitDevice(SDL_AudioDevice *device)
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
}
static void PULSEAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
static int PULSEAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
{
struct SDL_PrivateAudioData *h = device->hidden;
@ -401,14 +401,14 @@ static void PULSEAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer,
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
if (rc < 0) {
SDL_AudioDeviceDisconnected(device);
return;
return -1;
}
/*printf("PULSEAUDIO FEED! nbytes=%d\n", buffer_size);*/
h->bytes_requested -= buffer_size;
/*printf("PULSEAUDIO PLAYDEVICE END! written=%d\n", written);*/
return 0;
}
static Uint8 *PULSEAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
@ -606,22 +606,22 @@ static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *device)
case SDL_AUDIO_U8:
format = PA_SAMPLE_U8;
break;
case SDL_AUDIO_S16LSB:
case SDL_AUDIO_S16LE:
format = PA_SAMPLE_S16LE;
break;
case SDL_AUDIO_S16MSB:
case SDL_AUDIO_S16BE:
format = PA_SAMPLE_S16BE;
break;
case SDL_AUDIO_S32LSB:
case SDL_AUDIO_S32LE:
format = PA_SAMPLE_S32LE;
break;
case SDL_AUDIO_S32MSB:
case SDL_AUDIO_S32BE:
format = PA_SAMPLE_S32BE;
break;
case SDL_AUDIO_F32LSB:
case SDL_AUDIO_F32LE:
format = PA_SAMPLE_FLOAT32LE;
break;
case SDL_AUDIO_F32MSB:
case SDL_AUDIO_F32BE:
format = PA_SAMPLE_FLOAT32BE;
break;
default:
@ -723,17 +723,17 @@ static SDL_AudioFormat PulseFormatToSDLFormat(pa_sample_format_t format)
case PA_SAMPLE_U8:
return SDL_AUDIO_U8;
case PA_SAMPLE_S16LE:
return SDL_AUDIO_S16LSB;
return SDL_AUDIO_S16LE;
case PA_SAMPLE_S16BE:
return SDL_AUDIO_S16MSB;
return SDL_AUDIO_S16BE;
case PA_SAMPLE_S32LE:
return SDL_AUDIO_S32LSB;
return SDL_AUDIO_S32LE;
case PA_SAMPLE_S32BE:
return SDL_AUDIO_S32MSB;
return SDL_AUDIO_S32BE;
case PA_SAMPLE_FLOAT32LE:
return SDL_AUDIO_F32LSB;
return SDL_AUDIO_F32LE;
case PA_SAMPLE_FLOAT32BE:
return SDL_AUDIO_F32MSB;
return SDL_AUDIO_F32BE;
default:
return 0;
}

View File

@ -110,10 +110,10 @@ static void QSA_WaitDevice(SDL_AudioDevice *device)
}
}
static void QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
static int QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{
if (SDL_AtomicGet(&device->shutdown) || !device->hidden) {
return;
return 0;
}
int towrite = buflen;
@ -125,7 +125,7 @@ static void QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buf
// 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; // oh well, try again next time. !!! FIXME: Should we just disconnect the device in this case?
return 0; // oh well, try again next time. !!! FIXME: Should we just disconnect the device in this case?
}
}
@ -145,17 +145,17 @@ static void QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buf
int status = snd_pcm_plugin_status(device->hidden->audio_handle, &cstatus);
if (status < 0) {
QSA_SetError("snd_pcm_plugin_status", status);
return; // !!! FIXME: disconnect the device?
return -1;
} else if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY)) {
status = snd_pcm_plugin_prepare(device->hidden->audio_handle, device->iscapture ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK);
if (status < 0) {
QSA_SetError("snd_pcm_plugin_prepare", status);
return; // !!! FIXME: disconnect the device?
return -1;
}
}
continue;
} else {
return; // !!! FIXME: disconnect the device?
return -1;
}
} else {
// we wrote all remaining data
@ -165,9 +165,7 @@ static void QSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buf
}
// If we couldn't write, assume fatal error for now
if (towrite != 0) {
SDL_AudioDeviceDisconnected(device);
}
return (towrite != 0) ? -1 : 0;
}
static Uint8 *QSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
@ -311,7 +309,7 @@ static SDL_AudioFormat QnxFormatToSDLFormat(const int32_t qnxfmt)
#undef CHECKFMT
default: break;
}
return SDL_AUDIO_S16SYS; // oh well.
return SDL_AUDIO_S16; // oh well.
}
static void QSA_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)

View File

@ -175,16 +175,17 @@ static void SNDIO_WaitDevice(SDL_AudioDevice *device)
}
}
static void SNDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
static int 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) {
SDL_AudioDeviceDisconnected(device); // If we couldn't write, assume fatal error for now
return -1; // 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 0;
}
static int SNDIO_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
@ -283,13 +284,13 @@ static int SNDIO_OpenDevice(SDL_AudioDevice *device)
}
if ((par.bps == 4) && (par.sig) && (par.le)) {
device->spec.format = SDL_AUDIO_S32LSB;
device->spec.format = SDL_AUDIO_S32LE;
} else if ((par.bps == 4) && (par.sig) && (!par.le)) {
device->spec.format = SDL_AUDIO_S32MSB;
device->spec.format = SDL_AUDIO_S32BE;
} else if ((par.bps == 2) && (par.sig) && (par.le)) {
device->spec.format = SDL_AUDIO_S16LSB;
device->spec.format = SDL_AUDIO_S16LE;
} else if ((par.bps == 2) && (par.sig) && (!par.le)) {
device->spec.format = SDL_AUDIO_S16MSB;
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)) {

View File

@ -71,7 +71,7 @@ static int VITAAUD_OpenDevice(SDL_AudioDevice *device)
closefmts = SDL_ClosestAudioFormats(device->spec.format);
while ((test_format = *(closefmts++)) != 0) {
if (test_format == SDL_AUDIO_S16LSB) {
if (test_format == SDL_AUDIO_S16LE) {
device->spec.format = test_format;
break;
}
@ -130,9 +130,9 @@ static int VITAAUD_OpenDevice(SDL_AudioDevice *device)
return 0;
}
static void VITAAUD_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
static int VITAAUD_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
{
sceAudioOutOutput(device->hidden->port, buffer);
return (sceAudioOutOutput(device->hidden->port, buffer) == 0) ? 0 : -1;
}
// This function waits until it is possible to write a full sound buffer

View File

@ -403,22 +403,22 @@ static Uint8 *WASAPI_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
// get an endpoint buffer from WASAPI.
BYTE *buffer = NULL;
while (RecoverWasapiIfLost(device) && device->hidden->render) {
if (!WasapiFailed(device, IAudioRenderClient_GetBuffer(device->hidden->render, device->sample_frames, &buffer))) {
return (Uint8 *)buffer;
if (RecoverWasapiIfLost(device) && device->hidden->render) {
if (WasapiFailed(device, IAudioRenderClient_GetBuffer(device->hidden->render, device->sample_frames, &buffer))) {
SDL_assert(buffer == NULL);
}
SDL_assert(buffer == NULL);
}
return (Uint8 *)buffer;
}
static void WASAPI_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
static int WASAPI_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
{
if (device->hidden->render != NULL) { // definitely activated?
// WasapiFailed() will mark the device for reacquisition or removal elsewhere.
WasapiFailed(device, IAudioRenderClient_ReleaseBuffer(device->hidden->render, device->sample_frames, 0));
}
return 0;
}
static void WASAPI_WaitDevice(SDL_AudioDevice *device)
@ -620,7 +620,7 @@ static int mgmtthrtask_PrepDevice(void *userdata)
return -1;
}
device->hidden->framesize = (SDL_AUDIO_BITSIZE(device->spec.format) / 8) * device->spec.channels;
device->hidden->framesize = SDL_AUDIO_FRAMESIZE(device->spec);
if (device->iscapture) {
IAudioCaptureClient *capture = NULL;