char-slop/chordpad.vibed.software
git clone https://git.t4t.associates/char-slop/chordpad.vibed.software
a427fe3
main
1import { assertEquals } from "@std/assert" ; 2import { type Chord , chordName , parseChord } from "./chord.ts" ; 3import type { Key , ModeId } from "./key.ts" ; 4import { parseNote } from "./pitch.ts" ; 5import { parallel , relative , suspend , toggleSeventh , tritoneSub } from "./transform.ts" ; 6 7const key = ( tonic = "C" , mode :ModeId = "ionian" ) :Key => ({ 8tonic :parseNote ( tonic ) ! . note , 9 mode, 10}); 11const C = key (); 12const apply = ( f :( c :Chord ) => Chord | undefined , ...symbols :string []) => 13symbols . map ( s => { 14const out = f ( parseChord ( s , C ) ! ); 15return out && chordName ( out ); 16}); 17 18Deno . test ( "parallel swaps the third" , () => { 19assertEquals ( apply ( parallel , "C" , "Am" , "Cmaj7" , "Dm7" , "Bdim" , "Csus4" ), [ 20"Cm" , 21"A" , 22"CmMaj7" , 23"D7" , 24undefined , 25undefined , 26]); 27}); 28 29Deno . test ( "relative moves a minor third, keeping the shared notes" , () => { 30assertEquals ( apply ( relative , "C" , "Cmaj7" , "Am" , "Am7" , "Eb" , "G7" , "Bdim" ), [ 31"Am" , 32"Am7" , 33"C" , 34"Cmaj7" , 35"Cm" , 36undefined , 37undefined , 38]); 39}); 40 41Deno . test ( "tritone substitution only applies to dominants" , () => { 42assertEquals ( apply ( tritoneSub , "G7" , "D9" , "C" , "Cmaj7" ), [ 43"D♭7" , 44"A♭9" , 45undefined , 46undefined , 47]); 48}); 49 50Deno . test ( "sevenths come from the key" , () => { 51const seventh = ( s :string , k = C ) => chordName ( toggleSeventh ( parseChord ( s , k ) ! , k ) ! ); 52assertEquals ( 53[ "C" , "Dm" , "G" , "Bdim" , "F" ]. map ( s => seventh ( s )), 54[ "Cmaj7" , "Dm7" , "G7" , "Bm7♭5" , "Fmaj7" ], 55); 56assertEquals ( seventh ( "E" , key ( "A" , "harmonicMinor" )), "E7" ); 57assertEquals ( seventh ( "G#dim" , key ( "A" , "harmonicMinor" )), "G♯dim7" ); 58// outside the key: whichever seventh the key has 59assertEquals ([ seventh ( "Bb" ), seventh ( "E" ), seventh ( "Ab" )], [ "B♭maj7" , "E7" , "A♭maj7" ]); 60assertEquals ( 61[ "Cmaj7" , "G7" , "Bdim7" ]. map ( s => seventh ( s )), 62[ "C" , "G" , "Bdim" ], 63); 64}); 65 66Deno . test ( "suspending, and unsuspending back to the key's third" , () => { 67const sus = ( s :string , tone :2 | 5 ) => chordName ( suspend ( parseChord ( s , C ) ! , C , tone ) ! ); 68assertEquals ([ sus ( "C" , 5 ), sus ( "Am" , 2 ), sus ( "G7" , 5 )], [ "Csus4" , "Asus2" , "G7sus4" ]); 69assertEquals ([ sus ( "Csus4" , 5 ), sus ( "Dsus4" , 5 ), sus ( "Csus2" , 5 )], [ "C" , "Dm" , "Csus4" ]); 70});