Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

calcBiquad returns nonsense for FILTER_HISHELF case #343

Closed
jimlenz2000 opened this issue May 15, 2020 · 6 comments · Fixed by #373
Closed

calcBiquad returns nonsense for FILTER_HISHELF case #343

jimlenz2000 opened this issue May 15, 2020 · 6 comments · Fixed by #373

Comments

@jimlenz2000
Copy link

Description

in control_sgtl5000.cpp calcBiquad subroutine, the FILTER_HISHELF case needs a "break" statement to prevent falling through to the default code.

Steps To Reproduce Problem

the sketch below returns the same nonsense coefficients regards of HiShelfGain value.

Hardware & Software

Teensy 3.2 and Teensy Audio Library
Audio Workshop Kit
Arduino IDE version: 1.8.10
Teensyduino version: 1.48
Operating system & version: Windows 10

Arduino Sketch

#include <Audio.h>
int coef[5];
void setup() {
  Serial.begin(9600);
  while (!Serial) {} // wait for serial port to connect.
  float HiShelfGain = -6.02;
  calcBiquad(FILTER_HISHELF, 440., HiShelfGain, 0.7071, 524288, 44100, coef);
  for (int i = 0; i < 5; i++) Serial.println(float(coef[i])/524288, 10);
}
void loop() {}

Errors or Incorrect Output

0.25
0.00
0.00
0.00
0.00

@grahamwhaley
Copy link
Contributor

Indeed, that looks broken to me (I happen to be staring at that code, thought I'd confirm it). For ref, snippet from the code:

  case FILTER_LOSHELF:
    b0 = A * ((A+1.0F) - ((A-1.0F)*cosw) + (beta*sinw));
    b1 = 2.0F * A * ((A-1.0F) - ((A+1.0F)*cosw));
    b2 = A * ((A+1.0F) - ((A-1.0F)*cosw) - (beta*sinw));
    a0 = (A+1.0F) + ((A-1.0F)*cosw) + (beta*sinw);
    a1 = 2.0F * ((A-1.0F) + ((A+1.0F)*cosw));
    a2 = -((A+1.0F) + ((A-1.0F)*cosw) - (beta*sinw));
  break;
  case FILTER_HISHELF:
    b0 = A * ((A+1.0F) + ((A-1.0F)*cosw) + (beta*sinw));
    b1 = -2.0F * A * ((A-1.0F) + ((A+1.0F)*cosw));
    b2 = A * ((A+1.0F) + ((A-1.0F)*cosw) - (beta*sinw));
    a0 = (A+1.0F) - ((A-1.0F)*cosw) + (beta*sinw);
    a1 = -2.0F * ((A-1.0F) - ((A+1.0F)*cosw));
    a2 = -((A+1.0F) - ((A-1.0F)*cosw) - (beta*sinw));
  default:
    b0 = 0.5;
    b1 = 0.0;
    b2 = 0.0;
    a0 = 1.0;
    a1 = 0.0;
    a2 = 0.0;

@robsoles
Copy link
Contributor

robsoles commented Dec 29, 2020 via email

@grahamwhaley
Copy link
Contributor

Hi @robsoles - thanks for responding. The bug isn't a problem with the calculations themselves, just with a missing break; at the end of the hishelf switch case, which means it falls through and wipes out all the calculated coeffs with the default (benign passthrough) ones... I knocked up a simple example ino to show it (as generally requested in the Issue template):

#include <Audio.h>

void setup() {
  int coeffs[5];

  delay(1000);
  Serial.begin(115200);
  Serial.println("Starting");
  calcBiquad(FILTER_HISHELF, 1000, 0.0, 0.707, 524288, AUDIO_SAMPLE_RATE_EXACT, coeffs);

  for( int i=0; i<5; i++) Serial.printf("%d: %d\n", i, coeffs[i]);
}

void loop() {
  delay(1000);
}

On the library as it stands, you get:

Starting
0: 131072
1: 0
2: 0
3: 0
4: 0

Which looks like the default: case values of a 'benign passthrough' function. With the simple patch of:

diff --git a/control_sgtl5000.cpp b/control_sgtl5000.cpp
index fe2c374..3f52b86 100644
--- a/control_sgtl5000.cpp
+++ b/control_sgtl5000.cpp
@@ -1000,6 +1000,7 @@ void calcBiquad(uint8_t filtertype, float fC, float dB_Gain, float Q, uint32_t q
     a0 = (A+1.0F) - ((A-1.0F)*cosw) + (beta*sinw);
     a1 = -2.0F * ((A-1.0F) - ((A+1.0F)*cosw));
     a2 = -((A+1.0F) - ((A-1.0F)*cosw) - (beta*sinw));
+  break;
   default:
     b0 = 0.5;
     b1 = 0.0;

you get:

Starting
0: 262144
1: -471615
2: 214299
3: 471616
4: -214298

which is looking much more like what you'd probably expect.

I'll send the trivial PR in a moment.

grahamwhaley added a commit to grahamwhaley/TeensyAudio that referenced this issue Dec 29, 2020
There was a missing `break;` at the end of the FILTER_HISHELF
case in the calculations, meaning a hishelf coeff calc always
returned the default fallthrough case, which is a benign passthrough
filter.

Fix by adding the `break;`.

Fixes: PaulStoffregen#343

Signed-off-by: Graham Whaley <graham.whaley@gmail.com>
@robsoles
Copy link
Contributor

robsoles commented Dec 29, 2020 via email

@grahamwhaley
Copy link
Contributor

Well, that's embarrassing

Heh, happens to us all!

Cheers for clearing that up

np

did you include coeff flips for those two coefficients? It may as well be done in the final assignment of each in the calc_biquad routine itself imho

I didn't. I normally keep Issues and PRs small and related to one item (bug, issue) at a time, so if I pushed a PR it would be just for the -1 coefficient fix, and tied to Issue #372 . I was not intending to do a PR for that, given I don't really understand the math or exactly how biquad filters work. Sure, I could do it rote, but I've no simple way of checking I got it right to hand (but, one could probably be constructed with some tone generator components and some tone detect or fft blocks maybe).

We probably want some input from @PaulStoffregen on the best place to make the fix. Personally, factoring out the coeff calculations to a single piece of code might be nice, rather than having what look to be near duplicates for all the calculations in both the sg5k code and the biquad_filter code :-)

Anyway, does that "unsigned short AudioControlSGTL5000::autoVolumeControl(uint8_t maxGain, uint8_t lbiResponse, uint8_t hardLimit, float threshold, float attack, float decay)" routine of mine do anything rational? I was just casting my eye over my old repository copy [forgive me, I should look at the live copy to see if anyone has had to fix it or anything] but it looks at least a bit like I am inviting the compiler to do floating point calculations in an integer container

Heh, no idea!. Well, I do call it to set the AGC up in some other code (not yet up on github, but one day). I took the argument values from the one and only place I could find relevant numbers, from the example code. I did try to do a bit of research to find now to map what one might consider a 'normal' AGC for speech (low bandwidth speech - 3.5KHz amateur radio for instance) to the SGTL hardware settings, but failed to find much. So far those settings seem to do something and are not hurting my ears...

There is the open Issue #164 that looks vaguely related to that area btw.

@robsoles
Copy link
Contributor

robsoles commented Dec 30, 2020 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants