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. Change the key while the track plays and the next cycle is in the new key, degrees and absolute notes alike, with no need to send the pattern again:

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 the project’s scales/, then in ~/.resonon/scales/, then in the scales shipped with resonon:

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);