Sounds editor — keyboard reference
The Sounds tab is a tracker-style pattern editor. Click any cell in the 32×5 pattern grid to place the cursor, then use the keymap below to write notes, instruments, volumes, and stops. The cursor's sub-column (Note / Inst / Vol) determines which keys are active.
Notes — FT2 two-octave keymap
Two rows on the keyboard map to two consecutive octaves, anchored on the editor's current octave (default 4). The lower row (Z–M plus ", L .") covers C through D# of the next octave; the upper row (Q–U plus 2-7) covers C through B of the next octave; I–P plus 9 0 reach the third octave's C through E.
| Key | Note | Octave offset |
|---|---|---|
Z | C | +0 |
S | C# | +0 |
X | D | +0 |
D | D# | +0 |
C | E | +0 |
V | F | +0 |
G | F# | +0 |
B | G | +0 |
H | G# | +0 |
N | A | +0 |
J | A# | +0 |
M | B | +0 |
, | C | +1 |
L | C# | +1 |
. | D | +1 |
Q | C | +1 |
2 | C# | +1 |
W | D | +1 |
3 | D# | +1 |
E | E | +1 |
R | F | +1 |
5 | F# | +1 |
T | G | +1 |
6 | G# | +1 |
Y | A | +1 |
7 | A# | +1 |
U | B | +1 |
I | C | +2 |
9 | C# | +2 |
O | D | +2 |
0 | D# | +2 |
P | E | +2 |
Pressing a note key writes the note at the cursor, advances the cursor down one row, and live-previews the note via the cell's instrument.
Stop / clear
| Key | Effect |
|---|---|
= | Note-off — triggers ADSR release; voice rings through release phase, then silences. |
- | Note-cut — immediate silence, no release ramp. |
Backspace / Delete | Clear the active sub-column at the cursor (Note / Inst / Vol). |
Octave shift
| Key | Effect |
|---|---|
< | Lower the editor's base octave (clamped to 0). |
> | Raise the editor's base octave (clamped to 8). |
The current octave is shown in the transport bar at the bottom of the Sounds tab. Default is 4 (so Z = C4).
Hex digits — Inst and Vol sub-columns
When the cursor is on the Inst or Vol sub-column, 0–9 and A–F write hex values.
| Sub-column | Behavior |
|---|---|
| Inst (2 hex digits) | Shift-register: each keypress shifts the existing value left and appends the new digit. Type 0 then 5 to enter 05. Cursor does NOT auto-advance, so you can build a 2-digit instrument id without losing the row. |
| Vol (1 hex digit) | Replace: each keypress overwrites the current value. Cursor does NOT auto-advance. |
Navigation
| Key | Effect |
|---|---|
↑ / ↓ | Move cursor up / down one row. |
← / → | Move cursor left / right one sub-column (wraps across channel boundaries). |
Tab | Move cursor right (same as →). |
Shift+Tab | Move cursor left (same as ←). |
Home | Jump to row 0, channel 0, Note sub-column. |
End | Jump to last row of the current channel + sub-column. |
PageUp / PageDown | Jump 8 rows up / down. |
Undo / redo
| Key | Effect |
|---|---|
Ctrl+Z / ⌘+Z | Undo. |
Ctrl+Shift+Z / ⌘+Shift+Z | Redo. |
Ctrl+Y | Redo (alternate). |
Undo snapshots the entire SOND data on each meaningful change. Typing bursts are debounced with a 250 ms window so a sequence of note keypresses collapses into one undo step. The undo stack is capped at 50 snapshots; older snapshots drop FIFO.
Transport buttons
Below the pattern grid the transport bar shows the current pattern, row, and BPM. Buttons:
| Button | Effect |
|---|---|
▶ Play | Start the host engine on the active pattern (loops; same as cart-Forth's play-pattern). |
■ Stop | Halt the engine and all SFX slots (panic button). Equivalent to stop-music followed by -1 stop-sfx. |
⊙ Follow | Toggle whether the cursor tracks the playhead row while the engine is running. |
Music and SFX buses
As of P5.2 the host tracker engine has two parallel buses: a music slot that loops your background track, and four SFX slots that overlay one-shot or looping sound effects on top. Each slot reads patterns from the same SOND pool — the distinction is entirely about which cart-Forth word you call, not how you author the pattern.
Music bus: ( pattern -- ) play-music starts a looping
pattern; ( -- ) stop-music halts it. play-pattern is an alias retained from pre-P5.2 — both names produce identical bytecode.
SFX bus: ( pattern slot -- ) play-sfx fires a one-shot
pattern on slot 0–3 (or -1 to auto-allocate the first
idle slot, evicting the oldest if all four are busy). loop-sfx ( pattern slot -- ) is the looping variant. ( slot -- ) stop-sfx stops one slot; -1 stop-sfx stops all SFX without touching music.
When two slots write to the same voice on the same tick, the engine dispatches in fixed order — music first, then SFX 0, 1, 2, 3 — and the last write wins. Authors who want non-conflicting layering can author SFX patterns to use voices the music doesn't touch (typically the sawtooth + noise channels).
play-once (pre-P5.2 single-pattern player) is retained
as a shorthand: it's equivalent to passing your pattern number followed by -1 to play-sfx. For example, 2 play-once and 2 -1 play-sfx compile to
equivalent behavior — both fire pattern 2 on an auto-allocated SFX slot.
The semantic shift is intentional — pre-P5.2 it cut music; post-P5.2 it
overlays.
On GameTank carts the same music and SFX also play in native ROM builds
(cart-build --emit gtr) — the ROM embeds a 5-voice ACP
mixer plus a tracker sequencer, and tempo stays locked to 60 Hz wall-time
even when a frame runs over budget.
Dynamic tempo
A cart can change the music slot's tempo at any time by calling ( frames-per-row -- ) set-bpm. The new tempo (clamped to 2..=64) takes effect at the next row boundary. SFX slots are
never affected.
Call set-bpm every frame to drive smooth tempo ramps —
useful for Space-Invaders-style march acceleration or any rhythm-based
gameplay that responds to game state.
\ Halve the music tempo from frame ~60 onward.
variable frame
: update
frame @ 1 + frame !
frame @ 60 = if 4 set-bpm then
;
set-bpm is a no-op when no music is playing — it does not
pre-stage a tempo for a future play-music; that call
always re-reads the cart's SOND header tempo.