A parametric EQ that uses a fixed point, direct form 2 transposed, implementation of a Butterworth filter. The following parameters can be adjusted: Gain (dB), Bandwidth Gain (dB), Centre frequency (Hz), Bandwidth (Hz), Order, Type (Peaking, Bandstop, Bandpass, Low Shelf, High Shelf, Low Pass or High Pass).
Diff: ParametricEQ.cpp
- Revision:
- 0:2e17a3d6907c
- Child:
- 1:0545d8890dc6
diff -r 000000000000 -r 2e17a3d6907c ParametricEQ.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ParametricEQ.cpp Thu Aug 04 19:34:39 2016 +0000 @@ -0,0 +1,251 @@ +/** + * @file ParametricEQ.cpp + * @brief ParametricEQ - fixed point implementation of a Parametric EQ + * @author Patrick Thomas + * @version 1.0 + * @see + * + * Copyright (c) 2016 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ParametricEQ.h" + +int biquad::execute(int input) { + + int block_output = bhat_0*input + v1; + w1 = bhat_1*input - ahat_1*(block_output/scaling_factor) + v3; + v1 = c0*(w1/scaling_factor) - s0*(v2/scaling_factor); + v2 = s0*(w1/scaling_factor) + c0*(v2/scaling_factor); + w2 = bhat_2*input - ahat_2*(block_output/scaling_factor); + v3 = c0*(w2/scaling_factor) - s0*(v4/scaling_factor); + v4 = s0*(w2/scaling_factor) + c0*(v4/scaling_factor); + + return PIN(block_output/scaling_factor, -sample_bounds, sample_bounds); +} + +void ParametricEQ::check_GBW() { + + // Find upper and lower bounds + float upper = Gain_amplitude > G0_amplitude ? Gain_amplitude : G0_amplitude; + float lower = Gain_amplitude < G0_amplitude ? G0_amplitude : Gain_amplitude; + + // Convert current GBW_dB into amplitude form + Bandwidth_gain_amplitude = pow(10, GBW_dB/20); + + // Check this against the limits and clip if necessary + Bandwidth_gain_amplitude = PIN(Bandwidth_gain_amplitude, lower + GBW_MARGIN, upper - GBW_MARGIN); + + // Convert the checked value back to decibel form + GBW_dB = 20*log10(Bandwidth_gain_amplitude); +} + +void ParametricEQ::calculate() { + + // Calculate sampling variables + Scaling_factor = 1 << (30 - Sample_bits); + Sample_bounds = (1 << (Sample_bits - 1)) - 1; + + // Calculate parameter variables + Normalised_centre_frequency = (F0_Hz*2*M_PI)/Sample_rate; + Normalised_bandwidth = (BW_Hz*2*M_PI)/Sample_rate; + f0_cosine = cos(Normalised_centre_frequency); + f0_sine = sin(Normalised_centre_frequency); + g = pow(Gain_amplitude,double(1)/Order); + g_squared = g*g; + g0 = pow(G0_amplitude,double(1)/Order); + g0_squared = g0*g0; + epsilon = sqrt((Gain_amplitude*Gain_amplitude - Bandwidth_gain_amplitude*Bandwidth_gain_amplitude)/ + (Bandwidth_gain_amplitude*Bandwidth_gain_amplitude - G0_amplitude*G0_amplitude)); + beta = tan(Normalised_bandwidth/2)/pow(epsilon,double(1)/Order); + beta_squared = beta*beta; + counter = (Order + 1)/2; + + update_blocks(); +} + +void ParametricEQ::update_blocks() { + + // Iterate through filter blocks + for (int i = 0; i < counter; i++) + { + // Assign new sampling parameters + fx_blocks[i].scaling_factor = Scaling_factor; + fx_blocks[i].sample_bounds = Sample_bounds; + + // Assign new coefficients + fx_blocks[i].c0 = f0_cosine*Scaling_factor; + fx_blocks[i].s0 = f0_sine*Scaling_factor; + + if ((Order%2 == 1) && (i == 0)) + { + D = beta + 1; + fx_blocks[i].bhat_0 = ((g*beta + g0)/D)*Scaling_factor; + fx_blocks[i].bhat_1 = ((g*beta - g0)/D)*Scaling_factor; + fx_blocks[i].bhat_2 = 0; + fx_blocks[i].ahat_1 = ((beta - 1)/D)*Scaling_factor; + fx_blocks[i].ahat_2 = 0; + } + + else + { + phi = (((2*i) + 1)*M_PI)/(2*Order); + si = sin(double(phi)); + D = beta_squared + 2*si*beta + 1; + fx_blocks[i].bhat_0 = ((g_squared*beta_squared + 2*g*g0*si*beta + g0_squared)/D)*Scaling_factor; + fx_blocks[i].bhat_1 = (2*(g_squared*beta_squared - g0_squared)/D)*Scaling_factor; + fx_blocks[i].bhat_2 = ((g_squared*beta_squared - 2*g*g0*si*beta + g0_squared)/D)*Scaling_factor; + fx_blocks[i].ahat_1 = (2*(beta_squared - 1)/D)*Scaling_factor; + fx_blocks[i].ahat_2 = ((beta_squared - 2*si*beta + 1)/D)*Scaling_factor; + } + } +} + +ParametricEQ::ParametricEQ() { + + // Initial parameter values + Gain_dB = 6; + GBW_dB = 3; + F0_Hz = 4000; + BW_Hz = 1000; + Order = 2; + Type = Peaking; + + // Initial sampling values + Sample_rate = 48000; + Sample_bits = 16; + + // Initialise filter blocks + calculate(); +} + +float ParametricEQ::set_Gain_dB(float value) { + + Gain_dB = PIN(value, -GAIN_DB_MAX, GAIN_DB_MAX); + + set_Type(Type); + return Gain_dB; +} + +float ParametricEQ::set_GBW_dB(float value) { + + GBW_dB = PIN(value, -GAIN_DB_MAX, GAIN_DB_MAX); + + set_Type(Type); + return GBW_dB; +} + +int ParametricEQ::set_F0_Hz(int value) { + + F0_Hz = PIN(value, 0, Sample_rate/2); + + set_Type(Type); + return F0_Hz; +} + +int ParametricEQ::set_BW_Hz(int value) { + + BW_Hz = PIN(value, BW_HZ_MIN, Sample_rate/2); + + set_Type(Type); + return BW_Hz; +} + +int ParametricEQ::set_Order(int value) { + + Order = PIN(value, 1, MAX_ORDER); + + set_Type(Type); + return Order; +} + +FilterType ParametricEQ::set_Type(FilterType type) { + + Type = type; + + // Tailor parameters to suite the chosen filter type + switch (Type) { + + case Peaking: + Gain_amplitude = pow(10, Gain_dB/20); + G0_amplitude = 1; + break; + + case BandPass: + Gain_amplitude = 1; + G0_amplitude = 0; + break; + + case BandStop: + Gain_amplitude = 0; + G0_amplitude = 1; + break; + + case LowShelf: + Gain_amplitude = pow(10, Gain_dB/20); + G0_amplitude = 1; + F0_Hz = 0; + break; + + case HighShelf: + Gain_amplitude = pow(10, Gain_dB/20); + G0_amplitude = 1; + F0_Hz = Sample_rate/2; + break; + + case LowPass: + Gain_amplitude = 1; + G0_amplitude = 0; + F0_Hz = 0; + break; + + case HighPass: + Gain_amplitude = 1; + G0_amplitude = 0; + F0_Hz = Sample_rate/2; + break; + } + + check_GBW(); + calculate(); + return Type; +} + +int ParametricEQ::set_Sample_rate(int value) { + + Sample_rate = PIN(value, SAMPLE_RATE_MIN, SAMPLE_RATE_MAX); + + set_Type(Type); + return Sample_rate; +} + +int ParametricEQ::set_Sample_bits(int value) { + + Sample_bits = PIN(value, SAMPLE_BITS_MIN, SAMPLE_BITS_MAX); + + set_Type(Type); + return Sample_bits; +} + +int ParametricEQ::filter(int input) { + + // Send sample through filter blocks + for (int i = 0; i < counter; i++) + { + input = fx_blocks[i].execute(input); + } + + return input; +} +