Contents

Chords

Two paths to chordal patterns: the chord() function with chord-symbol literals (Am7, Cmaj7), and inline chord brackets in n"…" strings ([c4 e4 g4]). For voice leading, the anchor, mode, voicing, and addVoicings transforms reshape chord events into musical voicings.

chord

Chord pattern - Parse a string of chord symbols into a polyphonic event stream.

ParamTypeDefaultDescription
strstring-Chord symbols separated by spaces
// Major chord
chord("C")          // C major triad

// Minor seventh
chord("Am7")        // A minor seventh

// Extended chord
chord("Cmaj9")      // C major 9th — 5 voices

// Chord progression
chord("Am C Dm G")  // One chord per beat

Both syntaxes — chord("C^7") and c"C^7" mini-notation — share one canonical quality table (akkado::CHORD_INTERVALS). Whatever works in one works in the other.

triad

A triad is a 3-note chord: root, third, fifth. Bare letter names (C, Am, F) parse as major triads. All quality aliases:

SuffixIntervalsMeaning
(none), M, maj0 4 7Major triad
m, min, -0 3 7Minor triad
dim, o0 3 6Diminished
aug, +0 4 8Augmented
sus20 2 7Suspended 2nd
sus4, sus0 5 7Suspended 4th (bare sus = sus4)
50 7Power chord (no third)
// Lots of triad flavors
c"C Dm Eo Faug Gsus4 A- B+ E5" |> soundfont(@, "gm", 0) |> out(@)

seventh

A seventh chord adds a 7th degree:

SuffixIntervalsMeaning
7, dom70 4 7 10Dominant 7
M7, maj7, ^, ^70 4 7 11Major 7
m7, min7, -70 3 7 10Minor 7
dim7, o70 3 6 9Diminished 7 (fully dim)
m7b5, 00 3 6 10Half-diminished 7
aug7, +70 4 8 10Augmented 7
mM7, m^7, minmaj70 3 7 11Minor-major 7
// Jazz progression — same chord, three ways
chord("Cmaj7 Am7 Dm7 G7")
chord("CM7 A-7 D-7 G7")        // alt-symbol style
chord("C^7 Am7 Dm7 G7")        // Strudel-style ^

sixth

A sixth chord stacks a 6th instead of a 7th — open, lush, bossa-nova flavor:

SuffixIntervalsMeaning
60 4 7 9Major 6
m6, min60 3 7 9Minor 6
chord("C6 Am6 Dm6 G6") |> soundfont(@, "gm", 0) |> out(@)

extended

Extended chords stack 9ths, 11ths, or 13ths above a 7th. They produce 5–6 voices per step.

SuffixIntervalsMeaning
90 4 7 10 14Dominant 9
M9, maj90 4 7 11 14Major 9
m9, min90 3 7 10 14Minor 9
add90 4 7 14Major triad + 9 (no 7th)
add20 2 4 7Major triad + 2 (close-voiced)
110 4 7 10 14 17Dominant 11
m110 3 7 10 14 17Minor 11
130 4 7 10 14 21Dominant 13
// Modal jazz vamp — full extensions
c"Cmaj9 Am11 D13 G13" |> soundfont(@, "gm", 0) |> out(@)

inline

Inline chords in pattern strings use square brackets: every note plays simultaneously rather than in sequence.

// C major as inline chord
n"[c4 e4 g4]"
// Mixed sequence and chord
n"c4 [c4 e4 g4] e4 g4"

polyphony

Chord patterns produce events with multiple voices per step. How those voices reach the audio output depends on what consumes the pattern:

  • Internally polyphonic instruments (soundfont) accept chord patterns directly. Every chord voice is dispatched to a separate voice slot inside the instrument, and the per-voice outputs are summed automatically.

    c"CM Am Dm G" |> soundfont(@, "gm", 0) |> out(@)
  • Mono synths (oscillators, filters, single-voice DSP) require an explicit poly(N, instrument_fn) wrapper to allocate voices. A chord patched directly into a mono synth raises E410 because there is no implicit voice allocation:

    // ✗ E410 — chord into a mono synth has no voice allocator
    c"CM Am Dm G" |> saw(@freq) |> out(@)
    
    // ✓ poly() wraps the synth in N parallel voices
    fn lead({freq, gate, vel}) ->
      saw(freq) |> lp(@, 2000 * adsr(gate)) |> @ * vel
    c"CM Am Dm G" |> poly(@, lead, 8) |> out(@)

Voice limit: chord events carry up to 16 voices per step (MAX_VALUES_PER_EVENT). The largest built-in quality is 13 at 6 voices, so every chord in the standard table fits comfortably. Custom voicings registered via addVoicings() are truncated past 16 intervals.

notes

poly() and soundfont() sound a chord; notes() and freqs() hand you the chord as data you can transform. They take a pattern and return a dynamic array — an array whose length is the current event’s chord size, varying per event at runtime.

FunctionReturns
notes(e)dynamic array of MIDI numbers
freqs(e)dynamic array of frequencies (Hz)

The method-style forms e.notes and e.freqs are equivalent and usually read better:

n"[c4,e4,g4] g3 [a3,c4]" as e
e.notes        // dynamic array: [60,64,67], then [55], then [57,60]
e.freqs        // same chords in Hz
len(e.notes)   // runtime signal: 3, then 1, then 2

len() is polymorphic — a constant for static arrays, a runtime signal for dynamic ones. Indexing wraps by default, so a free-running counter walks the chord regardless of its size. Combined with step() (see State Cells) that is a complete arpeggiator:

fn step (arr, trig) -> arr[counter(trig)]

n"[c4,e4,g4] [a3,c4,e4] g3 [b3,d4,f#4]" as e
e.notes.step(trigger(8))     // walk the current chord on each 8th
  |> mtof(@) |> saw(@) |> out(@)

map() over a dynamic array transposes every chord note and stays dynamic:

map(e.notes, (v) -> v + 7)   // the chord up a fifth — still a dynamic array

Dynamic arrays cannot auto-fan-out across a stateful UGen (the chord size is not known at compile time): sine(e.freqs) is a compile error (E181) directing you to poly(). For a fixed-size transform, map over a static array instead — that fans out at compile time:

n"c4 e4 g4" as e
sum(saw(mtof(map([0, 4, 7], (i) -> e.note + i))))   // every note → a triad

anchor

anchor(pattern, "c4") sets the MIDI anchor note for chord voicing. Note names accept letter + optional accidental (# / b) + octave (c4, F#3, Bb-1).

// Voice the chords around c4
chord("Am C G F").anchor("c4")

mode

mode(pattern, "below") sets the chord voicing mode:

  • below: all chord notes ≤ anchor
  • above: all chord notes ≥ anchor
  • duck: closest to anchor, avoiding the anchor itself
  • root: root in bass octave near anchor, rest stacked near anchor
// Voice-led progression, top note ≤ c4
chord("Am C G F").anchor("c4").mode("below")
  |> mtof(@)
  |> saw(@)
  |> out(@)

voicing

voicing(pattern, "drop2") applies a named voicing dictionary. Built-in dictionaries:

  • close: all notes within an octave
  • open: wide spacing, root on bottom
  • drop2: second-from-top dropped one octave
  • drop3: third-from-top dropped one octave
// Drop-2 voicing on a jazz progression
chord("Cmaj7 Am7 Dm7 G7").voicing("drop2")

drop2

drop2: the second-highest note dropped down an octave. Classic guitar/piano jazz voicing.

drop3

drop3: the third-highest note dropped down an octave. Wider spread than drop2.

close

close: all notes packed within an octave. Tight, pad-like voicing.

open

open: root in bass, upper notes stacked widely. Broad, brassy voicing.

addVoicings

addVoicings("name", {quality: [intervals], ...}) registers a custom voicing dictionary by chord-quality name.

// Custom piano-jazz voicing
addVoicings("piano-jazz", {
    M: [0, 4, 7, 11, 14],
    m: [0, 3, 7, 10, 14]
})
chord("CM Am Dm G").voicing("piano-jazz")

Related: Mini-Notation Basics, Sequencing, poly