Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Song functions

Song functions edit the current song. Each one changes only what it names, in the order the script calls them. Sections and arrangement are on Form and sections; mix(), master() and the other mixer edits are on Mixing functions.

Starting a song

song()

song(genre)               // a new song, seeded from the clock
song(genre, seed)         // seed: integer
song(genre, options)      // options: map

Replaces the current song with one the generator writes: tempo, key, swing, two progressions, a form, drum lanes, theory-written parts, sounds and a mix, all from the genre’s profile. The same genre and seed give the same song on any machine. The recordings installed (sample packs, Apple Loops) change only which recordings it plays.

optiontypedefaulteffect
seednumber1which song
bpmnumberfrom the genre’s rangetempo
keystringrandom tonic in one of the genre’s modeskey, e.g. "F minor"
shortboolfalsehalve every section (at least 4 bars), for sketches
loopsboolfalsebuild the song around a family of Apple Loops (GarageBand style), when they are installed
samplesnumber0.75chance that each drum lane uses a recorded one-shot from your library instead of synthesis

The genre is one of house deep_house techno trap lofi dnb future_bass synthwave, or one of your own (see Genres); case, spaces and hyphens do not matter ("deep house"). A negative seed acts as 0. bpm and key change only the tempo and the key: the title, the form, progression A and the drum patterns are the ones the seed gives without them. A key in another mode than the seed’s moves the chords into that mode, as key() does. The options are checked like tempo() and key() check their arguments, and an option the map should not have is an error rather than ignored (so is a misspelt one).

Errors: unknown genre 'polka' (try: deep_house, dnb, future_bass, house, lofi, synthwave, techno, trap); tempo between 40 and 240 BPM; cannot read key 'H minor' (try "A minor", "F#m", "Eb", "D dorian"); song() has no option 'tempo' (options: seed bpm key loops short samples).

song("techno", #{ seed: 3, bpm: 130, key: "F minor", short: true });

blank()

blank(genre)

Replaces the current song with an empty one: a single 8-bar section called loop (kind groove, energy 0.8), no tracks, and one progression, A, which is the key’s tonic chord alone (i in a minor key, I in a major one) at bars_per_chord(1). The rest comes from the genre: the mix (bus effects, master chain, loudness target, reverb and delay), the critic’s genre, and a tempo, key, swing and humanize from the generator. Set the ones you care about; in particular, set prog("A", ...) to the chords you play, since the critic checks the bass and melody against it.

blank("lofi");
tempo(84); key("D minor"); swing(0.58);
track("drums", s("bd ~ ~ bd, ~ sd, hh*8?0.15"));

A script that calls neither song() nor blank() edits the song that is open (the studio’s, or the one passed with claudius script --on), and with no song open it starts from blank("house"). So a script for another genre that should run on its own starts with blank().

Errors: as for song().

Global settings

title()

title(name)    // string

The song’s title, used in the song folder’s name (out/house-night-drive-1/).

tempo()

tempo(bpm)     // number, 40..240
bpm(bpm)       // the same function

Errors: tempo between 40 and 240 BPM; tempo needs a number (also from bpm()).

key()

key(name)      // "A minor", "Am", "F#m", "Eb", "D dorian", "C# phrygian" ...

Sets the key. Roman-numeral progressions and n() patterns follow it; note names in note() patterns do not, since they name absolute pitches. When the mode changes (minor to dorian, major to minor), every diatonic chord in every progression moves to the same degree in the new mode: Imaj7 in a major key becomes i7 in minor, IV7 in dorian becomes iv7 in minor. A borrowed chord keeps its sound and is spelled in the new mode: bIII in F major (A flat) becomes III in F minor, where it is diatonic. Applied chords (V/V) stay as written. Because a borrowed chord can become diatonic, going to minor and back does not always restore the original: bVII becomes VII in minor, and VII becomes vii° back in major. Tonal Apple Loops in the song are repitched the shortest way (at most six semitones).

Errors: cannot read key 'H minor' (try "A minor", "F#m", "Eb", "D dorian").

// G dorian to A minor: "i7 ii7 i7 IV7" becomes "i7 iiø7 i7 iv7"
key("A minor");

The accepted names and modes are in Pitch.

swing()

swing(amount)  // number, clamped to 0..0.8

Sixteenth-note swing for the whole song. Each off-beat sixteenth (the second of each pair) is moved to amount of the way through its eighth note: 0.5 is straight, 0.58 is a lazy hip-hop feel, 0.66 is close to triplets. Values up to 0.501 play straight. Only notes exactly on the sixteenth grid move; eighth notes and triplets stay where they are. Swing applies when the song renders, to drums, parts and pattern tracks alike.

humanize()

humanize(amount)   // number, clamped to 0..1

Random timing and velocity, applied at render. The timing offset has a standard deviation of 4 ms × amount on drum roles and 60 % of that on other tracks (synth tracks use 3 ms instead of 4); velocity varies by 5 % × amount. The offsets are seeded by the song and the track name, so they are the same on every render.

Harmony

prog()

prog(name, chords)   // name: string; chords: string, separated by spaces

Sets a progression. Chords are Roman numerals ("i7 VImaj7 iv7 v7") or chord names ("Am7 Fmaj7 Dm7 Em7"); names are converted to numerals in the current key when prog() runs, so call key() first. Each chord lasts bars_per_chord() bars, and the progression loops.

Sections play A unless they say otherwise (sec().prog()), and breaks play B, or A when the song has no B. Any name works: prog("verse", ...) is used by sections that ask for "verse". The progressions drive chord("prog"), the generated parts, the chord track the critic checks melodies and basslines against, and the harmony tab of the studio. Pitch has the numeral rules.

A chord name outside the key gets an accidental (Bb in A minor is bII). A slash chord keeps its bass note, as a name or as a numeral: F/A in A minor is stored as VI/A, and key() moves the bass note with the chord.

Errors: cannot read chord 'Q7' (Roman numerals like i7 VImaj7 bVII, or names like Am7).

blank("house");
key("A minor");
prog("A", "Am7 Fmaj7 Dm7 Em7");     // stored as i7 VImaj7 iv7 v7
prog("B", "Fmaj7/A G Em7 Am7");     // stored as VImaj7/A VII v7 i7
bars_per_chord(1);
track("keys", chord("prog").s("epiano"));

bars_per_chord()

bars_per_chord(bars)   // integer, clamped to 1..8

How many bars each chord lasts: bars_per_chord(2) holds each chord for two bars. It applies to every progression. blank() starts at 1; a generated song has the generator’s value, often 2.

Drums

drum()

drum(lane, steps)                     // the "main" variant, 16 steps per bar
drum(lane, variant, steps)
drum(lane, variant, steps, per_bar)   // per_bar: integer, clamped to 1..96

Writes a drum lane’s pattern as a step string, the way a step sequencer shows it:

characterhit
Xaccent, velocity 1.0
xhit, 0.8
oghost note, 0.45
1 to 9velocity 0.1 to 0.9
|, spacesignored, for readability
anything else (., -, 0 …)rest

There are 16 steps per bar unless per_bar says otherwise: 32 for thirty-second notes, 12 or 48 for triplet grids. The length of the string decides the length of the pattern, rounded up to whole bars (at most 16): 16 steps are one bar, 20 steps are two bars with the last 12 steps empty. All the variants of a lane have the same length; a shorter one is repeated to fit.

On an existing lane, drum() replaces that variant. On a song without the lane, it creates one, if the name is a drum name (or hats): the lane uses the synthesized voice at its default settings, and joins every section with energy 0.6 or more. drum_sound() changes what it plays.

Sections choose a variant by name with .variant(); intros and outros use sparse, the others main, and a lane without the requested variant plays main.

A lane’s mixing role comes from its name when the name is a drum name (bd and kick are kicks, sd and snare snares), else from the sound it plays. The arranger finds lanes by role: snare rolls and fills go on the snare lane, else the clap; the kick drops out at the end of a build, and the other tracks duck against it.

Errors: no drum lane 'bongo', and drum() makes lanes only for drum names (bd kick sd ...); the song's lanes: kick clap hat ohat shaker perc. For other sounds, track("bongo", s("...")).

drum("kick", "X...x...X...x..x");                     // a pickup into the next bar
drum("hat", "sparse", "..x...x...x...x.");
drum("hat", "main", "x.x.x.x.x.x.x.x.x.x.x.x.x.x.xxxx", 32);

drum_sound()

drum_sound(lane, sound)         // sound: string
drum_sound(lane, sound, bank)   // bank: string, a drum machine or kit

Sets what a drum lane plays, keeping its patterns:

  • a drum name, for the synthesized voice: drum_sound("snare", "cp");
  • a kit sound from the sample packs: drum_sound("kick", "tr909:bd"), or the same as drum_sound("kick", "bd", "tr909"), with any bank; a variation number picks another recording ("tr909:bd:12");
  • another recording from the packs, such as "conga";
  • an audio file: drum_sound("perc", "/Users/me/Samples/rim.wav").

Naming the sound the lane already plays keeps it as it is, including the tuning evolution gave a synthesized voice. The lane keeps its name, so a lane named with a drum name keeps its role; a lane named otherwise takes the role of its new sound.

Errors: no drum lane 'bongo' (lanes: kick clap hat ohat shaker perc); drum() makes one, or (lanes: none yet) on a song without drum lanes; 'pad' is not a drum sound (a drum name, a kit sound, or a sample) for a synth preset; the sound errors for a name that does not resolve.

song("house", 9);
drum_sound("kick", "tr909:bd");
drum_sound("clap", "cp", "tr808");

Parts

part()

part(name, options)   // options: map with any of the fields below

Edits a part the theory engine writes: the bass, chords, pad, lead, arp or keys of a generated song. On a song without that part (a blank() song has none), part() first adds the genre’s part, written by the theory engine over the song’s progressions and joining the sections the way a new track does; the options then edit it. part("bass", #{}) adds the genre’s bass as it comes.

fieldtypeeffect
stylestringhow the part is written (below)
presetstringa synth preset or a sampled instrument; naming the current preset keeps its patch
register[low, high]MIDI range the part is written in
densitynumber, clamped to 0..1how busy a lead is
seedintegera new seed rewrites this part, and only this part
ratenumber, clamped to 1/32..4arpeggio step in beats (0.25 is sixteenths)
rolestyles
basssustain, pulse, root8, offbeat, octave, rolling, syncopated, kick_follow (follows the kick, 808 style), acid, walking, reese
chords, pad, keyssustain, half, stabs, offbeat, pump, syncopated, charleston
arpup, down, updown, random, pinky
leadmotif, the only one: leads are motifs with A A′ A B phrasing; density and seed change them

A style the part’s role does not have is an error. A decimal seed is ignored.

Errors: no part 'keys' (the song has: bass chords pad lead arp; house parts: bass chords pad lead arp); track() writes your own; a bass part has no style 'wobbly' (styles: sustain pulse root8 ...); unknown preset 'nope' (synths: ...; or an installed instrument such as piano, rhodes, string_ensemble); part() has no option 'styles' (options: style preset register density seed rate).

part("bass", #{ style: "rolling", preset: "reese" });
part("lead", #{ seed: 42, density: 0.4 });
part("chords", #{ style: "stabs", register: [55, 72] });

On a blank song, part() gives you the generator’s parts over your own progression:

blank("house");
key("A minor"); prog("A", "Am F C G");
track("drums", s("bd*4, [~ cp]*2, [~ hh]*4"));
part("bass", #{ style: "offbeat" });
part("chords", #{ style: "stabs", preset: "stab" });

Tracks

track()

track(name, pattern)

Makes a pattern track, or replaces the track of that name: a drum lane, a generated part, a loop or another pattern track (track("bass", ...) on a generated song replaces its bass part). The pattern’s sounds and words are read here, so an unknown sound, a word the pattern cannot read (note("h3")) or a pattern that is too dense fails at this line.

Where it plays:

  • A replaced track keeps the replaced track’s place in every section.
  • A new track joins every section with energy 0.6 or more, or every section when no section has any track yet (the first track of a blank() song).
  • form() and layer() decide it exactly.

Its mix comes from the pattern’s own settings (.level(), .pan(), .room() …); a mix() on the old track does not carry over. Its role comes from .role(), else from its sound (see Roles). A sound pattern with several sounds becomes one track per sound when arranged: drums.bd, drums.cp, drums.hh.

Each track() writes a log line with the sounds it found:

blank("house");
track("drums", s("bd*4, [~ cp]*2, [~ hh]*4"));
track drums: bd, cp, hh

Errors: track 'x': unknown sound 'bdd' (...); track 'x': cannot read 'h3' as a note (...); track 'x': this pattern is too dense to play: it makes more than 262144 events (or it needs more than 4000000 steps of work); track 'x' plays 2048 notes per bar; the limit is 1024. See Errors and limits.

mute(), unmute(), solo(), unsolo(), remove()

mute(track)    unmute(track)
solo(track)    unsolo(track)
remove(track)

mute() silences a track; while any track is soloed, only soloed tracks play, and only they are judged. remove() deletes the track and takes it out of every section. They work on drum lanes, parts, loops and pattern tracks.

Errors: no track 'nope'.

mute("hat");
remove("arp");