Re: [PATCH v2 0/5] MR9588: dsound: Introduce a cubic spline interpolation resampler.
On Mon Jan 12 10:50:32 2026 +0000, Matteo Bruni wrote:
I should have mentioned it together with a few more things, but I forgot :sweat_smile: This is basically a C port of the default resampler from openal-soft. It's MUCH faster while not being much worse in theoretical quality compared to the old FIR resampler. In practice the old resampler had a couple of shortcomings that lowered the effective quality[*], so I don't think this is much a downgrade quality-wise. Otherwise we could bring in the sinc resampler, which is theoretically very solid and still quite a bit faster than the old FIR resampler. It is sensibly slower than this one though, and slower than Windows 10 / 11 dsound's resampler. I'm not opposed to having it as an option; I did in fact try to make the surrounding code somewhat generic to facilitate that. The SSE2 version gives about a 2x speedup for me. For the records it makes even more of a difference in openal-soft, most likely because we start running into performance limitations in our dsound outside of the resampler proper; I should have a few more patches to improve on that. The SSE2 version is effectively always enabled on (x86) 64-bit. To compile the SSE2 version on 32-bit you need to have `-msse2` in `i386_CFLAGS`, or some other option that includes it (e.g. the typical `-march=nocona` will do that). [*]: The FIR resampler used a fixed Gaussian window instead of the parametrized Kaiser that's customarily used for audio resampling. That can introduce some aliasing or some distortion depending on the input signal. Additionally, the position computation (`total_fir_steps`, `rem`) using floats wasn't exact, which in turn caused some mid-high frequency distortion. In fact I had originally preserved it when porting the cubic resampler and I did see the same kind of distortion in the new output, so I ended up using fixed point for that, which is also what openal-soft does. Thanks for the detailed explanation.
Otherwise we could bring in the sinc resampler, which is theoretically very solid and still quite a bit faster than the old FIR resampler.
I studied the code of the openal-soft sinc resampler, and I think that our resampler is theoretically better: - Our upsampling window width is 65.89 samples, which is much wider than openal-soft's 24. This should result in a sharper pass-band to stop-band transition, which means less aliasing. - openal-soft's downsampling window is limited to 48 samples, whereas we don’t have such a limit. - The way openal-soft expands the upsampling window by linearly interpolating between windows of different sizes is a bit questionable. We expand the window by varying `dsbfirstep`, which should provide a smoother transition.
The FIR resampler used a fixed Gaussian window instead of the parametrized Kaiser that's customarily used for audio resampling.
It shouldn't be too hard to change the window function in `make_fir`.
Additionally, the position computation (`total_fir_steps`, `rem`) using floats wasn't exact, which in turn caused some mid-high frequency distortion.
This can be easily fixed by using integer arithmetic: <details> <summary>Click to expand</summary> ```diff diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c index b50d9f3a8c8..fe1c8a70db3 100644 --- a/dlls/dsound/mixer.c +++ b/dlls/dsound/mixer.c @@ -365,12 +365,12 @@ static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, LONG64 * } for(i = 0; i < count; ++i) { - UINT int_fir_steps = (freqAcc_start + i * dsb->freqAdjustNum) * dsbfirstep / dsb->freqAdjustDen; - float total_fir_steps = (freqAcc_start + i * dsb->freqAdjustNum) * dsbfirstep / (float)dsb->freqAdjustDen; + LONG64 fir_steps_num = (freqAcc_start + i * dsb->freqAdjustNum) * dsbfirstep; + UINT int_fir_steps = fir_steps_num / dsb->freqAdjustDen; UINT ipos = int_fir_steps / dsbfirstep; UINT idx = (ipos + 1) * dsbfirstep - int_fir_steps - 1; - float rem = int_fir_steps + 1.0 - total_fir_steps; + float rem = 1.0 - (fir_steps_num - int_fir_steps * dsb->freqAdjustDen) / (float)dsb->freqAdjustDen; int fir_used = 0; while (idx < fir_len - 1) { ``` </details> I also did some experiments, and I was able to make our resampler 5 times faster without sacrificing the quality by rearranging the FIR array and using SSE. Maybe we should give it a try. I'll try to come up with a draft MR. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9588#note_126626
participants (1)
-
Anton Baskanov (@baskanov)