Skip to content

Write in a Key

A Scale is a set of intervals with no pitch. A Key is a scale rooted at a note, and it is what turns degrees into notes. Events & Notes explains why degrees stay abstract until a key resolves them. The names these recipes accept are listed in Scales, Chords & Intervals.

Write degrees with ^ inside a pattern and resolve them with .in_key():

let melody = [^1 ^3 ^5 ^-2].in_key(Key(C4, "major"));
print(melody.describe());

^8 is the root an octave up; ^0 and negative degrees go below the root.

track.set_key(...) resolves every degree sent to the track, and quantizes absolute notes into the scale. It works on MidiTrack and AudioTrack:

let lead = MidiTrack("lead");
lead >> MidiOutput("synth");
lead.set_key(Key(C4, "minor")); // or lead.set_key("minor", C4)
lead << [^1 ^3 ^5];

Change the key and the same degrees follow it.

A trailing : on a degree builds the diatonic chord on it; add a quality to override it:

let cadence = [^1: ^4: ^5: ^1:].in_key(Key(A3, "minor"));
let colored = [^1:min7 ^4:min7].in_key(Key(D4, "dorian"));

Outside a pattern, ask the key directly:

let k = Key(C4, "major");
print(k.chord(2)); // [D4, F4, A4] (ii)
print(k.chord7(5)); // [G4, B4, D5, F5] (V7)
print(k.quantize(C#4)); // C4

Borrow a note or chord from outside the key

Section titled “Borrow a note or chord from outside the key”

Put b or # before the degree number to play it a semitone lower or higher — a blue note, a leading tone, a chord from the parallel key. The degree still follows the key:

let blues = [^1 ^b3 ^3 ^5].in_key(Key(C4, "major")); // the b3 rubs against the 3
let leading = [^5 ^6 ^#7 ^8].in_key(Key(A3, "minor")); // G# leads up to A
let borrowed = <^1: ^b6:maj ^b7:maj ^1:>.in_key(Key(C4, "major"));

An altered degree has no diatonic chord, so as a chord root it names its quality: ^b6:maj, not ^b6:. For a negative degree the accidental comes first: ^b-2.

Write Root:Quality in a pattern, and add ~n for an inversion. <…> changes chord each cycle:

let progression = <D4:min7 G4:7 C4:maj7~1>; // ii–V–I, last chord inverted

chord(root, quality, inversion) returns the same chord as an array of notes. Positive inversions raise the lowest notes by an octave; negative ones drop the highest:

print(chord(C4, "major")); // [C4, E4, G4]
print(chord(C4, "major", 1)); // [E4, G4, C5]
print(chord(C4, "major", -1)); // [G3, C4, E4]

To play an array as a chord, wrap it in Stack(): Stack(chord(C4, "min7")).

Name the mode, or rotate a scale with .mode(n):

let dorian = Scale("dorian");
let also_dorian = Scale("major").mode(2); // major mode 2 = dorian
print(dorian.intervals()); // [0, 2, 3, 5, 7, 9, 10]
print(also_dorian.intervals()); // the same
print(Key(D4, dorian).notes());

Pass semitone intervals to Scale(). They start at 0 and ascend; fractional values are microtonal, and a second argument sets a period other than the octave:

let neutral = Scale(#[0, 1.5, 3.5, 5, 7, 8.5, 10]);
let stretched = Scale(#[0, 2, 4, 5, 7, 9, 11], 19);
let k = Key(D4, neutral);
print(k.notes());

Load a Scala .scl file with load_scala(). It looks next to your .non file, then in ~/.resonon/scales/, then in the bundled scales:

let just = load_scala("just/5-limit");
let bp = load_scala("bohlen-pierce");

On a MIDI track, fractional pitches play through pitch bend; turn on track.set_tuning() or track.set_mpe() for the track.

interval(note, name) returns one note. Collect several to spell a scale or chord that isn’t built in:

let root = C4;
let diminished = #[
root,
interval(root, "M2"),
interval(root, "m3"),
interval(root, "P4"),
interval(root, "tritone"),
interval(root, "m6"),
interval(root, "M6"),
interval(root, "M7")
];
print(diminished);