Repository navigation
Conversation
The old fit covered [0, pi/2] only. A filter above sample_rate / 4 used it outside that range, where the error grows to 0.17 by Nyquist, so a low-pass at 22 kHz / 48 kHz was 7 dB down at its own cutoff and a 22 kHz high-pass rejected a 12 kHz tone by only 28 dB. Reflect the angle into [0, pi/2] and then into [0, pi/4] by swapping sine and cosine, and evaluate a four-term minimax polynomial for each. The error is at the last bits of a float over the whole 0..Nyquist range, and a frequency above Nyquist is merely wrong rather than absurd.
The five coefficients were Q15 for every filter, and the feedback state was rounded to whole samples. Low down that loses everything. A low-pass at 100 Hz has b0 = 4.2e-5, which is 1.4 in Q15 and is kept as 1. And near 0 Hz a biquad turns each unit of error in its feedback into thousands of units of output (about 5900 at DC for a 100 Hz low-pass, 23000 for 50 Hz), so rounding the feedback hands that gain half a count to amplify. Give all five coefficients as many fractional bits as the largest of them leaves room for in an int32_t, up to 30, and keep the count in the filter. Keep the feedback state with 12 bits below the sample. A 30-bit coefficient times a 28-bit state does not fit in 32 bits, so the sum of products is an int64_t. The state is still an int32_t. Round to nearest by adding half a step before the shift. The old code added half a step and then rounded towards zero as well, so every negative result came out one count high, and a shelf with a gain of 1.0 at 200 Hz settled about 370 counts off zero in silence.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Steps 1 and 2 of #11269. This sits on top of #11678, so only the last commit is new. The two steps go together because the wider coefficients do nothing audible without the wider state.
Coefficients were Q15 and the feedback state was rounded to whole samples, so a quiet 50 Hz tone through a 100 Hz low-pass came out silent. Now the coefficients get as many fractional bits as the largest one leaves room for (up to 30), and the state keeps 12 bits below the sample. The sum of products is 64-bit. On a Cortex-M0+ that means five
__aeabi_lmulcalls a sample, and raspberry_pi_pico grows by 864 bytes. The output now rounds to nearest, which removes the DC offset GlasierXplor reported.Tested with new low-frequency and DC-offset tests on the unix coverage build, and on a Pico Plus 2 W and a Feather RP2040. With both PRs, a 50 Hz low-pass is within 11 LSB worst and 7.4 RMS of a float64 reference, against 7300 and 3627 before.
Written by an agent, reviewed by a human