Repository navigation
audiosampler: a resonant low-pass, a modulation envelope and two LFOs per voice - #337
Merged
Merged
Conversation
… per voice A voice can now play the rest of what a SoundFont region says about a note: a two-pole state-variable low-pass with resonance, a DAHDSR modulation envelope on pitch and cutoff, a vibrato LFO on pitch, and a modulation LFO on pitch, cutoff and volume. cutoff and vibrato_depth are live properties, so a mod wheel or a velocity curve can move them on a sounding note. They are worked out every 32 frames from polynomial approximations, so every target renders the same bits. A voice that uses none of them takes the old path and renders exactly as before, at the same cost. Measured in tests/test_cpython_audiosampler_modulation.py (response at and below the cutoff, the envelope's sweep, the LFOs' rate, depth and delay, the live properties, 64 filtered voices inside a block), with planted faults that fail them; timed by tests/parity/sampler_cost.py on any target.
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.
A sampler voice can now play the rest of what a SoundFont region says about a note:
cutoffin Hz,resonancein dB),mod_*),vibrato_delay,vibrato_rate,vibrato_depth),lfo_*).They're new keywords on
play(), on CPython, MicroPython and CircuitPython.cutoffandvibrato_depthare also live properties, so a mod wheel or a velocity curve can move them on a sounding note.How it renders
A voice that sets none of these takes the old path and renders the same bits;
sampler_probe.py's output matched main's before its new section was added. A voice that does set them renders in 32-frame chunks. Once a chunk it works out its pitch, its filter coefficients and its volume, then runs the chunk through a state-variable low-pass. The coefficients come from a polynomial tangent andaudiodsp_exp2_poly, so CPython and MicroPython print the same probe output.What it costs
tests/parity/sampler_cost.pygives microseconds per 256-frame stereo block at 48 kHz, where the block lasts 5333 µs. filter is a 2 kHz low-pass with 6 dB of resonance. full adds the modulation envelope and both LFOs on everything they reach.On the P4, plain before is the same firmware built from main. At 64 voices the plain path reads 2% slower, which is about the spread between two images of the same code: main's sampler read 2704 µs at 64 voices in a bigger image. The voice struct grows from 112 to 256 bytes on a 64-bit desktop. The numbers are in
docs/how-it-works/upstream-diff.md.Tests
tests/test_cpython_audiosampler_modulation.pyhas ten traits, each with its bar:Three planted faults each failed tests:
The CircuitPython binding is written but hasn't been built here.