Use class methods for the biquad filter
This commit is contained in:
+8
-16
@@ -964,20 +964,16 @@ void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALfloat Elev
|
||||
voice->Direct.FilterType = AF_None;
|
||||
if(gainHF != 1.0f) voice->Direct.FilterType |= AF_LowPass;
|
||||
if(gainLF != 1.0f) voice->Direct.FilterType |= AF_HighPass;
|
||||
BiquadFilter_setParams(
|
||||
&voice->Direct.Params[0].LowPass, BiquadType::HighShelf,
|
||||
voice->Direct.Params[0].LowPass.setParams(BiquadType::HighShelf,
|
||||
gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
|
||||
);
|
||||
BiquadFilter_setParams(
|
||||
&voice->Direct.Params[0].HighPass, BiquadType::LowShelf,
|
||||
voice->Direct.Params[0].HighPass.setParams(BiquadType::LowShelf,
|
||||
gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
|
||||
);
|
||||
for(c = 1;c < num_channels;c++)
|
||||
{
|
||||
BiquadFilter_copyParams(&voice->Direct.Params[c].LowPass,
|
||||
&voice->Direct.Params[0].LowPass);
|
||||
BiquadFilter_copyParams(&voice->Direct.Params[c].HighPass,
|
||||
&voice->Direct.Params[0].HighPass);
|
||||
voice->Direct.Params[c].LowPass.copyParamsFrom(voice->Direct.Params[0].LowPass);
|
||||
voice->Direct.Params[c].HighPass.copyParamsFrom(voice->Direct.Params[0].HighPass);
|
||||
}
|
||||
}
|
||||
for(i = 0;i < NumSends;i++)
|
||||
@@ -990,20 +986,16 @@ void CalcPanningAndFilters(ALvoice *voice, const ALfloat Azi, const ALfloat Elev
|
||||
voice->Send[i].FilterType = AF_None;
|
||||
if(gainHF != 1.0f) voice->Send[i].FilterType |= AF_LowPass;
|
||||
if(gainLF != 1.0f) voice->Send[i].FilterType |= AF_HighPass;
|
||||
BiquadFilter_setParams(
|
||||
&voice->Send[i].Params[0].LowPass, BiquadType::HighShelf,
|
||||
voice->Send[i].Params[0].LowPass.setParams(BiquadType::HighShelf,
|
||||
gainHF, hfScale, calc_rcpQ_from_slope(gainHF, 1.0f)
|
||||
);
|
||||
BiquadFilter_setParams(
|
||||
&voice->Send[i].Params[0].HighPass, BiquadType::LowShelf,
|
||||
voice->Send[i].Params[0].HighPass.setParams(BiquadType::LowShelf,
|
||||
gainLF, lfScale, calc_rcpQ_from_slope(gainLF, 1.0f)
|
||||
);
|
||||
for(c = 1;c < num_channels;c++)
|
||||
{
|
||||
BiquadFilter_copyParams(&voice->Send[i].Params[c].LowPass,
|
||||
&voice->Send[i].Params[0].LowPass);
|
||||
BiquadFilter_copyParams(&voice->Send[i].Params[c].HighPass,
|
||||
&voice->Send[i].Params[0].HighPass);
|
||||
voice->Send[i].Params[c].LowPass.copyParamsFrom(voice->Send[i].Params[0].LowPass);
|
||||
voice->Send[i].Params[c].HighPass.copyParamsFrom(voice->Send[i].Params[0].HighPass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ struct ALdistortionState final : public EffectState {
|
||||
|
||||
ALboolean ALdistortionState::deviceUpdate(ALCdevice *UNUSED(device))
|
||||
{
|
||||
BiquadFilter_clear(&mLowpass);
|
||||
BiquadFilter_clear(&mBandpass);
|
||||
mLowpass.clear();
|
||||
mBandpass.clear();
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
@@ -78,15 +78,15 @@ void ALdistortionState::update(const ALCcontext *context, const ALeffectslot *sl
|
||||
/* Multiply sampling frequency by the amount of oversampling done during
|
||||
* processing.
|
||||
*/
|
||||
BiquadFilter_setParams(&mLowpass, BiquadType::LowPass, 1.0f,
|
||||
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
|
||||
mLowpass.setParams(BiquadType::LowPass, 1.0f, cutoff / (frequency*4.0f),
|
||||
calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
|
||||
);
|
||||
|
||||
cutoff = props->Distortion.EQCenter;
|
||||
/* Convert bandwidth in Hz to octaves. */
|
||||
bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
|
||||
BiquadFilter_setParams(&mBandpass, BiquadType::BandPass, 1.0f,
|
||||
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
|
||||
mBandpass.setParams(BiquadType::BandPass, 1.0f, cutoff / (frequency*4.0f),
|
||||
calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
|
||||
);
|
||||
|
||||
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
|
||||
@@ -120,7 +120,7 @@ void ALdistortionState::process(ALsizei SamplesToDo, const ALfloat (*RESTRICT Sa
|
||||
* (which is fortunately first step of distortion). So combine three
|
||||
* operations into the one.
|
||||
*/
|
||||
BiquadFilter_process(&mLowpass, buffer[1], buffer[0], todo);
|
||||
mLowpass.process(buffer[1], buffer[0], todo);
|
||||
|
||||
/* Second step, do distortion using waveshaper function to emulate
|
||||
* signal processing during tube overdriving. Three steps of
|
||||
@@ -139,7 +139,7 @@ void ALdistortionState::process(ALsizei SamplesToDo, const ALfloat (*RESTRICT Sa
|
||||
}
|
||||
|
||||
/* Third step, do bandpass filtering of distorted signal. */
|
||||
BiquadFilter_process(&mBandpass, buffer[1], buffer[0], todo);
|
||||
mBandpass.process(buffer[1], buffer[0], todo);
|
||||
|
||||
todo >>= 2;
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
|
||||
+6
-11
@@ -112,8 +112,8 @@ void ALechoState::update(const ALCcontext *context, const ALeffectslot *slot, co
|
||||
mFeedGain = props->Echo.Feedback;
|
||||
|
||||
gainhf = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */
|
||||
BiquadFilter_setParams(&mFilter, BiquadType::HighShelf,
|
||||
gainhf, LOWPASSFREQREF/frequency, calc_rcpQ_from_slope(gainhf, 1.0f)
|
||||
mFilter.setParams(BiquadType::HighShelf, gainhf, LOWPASSFREQREF/frequency,
|
||||
calc_rcpQ_from_slope(gainhf, 1.0f)
|
||||
);
|
||||
|
||||
/* First tap panning */
|
||||
@@ -132,12 +132,11 @@ void ALechoState::process(ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesI
|
||||
const ALsizei tap2 = mTap[1].delay;
|
||||
ALfloat *RESTRICT delaybuf = mSampleBuffer.data();
|
||||
ALsizei offset = mOffset;
|
||||
ALfloat z1, z2, in, out;
|
||||
ALfloat z1, z2;
|
||||
ALsizei base;
|
||||
ALsizei c, i;
|
||||
|
||||
z1 = mFilter.z1;
|
||||
z2 = mFilter.z2;
|
||||
std::tie(z1, z2) = mFilter.getComponents();
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
alignas(16) ALfloat temps[2][128];
|
||||
@@ -156,10 +155,7 @@ void ALechoState::process(ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesI
|
||||
/* Apply damping to the second tap, then add it to the buffer with
|
||||
* feedback attenuation.
|
||||
*/
|
||||
in = temps[1][i];
|
||||
out = in*mFilter.b0 + z1;
|
||||
z1 = in*mFilter.b1 - out*mFilter.a1 + z2;
|
||||
z2 = in*mFilter.b2 - out*mFilter.a2;
|
||||
float out{mFilter.processOne(temps[1][i], z1, z2)};
|
||||
|
||||
delaybuf[offset&mask] += out * mFeedGain;
|
||||
offset++;
|
||||
@@ -171,8 +167,7 @@ void ALechoState::process(ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesI
|
||||
|
||||
base += td;
|
||||
}
|
||||
mFilter.z1 = z1;
|
||||
mFilter.z2 = z2;
|
||||
mFilter.setComponents(z1, z2);
|
||||
|
||||
mOffset = offset;
|
||||
}
|
||||
|
||||
+17
-19
@@ -101,9 +101,7 @@ ALboolean ALequalizerState::deviceUpdate(ALCdevice *UNUSED(device))
|
||||
for(auto &e : mChans)
|
||||
{
|
||||
std::for_each(std::begin(e.filter), std::end(e.filter),
|
||||
[](BiquadFilter &f) -> void
|
||||
{ BiquadFilter_clear(&f); }
|
||||
);
|
||||
std::mem_fun_ref(&BiquadFilter::clear));
|
||||
std::fill(std::begin(e.CurrentGains), std::end(e.CurrentGains), 0.0f);
|
||||
}
|
||||
return AL_TRUE;
|
||||
@@ -122,35 +120,35 @@ void ALequalizerState::update(const ALCcontext *context, const ALeffectslot *slo
|
||||
*/
|
||||
gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
|
||||
f0norm = props->Equalizer.LowCutoff/frequency;
|
||||
BiquadFilter_setParams(&mChans[0].filter[0], BiquadType::LowShelf,
|
||||
gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
mChans[0].filter[0].setParams(BiquadType::LowShelf, gain, f0norm,
|
||||
calc_rcpQ_from_slope(gain, 0.75f)
|
||||
);
|
||||
|
||||
gain = maxf(props->Equalizer.Mid1Gain, 0.0625f);
|
||||
f0norm = props->Equalizer.Mid1Center/frequency;
|
||||
BiquadFilter_setParams(&mChans[0].filter[1], BiquadType::Peaking,
|
||||
gain, f0norm, calc_rcpQ_from_bandwidth(f0norm, props->Equalizer.Mid1Width)
|
||||
mChans[0].filter[1].setParams(BiquadType::Peaking, gain, f0norm,
|
||||
calc_rcpQ_from_bandwidth(f0norm, props->Equalizer.Mid1Width)
|
||||
);
|
||||
|
||||
gain = maxf(props->Equalizer.Mid2Gain, 0.0625f);
|
||||
f0norm = props->Equalizer.Mid2Center/frequency;
|
||||
BiquadFilter_setParams(&mChans[0].filter[2], BiquadType::Peaking,
|
||||
gain, f0norm, calc_rcpQ_from_bandwidth(f0norm, props->Equalizer.Mid2Width)
|
||||
mChans[0].filter[2].setParams(BiquadType::Peaking, gain, f0norm,
|
||||
calc_rcpQ_from_bandwidth(f0norm, props->Equalizer.Mid2Width)
|
||||
);
|
||||
|
||||
gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
|
||||
f0norm = props->Equalizer.HighCutoff/frequency;
|
||||
BiquadFilter_setParams(&mChans[0].filter[3], BiquadType::HighShelf,
|
||||
gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
mChans[0].filter[3].setParams(BiquadType::HighShelf, gain, f0norm,
|
||||
calc_rcpQ_from_slope(gain, 0.75f)
|
||||
);
|
||||
|
||||
/* Copy the filter coefficients for the other input channels. */
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
{
|
||||
BiquadFilter_copyParams(&mChans[i].filter[0], &mChans[0].filter[0]);
|
||||
BiquadFilter_copyParams(&mChans[i].filter[1], &mChans[0].filter[1]);
|
||||
BiquadFilter_copyParams(&mChans[i].filter[2], &mChans[0].filter[2]);
|
||||
BiquadFilter_copyParams(&mChans[i].filter[3], &mChans[0].filter[3]);
|
||||
mChans[i].filter[0].copyParamsFrom(mChans[0].filter[0]);
|
||||
mChans[i].filter[1].copyParamsFrom(mChans[0].filter[1]);
|
||||
mChans[i].filter[2].copyParamsFrom(mChans[0].filter[2]);
|
||||
mChans[i].filter[3].copyParamsFrom(mChans[0].filter[3]);
|
||||
}
|
||||
|
||||
mOutBuffer = device->FOAOut.Buffer;
|
||||
@@ -167,10 +165,10 @@ void ALequalizerState::process(ALsizei SamplesToDo, const ALfloat (*RESTRICT Sam
|
||||
|
||||
for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
|
||||
{
|
||||
BiquadFilter_process(&mChans[c].filter[0], temps[0], SamplesIn[c], SamplesToDo);
|
||||
BiquadFilter_process(&mChans[c].filter[1], temps[1], temps[0], SamplesToDo);
|
||||
BiquadFilter_process(&mChans[c].filter[2], temps[2], temps[1], SamplesToDo);
|
||||
BiquadFilter_process(&mChans[c].filter[3], temps[3], temps[2], SamplesToDo);
|
||||
mChans[c].filter[0].process(temps[0], SamplesIn[c], SamplesToDo);
|
||||
mChans[c].filter[1].process(temps[1], temps[0], SamplesToDo);
|
||||
mChans[c].filter[2].process(temps[2], temps[1], SamplesToDo);
|
||||
mChans[c].filter[3].process(temps[3], temps[2], SamplesToDo);
|
||||
|
||||
MixSamples(temps[3], NumChannels, SamplesOut, mChans[c].CurrentGains,
|
||||
mChans[c].TargetGains, SamplesToDo, 0, SamplesToDo);
|
||||
|
||||
@@ -99,7 +99,7 @@ ALboolean ALmodulatorState::deviceUpdate(ALCdevice *UNUSED(device))
|
||||
{
|
||||
for(auto &e : mChans)
|
||||
{
|
||||
BiquadFilter_clear(&e.Filter);
|
||||
e.Filter.clear();
|
||||
std::fill(std::begin(e.CurrentGains), std::end(e.CurrentGains), 0.0f);
|
||||
}
|
||||
return AL_TRUE;
|
||||
@@ -126,10 +126,10 @@ void ALmodulatorState::update(const ALCcontext *context, const ALeffectslot *slo
|
||||
f0norm = props->Modulator.HighPassCutoff / (ALfloat)device->Frequency;
|
||||
f0norm = clampf(f0norm, 1.0f/512.0f, 0.49f);
|
||||
/* Bandwidth value is constant in octaves. */
|
||||
BiquadFilter_setParams(&mChans[0].Filter, BiquadType::HighPass, 1.0f,
|
||||
f0norm, calc_rcpQ_from_bandwidth(f0norm, 0.75f));
|
||||
mChans[0].Filter.setParams(BiquadType::HighPass, 1.0f, f0norm,
|
||||
calc_rcpQ_from_bandwidth(f0norm, 0.75f));
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
BiquadFilter_copyParams(&mChans[i].Filter, &mChans[0].Filter);
|
||||
mChans[i].Filter.copyParamsFrom(mChans[0].Filter);
|
||||
|
||||
mOutBuffer = device->FOAOut.Buffer;
|
||||
mOutChannels = device->FOAOut.NumChannels;
|
||||
@@ -157,7 +157,7 @@ void ALmodulatorState::process(ALsizei SamplesToDo, const ALfloat (*RESTRICT Sam
|
||||
{
|
||||
alignas(16) ALfloat temps[MAX_UPDATE_SAMPLES];
|
||||
|
||||
BiquadFilter_process(&mChans[c].Filter, temps, &SamplesIn[c][base], td);
|
||||
mChans[c].Filter.process(temps, &SamplesIn[c][base], td);
|
||||
for(i = 0;i < td;i++)
|
||||
temps[i] *= modsamples[i];
|
||||
|
||||
|
||||
+18
-18
@@ -478,8 +478,8 @@ ALboolean ReverbState::deviceUpdate(ALCdevice *Device)
|
||||
*/
|
||||
for(i = 0;i < NUM_LINES;i++)
|
||||
{
|
||||
BiquadFilter_clear(&mFilter[i].Lp);
|
||||
BiquadFilter_clear(&mFilter[i].Hp);
|
||||
mFilter[i].Lp.clear();
|
||||
mFilter[i].Hp.clear();
|
||||
}
|
||||
|
||||
for(i = 0;i < NUM_LINES;i++)
|
||||
@@ -500,8 +500,8 @@ ALboolean ReverbState::deviceUpdate(ALCdevice *Device)
|
||||
{
|
||||
mLate.T60[i].MidGain[0] = 0.0f;
|
||||
mLate.T60[i].MidGain[1] = 0.0f;
|
||||
BiquadFilter_clear(&mLate.T60[i].HFFilter);
|
||||
BiquadFilter_clear(&mLate.T60[i].LFFilter);
|
||||
mLate.T60[i].HFFilter.clear();
|
||||
mLate.T60[i].LFFilter.clear();
|
||||
}
|
||||
|
||||
for(i = 0;i < NUM_LINES;i++)
|
||||
@@ -615,10 +615,10 @@ static void CalcT60DampingCoeffs(const ALfloat length, const ALfloat lfDecayTime
|
||||
ALfloat hfGain = CalcDecayCoeff(length, hfDecayTime);
|
||||
|
||||
filter->MidGain[1] = mfGain;
|
||||
BiquadFilter_setParams(&filter->LFFilter, BiquadType::LowShelf, lfGain/mfGain, lf0norm,
|
||||
calc_rcpQ_from_slope(lfGain/mfGain, 1.0f));
|
||||
BiquadFilter_setParams(&filter->HFFilter, BiquadType::HighShelf, hfGain/mfGain, hf0norm,
|
||||
calc_rcpQ_from_slope(hfGain/mfGain, 1.0f));
|
||||
filter->LFFilter.setParams(BiquadType::LowShelf, lfGain/mfGain, lf0norm,
|
||||
calc_rcpQ_from_slope(lfGain/mfGain, 1.0f));
|
||||
filter->HFFilter.setParams(BiquadType::HighShelf, hfGain/mfGain, hf0norm,
|
||||
calc_rcpQ_from_slope(hfGain/mfGain, 1.0f));
|
||||
}
|
||||
|
||||
/* Update the offsets for the main effect delay line. */
|
||||
@@ -851,16 +851,16 @@ void ReverbState::update(const ALCcontext *Context, const ALeffectslot *Slot, co
|
||||
* killing most of the signal.
|
||||
*/
|
||||
gainhf = maxf(props->Reverb.GainHF, 0.001f);
|
||||
BiquadFilter_setParams(&mFilter[0].Lp, BiquadType::HighShelf, gainhf, hf0norm,
|
||||
calc_rcpQ_from_slope(gainhf, 1.0f));
|
||||
mFilter[0].Lp.setParams(BiquadType::HighShelf, gainhf, hf0norm,
|
||||
calc_rcpQ_from_slope(gainhf, 1.0f));
|
||||
lf0norm = minf(props->Reverb.LFReference / frequency, 0.49f);
|
||||
gainlf = maxf(props->Reverb.GainLF, 0.001f);
|
||||
BiquadFilter_setParams(&mFilter[0].Hp, BiquadType::LowShelf, gainlf, lf0norm,
|
||||
calc_rcpQ_from_slope(gainlf, 1.0f));
|
||||
mFilter[0].Hp.setParams(BiquadType::LowShelf, gainlf, lf0norm,
|
||||
calc_rcpQ_from_slope(gainlf, 1.0f));
|
||||
for(i = 1;i < NUM_LINES;i++)
|
||||
{
|
||||
BiquadFilter_copyParams(&mFilter[i].Lp, &mFilter[0].Lp);
|
||||
BiquadFilter_copyParams(&mFilter[i].Hp, &mFilter[0].Hp);
|
||||
mFilter[i].Lp.copyParamsFrom(mFilter[0].Lp);
|
||||
mFilter[i].Hp.copyParamsFrom(mFilter[0].Hp);
|
||||
}
|
||||
|
||||
/* Update the main effect delay and associated taps. */
|
||||
@@ -1245,8 +1245,8 @@ static void EarlyReflection_Faded(ReverbState *State, ALsizei offset, const ALsi
|
||||
static inline void LateT60Filter(ALfloat *RESTRICT samples, const ALsizei todo, T60Filter *filter)
|
||||
{
|
||||
ALfloat temp[MAX_UPDATE_SAMPLES];
|
||||
BiquadFilter_process(&filter->HFFilter, temp, samples, todo);
|
||||
BiquadFilter_process(&filter->LFFilter, samples, temp, todo);
|
||||
filter->HFFilter.process(temp, samples, todo);
|
||||
filter->LFFilter.process(samples, temp, todo);
|
||||
}
|
||||
|
||||
/* This generates the reverb tail using a modified feed-back delay network
|
||||
@@ -1389,8 +1389,8 @@ void ReverbState::process(ALsizei SamplesToDo, const ALfloat (*RESTRICT SamplesI
|
||||
for(c = 0;c < NUM_LINES;c++)
|
||||
{
|
||||
/* Band-pass the incoming samples. */
|
||||
BiquadFilter_process(&mFilter[c].Lp, samples[0], afmt[c], todo);
|
||||
BiquadFilter_process(&mFilter[c].Hp, samples[1], samples[0], todo);
|
||||
mFilter[c].Lp.process(samples[0], afmt[c], todo);
|
||||
mFilter[c].Hp.process(samples[1], samples[0], todo);
|
||||
|
||||
/* Feed the initial delay line. */
|
||||
DelayLineIn(&mDelay, offset, c, samples[1], todo);
|
||||
|
||||
+59
-51
@@ -33,16 +33,72 @@ enum class BiquadType {
|
||||
BandPass,
|
||||
};
|
||||
|
||||
struct BiquadFilter {
|
||||
class BiquadFilter {
|
||||
/* Last two delayed components for direct form II. */
|
||||
float z1{0.0f}, z2{0.0f};
|
||||
/* Transfer function coefficients "b" (numerator) */
|
||||
float b0{1.0f}, b1{0.0f}, b2{0.0f};
|
||||
/* Transfer function coefficients "a" (denominator; a0 is pre-applied). */
|
||||
float a1{0.0f}, a2{0.0f};
|
||||
|
||||
public:
|
||||
void clear() noexcept { z1 = z2 = 0.0f; }
|
||||
|
||||
/**
|
||||
* Sets the filter state for the specified filter type and its parameters.
|
||||
*
|
||||
* \param type The type of filter to apply.
|
||||
* \param gain The gain for the reference frequency response. Only used by
|
||||
* the Shelf and Peaking filter types.
|
||||
* \param f0norm The reference frequency normal (ref_freq / sample_rate).
|
||||
* This is the center point for the Shelf, Peaking, and
|
||||
* BandPass filter types, or the cutoff frequency for the
|
||||
* LowPass and HighPass filter types.
|
||||
* \param rcpQ The reciprocal of the Q coefficient for the filter's
|
||||
* transition band. Can be generated from calc_rcpQ_from_slope
|
||||
* or calc_rcpQ_from_bandwidth as needed.
|
||||
*/
|
||||
void setParams(BiquadType type, float gain, float f0norm, float rcpQ);
|
||||
|
||||
void copyParamsFrom(const BiquadFilter &other)
|
||||
{
|
||||
b0 = other.b0;
|
||||
b1 = other.b1;
|
||||
b2 = other.b2;
|
||||
a1 = other.a1;
|
||||
a2 = other.a2;
|
||||
}
|
||||
|
||||
|
||||
void process(float *RESTRICT dst, const float *RESTRICT src, int numsamples);
|
||||
|
||||
void passthru(int numsamples) noexcept
|
||||
{
|
||||
if(LIKELY(numsamples >= 2))
|
||||
{
|
||||
z1 = 0.0f;
|
||||
z2 = 0.0f;
|
||||
}
|
||||
else if(numsamples == 1)
|
||||
{
|
||||
z1 = z2;
|
||||
z2 = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* Rather hacky. It's just here to support "manual" processing. */
|
||||
std::pair<float,float> getComponents() const noexcept
|
||||
{ return {z1, z2}; }
|
||||
void setComponents(float z1_, float z2_) noexcept
|
||||
{ z1 = z1_; z2 = z2_; }
|
||||
float processOne(const float in, float &z1_, float &z2_) const noexcept
|
||||
{
|
||||
float out{in*b0 + z1_};
|
||||
z1_ = in*b1 - out*a1 + z2_;
|
||||
z2_ = in*b2 - out*a2;
|
||||
return out;
|
||||
}
|
||||
};
|
||||
/* Currently only a C-based filter process method is implemented. */
|
||||
#define BiquadFilter_process BiquadFilter_processC
|
||||
|
||||
/**
|
||||
* Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using the
|
||||
@@ -66,52 +122,4 @@ inline ALfloat calc_rcpQ_from_bandwidth(float f0norm, float bandwidth)
|
||||
return 2.0f*std::sinh(std::log(2.0f)/2.0f*bandwidth*w0/std::sin(w0));
|
||||
}
|
||||
|
||||
inline void BiquadFilter_clear(BiquadFilter *filter)
|
||||
{
|
||||
filter->z1 = 0.0f;
|
||||
filter->z2 = 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter state for the specified filter type and its parameters.
|
||||
*
|
||||
* \param filter The filter object to prepare.
|
||||
* \param type The type of filter for the object to apply.
|
||||
* \param gain The gain for the reference frequency response. Only used by the
|
||||
* Shelf and Peaking filter types.
|
||||
* \param f0norm The normalized reference frequency (ref_freq / sample_rate).
|
||||
* This is the center point for the Shelf, Peaking, and BandPass
|
||||
* filter types, or the cutoff frequency for the LowPass and
|
||||
* HighPass filter types.
|
||||
* \param rcpQ The reciprocal of the Q coefficient for the filter's transition
|
||||
* band. Can be generated from calc_rcpQ_from_slope or
|
||||
* calc_rcpQ_from_bandwidth depending on the available data.
|
||||
*/
|
||||
void BiquadFilter_setParams(BiquadFilter *filter, BiquadType type, float gain, float f0norm, float rcpQ);
|
||||
|
||||
inline void BiquadFilter_copyParams(BiquadFilter *RESTRICT dst, const BiquadFilter *RESTRICT src)
|
||||
{
|
||||
dst->b0 = src->b0;
|
||||
dst->b1 = src->b1;
|
||||
dst->b2 = src->b2;
|
||||
dst->a1 = src->a1;
|
||||
dst->a2 = src->a2;
|
||||
}
|
||||
|
||||
void BiquadFilter_processC(BiquadFilter *filter, float *RESTRICT dst, const float *RESTRICT src, int numsamples);
|
||||
|
||||
inline void BiquadFilter_passthru(BiquadFilter *filter, int numsamples)
|
||||
{
|
||||
if(LIKELY(numsamples >= 2))
|
||||
{
|
||||
filter->z1 = 0.0f;
|
||||
filter->z2 = 0.0f;
|
||||
}
|
||||
else if(numsamples == 1)
|
||||
{
|
||||
filter->z1 = filter->z2;
|
||||
filter->z2 = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ALC_FILTER_H */
|
||||
|
||||
+17
-17
@@ -10,7 +10,7 @@
|
||||
#include "defs.h"
|
||||
|
||||
|
||||
void BiquadFilter_setParams(BiquadFilter *filter, BiquadType type, float gain, float f0norm, float rcpQ)
|
||||
void BiquadFilter::setParams(BiquadType type, float gain, float f0norm, float rcpQ)
|
||||
{
|
||||
float alpha, sqrtgain_alpha_2;
|
||||
float w0, sin_w0, cos_w0;
|
||||
@@ -82,25 +82,25 @@ void BiquadFilter_setParams(BiquadFilter *filter, BiquadType type, float gain, f
|
||||
break;
|
||||
}
|
||||
|
||||
filter->a1 = a[1] / a[0];
|
||||
filter->a2 = a[2] / a[0];
|
||||
filter->b0 = b[0] / a[0];
|
||||
filter->b1 = b[1] / a[0];
|
||||
filter->b2 = b[2] / a[0];
|
||||
a1 = a[1] / a[0];
|
||||
a2 = a[2] / a[0];
|
||||
b0 = b[0] / a[0];
|
||||
b1 = b[1] / a[0];
|
||||
b2 = b[2] / a[0];
|
||||
}
|
||||
|
||||
|
||||
void BiquadFilter_processC(BiquadFilter *filter, float *RESTRICT dst, const float *RESTRICT src, int numsamples)
|
||||
void BiquadFilter::process(float *RESTRICT dst, const float *RESTRICT src, int numsamples)
|
||||
{
|
||||
ASSUME(numsamples > 0);
|
||||
|
||||
const float b0{filter->b0};
|
||||
const float b1{filter->b1};
|
||||
const float b2{filter->b2};
|
||||
const float a1{filter->a1};
|
||||
const float a2{filter->a2};
|
||||
float z1{filter->z1};
|
||||
float z2{filter->z2};
|
||||
const float b0{this->b0};
|
||||
const float b1{this->b1};
|
||||
const float b2{this->b2};
|
||||
const float a1{this->a1};
|
||||
const float a2{this->a2};
|
||||
float z1{this->z1};
|
||||
float z2{this->z2};
|
||||
|
||||
/* Processing loop is Transposed Direct Form II. This requires less storage
|
||||
* compared to Direct Form I (only two delay components, instead of a four-
|
||||
@@ -117,8 +117,8 @@ void BiquadFilter_processC(BiquadFilter *filter, float *RESTRICT dst, const floa
|
||||
z2 = input*b2 - output*a2;
|
||||
return output;
|
||||
};
|
||||
std::transform<const float*RESTRICT>(src, src+numsamples, dst, proc_sample);
|
||||
std::transform(src, src+numsamples, dst, proc_sample);
|
||||
|
||||
filter->z1 = z1;
|
||||
filter->z2 = z2;
|
||||
this->z1 = z1;
|
||||
this->z2 = z2;
|
||||
}
|
||||
|
||||
+8
-8
@@ -250,17 +250,17 @@ const ALfloat *DoFilters(BiquadFilter *lpfilter, BiquadFilter *hpfilter,
|
||||
switch(type)
|
||||
{
|
||||
case AF_None:
|
||||
BiquadFilter_passthru(lpfilter, numsamples);
|
||||
BiquadFilter_passthru(hpfilter, numsamples);
|
||||
lpfilter->passthru(numsamples);
|
||||
hpfilter->passthru(numsamples);
|
||||
break;
|
||||
|
||||
case AF_LowPass:
|
||||
BiquadFilter_process(lpfilter, dst, src, numsamples);
|
||||
BiquadFilter_passthru(hpfilter, numsamples);
|
||||
lpfilter->process(dst, src, numsamples);
|
||||
hpfilter->passthru(numsamples);
|
||||
return dst;
|
||||
case AF_HighPass:
|
||||
BiquadFilter_passthru(lpfilter, numsamples);
|
||||
BiquadFilter_process(hpfilter, dst, src, numsamples);
|
||||
lpfilter->passthru(numsamples);
|
||||
hpfilter->process(dst, src, numsamples);
|
||||
return dst;
|
||||
|
||||
case AF_BandPass:
|
||||
@@ -269,8 +269,8 @@ const ALfloat *DoFilters(BiquadFilter *lpfilter, BiquadFilter *hpfilter,
|
||||
ALfloat temp[256];
|
||||
ALsizei todo = mini(256, numsamples-i);
|
||||
|
||||
BiquadFilter_process(lpfilter, temp, src+i, todo);
|
||||
BiquadFilter_process(hpfilter, dst+i, temp, todo);
|
||||
lpfilter->process(temp, src+i, todo);
|
||||
hpfilter->process(dst+i, temp, todo);
|
||||
i += todo;
|
||||
}
|
||||
return dst;
|
||||
|
||||
Reference in New Issue
Block a user