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

SGTL5000 and filter_biquad biquad coefficients are inconsistent/incompatible #372

Open
grahamwhaley opened this issue Dec 28, 2020 · 3 comments

Comments

@grahamwhaley
Copy link
Contributor

Description

The biquad coefficients generated from the (somewhat undocumented) control_sgtl5000.cpp:calcBiquad() function differ from those generated and required by the filter_biquad.cpp functions, and thus are not directly useable.

The difference is a x*-1 where x==[a0|a1] - that is, the sign of the Ax coefficients is inverted between the two.

That fundamental difference I think comes from comparing the SGTL5000 spreadsheet against the Robert Bristow-Johnson / Audio-EQ-cookbook text.

Steps To Reproduce Problem

I am constructing a system where the software AudioFilterBiquad is used to process and view (on a small tft) the effects of the biquad filters, but the actual audio processing is handled in the hardware sgtl5000 PEQ. I noticed that the coefficients generated from the sgtl5000 calcBiquad, when passed the correct quantization unit for the software biquad, did not work. The filtering works if I call the AudioFilterBiquad calculation functions directly (such as setLowpass()). I added some debug code to the AudioFilterBiquad in the form of a getCoefficents function to allow comparison of the two co-efficents.

This is the addition to allow viewing/extraction of the internal private coefficients:

void AudioFilterBiquad::getCoefficients(uint32_t stage, int *coefficients)
{
  if (stage >= 4) return;
  int32_t *dest = definition + (stage << 3);
  __disable_irq();
  *coefficients++ = *dest++;
  *coefficients++ = *dest++;
  *coefficients++ = *dest++;
  *coefficients++ = *dest++;
  *coefficients++ = *dest++;
  __enable_irq();
}

This is the debug code in my Sketch:

  if(1) { //debug!
    int calc_coeffs_sg5k[5];
    int calc_coeffs_sw[5];
    int swcoeffs[5];
    int swcoeffs2[5];
    
    calcBiquad(FILTER_LOPASS, 1000, 0.0, 0.707, 524288, AUDIO_SAMPLE_RATE_EXACT, calc_coeffs_sg5k);
    calcBiquad(FILTER_LOPASS, 1000, 0.0, 0.707, 2147483648, AUDIO_SAMPLE_RATE_EXACT, calc_coeffs_sw);
    mybiquad_vis.setLowpass(0, 1000, 0.707);
    mybiquad_vis.getCoefficients(0, swcoeffs);

    mybiquad_vis.setCoefficients(0, calc_coeffs_sw);
    mybiquad_vis.getCoefficients(0, swcoeffs2);

    for( int i=0; i<5; i++ ) {
      Serial.printf("%d: 5k: %d\n", i, calc_coeffs_sg5k[i]);
      Serial.printf("%d: csw: %d\n", i, calc_coeffs_sw[i]);
      Serial.printf("%d: sw: %d\n", i, swcoeffs[i]);
      Serial.printf("%d: sw2: %d\n---\n", i, swcoeffs2[i]);
    }
  }

And here is the resulting serial log output:

0: 5k: 1207
0: csw: 4943449
0: sw: 4943437
0: sw2: 4943449
---
1: 5k: 2414
1: csw: 9886898
1: sw: 9886875
1: sw2: 9886898
---
2: 5k: 1207
2: csw: 4943449
2: sw: 4943437
2: sw2: 4943449
---
3: 5k: 471616
3: csw: 1931738368
3: sw: 1931738443
3: sw2: -1931738368
---
4: 5k: -214298
4: csw: -877770367
4: sw: -877770369
4: sw2: 877770367

The minor numerical differences are I suspect as the SGTL5000 calculations are done as float, but the filter_biquad calculations are done as double. The real obvious difference being the signed-ness of the 4'th and 5'th coefficients - those being a1 and a2.

And, adding code such as this snippet to my sketch makes the code work as expected - the visual representation via the software biquad matches the audio produced via the sgtl5000 hardware biquads.

    //Calculate coeffs for filter_biquad use case
    calcBiquad(FILTER_LOPASS, 1000, 0.0, 0.707, 2147483648, AUDIO_SAMPLE_RATE_EXACT, coeffs);

    //Correct a1 and a2 coeffs for sign inversion 'bug'
    coeffs[3] *= -1;
    coeffs[4] *= -1;
    biquad_vis.setCoefficients(0, coeffs);

Looking at the code, there is indeed a -1 difference between the two (sgtl5000 and filter_biquad) code sets. The primary item can be seen in the filter_biquad code, where we have:

	*dest++ = *coefficients++;
	*dest++ = *coefficients++;
	*dest++ = *coefficients++;
	*dest++ = *coefficients++ * -1;
	*dest++ = *coefficients++ * -1;

I think the easiest overall clean fix would be to remove those * -1 from those two coefficient saves, and modify the internal coefficient calculations to match. Alternatively, one could:

  • modify the sgtl5000 code to match the filter_biquad code.
  • heavily document the current difference and requirement.

Hardware & Software

Teensy 4.0 with audio shield
Teensyduino v1.53

Arduino Sketch

The full sketch currently relies on having a 128x160tft installed - the snippets above should provide enough information to reproduce - shout if you need me to distill them into a single ino sketch to show the output on the serial port. It is obviously difficult to show the result of the biquads themselves, although not impossible for the software biquad by passing the white noise generator through it and then through the fft and plotting it (which is effectively what I'm doing on my screen).

@grahamwhaley
Copy link
Contributor Author

@robsoles - iirc, was the sgtl5000 coeff code from you?

@robsoles
Copy link
Contributor

robsoles commented Dec 29, 2020 via email

@robsoles
Copy link
Contributor

robsoles commented Dec 29, 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

No branches or pull requests

2 participants