DUET docs: DUST and DUSK, the two synths (summary)
duet's two own synths (m64's tracker, not yet published): DUST, a sid synth — one whole reSID per slot, three voices, table programs and a mod matrix on the patch's own clock — and DUSK, wavetable synthesis over the song's own samples with a software copy of the SID envelope and filter. Both live only in a [DUET] project; a session with a SID-Wizard module ([SWM], [CO]) has no synth rows at all — "the module brings its own chips".
Key claims#
DUST — slots and column#
- Four slots (
DUST1–DUST4) on the PROJECT page's SETUP;SHIFT+LEFT/RIGHTwalk one throughoff > 8580 > 6581— the chip model is the slot's, not the patch's, and swaps live mid-note. An armed slot is a reSID of its own, beyond the SID setup's four chips; it puts a synth column headedDUST1on the grid and a strip on the mixer. The four slots stand on channels 49–52, so a song with DUST keeps at most 48 IT channels. Switching a slot off parks its column — the notes wait in the song. QUALITY reaches the slots as it reaches the chips. - Column fields: note (
===gates off,~~~fades) · instrument (a patch number01–20— "a DUST ins is a PATCH ADDRESS (D01-D20)") · volume ("a DUST cell's volume IS velocity, 0-64 — it has no commands, only the number"; empty = 64) · effect (DUST's own list). - The column's effects:
Gxxglide to note (cents per tick, no retrigger) ·Fxx/Exxpitch slide up/down (cents per tick;00repeats the column's last) ·Zxxmacro value$00–$7F·SFxselect macro (SF0= M01, sixteen slots) ·SDxnote delay (ticks the whole cell waits; the restart follows it) ·a–dthe mixer's send units.
The D## patch — four faces#
The D## list holds patch slots D01–D20 (hex — 32; every one born as the default THREE SAWS; Ctrl+D copies, BKSP restores the default). The editor's faces:
- VOICE: CLOCK (control clock 250–4000 Hz — how often modulation is resolved), VOL (the chip's own volume nibble — "it clicks when it moves"), LEVEL (patch level 0–64, where velocity lands), MODE (UNISON, or CHORD — each voice takes its interval), GATE (WAVE TBL — bit 0 of each wave step is the gate — or GRAIN, gated on the grain clock), RESTART (OFF / SOFT / FULL hard-restart with a 1–200 ms window spent before the note), per-voice WAVE bytes (bits spelled out; "the gate bit is never yours"), PW
$000–$FFF($800is a square), ADSR nibbles, SRC (CHIP: the SID's own ADSR shapes the note; or ENV A/B), DETUNE in cents per voice ("three saws a few cents apart" — see detune), CHORD + STRUM (semitones per voice, ms between gates), GRAIN (RATE/SPREAD/WINDOW), FILTER (CUT$000–$7FF, RES$0–$F, ROUTE a bit per voice, LP/BP/HP). - TABLES: four table programs side by side — WAVE, PITCH, PULSE, FILTER — 64 steps each, each with its own clock (
Hzfree-running,tkticks orrwrows per step; per-tick/per-row follow tempo changes), direction fwd/rev/png/rnd, length and loop (0disables; loop end on the last step = one-shot), and a per-voice read OFFSET — "three grains, one table". A step reads itself back ($41 -> $D404: pulse gate UP); a PITCH step is+3semitones,=absolute,~cents or>glide; a PULSE step=100sets,+018sweeps;.is a hold ("the waveform stays where the table left it"). The status row counts the cost live (720 writes/s 272 steps/s peak 22/block). - MOD: 4 LFOs (off/tri/saw up/saw down/square/sine/S&H/smooth random; RATE in Hz/ticks/rows, depth %, retrig free/note, quantise, phase, fade-in ms; "an LFO that is off costs nothing") · 2 envelopes (DLY/ATK/HLD/DEC ms, SUS %, REL, curve lin/exp/log, LOOP) · 8 routes SOURCE → DESTINATION (sources: LFOs, envelopes, table positions, note, random, velocity, the last
Zxx; destinations: cutoff, resonance, pulse widths, frequencies, detune, ADSR nibbles, the tables' rates and jumps, master volume, grain knobs, sync and ring, amp). "There is no vibrato effect on the column: an LFO on the pitch is the vibrato." A RESOLUTION pane shows BASE/HELD/MOD/FINAL per moved parameter, live. - MACRO: VEL binds the volume column to
ampbetween MIN and MAX; M01–M10 (sixteen, hex) are free knobs — LABEL, DESTINATION, MIN (whereZ00lands), MAX (Z7F), curve. "SF0 picks the slot Z00-Z7F sends into it."
DUSK — slots and column#
- Eight slots (
DUSK1–DUSK8) on channels 41–48 (IT side stops at 40 when all are armed). The column stands in the synth block between channels and SID columns. Fields as DUST's, except: one note cell can be a whole chord when the patch's VOICES say so;===releases (the tail plays out),^^^cuts. Effects: same list as DUST's (Gxx,Fxx/Exx,Zxx,SFx,SDx,a–d). - K## patches
K01–K20(hex: thirty-two), defaultAFTERGLOW(built-in table, ADSR00F6, no filter).
The K## patch#
- VOICE: SAMPLE —
BUILT-IN($00, a saw > pulse > sine morph table, 32 frames × 512) or a P## number: the song's own sample read as a wavetable — frames of FRAME samples each (8–4096; "2048 is the Polyend standard"). MORPH is the position across the frames in %, always a blend of the two nearest. ALIASclean(band-limited copies for high notes) orraw("the aliasing on purpose"). ADSR is the 6581's own times (attack 2 ms–8 s, decay/release 6 ms–24 s, sustain level; release6≈ 204 ms — see adsr-envelope). AMP SRC: the nibbles, or ENV A–D as the VCA. FILTER$000–$7FF= 20 Hz–20 kHz, RES$00–$FF($B4rings,$F0is the edge — past it the loop self-oscillates), LP/BP/HP summable outputs (LP+HP = notch; all off = no filter), DRV drive into the filter. - VOICES — the chord from one cell: 1–8 oscillators per note; STRUM (ms between voice starts), WIDTH (stereo spread 0–64), PHASE (
spreadrandom /align"the coherent punch"), INT (semitones per voice — a chord from one cell), DET (cents; "the default ladder is DUST's own, grown outward"). The slot's pool: 16 voices; a new note releases the last; when full, the oldest note is stolen whole. - TABLES: MORPH, PITCH and FILTER, "scanned per NOTE: every note reads them from the top" (a released tail keeps walking its own copies). Same clock/direction/loop grammar as DUST; a MORPH step is
-hold,=set %,+add (a sweep); the status row converts every clock into steps per second at the song's tempo. - MOD: 4 LFOs (slot-wide, shared by every sounding note) + 4 envelopes (per note) + 8 routes. Sources add
morph_pos,note(centred on C-5),velocity,random(per note, seeded),chan_fx; destinations: cutoff, resonance, drive, morph ("a slow LFO here is the classic wavetable sweep" — see wavetable-programming), pitch (cents, summed — vibrato), amp. RESOLUTION panel shows BASE/HELD/MOD/FINAL/FROM live. - MACRO: VEL default
amp -64 .. 0(full note untouched); M01–M10 (sixteen hex) driven bySFx+Zxxon the column, walked MIN→MAX in the destination's own units on a lin/exp/log curve.
Internals in brief — the frozen contract#
- Both synths are a pure core (no chip, no allocator, no clock) beside a rack owning hardware and memory; DUST's core only ever emits
(reg, val, src)writes, every one shadow-compared (write-on-change over 25 registers) and attributed. DUSK's envelope is the 6581's in software — rate counter periods in PAL cycles, an 8-bit level, the ladder's slowing thresholds: "the staircase on a long release is deliberate". Wavetables get band-limited mip halvings (down to 16 samples, at most 9 levels);rawpins level 0. Modulation runs on a 1000 Hz control grid; a render is split-invariant. - Every patch carries an engine byte first (
ENGINE_DUST_V1= 1,ENGINE_DUSK_V1= 1). A patch with any other value is left alone: it makes no sound — never the wrong one — the editor refuses to touch it, and the project writes it back verbatim. The contract: a v1 patch renders the same forever; a change that alters an existing patch's sound is a new engine version with an explicit migration. Renders are pure functions of the file (fixed per-slot seeds, never a clock). - Verification: no external reference exists, so golden pins are the oracle — nine corpus projects per synth, each pinned by wav SHA-256, peak and a gzipped trace;
tools/dust-ab.sh/tools/dusk-ab.shmust close at 9/9 + 9/9; a moved pin is a regression unless explicitly re-blessed with a note.--dust-test/--dusk-testrender the default patches (THREE SAWS, 3 voices, detune 0/−9/+9 cents;AFTERGLOW, 32 frames × 512, ADSR00F6). The DUST trace is the attributed register stream (960 $D404=$21 wave_tab); the DUSK trace records note-engine decisions (note/steal/glide/gate…), since it has no registers.
Practical takeaways#
- DUST is table-driven sid sound design with the frame-tick removed: each of WAVE/PITCH/PULSE/FILTER runs at its own rate (up to 4 kHz), free-running or tempo-locked — instrument-tables thinking, decoupled from 50 Hz.
- DUST's RESTART OFF/SOFT/FULL with a millisecond window is hard-restart as a synth parameter; the engine even looks ahead one row so the attack, not the restart, lands on the beat.
- DUSK turns any P## sample into a morphing wavetable — set FRAME to the single-cycle length (2048 for Polyend-style tables), sweep MORPH from a table or a slow LFO; the SID-true ADSR and self-oscillating filter keep it in the chip idiom.
- Velocity is the volume column on both synth columns;
SFx+Zxxare per-column macro automation into any destination.
Notable quotes#
- "There is no vibrato effect on the column: an LFO on the pitch is the vibrato."
- "The staircase on a long release is deliberate." (DUSK's software 6581 envelope)
- "A v1 patch renders the same forever."
Relevance to the wiki#
Documents the two synth halves the duet entity only names, with the concrete ranges a sound designer needs (clock rates, table grammar, filter and resonance edges, the velocity and macro bindings). The engine-byte / golden-pin contract is a rare written-down policy for keeping a tracker synth's old songs rendering identically.
Pages touched#
duet (maintainer rewrite). Suggested (report only): hard-restart, wavetable-programming.