Circuit Tracks · dsp-lab preview

Custom DSP engines & FX — control cheat sheet

Six new oscillator engines and six effects, selectable and knob-controllable from the box. These controls were developed on the dsp-lab branch with the same XT8 payload discipline (held overlays, swallowed buttons, LED repaint) and are now merged onto XT8 v0.2.10 (branch xt8-dsp-merge, custom build 4572 — see the MERGED section below). Pad numbers follow the 4×8 grid, 1–32.

← Back to the firmware loader & MIDI scope

Shortcuts

The four control gestures

Hold FX effect on/off page Shift + FX pick the synth engine for the selected part Multi-tap Preset cycle patch tabs (per engine family) Turn a macro knob (while a page is held) adjust the selected effect / engine — soft-pickup

Every page is a transient held overlay, exactly like XT8's Shift + Scales quick-setup: hold to open, the pads repaint, release to return to normal. Nothing here changes stock behaviour when no page is held — plain FX, Preset and the macros work as they always did.

Pages

Effects, engines & patches

Hold FX

Effects — five independent on/off toggles

Tap a pad to switch that effect between custom and stock

Each pad toggles one effect between the custom DSP algorithm and stock (bright = custom running, dim = stock/bypassed). This only picks the algorithm — your per-channel reverb/delay send amounts stay exactly as they are (they're computed upstream by stock code, so the normal track macros still work as per-channel FX level). Default: reverb + delay custom, the rest stock. Ships in image 2 (build 4542).

1Reverb
2Delay
3Chorus
4Lo-fi
5Sidechain
6
7
8

Knobs while held shape the global effect's character — reverb size / decay, delay time / feedback, shimmer depth — not the per-channel amount (that's your normal track send). Turn past the current value to pick up (no jump).

Shift + FX

Synth engine — per synth part

Sets which oscillator engine plays the selected synth part

Pick the tone generator for the selected part. The part's stock patch shell (filter, envelopes, macros, FX) keeps running — only the oscillator swaps, so you can play an existing pack through any engine. Pad 1 returns the part to the stock oscillator.

1Stock
2FM4
3DX 6-op
4Supersaw
5Pluck
6Organ
7Chip
8

Voices with all effects on: Chip 12 · FM4 8 · Supersaw 6 · Organ / Pluck 5 · DX 6-op 4. Fewer effects = more voices.

Multi-tap Preset

Patches — tabbed by engine family

Tap Preset again (within ~½ s) to cycle tabs

The first tap is the normal stock patch grid. Each further tap cycles to the next engine's patch tab, colour-coded per family. On a family tab, pads 1–5 are that engine's patches (default / 10 / 20 / 30 / 40); the lit pad is selected. Tapping the lit pad reverts the part to stock. Convert your own DX7 banks with the recipe-bank tool; patches live on the microSD card.

1Default
2Bell
3Pad
4Bass
5Organ
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25Stock
26DX
27Saw
28Pluck
29Organ
30Chip
31
32

Bottom row (pads 25–30) is the tab bar — the active family tab is bright. Colours match the engine selector: DX amber, supersaw blue, pluck green, organ red, chip magenta.

Macro knobs

Knob control — soft-pickup

The 8 macros adjust the effect / engine on the held page

While an FX or engine page is held, the eight macro knobs adjust that effect's or engine's character parameters (not the per-channel send — that's the normal track macro, unchanged). Soft-pickup: a knob only starts moving once you turn past its current value, so nothing jumps. Bipolar parameters (detune, pan) are centred at the knob's midpoint. When no page is held, the macros do their normal patch job.

M1Reverb size
M2Reverb decay
M3Delay time
M4Delay feedback
M5Shimmer depth
M6Supersaw detune ±
M7
M8

Wired first (more are one table-row each). The master filter pot (CC 74) is also available for a global sweep. Which macro maps to which parameter depends on the held page and the selected effect / engine.

Downloads

Flashable images (dsp-lab)

Stock recovery stays one click away — flash it any time to restore the unit in one step.

Resolved

Why every custom image was rejected — RESOLVED

The diagnostics did their job. Diagnostic A (stock DSP program relocated, build 4543, 347,936 B) booted normally — the relocate → upload → DSP-execute mechanism is sound. Diagnostic B (everything, build 4544, 362,081 B) was rejected twice at update-validate ("CRC failed; installed composition remains viable") and rolled back before the image ever reached flash or the DSP. Two things were wrong, now both understood: (1) every old custom image was exactly 362,081 B because the build parked code caves at a fixed high address, shipping a 4,514-word gap of zeros — the caves are now packed contiguously (no gap); (2) the "CRC failed" itself is not a size-limit branch — a disassembly trace of the updater found no size check; "CRC failed" means only that the staged bytes didn't match the declared CRC (a content/staging issue, most likely the QSPI staging area's erased extent). So the 362,081-B failure is a transfer question, not a proven ceiling — the SIZE-CONTROL images below settle it on hardware. Full analysis: docs/dsp-boot-isolation-RESOLVED.md.

Flash order

New flash order (compact images)

2026-08-18 hardware result: build 4545 flashed and transferred, but showed the red-chessboard DSP fault at cold boot and rolled back safely. Second-opinion diagnosis: the FDN reverb ran unconditionally at cold boot against uninitialized external Y-memory (the flash image carries program words only — no external-memory initializer — and the stock startup zeroes only internal memory), and its first frame drove four external delay-line writes with the raw unmasked write counter. Build 4548 below is the fix.

1 — Reverb, defensive cold-safe (build 4548, 348,986 B — under the proven ceiling). Supersedes 4545. The same FDN reverb behind a cold-boot gate: until the internal gate word (zeroed by the stock startup at every cold boot) carries the init magic, every reverb call plays the bit-identical stock reverb and performs one small bounded chunk of external-memory initialization (33 blocks ≈ 24 ms total; never a single 16K clear), then flips to the FDN with fully self-initialized state. The FDN also masks its write counter before the first delay-line access of every block. Emulator proofs: initialized-path seam digests identical to the qualified FDN (0-mismatch shadows); cold-window output word-identical to an unpatched stock run; an interpreter-level write guard observed every DSP memory write across boot and render with zero out-of-range external writes; convergence to the FDN 0-mismatch after exactly 33 blocks. Honest status: emulator cold-boot-safe-proven and size-gated; a hardware cold boot of custom DSP code is still not proven. If it fails, the self-test rolls back automatically and stock recovery is one flash.

1b — Optional isolation image (build 4547, "FDN present, seam stock"). The exact 4545 payload with the one seam word reverted to stock, so the 201 custom words are present but never executed. If 4548 were also to fail, flashing 4547 separates "custom code executing" from "image length/upload shape" in one boot. Superseded builds: 4545 (red chessboard, diagnosis above).

2 — Everything compact, CEILING PROBE (build 4546, 358,694 B — over the proven-safe gate by design). All five toggleable effects + all six engines, compacted from 362,081 B down to 358,694 B. It deliberately does not pass the flashable gate: it exists to bracket the real firmware ceiling with one flash. Accepted at update-validate → the real ceiling is ≥ 358,694 B and the full everything image ships. Rejected (same "CRC failed" message, before any reboot — the installed firmware keeps running) → we stay with compact subset images under 349,464 B. Same audio-identity proof in the emulator as the reverb build (all nine everything renders byte-identical, every shadow 0-mismatch). A size rejection never touches flash; a post-flash boot failure rolls back.

3 — Everything, defensive COLD-SAFE (build 4551, 359,471 B — TRANSFER-UNVERIFIED, needs the SIZE-CONTROL hardware test first). Supersedes 4546 as the everything image. The full deployment (all five effects + all six engines) with the proven 4548/4549 cold-boot gating ported to every path that touched external memory at cold boot: the always-on reverb (gate X:0x5fff) and delay (gate X:0x5fbf — in this image X:0x5ffe is engine scratch) each default to the bit-identical stock effect until 33 bounded per-block init jobs have self-initialized their external state, and the engine dispatcher now sits behind an internal-only mailbox pre-gate (the committed dispatcher read an external dispatch table before its magic check — a guaranteed cold-boot garbage jump on the first note; fixed). Emulator proofs: every engine and effect shadow bit-exact per block on the dense 12-voice qualification; each cold window word-identical to an unpatched stock run; both gates flip at exactly seam call 33 with 0-mismatch convergence. Honesty: the image passes the P-partition and structural gates but is larger than any transfer the hardware has yet accepted (349,464 B) — the gate withholds it as transfer-unverified, not gate-passing. Flash the SIZE-CONTROL padded-stock image (362,081 B) first: accepted → large transfers are fine and 4551 can be attempted; rejected → a real staging limit exists and 4551 stays shelved. A transfer rejection never touches flash; a post-flash boot failure rolls back automatically.

Complete deployment

MERGED — XT8 v0.2.10 + DSP controls + all engines/effects (build 4572, restored)

RESTORED as build 4572 — the earlier merged builds (4562/4570) stay WITHDRAWN. Those first merged images bundled a stale XT8 v0.2.4 payload (owner caught it — thank you); flashing them would have rolled XT8 back, losing v0.2.5–v0.2.10. The promised source merge has now landed: the four DSP-control commits were cherry-picked onto XT8 v0.2.10 (branch xt8-dsp-merge), rebuilt, and the merged payload passed both the full XT8 release suite (pinned 4572/4573 artifacts, hook-surface, container and web-validator tests) and the coupled DSP control tests (hold-FX toggles write the enable words with OFF bit-identical to stock at fixed-PC digests; Shift+FX/Preset tabs write the engine/identity mailboxes; pots drive DSP words with soft-pickup and bipolar mapping). Build 4572 is XT8 v0.2.10 (generative engine, project isolation, microSD) + the DSP control UI + the everything cold-safe DSP (all five effects, all six engines), one flash. The XT8-integrity gate proves the image differs from the standalone 4572 XT8 build only by the DSP pointer word + the appended DSP program (relocated to 0x08066000, after the real payload end, ending 0x08070c2f inside the reserved region).

Honesty — TRANSFER-UNVERIFIED, hardware boot not claimed. The image is 396,335 B — above the largest transfer the hardware has accepted (349,464 B) and above both SIZE-CONTROL probes (362 KB / 384 KB), so even an accepted 384 KB probe does not fully cover it. Flash the SIZE-CONTROL images first; only if large transfers prove out should 4572 be attempted. Everything above is emulator/structural evidence only: no hardware boot of 4572 is claimed. A transfer rejection never touches flash; a post-flash boot failure rolls back automatically. Recovery is always one flash (stock 4486 below).

SHA-256 2dd65d0c1a3b0acbb6835d9e934e2bd12256064303ca2a15f9a3a41918664bc1 · 545,029 B SysEx container · flash image 396,335 B ending 0x08070c2f · XT8 custom build 4572 (diag pair 4573).

Do first

SIZE-CONTROL — does a big transfer even work?

These are the known-good stock DSP program padded with inert bytes to the exact sizes of the deployment images, with valid CRCs. They isolate the one open question — can the firmware accept a >362 KB update — from everything else. Accepted (boots like stock) → large transfers are fine and the everything image (4551) — and, once a probe of at least 396,335 B is accepted, the merged 4572 deployment — can be flashed. Rejected ("CRC failed", nothing written) → there is a real staging/size limit and we ship the under-350 KB images (4548 reverb, 4549 reverb+delay) instead. Flash the 362 KB one first, then the 384 KB one.

Guaranteed-size

Under the proven transfer size (flash anytime)

These are ≤ 349,464 B — the largest size the hardware has already accepted (every XT8 build) — so they transfer for sure. DSP-only (no UI): they replace stock reverb/delay with the cold-safe custom versions. Best first real test of custom DSP on hardware.

Suggested order: (1) SIZE-CONTROL 362 KB → then 384 KB — does big transfer work? (2) 4548 or 4549 — does cold-safe custom DSP boot? If both are yes → the everything image (4551) and the merged 4572 deployment above can be attempted (4572 is 396,335 B — still beyond both probes, so it remains transfer-unverified until a probe of at least its size is accepted). Flash exactly one custom image at a time; prove stock recovery first; each result is one bit that settles the roadmap. Recovery is always one flash: stock build 4486 below.