SGTL5000 and filter_biquad biquad coefficients are inconsistent/incompatible
grahamwhaley opened this issue · 3 comments
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).
@robsoles - iirc, was the sgtl5000 coeff code from you?