Open items only. Finished work is recorded in the plugin docs and MISTAKES.md.
- check builds for warning messages. Any that can be resolved in the local codebase should be
(the
-Wall -Wextrasweep below is the current state of this).
- Promote the shared
OnePoleLowpass/DCBlocker/BiquadFilterduplicates intoinclude/downspout/dsp/. Worth doing but a separate change, and CLAUDE.md requires approval before touching shared headers. HANDLE WITH CARE. - Replace the 36 copy-pasted
-UNDEBUG+ include-dir blocks with one root helper, e.g.downspout_add_core_test(<target> SOURCES ... LIBS ...), so a new plugin cannot forget either. Low urgency, sinceinclude/downspout/test_assert.halready makes a lost-UNDEBUGa compile error. Touches root glue and ~36 plugin CMake files, so it needs approval and a full test run.
scripts/check-plugin-state.sh reports 42 correct / 0 broken / 22 no state (see the note below: "no state" is not data loss). The six
plugins that silently wrote their defaults into every project (chipper, damiano, ghost,
helterskelter, skream, spliff) now have WANT_FULL_STATE and a getState().
- Verify those six in a host. The failure was silent, so a green build proves nothing and the core tests never touch the wrapper. Change a setting, save, reopen, confirm it stuck.
- lifeform, luma and paunchlad now have session state (2026-10-06), following
Plank's pattern: one versioned, symbol-keyed text state, bad input rejected without
touching the patch, triggers never stored. Lifeform and luma also save the live
64-cell pattern as a bitmap (restoring lifeform's
seeddeliberately does not reseed over it); paunchlad's pad cells are momentary so only its nine settings are stored. Each got round-trip, rejection, trigger and host-activation tests. Verify in a host, as with the six above. - gremlin, gremlin-driver and flues-synth-driver keep the patch across host activation and sample-rate change (2026-10-09); verify in a host when they get state.
- Confirm in a host that the 22 "parameters only" plugins survive save/reopen (arpgen,
basilico, canticle, conductor, drift, drumkit, floozy, flues-synth-driver, gremlin-driver,
gremlin, guardian, harmonic-atlas, m-mix, mixgen, moka, oracle, orbit, polymeter,
resonance-garden, sprout, syrinx). DPF's VST3
getStatesaves every non-output, non-trigger parameter by symbol (third_party/DPF/distrho/src/DistrhoPluginVST3.cpp:1169), so they need noWANT_STATE;scripts/check-plugin-state.shnow reports them as "parameters only" rather than data loss (2026-10-09). Real state is only needed for data outside parameters (patterns, sample paths, text), as in lifeform, luma, paunchlad, plank. -
gaterstate macros removed (2026-10-09): it has no parameters and only momentary MIDI state. - Re-run the
-Wall -Wextrasweep after any state change: the wrapper callbacks are what the core tests never touch.
Standalone g++ -std=c++20 -Wall -Wextra -fsyntax-only over plugins/*/src/*.cpp (add
-Iplugins/generative-common/include or conductor, drift, ghost, guardian, harmonic-atlas
and others will not compile). No -Wswitch warnings remain. What was left on purpose:
cadenceanddrumgenmemsetcalls arevoid*-cast, so they no longer warn. (Re-swept 2026-10-10: zero warnings acrossplugins/*/src/*.cpp.)cadence_clear_progressionnow resets slots toChordSlot{}(velocity 96) instead of zeroing; cadence tests pass.campioneloader: thesmplloop end is inclusive in the WAV spec, but the engine wraps atposition >= loopEnd, so the last loop sample is skipped (one frame). Changing it means adjusting load, save and the engine together; decide whether it is worth it. (The ignoredparseSmplChunkresult is now explicit: a short chunk is the same as no chunk. Only the first loop record is read, which is documented in the code.)
- Host validation in REAPER, especially the resonator engines: CPU load, audio out, and the patch surviving playback start and a sample-rate change (the fixes are covered by offline tests only, and the original "no audio" symptom was never reproduced offline).
- Plinky's other scales (Hirajoshi, Insen, Iwato, Minyo, major and minor triads, fifths,
Romanian) are not in Plank. Adding them means appending to
docs/scales.md. The microtonal ones (Harmonics, Hexany, Just) also need a cents-based table, which the current integer ladder cannot express. - The root
README.md,docs/plan.mdanddocs/summary.mdstill call Plank an "eight-string synthesizer". Shared docs; wording agreed, awaiting approval to change.
-
skreamMorph LP-HP (bipolar, default off) is in and tested. Not yet listened to in a host; try it with Drift on Cutoff. Decisions worth a listen: the morph weight follows cutoff position linearly (t = 0.5 is a notch at the cutoff), and it only reshapes the forward filter, not the Scream feedback high-pass. -
Damiano Linked/Split stereo is in and tested (per-channel Mode, Drive, Tone, Folds; CC Drive and CC Shape per channel on a shared CC channel; see
plugins/damiano/docs/porting-notes.md). Not yet auditioned in a host: try Split with Soft left and Fuzz right, and two Drift lanes on CC Drive / CC Drive R. Verify an old project (pre-split state) loads Linked. Open: "shape" is the stepped waveshaper mode; a continuous shape (asymmetry/bias) would be new DSP. Tone and Folds are not CC-controlled. -
voxmod: host validation in REAPER still pending. -
keyframe: the leash diagram is static at unity rates because drift is genuinely ~0 there; a splice halo was added so it reads live during crossfades. If it still reads dead at defaults, the diagram needs a rethink, not another lamp.
Fully wired (root CMake option, install/release scripts, workflow, docs, product page, screenshot script entry and asset). 4 core suites pass, threading suite clean under TSan. Remaining:
- Verify in a host (REAPER): load, play notes, filter an audio track, save/reopen state. Nothing has been listened to. Also confirm the host accepts an instrument with audio inputs.
- Listen to drum levels (normalised per hit, consistent at ~0.15); check voice-stealing clicks.
- GM Program mode now queues a warm-up per Program Change (last 8 voices); host-check that switching programs on several channels no longer glitches on first notes. Offline-tested only.
scripts/capture-plugin-screenshots.shcaptures the real desktop ifDISPLAYis set (seeMISTAKES.md). Make it always use Xvfb. Shared script, needs approval.- Catalogue screenshot shows Synth mode (filter dimmed); consider a Synth + Filter capture.
- Timbre Index (separate fixed filter chain, default 1 = off) is in and tested; not yet
listened to. Open: it is limited to one index <= 8192 (degree <= 13), so very dark voices still
lean on Rolloff; a second chain or a larger limit would need
kMaxSectionsraised and a warm-up that touches only the indices in use (seeplugins/pratt/docs/high-base.md). Filter mode keeps A x B capped at 8192.
- melgen Inertia is in and tested; screenshot recaptured. Not yet listened to in a host. The Channel selector showed "2" for the default of 1; fixed (selector values are now offset by their base).
- counterpointer Inertia is in and tested (scoring term; Counterpoint mode only, not Bass Descend). Its Routing-column layout was fixed (selectors now share the height so Freeze is no longer covered by the buttons, and the header band ends under the header).
- Retune plugin (
plugins/retune/): wired into the root build, install/release scripts, workflow, docs and Pages; core suite passes under ASan/UBSan. Remaining: host validation, a catalogue screenshot with a real.sclloaded (the standalone shows 12-TET),.kbmsupport, saving scale contents with the session (only the path is saved), and an audition against a per-channel-bend synth. - Per-channel pitch bend in existing instruments (needed for Retune): Retune sends
one note per channel (2-16) with its own bend. Each instrument must apply pitch bend
per MIDI channel at a configurable range (default 2 semitones) instead of globally,
and must not treat different channels as separate parts. Audit and fix, one at a time,
with a test that two simultaneous notes on different channels bend independently.
Done:
canticle,moka,floozyandsyrinx(per-channel bend, RPN 0 range, voices keyed by channel and note where they were keyed by note, tested). They default to a 2-semitone range, matching Retune. Not applicable (checked 2026-10-09):plank(input is Launchpad grid presses, not pitched notes) andmagneto(a note sets engine RPM, no pitched voice).basilicodone 2026-10-09 (mono: the sounding note's channel bend applies, RPN 0 range, tested).gremlindone 2026-10-09 (single source: the last-played channel's bend, RPN 0).campionedone 2026-10-09 (per-voice, uses its Bend Range control, no RPN; seeplugins/campione/docs/pitch-bend.md).mosaicdone 2026-10-09 (per-channel bend with a Pitch bend range control; only audible at Pitch range 0).drumkitis not applicable (notes select unpitched drum pieces). Not yet auditioned with Retune in a host.prattalready bends per channel and has a Bend Range control (confirm its default suits Retune's 2 st). Also add a Bend Range control where a synth has a fixed range, and note the result in each plugin's README. - Sprout (
plugins/sprout/, L-system generator): built, wired, documented, screenshot taken. Remaining: host validation, a rule editor (grammars are built in), and listening to which grammars sound good at which Step size. - Sprout MIDI input (held-notes pitch source, Drift CC 1-4, Conductor CC 21-24) is in and tested under ASan/UBSan. Not yet auditioned in a host: try harmonic-atlas into Sprout with Pitch source = Held notes, and Drift or Conductor into its CC channels. Pitch source has a Latched mode (the chord survives note-off; the next fresh press replaces it). Open: MIDI is applied at block start, not sample-accurately; no pass-through of incoming MIDI.
- drumgen has a Generation every (1-8 loops,
ca_every) control for its automaton; tested, not yet listened to. - CA lanes in
drumgenandxoxoloare in and tested. Not yet auditioned in a host: try Rule 90 and 30 on Hats in drumgen, and marking the hat and percussion lanes in xoxolo. The automaton arithmetic is now shared (include/downspout/cellular_automaton.hpp, tested intests/cellular_automaton_tests.cpp) and used bypolymeter,drumgenandxoxolo. Open: xoxolo's grid shows the programmed pattern, not the evolved row now playing. - Magneto look and feel: the kit is now shared,
include/downspout/dpf/MagnetoKit.hpp, used by Retune, Sprout, Markov and Damiano (new hooks: overridablesendValue()for plugins that carry values as state,setValue()andsetDim()). Older generator plugins still useGenerativePanelUI(mosaic, polymeter, etc.); restyle if wanted. Retune, Sprout and Markov were rebuilt against the shared header but not re-screenshotted; confirm they look unchanged. - Gravity is in
harmonic-atlas.cadence(learned harmony) has not had it; its progression model is different, so decide whether a gravity bias on the learned transitions makes sense. - Atlas voice-leading is local to
harmonic-atlas. Listen to it; the chords sit low (around C3-C4) because only the bottom and top of the range are penalised, so consider a centre pull. - Markov (
plugins/markov/, the Markov melody generator) is in and tested (core suite also under ASan/UBSan: scales against the docs, walk statistics, order 2, learning, locate and block-size independence, Conductor, state format). Not yet auditioned in a host: try each style, Order 2 after learning a line, and Learned mix at 50%. Open: learning assumes one melodic line (chords blur the counts); the matrix editor has click-to-cycle, drag-to-paint and right-click-to-zero but no undo; the plugin does not pass incoming MIDI through; the editor does not move while a CC drives a control. - Conductor awareness was added to
lifeform,luma,polymeterandxoxolo(off by default; not yet auditioned in a host).counterpointerandcadencefollowed (the CC mapping is a tested pure function,applyConductorCc, in their core-types headers; CC 24 relearns).arpgenfollowed (CC 21 Density -> Octaves, CC 22 Energy -> Rate;applyConductorCcinarpgen_core.hpp, tested; screenshot recaptured; not yet auditioned in a host).m-mixfollowed (CC 21 Density -> Open Bias, CC 22 Energy -> Maintain inverted; tested; UI slider added and the window made taller because its sixth slider row had been hidden under the toggles). Still without it among the generators:mnemosyne(only Novelty and a seed re-roll would fit, and its core takes parameters const, so it needs wrapper-level handling),sidecar(its wrapper is tangled up with the server path),tuney-vst. Worth adding where a density or energy knob exists. - Later candidate: chord-graph walker. A shared voice-leading helper in generative-common is only worth it if a second plugin needs it (shared code, needs approval).
- helterskelter
- keyframe
- ghost
- spliff
- ambo
- arpgen
- conductor
- drift
- harmonic-atlas
- mnemosyne
- magneto
- moka
- mosaic
- oracle
- orbit
- polymeter
- resonance-garden
- plank
- tuney-vst
- worms
- remove tasks that have been done from this file
- check MISTAKES.md for any systematic problems, promote info on these to CLAUDE.md
- if an issue in MISTAKES.md has been fully resolved, remove it from the file
- for new material, check test coverage
- ensure README.md and docs are up-to-date