diff --git a/miniaudio.h b/miniaudio.h index 69419dd3..cda57c62 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -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); -} - -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) {