Replace the cubic resampler with a 4-point sinc/lanczos filter
This commit is contained in:
parent
3e60b18989
commit
ab6622a8d6
10
Alc/ALc.c
10
Alc/ALc.c
@ -1009,14 +1009,18 @@ static void alc_initconfig(void)
|
|||||||
DefaultResampler = PointResampler;
|
DefaultResampler = PointResampler;
|
||||||
else if(strcasecmp(str, "linear") == 0)
|
else if(strcasecmp(str, "linear") == 0)
|
||||||
DefaultResampler = LinearResampler;
|
DefaultResampler = LinearResampler;
|
||||||
|
else if(strcasecmp(str, "sinc4") == 0)
|
||||||
|
DefaultResampler = FIR4Resampler;
|
||||||
else if(strcasecmp(str, "cubic") == 0)
|
else if(strcasecmp(str, "cubic") == 0)
|
||||||
DefaultResampler = CubicResampler;
|
{
|
||||||
|
ERR("Resampler option \"cubic\" is deprecated, using sinc4\n");
|
||||||
|
DefaultResampler = FIR4Resampler;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char *end;
|
char *end;
|
||||||
|
|
||||||
n = strtol(str, &end, 0);
|
n = strtol(str, &end, 0);
|
||||||
if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == CubicResampler))
|
if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == FIR4Resampler))
|
||||||
DefaultResampler = n;
|
DefaultResampler = n;
|
||||||
else
|
else
|
||||||
WARN("Invalid resampler: %s\n", str);
|
WARN("Invalid resampler: %s\n", str);
|
||||||
|
29
Alc/mixer.c
29
Alc/mixer.c
@ -43,7 +43,7 @@ static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
|
|||||||
|
|
||||||
extern inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size);
|
extern inline void InitiatePositionArrays(ALuint frac, ALuint increment, ALuint *frac_arr, ALuint *pos_arr, ALuint size);
|
||||||
|
|
||||||
alignas(16) ALfloat CubicLUT[FRACTIONONE][4];
|
alignas(16) ALfloat ResampleCoeffs[FRACTIONONE][4];
|
||||||
|
|
||||||
|
|
||||||
static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
|
static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
|
||||||
@ -94,16 +94,16 @@ static inline ResamplerFunc SelectResampler(enum Resampler resampler)
|
|||||||
return Resample_lerp32_SSE2;
|
return Resample_lerp32_SSE2;
|
||||||
#endif
|
#endif
|
||||||
return Resample_lerp32_C;
|
return Resample_lerp32_C;
|
||||||
case CubicResampler:
|
case FIR4Resampler:
|
||||||
#ifdef HAVE_SSE4_1
|
#ifdef HAVE_SSE4_1
|
||||||
if((CPUCapFlags&CPU_CAP_SSE4_1))
|
if((CPUCapFlags&CPU_CAP_SSE4_1))
|
||||||
return Resample_cubic32_SSE41;
|
return Resample_fir4_32_SSE41;
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_SSE2
|
#ifdef HAVE_SSE2
|
||||||
if((CPUCapFlags&CPU_CAP_SSE2))
|
if((CPUCapFlags&CPU_CAP_SSE2))
|
||||||
return Resample_cubic32_SSE2;
|
return Resample_fir4_32_SSE2;
|
||||||
#endif
|
#endif
|
||||||
return Resample_cubic32_C;
|
return Resample_fir4_32_C;
|
||||||
case ResamplerMax:
|
case ResamplerMax:
|
||||||
/* Shouldn't happen */
|
/* Shouldn't happen */
|
||||||
break;
|
break;
|
||||||
@ -113,17 +113,26 @@ static inline ResamplerFunc SelectResampler(enum Resampler resampler)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static float lanc2(float x)
|
||||||
|
{
|
||||||
|
if(x == 0.0f)
|
||||||
|
return 1.0f;
|
||||||
|
if(fabsf(x) >= 2.0f)
|
||||||
|
return 0.0f;
|
||||||
|
return 2.0f*sinf(x*F_PI)*sinf(x*F_PI/2.0f) /
|
||||||
|
(F_PI*F_PI * x*x);
|
||||||
|
}
|
||||||
|
|
||||||
void aluInitMixer(void)
|
void aluInitMixer(void)
|
||||||
{
|
{
|
||||||
ALuint i;
|
ALuint i;
|
||||||
for(i = 0;i < FRACTIONONE;i++)
|
for(i = 0;i < FRACTIONONE;i++)
|
||||||
{
|
{
|
||||||
ALfloat mu = (ALfloat)i / FRACTIONONE;
|
ALfloat mu = (ALfloat)i / FRACTIONONE;
|
||||||
ALfloat mu2 = mu*mu, mu3 = mu*mu*mu;
|
ResampleCoeffs[i][0] = lanc2(mu - -1.0f);
|
||||||
CubicLUT[i][0] = -0.5f*mu3 + mu2 + -0.5f*mu;
|
ResampleCoeffs[i][1] = lanc2(mu - 0.0f);
|
||||||
CubicLUT[i][1] = 1.5f*mu3 + -2.5f*mu2 + 1.0f;
|
ResampleCoeffs[i][2] = lanc2(mu - 1.0f);
|
||||||
CubicLUT[i][2] = -1.5f*mu3 + 2.0f*mu2 + 0.5f*mu;
|
ResampleCoeffs[i][3] = lanc2(mu - 2.0f);
|
||||||
CubicLUT[i][3] = 0.5f*mu3 + -0.5f*mu2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MixHrtfSamples = SelectHrtfMixer();
|
MixHrtfSamples = SelectHrtfMixer();
|
||||||
|
@ -12,8 +12,8 @@ static inline ALfloat point32(const ALfloat *vals, ALuint UNUSED(frac))
|
|||||||
{ return vals[0]; }
|
{ return vals[0]; }
|
||||||
static inline ALfloat lerp32(const ALfloat *vals, ALuint frac)
|
static inline ALfloat lerp32(const ALfloat *vals, ALuint frac)
|
||||||
{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
|
{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
|
||||||
static inline ALfloat cubic32(const ALfloat *vals, ALuint frac)
|
static inline ALfloat fir4_32(const ALfloat *vals, ALuint frac)
|
||||||
{ return cubic(vals[-1], vals[0], vals[1], vals[2], frac); }
|
{ return resample_fir4(vals[-1], vals[0], vals[1], vals[2], frac); }
|
||||||
|
|
||||||
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint UNUSED(frac),
|
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint UNUSED(frac),
|
||||||
ALuint UNUSED(increment), ALfloat *restrict dst, ALuint numsamples)
|
ALuint UNUSED(increment), ALfloat *restrict dst, ALuint numsamples)
|
||||||
@ -45,7 +45,7 @@ const ALfloat *Resample_##Sampler##_C(const ALfloat *src, ALuint frac, \
|
|||||||
|
|
||||||
DECL_TEMPLATE(point32)
|
DECL_TEMPLATE(point32)
|
||||||
DECL_TEMPLATE(lerp32)
|
DECL_TEMPLATE(lerp32)
|
||||||
DECL_TEMPLATE(cubic32)
|
DECL_TEMPLATE(fir4_32)
|
||||||
|
|
||||||
#undef DECL_TEMPLATE
|
#undef DECL_TEMPLATE
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ struct HrtfState;
|
|||||||
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
const ALfloat *Resample_copy32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||||
const ALfloat *Resample_point32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
const ALfloat *Resample_point32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||||
const ALfloat *Resample_lerp32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
const ALfloat *Resample_lerp32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||||
const ALfloat *Resample_cubic32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
const ALfloat *Resample_fir4_32_C(const ALfloat *src, ALuint frac, ALuint increment, ALfloat *restrict dst, ALuint dstlen);
|
||||||
|
|
||||||
|
|
||||||
/* C mixers */
|
/* C mixers */
|
||||||
@ -54,9 +54,9 @@ const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint incr
|
|||||||
const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
|
const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
|
||||||
ALfloat *restrict dst, ALuint numsamples);
|
ALfloat *restrict dst, ALuint numsamples);
|
||||||
|
|
||||||
const ALfloat *Resample_cubic32_SSE2(const ALfloat *src, ALuint frac, ALuint increment,
|
const ALfloat *Resample_fir4_32_SSE2(const ALfloat *src, ALuint frac, ALuint increment,
|
||||||
ALfloat *restrict dst, ALuint numsamples);
|
ALfloat *restrict dst, ALuint numsamples);
|
||||||
const ALfloat *Resample_cubic32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
|
const ALfloat *Resample_fir4_32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
|
||||||
ALfloat *restrict dst, ALuint numsamples);
|
ALfloat *restrict dst, ALuint numsamples);
|
||||||
|
|
||||||
/* Neon mixers */
|
/* Neon mixers */
|
||||||
|
@ -77,7 +77,7 @@ const ALfloat *Resample_lerp32_SSE2(const ALfloat *src, ALuint frac, ALuint incr
|
|||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALfloat *Resample_cubic32_SSE2(const ALfloat *src, ALuint frac, ALuint increment,
|
const ALfloat *Resample_fir4_32_SSE2(const ALfloat *src, ALuint frac, ALuint increment,
|
||||||
ALfloat *restrict dst, ALuint numsamples)
|
ALfloat *restrict dst, ALuint numsamples)
|
||||||
{
|
{
|
||||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||||
@ -100,10 +100,10 @@ const ALfloat *Resample_cubic32_SSE2(const ALfloat *src, ALuint frac, ALuint inc
|
|||||||
const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]);
|
const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]);
|
||||||
const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]);
|
const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]);
|
||||||
const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]);
|
const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]);
|
||||||
__m128 k0 = _mm_load_ps(CubicLUT[frac_.i[0]]);
|
__m128 k0 = _mm_load_ps(ResampleCoeffs[frac_.i[0]]);
|
||||||
__m128 k1 = _mm_load_ps(CubicLUT[frac_.i[1]]);
|
__m128 k1 = _mm_load_ps(ResampleCoeffs[frac_.i[1]]);
|
||||||
__m128 k2 = _mm_load_ps(CubicLUT[frac_.i[2]]);
|
__m128 k2 = _mm_load_ps(ResampleCoeffs[frac_.i[2]]);
|
||||||
__m128 k3 = _mm_load_ps(CubicLUT[frac_.i[3]]);
|
__m128 k3 = _mm_load_ps(ResampleCoeffs[frac_.i[3]]);
|
||||||
__m128 out;
|
__m128 out;
|
||||||
|
|
||||||
k0 = _mm_mul_ps(k0, val0);
|
k0 = _mm_mul_ps(k0, val0);
|
||||||
@ -130,7 +130,7 @@ const ALfloat *Resample_cubic32_SSE2(const ALfloat *src, ALuint frac, ALuint inc
|
|||||||
|
|
||||||
for(;i < numsamples;i++)
|
for(;i < numsamples;i++)
|
||||||
{
|
{
|
||||||
dst[i] = cubic(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
|
dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
|
||||||
|
|
||||||
frac += increment;
|
frac += increment;
|
||||||
pos += frac>>FRACTIONBITS;
|
pos += frac>>FRACTIONBITS;
|
||||||
|
@ -81,7 +81,7 @@ const ALfloat *Resample_lerp32_SSE41(const ALfloat *src, ALuint frac, ALuint inc
|
|||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALfloat *Resample_cubic32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
|
const ALfloat *Resample_fir4_32_SSE41(const ALfloat *src, ALuint frac, ALuint increment,
|
||||||
ALfloat *restrict dst, ALuint numsamples)
|
ALfloat *restrict dst, ALuint numsamples)
|
||||||
{
|
{
|
||||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||||
@ -104,10 +104,10 @@ const ALfloat *Resample_cubic32_SSE41(const ALfloat *src, ALuint frac, ALuint in
|
|||||||
const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]);
|
const __m128 val1 = _mm_loadu_ps(&src[pos_.i[1]]);
|
||||||
const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]);
|
const __m128 val2 = _mm_loadu_ps(&src[pos_.i[2]]);
|
||||||
const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]);
|
const __m128 val3 = _mm_loadu_ps(&src[pos_.i[3]]);
|
||||||
__m128 k0 = _mm_load_ps(CubicLUT[frac_.i[0]]);
|
__m128 k0 = _mm_load_ps(ResampleCoeffs[frac_.i[0]]);
|
||||||
__m128 k1 = _mm_load_ps(CubicLUT[frac_.i[1]]);
|
__m128 k1 = _mm_load_ps(ResampleCoeffs[frac_.i[1]]);
|
||||||
__m128 k2 = _mm_load_ps(CubicLUT[frac_.i[2]]);
|
__m128 k2 = _mm_load_ps(ResampleCoeffs[frac_.i[2]]);
|
||||||
__m128 k3 = _mm_load_ps(CubicLUT[frac_.i[3]]);
|
__m128 k3 = _mm_load_ps(ResampleCoeffs[frac_.i[3]]);
|
||||||
__m128 out;
|
__m128 out;
|
||||||
|
|
||||||
k0 = _mm_mul_ps(k0, val0);
|
k0 = _mm_mul_ps(k0, val0);
|
||||||
@ -140,7 +140,7 @@ const ALfloat *Resample_cubic32_SSE41(const ALfloat *src, ALuint frac, ALuint in
|
|||||||
|
|
||||||
for(;i < numsamples;i++)
|
for(;i < numsamples;i++)
|
||||||
{
|
{
|
||||||
dst[i] = cubic(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
|
dst[i] = resample_fir4(src[pos], src[pos+1], src[pos+2], src[pos+3], frac);
|
||||||
|
|
||||||
frac += increment;
|
frac += increment;
|
||||||
pos += frac>>FRACTIONBITS;
|
pos += frac>>FRACTIONBITS;
|
||||||
|
@ -528,7 +528,7 @@ enum DistanceModel {
|
|||||||
enum Resampler {
|
enum Resampler {
|
||||||
PointResampler,
|
PointResampler,
|
||||||
LinearResampler,
|
LinearResampler,
|
||||||
CubicResampler,
|
FIR4Resampler,
|
||||||
|
|
||||||
ResamplerMax,
|
ResamplerMax,
|
||||||
};
|
};
|
||||||
|
@ -202,16 +202,16 @@ inline ALuint64 clampu64(ALuint64 val, ALuint64 min, ALuint64 max)
|
|||||||
{ return minu64(max, maxu64(min, val)); }
|
{ return minu64(max, maxu64(min, val)); }
|
||||||
|
|
||||||
|
|
||||||
extern alignas(16) ALfloat CubicLUT[FRACTIONONE][4];
|
extern alignas(16) ALfloat ResampleCoeffs[FRACTIONONE][4];
|
||||||
|
|
||||||
|
|
||||||
inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu)
|
inline ALfloat lerp(ALfloat val1, ALfloat val2, ALfloat mu)
|
||||||
{
|
{
|
||||||
return val1 + (val2-val1)*mu;
|
return val1 + (val2-val1)*mu;
|
||||||
}
|
}
|
||||||
inline ALfloat cubic(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALuint frac)
|
inline ALfloat resample_fir4(ALfloat val0, ALfloat val1, ALfloat val2, ALfloat val3, ALuint frac)
|
||||||
{
|
{
|
||||||
const ALfloat *k = CubicLUT[frac];
|
const ALfloat *k = ResampleCoeffs[frac];
|
||||||
return k[0]*val0 + k[1]*val1 + k[2]*val2 + k[3]*val3;
|
return k[0]*val0 + k[1]*val1 + k[2]*val2 + k[3]*val3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,12 +42,12 @@ enum Resampler DefaultResampler = LinearResampler;
|
|||||||
const ALsizei ResamplerPadding[ResamplerMax] = {
|
const ALsizei ResamplerPadding[ResamplerMax] = {
|
||||||
0, /* Point */
|
0, /* Point */
|
||||||
1, /* Linear */
|
1, /* Linear */
|
||||||
2, /* Cubic */
|
2, /* FIR4 */
|
||||||
};
|
};
|
||||||
const ALsizei ResamplerPrePadding[ResamplerMax] = {
|
const ALsizei ResamplerPrePadding[ResamplerMax] = {
|
||||||
0, /* Point */
|
0, /* Point */
|
||||||
0, /* Linear */
|
0, /* Linear */
|
||||||
1, /* Cubic */
|
1, /* FIR4 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@
|
|||||||
# Selects the resampler used when mixing sources. Valid values are:
|
# Selects the resampler used when mixing sources. Valid values are:
|
||||||
# point - nearest sample, no interpolation
|
# point - nearest sample, no interpolation
|
||||||
# linear - extrapolates samples using a linear slope between samples
|
# linear - extrapolates samples using a linear slope between samples
|
||||||
# cubic - extrapolates samples using a Catmull-Rom spline
|
# sinc4 - extrapolates samples using a 4-point sinc/lanczos filter
|
||||||
# Specifying other values will result in using the default (linear).
|
# Specifying other values will result in using the default (linear).
|
||||||
#resampler = linear
|
#resampler = linear
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ static const struct {
|
|||||||
{ "Default", "" },
|
{ "Default", "" },
|
||||||
{ "Point (low quality, fast)", "point" },
|
{ "Point (low quality, fast)", "point" },
|
||||||
{ "Linear (basic quality, fast)", "linear" },
|
{ "Linear (basic quality, fast)", "linear" },
|
||||||
{ "Cubic Spline (good quality)", "cubic" },
|
{ "Sinc/Lanczos (good quality)", "sinc4" },
|
||||||
|
|
||||||
{ "", "" }
|
{ "", "" }
|
||||||
}, stereoModeList[] = {
|
}, stereoModeList[] = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user