import { assertEquals } from "@std/assert"; import { type Chord, chordName, parseChord } from "./chord.ts"; import type { Key, ModeId } from "./key.ts"; import { parseNote } from "./pitch.ts"; import { parallel, relative, suspend, toggleSeventh, tritoneSub } from "./transform.ts"; const key = (tonic = "C", mode: ModeId = "ionian"): Key => ({ tonic: parseNote(tonic)!.note, mode, }); const C = key(); const apply = (f: (c: Chord) => Chord | undefined, ...symbols: string[]) => symbols.map(s => { const out = f(parseChord(s, C)!); return out && chordName(out); }); Deno.test("parallel swaps the third", () => { assertEquals(apply(parallel, "C", "Am", "Cmaj7", "Dm7", "Bdim", "Csus4"), [ "Cm", "A", "CmMaj7", "D7", undefined, undefined, ]); }); Deno.test("relative moves a minor third, keeping the shared notes", () => { assertEquals(apply(relative, "C", "Cmaj7", "Am", "Am7", "Eb", "G7", "Bdim"), [ "Am", "Am7", "C", "Cmaj7", "Cm", undefined, undefined, ]); }); Deno.test("tritone substitution only applies to dominants", () => { assertEquals(apply(tritoneSub, "G7", "D9", "C", "Cmaj7"), [ "D♭7", "A♭9", undefined, undefined, ]); }); Deno.test("sevenths come from the key", () => { const seventh = (s: string, k = C) => chordName(toggleSeventh(parseChord(s, k)!, k)!); assertEquals( ["C", "Dm", "G", "Bdim", "F"].map(s => seventh(s)), ["Cmaj7", "Dm7", "G7", "Bm7♭5", "Fmaj7"], ); assertEquals(seventh("E", key("A", "harmonicMinor")), "E7"); assertEquals(seventh("G#dim", key("A", "harmonicMinor")), "G♯dim7"); // outside the key: whichever seventh the key has assertEquals([seventh("Bb"), seventh("E"), seventh("Ab")], ["B♭maj7", "E7", "A♭maj7"]); assertEquals( ["Cmaj7", "G7", "Bdim7"].map(s => seventh(s)), ["C", "G", "Bdim"], ); }); Deno.test("suspending, and unsuspending back to the key's third", () => { const sus = (s: string, tone: 2 | 5) => chordName(suspend(parseChord(s, C)!, C, tone)!); assertEquals([sus("C", 5), sus("Am", 2), sus("G7", 5)], ["Csus4", "Asus2", "G7sus4"]); assertEquals([sus("Csus4", 5), sus("Dsus4", 5), sus("Csus2", 5)], ["C", "Dm", "Csus4"]); });