From 41b2dce01ab5883d1b165dc6d123ab851fc86466 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 17 Sep 2025 19:45:30 +0200 Subject: [PATCH] add split eq --- filter/aw_eq.h | 650 ----------------------------------------------- filter/spliteq.h | 538 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 538 insertions(+), 650 deletions(-) delete mode 100644 filter/aw_eq.h create mode 100644 filter/spliteq.h diff --git a/filter/aw_eq.h b/filter/aw_eq.h deleted file mode 100644 index 2b7e878..0000000 --- a/filter/aw_eq.h +++ /dev/null @@ -1,650 +0,0 @@ -#pragma once -#include -#include -#include - -namespace trnr { -// 3 band equalizer with high/lowpass filters based on EQ by Chris Johnson. -class aw_eq { -public: - aw_eq() - { - samplerate = 44100; - - A = 0.5; // Treble -12 to 12 - B = 0.5; // Mid -12 to 12 - C = 0.5; // Bass -12 to 12 - D = 1.0; // Lowpass 16.0K log 1 to 16 defaulting to 16K - E = 0.4; // TrebFrq 6.0 log 1 to 16 defaulting to 6K - F = 0.4; // BassFrq 100.0 log 30 to 1600 defaulting to 100 hz - G = 0.0; // Hipass 30.0 log 30 to 1600 defaulting to 30 - H = 0.5; // OutGain -18 to 18 - - lastSampleL = 0.0; - last2SampleL = 0.0; - lastSampleR = 0.0; - last2SampleR = 0.0; - - iirHighSampleLA = 0.0; - iirHighSampleLB = 0.0; - iirHighSampleLC = 0.0; - iirHighSampleLD = 0.0; - iirHighSampleLE = 0.0; - iirLowSampleLA = 0.0; - iirLowSampleLB = 0.0; - iirLowSampleLC = 0.0; - iirLowSampleLD = 0.0; - iirLowSampleLE = 0.0; - iirHighSampleL = 0.0; - iirLowSampleL = 0.0; - - iirHighSampleRA = 0.0; - iirHighSampleRB = 0.0; - iirHighSampleRC = 0.0; - iirHighSampleRD = 0.0; - iirHighSampleRE = 0.0; - iirLowSampleRA = 0.0; - iirLowSampleRB = 0.0; - iirLowSampleRC = 0.0; - iirLowSampleRD = 0.0; - iirLowSampleRE = 0.0; - iirHighSampleR = 0.0; - iirLowSampleR = 0.0; - - tripletLA = 0.0; - tripletLB = 0.0; - tripletLC = 0.0; - tripletFactorL = 0.0; - - tripletRA = 0.0; - tripletRB = 0.0; - tripletRC = 0.0; - tripletFactorR = 0.0; - - lowpassSampleLAA = 0.0; - lowpassSampleLAB = 0.0; - lowpassSampleLBA = 0.0; - lowpassSampleLBB = 0.0; - lowpassSampleLCA = 0.0; - lowpassSampleLCB = 0.0; - lowpassSampleLDA = 0.0; - lowpassSampleLDB = 0.0; - lowpassSampleLE = 0.0; - lowpassSampleLF = 0.0; - lowpassSampleLG = 0.0; - - lowpassSampleRAA = 0.0; - lowpassSampleRAB = 0.0; - lowpassSampleRBA = 0.0; - lowpassSampleRBB = 0.0; - lowpassSampleRCA = 0.0; - lowpassSampleRCB = 0.0; - lowpassSampleRDA = 0.0; - lowpassSampleRDB = 0.0; - lowpassSampleRE = 0.0; - lowpassSampleRF = 0.0; - lowpassSampleRG = 0.0; - - highpassSampleLAA = 0.0; - highpassSampleLAB = 0.0; - highpassSampleLBA = 0.0; - highpassSampleLBB = 0.0; - highpassSampleLCA = 0.0; - highpassSampleLCB = 0.0; - highpassSampleLDA = 0.0; - highpassSampleLDB = 0.0; - highpassSampleLE = 0.0; - highpassSampleLF = 0.0; - - highpassSampleRAA = 0.0; - highpassSampleRAB = 0.0; - highpassSampleRBA = 0.0; - highpassSampleRBB = 0.0; - highpassSampleRCA = 0.0; - highpassSampleRCB = 0.0; - highpassSampleRDA = 0.0; - highpassSampleRDB = 0.0; - highpassSampleRE = 0.0; - highpassSampleRF = 0.0; - - flip = false; - flipthree = 0; - - fpdL = 1.0; - while (fpdL < 16386) fpdL = rand() * UINT32_MAX; - fpdR = 1.0; - while (fpdR < 16386) fpdR = rand() * UINT32_MAX; - // this is reset: values being initialized only once. Startup values, whatever they are. - } - - void set_treble(double value) { A = clamp(value); } - - void set_mid(double value) { B = clamp(value); } - - void set_bass(double value) { C = clamp(value); } - - void set_lowpass(double value) { D = clamp(value); } - - void set_treble_frq(double value) { E = clamp(value); } - - void set_bass_frq(double value) { F = clamp(value); } - - void set_hipass(double value) { G = clamp(value); } - - void set_out_gain(double value) { H = clamp(value); } - - void set_samplerate(double _samplerate) { samplerate = _samplerate; } - - template - void process_block(t_sample** inputs, t_sample** outputs, long sampleframes) - { - t_sample* in1 = inputs[0]; - t_sample* in2 = inputs[1]; - t_sample* out1 = outputs[0]; - t_sample* out2 = outputs[1]; - - double overallscale = 1.0; - overallscale /= 44100.0; - double compscale = overallscale; - overallscale = samplerate; - compscale = compscale * overallscale; - // compscale is the one that's 1 or something like 2.2 for 96K rates - - double inputSampleL; - double inputSampleR; - - double highSampleL = 0.0; - double midSampleL = 0.0; - double bassSampleL = 0.0; - - double highSampleR = 0.0; - double midSampleR = 0.0; - double bassSampleR = 0.0; - - double densityA = (A * 12.0) - 6.0; - double densityB = (B * 12.0) - 6.0; - double densityC = (C * 12.0) - 6.0; - bool engageEQ = true; - if ((0.0 == densityA) && (0.0 == densityB) && (0.0 == densityC)) engageEQ = false; - - densityA = pow(10.0, densityA / 20.0) - 1.0; - densityB = pow(10.0, densityB / 20.0) - 1.0; - densityC = pow(10.0, densityC / 20.0) - 1.0; - // convert to 0 to X multiplier with 1.0 being O db - // minus one gives nearly -1 to ? (should top out at 1) - // calibrate so that X db roughly equals X db with maximum topping out at 1 internally - - double tripletIntensity = -densityA; - - double iirAmountC = (((D * D * 15.0) + 1.0) * 0.0188) + 0.7; - if (iirAmountC > 1.0) iirAmountC = 1.0; - bool engageLowpass = false; - if (((D * D * 15.0) + 1.0) < 15.99) engageLowpass = true; - - double iirAmountA = (((E * E * 15.0) + 1.0) * 1000) / overallscale; - double iirAmountB = (((F * F * 1570.0) + 30.0) * 10) / overallscale; - double iirAmountD = (((G * G * 1570.0) + 30.0) * 1.0) / overallscale; - bool engageHighpass = false; - if (((G * G * 1570.0) + 30.0) > 30.01) engageHighpass = true; - // bypass the highpass and lowpass if set to extremes - double bridgerectifier; - double outA = fabs(densityA); - double outB = fabs(densityB); - double outC = fabs(densityC); - // end EQ - double outputgain = pow(10.0, ((H * 36.0) - 18.0) / 20.0); - - while (--sampleframes >= 0) { - inputSampleL = *in1; - inputSampleR = *in2; - if (fabs(inputSampleL) < 1.18e-23) inputSampleL = fpdL * 1.18e-17; - if (fabs(inputSampleR) < 1.18e-23) inputSampleR = fpdR * 1.18e-17; - - last2SampleL = lastSampleL; - lastSampleL = inputSampleL; - - last2SampleR = lastSampleR; - lastSampleR = inputSampleR; - - flip = !flip; - flipthree++; - if (flipthree < 1 || flipthree > 3) flipthree = 1; - // counters - - // begin highpass - if (engageHighpass) { - if (flip) { - highpassSampleLAA = (highpassSampleLAA * (1.0 - iirAmountD)) + (inputSampleL * iirAmountD); - inputSampleL -= highpassSampleLAA; - highpassSampleLBA = (highpassSampleLBA * (1.0 - iirAmountD)) + (inputSampleL * iirAmountD); - inputSampleL -= highpassSampleLBA; - highpassSampleLCA = (highpassSampleLCA * (1.0 - iirAmountD)) + (inputSampleL * iirAmountD); - inputSampleL -= highpassSampleLCA; - highpassSampleLDA = (highpassSampleLDA * (1.0 - iirAmountD)) + (inputSampleL * iirAmountD); - inputSampleL -= highpassSampleLDA; - } else { - highpassSampleLAB = (highpassSampleLAB * (1.0 - iirAmountD)) + (inputSampleL * iirAmountD); - inputSampleL -= highpassSampleLAB; - highpassSampleLBB = (highpassSampleLBB * (1.0 - iirAmountD)) + (inputSampleL * iirAmountD); - inputSampleL -= highpassSampleLBB; - highpassSampleLCB = (highpassSampleLCB * (1.0 - iirAmountD)) + (inputSampleL * iirAmountD); - inputSampleL -= highpassSampleLCB; - highpassSampleLDB = (highpassSampleLDB * (1.0 - iirAmountD)) + (inputSampleL * iirAmountD); - inputSampleL -= highpassSampleLDB; - } - highpassSampleLE = (highpassSampleLE * (1.0 - iirAmountD)) + (inputSampleL * iirAmountD); - inputSampleL -= highpassSampleLE; - highpassSampleLF = (highpassSampleLF * (1.0 - iirAmountD)) + (inputSampleL * iirAmountD); - inputSampleL -= highpassSampleLF; - - if (flip) { - highpassSampleRAA = (highpassSampleRAA * (1.0 - iirAmountD)) + (inputSampleR * iirAmountD); - inputSampleR -= highpassSampleRAA; - highpassSampleRBA = (highpassSampleRBA * (1.0 - iirAmountD)) + (inputSampleR * iirAmountD); - inputSampleR -= highpassSampleRBA; - highpassSampleRCA = (highpassSampleRCA * (1.0 - iirAmountD)) + (inputSampleR * iirAmountD); - inputSampleR -= highpassSampleRCA; - highpassSampleRDA = (highpassSampleRDA * (1.0 - iirAmountD)) + (inputSampleR * iirAmountD); - inputSampleR -= highpassSampleRDA; - } else { - highpassSampleRAB = (highpassSampleRAB * (1.0 - iirAmountD)) + (inputSampleR * iirAmountD); - inputSampleR -= highpassSampleRAB; - highpassSampleRBB = (highpassSampleRBB * (1.0 - iirAmountD)) + (inputSampleR * iirAmountD); - inputSampleR -= highpassSampleRBB; - highpassSampleRCB = (highpassSampleRCB * (1.0 - iirAmountD)) + (inputSampleR * iirAmountD); - inputSampleR -= highpassSampleRCB; - highpassSampleRDB = (highpassSampleRDB * (1.0 - iirAmountD)) + (inputSampleR * iirAmountD); - inputSampleR -= highpassSampleRDB; - } - highpassSampleRE = (highpassSampleRE * (1 - iirAmountD)) + (inputSampleR * iirAmountD); - inputSampleR -= highpassSampleRE; - highpassSampleRF = (highpassSampleRF * (1 - iirAmountD)) + (inputSampleR * iirAmountD); - inputSampleR -= highpassSampleRF; - } - // end highpass - - // begin EQ - if (engageEQ) { - switch (flipthree) { - case 1: - tripletFactorL = last2SampleL - inputSampleL; - tripletLA += tripletFactorL; - tripletLC -= tripletFactorL; - tripletFactorL = tripletLA * tripletIntensity; - iirHighSampleLC = (iirHighSampleLC * (1.0 - iirAmountA)) + (inputSampleL * iirAmountA); - highSampleL = inputSampleL - iirHighSampleLC; - iirLowSampleLC = (iirLowSampleLC * (1.0 - iirAmountB)) + (inputSampleL * iirAmountB); - bassSampleL = iirLowSampleLC; - - tripletFactorR = last2SampleR - inputSampleR; - tripletRA += tripletFactorR; - tripletRC -= tripletFactorR; - tripletFactorR = tripletRA * tripletIntensity; - iirHighSampleRC = (iirHighSampleRC * (1.0 - iirAmountA)) + (inputSampleR * iirAmountA); - highSampleR = inputSampleR - iirHighSampleRC; - iirLowSampleRC = (iirLowSampleRC * (1.0 - iirAmountB)) + (inputSampleR * iirAmountB); - bassSampleR = iirLowSampleRC; - break; - case 2: - tripletFactorL = last2SampleL - inputSampleL; - tripletLB += tripletFactorL; - tripletLA -= tripletFactorL; - tripletFactorL = tripletLB * tripletIntensity; - iirHighSampleLD = (iirHighSampleLD * (1.0 - iirAmountA)) + (inputSampleL * iirAmountA); - highSampleL = inputSampleL - iirHighSampleLD; - iirLowSampleLD = (iirLowSampleLD * (1.0 - iirAmountB)) + (inputSampleL * iirAmountB); - bassSampleL = iirLowSampleLD; - - tripletFactorR = last2SampleR - inputSampleR; - tripletRB += tripletFactorR; - tripletRA -= tripletFactorR; - tripletFactorR = tripletRB * tripletIntensity; - iirHighSampleRD = (iirHighSampleRD * (1.0 - iirAmountA)) + (inputSampleR * iirAmountA); - highSampleR = inputSampleR - iirHighSampleRD; - iirLowSampleRD = (iirLowSampleRD * (1.0 - iirAmountB)) + (inputSampleR * iirAmountB); - bassSampleR = iirLowSampleRD; - break; - case 3: - tripletFactorL = last2SampleL - inputSampleL; - tripletLC += tripletFactorL; - tripletLB -= tripletFactorL; - tripletFactorL = tripletLC * tripletIntensity; - iirHighSampleLE = (iirHighSampleLE * (1.0 - iirAmountA)) + (inputSampleL * iirAmountA); - highSampleL = inputSampleL - iirHighSampleLE; - iirLowSampleLE = (iirLowSampleLE * (1.0 - iirAmountB)) + (inputSampleL * iirAmountB); - bassSampleL = iirLowSampleLE; - - tripletFactorR = last2SampleR - inputSampleR; - tripletRC += tripletFactorR; - tripletRB -= tripletFactorR; - tripletFactorR = tripletRC * tripletIntensity; - iirHighSampleRE = (iirHighSampleRE * (1.0 - iirAmountA)) + (inputSampleR * iirAmountA); - highSampleR = inputSampleR - iirHighSampleRE; - iirLowSampleRE = (iirLowSampleRE * (1.0 - iirAmountB)) + (inputSampleR * iirAmountB); - bassSampleR = iirLowSampleRE; - break; - } - tripletLA /= 2.0; - tripletLB /= 2.0; - tripletLC /= 2.0; - highSampleL = highSampleL + tripletFactorL; - - tripletRA /= 2.0; - tripletRB /= 2.0; - tripletRC /= 2.0; - highSampleR = highSampleR + tripletFactorR; - - if (flip) { - iirHighSampleLA = (iirHighSampleLA * (1.0 - iirAmountA)) + (highSampleL * iirAmountA); - highSampleL -= iirHighSampleLA; - iirLowSampleLA = (iirLowSampleLA * (1.0 - iirAmountB)) + (bassSampleL * iirAmountB); - bassSampleL = iirLowSampleLA; - - iirHighSampleRA = (iirHighSampleRA * (1.0 - iirAmountA)) + (highSampleR * iirAmountA); - highSampleR -= iirHighSampleRA; - iirLowSampleRA = (iirLowSampleRA * (1.0 - iirAmountB)) + (bassSampleR * iirAmountB); - bassSampleR = iirLowSampleRA; - } else { - iirHighSampleLB = (iirHighSampleLB * (1.0 - iirAmountA)) + (highSampleL * iirAmountA); - highSampleL -= iirHighSampleLB; - iirLowSampleLB = (iirLowSampleLB * (1.0 - iirAmountB)) + (bassSampleL * iirAmountB); - bassSampleL = iirLowSampleLB; - - iirHighSampleRB = (iirHighSampleRB * (1.0 - iirAmountA)) + (highSampleR * iirAmountA); - highSampleR -= iirHighSampleRB; - iirLowSampleRB = (iirLowSampleRB * (1.0 - iirAmountB)) + (bassSampleR * iirAmountB); - bassSampleR = iirLowSampleRB; - } - - iirHighSampleL = (iirHighSampleL * (1.0 - iirAmountA)) + (highSampleL * iirAmountA); - highSampleL -= iirHighSampleL; - iirLowSampleL = (iirLowSampleL * (1.0 - iirAmountB)) + (bassSampleL * iirAmountB); - bassSampleL = iirLowSampleL; - - iirHighSampleR = (iirHighSampleR * (1.0 - iirAmountA)) + (highSampleR * iirAmountA); - highSampleR -= iirHighSampleR; - iirLowSampleR = (iirLowSampleR * (1.0 - iirAmountB)) + (bassSampleR * iirAmountB); - bassSampleR = iirLowSampleR; - - midSampleL = (inputSampleL - bassSampleL) - highSampleL; - midSampleR = (inputSampleR - bassSampleR) - highSampleR; - - // drive section - highSampleL *= (densityA + 1.0); - bridgerectifier = fabs(highSampleL) * 1.57079633; - if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633; - // max value for sine function - if (densityA > 0) bridgerectifier = sin(bridgerectifier); - else bridgerectifier = 1 - cos(bridgerectifier); - // produce either boosted or starved version - if (highSampleL > 0) highSampleL = (highSampleL * (1 - outA)) + (bridgerectifier * outA); - else highSampleL = (highSampleL * (1 - outA)) - (bridgerectifier * outA); - // blend according to densityA control - - highSampleR *= (densityA + 1.0); - bridgerectifier = fabs(highSampleR) * 1.57079633; - if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633; - // max value for sine function - if (densityA > 0) bridgerectifier = sin(bridgerectifier); - else bridgerectifier = 1 - cos(bridgerectifier); - // produce either boosted or starved version - if (highSampleR > 0) highSampleR = (highSampleR * (1 - outA)) + (bridgerectifier * outA); - else highSampleR = (highSampleR * (1 - outA)) - (bridgerectifier * outA); - // blend according to densityA control - - midSampleL *= (densityB + 1.0); - bridgerectifier = fabs(midSampleL) * 1.57079633; - if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633; - // max value for sine function - if (densityB > 0) bridgerectifier = sin(bridgerectifier); - else bridgerectifier = 1 - cos(bridgerectifier); - // produce either boosted or starved version - if (midSampleL > 0) midSampleL = (midSampleL * (1 - outB)) + (bridgerectifier * outB); - else midSampleL = (midSampleL * (1 - outB)) - (bridgerectifier * outB); - // blend according to densityB control - - midSampleR *= (densityB + 1.0); - bridgerectifier = fabs(midSampleR) * 1.57079633; - if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633; - // max value for sine function - if (densityB > 0) bridgerectifier = sin(bridgerectifier); - else bridgerectifier = 1 - cos(bridgerectifier); - // produce either boosted or starved version - if (midSampleR > 0) midSampleR = (midSampleR * (1 - outB)) + (bridgerectifier * outB); - else midSampleR = (midSampleR * (1 - outB)) - (bridgerectifier * outB); - // blend according to densityB control - - bassSampleL *= (densityC + 1.0); - bridgerectifier = fabs(bassSampleL) * 1.57079633; - if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633; - // max value for sine function - if (densityC > 0) bridgerectifier = sin(bridgerectifier); - else bridgerectifier = 1 - cos(bridgerectifier); - // produce either boosted or starved version - if (bassSampleL > 0) bassSampleL = (bassSampleL * (1 - outC)) + (bridgerectifier * outC); - else bassSampleL = (bassSampleL * (1 - outC)) - (bridgerectifier * outC); - // blend according to densityC control - - bassSampleR *= (densityC + 1.0); - bridgerectifier = fabs(bassSampleR) * 1.57079633; - if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633; - // max value for sine function - if (densityC > 0) bridgerectifier = sin(bridgerectifier); - else bridgerectifier = 1 - cos(bridgerectifier); - // produce either boosted or starved version - if (bassSampleR > 0) bassSampleR = (bassSampleR * (1 - outC)) + (bridgerectifier * outC); - else bassSampleR = (bassSampleR * (1 - outC)) - (bridgerectifier * outC); - // blend according to densityC control - - inputSampleL = midSampleL; - inputSampleL += highSampleL; - inputSampleL += bassSampleL; - - inputSampleR = midSampleR; - inputSampleR += highSampleR; - inputSampleR += bassSampleR; - } - // end EQ - - // EQ lowpass is after all processing like the compressor that might produce hash - if (engageLowpass) { - if (flip) { - lowpassSampleLAA = (lowpassSampleLAA * (1.0 - iirAmountC)) + (inputSampleL * iirAmountC); - inputSampleL = lowpassSampleLAA; - lowpassSampleLBA = (lowpassSampleLBA * (1.0 - iirAmountC)) + (inputSampleL * iirAmountC); - inputSampleL = lowpassSampleLBA; - lowpassSampleLCA = (lowpassSampleLCA * (1.0 - iirAmountC)) + (inputSampleL * iirAmountC); - inputSampleL = lowpassSampleLCA; - lowpassSampleLDA = (lowpassSampleLDA * (1.0 - iirAmountC)) + (inputSampleL * iirAmountC); - inputSampleL = lowpassSampleLDA; - lowpassSampleLE = (lowpassSampleLE * (1.0 - iirAmountC)) + (inputSampleL * iirAmountC); - inputSampleL = lowpassSampleLE; - - lowpassSampleRAA = (lowpassSampleRAA * (1.0 - iirAmountC)) + (inputSampleR * iirAmountC); - inputSampleR = lowpassSampleRAA; - lowpassSampleRBA = (lowpassSampleRBA * (1.0 - iirAmountC)) + (inputSampleR * iirAmountC); - inputSampleR = lowpassSampleRBA; - lowpassSampleRCA = (lowpassSampleRCA * (1.0 - iirAmountC)) + (inputSampleR * iirAmountC); - inputSampleR = lowpassSampleRCA; - lowpassSampleRDA = (lowpassSampleRDA * (1.0 - iirAmountC)) + (inputSampleR * iirAmountC); - inputSampleR = lowpassSampleRDA; - lowpassSampleRE = (lowpassSampleRE * (1.0 - iirAmountC)) + (inputSampleR * iirAmountC); - inputSampleR = lowpassSampleRE; - } else { - lowpassSampleLAB = (lowpassSampleLAB * (1.0 - iirAmountC)) + (inputSampleL * iirAmountC); - inputSampleL = lowpassSampleLAB; - lowpassSampleLBB = (lowpassSampleLBB * (1.0 - iirAmountC)) + (inputSampleL * iirAmountC); - inputSampleL = lowpassSampleLBB; - lowpassSampleLCB = (lowpassSampleLCB * (1.0 - iirAmountC)) + (inputSampleL * iirAmountC); - inputSampleL = lowpassSampleLCB; - lowpassSampleLDB = (lowpassSampleLDB * (1.0 - iirAmountC)) + (inputSampleL * iirAmountC); - inputSampleL = lowpassSampleLDB; - lowpassSampleLF = (lowpassSampleLF * (1.0 - iirAmountC)) + (inputSampleL * iirAmountC); - inputSampleL = lowpassSampleLF; - - lowpassSampleRAB = (lowpassSampleRAB * (1.0 - iirAmountC)) + (inputSampleR * iirAmountC); - inputSampleR = lowpassSampleRAB; - lowpassSampleRBB = (lowpassSampleRBB * (1.0 - iirAmountC)) + (inputSampleR * iirAmountC); - inputSampleR = lowpassSampleRBB; - lowpassSampleRCB = (lowpassSampleRCB * (1.0 - iirAmountC)) + (inputSampleR * iirAmountC); - inputSampleR = lowpassSampleRCB; - lowpassSampleRDB = (lowpassSampleRDB * (1.0 - iirAmountC)) + (inputSampleR * iirAmountC); - inputSampleR = lowpassSampleRDB; - lowpassSampleRF = (lowpassSampleRF * (1.0 - iirAmountC)) + (inputSampleR * iirAmountC); - inputSampleR = lowpassSampleRF; - } - lowpassSampleLG = (lowpassSampleLG * (1.0 - iirAmountC)) + (inputSampleL * iirAmountC); - lowpassSampleRG = (lowpassSampleRG * (1.0 - iirAmountC)) + (inputSampleR * iirAmountC); - - inputSampleL = (lowpassSampleLG * (1.0 - iirAmountC)) + (inputSampleL * iirAmountC); - inputSampleR = (lowpassSampleRG * (1.0 - iirAmountC)) + (inputSampleR * iirAmountC); - } - - // built in output trim and dry/wet if desired - if (outputgain != 1.0) { - inputSampleL *= outputgain; - inputSampleR *= outputgain; - } - - // begin 64 bit stereo floating point dither - // int expon; frexp((double)inputSampleL, &expon); - fpdL ^= fpdL << 13; - fpdL ^= fpdL >> 17; - fpdL ^= fpdL << 5; - // inputSampleL += ((double(fpdL)-uint32_t(0x7fffffff)) * 1.1e-44l * pow(2,expon+62)); - // frexp((double)inputSampleR, &expon); - fpdR ^= fpdR << 13; - fpdR ^= fpdR >> 17; - fpdR ^= fpdR << 5; - // inputSampleR += ((double(fpdR)-uint32_t(0x7fffffff)) * 1.1e-44l * pow(2,expon+62)); - // end 64 bit stereo floating point dither - - *out1 = inputSampleL; - *out2 = inputSampleR; - - *in1++; - *in2++; - *out1++; - *out2++; - } - } - -private: - double samplerate; - - uint32_t fpdL; - uint32_t fpdR; - // default stuff - - double lastSampleL; - double last2SampleL; - double lastSampleR; - double last2SampleR; - - // begin EQ - double iirHighSampleLA; - double iirHighSampleLB; - double iirHighSampleLC; - double iirHighSampleLD; - double iirHighSampleLE; - double iirLowSampleLA; - double iirLowSampleLB; - double iirLowSampleLC; - double iirLowSampleLD; - double iirLowSampleLE; - double iirHighSampleL; - double iirLowSampleL; - - double iirHighSampleRA; - double iirHighSampleRB; - double iirHighSampleRC; - double iirHighSampleRD; - double iirHighSampleRE; - double iirLowSampleRA; - double iirLowSampleRB; - double iirLowSampleRC; - double iirLowSampleRD; - double iirLowSampleRE; - double iirHighSampleR; - double iirLowSampleR; - - double tripletLA; - double tripletLB; - double tripletLC; - double tripletFactorL; - - double tripletRA; - double tripletRB; - double tripletRC; - double tripletFactorR; - - double lowpassSampleLAA; - double lowpassSampleLAB; - double lowpassSampleLBA; - double lowpassSampleLBB; - double lowpassSampleLCA; - double lowpassSampleLCB; - double lowpassSampleLDA; - double lowpassSampleLDB; - double lowpassSampleLE; - double lowpassSampleLF; - double lowpassSampleLG; - - double lowpassSampleRAA; - double lowpassSampleRAB; - double lowpassSampleRBA; - double lowpassSampleRBB; - double lowpassSampleRCA; - double lowpassSampleRCB; - double lowpassSampleRDA; - double lowpassSampleRDB; - double lowpassSampleRE; - double lowpassSampleRF; - double lowpassSampleRG; - - double highpassSampleLAA; - double highpassSampleLAB; - double highpassSampleLBA; - double highpassSampleLBB; - double highpassSampleLCA; - double highpassSampleLCB; - double highpassSampleLDA; - double highpassSampleLDB; - double highpassSampleLE; - double highpassSampleLF; - - double highpassSampleRAA; - double highpassSampleRAB; - double highpassSampleRBA; - double highpassSampleRBB; - double highpassSampleRCA; - double highpassSampleRCB; - double highpassSampleRDA; - double highpassSampleRDB; - double highpassSampleRE; - double highpassSampleRF; - - bool flip; - int flipthree; - // end EQ - - float A; - float B; - float C; - float D; - float E; - float F; - float G; - float H; - - double clamp(double& value) - { - if (value > 1) { - value = 1; - } else if (value < 0) { - value = 0; - } - return value; - } -}; -} // namespace trnr \ No newline at end of file diff --git a/filter/spliteq.h b/filter/spliteq.h new file mode 100644 index 0000000..b3803f6 --- /dev/null +++ b/filter/spliteq.h @@ -0,0 +1,538 @@ +#pragma once +#include "audio_math.h" +#include +#include + +namespace trnr { + +// Filter type enum +enum filter_type { + LOWPASS = 0, + HIGHPASS = 1 +}; + +struct cascade_filter { + filter_type type; + int stages; // Number of cascaded stages + double cutoff; // Cutoff frequency (Hz) + double samplerate; // Sample rate (Hz) + double alpha; // Filter coefficient + std::vector state; // State per stage +}; + +inline void cascade_filter_setup(cascade_filter& f, filter_type _type, int _stages, double _cutoff, double _samplerate) +{ + f.type = _type; + f.stages = _stages; + f.cutoff = _cutoff; + f.samplerate = _samplerate; + + // alpha = iirAmount = exp(-2 * pi * cutoff / samplerate); + double x = exp(-2.0 * M_PI * f.cutoff / f.samplerate); + f.alpha = 1.0 - x; + + f.state.resize(f.stages, 0.0); +} + +// Process one sample +inline double cascade_filter_process(cascade_filter& f, double input) +{ + double out = input; + for (int i = 0; i < f.stages; ++i) { + if (f.type == LOWPASS) { + f.state[i] = (f.state[i] * (1.0 - f.alpha)) + (out * f.alpha); + out = f.state[i]; + } else { // CASCADE_HIGHPASS + f.state[i] = (f.state[i] * (1.0 - f.alpha)) + (out * f.alpha); + out -= f.state[i]; + } + } + return out; +} + +// 2nd order Butterworth biquad filter +struct butterworth { + filter_type type; + double cutoff; + double a1, a2; + double b0, b1, b2; + double x1, x2; // previous inputs + double y1, y2; // previous outputs +}; + +// Biquad coefficient calculation +inline void butterworth_biquad_coeffs(butterworth& b, double samplerate) +{ + double omega = 2.0 * M_PI * b.cutoff / samplerate; + double sin_omega = sin(omega); + double cos_omega = cos(omega); + + double Q = 1.0 / sqrt(2.0); // Butterworth Q for 2nd order + double alpha = sin_omega / (2.0 * Q); + double a0 = 1.0 + alpha; + + switch (b.type) { + case LOWPASS: + b.b0 = (1.0 - cos_omega) / 2.0 / a0; + b.b1 = (1.0 - cos_omega) / a0; + b.b2 = (1.0 - cos_omega) / 2.0 / a0; + b.a1 = -2.0 * cos_omega / a0; + b.a2 = (1.0 - alpha) / a0; + break; + case HIGHPASS: + b.b0 = (1.0 + cos_omega) / 2.0 / a0; + b.b1 = -(1.0 + cos_omega) / a0; + b.b2 = (1.0 + cos_omega) / 2.0 / a0; + b.a1 = -2.0 * cos_omega / a0; + b.a2 = (1.0 - alpha) / a0; + break; + } +} + +// Biquad sample processing +inline double butterworth_biquad_process(butterworth& b, double input) +{ + double y = b.b0 * input + b.b1 * b.x1 + b.b2 * b.x2 - b.a1 * b.y1 - b.a2 * b.y2; + b.x2 = b.x1; + b.x1 = input; + b.y2 = b.y1; + b.y1 = y; + return y; +} + +struct aw_filter { + filter_type type; + float amount; + bool flip; + double sampleLAA; + double sampleLAB; + double sampleLBA; + double sampleLBB; + double sampleLCA; + double sampleLCB; + double sampleLDA; + double sampleLDB; + double sampleLE; + double sampleLF; + double sampleLG; + double samplerate; +}; + +inline void aw_filter_init(aw_filter& f, filter_type type, float amount, double samplerate) +{ + f.type = type; + f.amount = amount; + f.samplerate = samplerate; + + f.sampleLAA = 0.0; + f.sampleLAB = 0.0; + f.sampleLBA = 0.0; + f.sampleLBB = 0.0; + f.sampleLCA = 0.0; + f.sampleLCB = 0.0; + f.sampleLDA = 0.0; + f.sampleLDB = 0.0; + f.sampleLE = 0.0; + f.sampleLF = 0.0; + f.sampleLG = 0.0; + + f.flip = false; +} + +inline void aw_filter_process_block(aw_filter& f, float* audio, int frames) +{ + double overallscale = 1.0; + overallscale /= 44100.0; + double compscale = overallscale; + overallscale = f.samplerate; + compscale = compscale * overallscale; + bool engage = false; + + double iir_amt = 0.0; + + if (f.type == LOWPASS) { + iir_amt = (((f.amount * f.amount * 15.0) + 1.0) * 0.0188) + 0.7; + if (iir_amt > 1.0) iir_amt = 1.0; + if (((f.amount * f.amount * 15.0) + 1.0) < 15.99) engage = true; + } else if (f.type == HIGHPASS) { + iir_amt = (((f.amount * f.amount * 1570.0) + 30.0) * 1.0) / overallscale; + if (((f.amount * f.amount * 1570.0) + 30.0) > 30.01) engage = true; + } + + for (int i = 0; i < frames; i++) { + float input = audio[i]; + f.flip = !f.flip; + + if (engage) { + switch (f.type) { + case LOWPASS: + if (f.flip) { + f.sampleLAA = (f.sampleLAA * (1.0 - iir_amt)) + (input * iir_amt); + input = f.sampleLAA; + f.sampleLBA = (f.sampleLBA * (1.0 - iir_amt)) + (input * iir_amt); + input = f.sampleLBA; + f.sampleLCA = (f.sampleLCA * (1.0 - iir_amt)) + (input * iir_amt); + input = f.sampleLCA; + f.sampleLDA = (f.sampleLDA * (1.0 - iir_amt)) + (input * iir_amt); + input = f.sampleLDA; + f.sampleLE = (f.sampleLE * (1.0 - iir_amt)) + (input * iir_amt); + input = f.sampleLE; + } else { + f.sampleLAB = (f.sampleLAB * (1.0 - iir_amt)) + (input * iir_amt); + input = f.sampleLAB; + f.sampleLBB = (f.sampleLBB * (1.0 - iir_amt)) + (input * iir_amt); + input = f.sampleLBB; + f.sampleLCB = (f.sampleLCB * (1.0 - iir_amt)) + (input * iir_amt); + input = f.sampleLCB; + f.sampleLDB = (f.sampleLDB * (1.0 - iir_amt)) + (input * iir_amt); + input = f.sampleLDB; + f.sampleLF = (f.sampleLF * (1.0 - iir_amt)) + (input * iir_amt); + input = f.sampleLF; + } + f.sampleLG = (f.sampleLG * (1.0 - iir_amt)) + (input * iir_amt); + input = (f.sampleLG * (1.0 - iir_amt)) + (input * iir_amt); + break; + case HIGHPASS: + if (f.flip) { + f.sampleLAA = (f.sampleLAA * (1.0 - iir_amt)) + (input * iir_amt); + input -= f.sampleLAA; + f.sampleLBA = (f.sampleLBA * (1.0 - iir_amt)) + (input * iir_amt); + input -= f.sampleLBA; + f.sampleLCA = (f.sampleLCA * (1.0 - iir_amt)) + (input * iir_amt); + input -= f.sampleLCA; + f.sampleLDA = (f.sampleLDA * (1.0 - iir_amt)) + (input * iir_amt); + input -= f.sampleLDA; + } else { + f.sampleLAB = (f.sampleLAB * (1.0 - iir_amt)) + (input * iir_amt); + input -= f.sampleLAB; + f.sampleLBB = (f.sampleLBB * (1.0 - iir_amt)) + (input * iir_amt); + input -= f.sampleLBB; + f.sampleLCB = (f.sampleLCB * (1.0 - iir_amt)) + (input * iir_amt); + input -= f.sampleLCB; + f.sampleLDB = (f.sampleLDB * (1.0 - iir_amt)) + (input * iir_amt); + input -= f.sampleLDB; + } + f.sampleLE = (f.sampleLE * (1.0 - iir_amt)) + (input * iir_amt); + input -= f.sampleLE; + f.sampleLF = (f.sampleLF * (1.0 - iir_amt)) + (input * iir_amt); + input -= f.sampleLF; + break; + } + } + + audio[i] = input; + } +} + +struct spliteq { + aw_filter lp_l, lp_r, hp_l, hp_r; // lowpass and highpass filters + + // cascaded filters + cascade_filter bass_l, bass_r; + cascade_filter treble_l, treble_r; + + // butterworth biquads + butterworth bass1_l, bass2_l, bass1_r, bass2_r; + // Mid: two cascaded highpass THEN two cascaded lowpass per channel + butterworth mid_hp1_l, mid_hp2_l, mid_lp1_l, mid_lp2_l; + butterworth mid_hp1_r, mid_hp2_r, mid_lp1_r, mid_lp2_r; + // Treble: two cascaded highpass filters per channel + butterworth treble1_l, treble2_l, treble1_r, treble2_r; + + double low_mid_crossover = 150.0; // Hz + double mid_high_crossover = 1700.0; // Hz + + // adjusted crossover frequencies for cascade filters + double low_mid_crossover_adj = 150.0; + double mid_high_crossover_adj = 1700.0; + + double samplerate = 48000.0; + + float bass_gain = 1.0f; + float mid_gain = 1.0f; + float treble_gain = 1.0f; + + // adjusted gain for cascade filters + float bass_gain_adj = 1.0f; + float mid_gain_adj = 1.0f; + float treble_gain_adj = 1.0f; + + bool linkwitz_riley_enabled = false; +}; + +inline void spliteq_init(spliteq& eq, double samplerate, double low_mid_crossover, double mid_high_crossover) +{ + low_mid_crossover /= 2.0; + mid_high_crossover /= 2.0; + + eq.samplerate = samplerate; + + eq.low_mid_crossover = low_mid_crossover; + eq.mid_high_crossover = mid_high_crossover; + eq.low_mid_crossover_adj = low_mid_crossover; + eq.mid_high_crossover_adj = mid_high_crossover; + + // initialize lp/hp filters + aw_filter_init(eq.lp_l, LOWPASS, 1.0f, samplerate); + aw_filter_init(eq.lp_r, LOWPASS, 1.0f, samplerate); + aw_filter_init(eq.hp_l, HIGHPASS, 0.0f, samplerate); + aw_filter_init(eq.hp_r, HIGHPASS, 0.0f, samplerate); + + // init cascade filters + cascade_filter_setup(eq.bass_l, LOWPASS, 2, low_mid_crossover, samplerate); + cascade_filter_setup(eq.bass_r, LOWPASS, 2, low_mid_crossover, samplerate); + cascade_filter_setup(eq.treble_l, HIGHPASS, 2, mid_high_crossover, samplerate); + cascade_filter_setup(eq.treble_r, HIGHPASS, 2, mid_high_crossover, samplerate); + + // init butterworth filters + // bass filters + eq.bass1_l.type = LOWPASS; + eq.bass1_l.cutoff = low_mid_crossover; + eq.bass1_l.x1 = eq.bass1_l.x2 = eq.bass1_l.y1 = eq.bass1_l.y2 = 0.0; + butterworth_biquad_coeffs(eq.bass1_l, samplerate); + + eq.bass2_l.type = LOWPASS; + eq.bass2_l.cutoff = low_mid_crossover; + eq.bass2_l.x1 = eq.bass2_l.x2 = eq.bass2_l.y1 = eq.bass2_l.y2 = 0.0; + butterworth_biquad_coeffs(eq.bass2_l, samplerate); + + eq.bass1_r.type = LOWPASS; + eq.bass1_r.cutoff = low_mid_crossover; + eq.bass1_r.x1 = eq.bass1_r.x2 = eq.bass1_r.y1 = eq.bass1_r.y2 = 0.0; + butterworth_biquad_coeffs(eq.bass1_r, samplerate); + + eq.bass2_r.type = LOWPASS; + eq.bass2_r.cutoff = low_mid_crossover; + eq.bass2_r.x1 = eq.bass2_r.x2 = eq.bass2_r.y1 = eq.bass2_r.y2 = 0.0; + butterworth_biquad_coeffs(eq.bass2_r, samplerate); + + // mid filters (HPF x2, then LPF x2) + eq.mid_hp1_l.type = HIGHPASS; + eq.mid_hp1_l.cutoff = low_mid_crossover; + eq.mid_hp1_l.x1 = eq.mid_hp1_l.x2 = eq.mid_hp1_l.y1 = eq.mid_hp1_l.y2 = 0.0; + butterworth_biquad_coeffs(eq.mid_hp1_l, samplerate); + + eq.mid_hp2_l.type = HIGHPASS; + eq.mid_hp2_l.cutoff = low_mid_crossover; + eq.mid_hp2_l.x1 = eq.mid_hp2_l.x2 = eq.mid_hp2_l.y1 = eq.mid_hp2_l.y2 = 0.0; + butterworth_biquad_coeffs(eq.mid_hp2_l, samplerate); + + eq.mid_lp1_l.type = LOWPASS; + eq.mid_lp1_l.cutoff = mid_high_crossover; + eq.mid_lp1_l.x1 = eq.mid_lp1_l.x2 = eq.mid_lp1_l.y1 = eq.mid_lp1_l.y2 = 0.0; + butterworth_biquad_coeffs(eq.mid_lp1_l, samplerate); + + eq.mid_lp2_l.type = LOWPASS; + eq.mid_lp2_l.cutoff = mid_high_crossover; + eq.mid_lp2_l.x1 = eq.mid_lp2_l.x2 = eq.mid_lp2_l.y1 = eq.mid_lp2_l.y2 = 0.0; + butterworth_biquad_coeffs(eq.mid_lp2_l, samplerate); + + eq.mid_hp1_r.type = HIGHPASS; + eq.mid_hp1_r.cutoff = low_mid_crossover; + eq.mid_hp1_r.x1 = eq.mid_hp1_r.x2 = eq.mid_hp1_r.y1 = eq.mid_hp1_r.y2 = 0.0; + butterworth_biquad_coeffs(eq.mid_hp1_r, samplerate); + + eq.mid_hp2_r.type = HIGHPASS; + eq.mid_hp2_r.cutoff = low_mid_crossover; + eq.mid_hp2_r.x1 = eq.mid_hp2_r.x2 = eq.mid_hp2_r.y1 = eq.mid_hp2_r.y2 = 0.0; + butterworth_biquad_coeffs(eq.mid_hp2_r, samplerate); + + eq.mid_lp1_r.type = LOWPASS; + eq.mid_lp1_r.cutoff = mid_high_crossover; + eq.mid_lp1_r.x1 = eq.mid_lp1_r.x2 = eq.mid_lp1_r.y1 = eq.mid_lp1_r.y2 = 0.0; + butterworth_biquad_coeffs(eq.mid_lp1_r, samplerate); + + eq.mid_lp2_r.type = LOWPASS; + eq.mid_lp2_r.cutoff = mid_high_crossover; + eq.mid_lp2_r.x1 = eq.mid_lp2_r.x2 = eq.mid_lp2_r.y1 = eq.mid_lp2_r.y2 = 0.0; + butterworth_biquad_coeffs(eq.mid_lp2_r, samplerate); + + // treble filters + eq.treble1_l.type = HIGHPASS; + eq.treble1_l.cutoff = mid_high_crossover; + eq.treble1_l.x1 = eq.treble1_l.x2 = eq.treble1_l.y1 = eq.treble1_l.y2 = 0.0; + butterworth_biquad_coeffs(eq.treble1_l, samplerate); + + eq.treble2_l.type = HIGHPASS; + eq.treble2_l.cutoff = mid_high_crossover; + eq.treble2_l.x1 = eq.treble2_l.x2 = eq.treble2_l.y1 = eq.treble2_l.y2 = 0.0; + butterworth_biquad_coeffs(eq.treble2_l, samplerate); + + eq.treble1_r.type = HIGHPASS; + eq.treble1_r.cutoff = mid_high_crossover; + eq.treble1_r.x1 = eq.treble1_r.x2 = eq.treble1_r.y1 = eq.treble1_r.y2 = 0.0; + butterworth_biquad_coeffs(eq.treble1_r, samplerate); + + eq.treble2_r.type = HIGHPASS; + eq.treble2_r.cutoff = mid_high_crossover; + eq.treble2_r.x1 = eq.treble2_r.x2 = eq.treble2_r.y1 = eq.treble2_r.y2 = 0.0; + butterworth_biquad_coeffs(eq.treble2_r, samplerate); +} + +// Process block (stereo) +inline void spliteq_process_block(spliteq& eq, float** audio, int frames) +{ + aw_filter_process_block(eq.hp_l, audio[0], frames); + aw_filter_process_block(eq.hp_r, audio[1], frames); + + for (int i = 0; i < frames; i++) { + if (eq.linkwitz_riley_enabled) { + // butterwort/linkwitz-riley + + // Left channel + double input_l = audio[0][i]; + // Bass + double bass_l = butterworth_biquad_process(eq.bass1_l, input_l); + bass_l = butterworth_biquad_process(eq.bass2_l, bass_l); + // Mid + double mid_l = butterworth_biquad_process(eq.mid_hp1_l, input_l); + mid_l = butterworth_biquad_process(eq.mid_hp2_l, mid_l); + mid_l = butterworth_biquad_process(eq.mid_lp1_l, mid_l); + mid_l = butterworth_biquad_process(eq.mid_lp2_l, mid_l); + // Treble + double treble_l = butterworth_biquad_process(eq.treble1_l, input_l); + treble_l = butterworth_biquad_process(eq.treble2_l, treble_l); + + // Apply gains + bass_l *= eq.bass_gain; + mid_l *= eq.mid_gain; + treble_l *= eq.treble_gain; + + // Sum bands + audio[0][i] = bass_l + mid_l + treble_l; + + // Right channel + double input_r = audio[1][i]; + double bass_r = butterworth_biquad_process(eq.bass1_r, input_r); + bass_r = butterworth_biquad_process(eq.bass2_r, bass_r); + + double mid_r = butterworth_biquad_process(eq.mid_hp1_r, input_r); + mid_r = butterworth_biquad_process(eq.mid_hp2_r, mid_r); + mid_r = butterworth_biquad_process(eq.mid_lp1_r, mid_r); + mid_r = butterworth_biquad_process(eq.mid_lp2_r, mid_r); + + double treble_r = butterworth_biquad_process(eq.treble1_r, input_r); + treble_r = butterworth_biquad_process(eq.treble2_r, treble_r); + + bass_r *= eq.bass_gain; + mid_r *= eq.mid_gain; + treble_r *= eq.treble_gain; + + audio[1][i] = bass_r + mid_r + treble_r; + } else { + // cascade/sum + double input_l = audio[0][i]; + double input_r = audio[1][i]; + + double bass_l = cascade_filter_process(eq.bass_l, input_l); + double bass_r = cascade_filter_process(eq.bass_r, input_r); + + double treble_l = cascade_filter_process(eq.treble_l, input_l); + double treble_r = cascade_filter_process(eq.treble_r, input_r); + + double mid_l = input_l - bass_l - treble_l; + double mid_r = input_r - bass_r - treble_r; + + // Apply gains + bass_l *= eq.bass_gain_adj; + bass_r *= eq.bass_gain_adj; + mid_l *= eq.mid_gain_adj; + mid_r *= eq.mid_gain_adj; + treble_l *= eq.treble_gain_adj; + treble_r *= eq.treble_gain_adj; + + // Sum bands + audio[0][i] = bass_l + mid_l + treble_l; + audio[1][i] = bass_r + mid_r + treble_r; + } + } + + aw_filter_process_block(eq.lp_l, audio[0], frames); + aw_filter_process_block(eq.lp_r, audio[1], frames); +} + +inline void spliteq_update(spliteq& eq, double hp_freq, double lp_freq, double low_mid_crossover, + double mid_high_crossover, double bass_gain, double mid_gain, double treble_gain) +{ + low_mid_crossover /= 2.0; + mid_high_crossover /= 2.0; + + eq.bass_gain = db_2_lin(bass_gain); + eq.mid_gain = db_2_lin(mid_gain); + eq.treble_gain = db_2_lin(treble_gain); + + if (bass_gain > 0.f) { + eq.bass_gain = eq.bass_gain_adj = db_2_lin(bass_gain * 0.85f); + eq.low_mid_crossover_adj = low_mid_crossover; + } else { + eq.bass_gain_adj = db_2_lin(bass_gain); + eq.low_mid_crossover_adj = low_mid_crossover * 2.0; + } + + if (mid_gain > 0.0f) eq.mid_gain_adj = db_2_lin(mid_gain * 0.85f); + else eq.mid_gain_adj = db_2_lin(mid_gain * 0.74f); + + if (treble_gain > 0.f) { + eq.treble_gain_adj = db_2_lin(treble_gain * 1.1f); + eq.mid_high_crossover_adj = mid_high_crossover; + } else { + eq.treble_gain_adj = db_2_lin(treble_gain); + eq.mid_high_crossover_adj = mid_high_crossover / 2.0; + } + + eq.low_mid_crossover = low_mid_crossover; + eq.mid_high_crossover = mid_high_crossover; + + eq.hp_l.amount = hp_freq; + eq.hp_r.amount = hp_freq; + eq.lp_l.amount = lp_freq; + eq.lp_r.amount = lp_freq; + + cascade_filter_setup(eq.bass_l, LOWPASS, 2, eq.low_mid_crossover_adj, eq.samplerate); + cascade_filter_setup(eq.bass_r, LOWPASS, 2, eq.low_mid_crossover_adj, eq.samplerate); + cascade_filter_setup(eq.treble_l, HIGHPASS, 2, eq.mid_high_crossover_adj, eq.samplerate); + cascade_filter_setup(eq.treble_r, HIGHPASS, 2, eq.mid_high_crossover_adj, eq.samplerate); + + eq.bass1_l.cutoff = low_mid_crossover; + butterworth_biquad_coeffs(eq.bass1_l, eq.samplerate); + eq.bass2_l.cutoff = low_mid_crossover; + butterworth_biquad_coeffs(eq.bass2_l, eq.samplerate); + eq.bass1_r.cutoff = low_mid_crossover; + butterworth_biquad_coeffs(eq.bass1_r, eq.samplerate); + eq.bass2_r.cutoff = low_mid_crossover; + butterworth_biquad_coeffs(eq.bass2_r, eq.samplerate); + + eq.mid_hp1_l.cutoff = low_mid_crossover; + butterworth_biquad_coeffs(eq.mid_hp1_l, eq.samplerate); + eq.mid_hp2_l.cutoff = low_mid_crossover; + butterworth_biquad_coeffs(eq.mid_hp2_l, eq.samplerate); + eq.mid_lp1_l.cutoff = mid_high_crossover; + butterworth_biquad_coeffs(eq.mid_lp1_l, eq.samplerate); + eq.mid_lp2_l.cutoff = mid_high_crossover; + butterworth_biquad_coeffs(eq.mid_lp2_l, eq.samplerate); + + eq.mid_hp1_r.cutoff = low_mid_crossover; + butterworth_biquad_coeffs(eq.mid_hp1_r, eq.samplerate); + eq.mid_hp2_r.cutoff = low_mid_crossover; + butterworth_biquad_coeffs(eq.mid_hp2_r, eq.samplerate); + eq.mid_lp1_r.cutoff = mid_high_crossover; + butterworth_biquad_coeffs(eq.mid_lp1_r, eq.samplerate); + eq.mid_lp2_r.cutoff = mid_high_crossover; + butterworth_biquad_coeffs(eq.mid_lp2_r, eq.samplerate); + + eq.treble1_l.cutoff = mid_high_crossover; + butterworth_biquad_coeffs(eq.treble1_l, eq.samplerate); + eq.treble2_l.cutoff = mid_high_crossover; + butterworth_biquad_coeffs(eq.treble2_l, eq.samplerate); + eq.treble1_r.cutoff = mid_high_crossover; + butterworth_biquad_coeffs(eq.treble1_r, eq.samplerate); + eq.treble2_r.cutoff = mid_high_crossover; + butterworth_biquad_coeffs(eq.treble2_r, eq.samplerate); +} + +inline void spliteq_update(spliteq& eq, double bass_gain, double mid_gain, double treble_gain) +{ + trnr::spliteq_update(eq, eq.hp_l.amount, eq.lp_l.amount, eq.low_mid_crossover * 2.0, eq.mid_high_crossover * 2.0, + bass_gain, mid_gain, treble_gain); +} +} // namespace trnr