Resampler: Optimization to the s16 path in the linear resampler.

The main thing here is moving an integer division out of the inner loop.
This commit is contained in:
David Reid
2026-02-05 08:06:52 +10:00
parent 2c14e2e5a7
commit 445aefae06
+32 -22
View File
@@ -3031,6 +3031,12 @@ the input and output sample rates (Nyquist Frequency).
The API for the linear resampler is the same as the main resampler API, only it's called
`ma_linear_resampler`.
The linear resampler supports s16 and f32 sample formats. The s16 path uses purely integer math,
whereas the f32 path will use floating point. For the s16 path, make sure you use reasonable ratios
for the input and output sample rates. It's OK to do make it large like 8000 -> 88200, but it's not
OK to use stupid numbers like 8000 -> 89999. If you need ratios like this, use the f32 path, or use
a different resampler.
10.3.2. Custom Resamplers
-------------------------
@@ -59155,41 +59161,41 @@ MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma
}
}
static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift)
{
ma_int32 b;
ma_int32 c;
ma_int32 r;
#define MA_LINEAR_RESAMPLER_LERP_SHIFT 12
MA_ASSERT(a <= (1<<shift));
b = x * ((1<<shift) - a);
c = y * a;
r = b + c;
return (ma_int16)(r >> shift);
}
static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut)
static MA_INLINE void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_uint32 invSampleRateOut, ma_int16* MA_RESTRICT pFrameOut)
{
ma_uint32 c;
ma_uint32 a;
const ma_uint32 channels = pResampler->channels;
const ma_uint32 shift = 12;
MA_ASSERT(pResampler != NULL);
MA_ASSERT(pFrameOut != NULL);
a = (pResampler->inTimeFrac << shift) / pResampler->sampleRateOut;
/*
The fractional component (inTimeFrac) will be between 0 and the output sample rate. We need to apply a scaling
factor (invSampleRateOut). It is assumed invSampleRateOut has been shifted by MA_LINEAR_RESAMPLER_LERP_SHIFT.
The lerp below is based on the ma_mix_f32_fast(), but with fixed point math.
*/
a = pResampler->inTimeFrac * invSampleRateOut;
MA_ASSUME(channels > 0);
for (c = 0; c < channels; c += 1) {
ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift);
pFrameOut[c] = s;
ma_int16 x, y;
ma_int32 d;
ma_int32 n;
x = pResampler->x0.s16[c];
y = pResampler->x1.s16[c];
d = y - x;
n = d * a;
pFrameOut[c] = (ma_int16)(x + (n >> MA_LINEAR_RESAMPLER_LERP_SHIFT));
}
}
static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float invSampleRateOut, float* MA_RESTRICT pFrameOut)
static MA_INLINE void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float invSampleRateOut, float* MA_RESTRICT pFrameOut)
{
ma_uint32 c;
float a;
@@ -59215,6 +59221,7 @@ static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear
ma_uint64 frameCountOut;
ma_uint64 framesProcessedIn;
ma_uint64 framesProcessedOut;
ma_uint32 invSampleRateOut;
MA_ASSERT(pResampler != NULL);
MA_ASSERT(pFrameCountIn != NULL);
@@ -59226,6 +59233,7 @@ static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear
frameCountOut = *pFrameCountOut;
framesProcessedIn = 0;
framesProcessedOut = 0;
invSampleRateOut = (1 << MA_LINEAR_RESAMPLER_LERP_SHIFT) / pResampler->sampleRateOut;
while (framesProcessedOut < frameCountOut) {
/* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
@@ -59261,7 +59269,7 @@ static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear
/* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
if (pFramesOutS16 != NULL) {
MA_ASSERT(pResampler->inTimeInt == 0);
ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
ma_linear_resampler_interpolate_frame_s16(pResampler, invSampleRateOut, pFramesOutS16);
pFramesOutS16 += pResampler->channels;
}
@@ -59291,6 +59299,7 @@ static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_r
ma_uint64 frameCountOut;
ma_uint64 framesProcessedIn;
ma_uint64 framesProcessedOut;
ma_uint32 invSampleRateOut;
MA_ASSERT(pResampler != NULL);
MA_ASSERT(pFrameCountIn != NULL);
@@ -59302,6 +59311,7 @@ static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_r
frameCountOut = *pFrameCountOut;
framesProcessedIn = 0;
framesProcessedOut = 0;
invSampleRateOut = (1 << MA_LINEAR_RESAMPLER_LERP_SHIFT) / pResampler->sampleRateOut;
while (framesProcessedOut < frameCountOut) {
/* Before interpolating we need to load the buffers. */
@@ -59332,7 +59342,7 @@ static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_r
/* Getting here means the frames have been loaded and we can generate the next output frame. */
if (pFramesOutS16 != NULL) {
MA_ASSERT(pResampler->inTimeInt == 0);
ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
ma_linear_resampler_interpolate_frame_s16(pResampler, invSampleRateOut, pFramesOutS16);
/* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */
if (pResampler->inAdvanceInt == 1 && pResampler->inAdvanceFrac == 0) {