char-slop/chordpad.vibed.software

git clone https://git.t4t.associates/char-slop/chordpad.vibed.software

Charlotte Somallow midi import as chords040fec5

main
1.1 KiB25 linesraw
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(
6  notes: readonly MidiNote[],
7): { start: number; dur: number; notes: number[] }[] {
8  const spans = notes.map(n => ({
9    pitch: n.pitch,
10    start: canonBeat(n.start),
11    end: canonBeat(n.start + n.dur),
12  }));
13  const cuts = [...new Set(spans.flatMap(n => [n.start, n.end]))].sort((a, b) => a - b);
14  const runs: { start: number; end: number; notes: number[] }[] = [];
15  cuts.slice(1).forEach((end, i) => {
16    const start = cuts[i];
17    const sounding = spans.filter(n => n.start <= start && n.end >= end).map(n => n.pitch);
18    if (!sounding.length) return;
19    const pitches = [...new Set(sounding)].sort((a, b) => a - b);
20    const last = runs.at(-1);
21    if (last?.end === start && last.notes.join() === pitches.join()) last.end = end;
22    else runs.push({ start, end, notes: pitches });
23  });
24  return runs.map(({ start, end, notes }) => ({ start, dur: canonBeat(end - start), notes }));
25}