char-slop/chordpad.vibed.software
git clone https://git.t4t.associates/char-slop/chordpad.vibed.software
040fec5
main
1import { canonBeat } from "../project/model.ts" ; 2import type { MidiNote } from "./smf.ts" ; 3 4/** a part as a run of chords: a new one wherever the set of sounding notes changes */ 5export function chordsOf ( 6notes :readonly MidiNote [], 7) :{ start :number ; dur :number ; notes :number [] }[] { 8const spans = notes . map ( n => ({ 9pitch :n . pitch , 10start :canonBeat ( n . start ), 11end :canonBeat ( n . start + n . dur ), 12})); 13const cuts = [ ...new Set ( spans . flatMap ( n => [ n . start , n . end ]))]. sort (( a , b ) => a - b ); 14const runs :{ start :number ; end :number ; notes :number [] }[] = []; 15cuts . slice ( 1 ). forEach (( end , i ) => { 16const start = cuts [ i ]; 17const sounding = spans . filter ( n => n . start <= start && n . end >= end ). map ( n => n . pitch ); 18if ( ! sounding . length ) return ; 19const pitches = [ ...new Set ( sounding )]. sort (( a , b ) => a - b ); 20const last = runs . at ( - 1 ); 21if ( last ?. end === start && last . notes . join () === pitches . join ()) last . end = end ; 22else runs . push ({ start, end, notes :pitches }); 23}); 24return runs . map (({ start, end, notes}) => ({ start, dur :canonBeat ( end - start ), notes})); 25}