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

Add BlockBiquad shelf & peaking, AudioFilter support #9804

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

jepler
Copy link
Member

@jepler jepler commented Nov 11, 2024

@relic-se @gamblor21 @todbot for interest.

A potential limitation is:

Using the same LFO as an input to multiple other LFOs or Notes is OK, but the result if an LFO is tied to multiple Synthtesizer[sic, someone should fix that doc typo] objects is undefined.

(besides which, LFOs can now be used with AudioFilters, BlockBiquads, and other types of objects; LFOs, Math blocks and BlockBiquads should in the medium term be moved out of synthio into their own modules and the documentation updated to reflect what they can be used with)

AudioFilter is more likely to be used with AudioMixer. But you can't have one LFO drive the filters of two different AudioFilter objects.

I honestly don't know what the fix for this is. The lfo_tick routine needs to run once every SYNTHIO_MAX_DUR samples, but other parts of the audio pipeline produce or consume in different chunks. Using AudioMixer to gather up multiple chunks of synthio data is a common trick, but you still want the LFOs to tick every 256 samples even if AudioMixer is gathering 1024 samples at a time (ssay) In fact, this is making me doubt that a BlockBiquad with varying inputs is even working right in Filter with a single Filter and no mixer since it is using buffer_size of 512 bytes which might or might not be 256 samples.

This needs more thought, so I'm entering this as a draft.

This is just `Biquad | BlockBiquad` but it's useful shorthand.
& test by filtering some noise with a pair of BlockBiquads
The formulae are untested but taken directly from the Cookbook.

The `sqrt(A)` term is given a quick approximation via the famous
Quake reciprocal square root routine.
@jepler
Copy link
Member Author

jepler commented Nov 12, 2024

[two builds overflowed, looks like]

@relic-se
Copy link

relic-se commented Dec 5, 2024

@jepler The updates look great to me so far, and I'm happy with the updates to audiofilters.Filter. Namely, the rewrite of common_hal_audiofilters_filter_set_filter and removal of reset_filter_states.

@jepler
Copy link
Member Author

jepler commented Dec 5, 2024

@relic-se can you comment on this specifically?

this is making me doubt that a BlockBiquad with varying inputs is even working right in Filter with a single Filter and no mixer since it is using buffer_size of 512 bytes which might or might not be 256 samples.

this otherwise gets
```
astroid.exceptions.InferenceError: Inference failed for all members of [<BinOp l.13 at 0x7fd379c99e80>].
```
@relic-se
Copy link

relic-se commented Dec 5, 2024

@jepler Since the synthio.BlockInput system relies on an actively running instance of synthio.Synthesizer to operate, it isn't currently possible to use audiofilters.Filter without also using audiomixer.Mixer with two voices, one reserved for synthio.Synthesizer (even if it isn't doing anything other thank BlockInput calculations).

Here's an example of this in action: https://gist.github.com/relic-se/77e48df1920b20d3e50762cbc06d1bb9#file-audioeffects_microphone-py-L111-L112.

If synthio.BlockInput is separated from synthio.Synthesizer in the future, I think that's something that we can dive deeper into. I suspect that a static counter of some sort on whatever updates synthio.BlockInput would be used to mitigate this problem.

The example referenced relies on this PR, #9776, for the audiofilters.Distortion effect. I haven't marked that PR as ready for review yet because it still needs some additional optimization on per-sample calculations.

@jepler
Copy link
Member Author

jepler commented Dec 5, 2024

Just like using an LFO, math, or other object with a block input with two synthesizer objects is undefined, so is using an LFO with a synthesizer and an audiofilter...:

//|     Using the same LFO as an input to multiple other LFOs or Notes is OK, but            
//|     the result if an LFO is tied to multiple Synthtesizer objects is undefined.          

so sadly I have to say that the code linked above is relying on undefined behavior. I wish I had looked more closely at the use of block inputs in audiofilters because I would have asked that it be reworked to not rely on a side effect of a synthio.Synthesizer in this way.

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 this pull request may close these issues.

2 participants