diff options
Diffstat (limited to 'Source/3rdParty/SDL2/src/audio')
18 files changed, 1043 insertions, 395 deletions
diff --git a/Source/3rdParty/SDL2/src/audio/SDL_audio.c b/Source/3rdParty/SDL2/src/audio/SDL_audio.c index dcaebea..f4999f1 100644 --- a/Source/3rdParty/SDL2/src/audio/SDL_audio.c +++ b/Source/3rdParty/SDL2/src/audio/SDL_audio.c @@ -378,21 +378,57 @@ static int add_audio_device(const char *name, void *handle, SDL_AudioDeviceItem **devices, int *devCount) { int retval = -1; - const size_t size = sizeof (SDL_AudioDeviceItem) + SDL_strlen(name) + 1; - SDL_AudioDeviceItem *item = (SDL_AudioDeviceItem *) SDL_malloc(size); - if (item == NULL) { - return -1; - } + SDL_AudioDeviceItem *item; + const SDL_AudioDeviceItem *i; + int dupenum = 0; SDL_assert(handle != NULL); /* we reserve NULL, audio backends can't use it. */ + SDL_assert(name != NULL); + item = (SDL_AudioDeviceItem *) SDL_malloc(sizeof (SDL_AudioDeviceItem)); + if (!item) { + return SDL_OutOfMemory(); + } + + item->original_name = SDL_strdup(name); + if (!item->original_name) { + SDL_free(item); + return SDL_OutOfMemory(); + } + + item->dupenum = 0; + item->name = item->original_name; item->handle = handle; - SDL_strlcpy(item->name, name, size - sizeof (SDL_AudioDeviceItem)); SDL_LockMutex(current_audio.detectionLock); + + for (i = *devices; i != NULL; i = i->next) { + if (SDL_strcmp(name, i->original_name) == 0) { + dupenum = i->dupenum + 1; + break; /* stop at the highest-numbered dupe. */ + } + } + + if (dupenum) { + const size_t len = SDL_strlen(name) + 16; + char *replacement = (char *) SDL_malloc(len); + if (!replacement) { + SDL_UnlockMutex(current_audio.detectionLock); + SDL_free(item->original_name); + SDL_free(item); + SDL_OutOfMemory(); + return -1; + } + + SDL_snprintf(replacement, len, "%s (%d)", name, dupenum + 1); + item->dupenum = dupenum; + item->name = replacement; + } + item->next = *devices; *devices = item; - retval = (*devCount)++; + retval = (*devCount)++; /* !!! FIXME: this should be an atomic increment */ + SDL_UnlockMutex(current_audio.detectionLock); return retval; @@ -420,6 +456,11 @@ free_device_list(SDL_AudioDeviceItem **devices, int *devCount) if (item->handle != NULL) { current_audio.impl.FreeDeviceHandle(item->handle); } + /* these two pointers are the same if not a duplicate devname */ + if (item->name != item->original_name) { + SDL_free(item->name); + } + SDL_free(item->original_name); SDL_free(item); } *devices = NULL; @@ -451,7 +492,11 @@ void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device) SDL_assert(get_audio_device(device->id) == device); if (!SDL_AtomicGet(&device->enabled)) { - return; + return; /* don't report disconnects more than once. */ + } + + if (SDL_AtomicGet(&device->shutdown)) { + return; /* don't report disconnect if we're trying to close device. */ } /* Ends the audio callback and mark the device as STOPPED, but the @@ -651,7 +696,7 @@ SDL_RunAudio(void *devicep) SDL_assert(!device->iscapture); /* The audio mixing is always a high priority thread */ - SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); + SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL); /* Perform any thread setup */ device->threadid = SDL_ThreadID(); @@ -832,6 +877,8 @@ SDL_CaptureAudio(void *devicep) } } + current_audio.impl.PrepareToClose(device); + current_audio.impl.FlushCapture(device); current_audio.impl.ThreadDeinit(device); @@ -971,6 +1018,11 @@ clean_out_device_list(SDL_AudioDeviceItem **devices, int *devCount, SDL_bool *re } else { *devices = next; } + /* these two pointers are the same if not a duplicate devname */ + if (item->name != item->original_name) { + SDL_free(item->name); + } + SDL_free(item->original_name); SDL_free(item); } item = next; @@ -997,7 +1049,6 @@ SDL_GetNumAudioDevices(int iscapture) if (!iscapture && current_audio.outputDevicesRemoved) { clean_out_device_list(¤t_audio.outputDevices, ¤t_audio.outputDeviceCount, ¤t_audio.outputDevicesRemoved); - current_audio.outputDevicesRemoved = SDL_FALSE; } retval = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount; @@ -1054,16 +1105,14 @@ close_audio_device(SDL_AudioDevice * device) return; } - if (device->id > 0) { - SDL_AudioDevice *opendev = open_devices[device->id - 1]; - SDL_assert((opendev == device) || (opendev == NULL)); - if (opendev == device) { - open_devices[device->id - 1] = NULL; - } - } - + /* make sure the device is paused before we do anything else, so the + audio callback definitely won't fire again. */ + current_audio.impl.LockDevice(device); + SDL_AtomicSet(&device->paused, 1); SDL_AtomicSet(&device->shutdown, 1); SDL_AtomicSet(&device->enabled, 0); + current_audio.impl.UnlockDevice(device); + if (device->thread != NULL) { SDL_WaitThread(device->thread, NULL); } @@ -1074,6 +1123,14 @@ close_audio_device(SDL_AudioDevice * device) SDL_free(device->work_buffer); SDL_FreeAudioStream(device->stream); + if (device->id > 0) { + SDL_AudioDevice *opendev = open_devices[device->id - 1]; + SDL_assert((opendev == device) || (opendev == NULL)); + if (opendev == device) { + open_devices[device->id - 1] = NULL; + } + } + if (device->hidden != NULL) { current_audio.impl.CloseDevice(device); } @@ -1118,8 +1175,9 @@ prepare_audiospec(const SDL_AudioSpec * orig, SDL_AudioSpec * prepared) } case 1: /* Mono */ case 2: /* Stereo */ - case 4: /* surround */ - case 6: /* surround with center and lfe */ + case 4: /* Quadrophonic */ + case 6: /* 5.1 surround */ + case 8: /* 7.1 surround */ break; default: SDL_SetError("Unsupported number of audio channels."); @@ -1312,15 +1370,12 @@ open_audio_device(const char *devname, int iscapture, build_stream = SDL_TRUE; } } - - /* !!! FIXME in 2.1: add SDL_AUDIO_ALLOW_SAMPLES_CHANGE flag? - As of 2.0.6, we will build a stream to buffer the difference between - what the app wants to feed and the device wants to eat, so everyone - gets their way. In prior releases, SDL would force the callback to - feed at the rate the device requested, adjusted for resampling. - */ if (device->spec.samples != obtained->samples) { - build_stream = SDL_TRUE; + if (allowed_changes & SDL_AUDIO_ALLOW_SAMPLES_CHANGE) { + obtained->samples = device->spec.samples; + } else { + build_stream = SDL_TRUE; + } } SDL_CalculateAudioSpec(obtained); /* recalc after possible changes. */ diff --git a/Source/3rdParty/SDL2/src/audio/SDL_audiocvt.c b/Source/3rdParty/SDL2/src/audio/SDL_audiocvt.c index 7fde2b9..ee0ba32 100644 --- a/Source/3rdParty/SDL2/src/audio/SDL_audiocvt.c +++ b/Source/3rdParty/SDL2/src/audio/SDL_audiocvt.c @@ -724,7 +724,7 @@ SDL_ResampleCVT(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format SDL_assert(format == AUDIO_F32SYS); /* we keep no streaming state here, so pad with silence on both ends. */ - padding = (float *) SDL_calloc(paddingsamples, sizeof (float)); + padding = (float *) SDL_calloc(paddingsamples ? paddingsamples : 1, sizeof (float)); if (!padding) { SDL_OutOfMemory(); return; @@ -1291,7 +1291,7 @@ SDL_NewAudioStream(const SDL_AudioFormat src_format, retval->packetlen = packetlen; retval->rate_incr = ((double) dst_rate) / ((double) src_rate); retval->resampler_padding_samples = ResamplerPadding(retval->src_rate, retval->dst_rate) * pre_resample_channels; - retval->resampler_padding = (float *) SDL_calloc(retval->resampler_padding_samples, sizeof (float)); + retval->resampler_padding = (float *) SDL_calloc(retval->resampler_padding_samples ? retval->resampler_padding_samples : 1, sizeof (float)); if (retval->resampler_padding == NULL) { SDL_FreeAudioStream(retval); diff --git a/Source/3rdParty/SDL2/src/audio/SDL_audiodev_c.h b/Source/3rdParty/SDL2/src/audio/SDL_audiodev_c.h index 15928d1..2d3b0ea 100644 --- a/Source/3rdParty/SDL2/src/audio/SDL_audiodev_c.h +++ b/Source/3rdParty/SDL2/src/audio/SDL_audiodev_c.h @@ -18,6 +18,10 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ + +#ifndef SDL_audiodev_c_h_ +#define SDL_audiodev_c_h_ + #include "SDL.h" #include "../SDL_internal.h" #include "SDL_sysaudio.h" @@ -35,4 +39,6 @@ extern void SDL_EnumUnixAudioDevices(const int classic, int (*test)(int)); +#endif /* SDL_audiodev_c_h_ */ + /* vi: set ts=4 sw=4 expandtab: */ diff --git a/Source/3rdParty/SDL2/src/audio/SDL_audiotypecvt.c b/Source/3rdParty/SDL2/src/audio/SDL_audiotypecvt.c index 2fbd916..5f8cc22 100644 --- a/Source/3rdParty/SDL2/src/audio/SDL_audiotypecvt.c +++ b/Source/3rdParty/SDL2/src/audio/SDL_audiotypecvt.c @@ -25,8 +25,10 @@ #include "SDL_cpuinfo.h" #include "SDL_assert.h" -/* !!! FIXME: write NEON code. */ -#define HAVE_NEON_INTRINSICS 0 +/* !!! FIXME: disabled until we fix https://bugzilla.libsdl.org/show_bug.cgi?id=4186 */ +#if 0 /*def __ARM_NEON__*/ +#define HAVE_NEON_INTRINSICS 1 +#endif #ifdef __SSE2__ #define HAVE_SSE2_INTRINSICS 1 @@ -62,7 +64,7 @@ SDL_AudioFilter SDL_Convert_F32_to_S32 = NULL; #define DIVBY128 0.0078125f #define DIVBY32768 0.000030517578125f -#define DIVBY2147483648 0.00000000046566128730773926 +#define DIVBY8388607 0.00000011920930376163766f #if NEED_SCALAR_CONVERTER_FALLBACKS @@ -152,7 +154,7 @@ SDL_Convert_S32_to_F32_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format) LOG_DEBUG_CONVERT("AUDIO_S32", "AUDIO_F32"); for (i = cvt->len_cvt / sizeof (Sint32); i; --i, ++src, ++dst) { - *dst = (float) (((double) *src) * DIVBY2147483648); + *dst = ((float) (*src>>8)) * DIVBY8388607; } if (cvt->filters[++cvt->filter_index]) { @@ -171,10 +173,10 @@ SDL_Convert_F32_to_S8_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format) for (i = cvt->len_cvt / sizeof (float); i; --i, ++src, ++dst) { const float sample = *src; - if (sample > 1.0f) { + if (sample >= 1.0f) { *dst = 127; - } else if (sample < -1.0f) { - *dst = -127; + } else if (sample <= -1.0f) { + *dst = -128; } else { *dst = (Sint8)(sample * 127.0f); } @@ -197,9 +199,9 @@ SDL_Convert_F32_to_U8_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format) for (i = cvt->len_cvt / sizeof (float); i; --i, ++src, ++dst) { const float sample = *src; - if (sample > 1.0f) { + if (sample >= 1.0f) { *dst = 255; - } else if (sample < -1.0f) { + } else if (sample <= -1.0f) { *dst = 0; } else { *dst = (Uint8)((sample + 1.0f) * 127.0f); @@ -223,10 +225,10 @@ SDL_Convert_F32_to_S16_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format) for (i = cvt->len_cvt / sizeof (float); i; --i, ++src, ++dst) { const float sample = *src; - if (sample > 1.0f) { + if (sample >= 1.0f) { *dst = 32767; - } else if (sample < -1.0f) { - *dst = -32767; + } else if (sample <= -1.0f) { + *dst = -32768; } else { *dst = (Sint16)(sample * 32767.0f); } @@ -249,9 +251,9 @@ SDL_Convert_F32_to_U16_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format) for (i = cvt->len_cvt / sizeof (float); i; --i, ++src, ++dst) { const float sample = *src; - if (sample > 1.0f) { - *dst = 65534; - } else if (sample < -1.0f) { + if (sample >= 1.0f) { + *dst = 65535; + } else if (sample <= -1.0f) { *dst = 0; } else { *dst = (Uint16)((sample + 1.0f) * 32767.0f); @@ -275,12 +277,12 @@ SDL_Convert_F32_to_S32_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format) for (i = cvt->len_cvt / sizeof (float); i; --i, ++src, ++dst) { const float sample = *src; - if (sample > 1.0f) { + if (sample >= 1.0f) { *dst = 2147483647; - } else if (sample < -1.0f) { - *dst = -2147483647; + } else if (sample <= -1.0f) { + *dst = (Sint32) -2147483648LL; } else { - *dst = (Sint32)((double)sample * 2147483647.0); + *dst = ((Sint32)(sample * 8388607.0f)) << 8; } } @@ -509,16 +511,6 @@ SDL_Convert_U16_to_F32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) } } -#if defined(__GNUC__) && (__GNUC__ < 4) -/* these were added as of gcc-4.0: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=19418 */ -static inline __m128 _mm_castsi128_ps(__m128i __A) { - return (__m128) __A; -} -static inline __m128i _mm_castps_si128(__m128 __A) { - return (__m128i) __A; -} -#endif - static void SDLCALL SDL_Convert_S32_to_F32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) { @@ -530,7 +522,7 @@ SDL_Convert_S32_to_F32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) /* Get dst aligned to 16 bytes */ for (i = cvt->len_cvt / sizeof (Sint32); i && (((size_t) dst) & 15); --i, ++src, ++dst) { - *dst = (float) (((double) *src) * DIVBY2147483648); + *dst = ((float) (*src>>8)) * DIVBY8388607; } SDL_assert(!i || ((((size_t) dst) & 15) == 0)); @@ -538,15 +530,11 @@ SDL_Convert_S32_to_F32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) { /* Aligned! Do SSE blocks as long as we have 16 bytes available. */ - const __m128d divby2147483648 = _mm_set1_pd(DIVBY2147483648); + const __m128 divby8388607 = _mm_set1_ps(DIVBY8388607); const __m128i *mmsrc = (const __m128i *) src; while (i >= 4) { /* 4 * sint32 */ - const __m128i ints = _mm_load_si128(mmsrc); - /* bitshift the whole register over, so _mm_cvtepi32_pd can read the top ints in the bottom of the vector. */ - const __m128d doubles1 = _mm_mul_pd(_mm_cvtepi32_pd(_mm_srli_si128(ints, 8)), divby2147483648); - const __m128d doubles2 = _mm_mul_pd(_mm_cvtepi32_pd(ints), divby2147483648); - /* convert to float32, bitshift/or to get these into a vector to store. */ - _mm_store_ps(dst, _mm_castsi128_ps(_mm_or_si128(_mm_slli_si128(_mm_castps_si128(_mm_cvtpd_ps(doubles1)), 8), _mm_castps_si128(_mm_cvtpd_ps(doubles2))))); + /* 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; @@ -554,7 +542,7 @@ SDL_Convert_S32_to_F32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) /* Finish off any leftovers with scalar operations. */ while (i) { - *dst = (float) (((double) *src) * DIVBY2147483648); + *dst = ((float) (*src>>8)) * DIVBY8388607; i--; src++; dst++; } @@ -574,7 +562,14 @@ SDL_Convert_F32_to_S8_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) /* Get dst aligned to 16 bytes */ for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) { - *dst = (Sint8) (*src * 127.0f); + const float sample = *src; + if (sample >= 1.0f) { + *dst = 127; + } else if (sample <= -1.0f) { + *dst = -128; + } else { + *dst = (Sint8)(sample * 127.0f); + } } SDL_assert(!i || ((((size_t) dst) & 15) == 0)); @@ -582,13 +577,15 @@ SDL_Convert_F32_to_S8_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) /* Make sure src is aligned too. */ if ((((size_t) src) & 15) == 0) { /* 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_load_ps(src), mulby127)); /* load 4 floats, convert to sint32 */ - const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src+4), mulby127)); /* load 4 floats, convert to sint32 */ - const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src+8), mulby127)); /* load 4 floats, convert to sint32 */ - const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src+12), mulby127)); /* load 4 floats, convert to sint32 */ + 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++; } @@ -597,7 +594,14 @@ SDL_Convert_F32_to_S8_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) /* Finish off any leftovers with scalar operations. */ while (i) { - *dst = (Sint8) (*src * 127.0f); + 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++; } @@ -618,7 +622,14 @@ SDL_Convert_F32_to_U8_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) /* Get dst aligned to 16 bytes */ for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) { - *dst = (Uint8) ((*src + 1.0f) * 127.0f); + 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); + } } SDL_assert(!i || ((((size_t) dst) & 15) == 0)); @@ -626,14 +637,15 @@ SDL_Convert_F32_to_U8_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) /* Make sure src is aligned too. */ if ((((size_t) src) & 15) == 0) { /* Aligned! Do SSE blocks as long as we have 16 bytes available. */ - const __m128 add1 = _mm_set1_ps(1.0f); + 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_load_ps(src), add1), mulby127)); /* load 4 floats, convert to sint32 */ - const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_load_ps(src+4), add1), mulby127)); /* load 4 floats, convert to sint32 */ - const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_load_ps(src+8), add1), mulby127)); /* load 4 floats, convert to sint32 */ - const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_load_ps(src+12), add1), mulby127)); /* load 4 floats, convert to sint32 */ + 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++; } @@ -642,7 +654,14 @@ SDL_Convert_F32_to_U8_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) /* Finish off any leftovers with scalar operations. */ while (i) { - *dst = (Uint8) ((*src + 1.0f) * 127.0f); + 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++; } @@ -663,7 +682,14 @@ SDL_Convert_F32_to_S16_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) /* Get dst aligned to 16 bytes */ for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) { - *dst = (Sint16) (*src * 32767.0f); + const float sample = *src; + if (sample >= 1.0f) { + *dst = 32767; + } else if (sample <= -1.0f) { + *dst = -32768; + } else { + *dst = (Sint16)(sample * 32767.0f); + } } SDL_assert(!i || ((((size_t) dst) & 15) == 0)); @@ -671,11 +697,13 @@ SDL_Convert_F32_to_S16_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) /* Make sure src is aligned too. */ if ((((size_t) src) & 15) == 0) { /* 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_load_ps(src), mulby32767)); /* load 4 floats, convert to sint32 */ - const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src+4), mulby32767)); /* load 4 floats, convert to sint32 */ + 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++; } @@ -684,7 +712,14 @@ SDL_Convert_F32_to_S16_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) /* Finish off any leftovers with scalar operations. */ while (i) { - *dst = (Sint16) (*src * 32767.0f); + 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++; } @@ -705,7 +740,14 @@ SDL_Convert_F32_to_U16_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) /* Get dst aligned to 16 bytes */ for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) { - *dst = (Uint16) ((*src + 1.0f) * 32767.0f); + const float sample = *src; + if (sample >= 1.0f) { + *dst = 65535; + } else if (sample <= -1.0f) { + *dst = 0; + } else { + *dst = (Uint16)((sample + 1.0f) * 32767.0f); + } } SDL_assert(!i || ((((size_t) dst) & 15) == 0)); @@ -722,10 +764,12 @@ SDL_Convert_F32_to_U16_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) though it looks like dark magic. */ const __m128 mulby32767 = _mm_set1_ps(32767.0f); const __m128i topbit = _mm_set1_epi16(-32768); + const __m128 one = _mm_set1_ps(1.0f); + const __m128 negone = _mm_set1_ps(-1.0f); __m128i *mmdst = (__m128i *) dst; while (i >= 8) { /* 8 * float32 */ - const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src), mulby32767)); /* load 4 floats, convert to sint32 */ - const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(src+4), mulby32767)); /* load 4 floats, convert to sint32 */ + 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_xor_si128(_mm_packs_epi32(ints1, ints2), topbit)); /* pack to sint16, xor top bit, store out. */ i -= 8; src += 8; mmdst++; } @@ -734,7 +778,14 @@ SDL_Convert_F32_to_U16_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) /* Finish off any leftovers with scalar operations. */ while (i) { - *dst = (Uint16) ((*src + 1.0f) * 32767.0f); + const float sample = *src; + if (sample >= 1.0f) { + *dst = 65535; + } else if (sample <= -1.0f) { + *dst = 0; + } else { + *dst = (Uint16)((sample + 1.0f) * 32767.0f); + } i--; src++; dst++; } @@ -755,7 +806,14 @@ SDL_Convert_F32_to_S32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) /* Get dst aligned to 16 bytes */ for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) { - *dst = (Sint32) (((double) *src) * 2147483647.0); + 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; + } } SDL_assert(!i || ((((size_t) dst) & 15) == 0)); @@ -763,14 +821,12 @@ SDL_Convert_F32_to_S32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) { /* Aligned! Do SSE blocks as long as we have 16 bytes available. */ - const __m128d mulby2147483647 = _mm_set1_pd(2147483647.0); + 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 */ - const __m128 floats = _mm_load_ps(src); - /* bitshift the whole register over, so _mm_cvtps_pd can read the top floats in the bottom of the vector. */ - const __m128d doubles1 = _mm_mul_pd(_mm_cvtps_pd(_mm_castsi128_ps(_mm_srli_si128(_mm_castps_si128(floats), 8))), mulby2147483647); - const __m128d doubles2 = _mm_mul_pd(_mm_cvtps_pd(floats), mulby2147483647); - _mm_store_si128(mmdst, _mm_or_si128(_mm_slli_si128(_mm_cvtpd_epi32(doubles1), 8), _mm_cvtpd_epi32(doubles2))); + _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; @@ -778,7 +834,14 @@ SDL_Convert_F32_to_S32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) /* Finish off any leftovers with scalar operations. */ while (i) { - *dst = (Sint32) (((double) *src) * 2147483647.0); + 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++; } @@ -789,6 +852,538 @@ SDL_Convert_F32_to_S32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format) #endif +#if HAVE_NEON_INTRINSICS +static void SDLCALL +SDL_Convert_S8_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format) +{ + const Sint8 *src = ((const Sint8 *) (cvt->buf + cvt->len_cvt)) - 1; + float *dst = ((float *) (cvt->buf + cvt->len_cvt * 4)) - 1; + int i; + + LOG_DEBUG_CONVERT("AUDIO_S8", "AUDIO_F32 (using NEON)"); + + /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */ + for (i = cvt->len_cvt; i && (((size_t) (dst-15)) & 15); --i, --src, --dst) { + *dst = ((float) *src) * DIVBY128; + } + + src -= 15; dst -= 15; /* adjust to read NEON blocks from the start. */ + SDL_assert(!i || ((((size_t) dst) & 15) == 0)); + + /* Make sure src is aligned too. */ + if ((((size_t) src) & 15) == 0) { + /* Aligned! Do NEON blocks as long as we have 16 bytes available. */ + const int8_t *mmsrc = (const int8_t *) src; + const float32x4_t divby128 = vdupq_n_f32(DIVBY128); + while (i >= 16) { /* 16 * 8-bit */ + const int8x16_t bytes = vld1q_s8(mmsrc); /* get 16 sint8 into a NEON register. */ + const int16x8_t int16hi = vmovl_s8(vget_high_s8(bytes)); /* convert top 8 bytes to 8 int16 */ + const int16x8_t int16lo = vmovl_s8(vget_low_s8(bytes)); /* convert bottom 8 bytes to 8 int16 */ + /* split int16 to two int32, then convert to float, then multiply to normalize, store. */ + vst1q_f32(dst, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_high_s16(int16hi))), divby128)); + vst1q_f32(dst+4, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_low_s16(int16hi))), divby128)); + vst1q_f32(dst+8, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_high_s16(int16lo))), divby128)); + vst1q_f32(dst+12, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_low_s16(int16lo))), divby128)); + i -= 16; mmsrc -= 16; 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--; + } + + cvt->len_cvt *= 4; + if (cvt->filters[++cvt->filter_index]) { + cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS); + } +} + +static void SDLCALL +SDL_Convert_U8_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format) +{ + const Uint8 *src = ((const Uint8 *) (cvt->buf + cvt->len_cvt)) - 1; + float *dst = ((float *) (cvt->buf + cvt->len_cvt * 4)) - 1; + int i; + + LOG_DEBUG_CONVERT("AUDIO_U8", "AUDIO_F32 (using NEON)"); + + /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */ + for (i = cvt->len_cvt; i && (((size_t) (dst-15)) & 15); --i, --src, --dst) { + *dst = (((float) *src) * DIVBY128) - 1.0f; + } + + src -= 15; dst -= 15; /* adjust to read NEON blocks from the start. */ + SDL_assert(!i || ((((size_t) dst) & 15) == 0)); + + /* Make sure src is aligned too. */ + if ((((size_t) src) & 15) == 0) { + /* Aligned! Do NEON blocks as long as we have 16 bytes available. */ + const uint8_t *mmsrc = (const uint8_t *) src; + const float32x4_t divby128 = vdupq_n_f32(DIVBY128); + const float32x4_t one = vdupq_n_f32(1.0f); + while (i >= 16) { /* 16 * 8-bit */ + const uint8x16_t bytes = vld1q_u8(mmsrc); /* get 16 uint8 into a NEON register. */ + const uint16x8_t uint16hi = vmovl_u8(vget_high_u8(bytes)); /* convert top 8 bytes to 8 uint16 */ + const uint16x8_t uint16lo = vmovl_u8(vget_low_u8(bytes)); /* convert bottom 8 bytes to 8 uint16 */ + /* split uint16 to two uint32, then convert to float, then multiply to normalize, subtract to adjust for sign, store. */ + vst1q_f32(dst, vmlsq_f32(vcvtq_f32_u32(vmovl_u16(vget_high_u16(uint16hi))), divby128, one)); + vst1q_f32(dst+4, vmlsq_f32(vcvtq_f32_u32(vmovl_u16(vget_low_u16(uint16hi))), divby128, one)); + vst1q_f32(dst+8, vmlsq_f32(vcvtq_f32_u32(vmovl_u16(vget_high_u16(uint16lo))), divby128, one)); + vst1q_f32(dst+12, vmlsq_f32(vcvtq_f32_u32(vmovl_u16(vget_low_u16(uint16lo))), divby128, one)); + i -= 16; mmsrc -= 16; 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--; + } + + cvt->len_cvt *= 4; + if (cvt->filters[++cvt->filter_index]) { + cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS); + } +} + +static void SDLCALL +SDL_Convert_S16_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format) +{ + const Sint16 *src = ((const Sint16 *) (cvt->buf + cvt->len_cvt)) - 1; + float *dst = ((float *) (cvt->buf + cvt->len_cvt * 2)) - 1; + int i; + + LOG_DEBUG_CONVERT("AUDIO_S16", "AUDIO_F32 (using NEON)"); + + /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */ + for (i = cvt->len_cvt / sizeof (Sint16); i && (((size_t) (dst-7)) & 15); --i, --src, --dst) { + *dst = ((float) *src) * DIVBY32768; + } + + src -= 7; dst -= 7; /* adjust to read NEON blocks from the start. */ + SDL_assert(!i || ((((size_t) dst) & 15) == 0)); + + /* Make sure src is aligned too. */ + if ((((size_t) src) & 15) == 0) { + /* Aligned! Do NEON blocks as long as we have 16 bytes available. */ + const float32x4_t divby32768 = vdupq_n_f32(DIVBY32768); + while (i >= 8) { /* 8 * 16-bit */ + const int16x8_t ints = vld1q_s16((int16_t const *) src); /* get 8 sint16 into a NEON register. */ + /* split int16 to two int32, then convert to float, then multiply to normalize, store. */ + vst1q_f32(dst, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_low_s16(ints))), divby32768)); + vst1q_f32(dst+4, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_high_s16(ints))), 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--; + } + + cvt->len_cvt *= 2; + if (cvt->filters[++cvt->filter_index]) { + cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS); + } +} + +static void SDLCALL +SDL_Convert_U16_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format) +{ + const Uint16 *src = ((const Uint16 *) (cvt->buf + cvt->len_cvt)) - 1; + float *dst = ((float *) (cvt->buf + cvt->len_cvt * 2)) - 1; + int i; + + LOG_DEBUG_CONVERT("AUDIO_U16", "AUDIO_F32 (using NEON)"); + + /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */ + for (i = cvt->len_cvt / sizeof (Sint16); i && (((size_t) (dst-7)) & 15); --i, --src, --dst) { + *dst = (((float) *src) * DIVBY32768) - 1.0f; + } + + src -= 7; dst -= 7; /* adjust to read NEON blocks from the start. */ + SDL_assert(!i || ((((size_t) dst) & 15) == 0)); + + /* Make sure src is aligned too. */ + if ((((size_t) src) & 15) == 0) { + /* Aligned! Do NEON blocks as long as we have 16 bytes available. */ + const float32x4_t divby32768 = vdupq_n_f32(DIVBY32768); + const float32x4_t one = vdupq_n_f32(1.0f); + while (i >= 8) { /* 8 * 16-bit */ + const uint16x8_t uints = vld1q_u16((uint16_t const *) src); /* get 8 uint16 into a NEON register. */ + /* split uint16 to two int32, then convert to float, then multiply to normalize, subtract for sign, store. */ + vst1q_f32(dst, vmlsq_f32(one, vcvtq_f32_u32(vmovl_u16(vget_low_u16(uints))), divby32768)); + vst1q_f32(dst+4, vmlsq_f32(one, vcvtq_f32_u32(vmovl_u16(vget_high_u16(uints))), 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) - 1.0f; + i--; src--; dst--; + } + + cvt->len_cvt *= 2; + if (cvt->filters[++cvt->filter_index]) { + cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS); + } +} + +static void SDLCALL +SDL_Convert_S32_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format) +{ + const Sint32 *src = (const Sint32 *) cvt->buf; + float *dst = (float *) cvt->buf; + int i; + + LOG_DEBUG_CONVERT("AUDIO_S32", "AUDIO_F32 (using NEON)"); + + /* Get dst aligned to 16 bytes */ + for (i = cvt->len_cvt / sizeof (Sint32); i && (((size_t) dst) & 15); --i, ++src, ++dst) { + *dst = ((float) (*src>>8)) * DIVBY8388607; + } + + SDL_assert(!i || ((((size_t) dst) & 15) == 0)); + SDL_assert(!i || ((((size_t) src) & 15) == 0)); + + { + /* Aligned! Do NEON blocks as long as we have 16 bytes available. */ + const float32x4_t divby8388607 = vdupq_n_f32(DIVBY8388607); + const int32_t *mmsrc = (const int32_t *) src; + while (i >= 4) { /* 4 * sint32 */ + /* shift out lowest bits so int fits in a float32. Small precision loss, but much faster. */ + vst1q_f32(dst, vmulq_f32(vcvtq_f32_s32(vshrq_n_s32(vld1q_s32(mmsrc), 8)), divby8388607)); + i -= 4; mmsrc += 4; dst += 4; + } + src = (const Sint32 *) mmsrc; + } + + /* Finish off any leftovers with scalar operations. */ + while (i) { + *dst = ((float) (*src>>8)) * DIVBY8388607; + i--; src++; dst++; + } + + if (cvt->filters[++cvt->filter_index]) { + cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS); + } +} + +static void SDLCALL +SDL_Convert_F32_to_S8_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format) +{ + const float *src = (const float *) cvt->buf; + Sint8 *dst = (Sint8 *) cvt->buf; + int i; + + LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S8 (using NEON)"); + + /* Get dst aligned to 16 bytes */ + for (i = cvt->len_cvt / sizeof (float); 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); + } + } + + SDL_assert(!i || ((((size_t) dst) & 15) == 0)); + + /* Make sure src is aligned too. */ + if ((((size_t) src) & 15) == 0) { + /* Aligned! Do NEON blocks as long as we have 16 bytes available. */ + const float32x4_t one = vdupq_n_f32(1.0f); + const float32x4_t negone = vdupq_n_f32(-1.0f); + const float32x4_t mulby127 = vdupq_n_f32(127.0f); + int8_t *mmdst = (int8_t *) dst; + while (i >= 16) { /* 16 * float32 */ + const int32x4_t ints1 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */ + const int32x4_t ints2 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+4)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */ + const int32x4_t ints3 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+8)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */ + const int32x4_t ints4 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+12)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */ + const int8x8_t i8lo = vmovn_s16(vcombine_s16(vmovn_s32(ints1), vmovn_s32(ints2))); /* narrow to sint16, combine, narrow to sint8 */ + const int8x8_t i8hi = vmovn_s16(vcombine_s16(vmovn_s32(ints3), vmovn_s32(ints4))); /* narrow to sint16, combine, narrow to sint8 */ + vst1q_s8(mmdst, vcombine_s8(i8lo, i8hi)); /* combine to int8x16_t, store out */ + i -= 16; src += 16; mmdst += 16; + } + 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++; + } + + cvt->len_cvt /= 4; + if (cvt->filters[++cvt->filter_index]) { + cvt->filters[cvt->filter_index](cvt, AUDIO_S8); + } +} + +static void SDLCALL +SDL_Convert_F32_to_U8_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format) +{ + const float *src = (const float *) cvt->buf; + Uint8 *dst = (Uint8 *) cvt->buf; + int i; + + LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_U8 (using NEON)"); + + /* Get dst aligned to 16 bytes */ + for (i = cvt->len_cvt / sizeof (float); 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); + } + } + + SDL_assert(!i || ((((size_t) dst) & 15) == 0)); + + /* Make sure src is aligned too. */ + if ((((size_t) src) & 15) == 0) { + /* Aligned! Do NEON blocks as long as we have 16 bytes available. */ + const float32x4_t one = vdupq_n_f32(1.0f); + const float32x4_t negone = vdupq_n_f32(-1.0f); + const float32x4_t mulby127 = vdupq_n_f32(127.0f); + uint8_t *mmdst = (uint8_t *) dst; + while (i >= 16) { /* 16 * float32 */ + const uint32x4_t uints1 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), one), mulby127)); /* load 4 floats, clamp, convert to uint32 */ + const uint32x4_t uints2 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+4)), one), one), mulby127)); /* load 4 floats, clamp, convert to uint32 */ + const uint32x4_t uints3 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+8)), one), one), mulby127)); /* load 4 floats, clamp, convert to uint32 */ + const uint32x4_t uints4 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+12)), one), one), mulby127)); /* load 4 floats, clamp, convert to uint32 */ + const uint8x8_t ui8lo = vmovn_u16(vcombine_u16(vmovn_u32(uints1), vmovn_u32(uints2))); /* narrow to uint16, combine, narrow to uint8 */ + const uint8x8_t ui8hi = vmovn_u16(vcombine_u16(vmovn_u32(uints3), vmovn_u32(uints4))); /* narrow to uint16, combine, narrow to uint8 */ + vst1q_u8(mmdst, vcombine_u8(ui8lo, ui8hi)); /* combine to uint8x16_t, store out */ + i -= 16; src += 16; mmdst += 16; + } + + 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++; + } + + cvt->len_cvt /= 4; + if (cvt->filters[++cvt->filter_index]) { + cvt->filters[cvt->filter_index](cvt, AUDIO_U8); + } +} + +static void SDLCALL +SDL_Convert_F32_to_S16_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format) +{ + const float *src = (const float *) cvt->buf; + Sint16 *dst = (Sint16 *) cvt->buf; + int i; + + LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S16 (using NEON)"); + + /* Get dst aligned to 16 bytes */ + for (i = cvt->len_cvt / sizeof (float); 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); + } + } + + SDL_assert(!i || ((((size_t) dst) & 15) == 0)); + + /* Make sure src is aligned too. */ + if ((((size_t) src) & 15) == 0) { + /* Aligned! Do NEON blocks as long as we have 16 bytes available. */ + const float32x4_t one = vdupq_n_f32(1.0f); + const float32x4_t negone = vdupq_n_f32(-1.0f); + const float32x4_t mulby32767 = vdupq_n_f32(32767.0f); + int16_t *mmdst = (int16_t *) dst; + while (i >= 8) { /* 8 * float32 */ + const int32x4_t ints1 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), mulby32767)); /* load 4 floats, clamp, convert to sint32 */ + const int32x4_t ints2 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+4)), one), mulby32767)); /* load 4 floats, clamp, convert to sint32 */ + vst1q_s16(mmdst, vcombine_s16(vmovn_s32(ints1), vmovn_s32(ints2))); /* narrow to sint16, combine, store out. */ + i -= 8; src += 8; mmdst += 8; + } + 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++; + } + + cvt->len_cvt /= 2; + if (cvt->filters[++cvt->filter_index]) { + cvt->filters[cvt->filter_index](cvt, AUDIO_S16SYS); + } +} + +static void SDLCALL +SDL_Convert_F32_to_U16_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format) +{ + const float *src = (const float *) cvt->buf; + Uint16 *dst = (Uint16 *) cvt->buf; + int i; + + LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_U16 (using NEON)"); + + /* Get dst aligned to 16 bytes */ + for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) { + const float sample = *src; + if (sample >= 1.0f) { + *dst = 65535; + } else if (sample <= -1.0f) { + *dst = 0; + } else { + *dst = (Uint16)((sample + 1.0f) * 32767.0f); + } + } + + SDL_assert(!i || ((((size_t) dst) & 15) == 0)); + + /* Make sure src is aligned too. */ + if ((((size_t) src) & 15) == 0) { + /* Aligned! Do NEON blocks as long as we have 16 bytes available. */ + const float32x4_t one = vdupq_n_f32(1.0f); + const float32x4_t negone = vdupq_n_f32(-1.0f); + const float32x4_t mulby32767 = vdupq_n_f32(32767.0f); + uint16_t *mmdst = (uint16_t *) dst; + while (i >= 8) { /* 8 * float32 */ + const uint32x4_t uints1 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), one), mulby32767)); /* load 4 floats, clamp, convert to uint32 */ + const uint32x4_t uints2 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src+4)), one), one), mulby32767)); /* load 4 floats, clamp, convert to uint32 */ + vst1q_u16(mmdst, vcombine_u16(vmovn_u32(uints1), vmovn_u32(uints2))); /* narrow to uint16, combine, store out. */ + i -= 8; src += 8; mmdst += 8; + } + dst = (Uint16 *) mmdst; + } + + /* Finish off any leftovers with scalar operations. */ + while (i) { + const float sample = *src; + if (sample >= 1.0f) { + *dst = 65535; + } else if (sample <= -1.0f) { + *dst = 0; + } else { + *dst = (Uint16)((sample + 1.0f) * 32767.0f); + } + i--; src++; dst++; + } + + cvt->len_cvt /= 2; + if (cvt->filters[++cvt->filter_index]) { + cvt->filters[cvt->filter_index](cvt, AUDIO_U16SYS); + } +} + +static void SDLCALL +SDL_Convert_F32_to_S32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format) +{ + const float *src = (const float *) cvt->buf; + Sint32 *dst = (Sint32 *) cvt->buf; + int i; + + LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S32 (using NEON)"); + + /* Get dst aligned to 16 bytes */ + for (i = cvt->len_cvt / sizeof (float); i && (((size_t) dst) & 15); --i, ++src, ++dst) { + const float sample = *src; + if (sample >= 1.0f) { + *dst = 2147483647; + } else if (sample <= -1.0f) { + *dst = -2147483648; + } else { + *dst = ((Sint32)(sample * 8388607.0f)) << 8; + } + } + + SDL_assert(!i || ((((size_t) dst) & 15) == 0)); + SDL_assert(!i || ((((size_t) src) & 15) == 0)); + + { + /* Aligned! Do NEON blocks as long as we have 16 bytes available. */ + const float32x4_t one = vdupq_n_f32(1.0f); + const float32x4_t negone = vdupq_n_f32(-1.0f); + const float32x4_t mulby8388607 = vdupq_n_f32(8388607.0f); + int32_t *mmdst = (int32_t *) dst; + while (i >= 4) { /* 4 * float32 */ + vst1q_s32(mmdst, vshlq_n_s32(vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), mulby8388607)), 8)); + i -= 4; src += 4; mmdst += 4; + } + 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 = -2147483648; + } else { + *dst = ((Sint32)(sample * 8388607.0f)) << 8; + } + i--; src++; dst++; + } + + if (cvt->filters[++cvt->filter_index]) { + cvt->filters[cvt->filter_index](cvt, AUDIO_S32SYS); + } +} +#endif + + + void SDL_ChooseAudioConverters(void) { static SDL_bool converters_chosen = SDL_FALSE; @@ -817,6 +1412,13 @@ void SDL_ChooseAudioConverters(void) } #endif +#if HAVE_NEON_INTRINSICS + if (SDL_HasNEON()) { + SET_CONVERTER_FUNCS(NEON); + return; + } +#endif + #if NEED_SCALAR_CONVERTER_FALLBACKS SET_CONVERTER_FUNCS(Scalar); #endif diff --git a/Source/3rdParty/SDL2/src/audio/SDL_sysaudio.h b/Source/3rdParty/SDL2/src/audio/SDL_sysaudio.h index f0e1f3d..579dea5 100644 --- a/Source/3rdParty/SDL2/src/audio/SDL_sysaudio.h +++ b/Source/3rdParty/SDL2/src/audio/SDL_sysaudio.h @@ -98,8 +98,10 @@ typedef struct SDL_AudioDriverImpl typedef struct SDL_AudioDeviceItem { void *handle; + char *name; + char *original_name; + int dupenum; struct SDL_AudioDeviceItem *next; - char name[SDL_VARIABLE_LENGTH_ARRAY]; } SDL_AudioDeviceItem; diff --git a/Source/3rdParty/SDL2/src/audio/alsa/SDL_alsa_audio.c b/Source/3rdParty/SDL2/src/audio/alsa/SDL_alsa_audio.c index 2dba1ff..eff192b 100644 --- a/Source/3rdParty/SDL2/src/audio/alsa/SDL_alsa_audio.c +++ b/Source/3rdParty/SDL2/src/audio/alsa/SDL_alsa_audio.c @@ -22,6 +22,10 @@ #if SDL_AUDIO_DRIVER_ALSA +#ifndef SDL_ALSA_NON_BLOCKING +#define SDL_ALSA_NON_BLOCKING 0 +#endif + /* Allow access to a raw mixing buffer */ #include <sys/types.h> @@ -90,6 +94,7 @@ static int (*ALSA_snd_pcm_reset)(snd_pcm_t *); static int (*ALSA_snd_device_name_hint) (int, const char *, void ***); static char* (*ALSA_snd_device_name_get_hint) (const void *, const char *); static int (*ALSA_snd_device_name_free_hint) (void **); +static snd_pcm_sframes_t (*ALSA_snd_pcm_avail)(snd_pcm_t *); #ifdef SND_CHMAP_API_VERSION static snd_pcm_chmap_t* (*ALSA_snd_pcm_get_chmap) (snd_pcm_t *); static int (*ALSA_snd_pcm_chmap_print) (const snd_pcm_chmap_t *map, size_t maxlen, char *buf); @@ -158,6 +163,7 @@ load_alsa_syms(void) SDL_ALSA_SYM(snd_device_name_hint); SDL_ALSA_SYM(snd_device_name_get_hint); SDL_ALSA_SYM(snd_device_name_free_hint); + SDL_ALSA_SYM(snd_pcm_avail); #ifdef SND_CHMAP_API_VERSION SDL_ALSA_SYM(snd_pcm_get_chmap); SDL_ALSA_SYM(snd_pcm_chmap_print); @@ -243,7 +249,24 @@ get_audio_device(void *handle, const int channels) static void ALSA_WaitDevice(_THIS) { - /* We're in blocking mode, so there's nothing to do here */ +#if SDL_ALSA_NON_BLOCKING + const snd_pcm_sframes_t needed = (snd_pcm_sframes_t) this->spec.samples; + while (SDL_AtomicGet(&this->enabled)) { + const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(this->hidden->pcm_handle); + if ((rc < 0) && (rc != -EAGAIN)) { + /* Hmm, not much we can do - abort */ + fprintf(stderr, "ALSA snd_pcm_avail failed (unrecoverable): %s\n", + ALSA_snd_strerror(rc)); + SDL_OpenedAudioDeviceDisconnected(this); + return; + } else if (rc < needed) { + const Uint32 delay = ((needed - (SDL_max(rc, 0))) * 1000) / this->spec.freq; + SDL_Delay(SDL_max(delay, 10)); + } else { + break; /* ready to go! */ + } + } +#endif } @@ -422,7 +445,7 @@ static void ALSA_CloseDevice(_THIS) { if (this->hidden->pcm_handle) { - /* Wait for the submitted audio to drain + /* Wait for the submitted audio to drain ALSA_snd_pcm_drop() can hang, so don't use that. */ Uint32 delay = ((this->spec.samples * 1000) / this->spec.freq) * 2; @@ -435,35 +458,45 @@ ALSA_CloseDevice(_THIS) } static int -ALSA_finalize_hardware(_THIS, snd_pcm_hw_params_t *hwparams, int override) +ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params) { int status; + snd_pcm_hw_params_t *hwparams; snd_pcm_uframes_t bufsize; + snd_pcm_uframes_t persize; - /* "set" the hardware with the desired parameters */ - status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams); + /* Copy the hardware parameters for this setup */ + snd_pcm_hw_params_alloca(&hwparams); + ALSA_snd_pcm_hw_params_copy(hwparams, params); + + /* Prioritize matching the period size to the requested buffer size */ + persize = this->spec.samples; + status = ALSA_snd_pcm_hw_params_set_period_size_near( + this->hidden->pcm_handle, hwparams, &persize, NULL); if ( status < 0 ) { return(-1); } - /* Get samples for the actual buffer size */ - status = ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize); + /* Next try to restrict the parameters to having only two periods */ + bufsize = this->spec.samples * 2; + status = ALSA_snd_pcm_hw_params_set_buffer_size_near( + this->hidden->pcm_handle, hwparams, &bufsize); if ( status < 0 ) { return(-1); } - if ( !override && bufsize != this->spec.samples * 2 ) { + + /* "set" the hardware with the desired parameters */ + status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams); + if ( status < 0 ) { return(-1); } - /* !!! FIXME: Is this safe to do? */ - this->spec.samples = bufsize / 2; + this->spec.samples = persize; /* This is useful for debugging */ if ( SDL_getenv("SDL_AUDIO_ALSA_DEBUG") ) { - snd_pcm_uframes_t persize = 0; unsigned int periods = 0; - ALSA_snd_pcm_hw_params_get_period_size(hwparams, &persize, NULL); ALSA_snd_pcm_hw_params_get_periods(hwparams, &periods, NULL); fprintf(stderr, @@ -475,78 +508,6 @@ ALSA_finalize_hardware(_THIS, snd_pcm_hw_params_t *hwparams, int override) } static int -ALSA_set_period_size(_THIS, snd_pcm_hw_params_t *params, int override) -{ - const char *env; - int status; - snd_pcm_hw_params_t *hwparams; - snd_pcm_uframes_t frames; - unsigned int periods; - - /* Copy the hardware parameters for this setup */ - snd_pcm_hw_params_alloca(&hwparams); - ALSA_snd_pcm_hw_params_copy(hwparams, params); - - if ( !override ) { - env = SDL_getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE"); - if ( env ) { - override = SDL_atoi(env); - if ( override == 0 ) { - return(-1); - } - } - } - - frames = this->spec.samples; - status = ALSA_snd_pcm_hw_params_set_period_size_near( - this->hidden->pcm_handle, hwparams, &frames, NULL); - if ( status < 0 ) { - return(-1); - } - - periods = 2; - status = ALSA_snd_pcm_hw_params_set_periods_near( - this->hidden->pcm_handle, hwparams, &periods, NULL); - if ( status < 0 ) { - return(-1); - } - - return ALSA_finalize_hardware(this, hwparams, override); -} - -static int -ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params, int override) -{ - const char *env; - int status; - snd_pcm_hw_params_t *hwparams; - snd_pcm_uframes_t frames; - - /* Copy the hardware parameters for this setup */ - snd_pcm_hw_params_alloca(&hwparams); - ALSA_snd_pcm_hw_params_copy(hwparams, params); - - if ( !override ) { - env = SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE"); - if ( env ) { - override = SDL_atoi(env); - if ( override == 0 ) { - return(-1); - } - } - } - - frames = this->spec.samples * 2; - status = ALSA_snd_pcm_hw_params_set_buffer_size_near( - this->hidden->pcm_handle, hwparams, &frames); - if ( status < 0 ) { - return(-1); - } - - return ALSA_finalize_hardware(this, hwparams, override); -} - -static int ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) { int status = 0; @@ -692,14 +653,11 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) this->spec.freq = rate; /* Set the buffer size, in samples */ - if ( ALSA_set_period_size(this, hwparams, 0) < 0 && - ALSA_set_buffer_size(this, hwparams, 0) < 0 ) { - /* Failed to set desired buffer size, do the best you can... */ - status = ALSA_set_period_size(this, hwparams, 1); - if (status < 0) { - return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status)); - } + status = ALSA_set_buffer_size(this, hwparams); + if (status < 0) { + return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status)); } + /* Set the software parameters */ snd_pcm_sw_params_alloca(&swparams); status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams); @@ -737,9 +695,11 @@ ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen); } + #if !SDL_ALSA_NON_BLOCKING if (!iscapture) { ALSA_snd_pcm_nonblock(pcm_handle, 0); } + #endif /* We're ready to rock and roll. :-) */ return 0; diff --git a/Source/3rdParty/SDL2/src/audio/android/SDL_androidaudio.c b/Source/3rdParty/SDL2/src/audio/android/SDL_androidaudio.c index 7a25424..77a5f0d 100644 --- a/Source/3rdParty/SDL2/src/audio/android/SDL_androidaudio.c +++ b/Source/3rdParty/SDL2/src/audio/android/SDL_androidaudio.c @@ -57,7 +57,9 @@ ANDROIDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) test_format = SDL_FirstAudioFormat(this->spec.format); while (test_format != 0) { /* no "UNKNOWN" constant */ - if ((test_format == AUDIO_U8) || (test_format == AUDIO_S16LSB)) { + if ((test_format == AUDIO_U8) || + (test_format == AUDIO_S16) || + (test_format == AUDIO_F32)) { this->spec.format = test_format; break; } @@ -69,25 +71,8 @@ ANDROIDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) return SDL_SetError("No compatible audio format!"); } - if (this->spec.channels > 1) { - this->spec.channels = 2; - } else { - this->spec.channels = 1; - } - - if (this->spec.freq < 8000) { - this->spec.freq = 8000; - } - if (this->spec.freq > 48000) { - this->spec.freq = 48000; - } - - /* TODO: pass in/return a (Java) device ID */ - this->spec.samples = Android_JNI_OpenAudioDevice(iscapture, this->spec.freq, this->spec.format == AUDIO_U8 ? 0 : 1, this->spec.channels, this->spec.samples); - - if (this->spec.samples == 0) { - /* Init failed? */ - return SDL_SetError("Java-side initialization failed!"); + if (Android_JNI_OpenAudioDevice(iscapture, &this->spec) < 0) { + return -1; } SDL_CalculateAudioSpec(&this->spec); diff --git a/Source/3rdParty/SDL2/src/audio/arts/SDL_artsaudio.c b/Source/3rdParty/SDL2/src/audio/arts/SDL_artsaudio.c index 4e3ebf2..47bad4b 100644 --- a/Source/3rdParty/SDL2/src/audio/arts/SDL_artsaudio.c +++ b/Source/3rdParty/SDL2/src/audio/arts/SDL_artsaudio.c @@ -39,7 +39,7 @@ #include "SDL_name.h" #include "SDL_loadso.h" #else -#define SDL_NAME(X) X +#define SDL_NAME(X) X #endif #ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC diff --git a/Source/3rdParty/SDL2/src/audio/coreaudio/SDL_coreaudio.h b/Source/3rdParty/SDL2/src/audio/coreaudio/SDL_coreaudio.h index 7ce8b8d..dcce3f7 100644 --- a/Source/3rdParty/SDL2/src/audio/coreaudio/SDL_coreaudio.h +++ b/Source/3rdParty/SDL2/src/audio/coreaudio/SDL_coreaudio.h @@ -45,16 +45,14 @@ struct SDL_PrivateAudioData { - SDL_Thread *thread; AudioQueueRef audioQueue; + int numAudioBuffers; AudioQueueBufferRef *audioBuffer; void *buffer; - UInt32 bufferOffset; UInt32 bufferSize; AudioStreamBasicDescription strdesc; - SDL_sem *ready_semaphore; - char *thread_error; - SDL_atomic_t shutdown; + SDL_bool refill; + SDL_AudioStream *capturestream; #if MACOSX_COREAUDIO AudioDeviceID deviceID; #else diff --git a/Source/3rdParty/SDL2/src/audio/coreaudio/SDL_coreaudio.m b/Source/3rdParty/SDL2/src/audio/coreaudio/SDL_coreaudio.m index 92f5f12..59242f9 100644 --- a/Source/3rdParty/SDL2/src/audio/coreaudio/SDL_coreaudio.m +++ b/Source/3rdParty/SDL2/src/audio/coreaudio/SDL_coreaudio.m @@ -26,6 +26,7 @@ #include "SDL_audio.h" #include "SDL_hints.h" +#include "SDL_timer.h" #include "../SDL_audio_c.h" #include "../SDL_sysaudio.h" #include "SDL_coreaudio.h" @@ -354,7 +355,7 @@ static BOOL update_audio_session(_THIS, SDL_bool open) return NO; } - if (open_playback_devices + open_capture_devices == 1) { + if (open && (open_playback_devices + open_capture_devices) == 1) { if (![session setActive:YES error:&err]) { NSString *desc = err.description; SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String); @@ -391,10 +392,10 @@ static BOOL update_audio_session(_THIS, SDL_bool open) if (this->hidden->interruption_listener != NULL) { SDLInterruptionListener *listener = nil; listener = (SDLInterruptionListener *) CFBridgingRelease(this->hidden->interruption_listener); + [center removeObserver:listener]; @synchronized (listener) { listener.device = NULL; } - [center removeObserver:listener]; } } } @@ -409,43 +410,27 @@ static void outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) { SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData; - if (SDL_AtomicGet(&this->hidden->shutdown)) { - return; /* don't do anything. */ - } + SDL_assert(inBuffer->mAudioDataBytesCapacity == this->hidden->bufferSize); + SDL_memcpy(inBuffer->mAudioData, this->hidden->buffer, this->hidden->bufferSize); + SDL_memset(this->hidden->buffer, '\0', this->hidden->bufferSize); /* zero out in case we have to fill again without new data. */ + inBuffer->mAudioDataByteSize = this->hidden->bufferSize; + AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL); + this->hidden->refill = SDL_TRUE; +} - if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) { - /* Supply silence if audio is not enabled or paused */ - SDL_memset(inBuffer->mAudioData, this->spec.silence, inBuffer->mAudioDataBytesCapacity); - } else { - UInt32 remaining = inBuffer->mAudioDataBytesCapacity; - Uint8 *ptr = (Uint8 *) inBuffer->mAudioData; - - while (remaining > 0) { - UInt32 len; - if (this->hidden->bufferOffset >= this->hidden->bufferSize) { - /* Generate the data */ - SDL_LockMutex(this->mixer_lock); - (*this->callbackspec.callback)(this->callbackspec.userdata, - this->hidden->buffer, this->hidden->bufferSize); - SDL_UnlockMutex(this->mixer_lock); - this->hidden->bufferOffset = 0; - } +static Uint8 * +COREAUDIO_GetDeviceBuf(_THIS) +{ + return this->hidden->buffer; +} - len = this->hidden->bufferSize - this->hidden->bufferOffset; - if (len > remaining) { - len = remaining; - } - SDL_memcpy(ptr, (char *)this->hidden->buffer + - this->hidden->bufferOffset, len); - ptr = ptr + len; - remaining -= len; - this->hidden->bufferOffset += len; - } +static void +COREAUDIO_WaitDevice(_THIS) +{ + while (SDL_AtomicGet(&this->enabled) && !this->hidden->refill) { + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1); } - - AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL); - - inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity; + this->hidden->refill = SDL_FALSE; } static void @@ -454,36 +439,46 @@ inputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer const AudioStreamPacketDescription *inPacketDescs ) { SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData; - - if (SDL_AtomicGet(&this->shutdown)) { - return; /* don't do anything. */ + if (SDL_AtomicGet(&this->enabled)) { + SDL_AudioStream *stream = this->hidden->capturestream; + if (SDL_AudioStreamPut(stream, inBuffer->mAudioData, inBuffer->mAudioDataByteSize) == -1) { + /* yikes, out of memory or something. I guess drop the buffer. Our WASAPI target kills the device in this case, though */ + } + AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL); + this->hidden->refill = SDL_TRUE; } +} - /* ignore unless we're active. */ - if (!SDL_AtomicGet(&this->paused) && SDL_AtomicGet(&this->enabled) && !SDL_AtomicGet(&this->paused)) { - const Uint8 *ptr = (const Uint8 *) inBuffer->mAudioData; - UInt32 remaining = inBuffer->mAudioDataByteSize; - while (remaining > 0) { - UInt32 len = this->hidden->bufferSize - this->hidden->bufferOffset; - if (len > remaining) { - len = remaining; - } - - SDL_memcpy((char *)this->hidden->buffer + this->hidden->bufferOffset, ptr, len); - ptr += len; - remaining -= len; - this->hidden->bufferOffset += len; +static int +COREAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen) +{ + SDL_AudioStream *stream = this->hidden->capturestream; + while (SDL_AtomicGet(&this->enabled)) { + const int avail = SDL_AudioStreamAvailable(stream); + if (avail > 0) { + const int cpy = SDL_min(buflen, avail); + SDL_AudioStreamGet(stream, buffer, cpy); + return cpy; + } - if (this->hidden->bufferOffset >= this->hidden->bufferSize) { - SDL_LockMutex(this->mixer_lock); - (*this->callbackspec.callback)(this->callbackspec.userdata, this->hidden->buffer, this->hidden->bufferSize); - SDL_UnlockMutex(this->mixer_lock); - this->hidden->bufferOffset = 0; - } + /* wait for more data, try again. */ + while (SDL_AtomicGet(&this->enabled) && !this->hidden->refill) { + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1); } + this->hidden->refill = SDL_FALSE; } - AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL); + return 0; /* not enabled, giving up. */ +} + +static void +COREAUDIO_FlushCapture(_THIS) +{ + while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, 1) == kCFRunLoopRunHandledSource) { + /* spin. */ + } + this->hidden->refill = SDL_FALSE; + SDL_AudioStreamClear(this->hidden->capturestream); } @@ -541,25 +536,16 @@ COREAUDIO_CloseDevice(_THIS) update_audio_session(this, SDL_FALSE); #endif - /* if callback fires again, feed silence; don't call into the app. */ - SDL_AtomicSet(&this->paused, 1); - if (this->hidden->audioQueue) { AudioQueueDispose(this->hidden->audioQueue, 1); } - if (this->hidden->thread) { - SDL_AtomicSet(&this->hidden->shutdown, 1); - SDL_WaitThread(this->hidden->thread, NULL); - } - - if (this->hidden->ready_semaphore) { - SDL_DestroySemaphore(this->hidden->ready_semaphore); + if (this->hidden->capturestream) { + SDL_FreeAudioStream(this->hidden->capturestream); } /* AudioQueueDispose() frees the actual buffer objects. */ SDL_free(this->hidden->audioBuffer); - SDL_free(this->hidden->thread_error); SDL_free(this->hidden->buffer); SDL_free(this->hidden); @@ -625,6 +611,8 @@ prepare_device(_THIS, void *handle, int iscapture) } #endif + +/* this all happens in the audio thread, since it needs a separate runloop. */ static int prepare_audioqueue(_THIS) { @@ -664,19 +652,6 @@ prepare_audioqueue(_THIS) } #endif - /* Calculate the final parameters for this audio specification */ - SDL_CalculateAudioSpec(&this->spec); - - /* Allocate a sample buffer */ - this->hidden->bufferSize = this->spec.size; - this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize; - - this->hidden->buffer = SDL_malloc(this->hidden->bufferSize); - if (this->hidden->buffer == NULL) { - SDL_OutOfMemory(); - return 0; - } - /* Make sure we can feed the device a minimum amount of time */ double MINIMUM_AUDIO_BUFFER_TIME_MS = 15.0; #if defined(__IPHONEOS__) @@ -691,6 +666,7 @@ prepare_audioqueue(_THIS) numAudioBuffers = ((int)SDL_ceil(MINIMUM_AUDIO_BUFFER_TIME_MS / msecs) * 2); } + this->hidden->numAudioBuffers = numAudioBuffers; this->hidden->audioBuffer = SDL_calloc(1, sizeof (AudioQueueBufferRef) * numAudioBuffers); if (this->hidden->audioBuffer == NULL) { SDL_OutOfMemory(); @@ -717,29 +693,23 @@ prepare_audioqueue(_THIS) return 1; } -static int -audioqueue_thread(void *arg) +static void +COREAUDIO_ThreadInit(_THIS) { - SDL_AudioDevice *this = (SDL_AudioDevice *) arg; const int rc = prepare_audioqueue(this); if (!rc) { - this->hidden->thread_error = SDL_strdup(SDL_GetError()); - SDL_SemPost(this->hidden->ready_semaphore); - return 0; - } - - /* init was successful, alert parent thread and start running... */ - SDL_SemPost(this->hidden->ready_semaphore); - while (!SDL_AtomicGet(&this->hidden->shutdown)) { - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1); - } - - if (!this->iscapture) { /* Drain off any pending playback. */ - const CFTimeInterval secs = (((this->spec.size / (SDL_AUDIO_BITSIZE(this->spec.format) / 8)) / this->spec.channels) / ((CFTimeInterval) this->spec.freq)) * 2.0; - CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 0); + /* !!! FIXME: do this in RunAudio, and maybe block OpenDevice until ThreadInit finishes, too, to report an opening error */ + SDL_OpenedAudioDeviceDisconnected(this); /* oh well. */ } +} - return 0; +static void +COREAUDIO_PrepareToClose(_THIS) +{ + /* run long enough to queue some silence, so we know our actual audio + has been played */ + CFRunLoopRunInMode(kCFRunLoopDefaultMode, (((this->spec.samples * 1000) / this->spec.freq) * 2) / 1000.0f, 0); + AudioQueueStop(this->hidden->audioQueue, 1); } static int @@ -826,28 +796,23 @@ COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) } #endif - /* This has to init in a new thread so it can get its own CFRunLoop. :/ */ - SDL_AtomicSet(&this->hidden->shutdown, 0); - this->hidden->ready_semaphore = SDL_CreateSemaphore(0); - if (!this->hidden->ready_semaphore) { - return -1; /* oh well. */ - } - - this->hidden->thread = SDL_CreateThreadInternal(audioqueue_thread, "AudioQueue thread", 512 * 1024, this); - if (!this->hidden->thread) { - return -1; - } - - SDL_SemWait(this->hidden->ready_semaphore); - SDL_DestroySemaphore(this->hidden->ready_semaphore); - this->hidden->ready_semaphore = NULL; + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(&this->spec); - if ((this->hidden->thread != NULL) && (this->hidden->thread_error != NULL)) { - SDL_SetError("%s", this->hidden->thread_error); - return -1; + if (iscapture) { + this->hidden->capturestream = SDL_NewAudioStream(this->spec.format, this->spec.channels, this->spec.freq, this->spec.format, this->spec.channels, this->spec.freq); + if (!this->hidden->capturestream) { + return -1; /* already set SDL_Error */ + } + } else { + this->hidden->bufferSize = this->spec.size; + this->hidden->buffer = SDL_malloc(this->hidden->bufferSize); + if (this->hidden->buffer == NULL) { + return SDL_OutOfMemory(); + } } - return (this->hidden->thread != NULL) ? 0 : -1; + return 0; } static void @@ -867,6 +832,12 @@ COREAUDIO_Init(SDL_AudioDriverImpl * impl) impl->OpenDevice = COREAUDIO_OpenDevice; impl->CloseDevice = COREAUDIO_CloseDevice; impl->Deinitialize = COREAUDIO_Deinitialize; + impl->ThreadInit = COREAUDIO_ThreadInit; + impl->WaitDevice = COREAUDIO_WaitDevice; + impl->GetDeviceBuf = COREAUDIO_GetDeviceBuf; + impl->PrepareToClose = COREAUDIO_PrepareToClose; + impl->CaptureFromDevice = COREAUDIO_CaptureFromDevice; + impl->FlushCapture = COREAUDIO_FlushCapture; #if MACOSX_COREAUDIO impl->DetectDevices = COREAUDIO_DetectDevices; @@ -876,7 +847,6 @@ COREAUDIO_Init(SDL_AudioDriverImpl * impl) impl->OnlyHasDefaultCaptureDevice = 1; #endif - impl->ProvidesOwnCallbackThread = 1; impl->HasCaptureSupport = 1; return 1; /* this audio target is available. */ diff --git a/Source/3rdParty/SDL2/src/audio/directsound/SDL_directsound.c b/Source/3rdParty/SDL2/src/audio/directsound/SDL_directsound.c index 09b83ae..a943ba2 100644 --- a/Source/3rdParty/SDL2/src/audio/directsound/SDL_directsound.c +++ b/Source/3rdParty/SDL2/src/audio/directsound/SDL_directsound.c @@ -477,8 +477,8 @@ DSOUND_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) SDL_bool tried_format = SDL_FALSE; SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format); LPGUID guid = (LPGUID) handle; - DWORD bufsize; - + DWORD bufsize; + /* Initialize all variables that we clean on shutdown */ this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc((sizeof *this->hidden)); @@ -526,7 +526,7 @@ DSOUND_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) (int) (DSBSIZE_MAX / numchunks)); } else { int rc; - WAVEFORMATEX wfmt; + WAVEFORMATEX wfmt; SDL_zero(wfmt); if (SDL_AUDIO_ISFLOAT(this->spec.format)) { wfmt.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; diff --git a/Source/3rdParty/SDL2/src/audio/jack/SDL_jackaudio.c b/Source/3rdParty/SDL2/src/audio/jack/SDL_jackaudio.c index a252da7..76ff431 100644 --- a/Source/3rdParty/SDL2/src/audio/jack/SDL_jackaudio.c +++ b/Source/3rdParty/SDL2/src/audio/jack/SDL_jackaudio.c @@ -44,7 +44,9 @@ static const char ** (*JACK_jack_get_ports) (jack_client_t *, const char *, cons static jack_nframes_t (*JACK_jack_get_sample_rate) (jack_client_t *); static jack_nframes_t (*JACK_jack_get_buffer_size) (jack_client_t *); static jack_port_t * (*JACK_jack_port_register) (jack_client_t *, const char *, const char *, unsigned long, unsigned long); +static jack_port_t * (*JACK_jack_port_by_name) (jack_client_t *, const char *); static const char * (*JACK_jack_port_name) (const jack_port_t *); +static const char * (*JACK_jack_port_type) (const jack_port_t *); static int (*JACK_jack_connect) (jack_client_t *, const char *, const char *); static int (*JACK_jack_set_process_callback) (jack_client_t *, JackProcessCallback, void *); @@ -135,7 +137,9 @@ load_jack_syms(void) SDL_JACK_SYM(jack_get_sample_rate); SDL_JACK_SYM(jack_get_buffer_size); SDL_JACK_SYM(jack_port_register); + SDL_JACK_SYM(jack_port_by_name); SDL_JACK_SYM(jack_port_name); + SDL_JACK_SYM(jack_port_type); SDL_JACK_SYM(jack_connect); SDL_JACK_SYM(jack_set_process_callback); return 0; @@ -273,10 +277,6 @@ JACK_CloseDevice(_THIS) SDL_DestroySemaphore(this->hidden->iosem); } - if (this->hidden->devports) { - JACK_jack_free(this->hidden->devports); - } - SDL_free(this->hidden->iobuffer); } @@ -292,9 +292,11 @@ JACK_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) const JackProcessCallback callback = iscapture ? jackProcessCaptureCallback : jackProcessPlaybackCallback; const char *sdlportstr = iscapture ? "input" : "output"; const char **devports = NULL; + int *audio_ports; jack_client_t *client = NULL; jack_status_t status; int channels = 0; + int ports = 0; int i; /* Initialize all variables that we clean on shutdown */ @@ -311,15 +313,30 @@ JACK_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) } devports = JACK_jack_get_ports(client, NULL, NULL, JackPortIsPhysical | sysportflags); - this->hidden->devports = devports; if (!devports || !devports[0]) { return SDL_SetError("No physical JACK ports available"); } - while (devports[++channels]) { + while (devports[++ports]) { /* spin to count devports */ } + /* Filter out non-audio ports */ + audio_ports = SDL_calloc(ports, sizeof *audio_ports); + for (i = 0; i < ports; i++) { + const jack_port_t *dport = JACK_jack_port_by_name(client, devports[i]); + const char *type = JACK_jack_port_type(dport); + const int len = SDL_strlen(type); + /* See if type ends with "audio" */ + if (len >= 5 && !SDL_memcmp(type+len-5, "audio", 5)) { + audio_ports[channels++] = i; + } + } + if (channels == 0) { + return SDL_SetError("No physical JACK ports available"); + } + + /* !!! 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. */ @@ -368,16 +385,16 @@ JACK_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) /* once activated, we can connect all the ports. */ for (i = 0; i < channels; i++) { const char *sdlport = JACK_jack_port_name(this->hidden->sdlports[i]); - const char *srcport = iscapture ? devports[i] : sdlport; - const char *dstport = iscapture ? sdlport : devports[i]; + const char *srcport = iscapture ? devports[audio_ports[i]] : sdlport; + const char *dstport = iscapture ? sdlport : devports[audio_ports[i]]; if (JACK_jack_connect(client, srcport, dstport) != 0) { return SDL_SetError("Couldn't connect JACK ports: %s => %s", srcport, dstport); } } /* don't need these anymore. */ - this->hidden->devports = NULL; JACK_jack_free(devports); + SDL_free(audio_ports); /* We're ready to rock and roll. :-) */ return 0; diff --git a/Source/3rdParty/SDL2/src/audio/jack/SDL_jackaudio.h b/Source/3rdParty/SDL2/src/audio/jack/SDL_jackaudio.h index aab199a..5bc04bd 100644 --- a/Source/3rdParty/SDL2/src/audio/jack/SDL_jackaudio.h +++ b/Source/3rdParty/SDL2/src/audio/jack/SDL_jackaudio.h @@ -33,7 +33,6 @@ struct SDL_PrivateAudioData jack_client_t *client; SDL_sem *iosem; float *iobuffer; - const char **devports; jack_port_t **sdlports; }; diff --git a/Source/3rdParty/SDL2/src/audio/pulseaudio/SDL_pulseaudio.c b/Source/3rdParty/SDL2/src/audio/pulseaudio/SDL_pulseaudio.c index 1e98580..053a1c3 100644 --- a/Source/3rdParty/SDL2/src/audio/pulseaudio/SDL_pulseaudio.c +++ b/Source/3rdParty/SDL2/src/audio/pulseaudio/SDL_pulseaudio.c @@ -109,7 +109,7 @@ static pa_operation * (*PULSEAUDIO_pa_stream_drain) (pa_stream *, pa_stream_success_cb_t, void *); static int (*PULSEAUDIO_pa_stream_peek) (pa_stream *, const void **, size_t *); static int (*PULSEAUDIO_pa_stream_drop) (pa_stream *); -static pa_operation * (*PULSEAUDIO_pa_stream_flush) (pa_stream *, +static pa_operation * (*PULSEAUDIO_pa_stream_flush) (pa_stream *, pa_stream_success_cb_t, void *); static int (*PULSEAUDIO_pa_stream_disconnect) (pa_stream *); static void (*PULSEAUDIO_pa_stream_unref) (pa_stream *); diff --git a/Source/3rdParty/SDL2/src/audio/wasapi/SDL_wasapi.c b/Source/3rdParty/SDL2/src/audio/wasapi/SDL_wasapi.c index b7c8dda..f517539 100644 --- a/Source/3rdParty/SDL2/src/audio/wasapi/SDL_wasapi.c +++ b/Source/3rdParty/SDL2/src/audio/wasapi/SDL_wasapi.c @@ -725,6 +725,12 @@ WASAPI_ThreadDeinit(_THIS) WASAPI_PlatformThreadDeinit(this); } +void +WASAPI_BeginLoopIteration(_THIS) +{ + /* no-op. */ +} + static void WASAPI_Deinitialize(void) { diff --git a/Source/3rdParty/SDL2/src/audio/wasapi/SDL_wasapi_win32.c b/Source/3rdParty/SDL2/src/audio/wasapi/SDL_wasapi_win32.c index 8b55582..9d7c159 100644 --- a/Source/3rdParty/SDL2/src/audio/wasapi/SDL_wasapi_win32.c +++ b/Source/3rdParty/SDL2/src/audio/wasapi/SDL_wasapi_win32.c @@ -351,10 +351,42 @@ WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery) } +typedef struct +{ + LPWSTR devid; + char *devname; +} EndpointItem; + +static int sort_endpoints(const void *_a, const void *_b) +{ + LPWSTR a = ((const EndpointItem *) _a)->devid; + LPWSTR b = ((const EndpointItem *) _b)->devid; + if (!a && b) { + return -1; + } else if (a && !b) { + return 1; + } + + while (SDL_TRUE) { + if (*a < *b) { + return -1; + } else if (*a > *b) { + return 1; + } else if (*a == 0) { + break; + } + a++; + b++; + } + + return 0; +} + static void WASAPI_EnumerateEndpointsForFlow(const SDL_bool iscapture) { IMMDeviceCollection *collection = NULL; + EndpointItem *items; UINT i, total; /* Note that WASAPI separates "adapter devices" from "audio endpoint devices" @@ -369,22 +401,36 @@ WASAPI_EnumerateEndpointsForFlow(const SDL_bool iscapture) return; } + items = (EndpointItem *) SDL_calloc(total, sizeof (EndpointItem)); + if (!items) { + return; /* oh well. */ + } + for (i = 0; i < total; i++) { + EndpointItem *item = items + i; IMMDevice *device = NULL; if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &device))) { - LPWSTR devid = NULL; - if (SUCCEEDED(IMMDevice_GetId(device, &devid))) { - char *devname = GetWasapiDeviceName(device); - if (devname) { - WASAPI_AddDevice(iscapture, devname, devid); - SDL_free(devname); - } - CoTaskMemFree(devid); + if (SUCCEEDED(IMMDevice_GetId(device, &item->devid))) { + item->devname = GetWasapiDeviceName(device); } IMMDevice_Release(device); } } + /* sort the list of devices by their guid so list is consistent between runs */ + SDL_qsort(items, total, sizeof (*items), sort_endpoints); + + /* Send the sorted list on to the SDL's higher level. */ + for (i = 0; i < total; i++) { + EndpointItem *item = items + i; + if ((item->devid) && (item->devname)) { + WASAPI_AddDevice(iscapture, item->devname, item->devid); + } + SDL_free(item->devname); + CoTaskMemFree(item->devid); + } + + SDL_free(items); IMMDeviceCollection_Release(collection); } @@ -405,12 +451,6 @@ WASAPI_PlatformDeleteActivationHandler(void *handler) SDL_assert(!"This function should have only been called on WinRT."); } -void -WASAPI_BeginLoopIteration(_THIS) -{ - /* no-op. */ -} - #endif /* SDL_AUDIO_DRIVER_WASAPI && !defined(__WINRT__) */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/Source/3rdParty/SDL2/src/audio/wasapi/SDL_wasapi_winrt.cpp b/Source/3rdParty/SDL2/src/audio/wasapi/SDL_wasapi_winrt.cpp index 309ec6a..2ca09de 100644 --- a/Source/3rdParty/SDL2/src/audio/wasapi/SDL_wasapi_winrt.cpp +++ b/Source/3rdParty/SDL2/src/audio/wasapi/SDL_wasapi_winrt.cpp @@ -185,20 +185,9 @@ struct SDL_WasapiActivationHandler : public RuntimeClass< RuntimeClassFlags< Cla HRESULT SDL_WasapiActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation *async) { - HRESULT result = S_OK; - IUnknown *iunknown = nullptr; - const HRESULT ret = async->GetActivateResult(&result, &iunknown); - - if (SUCCEEDED(ret) && SUCCEEDED(result)) { - iunknown->QueryInterface(IID_PPV_ARGS(&device->hidden->client)); - if (device->hidden->client) { - // Just set a flag, since we're probably in a different thread. We'll pick it up and init everything on our own thread to prevent races. - SDL_AtomicSet(&device->hidden->just_activated, 1); - } - } - + // Just set a flag, since we're probably in a different thread. We'll pick it up and init everything on our own thread to prevent races. + SDL_AtomicSet(&device->hidden->just_activated, 1); WASAPI_UnrefDevice(device); - return S_OK; } @@ -236,27 +225,47 @@ WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery) IActivateAudioInterfaceAsyncOperation *async = nullptr; const HRESULT ret = ActivateAudioInterfaceAsync(devid, __uuidof(IAudioClient), nullptr, handler.Get(), &async); - if (async != nullptr) { - async->Release(); - } - - if (FAILED(ret)) { + if (FAILED(ret) || async == nullptr) { + if (async != nullptr) { + async->Release(); + } handler.Get()->Release(); WASAPI_UnrefDevice(_this); return WIN_SetErrorFromHRESULT("WASAPI can't activate requested audio endpoint", ret); } - return 0; -} + /* Spin until the async operation is complete. + * If we don't PrepDevice before leaving this function, the bug list gets LONG: + * - device.spec is not filled with the correct information + * - The 'obtained' spec will be wrong for ALLOW_CHANGE properties + * - SDL_AudioStreams will/will not be allocated at the right time + * - SDL_assert(device->callbackspec.size == device->spec.size) will fail + * - When the assert is ignored, skipping or a buffer overflow will occur + */ + while (!SDL_AtomicCAS(&_this->hidden->just_activated, 1, 0)) { + SDL_Delay(1); + } -void -WASAPI_BeginLoopIteration(_THIS) -{ - if (SDL_AtomicCAS(&_this->hidden->just_activated, 1, 0)) { - if (WASAPI_PrepDevice(_this, SDL_TRUE) == -1) { - SDL_OpenedAudioDeviceDisconnected(_this); - } + HRESULT activateRes = S_OK; + IUnknown *iunknown = nullptr; + const HRESULT getActivateRes = async->GetActivateResult(&activateRes, &iunknown); + async->Release(); + if (FAILED(getActivateRes)) { + return WIN_SetErrorFromHRESULT("Failed to get WASAPI activate result", getActivateRes); + } else if (FAILED(activateRes)) { + return WIN_SetErrorFromHRESULT("Failed to activate WASAPI device", activateRes); + } + + iunknown->QueryInterface(IID_PPV_ARGS(&_this->hidden->client)); + if (!_this->hidden->client) { + return SDL_SetError("Failed to query WASAPI client interface"); + } + + if (WASAPI_PrepDevice(_this, isrecovery) == -1) { + return -1; } + + return 0; } void diff --git a/Source/3rdParty/SDL2/src/audio/winmm/SDL_winmm.c b/Source/3rdParty/SDL2/src/audio/winmm/SDL_winmm.c index 8e5c17b..20426f1 100644 --- a/Source/3rdParty/SDL2/src/audio/winmm/SDL_winmm.c +++ b/Source/3rdParty/SDL2/src/audio/winmm/SDL_winmm.c @@ -78,7 +78,7 @@ static void DetectWave##typ##Devs(void) { \ capstyp##2W caps; \ UINT i; \ for (i = 0; i < devcount; i++) { \ - if (wave##typ##GetDevCaps(i,(LP##capstyp##W)&caps,sizeof(caps))==MMSYSERR_NOERROR) { \ + if (wave##typ##GetDevCaps(i,(LP##capstyp##W)&caps,sizeof(caps))==MMSYSERR_NOERROR) { \ char *name = WIN_LookupAudioDeviceName(caps.szPname,&caps.NameGuid); \ if (name != NULL) { \ SDL_AddAudioDevice((int) iscapture, name, (void *) ((size_t) i+1)); \ @@ -375,8 +375,7 @@ WINMM_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) #endif /* Create the audio buffer semaphore */ - this->hidden->audio_sem = - CreateSemaphore(NULL, iscapture ? 0 : NUM_BUFFERS - 1, NUM_BUFFERS, NULL); + this->hidden->audio_sem = CreateSemaphore(NULL, iscapture ? 0 : NUM_BUFFERS - 1, NUM_BUFFERS, NULL); if (this->hidden->audio_sem == NULL) { return SDL_SetError("Couldn't create semaphore"); } |