Skip to content

line

line is, in its author’s (pd3v) own words, “a tiny command-line MIDI sequencer for live coding.” You launch it, you get a line> prompt, and you type text phrases that are music. Each line of text is parsed into a rhythmic phrase of MIDI notes that loops in time; you edit the text live and the loop updates on the next bar. It sends MIDI to one channel (one instrument or one CC), and the Ableton-Link build keeps multiple line instances — and any Link-aware DAW — locked to a shared tempo grid. The cover image is seven line instances running in seven tmux panes: one playing notes, six driving CC. That is the whole instrument: a terminal prompt is the sequencer.

What makes line the headline reference for Batch 8 is not the MIDI plumbing — it’s the pattern grammar. The language is tiny, dense, and complete enough to perform with. And critically for KN-86: line is architecturally a text grammar parsed by an embedded language VM (Lua/LPeg) into a data tree that a host runtime schedules — which is exactly the nOSh-runtime-plus-Fe-VM split the Deckline already runs. The mapping from line’s phrase grammar to a KEC Lisp s-expression is nearly one-to-one.

The grammar is defined in lineparser.lua as an LPeg PEG and consumed by a C++ scheduler. The phrase parses into a nested tree — phraseT = vector<vector<vector<noteAmp>>> — i.e. phrase → parts → notes, with each leaf a (note, amplitude) pair. The Lua parser tags each node: {'n', …} note, {'c', …} chord, {'s', …} rhythmic sub-division.

Notes — space-separated tokens, each a quarter-note by default:

45 46 47 48 → four 1/4 notes (MIDI numbers)
a3 as3 b3 c4 → same, as note ciphers (a/as/b/c + octave)

Rhythm via dots — a leading . halves a note’s duration (subdivides it); the .x x. syntax wraps a run of notes into a faster rhythmic sub-group:

45 .34 35. 48 49 → 1/4, 1/8, 1/8, 1/4, 1/4

Rests- is a silent note:

36 .37 38. 41 .- 46.

Chords — parenthesized note groups play simultaneously; an arpeggio is just bare notes after the chord:

(c3 e3 g3) g5 e6 c2 → C-major chord, then a 3-note arpeggio, all 1/4

Amplitude~ suffix sets per-note or per-chord velocity (0.0–1.0, default 1.0):

d3~.5 → D3 at 0.5 amplitude
(c4 eb4 g4)~.7 → C-minor chord at 0.7 amplitude

Phrase transforms (single-key commands typed at the prompt, applied to the live loop):

CommandEffect
rreverse the phrase (recursively reverses the whole note tree)
s / xscramble within parts / xscramble across the whole phrase
sa / xascramble amplitudes only
rl / rrrotate notes left / right N positions
gagenerate random amplitudes for every note
*4concatenate the phrase 4×
/8stretch phrase duration 8×
m / ummute / unmute

Phrase queue + persistence (the live-set workflow):

CommandEffect
sp / sp3save phrase to queue at position 0 / replace position 3
lp0 or :0load phrase from position 0 — plays next
llist saved phrases
sf / sfmysynthsave the whole queue to a .line text file
lfsamplerload a .line file (restores phrases and instance params)

Instance / clock controlsch2 MIDI channel, bpm120 tempo, mi/ma value range, am50 overall amplitude, n/cc2 switch notes-vs-CC mode, i/o CC in/out sync, lt10 Link latency trim, lb<name> relabel the prompt. The _ suffix repeats a command indefinitely (<cmd>_) or N times (<cmd>_8); < composes several commands on one line.

  1. Embedded language VM parses text → data tree. line links Lua + LPeg and runs the PEG in lineparser.lua to turn the prompt string into the nested phraseT vector. The C++ side never parses music text itself — it hands the string to the embedded VM and reads back a tagged tree. This is the same boundary KN-86 draws between the KEC Lisp VM and the nOSh C runtime (ADR-0001/0004): the language layer owns parsing and structure; the host owns scheduling and I/O.
  2. Ableton Link owns the clock. A State struct holds the ableton::Link instance and a dummy audio platform; all phrase timing is derived from Link’s shared beat/quantum, so multiple lines and any Link DAW stay phase-locked. REF_BAR_DUR / REF_QUANTUM define the bar; loop updates land on bar boundaries.
  3. rtmidi for output. A MidiEvent vector is rebuilt each cycle; notesPlayStop / ccPlayStop emit note-on/off or CC at the scheduled microsecond, with explicit dangling-note cleanup across cycle boundaries.
  4. Transforms are pure tree operations. reverse, scramble, xscramble, scrambleAmp are plain std::reverse / std::shuffle over the nested vector — the phrase is data, and editing it is list manipulation. (See the Toneline-relevance note: this is the crux of the Lisp mapping.)
  5. readline REPL. The prompt is a raw-mode readline loop with history; commands are split (splitCommands) and queued (std::deque<LineCommand>), so one input line can carry a phrase plus transforms.

line is THE core sequencer-language reference for the Toneline. The Toneline’s fiction already frames music as list processing — a song is a list of patterns, a pattern is a list of sequences, a sequence is a list of notes, LAMBDA defines a reusable phrase (see kn90t-toneline.md). line is the working proof that this framing is not just thematic dressing — it is a performable live-coding grammar, and its grammar should be the seed of the Toneline’s sequencer language.

Concrete inputs to the Toneline sequencer design:

  • Adopt the dot-rhythm + chord + amplitude grammar wholesale as the Toneline’s terse text-entry mode. 45 .34 35. 48, (c3 e3 g3), d3~.5 are about as compact as a phrase notation can get, and they type cleanly on the Ferris Sweep’s number/punctuation right half — digits for MIDI/scale-degree input, . ( ) ~ - already on the keymap. A Toneline operator could enter a bar of music in a single line on the CIPHER-LINE-flanked main grid.
  • Phrase queue + save/load is the live-set model. sp/lp/l (queue) and sf/lf (file persistence, restoring instance params too) is precisely the workflow a portable performance device needs: stash phrases as you build them, recall them in performance, save the set to the cartridge SD. The Toneline’s .line-equivalent is a saved phrase queue on the cartridge filesystem.
  • Multi-instance sync = the Toneline’s LINK port. line’s reason-for-existence demo is seven instances locked together. The Toneline fiction already specifies a LINK port for two Tonelines to sync and jam over serialline shows the payoff (multiple voices, one clock) and Ableton Link is the protocol blueprint. Adopt a Link-style shared-beat model for the LINK port (or literally speak Ableton Link if a host-DAW bridge is ever wanted).
  • notes-vs-CC mode switch maps to the Toneline distinguishing note phrases (drive the YM2612 FM voices) from control phrases (sweep an FM operator’s envelope, modulate tempo, automate a parameter) — same instrument, two phrase semantics, one n/cc toggle.
  • The transform verbs (r reverse, s/x scramble, rl/rr rotate, * concat, / stretch) are the generative/performance layer. These turn a static phrase into a living one with one keystroke. The Toneline should expose the same verb set — and because Toneline phrases are Lisp lists (below), these verbs are just reverse, shuffle, rotate, append, stretch over a list.

The Deckline + LISP angle (why this is the headline)

Section titled “The Deckline + LISP angle (why this is the headline)”

A line phrase is a Lisp s-expression in disguise. line already parses its text grammar into a tagged nested tree ({'n' …} / {'c' …} / {'s' …}) — which is structurally an s-expression. On KN-86/Toneline that intermediate tree doesn’t need to be hidden behind a bespoke grammar: it can be the surface syntax, because the Deckline already ships a KEC Lisp REPL on the TERM key (ADR-0002, ADR-0016, repl.md).

The mapping:

line: (c3 e3 g3) g5 e6 c2
Lisp: (seq (chord c3 e3 g3) g5 e6 c2)
line: 45 .34 35. 48
Lisp: (seq 45 (div 34) (div 35) 48) ; .x = subdivide
line: d3~.5
Lisp: (amp 0.5 d3) ; or (note d3 :amp 0.5)
line: r (reverse the live phrase)
Lisp: (reverse phrase)
line: *4 (concat 4×)
Lisp: (repeat 4 phrase)

This collapses two KN-86 design problems into one. The Toneline doesn’t need a separate sequencer DSL and a separate scripting language — the sequencer language IS the scripting language. A phrase is a list; a song is a list of lists; LAMBDA (already in the Toneline fiction and on the left-half keymap) defines a reusable phrase exactly as it defines any other function; the transform verbs are the standard list library (reverse, rotate, shuffle, append). A performer who learns the Toneline’s music grammar has, without noticing, learned to program the deck. That is the single strongest synthesis the Toneline can make from this batch.

This also gives the Toneline a terse and a structural editing mode for free: type the line-style compact grammar for speed at the prompt, or open the same phrase in nEmacs (ADR-0008) as an s-expression for structural editing. Two front ends, one phrase representation.

line is already monochrome — it is a text prompt and a phrase string, no color semantics at all. That makes it the cleanest possible fit for the amber-on-black 80×25 grid: the live-coding surface is just text, which the Deckline renders natively. Where the Toneline needs to show phrase state beyond the raw text (which note is currently sounding, where the playhead is in the bar), use the established single-color toolkit — inversion for the current step, position for the playhead column, Unicode density bars (▁▂▃▄▅▆▇█) for per-step amplitude (the ~ value) drawn under each note token, and character shape (. ticks vs full glyphs) for subdivisions. No color is needed because line’s information is carried entirely by the grammar itself; the amber grid only has to render the characters and mark one cursor.

line — 7 instances live-coding, 1 synth + 6 CC

Source: pd3v/line repo image line0.4.3.png — multiple line> prompts running simultaneously across tmux panes, each a one-line phrase driving a separate MIDI channel. The phrase text on screen is the entire instrument.

  • Clone, don’t just read. The grammar’s precise behavior (dot subdivision, the ~ amplitude rule, chord-amplitude propagation) is only legible from lineparser.lua + the phraseT consumer in line.cpp. The s-expression mapping above is derived from that tree shape, not from the README alone.
  • The embedded-VM-parses-text-to-tree architecture is the deep lesson, beyond the music: line validates the exact runtime boundary KN-86 already committed to (Fe VM owns the language; C runtime owns scheduling + I/O). It is a real-world existence proof that a tiny embedded language can drive realtime audio from a terminal prompt.
  • Cross-link 4trk.md — 4TRK is the groovebox/tracker GUI music-workstation reference; line is the text/live-coding reference. Together they bracket the Toneline’s two plausible interaction models (step-grid vs typed phrases). The Toneline can ship both: a 4TRK-style step view and a line-style phrase prompt over the same Lisp phrase data.
  • Cross-link x0xb0x.md — the hardware-instrument lineage (TB-303 clone, DIY ethos); line is the software-instrument lineage in the same music-hardware family.
  • Cross-link pulsedeck.md and aethertune.md — both carry the audio-visualizer-as-first-class-surface pattern the Toneline needs alongside the sequencer grammar (the visualizer shows what line’s text is producing).
  • Cross-link cava (Batch 8, effect/) — the single-color spectrum/visualizer rendering technique that pairs with this sequencer language to close the see-what-you-hear loop on the Toneline.