LPG, and an FM-pair shaper insert #43
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
bcox/tymbal#43
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Scoped 2026-09-05. The wave-wrapper half moved to #64 (more shaper shapes).
What is left here is the LPG, plus the one shaper reach that has no other
route.
An LPG is a lowpass and a VCA driven off one control, behind a vactrol -- fast
attack, slow and level-dependent release. The musical content is that
amplitude and brightness fall together, so quieter is always darker. That is
what natural instruments do and what a filter plus an independently enveloped
VCA does not.
1.
dsp/low_pass_gate.hResonant two-pole lowpass plus a gain, both off one control, behind a vactrol.
va_filter's measured Q=64 floor, with the same injectionidiom at the top of travel: a ring that is fed, not one that is generated.
No negative k, so no limiter and no oversampled stage.
dsp/va_filter.h:25-62already priced true self-oscillation and declined it.
SvfStatewidth, notTiltState.dsp/layer_strip.h:57-70makes theargument: a resonant bandpass swings 81x its input at Q=64.
Round::kTowardZero, which is what makes the ring reach exact zero and solets the voice free.
2. Three placements, one core, differing only by policy
Strip. Control is the vactrol and nothing else, which keeps
dsp/layer_strip.h:266-270's no-modulation rule intact.NoteOngains thenote number alongside the sample rate, for key follow on the base cutoff.
Activity is a scalar "is the vactrol still open", OR'd into
Voice::IsActive-- which today folds over layers only and never consults the strip, so a short
exciter would retire the voice while the resonator is still ringing. Controls:
base cutoff, key follow, resonance, response, mode (VCF/VCA/both), depth.
Every engine inherits it, and this is the FM pair's LPG.
VA. A new
VaFilterTopologyvalue. Cutoff, resonance, matrix routing andthe injection are already in the filter row, and the VCA sits downstream of
the row, so the activity story needs nothing.
Unvoiced. A real stage with matrix-modulatable cutoff, against the
existing
backstop_/flush_tail logic atdsp/unvoiced_layer.h:367-379--the one file in the tree that already holds a voice for a ringing filter and
bounds it. A short noise burst into high resonance is the techno kick, and it
lands in the engine that already solved the hard part.
Not the FM pair, not modal, not partial. Modal already darkens as it decays
because
decay_secondsis per mode; an LPG there imposes a fake decay over atrue one. Partial is additive and expresses it exactly already. The FM pair's
case was the amplitude-brightness coupling, which the strip delivers.
3. An FM-pair shaper insert
dsp/fm_pair_layer.hhas no nonlinearity at all today. Layers do not chain --Voice::Rendersums slots with no slot-to-slot routing -- so an FM tonecannot be routed through the VA layer's shaper, and the strip cannot host one
at 1x. A saturated FM tone is unreachable unless the shaper is in the engine.
ShaperShapeset, not a subset.dsp/shaper.his the single ownerof the curve list; a per-engine allowlist is the second edit site that file
exists to prevent, and it would go stale the moment #64 lands.
the reason.
ratio_becomesmax(Carson, MinimumRatio(shape)), still clamped tokOversample. It must readMinimumRatiorather than hardcode a number, so#64's shapes are inherited automatically.
path onto the decimator path, where
flush_anddecimator_flush_differ(
dsp/fm_pair_layer.h:2185-2223); and reported latency must not move, sinceTapsForRatio(r) = 64r + 1puts group delay at 32 output samples at everyratio and
kLatencySamplesreadskOversample, notratio_.Open
Naming. The strip stage and the engine stage behave differently, and calling
both "LPG" is a patch-author trap. Provisionally Damper for the strip and
LPG for the engine version.
LPG, wave wrappersto LPG, and an FM-pair shaper insertLanded on
mainas of0910519.What shipped
dsp/low_pass_gate.h-- the shared core: resonant two-pole lowpassplus gain off one control, behind a
Vactrol.kGateMinDampingat themeasured Q=64 floor,
SvfStatewidth,Round::kTowardZeroso the ringreaches exact zero and frees the voice.
sources each reaches: the Damper in
dsp/layer_strip.h,VaFilterTopology::kLowPassGatein the VA filter row, and a real LPGstage in
dsp/unvoiced_layer.hagainst the existingbackstop_/flush_tail logic.ShaperShapeset, withratio_ = max(Carson, MinimumRatio(shape))clamped tokOversample.SMTXchunk, editor controls for theDamper, and
patches/lpg_kick.has a factory patch.Both of the "pin rather than assume" items in section 3 are tested rather
than assumed.
TheShaperDoesNotMoveTheAlignmentasserts the unshaped casestands at ratio 1 and the shaped case has crossed onto the decimator path
before it measures realized delay, so it fails loudly if it ever stops
exercising that boundary;
ReportsDecimatorLatencyholdskLatencySamplesat 32 across every ratio.One deviation from this issue's text
Section 2 said the strip's control would be "the vactrol and nothing
else", keeping
dsp/layer_strip.h:266-270's no-modulation rule intact.That was overruled during design: velocity and pressure should not be
hardwired, they should be default modulation routings. So the strip gained
a
ModMatrixwith four destinations (kDamperTarget,kDamperCutoff,kDamperResonance,kDamperDepth) and two default routings, and thatcomment was rewritten rather than preserved. A superset of what was asked
for here, but this issue's body no longer describes what shipped.
Verification
bazel test //...93/93 at fastbuild and--config=opt, goldens unmoved;--config=asan93/93,--config=ubsan93/93,--config=tsanon//plugin:preset_race_test1/1.Follow-up
#73 -- the unvoiced engine's mod matrix has no editor UI, so this issue's
three new LPG destinations are unreachable from the panel. Pre-existing
rather than introduced here: every unvoiced destination has always been
unreachable, and the routes still round-trip correctly through a
.dsppatch.