Added ResampleFrame_SSE

main
Brick 2023-08-24 19:00:17 +01:00 committed by Ryan C. Gordon
parent 958b3cfaea
commit 5b696996cd
4 changed files with 119 additions and 29 deletions

View File

@ -493,6 +493,7 @@ int SDL_InitAudio(const char *driver_name)
} }
SDL_ChooseAudioConverters(); 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. 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) { if (!device_list_lock) {

View File

@ -84,10 +84,9 @@ static int GetHistoryBufferSampleFrames(const int required_resampler_frames)
#define RESAMPLER_SAMPLES_PER_FRAME (RESAMPLER_ZERO_CROSSINGS * 2) #define RESAMPLER_SAMPLES_PER_FRAME (RESAMPLER_ZERO_CROSSINGS * 2)
#define RESAMPLER_FULL_FILTER_SIZE RESAMPLER_SAMPLES_PER_FRAME * (RESAMPLER_SAMPLES_PER_ZERO_CROSSING + 1) #define RESAMPLER_FULL_FILTER_SIZE (RESAMPLER_SAMPLES_PER_FRAME * (RESAMPLER_SAMPLES_PER_ZERO_CROSSING + 1))
// TODO: Add SIMD-accelerated versions static void ResampleFrame_Scalar(const float* src, float* dst, const float* raw_filter, const float interp, const int chans)
static void ResampleFrame(const float* src, float* dst, const float* raw_filter, const float interp, const int chans)
{ {
int i, chan; int i, chan;
@ -124,33 +123,122 @@ static void ResampleFrame(const float* src, float* dst, const float* raw_filter,
return; return;
} }
// Try and give the compiler a hint about how many channels there are for (chan = 0; chan < chans; chan++) {
if (chans < 1 || chans > 8) { float f = 0.0f;
SDL_assert(!"Invalid channel count");
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, const float interp, const 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_shuffle_ps(f0, f0, _MM_SHUFFLE(3, 3, 2, 2));
f0 = _mm_shuffle_ps(f0, f0, _MM_SHUFFLE(1, 1, 0, 0));
g1 = _mm_shuffle_ps(f1, f1, _MM_SHUFFLE(3, 3, 2, 2));
f1 = _mm_shuffle_ps(f1, f1, _MM_SHUFFLE(1, 1, 0, 0));
f2 = _mm_shuffle_ps(f2, f2, _MM_SHUFFLE(1, 1, 0, 0));
// 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; return;
} }
// Calculate the result in-place if (chans == 1) {
for (chan = 0; chan < chans; ++chan) { // Multiply the filter by the input
dst[chan] = 0.0f; 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;
} }
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) { float filter[RESAMPLER_SAMPLES_PER_FRAME];
const float* inputs = &src[i * chans]; _mm_storeu_ps(filter + 0, f0);
const float scale = filter[i]; _mm_storeu_ps(filter + 4, f1);
_mm_storel_pi((__m64*)(filter + 8), f2);
for (chan = 0; chan < chans; chan++) { int i, chan = 0;
dst[chan] += inputs[chan] * scale;
for (; chan + 4 <= chans; chan++) {
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, const float interp, const int chans);
static float FullResamplerFilter[RESAMPLER_FULL_FILTER_SIZE]; static float FullResamplerFilter[RESAMPLER_FULL_FILTER_SIZE];
void SDL_SetupAudioResampler() void SDL_SetupAudioResampler()
{ {
// Build a table combining the left and right wings, for faster access static SDL_bool setup = SDL_FALSE;
if (setup) {
return;
}
// Build a table combining the left and right wings, for faster access
int i, j; int i, j;
for (i = 0; i < RESAMPLER_SAMPLES_PER_ZERO_CROSSING; ++i) { for (i = 0; i < RESAMPLER_SAMPLES_PER_ZERO_CROSSING; ++i) {
@ -171,6 +259,16 @@ void SDL_SetupAudioResampler()
FullResamplerFilter[lwing] = 0.0f; FullResamplerFilter[lwing] = 0.0f;
FullResamplerFilter[rwing] = 0.0f; FullResamplerFilter[rwing] = 0.0f;
} }
ResampleFrame = ResampleFrame_Scalar;
#ifdef SDL_SSE_INTRINSICS
if (SDL_HasSSE()) {
ResampleFrame = ResampleFrame_SSE;
}
#endif
setup = SDL_TRUE;
} }
static void ResampleAudio(const int chans, const float *inbuf, const int inframes, float *outbuf, const int outframes, static void ResampleAudio(const int chans, const float *inbuf, const int inframes, float *outbuf, const int outframes,
@ -651,6 +749,7 @@ SDL_AudioStream *SDL_CreateAudioStream(const SDL_AudioSpec *src_spec, const SDL_
// Make sure we've chosen audio conversion functions (SIMD, scalar, etc.) // Make sure we've chosen audio conversion functions (SIMD, scalar, etc.)
SDL_ChooseAudioConverters(); // !!! FIXME: let's do this during SDL_Init SDL_ChooseAudioConverters(); // !!! FIXME: let's do this during SDL_Init
SDL_SetupAudioResampler();
retval->packetlen = packetlen; retval->packetlen = packetlen;
SDL_memcpy(&retval->src_spec, src_spec, sizeof (SDL_AudioSpec)); SDL_memcpy(&retval->src_spec, src_spec, sizeof (SDL_AudioSpec));
@ -825,17 +924,12 @@ static Uint8 *EnsureStreamWorkBufferSize(SDL_AudioStream *stream, size_t newlen)
static int CalculateAudioStreamWorkBufSize(const SDL_AudioStream *stream, int input_frames, int output_frames) static int CalculateAudioStreamWorkBufSize(const SDL_AudioStream *stream, int input_frames, int output_frames)
{ {
int workbuflen = SDL_max(input_frames, output_frames) * stream->max_sample_frame_size; int workbuf_frames = input_frames + (stream->resampler_padding_frames * 2);
int workbuflen = workbuf_frames * stream->max_sample_frame_size;
if (stream->resample_rate) { if (stream->resample_rate) {
int resample_frame_size = stream->pre_resample_channels * sizeof(float);
// Calculate space needed to move to format/channels used for resampling stage.
int inputlen = (input_frames + (stream->resampler_padding_frames * 2)) * resample_frame_size;
workbuflen = SDL_max(workbuflen, inputlen);
// Calculate space needed after resample (which lives in a second copy in the same buffer). // Calculate space needed after resample (which lives in a second copy in the same buffer).
int resample_frame_size = stream->pre_resample_channels * sizeof(float);
workbuflen += output_frames * resample_frame_size; workbuflen += output_frames * resample_frame_size;
} }
@ -888,7 +982,6 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
input_frames = GetResamplerNeededInputFrames(output_frames, resample_rate, stream->resample_offset); input_frames = GetResamplerNeededInputFrames(output_frames, resample_rate, stream->resample_offset);
} }
// !!! FIXME: this could be less aggressive about allocation, if we decide the necessary size at each stage and select the maximum required.
int work_buffer_capacity = CalculateAudioStreamWorkBufSize(stream, input_frames, output_frames); int work_buffer_capacity = CalculateAudioStreamWorkBufSize(stream, input_frames, output_frames);
Uint8* work_buffer = EnsureStreamWorkBufferSize(stream, work_buffer_capacity); Uint8* work_buffer = EnsureStreamWorkBufferSize(stream, work_buffer_capacity);

View File

@ -962,8 +962,6 @@ void (*SDL_Convert_F32_to_U8)(Uint8 *dst, const float *src, int num_samples) = N
void (*SDL_Convert_F32_to_S16)(Sint16 *dst, const float *src, int num_samples) = NULL; void (*SDL_Convert_F32_to_S16)(Sint16 *dst, const float *src, int num_samples) = NULL;
void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_samples) = NULL; void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_samples) = NULL;
extern void SDL_SetupAudioResampler(void);
void SDL_ChooseAudioConverters(void) void SDL_ChooseAudioConverters(void)
{ {
static SDL_bool converters_chosen = SDL_FALSE; static SDL_bool converters_chosen = SDL_FALSE;
@ -971,9 +969,6 @@ void SDL_ChooseAudioConverters(void)
return; return;
} }
// FIXME: Hacks on top of hacks.
SDL_SetupAudioResampler();
#define SET_CONVERTER_FUNCS(fntype) \ #define SET_CONVERTER_FUNCS(fntype) \
SDL_Convert_S8_to_F32 = SDL_Convert_S8_to_F32_##fntype; \ SDL_Convert_S8_to_F32 = SDL_Convert_S8_to_F32_##fntype; \
SDL_Convert_U8_to_F32 = SDL_Convert_U8_to_F32_##fntype; \ SDL_Convert_U8_to_F32 = SDL_Convert_U8_to_F32_##fntype; \

View File

@ -72,6 +72,7 @@ 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 (SDL_CreateAudioStream will call it !!! FIXME but probably shouldn't).
extern void SDL_ChooseAudioConverters(void); extern void SDL_ChooseAudioConverters(void);
extern void SDL_SetupAudioResampler(void);
/* Backends should call this as devices are added to the system (such as /* 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 a USB headset being plugged in), and should also be called for