3-Band EQ help

PegasYs

Member
Hello I am very new to digital audio software designing, though I've been a producer of electronic music for more than a decade. I'm trying to make a simple 3-band EQ with the Teensy audio shield. So far I am testing out the Linkwitz-Riley filter as I know it is generally what you need to create clean crossovers for audio bands, but when the band frequency is low, the highpassed band sounds heavily distored. Is there any way around this? I'll post my code as is

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           //xy=229,383
AudioFilterBiquad        biquad2;        //xy=531,489
AudioFilterBiquad        biquad1;        //xy=540,315
AudioMixer4              mixer1;         //xy=861,288
AudioOutputI2S           i2s2;           //xy=1332,411
AudioConnection          patchCord1(i2s1, 0, biquad1, 0);
AudioConnection          patchCord2(i2s1, 0, biquad2, 0);
AudioConnection          patchCord3(biquad2, 0, mixer1, 1);
AudioConnection          patchCord4(biquad1, 0, mixer1, 0);
AudioConnection          patchCord5(mixer1, 0, i2s2, 0);
AudioConnection          patchCord6(mixer1, 0, i2s2, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=340,169
// GUItool: end automatically generated code




const int myInput = AUDIO_INPUT_LINEIN;
//const int myInput = AUDIO_INPUT_MIC;

void setup() {
  AudioMemory(12);

  sgtl5000_1.enable();  // Enable the audio shield
  sgtl5000_1.inputSelect(myInput);
  sgtl5000_1.volume(0.5);




 
}


void loop() {

int knob = analogRead(A5);
int knob1 = analogRead(A0);
int knob2 = analogRead(A1);

float F = (float(knob) * 18) + 20;
float lowGain = float(knob1) / 1023;
float highGain = float(knob2) / 1023;


  biquad1.setLowpass(0, F, 0.54);
  biquad1.setLowpass(1, F, 1.3);
  biquad1.setLowpass(2, F, 0.54);
  biquad1.setLowpass(3, F, 1.3);

  biquad2.setHighpass(0, F, 0.54);
  biquad2.setHighpass(1, F, 1.3);
  biquad2.setHighpass(2, F, 0.54);
  biquad2.setHighpass(3, F, 1.3);

  Serial.println(F);

  mixer1.gain(0, lowGain);
  mixer1.gain(1, highGain);
}
 
In my experience, for all inputs into a single mixer, the sum of these should never be allowed to exceed 1.0 (in your sketch above, your two xxxGain values can each theoretically reach 1.0, allowing their sum to theoretically reach 2.0).

Try changing these two lines in your sketch:

Code:
  mixer1.gain(0, lowGain);
  mixer1.gain(1, highGain);

to this:

Code:
  mixer1.gain(0, lowGain / 2);
  mixer1.gain(1, highGain / 2);

See if that helps . . .

Mark J Culross
KD5RXT
 
Hi, thank you it helps a little, that was probably an unwise design choice.

Despite this though, it still has very noticeable distortion in the high band, it becomes very apparent when I sweep my crossover frequency to less than 1000 Hz. And it even persists without the lowpass being enabled so it doesn't have to do with the summing of the two channels. When I isolate the lowpass and bring it up to total pass (well, in this case about 18kHz) I do not hear any distortion at all
 
To add on to this, it's also not related to any actual internal hard clipping, as the distortion persists and stays tonally the same as I bring down the gain (I'm then using my audio interface to make up the gain so I can more or less hear it at the same output volume)
 
From the Notes section for the biquad filter:
Biquad filters with low corner frequency (under about 400 Hz) can run into trouble with limited numerical precision, causing the filter to perform poorly. For very low corner frequency, the State Variable (Chamberlin) filter should be used
 
The low frequency performance issue at #5 is the first things I'd suspect.

You're also reading the pots and updating coefficients every loop which will result in coefficients being reset millions of times a second and could be adding distortion. You'd be better checking if the pots have moved significantly (they will tend to jump around by a few points) before updating the filters - for a very quick check add a delay(1000); into the loop and see if it makes any difference.

If it is the Biquad then you could try either using the AudioFilterStateVariable in series or the sgtl5000 audio board directly (which has tone control plus a five band equaliser built in) - I've never used to but if you look under the documentation in the audio tool, it is comprehensive.

cheers, Paul
 
The low frequency performance issue at #5 is the first things I'd suspect.

You're also reading the pots and updating coefficients every loop which will result in coefficients being reset millions of times a second and could be adding distortion. You'd be better checking if the pots have moved significantly (they will tend to jump around by a few points) before updating the filters - for a very quick check add a delay(1000); into the loop and see if it makes any difference.

If it is the Biquad then you could try either using the AudioFilterStateVariable in series or the sgtl5000 audio board directly (which has tone control plus a five band equaliser built in) - I've never used to but if you look under the documentation in the audio tool, it is comprehensive.

cheers, Paul
Beginner question but I'll try to see if I can pick your brain about it. I think I've redone the code to have basically no distortion, but how do I make it to where the coefficient only updates with a larger change in value? I'm sure I could go research myself but since I got you here I thought I'd ask
 
lots of different way to do it, with your code, something like this would be the most basic:
Code:
float lastF = -1.0f;

void loop() {
  int knob  = analogRead(A5);
  int knob1 = analogRead(A0);
  int knob2 = analogRead(A1);

  float F = (float(knob) * 18.0f) + 20.0f;
  float lowGain  = float(knob1) / 1023.0f;
  float highGain = float(knob2) / 1023.0f;

  // only update filters when frequency meaningfully changes
  if (fabsf(F - lastF) > 40.0f) {
    biquad1.setLowpass(0, F, 0.54);
    biquad1.setLowpass(1, F, 1.3);
    biquad1.setLowpass(2, F, 0.54);
    biquad1.setLowpass(3, F, 1.3);

    biquad2.setHighpass(0, F, 0.54);
    biquad2.setHighpass(1, F, 1.3);
    biquad2.setHighpass(2, F, 0.54);
    biquad2.setHighpass(3, F, 1.3);

    lastF = F;
  }

  // keep some headroom
  mixer1.gain(0, lowGain * 0.5f);
  mixer1.gain(1, highGain * 0.5f);
}

even better use something like https://github.com/dxinteractive/ResponsiveAnalogRead to filter/smooth the pot output ResponsiveAnalogRead may already be part of Teensyduino

cheers, Paul
 
Back
Top