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).
ParametricEQ.cpp
- Committer:
- Padman
- Date:
- 2016-08-04
- Revision:
- 5:407aa8a0a5c2
- Parent:
- 1:0545d8890dc6
File content as of revision 5:407aa8a0a5c2:
/** * @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 ParametricEQ::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 suit 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; }