char-slop/chordpad.vibed.software
git clone https://git.t4t.associates/char-slop/chordpad.vibed.software
ad1ae60
main
1import { assert , assertEquals } from "@std/assert" ; 2import { chordName , parseChord } from "./chord.ts" ; 3import { connections } from "./connect.ts" ; 4import { parseNote } from "./pitch.ts" ; 5 6const C = { tonic :parseNote ( "C" ) ! . note , mode :"ionian" as const }; 7const chord = ( s :string ) => parseChord ( s , C ) ! ; 8const routes = ( prev :string | undefined , target :string ) => 9connections ( prev ?chord ( prev ) :undefined , chord ( target )). map ( 10r => ` ${ r . name } : ${ r . chords . map ( chordName ). join ( " " )} ` , 11); 12 13Deno . test ( "the way in is coloured like where it's going" , () => { 14const into = ( target :string ) => routes ( undefined , target ). slice ( 0 , 2 ); 15assertEquals ( into ( "Cmaj9" ), [ "V: G9" , "ii–V: Dm9 G9" ]); 16assertEquals ( into ( "Cmaj7" ), [ "V: G7" , "ii–V: Dm7 G7" ]); 17assertEquals ( into ( "C" ), [ "V: G7" , "ii–V: Dm7 G7" ]); 18// into a minor chord: the minor key's ii and V 19assertEquals ( into ( "Am" ), [ "V: E7♭9" , "ii–V: Bm7♭5 E7♭9" ]); 20}); 21 22Deno . test ( "the ways in that follow on best from the chord before come first" , () => { 23// from Am7, down a fifth to the ii 24assertEquals ( routes ( "Am7" , "Cmaj7" )[ 0 ], "ii–V: Dm7 G7" ); 25// from E♭maj7, down a fifth to ♭VI, then a semitone down to ii; and C's own shape planed down 26// the scale, ♭III–♭II–I, as the autocomposer does 27const fromThree = routes ( "Ebmaj7" , "Cmaj7" ); 28assertEquals ( fromThree . slice ( 0 , 2 ), [ "♭VI–V: A♭maj7 G7" , "ii–V: Dm7 G7" ]); 29assert ( fromThree . includes ( "planed ♭II: D♭maj7" )); 30// from Dm7, the ii is already there: no playing it again 31const fromTwo = routes ( "Dm7" , "Cmaj7" ); 32assertEquals ( fromTwo [ 0 ], "V: G7" ); 33assert ( ! fromTwo . some ( r => r . startsWith ( "ii–V" ))); 34}); 35 36Deno . test ( "colourful ways in, where they fit" , () => { 37const all = routes ( undefined , "Cmaj7" ); 38assert ( all . includes ( "♭VI–V: A♭maj7 G7" )); 39assert ( all . includes ( "iv–♭VII: Fm7 B♭7" )); 40assert ( all . includes ( "iii–VI–ii–V: Em7 A7 Dm7 G7" )); 41assert ( all . includes ( "planed ♭II: D♭maj7" )); 42// coltrane's major thirds, only from a major third away 43assert ( ! all . some ( r => r . startsWith ( "coltrane" ))); 44assert ( routes ( "Abmaj7" , "Cmaj7" ). includes ( "coltrane: B7 Emaj7 G7" )); 45// no backdoor or iii–VI into a minor chord 46assert ( ! routes ( undefined , "Am" ). some ( r => r . startsWith ( "iv–" ) || r . startsWith ( "iii–" ))); 47}); 48 49Deno . test ( "ways in are spelled without double flats or sharps" , () => { 50for ( const target of [ "Ab" , "Db" , "Gbmaj7" , "F#m" , "C#" ]) { 51for ( const r of routes ( undefined , target )) assert ( ! /𝄫|𝄪|♭♭|♯♯ / . test ( r ), ` ${ target } : ${ r } ` ); 52} 53assert ( routes ( undefined , "Ab" ). includes ( "subV: A7" )); 54});