char-slop/chordpad.vibed.software

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

Charlotte Somallow copy and pasting notes2b249d4

main
2.9 KiB97 linesraw
1import { assertEquals } from "@std/assert";
2import {
3  chordsFromClipboard,
4  chordsFromText,
5  chordsToClipboard,
6  notesFromClipboard,
7  notesToClipboard,
8} from "./clipboard.ts";
9import { keyOf, newChordTrack, newNoteTrack, pasteNotes, placeChord } from "./model.ts";
10
11const C = { tonic: "C", mode: "ionian" } as const;
12const Am = { tonic: "A", mode: "aeolian" } as const;
13
14function track() {
15  const t = newChordTrack();
16  for (const [start, symbol] of [
17    [0, "I"],
18    [4, "bVI"],
19    [8, "Dm7"],
20    [12, "4"],
21  ] as const) {
22    placeChord(t, { id: symbol, start, dur: 4, symbol });
23  }
24  t.chords.push({ id: "pinned", start: 16, dur: 2, notes: [43, 59, 62, 65] });
25  return t;
26}
27
28Deno.test("copied chords go out as letter names, whatever they were written as", () => {
29  const copied = chordsToClipboard(track(), ["I", "bVI", "Dm7", "4", "pinned"], C)!;
30  assertEquals(copied.text, "C Ab Dm7 F G7");
31});
32
33Deno.test("copied chords paste back exactly, renamed for the key they land in", () => {
34  const copied = chordsToClipboard(track(), ["bVI", "Dm7", "pinned"], C)!;
35  const clip = chordsFromClipboard(copied.data, keyOf(Am))!;
36  assertEquals(
37    clip.chords.map(c => [c.start, c.dur, c.symbol ?? c.notes]),
38    [
39      [0, 4, "#VII"],
40      [4, 4, "Dm7"],
41      [12, 2, [43, 59, 62, 65]],
42    ],
43  );
44  assertEquals(clip.span, 14);
45  assertEquals(chordsFromClipboard("not json", keyOf(C)), undefined);
46  assertEquals(chordsFromClipboard('{"something": "else"}', keyOf(C)), undefined);
47});
48
49Deno.test("chord symbols pasted as text, from anywhere", () => {
50  const clip = chordsFromText("Dm7  G7 | Cmaj7, vi nonsense\nF/A", keyOf(C), 2)!;
51  assertEquals(
52    clip.chords.map(c => [c.start, c.symbol]),
53    [
54      [0, "Dm7"],
55      [2, "G7"],
56      [4, "Cmaj7"],
57      [6, "vi"],
58      [8, "F/A"],
59    ],
60  );
61  assertEquals(clip.span, 10);
62  assertEquals(chordsFromText("hello there", keyOf(C), 4), undefined);
63});
64
65Deno.test(
66  "notes copy with their timing, pitch and velocity, and paste alongside what's there",
67  () => {
68    const t = newNoteTrack();
69    t.notes.push(
70      { id: "a", start: 2, dur: 1, pitch: 63, vel: 0.5 },
71      { id: "b", start: 2, dur: 2, pitch: 67, vel: 0.8 },
72      { id: "c", start: 3.5, dur: 0.5, pitch: 70, vel: 1 },
73      { id: "d", start: 8, dur: 1, pitch: 72, vel: 1 },
74    );
75    const copied = notesToClipboard(
76      t,
77      ["c", "a", "b"],
78      keyOf({ tonic: "C", mode: "aeolian" }),
79    )!;
80    assertEquals(copied.text, "E♭4 G4 B♭4");
81    const clip = notesFromClipboard(copied.data)!;
82    assertEquals(clip.span, 2);
83    const ids = pasteNotes(t, clip, 7.5);
84    const pasted = t.notes.filter(n => ids.includes(n.id));
85    assertEquals(
86      pasted.map(n => [n.start, n.dur, n.pitch, n.vel]),
87      [
88        [7.5, 1, 63, 0.5],
89        [7.5, 2, 67, 0.8],
90        [9, 0.5, 70, 1],
91      ],
92    );
93    // and the note already at 8 is still there
94    assertEquals(t.notes.length, 7);
95    assertEquals(notesFromClipboard("[1, 2]"), undefined);
96  },
97);