Skip to content

Effect polish - #112

Closed
spamatica wants to merge 99 commits into
bkshepherd:mainfrom
spamatica:effect-polish
Closed

spamatica wants to merge 99 commits into
bkshepherd:mainfrom
spamatica:effect-polish

Conversation

@spamatica

Copy link
Copy Markdown
Contributor

Merge back all changes from fix branches

spamatica and others added 30 commits March 10, 2026 21:08
Some refactoring to make it easier to follow the code.
Initial work on integrating a ducking compressor.
It is not yet functioning but does compile

The sources of SimpleCompressor, which is the base of the ducking
compressor, are created by ChunkWare, see more information in the
source files license text.
…le block

BaseEffectModule gets ProcessMonoBlock/ProcessStereoBlock. The default
implementations loop over the existing per-sample ProcessMono/ProcessStereo, so
effects work unchanged until they override them. In-place processing is allowed.

AudioCallback now runs the active effect over the whole block first and then
does the mute/bypass timing and master crossfade per sample. Whether the effect
runs is decided once per block; LED brightness is read once per block.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
ProcessSample holds the per-sample staging logic shared by ProcessMono. When a
caller passes exactly one model block (48 samples) with nothing staged, the model
runs directly on the block and returns the result in the same call, removing the
one block (about 1 ms) of latency and the staging copies. Other sizes fall back to
the per-sample path.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The FIR keeps its own history, so an aligned block of up to 48 samples is
convolved directly into the caller's buffer, skipping the staging copies and the
one block of latency. Oversized or unaligned calls use the per-sample path.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The reverb stages were already block-capable but compiled with a block size
of 1. ReverbController bufferSize is now 4 and the module calls it per block with
the wet/dry mix applied per sample. Measured on hardware: 48 was slower than the
per-sample original (scratch buffers outgrow the D-cache), 4 was the fastest of
48/16/8/4/2. The block size shifts the internal feedback delay by a few samples, so
the sound differs very slightly from the original; level and decay match closely.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Feedback, low-pass frequency and mix gains are computed once per block and the
mix uses float instead of double. Output matches the per-sample path to within
1 ulp of float rounding.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Parameter reads, reverb feedback/low-pass setup, overdrive drive setup and the
drive-volume compensation are computed once per call instead of once per sample.
Expression types are unchanged, so output is bit-identical to the original.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Tone::SetFreq and Svf::SetFreq (cosf/sqrtf and sinf/powf, for both channels)
were recomputed every sample. They, the wow/flutter rates, imperfection
probabilities, head-mode lookups and drive are now computed once per call.
ProcessMono/ProcessStereo and the block overrides share ProcessSamples. Output is
bit-identical to the original; measured CPU 63.8% -> 39.5%.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Mix gain and the dry-fallback check are computed once per block. The engine
itself still runs one sample at a time (feedback network). Output is bit-identical
to the per-sample path. The module is still disabled by default.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Uncomments the two Dattorro sources in the Makefile and the include/instance in
loaded_effects.h, as described in Effect-Modules/Dattorro/README.md.

NOTE: the Dattorro code is GPL-3.0-or-later. A firmware binary built with this
commit must be distributed under GPLv3 (or not distributed). Revert this commit
before sending anything upstream. Costs about 4% of SRAM.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
showCPU is turned on: the default effect screen now shows the audio callback's
average CPU load as a fraction (0.412 = 41%).

disableKnobs (default false) skips reading the knobs and applying them to the
active effect, so parameters stay at their defaults and CPU readings are steady.
Set it to true while measuring; knob noise otherwise triggers parameter updates.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…cessing work

tests/host compiles the real effect modules on a PC against small libDaisy and
CMSIS stubs and checks the block path against the per-sample path (Reverb,
Dattorro, IR, NAM) or against the original implementation in git (SciFi, Tape
Delay, CloudSeed). Run tests/host/run_tests.sh.

HANDOFF_block_processing.md records what was built, the measured CPU results, the
findings, things to know before building, and candidate next steps.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
libDaisy's Led::Init() never initialises the software-PWM phase (pwm_) and Led's
constructor does nothing, so an Led created on the stack starts with leftover stack
contents. Depending on the compiler and optimiser that can be huge or NaN, which
keeps the LED permanently off (or on); the update does `pwm_ += rate; if (pwm_ > 1)
pwm_ -= 1`, so a large value never comes back into range.

InitLeds now zeroes the object before calling Init(). Verified on hardware with
arm-none-eabi-gcc 13.3, where the LEDs did not work before. This is an upstream
libDaisy bug; libDaisy is a submodule so the workaround lives in the project.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
ParameterMetaData gets three trailing fields: unit (text appended to the value, e.g.
" dB" or ":1"), decimals and showSign. They are exposed through
GetParameterUnit/Decimals/ShowSign, copied for effect-chain children and passed to
the menu's float value, so a parameter stored in real units can be shown as such
instead of a bare number. No effect changes behaviour yet.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…artup

The Compressor's parameters are now stored in real units and shown as such: Level -12..+12 dB
(default 0), Ratio 1..20 (default 4:1), Thresh -60..0 dB (default -20), Attack 1..100 ms
(default 10, knob log), Release 10..1000 ms (default 200, knob log).

Init() now applies every parameter. ParameterChanged() only fires on a change and first-boot
storage equals the metadata defaults, so previously the DaisySP compressor ran on its own
built-in settings while the menu showed 0.5 everywhere (which would have meant a 5 s attack
and release). Values are clamped in case old settings (0..1) are loaded.

Gain at default: +3.6 dB on a guitar-like signal, +2.1 dB on sines at -12 dBFS peak (was -3.0 dB).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The Compressor's stored parameters changed meaning (0..1 -> real units). The layout hash only
covers parameter counts and types, so the version is bumped to force a one-time restore of
factory defaults, which also picks up the new Level defaults. Wipes all saved settings and
presets on first boot. The header comment now says to bump it for this kind of change too.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…'s Level range

Crusher (was 1.0), Dual Echo (was 0.4) and Overdrive (was 0.3) now default their Level to 0.5
like the other effects. Overdrive's Level range maximum goes from 0.20 to 0.36 so the default
maps to 0.185 and the effect measures +3.0 dB at -12 dBFS peak (was -1.9 dB on a guitar-like signal).
Crusher and Dual Echo gains at default have not been measured yet.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The normalizeVolume factors are 2.38 (Hard Clip), 1.11 (Soft Clip), 1.10 (Fuzz), 0.87 (Tube),
0.79 (Multi Stage) and 2.38 (Diode Clip), so every type gives +2.9..+3.1 dB RMS at default
settings for a -12 dBFS peak sine (was -1.0..+3.3 dB, with the default Hard Clip at +0.5 dB).
Calibrated by measurement only; not yet checked by ear.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
outputGain is now BE-100 1.70, JCM800 1.08, Ampeg 0.85, Mesa 1.06, 1959BJA 1.50 (was 0.6, 1.1,
1.0, 0.5, 1.0, tuned by ear). Every model measures +3.0 dB (mean of a 110 Hz and a 330 Hz
guitar-like note at -12 dBFS peak, IR cab Off); before, they ranged from -6.1 to +4.4 dB.
Calibrated by measurement only; the gain depends strongly on input level, so check the real
input level against the -12 dBFS reference.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
tests/host/run_level_tests.sh measures each effect's gain at default parameters on the PC
(Distortion, NAM models, Overdrive, Compressor so far) against the 0..+6 dB / +3 dB rule, using
sine and guitar-like signals at three input levels. HANDOFF_level_calibration.md records the
rule, method, results, findings and next steps.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…SRAM)

ProcessMono/ProcessStereo call the shared ProcessSamples routine with n = 1, which made
GCC (-Ofast, interprocedural constant propagation) emit a second, specialised copy
(`[clone .constprop.0]`) of each: 4.8 KB for SciFi and 1.2 KB for Tape Delay. Only the
rarely used per-sample path (effect chains, fallback) called the clones; the block path
that the audio callback uses runs the generic routine.

Marking both routines noclone removes the clones: SRAM 456,960 -> 450,944 bytes (92.97% ->
91.74%). Output is unchanged (still bit-identical to the original in the host tests).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds the measured memory map (SRAM is almost all code; SDRAM, RAM_D2 and DTCM contents), why creating
effects dynamically would not relieve SRAM, the GCC constprop-clone finding and the noclone fix, and
ideas for reducing SRAM further. Updates the SRAM figure to 91.7%.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ault levels to a -37 dBFS reference

Display: I/O peak readout in dB on the default effect screen (integer formatting, no float printf on target).
Levels: Distortion and Overdrive about +6 dB, NAM about +9 dB (Level default 0.3), Compressor +3 dB (displayed Level offset by 7.5 dB).
Level tests now use -49/-37/-25/-12 dBFS inputs with -37 as the reference. Settings format version 14.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… on the default screen

Global Settings: runtime-only "Lock Knobs" checkbox (disableKnobs is no longer a compile-time constant; not saved).
Default effect screen: CPU readout as a whole percentage in the small font, the "..." placeholder removed, thin line above the readouts.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Show Debug (Global Settings): draws the CPU percentage and the averaged input/output peak level (dB) on the default effect screen. The effect gets
the flag from the main loop (SetShowDebug) instead of compile-time constants. The old text-page display takeover that useDebugDisplay used to trigger
is now behind the compile-time constant useDebugTextScreen (default off).

Both Show Debug and Lock Knobs are now fields of Settings (globalShowDebug, globalLockKnobs, default off) edited directly by their menu checkboxes, so
they persist across reboots; the useDebugDisplay and disableKnobs globals are removed. SETTINGS_FILE_FORMAT_VERSION 14 -> 15 (the Settings layout
changed; the first boot restores factory defaults).

Lock Knobs no longer stops the knobs being sampled, only being applied (and clears their pending-change flags). A saved lock can be active from boot,
and the knob cache is only initialised by sampling, so unlocking would otherwise apply uninitialised values to the effect's parameters.

Hand-off notes updated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Init() called UpdateMix() before ResetInternalState() had set m_smoothedMix, and the member was not initialised in the
constructor, so the wet/dry balance came from an uninitialised value: dry-only with zero memory, silence with NaN bits. ParameterChanged never fires for defaults, so
it stayed that way until Mix was touched. The balance was also only recomputed on a Mix change, from the still-trailing smoothed value, so a one-step change ended up at
the previous mix.

The smoothed values are now initialised in the constructor, Init() resets them from the parameters before deriving the balance, and UpdateMix() runs every sample after
the smoothing. The ParameterChanged override only handled Mix, so it is removed.

Adds tests/host/test_tape_delay_defaults.cpp (startup state independent of memory contents, startup equals touched-to-default, a one-step Mix change reaches its target;
all fail on the old code). The equivalence step in run_tests.sh mirrors the same fix into its copy of the original so it keeps checking only that the refactor changed nothing.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
These effects only configured parts of their state from ParameterChanged(), which never fires for defaults, so they started with their built-in settings while the menu showed
the defaults.

Spectral Delay: the per-bin delay and feedback tables (Time, Feedback and both modes) started at 100 samples / no feedback. Drum: Timbre, Tone, Decay, MIDI Voice and Beats were not applied, so
the voices kept DaisySP's settings. PluckEcho: String Decay was not applied. Init() now applies them once the effect's state exists. Found by the startup-state audit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
spamatica and others added 29 commits October 18, 2026 10:49
update_models.sh asks (when run by hand in a terminal; --calibrate and
--no-calibrate skip the question) whether to calibrate: it measures every
model on the host (tests/host/calibrate_nam.sh, calibrate_nam.cpp: the
guitar-like test signal at the reference input level, default settings,
IR cab Off), sets kOutputGain in each model file so all models give the
same level (default +12 dB), and measures again to check. The Makefile
and the test scripts run it with --no-calibrate. The NAM level test now
expects +12 dB. nam_to_cpp_array.py: fix a line that lost its closing
quote (syntax error) and write kOutputGain = 1.0f into new model files.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Output gains from update_models.sh --calibrate (guitar-like test signal at
the reference input level, default settings, IR cab Off): Ampeg 1.6560,
BE-100 0.4756, JCM800 0.4929, 1959BJA 0.6122, Mesa 0.3682. The level
test measures +12.00 dB for all five.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
CHANGELOG.md: the Reverb Delay memory fix, block path and coexistence
with Delay, and the NAM level calibration (the models are about 3 dB
louder). PERFORMANCE_AND_MEMORY.md section 9: the measured CPU figures
and what they mean for chains.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The Delay Time parameter covered 50-4000 ms with a default of 1828 ms.
It now covers 50-1500 ms (the delay buffers are unchanged) and defaults
to 400 ms. Saved settings store the knob position, so a saved value now
plays a shorter time.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
GetBrightnessForLED advanced the LED oscillator, but the main loop calls it
only twice per 48-sample block, so the LED ran about 24 times too slowly;
the rate was also only set after the Delay Time knob had been moved. The
LED phase is now advanced with the audio (per sample or per block, as in
the Delay and Tape Delay modules) and the rate is set at start-up. The
blink period is two delay times, as in the Delay module. New host test
(period at 100, 400, 1000 and 1500 ms, block and per-sample path).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
SECOND_FOOTSWITCH_AND_LED.md: how the second footswitch and the left LED
are handled, a table per kind of use for every effect, and a list of
inconsistencies to look at later (a level LED that is never updated in
eleven modules, CloudSeed's call-counting flash, taps that are ignored).
Written from the source; not checked on hardware.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The Time parameter covers 100-2000 ms (it was 4000 ms; the menu step is
still 5 ms) and a time saved with an older version is limited to 2 s.
Tap tempo limits the interval to 2000 ms and a tap that is not followed
by another within 2 s ends the series (it was 4 s). The delay lines are
unchanged. The host test now checks delay times up to 2000 ms.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The second footswitch's tap tempo needed two taps and used the interval
between them. It now needs three taps and takes the tempo from the
interval between the last two (the first interval is usually off); every
further tap of the series adjusts it again, and a tap more than 2 s after
the previous one starts a new series. The series logic is in
Util/tap_tempo.h with a host test; guitar_pedal.cpp uses it. The user
manual and the footswitch review say three taps.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The left LED dimmed and brightened with the LFO, scaled by the depth, so
it barely moved at low depth and never showed the tempo. It now blinks
once per LFO cycle (once per beat after a tempo tap), locked to the LFO
and on for about 27% of the cycle like the delay effects, whatever the
depth. New host test (60 to 180 BPM, several depths, mono and stereo).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The menu writes its cached value of every parameter back into the effect
on each main-loop pass. A parameter changed in the meantime by the effect
(a footswitch handled in the audio callback, MIDI, a tempo tap) could be
overwritten with the menu's older value, so the Crusher's Pitch Tracking
and the Uranus Hold toggles worked only sometimes. The menu now remembers
the raw value it last agreed on for each parameter and, if the effect's
value differs, takes the effect's instead of overwriting it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… fix

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…, Exciter, Bass Dist, Octa Fuzz, Tape Sat

Lighter alternatives to the Distortion and to a NAM model (4-5% CPU each on
the pedal). They share a base class (MonoEffectModule) and the DSP pieces in
Util/dist_dsp.h (filters, and soft and hard clippers with first-order
antiderivative anti-aliasing instead of oversampling). Each effect's output
level is matched at the reference input level (+6 dB; +3 dB for the Exciter
and Tape Sat). Host tests: the block path equals the per-sample path, bounded
output for random parameters, silence in gives silence out, behaviour checks
and the default levels. New preset configs/distortions.config, entries in
effects.json and effects-memory.json, manual and changelog entries, and
HANDOVER_DISTORTION_FAMILY.md. The effect list grows by eight, so the MIDI
Program Change numbers shift and saved settings reset once more.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ed Tremolo and Harmonic Tremolo

Each has ProcessMonoBlock/ProcessStereoBlock with the same arithmetic as its
per-sample path (bit-identical output, new host tests), reading the
parameters and setting the filters once per block. Distortion also picks the
clipping type and oversampling mode once per block, and its pre-filter cutoff
(which follows the input level) is now updated every 8 samples instead of
every sample, in both paths: the one change that is not bit-exact against
the old code (the default level is unchanged). Measured on the pedal:
Distortion Hard Clip 18% to 12% CPU, Overdrive about 4%. On the PC: Crusher
212 to 56 ns per sample, Chopper 65 to 41, Modulated Tremolo 95 to 64,
Harmonic Tremolo 110 to 91.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The wet-only LED flash counted calls of the LED function (10,000 per half
period), which is called once per audio block, so it stayed on for about 10
seconds and off for 10 seconds. A phase advanced with the audio now gives a
sine-curve pulse with a period of half a second, starting bright when
wet-only is switched on. New host test (period, range and curve, block and
per-sample paths).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…e older effects

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… Preamp Lead level

The top of the Drive knob of Preamp Clean, Screamer, Wavefolder and Bass Dist
did not reach full saturation (35 dB of gain compression) before full scale.
distdsp::topBoost multiplies the drive gain from the middle of the knob up to
its end (x39, x5, x13, x10), so the maximum gets there at about -12 dBFS peak.
At full Drive the Bass Dist now clips some of a 50 Hz tone; its clean-lows
test runs at Drive 0.9.

New defaults: Wavefolder Fold 30x, Screamer Drive 6.6, both recalibrated to
+6 dB at the reference level.

Preamp Lead was level-matched to Crunch at -37 dBFS, where Lead is already
saturated and Crunch nearly clean, so on a guitar Lead was about 10 dB
quieter. It is now matched to Crunch at -12 dBFS, where both saturate; the
level test checks that and explains why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Defaults are 8 bits, 8000 Hz and a 6000 Hz cutoff (were 32 bits, 48 kHz and
20 kHz, which left the sound unchanged). Level is now -24 to +6 dB around a
level-matched default (was a linear 0 to 1, default -6 dB), calibrated to
+6 dB like the other distortions, with a new default-level test.

The level-test rule in tests/host/README.md said distortion aims for +3 dB;
every distortion test uses +6 dB, so the text now says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pre-filter, the post-filter and the oversampling low-pass were
file-level globals, shared by every DistortionModule: two Distortions (in
an effect chain, or a Dist Bench stage next to the Distortion on its own)
stepped and reconfigured the same filters. They are members now. A single
Distortion's output is bit-identical to before (24 configurations: six
types, oversampling on and off, per-sample and block path).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…turated

Dist Bench: up to four distortion stages in series, each Off or one of
Preamp, Screamer, Muff Fuzz, Wavefolder, Exciter, Bass Dist, Octa Fuzz,
Tape Sat, Chainsaw, Overdrive and Distortion (oversampling off). Every
type gets the same Drive, Tone, Char and Level controls, where 50% is the
effect's own default, so a stage at 50% is bit-identical to the effect on
its own. Each active stage is trimmed by -6 dB and the bench adds +6 dB.
Every stage holds an instance of every type; a type change crossfades over
0.1 s. Player's guide in DIST_BENCH.md.

Chainsaw: an HM-2-style heavy metal distortion: soft-clip pre-gain, an
asymmetric op-amp clip, diodes to ground, a dead band (back-to-back
diodes), then resonant Low (87 Hz) and High (960 Hz and 1.28 kHz) peaks
after the clipping. New distdsp::DeadZoneAdaa.

Muff Fuzz and Chainsaw are saturated from about -45 dBFS, so matched at
the -37 dBFS reference level they were 11 to 14 dB quieter than Crunch
played hard. Like the Preamp's Lead they are now matched to Crunch at
-12 dBFS; the level test shares one check for the three.

tools/effects.py lists each source once (the bench uses the sources of the
effects it runs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deselects every effect, removes the chains and forbids the GPL effects
again, to start a selection from scratch. It asks first and writes nothing
until the selection is saved; an empty selection cannot be saved (the
firmware's effect list would be empty).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every chain of two or more slots gets a "Switches" parameter after its On
switches: Normal, or 1+2, 1+3, 2+3 (a chain of two only offers Normal and
1+2). Set to a pair, the right footswitch switches the lower slot and the
left one the other, each LED shows its slot, and the chain itself stays
on. Tap tempo and the left footswitch's own functions (the shift bank,
footswitchTogglesSlots) are given up; the hold gestures (tuner, effect
cycling, saving) still work and undo the slot their press switched. It is
saved with the chain's presets.

BaseEffectModule gets GetFootswitchSlots and a slot interface (count, on,
switch, fade level) that EffectChain implements on its On parameters; the
slot choice is in Util/chain_switches.h with a host test. The chain's
parameter ids after Switches shift by one, and the settings format goes to
20 (an intermediate version had this as a global setting).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Switching the Delay on brought back the repeats of what was played the
last time it was on: in a chain a switched-off slot is not processed, so
its delay lines stayed frozen (on its own the same happened without
Trails). SetEnabled(true) now clears the lines, except while the tail is
still ringing (Trails). A whole clear of the four 8 s lines would stop the
audio for tens of milliseconds, so they are zeroed a little per sample
(about 0.12 s) from the stale end towards the new writes, with the delay's
output and feedback muted meanwhile; what is played during the clearing is
kept. New host test, which fails on the old code.

Selecting another effect now switches the old one off (not when a preset of
the same effect is loaded), so coming back to a delay also starts from
silence (Tape Delay and Dual Echo too).

Delay Time is 50-2000 ms (was 50-4000 ms), default 400 ms (was 2025 ms);
Delay Mix defaults to 35% (was 50%).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Booster: Clean, Treble, Mid and Warm types.

Spring Reverb: physical-modelling tank of 2 to 6 springs (4 by default)
with dispersive allpass chains running at a third of the sample rate,
additive Mix, Dwell, Decay, Tone, Drip, Tank, Output and Springs.
About 30% cheaper than the previous 2-spring version at the default.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
All documents except README.md and CHANGELOG.md (and the per-effect READMEs
beside their code), with effects_config.png. Relative links and the
references in comments and tools updated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The layers are accumulated in tiles of four frames of the 48-sample block
(after the A2-Lite kernel of squatch-dsp), so each weight loaded serves four
frames. Same output (bit for bit without fast-math), same latency, 552 B more
history; NAM CPU load 69% -> 65% on the pedal. NAM_A2_TILE4=0 selects the
previous kernels.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Up to four phasers in one effect, each with its own Rate, Depth, Feedback,
Stages, Center, Wave and Phase, in series or in parallel, mono or stereo.
Each phaser makes up its level (default +2 dB on a guitar-like signal) and
corrects for its feedback; switching a phaser, the routing, Stereo or the
number of stages fades without clicks.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Moved all effect documentation into docs/EFFECTS.md
@spamatica spamatica closed this Oct 24, 2026
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.

1 participant