@typeonce/ritmo-tools 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +304 -24
  3. package/dist/cli.js +5 -1
  4. package/dist/cli.js.map +1 -1
  5. package/dist/internal/version.d.ts +1 -1
  6. package/dist/internal/version.js +1 -1
  7. package/dist/music/cli/commands.d.ts +15 -0
  8. package/dist/music/cli/commands.d.ts.map +1 -0
  9. package/dist/music/cli/commands.js +180 -0
  10. package/dist/music/cli/commands.js.map +1 -0
  11. package/dist/music/cli/io.d.ts +11 -0
  12. package/dist/music/cli/io.d.ts.map +1 -0
  13. package/dist/music/cli/io.js +28 -0
  14. package/dist/music/cli/io.js.map +1 -0
  15. package/dist/music/cli/serve.d.ts +13 -0
  16. package/dist/music/cli/serve.d.ts.map +1 -0
  17. package/dist/music/cli/serve.js +168 -0
  18. package/dist/music/cli/serve.js.map +1 -0
  19. package/dist/music/core/audio.d.ts +22 -0
  20. package/dist/music/core/audio.d.ts.map +1 -0
  21. package/dist/music/core/audio.js +95 -0
  22. package/dist/music/core/audio.js.map +1 -0
  23. package/dist/music/core/check.d.ts +51 -0
  24. package/dist/music/core/check.d.ts.map +1 -0
  25. package/dist/music/core/check.js +113 -0
  26. package/dist/music/core/check.js.map +1 -0
  27. package/dist/music/core/instruments.d.ts +179 -0
  28. package/dist/music/core/instruments.d.ts.map +1 -0
  29. package/dist/music/core/instruments.js +531 -0
  30. package/dist/music/core/instruments.js.map +1 -0
  31. package/dist/music/core/listen.d.ts +32 -0
  32. package/dist/music/core/listen.d.ts.map +1 -0
  33. package/dist/music/core/listen.js +350 -0
  34. package/dist/music/core/listen.js.map +1 -0
  35. package/dist/music/core/midi.d.ts +4 -0
  36. package/dist/music/core/midi.d.ts.map +1 -0
  37. package/dist/music/core/midi.js +76 -0
  38. package/dist/music/core/midi.js.map +1 -0
  39. package/dist/music/core/render.d.ts +37 -0
  40. package/dist/music/core/render.d.ts.map +1 -0
  41. package/dist/music/core/render.js +204 -0
  42. package/dist/music/core/render.js.map +1 -0
  43. package/dist/music/core/review.d.ts +41 -0
  44. package/dist/music/core/review.d.ts.map +1 -0
  45. package/dist/music/core/review.js +139 -0
  46. package/dist/music/core/review.js.map +1 -0
  47. package/dist/music/core/song.d.ts +144 -0
  48. package/dist/music/core/song.d.ts.map +1 -0
  49. package/dist/music/core/song.js +291 -0
  50. package/dist/music/core/song.js.map +1 -0
  51. package/dist/music/page/assets/index-BfO_jOqP.js +4 -0
  52. package/dist/music/page/assets/index-Dw14kxkL.css +1 -0
  53. package/dist/music/page/index.html +19 -0
  54. package/dist/pixel/cli/commands.d.ts.map +1 -1
  55. package/dist/pixel/cli/commands.js +122 -46
  56. package/dist/pixel/cli/commands.js.map +1 -1
  57. package/dist/pixel/cli/io.d.ts +5 -1
  58. package/dist/pixel/cli/io.d.ts.map +1 -1
  59. package/dist/pixel/cli/io.js +16 -1
  60. package/dist/pixel/cli/io.js.map +1 -1
  61. package/dist/pixel/core/animation.d.ts +23 -8
  62. package/dist/pixel/core/animation.d.ts.map +1 -1
  63. package/dist/pixel/core/animation.js +41 -18
  64. package/dist/pixel/core/animation.js.map +1 -1
  65. package/dist/pixel/core/grid.d.ts +3 -2
  66. package/dist/pixel/core/grid.d.ts.map +1 -1
  67. package/dist/pixel/core/grid.js +14 -6
  68. package/dist/pixel/core/grid.js.map +1 -1
  69. package/dist/pixel/core/preview.d.ts +8 -3
  70. package/dist/pixel/core/preview.d.ts.map +1 -1
  71. package/dist/pixel/core/preview.js +39 -25
  72. package/dist/pixel/core/preview.js.map +1 -1
  73. package/dist/pixel/core/project.d.ts +27 -0
  74. package/dist/pixel/core/project.d.ts.map +1 -0
  75. package/dist/pixel/core/project.js +117 -0
  76. package/dist/pixel/core/project.js.map +1 -0
  77. package/dist/pixel/core/recipe.d.ts.map +1 -1
  78. package/dist/pixel/core/recipe.js +43 -6
  79. package/dist/pixel/core/recipe.js.map +1 -1
  80. package/dist/pixel/core/report.d.ts +21 -3
  81. package/dist/pixel/core/report.d.ts.map +1 -1
  82. package/dist/pixel/core/report.js +52 -22
  83. package/dist/pixel/core/report.js.map +1 -1
  84. package/dist/pixel/core/sheet.d.ts +68 -8
  85. package/dist/pixel/core/sheet.d.ts.map +1 -1
  86. package/dist/pixel/core/sheet.js +250 -32
  87. package/dist/pixel/core/sheet.js.map +1 -1
  88. package/dist/pixel/core/spritesheet.d.ts +43 -35
  89. package/dist/pixel/core/spritesheet.d.ts.map +1 -1
  90. package/dist/pixel/core/spritesheet.js +63 -59
  91. package/dist/pixel/core/spritesheet.js.map +1 -1
  92. package/dist/sound/cli/commands.d.ts +25 -0
  93. package/dist/sound/cli/commands.d.ts.map +1 -0
  94. package/dist/sound/cli/commands.js +137 -0
  95. package/dist/sound/cli/commands.js.map +1 -0
  96. package/dist/sound/cli/io.d.ts +24 -0
  97. package/dist/sound/cli/io.d.ts.map +1 -0
  98. package/dist/sound/cli/io.js +51 -0
  99. package/dist/sound/cli/io.js.map +1 -0
  100. package/dist/sound/cli/serve.d.ts +11 -0
  101. package/dist/sound/cli/serve.d.ts.map +1 -0
  102. package/dist/sound/cli/serve.js +147 -0
  103. package/dist/sound/cli/serve.js.map +1 -0
  104. package/dist/sound/core/measure.d.ts +67 -0
  105. package/dist/sound/core/measure.d.ts.map +1 -0
  106. package/dist/sound/core/measure.js +138 -0
  107. package/dist/sound/core/measure.js.map +1 -0
  108. package/dist/sound/core/recipe.d.ts +143 -0
  109. package/dist/sound/core/recipe.d.ts.map +1 -0
  110. package/dist/sound/core/recipe.js +292 -0
  111. package/dist/sound/core/recipe.js.map +1 -0
  112. package/dist/sound/core/render.d.ts +37 -0
  113. package/dist/sound/core/render.d.ts.map +1 -0
  114. package/dist/sound/core/render.js +317 -0
  115. package/dist/sound/core/render.js.map +1 -0
  116. package/dist/sound/core/review.d.ts +52 -0
  117. package/dist/sound/core/review.d.ts.map +1 -0
  118. package/dist/sound/core/review.js +89 -0
  119. package/dist/sound/core/review.js.map +1 -0
  120. package/dist/sound/core/takes.d.ts +29 -0
  121. package/dist/sound/core/takes.d.ts.map +1 -0
  122. package/dist/sound/core/takes.js +109 -0
  123. package/dist/sound/core/takes.js.map +1 -0
  124. package/dist/sound/core/wav.d.ts +4 -0
  125. package/dist/sound/core/wav.d.ts.map +1 -0
  126. package/dist/sound/core/wav.js +28 -0
  127. package/dist/sound/core/wav.js.map +1 -0
  128. package/dist/sound/page/assets/index-Cjun5b92.js +6 -0
  129. package/dist/sound/page/assets/index-DZm59wkQ.css +1 -0
  130. package/dist/sound/page/index.html +19 -0
  131. package/package.json +10 -4
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Songs: a piece of game music as plain JSON. A song is a tempo, a key,
3
+ * a number of bars, and tracks; each track is one instrument playing notes
4
+ * on a timeline counted in beats. The renderer (`render.ts`) turns it into
5
+ * sound the same way in Node and in the browser, so what the page plays,
6
+ * what the CLI measures, and the WAV the game gets are one song.
7
+ *
8
+ * ```json
9
+ * {
10
+ * "version": 1,
11
+ * "tempo": 96,
12
+ * "key": "A minor",
13
+ * "bars": 8,
14
+ * "tracks": [
15
+ * { "name": "Melody", "instrument": "flute", "notes": [{ "at": 0, "length": 1, "pitch": "E5" }] },
16
+ * { "name": "Drums", "instrument": "drums", "notes": [{ "at": 0, "length": 0.5, "pitch": "kick" }] }
17
+ * ]
18
+ * }
19
+ * ```
20
+ *
21
+ * Time is in beats from the start of the song (`at: 4` is the first beat of
22
+ * bar 2 in 4/4), pitches are note names (`"C4"` is middle C, `"F#3"`), or
23
+ * drum names on a drum track. A song may carry a `reference`: what
24
+ * `music listen` heard in a hum or a recording (its tempo, key, and melody),
25
+ * for the agent to build the song around and the page to draw under it.
26
+ */
27
+ import { drumNames, instrumentIds, instruments, isDrum } from "./instruments.js";
28
+ // ---------------------------------------------------------------------------
29
+ // Pitches and keys
30
+ const NAMES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
31
+ const LETTERS = { C: 0, D: 2, E: 4, F: 5, G: 7, A: 9, B: 11 };
32
+ /** A note name's MIDI number (`"C4"` is 60, `"A4"` 69), or undefined if it is not one. */
33
+ export const midiOf = (pitch) => {
34
+ const match = /^([A-G])(#|b)?(-?\d)$/.exec(pitch);
35
+ if (match === null)
36
+ return undefined;
37
+ const base = LETTERS[match[1]];
38
+ const accidental = match[2] === "#" ? 1 : match[2] === "b" ? -1 : 0;
39
+ const midi = (Number(match[3]) + 1) * 12 + base + accidental;
40
+ return midi >= 0 && midi <= 127 ? midi : undefined;
41
+ };
42
+ /** A MIDI number's note name, sharps for black keys (`61` is `"C#4"`). */
43
+ export const nameOf = (midi) => `${NAMES[((midi % 12) + 12) % 12]}${Math.floor(midi / 12) - 1}`;
44
+ /** A MIDI number's frequency in Hz (A4 = 440). */
45
+ export const frequencyOf = (midi) => 440 * Math.pow(2, (midi - 69) / 12);
46
+ /** The scales a key may name, as semitones above the key's note. */
47
+ export const scales = {
48
+ major: [0, 2, 4, 5, 7, 9, 11],
49
+ minor: [0, 2, 3, 5, 7, 8, 10],
50
+ "harmonic minor": [0, 2, 3, 5, 7, 8, 11],
51
+ dorian: [0, 2, 3, 5, 7, 9, 10],
52
+ phrygian: [0, 1, 3, 5, 7, 8, 10],
53
+ lydian: [0, 2, 4, 6, 7, 9, 11],
54
+ mixolydian: [0, 2, 4, 5, 7, 9, 10],
55
+ "major pentatonic": [0, 2, 4, 7, 9],
56
+ "minor pentatonic": [0, 3, 5, 7, 10],
57
+ blues: [0, 3, 5, 6, 7, 10]
58
+ };
59
+ export const scaleNames = ["major", "minor", "harmonic minor", "dorian", "phrygian", "lydian", "mixolydian", "major pentatonic", "minor pentatonic", "blues"];
60
+ export const tonics = ["C", "C#", "Db", "D", "D#", "Eb", "E", "F", "F#", "Gb", "G", "G#", "Ab", "A", "A#", "Bb", "B"];
61
+ /** Reads `"A minor"`, `"F# dorian"`, `"Bb major pentatonic"`. */
62
+ export const parseKey = (key) => {
63
+ const match = /^([A-G])(#|b)? (.+)$/.exec(key.trim());
64
+ if (match === null)
65
+ return undefined;
66
+ const scale = scaleNames.find((name) => name === match[3]);
67
+ if (scale === undefined)
68
+ return undefined;
69
+ const root = (LETTERS[match[1]] + (match[2] === "#" ? 1 : match[2] === "b" ? -1 : 0) + 12) % 12;
70
+ return { root, scale, classes: scales[scale].map((step) => (root + step) % 12) };
71
+ };
72
+ export const inKey = (key, midi) => key.classes.includes(((midi % 12) + 12) % 12);
73
+ // ---------------------------------------------------------------------------
74
+ // Time
75
+ export const secondsPerBeat = (song) => 60 / song.tempo;
76
+ export const lengthInBeats = (song) => song.bars * song.beatsPerBar;
77
+ export const lengthInSeconds = (song) => lengthInBeats(song) * secondsPerBeat(song);
78
+ /** `"bar 3"`, or `"bars 3–4"`: where a span of beats lies, bars counted from 1. */
79
+ export const barsOf = (song, from, to) => {
80
+ const first = Math.floor(from / song.beatsPerBar) + 1;
81
+ const last = Math.max(first, Math.ceil(to / song.beatsPerBar));
82
+ return first === last ? `bar ${first}` : `bars ${first}–${last}`;
83
+ };
84
+ // ---------------------------------------------------------------------------
85
+ // Decoding
86
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
87
+ const reader = (errors) => {
88
+ const number = (value, name, fallback, min, max) => {
89
+ if (value === undefined) {
90
+ if (fallback === undefined)
91
+ errors.push(`${name} is missing`);
92
+ return fallback ?? 0;
93
+ }
94
+ if (typeof value !== "number" || !Number.isFinite(value)) {
95
+ errors.push(`${name} must be a number`);
96
+ return fallback ?? 0;
97
+ }
98
+ if (min !== undefined && value < min)
99
+ errors.push(`${name} must be at least ${min}`);
100
+ if (max !== undefined && value > max)
101
+ errors.push(`${name} must be at most ${max}`);
102
+ return value;
103
+ };
104
+ const text = (value, name, fallback) => {
105
+ if (value === undefined && fallback !== undefined)
106
+ return fallback;
107
+ if (typeof value !== "string") {
108
+ errors.push(`${name} must be text`);
109
+ return fallback ?? "";
110
+ }
111
+ return value;
112
+ };
113
+ return { number, text };
114
+ };
115
+ const decodeNote = (raw, at, drums, errors) => {
116
+ if (!isRecord(raw)) {
117
+ errors.push(`${at} must be an object`);
118
+ return undefined;
119
+ }
120
+ const { number, text } = reader(errors);
121
+ const pitch = text(raw.pitch, `${at}.pitch`);
122
+ if (drums && !isDrum(pitch))
123
+ errors.push(`${at}.pitch must be a drum on a drum track: ${drumNames.join(", ")}`);
124
+ if (!drums && midiOf(pitch) === undefined)
125
+ errors.push(`${at}.pitch must be a note name like "C4", "F#3", or "Bb5" (got ${JSON.stringify(raw.pitch)})`);
126
+ return {
127
+ at: number(raw.at, `${at}.at`, undefined, 0),
128
+ length: number(raw.length, `${at}.length`, undefined, 1 / 64, 64),
129
+ pitch,
130
+ velocity: number(raw.velocity, `${at}.velocity`, 0.8, 0, 1)
131
+ };
132
+ };
133
+ const decodeTrack = (raw, at, errors) => {
134
+ if (!isRecord(raw)) {
135
+ errors.push(`${at} must be an object`);
136
+ return undefined;
137
+ }
138
+ const { number, text } = reader(errors);
139
+ const instrument = instrumentIds.find((id) => id === raw.instrument);
140
+ if (instrument === undefined)
141
+ errors.push(`${at}.instrument must be one of ${instrumentIds.join(", ")}`);
142
+ const drums = instrument !== undefined && instruments[instrument].kind === "drums";
143
+ if (!Array.isArray(raw.notes))
144
+ errors.push(`${at}.notes must be a list`);
145
+ const notes = (Array.isArray(raw.notes) ? raw.notes : []).flatMap((note, i) => decodeNote(note, `${at}.notes[${i}]`, drums, errors) ?? []);
146
+ if (raw.mute !== undefined && typeof raw.mute !== "boolean")
147
+ errors.push(`${at}.mute must be true or false`);
148
+ return {
149
+ name: text(raw.name, `${at}.name`, instrument === undefined ? "Track" : instruments[instrument].label),
150
+ instrument: instrument ?? "piano",
151
+ volume: number(raw.volume, `${at}.volume`, 0.8, 0, 1.5),
152
+ pan: number(raw.pan, `${at}.pan`, 0, -1, 1),
153
+ reverb: number(raw.reverb, `${at}.reverb`, 0.2, 0, 1),
154
+ mute: raw.mute === true ? true : undefined,
155
+ notes: [...notes].sort((a, b) => a.at - b.at)
156
+ };
157
+ };
158
+ const decodeReference = (raw, at, errors) => {
159
+ if (raw === undefined)
160
+ return undefined;
161
+ if (!isRecord(raw)) {
162
+ errors.push(`${at} must be an object`);
163
+ return undefined;
164
+ }
165
+ const { number, text } = reader(errors);
166
+ const notes = [];
167
+ for (const [i, note] of (Array.isArray(raw.notes) ? raw.notes : []).entries()) {
168
+ if (!isRecord(note) || typeof note.pitch !== "string" || midiOf(note.pitch) === undefined) {
169
+ errors.push(`${at}.notes[${i}] must be {"at", "length", "pitch"}`);
170
+ continue;
171
+ }
172
+ notes.push({ at: number(note.at, `${at}.notes[${i}].at`, 0, 0), length: number(note.length, `${at}.notes[${i}].length`, 0.25, 0), pitch: note.pitch });
173
+ }
174
+ const contour = Array.isArray(raw.contour) ? raw.contour.filter((value) => typeof value === "number" && Number.isFinite(value)) : [];
175
+ return {
176
+ audio: raw.audio === undefined ? undefined : text(raw.audio, `${at}.audio`),
177
+ tempo: number(raw.tempo, `${at}.tempo`, 100, 20, 300),
178
+ key: text(raw.key, `${at}.key`, "C major"),
179
+ length: number(raw.length, `${at}.length`, 0, 0),
180
+ notes,
181
+ contour,
182
+ offset: number(raw.offset, `${at}.offset`, 0),
183
+ start: number(raw.start, `${at}.start`, 0, 0)
184
+ };
185
+ };
186
+ /** Reads a song from parsed JSON, collecting every problem with where it is. */
187
+ export const decodeSongValue = (raw) => {
188
+ if (!isRecord(raw))
189
+ return { ok: false, error: 'a song is {"version": 1, "tempo": 96, "key": "A minor", "bars": 8, "tracks": [...]}' };
190
+ const errors = [];
191
+ const { number, text } = reader(errors);
192
+ const key = text(raw.key, "key", "C major");
193
+ if (parseKey(key) === undefined)
194
+ errors.push(`key must be a note and a scale, like "A minor" or "F# dorian" (scales: ${scaleNames.join(", ")})`);
195
+ const beatsPerBar = number(raw.beatsPerBar, "beatsPerBar", 4, 2, 12);
196
+ if (!Number.isInteger(beatsPerBar))
197
+ errors.push("beatsPerBar must be a whole number");
198
+ const bars = number(raw.bars, "bars", undefined, 1, 256);
199
+ if (!Number.isInteger(bars))
200
+ errors.push("bars must be a whole number");
201
+ if (!Array.isArray(raw.tracks))
202
+ errors.push("tracks must be a list");
203
+ const tracks = (Array.isArray(raw.tracks) ? raw.tracks : []).flatMap((track, i) => decodeTrack(track, `tracks[${i}]`, errors) ?? []);
204
+ if (raw.loop !== undefined && typeof raw.loop !== "boolean")
205
+ errors.push("loop must be true or false");
206
+ if (raw.compressor !== undefined && typeof raw.compressor !== "boolean")
207
+ errors.push("compressor must be true or false");
208
+ if (raw.about !== undefined && typeof raw.about !== "string")
209
+ errors.push("about must be text");
210
+ const song = {
211
+ about: typeof raw.about === "string" ? raw.about : undefined,
212
+ tempo: number(raw.tempo, "tempo", undefined, 30, 300),
213
+ key,
214
+ beatsPerBar,
215
+ bars,
216
+ loop: raw.loop !== false,
217
+ master: number(raw.master, "master", 0.8, 0, 2),
218
+ compressor: raw.compressor === true,
219
+ tracks,
220
+ reference: decodeReference(raw.reference, "reference", errors)
221
+ };
222
+ return errors.length === 0 ? { ok: true, value: song } : { ok: false, error: errors.join("; ") };
223
+ };
224
+ export const decodeSong = (text) => {
225
+ try {
226
+ return decodeSongValue(JSON.parse(text));
227
+ }
228
+ catch (error) {
229
+ return { ok: false, error: `not JSON: ${error instanceof Error ? error.message : String(error)}` };
230
+ }
231
+ };
232
+ // ---------------------------------------------------------------------------
233
+ // Encoding: one note per line, so a song reads and diffs well
234
+ const round = (value, digits = 4) => Math.round(value * 10 ** digits) / 10 ** digits;
235
+ const noteValue = (note) => ({ at: round(note.at), length: round(note.length), pitch: note.pitch, ...(note.velocity === 0.8 ? {} : { velocity: round(note.velocity, 2) }) });
236
+ /** The song as JSON, with every default left out and one note per line. */
237
+ export const encodeSong = (song) => {
238
+ const head = {
239
+ version: 1,
240
+ ...(song.about === undefined ? {} : { about: song.about }),
241
+ tempo: round(song.tempo, 2),
242
+ key: song.key,
243
+ ...(song.beatsPerBar === 4 ? {} : { beatsPerBar: song.beatsPerBar }),
244
+ bars: song.bars,
245
+ ...(song.loop ? {} : { loop: false }),
246
+ master: round(song.master, 3),
247
+ ...(song.compressor ? { compressor: true } : {})
248
+ };
249
+ const lines = ["{"];
250
+ for (const [name, value] of Object.entries(head))
251
+ lines.push(` ${JSON.stringify(name)}: ${JSON.stringify(value)},`);
252
+ lines.push(` "tracks": [`);
253
+ song.tracks.forEach((track, t) => {
254
+ const fields = {
255
+ name: track.name,
256
+ instrument: track.instrument,
257
+ volume: round(track.volume, 3),
258
+ ...(track.pan === 0 ? {} : { pan: round(track.pan, 3) }),
259
+ reverb: round(track.reverb, 3),
260
+ ...(track.mute === true ? { mute: true } : {})
261
+ };
262
+ lines.push(` {`);
263
+ for (const [name, value] of Object.entries(fields))
264
+ lines.push(` ${JSON.stringify(name)}: ${JSON.stringify(value)},`);
265
+ const notes = [...track.notes].sort((a, b) => a.at - b.at || (midiOf(a.pitch) ?? 0) - (midiOf(b.pitch) ?? 0));
266
+ if (notes.length === 0)
267
+ lines.push(` "notes": []`);
268
+ else {
269
+ lines.push(` "notes": [`);
270
+ notes.forEach((note, i) => lines.push(` ${JSON.stringify(noteValue(note))}${i === notes.length - 1 ? "" : ","}`));
271
+ lines.push(` ]`);
272
+ }
273
+ lines.push(` }${t === song.tracks.length - 1 ? "" : ","}`);
274
+ });
275
+ if (song.reference === undefined)
276
+ lines.push(` ]`);
277
+ else {
278
+ const reference = song.reference;
279
+ lines.push(` ],`);
280
+ lines.push(` "reference": {`);
281
+ const fields = { ...(reference.audio === undefined ? {} : { audio: reference.audio }), tempo: round(reference.tempo, 1), key: reference.key, length: round(reference.length, 2), ...(reference.offset === 0 ? {} : { offset: round(reference.offset) }), start: round(reference.start, 3) };
282
+ for (const [name, value] of Object.entries(fields))
283
+ lines.push(` ${JSON.stringify(name)}: ${JSON.stringify(value)},`);
284
+ lines.push(` "notes": [${reference.notes.map((note) => JSON.stringify({ at: round(note.at), length: round(note.length), pitch: note.pitch })).join(", ")}],`);
285
+ lines.push(` "contour": [${reference.contour.map((value) => round(value, 1)).join(",")}]`);
286
+ lines.push(` }`);
287
+ }
288
+ lines.push("}");
289
+ return lines.join("\n") + "\n";
290
+ };
291
+ //# sourceMappingURL=song.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"song.js","sourceRoot":"","sources":["../../../src/music/core/song.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ (function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=(e,n)=>{let r=e.trim();if(r===``)return;let i=/^[*/+]/.test(r);if(i&&n===void 0)return;let a=t(i?`(${n})${r}`:r);return a===void 0||!Number.isFinite(a)?void 0:a},t=e=>{let t=e.match(/\d*\.?\d+(?:e[+-]?\d+)?|\d+\.|[-+*/()]|\S/gi)??[],n=0,r=()=>t[n],i=()=>{let e=a();for(;e!==void 0&&(r()===`+`||r()===`-`);){let r=t[n++],i=a();if(i===void 0)return;e=r===`+`?e+i:e-i}return e},a=()=>{let e=o();for(;e!==void 0&&(r()===`*`||r()===`/`);){let r=t[n++],i=o();if(i===void 0||r===`/`&&i===0)return;e=r===`*`?e*i:e/i}return e},o=()=>{let e=t[n++];if(e===`-`||e===`+`){let t=o();return t===void 0?void 0:e===`-`?-t:t}if(e===`(`){let e=i();return t[n++]===`)`?e:void 0}if(e!==void 0&&/^(\d*\.?\d+(e[+-]?\d+)?|\d+\.)$/i.test(e))return Number(e)},s=i();return n===t.length?s:void 0},n=(e,t={},...n)=>{let i=document.createElement(e);t.class!==void 0&&(i.className=t.class),t.title!==void 0&&(i.title=t.title);for(let[e,n]of Object.entries(t.attrs??{}))n!==void 0&&n!==!1&&i.setAttribute(e,n===!0?``:String(n));for(let[e,n]of Object.entries(t.props??{}))Reflect.set(i,e,n);let a=t.on??{};for(let e of Object.keys(a)){let t=a[e];t!==void 0&&i.addEventListener(e,t)}return r(i,n),i},r=(e,t)=>{for(let n of t)n!=null&&n!==!1&&(Array.isArray(n)?r(e,n):e.appendChild(n instanceof Node?n:document.createTextNode(String(n))))},i=(e,...t)=>{e.replaceChildren(),r(e,t)},a={move:`<path d="M5 9l-3 3 3 3M9 5l3-3 3 3M15 19l-3 3-3-3M19 9l3 3-3 3M2 12h20M12 2v20"/>`,erase:`<path d="m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21"/><path d="M22 21H7"/><path d="m5 11 9 9"/>`,restore:`<path d="m9.06 11.9 8.07-8.06a2.85 2.85 0 1 1 4.03 4.03l-8.06 8.08"/><path d="M7.07 14.94c-1.66 0-3 1.35-3 3.02 0 1.33-2.5 1.52-2 2.02 1.08 1.1 2.49 2.02 4 2.02 2.2 0 4-1.8 4-4.04a3.01 3.01 0 0 0-3-3.02z"/>`,pick:`<path d="m2 22 1-1h3l9-9"/><path d="M3 21v-3l9-9"/><path d="m15 6 3.4-3.4a2.1 2.1 0 1 1 3 3L18 9l.4.4a2.1 2.1 0 1 1-3 3l-3.8-3.8a2.1 2.1 0 1 1 3-3l.4.4Z"/>`,undo:`<path d="M9 14 4 9l5-5"/><path d="M4 9h10.5a5.5 5.5 0 0 1 0 11H11"/>`,redo:`<path d="m15 14 5-5-5-5"/><path d="M20 9H9.5a5.5 5.5 0 0 0 0 11H13"/>`,minus:`<path d="M5 12h14"/>`,plus:`<path d="M5 12h14M12 5v14"/>`,fit:`<path d="M3 7V5a2 2 0 0 1 2-2h2M17 3h2a2 2 0 0 1 2 2v2M21 17v2a2 2 0 0 1-2 2h-2M7 21H5a2 2 0 0 1-2-2v-2"/>`,open:`<path d="m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2"/>`,download:`<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/><path d="M12 15V3"/>`,chevron:`<path d="m6 9 6 6 6-6"/>`,info:`<circle cx="12" cy="12" r="9"/><path d="M12 16v-4M12 8h.01"/>`,lock:`<rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/>`,unlock:`<rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 7.9-1"/>`,trash:`<path d="M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>`,split:`<circle cx="6" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M20 4 8.12 15.88M14.47 14.48 20 20M8.12 8.12 12 12"/>`,wand:`<path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72"/><path d="m14 7 3 3"/><path d="M5 6v4M19 14v4M10 2v2M7 8H3M21 16h-4M11 3H9"/>`,image:`<rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.09-3.09a2 2 0 0 0-2.82 0L6 21"/>`,close:`<path d="M18 6 6 18M6 6l12 12"/>`,check:`<path d="M20 6 9 17l-5-5"/>`,alert:`<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4M12 17h.01"/>`,sparkle:`<path d="M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M5.6 18.4l2.1-2.1M16.3 7.7l2.1-2.1"/>`,lines:`<rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M3 15h18M9 3v18M15 3v18"/>`,brush:`<path d="m14.6 3.4 6 6L10 20H4v-6z"/><path d="m12 6 6 6"/>`,pixel:`<rect x="6" y="6" width="12" height="12" rx="1"/>`,area:`<path d="M4 4h7v7H4zM13 4h7v7h-7zM4 13h7v7H4z"/>`,play:`<path d="M7 4.5v15a1 1 0 0 0 1.5.86l12.5-7.5a1 1 0 0 0 0-1.72L8.5 3.64A1 1 0 0 0 7 4.5z"/>`,pause:`<rect x="6" y="4" width="4" height="16" rx="1"/><rect x="14" y="4" width="4" height="16" rx="1"/>`,previous:`<path d="M19 20 9 12l10-8v16zM5 19V5"/>`,next:`<path d="m5 4 10 8-10 8V4zM19 5v14"/>`,frames:`<rect x="2" y="6" width="6" height="12" rx="1"/><rect x="9" y="6" width="6" height="12" rx="1"/><rect x="16" y="6" width="6" height="12" rx="1"/>`,onion:`<rect x="3" y="3" width="13" height="13" rx="2" stroke-dasharray="2 2"/><rect x="8" y="8" width="13" height="13" rx="2"/>`,film:`<rect x="3" y="3" width="18" height="18" rx="2"/><path d="M7 3v18M17 3v18M3 7.5h4M3 12h18M3 16.5h4M17 7.5h4M17 16.5h4"/>`,volume:`<path d="M11 4.7a.7.7 0 0 0-1.2-.5L6.4 7.6A1.4 1.4 0 0 1 5.4 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.4a1.4 1.4 0 0 1 1 .4l3.4 3.4a.7.7 0 0 0 1.2-.5z"/><path d="M16 9a5 5 0 0 1 0 6"/><path d="M19.4 18.4a9 9 0 0 0 0-12.7"/>`,search:`<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>`,wave:`<path d="M2 13a2 2 0 0 0 2-2V7a2 2 0 0 1 4 0v13a2 2 0 0 0 4 0V4a2 2 0 0 1 4 0v13a2 2 0 0 0 4 0v-4a2 2 0 0 1 2-2"/>`,shuffle:`<path d="m18 14 4 4-4 4"/><path d="m18 2 4 4-4 4"/><path d="M2 18h1.97a4 4 0 0 0 3.3-1.7l5.46-7.6a4 4 0 0 1 3.3-1.7H22"/><path d="M2 6h1.97a4 4 0 0 1 3.3 1.7l.67.94"/><path d="M22 18h-6.03a4 4 0 0 1-3.3-1.7l-.67-.94"/>`,layers:`<path d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z"/><path d="m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65"/><path d="m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65"/>`,mic:`<rect x="9" y="2" width="6" height="12" rx="3"/><path d="M19 10v1a7 7 0 0 1-14 0v-1M12 18v4M8 22h8"/>`,music:`<path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/>`,stop:`<rect x="5" y="5" width="14" height="14" rx="2"/>`,repeat:`<path d="m17 2 4 4-4 4"/><path d="M3 11v-1a4 4 0 0 1 4-4h14"/><path d="m7 22-4-4 4-4"/><path d="M21 13v1a4 4 0 0 1-4 4H3"/>`,comment:`<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>`},o=e=>{let t=n(`span`,{class:`icon`,attrs:{"aria-hidden":`true`}});return t.innerHTML=`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${a[e]}</svg>`,t},s=()=>{let e=n(`span`,{class:`mark`,attrs:{"aria-hidden":`true`}});return e.innerHTML=`<svg viewBox="0 0 100 100"><g fill="currentColor"><rect x="6" y="6" width="40" height="40" rx="6"/><rect x="54" y="6" width="40" height="40" rx="6"/><rect x="6" y="54" width="40" height="40" rx="6"/></g><rect x="62" y="62" width="24" height="24" rx="4" fill="#FF5A36"/></svg>`,e},c=(e,t,r={})=>{let i=[`btn`,r.variant??`secondary`];return r.small===!0&&i.push(`small`),r.iconOnly===!0&&i.push(`icon-only`),r.class!==void 0&&i.push(r.class),n(`button`,{class:i.join(` `),title:r.title??(r.iconOnly===!0?e:void 0),attrs:{type:`button`,disabled:r.disabled===!0,"aria-pressed":r.pressed===void 0?void 0:String(r.pressed),"aria-label":r.iconOnly===!0?e:void 0},on:{click:t}},r.icon!==void 0&&o(r.icon),r.iconOnly!==!0&&n(`span`,{},e))},l=(e,t,r,i={})=>n(`div`,{class:`segmented${i.class===void 0?``:` ${i.class}`}`,attrs:{role:`group`,"aria-label":i.label}},t.map(t=>n(`button`,{class:t.id===e?`on`:``,title:t.title??(t.iconOnly===!0?t.label:void 0),attrs:{type:`button`,"aria-pressed":String(t.id===e),"aria-label":t.iconOnly===!0?t.label:void 0},on:{click:()=>r(t.id)}},t.icon!==void 0&&o(t.icon),t.iconOnly!==!0&&n(`span`,{},t.label)))),u=(e,t,r)=>n(`div`,{class:`prop`,title:r},n(`span`,{class:`prop-label`},e),n(`div`,{class:`prop-control`},t)),d=1e4,f=(t,r,i={})=>{let a=t===void 0?``:String(t),o=e=>Math.min(i.max??1/0,Math.max(i.min??-1/0,Math.round(e*d)/d)),s=(e,n)=>{let i=o(n);e.value=String(i),i!==t&&r(i)};return n(`label`,{class:`input`},i.prefix!==void 0&&n(`span`,{class:`affix`},i.prefix),n(`input`,{props:{type:`text`,value:a},attrs:{inputmode:`decimal`,autocomplete:`off`,spellcheck:`false`,placeholder:i.placeholder,"aria-label":i.label??i.prefix,title:`Math works: 1015/51, or *2 to double the value`},on:{change:n=>{let i=n.currentTarget;if(!(i instanceof HTMLInputElement))return;if(i.value.trim()===``){r(0);return}let o=e(i.value,t);o===void 0?i.value=a:s(i,o)},keydown:n=>{let r=n.currentTarget;if(r instanceof HTMLInputElement){if(n.key===`Enter`)r.blur();else if(n.key===`Escape`)r.value=a,r.blur();else if(n.key===`ArrowUp`||n.key===`ArrowDown`){n.preventDefault();let a=e(r.value,t)??t??0,o=(i.step??.01)*(n.shiftKey?10:1);s(r,a+(n.key===`ArrowUp`?o:-o))}}}}}),i.suffix!==void 0&&n(`span`,{class:`affix`},i.suffix))},p=(e,t,r={})=>n(`label`,{class:`input`},n(`input`,{props:{type:`text`,value:e},attrs:{placeholder:r.placeholder,"aria-label":r.label,spellcheck:`false`},on:{change:e=>{let n=e.currentTarget;n instanceof HTMLInputElement&&t(n.value)},keydown:e=>{e.key===`Enter`&&e.currentTarget instanceof HTMLInputElement&&e.currentTarget.blur()}}})),m=(e,t,r,i)=>n(`label`,{class:`input select`},n(`select`,{attrs:{"aria-label":i},on:{change:e=>{let n=e.currentTarget;if(!(n instanceof HTMLSelectElement))return;let i=t.find(e=>e.id===n.value);i!==void 0&&r(i.id)}}},t.map(t=>n(`option`,{props:{value:t.id,selected:t.id===e}},t.label))),o(`chevron`)),h=(e,t,r,i)=>n(`label`,{class:`switch-row`,title:i},n(`span`,{},e),n(`input`,{class:`switch`,props:{type:`checkbox`,checked:t},attrs:{role:`switch`},on:{change:e=>{let t=e.currentTarget;t instanceof HTMLInputElement&&r(t.checked)}}})),g=(e,t,r,i,a,o,s)=>{let c=n(`output`,{},i(t)),l=e=>{let t=(Number(e.value)-r.min)/Math.max(1e-9,r.max-r.min);e.style.setProperty(`--fill`,`${Math.round(t*1e3)/10}%`)},u=n(`input`,{class:`range`,props:{type:`range`,min:String(r.min),max:String(r.max),step:String(r.step),value:String(t)},attrs:{"aria-label":e},on:{input:e=>{let t=e.currentTarget;t instanceof HTMLInputElement&&(c.textContent=i(Number(t.value)),l(t),a(Number(t.value)))},change:()=>o()}});return l(u),n(`div`,{class:`prop slider`,title:s},n(`span`,{class:`prop-label`},e),n(`div`,{class:`prop-control`},u,c))},_=(...e)=>n(`p`,{class:`hint`},...e),ee=(e,t,r)=>n(`span`,{class:`badge ${t}`},r!==void 0&&o(r),e),v=(e,...t)=>n(`div`,{class:`callout ${e}`},o(e===`good`?`check`:e===`neutral`||e===`accent`?`info`:`alert`),n(`div`,{},...t)),y=`ritmo-tools:sections`,te=(()=>{try{let e=localStorage.getItem(y),t=e===null?{}:JSON.parse(e);return typeof t!=`object`||!t||Array.isArray(t)?new Map:new Map(Object.entries(t).filter(e=>typeof e[1]==`boolean`))}catch{return new Map}})(),ne=new Set,re=()=>{try{localStorage.setItem(y,JSON.stringify(Object.fromEntries(te)))}catch{}},b=(e,...t)=>{let r=te.get(e.id)??e.closed!==!0,i=ne.has(e.id);return n(`section`,{class:`section${r?``:` closed`}`,attrs:{"data-section":e.id}},n(`header`,{class:`section-head`},n(`button`,{class:`section-toggle`,attrs:{type:`button`,"aria-expanded":String(r)},on:{click:()=>{te.set(e.id,!r),re(),e.onToggle()}}},o(`chevron`),n(`h2`,{},e.title)),r&&e.help!==void 0&&n(`button`,{class:`btn ghost icon-only small${i?` on`:``}`,title:i?`Hide help`:`What is this?`,attrs:{type:`button`,"aria-pressed":String(i),"aria-label":`Help`},on:{click:()=>{i?ne.delete(e.id):ne.add(e.id),e.onToggle()}}},o(`info`)),r&&e.action!==void 0&&n(`div`,{class:`section-action`},e.action)),r&&n(`div`,{class:`section-body`},i&&e.help!==void 0&&n(`p`,{class:`help`},e.help),...t.flat()))},ie=(e,...t)=>n(`div`,{class:`group`},n(`h3`,{},e),...t.flat()),ae=350,oe=200,se=(e,t,n)=>{let r=[],i=[],a,o,s=()=>{o!==void 0&&clearTimeout(o),o=void 0;let t=e();t!==a&&(a!==void 0&&(r.push(a),r.length>oe&&r.shift(),i.length=0),a=t,n())},c=(e,r)=>{s();let i=e.pop();i!==void 0&&a!==void 0&&(r.push(a),a=i,t(i),n())};return{schedule:()=>{o!==void 0&&clearTimeout(o),o=setTimeout(s,ae)},undo:()=>c(r,i),redo:()=>c(i,r),canUndo:()=>r.length>0,canRedo:()=>i.length>0,reset:()=>{o!==void 0&&clearTimeout(o),o=void 0,r.length=0,i.length=0,a=void 0}}},ce=(e,t,n)=>{for(let r=0;r<n.length;r++)e[t+r]=n.charCodeAt(r)},le=(e,t)=>{let n=e.length,r=e[0]?.length??0,i=new Uint8Array(44+r*n*2),a=new DataView(i.buffer);ce(i,0,`RIFF`),a.setUint32(4,36+r*n*2,!0),ce(i,8,`WAVE`),ce(i,12,`fmt `),a.setUint32(16,16,!0),a.setUint16(20,1,!0),a.setUint16(22,n,!0),a.setUint32(24,t,!0),a.setUint32(28,t*n*2,!0),a.setUint16(32,n*2,!0),a.setUint16(34,16,!0),ce(i,36,`data`),a.setUint32(40,r*n*2,!0);for(let t=0;t<r;t++)for(let r=0;r<n;r++){let i=Math.max(-1,Math.min(1,e[r][t]));a.setInt16(44+(t*n+r)*2,Math.round(i*32767),!0)}return i},ue=e=>le([e.left,e.right],e.sampleRate),de=(e,t)=>le([e],t),x={piano:{label:`Piano`,kind:`melodic`,about:`A grand piano: chords, melodies, bass. Warm and full; notes ring and fade.`,range:[21,108],program:0,color:`#7aa2f7`},epiano:{label:`Electric piano`,kind:`melodic`,about:`A soft electric piano (Rhodes): mellow chords, lo-fi and cozy tunes.`,range:[28,103],program:4,color:`#9d8cf7`},guitar:{label:`Acoustic guitar`,kind:`melodic`,about:`A plucked steel-string guitar: strummed chords, picked patterns (arpeggios), folk melodies.`,range:[40,88],program:25,color:`#f0a843`},bass:{label:`Bass guitar`,kind:`melodic`,about:`A plucked electric bass: the low line under the chords, one note at a time.`,range:[28,60],program:33,color:`#e06c75`},strings:{label:`Strings`,kind:`melodic`,about:`A string section: long held chords that swell in, sweeping melodies. Slow to start, so give it long notes.`,range:[28,96],program:48,color:`#56b6c2`},flute:{label:`Flute`,kind:`melodic`,about:`A breathy wooden flute: gentle melodies, one note at a time, best in its middle and high notes.`,range:[60,96],program:73,color:`#98c379`},marimba:{label:`Marimba`,kind:`melodic`,about:`Wooden bars struck by mallets: short bright notes, playful patterns.`,range:[45,96],program:12,color:`#d19a66`},bells:{label:`Bells`,kind:`melodic`,about:`Glassy bells: sparkle, magic, a few long high notes over the rest.`,range:[60,108],program:9,color:`#e5c07b`},pad:{label:`Warm pad`,kind:`melodic`,about:`A soft synth pad: a cushion of held chords behind everything; slow to swell.`,range:[36,96],program:89,color:`#c678dd`},chipLead:{label:`Chip lead`,kind:`melodic`,about:`A retro game-console square wave: chiptune melodies, one note at a time.`,range:[48,96],program:80,color:`#4cc38a`},chipBass:{label:`Chip bass`,kind:`melodic`,about:`A retro game-console triangle wave: chiptune bass lines.`,range:[28,72],program:38,color:`#2fb3a3`},rockGuitar:{label:`Distorted guitar`,kind:`melodic`,about:`An electric guitar through an overdriven amp: rock riffs, power chords (a note and the one seven semitones up), crunchy rhythm parts.`,range:[40,88],program:30,color:`#ff7b54`},reese:{label:`Reese bass`,kind:`melodic`,about:`Detuned saws, a sub, and grit, slowly moving: the drum & bass bass, also for dark electronic lines. One note at a time; hold notes long.`,range:[28,60],program:38,color:`#b4475a`},synthLead:{label:`Synth lead`,kind:`melodic`,about:`A wide stack of detuned saws (a supersaw): modern electronic leads and riffs, bright and big. One note at a time.`,range:[48,96],program:81,color:`#3fb6f2`},drums:{label:`Drum kit`,kind:`drums`,about:`A clean drum kit. Its notes are drums, not pitches: kick, snare, hat, openHat, clap, tomLow, tomHigh, crash, rim, shaker.`,range:[0,127],program:0,color:`#bdbab3`},breaks:{label:`Punchy kit`,kind:`drums`,about:`A hard-hitting kit for rock, drum & bass, and electronic music: a clicky, driven kick, a cracking snare, crisp hats. The same drums as the drum kit.`,range:[0,127],program:0,color:`#e8e3d5`}},fe=[`piano`,`epiano`,`guitar`,`rockGuitar`,`bass`,`reese`,`strings`,`flute`,`marimba`,`bells`,`pad`,`synthLead`,`chipLead`,`chipBass`,`drums`,`breaks`],pe=e=>x[e].kind===`drums`,S=[{name:`crash`,label:`Crash`,gm:49},{name:`openHat`,label:`Open hat`,gm:46},{name:`hat`,label:`Hat`,gm:42},{name:`shaker`,label:`Shaker`,gm:70},{name:`tomHigh`,label:`High tom`,gm:50},{name:`tomLow`,label:`Low tom`,gm:45},{name:`rim`,label:`Rim`,gm:37},{name:`clap`,label:`Clap`,gm:39},{name:`snare`,label:`Snare`,gm:38},{name:`kick`,label:`Kick`,gm:36}],me=S.map(e=>e.name),he=e=>me.includes(e),C=2*Math.PI,ge=(e,t)=>{if(e<t){let n=e/t;return n+n-n*n-1}if(e>1-t){let n=(e-1)/t;return n*n+n+n+1}return 0},_e=(e,t)=>2*e-1-ge(e,t),ve=(e,t,n)=>(e<n?1:-1)+ge(e,t)-ge((e+1-n)%1,t),ye=(e,t)=>{let n=Math.exp(-C*e/t),r=0;return e=>r=e*(1-n)+r*n},w=(e,t)=>{let n=1/t,r=0,i=0,a=0,o=0,s=0;return{set:t=>{let r=Math.tan(Math.PI*Math.min(t,e*.45)/e);a=1/(1+r*(r+n)),o=r*a,s=r*o},step:e=>{let t=e-i,c=a*r+o*t,l=i+o*r+s*t;return r=2*c-r,i=2*l-i,{low:l,band:c,high:e-n*c-l}}}},T=(e,t,n,r)=>{let i=t<=0?1:Math.min(1,e/t);return e<=n?i:i*Math.max(0,1-(e-n)/r)},be=(e,t)=>Math.tanh(e*t)/Math.tanh(t),E=e=>440*2**((e-69)/12),xe=({midi:e,held:t,velocity:n,rate:r,random:i})=>{let a=E(e),o=Math.max(0,Math.min(1,(72-e)/48)),s=1.2+4*o,c=.18+.2*o,l=Math.min(t+c,s+.2),u=new Float32Array(Math.ceil(l*r)),d=2e-4+4e-4*(1-o),f=Math.min(12,Math.floor(9e3/a)),p=.6+.9*n;for(let e=1;e<=f;e++){let t=a*e*Math.sqrt(1+d*e*e),n=e**-(2.3-p)*(e===1?1:.8),o=3+e*1.6,c=(.9+.35*e)/s;for(let e of[1,1.0009]){let a=C*t*e/r,s=Math.cos(a),l=Math.sin(a),d=i()*C,f=Math.cos(d),p=Math.sin(d),m=.6*n*.5,h=.4*n*.5,g=Math.exp(-o/r),_=Math.exp(-c*3/r);for(let e=0;e<u.length;e++){u[e]+=p*(m+h);let t=f*s-p*l;if(p=f*l+p*s,f=t,m*=g,h*=_,m+h<1e-5)break}}}let m=ye(800+1500*n,r);for(let e=0;e<Math.min(u.length,r*.02);e++)u[e]+=m(i()*2-1)*.3*n*(1-e/(r*.02));for(let e=0;e<u.length;e++)u[e]*=T(e/r,.002,t,c)*.5;return u},Se=({midi:e,held:t,velocity:n,rate:r})=>{let i=E(e),a=.25,o=Math.min(t+a,3.5),s=new Float32Array(Math.ceil(o*r)),c=0,l=0,u=0;for(let o=0;o<s.length;o++){let d=o/r,f=(.6+2.2*n)*Math.exp(-d*2.2)+.25,p=.35*n*Math.exp(-d*18),m=Math.sin(c+f*Math.sin(l))+p*Math.sin(u);s[o]=m*Math.exp(-d*(.5+e/120))*T(d,.003,t,a)*.32,c+=C*i/r,l+=C*i/r,u+=C*i*14/r}return s},Ce=({midi:e,held:t,velocity:n,rate:r,random:i},a,o,s)=>{let c=E(e),l=Math.min(t+s,o+.1),u=new Float32Array(Math.ceil(l*r)),d=r/c,f=Math.floor(d-.5),p=d-.5-f,m=(1-p)/(1+p),h=new Float64Array(f+2),g=ye(c*(2+10*a*(.4+.6*n)),r);for(let e=0;e<h.length;e++)h[e]=g(i()*2-1);let _=0;for(let e of h)_+=e/h.length;for(let e=0;e<h.length;e++)h[e]-=_;let ee=.001**(1/(c*o*(1.2-Math.min(.6,e/160)))),v=0,y=0,te=0;for(let e=0;e<u.length;e++){let n=e/r,i=h[v],a=.5*(i+h[(v+1)%h.length])*(n>t?ee*.985:ee),o=m*a+y-m*te;y=a,te=o,h[v]=o,v=(v+1)%h.length,u[e]=i}for(let e=0;e<u.length;e++)u[e]*=T(e/r,.001,t,s)*.55;return u},we=e=>{let t=Ce(e,.55,3,.12);for(let e=0;e<t.length;e++)t[e]*=2.4;let n=w(e.rate,1.2);n.set(210);for(let e=0;e<t.length;e++)t[e]=t[e]+n.step(t[e]).band*.25;return t},Te=e=>{let t=Ce(e,.12,2.5,.08),n=E(e.midi),r=0;for(let i=0;i<t.length;i++){let a=i/e.rate;t[i]=t[i]*1.2+Math.sin(r)*.22*Math.exp(-a*1.5)*T(a,.004,e.held,.08),r+=C*n/e.rate}return t},Ee=({midi:e,held:t,velocity:n,rate:r,random:i},a,o,s,c,l,u)=>{let d=E(e),f=new Float32Array(Math.ceil((t+s)*r)),p=w(r,.8),m=a.map(()=>i());for(let e=0;e<f.length;e++){let i=e/r,h=Math.min(1,i/o),g=1+l*Math.min(1,Math.max(0,i-.25)*2)*Math.sin(C*5.2*i),_=0;a.forEach((e,t)=>{let n=d*g*2**(e/1200)/r;_+=_e(m[t],n),m[t]=(m[t]+n)%1}),e%32==0&&p.set(d*1.5+c*(.5+.5*n)*(.35+.65*h)),f[e]=p.step(_/a.length).low*T(i,o,t,s)*u}return f},De=e=>Ee(e,[-9,-3,4,10],.22,.45,2600,.004,.5),Oe=e=>Ee(e,[-12,0,7,1200],.55,.9,1100,.002,.65),ke=({midi:e,held:t,velocity:n,rate:r,random:i})=>{let a=E(e),o=.12,s=new Float32Array(Math.ceil((t+o)*r)),c=w(r,4);c.set(a*2);let l=0;for(let e=0;e<s.length;e++){let u=e/r,d=1+.004*Math.min(1,Math.max(0,u-.3)*3)*Math.sin(C*5*u),f=Math.sin(l)+.22*Math.sin(2*l)+.06*Math.sin(3*l),p=Math.exp(-u*30)*.6,m=c.step(i()*2-1).band*(.12+p);s[e]=(f+m)*T(u,.05,t,o)*(.13+.08*n),l+=C*a*d/r}return s},Ae=({midi:e,held:t,velocity:n,rate:r,random:i})=>{let a=E(e),o=.4+1.4*Math.max(0,(84-e)/40),s=new Float32Array(Math.ceil(Math.min(t+.15,o)*r)),c=ye(3e3,r);for(let e=0;e<s.length;e++){let l=e/r,u=Math.sin(C*a*l)*Math.exp(-l*3/o)+.25*n*Math.sin(C*a*4*l)*Math.exp(-l*22)+.1*Math.sin(C*a*10*l)*Math.exp(-l*60),d=e<r*.004?c(i()*2-1)*.4:0;s[e]=(u+d)*T(l,.001,t,.15)*(.25+.15*n)}return s},je=({midi:e,held:t,velocity:n,rate:r})=>{let i=E(e),a=new Float32Array(Math.ceil(Math.min(t+1.2,3.5)*r));for(let e=0;e<a.length;e++){let o=e/r,s=(1+2.5*n)*Math.exp(-o*3),c=Math.sin(C*i*o+s*Math.sin(C*i*3.5*o))*Math.exp(-o*1.3);a[e]=c*T(o,.001,t,1.2)*.28}return a},Me=({midi:e,held:t,velocity:n,rate:r})=>{let i=E(e),a=new Float32Array(Math.ceil((t+.04)*r)),o=0;for(let e=0;e<a.length;e++){let s=e/r,c=i*(1+.006*Math.min(1,Math.max(0,s-.2)*4)*Math.sin(C*6*s))/r;a[e]=ve(o,c,.25)*T(s,.003,t,.04)*(.09+.07*n),o=(o+c)%1}return a},Ne=({midi:e,held:t,velocity:n,rate:r})=>{let i=E(e),a=new Float32Array(Math.ceil((t+.03)*r)),o=0;for(let e=0;e<a.length;e++){let s=e/r,c=o<.5?4*o-1:3-4*o;a[e]=Math.round(c*8)/8*T(s,.002,t,.03)*(.2+.1*n),o=(o+i/r)%1}return a},Pe=e=>{let t=Ce(e,.85,4,.1),n=w(e.rate,.7);n.set(110);let r=w(e.rate,.9);r.set(4200);let i=w(e.rate,1.6);i.set(1800);let a=10+14*e.velocity;for(let o=0;o<t.length;o++){let s=be(n.step(t[o]*3).high,a),c=r.step(s).low;t[o]=(c+i.step(c).band*.35)*T(o/e.rate,.002,e.held,.1)*.3}return t},Fe=({midi:e,held:t,velocity:n,rate:r,random:i})=>{let a=E(e),o=.06,s=new Float32Array(Math.ceil((t+o)*r)),c=w(r,1.1),l=[-10,0,9],u=l.map(()=>i()),d=i()*C,f=0;for(let e=0;e<s.length;e++){let i=e/r,p=0;l.forEach((e,t)=>{let n=a*2**(e/1200)/r;p+=_e(u[t],n),u[t]=(u[t]+n)%1}),e%32==0&&c.set(a*3+(500+700*n)*(1+.6*Math.sin(C*.35*i+d)));let m=be(c.step(p/3).low*1.6,2.2);s[e]=(m*.55+Math.sin(f)*.6)*T(i,.004,t,o)*(.2+.08*n),f+=C*a/r}return s},Ie=({midi:e,held:t,velocity:n,rate:r,random:i})=>{let a=E(e),o=.12,s=new Float32Array(Math.ceil((t+o)*r)),c=w(r,.9),l=[-21,-13,-6,0,6,13,21],u=l.map(()=>i());for(let e=0;e<s.length;e++){let i=e/r,d=1+.004*Math.min(1,Math.max(0,i-.25)*3)*Math.sin(C*5.5*i),f=0;l.forEach((e,t)=>{let n=a*d*2**(e/1200)/r;f+=_e(u[t],n)*(e===0?1.4:1),u[t]=(u[t]+n)%1}),e%32==0&&c.set(a*2+(2500+4e3*n)*(.45+.55*Math.exp(-i*6))),s[e]=c.step(f/Math.sqrt(l.length)).low*T(i,.004,t,o)*(.17+.07*n)}return s},D=(e,t,n,r)=>{let i=w(t,r.q??.9),a=Math.round((r.delay??0)*t);for(let o=a;o<e.length;o++){let s=(o-a)/t;o%16==0&&i.set(r.from*(r.to/r.from)**+Math.min(1,s/r.decay));let c=i.step(n()*2-1),l=r.type===`low`?c.low:r.type===`band`?c.band:c.high;e[o]+=l*r.gain*Math.exp(-s/(r.decay/4.6))}},O=(e,t,n)=>{let r=0;for(let i=0;i<e.length;i++){let a=i/t,o=n.to+(n.from-n.to)*Math.exp(-a/n.glide);e[i]+=Math.sin(r)*n.gain*Math.exp(-a/(n.decay/4.6)),r+=C*o/t}},Le=(e,t,n,r)=>{let i=new Float32Array(Math.ceil((e===`crash`?1.6:e===`openHat`?.45:e===`kick`||e===`tomLow`?.5:.3)*n)),a=.4+.6*t;switch(e){case`kick`:O(i,n,{from:160,to:48,glide:.03,decay:.45,gain:.9*a}),D(i,n,r,{from:3e3,to:1e3,decay:.012,gain:.25*a,type:`low`});break;case`snare`:O(i,n,{from:260,to:185,glide:.02,decay:.12,gain:.35*a}),D(i,n,r,{from:5e3,to:3500,decay:.2,gain:.45*a,type:`high`});break;case`hat`:D(i,n,r,{from:9e3,to:8e3,decay:.05,gain:.25*a,type:`high`});break;case`openHat`:D(i,n,r,{from:8500,to:7e3,decay:.4,gain:.2*a,type:`high`});break;case`shaker`:D(i,n,r,{from:6500,to:6e3,decay:.07,gain:.18*a,type:`band`,q:1.5});break;case`clap`:for(let e of[0,.011,.022])D(i,n,r,{from:1300,to:1100,decay:.03,gain:.35*a,type:`band`,q:1.4,delay:e});D(i,n,r,{from:1200,to:900,decay:.2,gain:.25*a,type:`band`,q:1.2,delay:.03});break;case`rim`:O(i,n,{from:1700,to:1600,glide:.01,decay:.03,gain:.3*a}),D(i,n,r,{from:3e3,to:2500,decay:.015,gain:.25*a,type:`band`,q:2});break;case`tomLow`:O(i,n,{from:140,to:90,glide:.08,decay:.45,gain:.6*a});break;case`tomHigh`:O(i,n,{from:220,to:150,glide:.06,decay:.3,gain:.5*a});break;case`crash`:D(i,n,r,{from:7e3,to:4e3,decay:1.5,gain:.22*a,type:`high`});for(let e of[1,1.47,2.09,2.56])O(i,n,{from:420*e,to:420*e,glide:1,decay:.9,gain:.02*a})}let o=Math.floor(n*.01);for(let e=0;e<o;e++)i[i.length-1-e]*=e/o;return i},Re=(e,t,n,r)=>{let i=.4+.6*t,a,o=1.6;switch(e){case`kick`:a=new Float32Array(Math.ceil(.45*n)),O(a,n,{from:240,to:46,glide:.022,decay:.4,gain:1*i}),D(a,n,r,{from:9e3,to:4e3,decay:.006,gain:.5*i,type:`high`}),o=2.6;break;case`snare`:a=new Float32Array(Math.ceil(.3*n)),O(a,n,{from:330,to:200,glide:.015,decay:.1,gain:.45*i}),O(a,n,{from:520,to:330,glide:.01,decay:.05,gain:.2*i}),D(a,n,r,{from:7e3,to:2500,decay:.22,gain:.7*i,type:`band`,q:.6}),o=2.4;break;case`hat`:a=new Float32Array(Math.ceil(.12*n)),D(a,n,r,{from:11e3,to:9e3,decay:.035,gain:.32*i,type:`high`,q:1.2});break;case`openHat`:a=new Float32Array(Math.ceil(.4*n)),D(a,n,r,{from:1e4,to:8e3,decay:.32,gain:.26*i,type:`high`,q:1.2});break;default:a=Le(e,t,n,r),o=1.4}for(let e=0;e<a.length;e++)a[e]=be(a[e],o);let s=Math.floor(n*.01);for(let e=0;e<s;e++)a[a.length-1-e]*=e/s;return a},ze={piano:xe,epiano:Se,guitar:we,rockGuitar:Pe,bass:Te,reese:Fe,strings:De,flute:ke,marimba:Ae,bells:je,pad:Oe,synthLead:Ie,chipLead:Me,chipBass:Ne},Be=(e,t,n,r,i,a)=>ze[e]({midi:t,held:Math.max(.02,n),velocity:r,rate:i,random:a}),Ve=(e,t,n,r,i)=>e===`breaks`?Re(t,n,r,i):Le(t,n,r,i),He=[`C`,`C#`,`D`,`D#`,`E`,`F`,`F#`,`G`,`G#`,`A`,`A#`,`B`],Ue={C:0,D:2,E:4,F:5,G:7,A:9,B:11},k=e=>{let t=/^([A-G])(#|b)?(-?\d)$/.exec(e);if(t===null)return;let n=Ue[t[1]],r=t[2]===`#`?1:t[2]===`b`?-1:0,i=(Number(t[3])+1)*12+n+r;return i>=0&&i<=127?i:void 0},A=e=>`${He[(e%12+12)%12]}${Math.floor(e/12)-1}`,We={major:[0,2,4,5,7,9,11],minor:[0,2,3,5,7,8,10],"harmonic minor":[0,2,3,5,7,8,11],dorian:[0,2,3,5,7,9,10],phrygian:[0,1,3,5,7,8,10],lydian:[0,2,4,6,7,9,11],mixolydian:[0,2,4,5,7,9,10],"major pentatonic":[0,2,4,7,9],"minor pentatonic":[0,3,5,7,10],blues:[0,3,5,6,7,10]},Ge=[`major`,`minor`,`harmonic minor`,`dorian`,`phrygian`,`lydian`,`mixolydian`,`major pentatonic`,`minor pentatonic`,`blues`],Ke=e=>{let t=/^([A-G])(#|b)? (.+)$/.exec(e.trim());if(t===null)return;let n=Ge.find(e=>e===t[3]);if(n===void 0)return;let r=(Ue[t[1]]+(t[2]===`#`?1:t[2]===`b`?-1:0)+12)%12;return{root:r,scale:n,classes:We[n].map(e=>(r+e)%12)}},qe=(e,t)=>e.classes.includes((t%12+12)%12),j=e=>60/e.tempo,M=e=>e.bars*e.beatsPerBar,Je=e=>M(e)*j(e),Ye=(e,t,n)=>{let r=Math.floor(t/e.beatsPerBar)+1,i=Math.max(r,Math.ceil(n/e.beatsPerBar));return r===i?`bar ${r}`:`bars ${r}–${i}`},Xe=e=>typeof e==`object`&&!!e&&!Array.isArray(e),Ze=e=>({number:(t,n,r,i,a)=>t===void 0?(r===void 0&&e.push(`${n} is missing`),r??0):typeof t!=`number`||!Number.isFinite(t)?(e.push(`${n} must be a number`),r??0):(i!==void 0&&t<i&&e.push(`${n} must be at least ${i}`),a!==void 0&&t>a&&e.push(`${n} must be at most ${a}`),t),text:(t,n,r)=>t===void 0&&r!==void 0?r:typeof t==`string`?t:(e.push(`${n} must be text`),r??``)}),Qe=(e,t,n,r)=>{if(!Xe(e)){r.push(`${t} must be an object`);return}let{number:i,text:a}=Ze(r),o=a(e.pitch,`${t}.pitch`);return n&&!he(o)&&r.push(`${t}.pitch must be a drum on a drum track: ${me.join(`, `)}`),!n&&k(o)===void 0&&r.push(`${t}.pitch must be a note name like "C4", "F#3", or "Bb5" (got ${JSON.stringify(e.pitch)})`),{at:i(e.at,`${t}.at`,void 0,0),length:i(e.length,`${t}.length`,void 0,1/64,64),pitch:o,velocity:i(e.velocity,`${t}.velocity`,.8,0,1)}},$e=(e,t,n)=>{if(!Xe(e)){n.push(`${t} must be an object`);return}let{number:r,text:i}=Ze(n),a=fe.find(t=>t===e.instrument);a===void 0&&n.push(`${t}.instrument must be one of ${fe.join(`, `)}`);let o=a!==void 0&&x[a].kind===`drums`;Array.isArray(e.notes)||n.push(`${t}.notes must be a list`);let s=(Array.isArray(e.notes)?e.notes:[]).flatMap((e,r)=>Qe(e,`${t}.notes[${r}]`,o,n)??[]);return e.mute!==void 0&&typeof e.mute!=`boolean`&&n.push(`${t}.mute must be true or false`),{name:i(e.name,`${t}.name`,a===void 0?`Track`:x[a].label),instrument:a??`piano`,volume:r(e.volume,`${t}.volume`,.8,0,1.5),pan:r(e.pan,`${t}.pan`,0,-1,1),reverb:r(e.reverb,`${t}.reverb`,.2,0,1),mute:e.mute===!0||void 0,notes:[...s].sort((e,t)=>e.at-t.at)}},et=(e,t,n)=>{if(e===void 0)return;if(!Xe(e)){n.push(`${t} must be an object`);return}let{number:r,text:i}=Ze(n),a=[];for(let[i,o]of(Array.isArray(e.notes)?e.notes:[]).entries())!Xe(o)||typeof o.pitch!=`string`||k(o.pitch)===void 0?n.push(`${t}.notes[${i}] must be {"at", "length", "pitch"}`):a.push({at:r(o.at,`${t}.notes[${i}].at`,0,0),length:r(o.length,`${t}.notes[${i}].length`,.25,0),pitch:o.pitch});let o=Array.isArray(e.contour)?e.contour.filter(e=>typeof e==`number`&&Number.isFinite(e)):[];return{audio:e.audio===void 0?void 0:i(e.audio,`${t}.audio`),tempo:r(e.tempo,`${t}.tempo`,100,20,300),key:i(e.key,`${t}.key`,`C major`),length:r(e.length,`${t}.length`,0,0),notes:a,contour:o,offset:r(e.offset,`${t}.offset`,0),start:r(e.start,`${t}.start`,0,0)}},tt=e=>{if(!Xe(e))return{ok:!1,error:`a song is {"version": 1, "tempo": 96, "key": "A minor", "bars": 8, "tracks": [...]}`};let t=[],{number:n,text:r}=Ze(t),i=r(e.key,`key`,`C major`);Ke(i)===void 0&&t.push(`key must be a note and a scale, like "A minor" or "F# dorian" (scales: ${Ge.join(`, `)})`);let a=n(e.beatsPerBar,`beatsPerBar`,4,2,12);Number.isInteger(a)||t.push(`beatsPerBar must be a whole number`);let o=n(e.bars,`bars`,void 0,1,256);Number.isInteger(o)||t.push(`bars must be a whole number`),Array.isArray(e.tracks)||t.push(`tracks must be a list`);let s=(Array.isArray(e.tracks)?e.tracks:[]).flatMap((e,n)=>$e(e,`tracks[${n}]`,t)??[]);e.loop!==void 0&&typeof e.loop!=`boolean`&&t.push(`loop must be true or false`),e.compressor!==void 0&&typeof e.compressor!=`boolean`&&t.push(`compressor must be true or false`),e.about!==void 0&&typeof e.about!=`string`&&t.push(`about must be text`);let c={about:typeof e.about==`string`?e.about:void 0,tempo:n(e.tempo,`tempo`,void 0,30,300),key:i,beatsPerBar:a,bars:o,loop:e.loop!==!1,master:n(e.master,`master`,.8,0,2),compressor:e.compressor===!0,tracks:s,reference:et(e.reference,`reference`,t)};return t.length===0?{ok:!0,value:c}:{ok:!1,error:t.join(`; `)}},nt=e=>{try{return tt(JSON.parse(e))}catch(e){return{ok:!1,error:`not JSON: ${e instanceof Error?e.message:String(e)}`}}},N=(e,t=4)=>Math.round(e*10**t)/10**t,rt=e=>({at:N(e.at),length:N(e.length),pitch:e.pitch,...e.velocity===.8?{}:{velocity:N(e.velocity,2)}}),it=e=>{let t={version:1,...e.about===void 0?{}:{about:e.about},tempo:N(e.tempo,2),key:e.key,...e.beatsPerBar===4?{}:{beatsPerBar:e.beatsPerBar},bars:e.bars,...e.loop?{}:{loop:!1},master:N(e.master,3),...e.compressor?{compressor:!0}:{}},n=[`{`];for(let[e,r]of Object.entries(t))n.push(` ${JSON.stringify(e)}: ${JSON.stringify(r)},`);if(n.push(` "tracks": [`),e.tracks.forEach((t,r)=>{let i={name:t.name,instrument:t.instrument,volume:N(t.volume,3),...t.pan===0?{}:{pan:N(t.pan,3)},reverb:N(t.reverb,3),...t.mute===!0?{mute:!0}:{}};n.push(` {`);for(let[e,t]of Object.entries(i))n.push(` ${JSON.stringify(e)}: ${JSON.stringify(t)},`);let a=[...t.notes].sort((e,t)=>e.at-t.at||(k(e.pitch)??0)-(k(t.pitch)??0));a.length===0?n.push(` "notes": []`):(n.push(` "notes": [`),a.forEach((e,t)=>n.push(` ${JSON.stringify(rt(e))}${t===a.length-1?``:`,`}`)),n.push(` ]`)),n.push(` }${r===e.tracks.length-1?``:`,`}`)}),e.reference===void 0)n.push(` ]`);else{let t=e.reference;n.push(` ],`),n.push(` "reference": {`);let r={...t.audio===void 0?{}:{audio:t.audio},tempo:N(t.tempo,1),key:t.key,length:N(t.length,2),...t.offset===0?{}:{offset:N(t.offset)},start:N(t.start,3)};for(let[e,t]of Object.entries(r))n.push(` ${JSON.stringify(e)}: ${JSON.stringify(t)},`);n.push(` "notes": [${t.notes.map(e=>JSON.stringify({at:N(e.at),length:N(e.length),pitch:e.pitch})).join(`, `)}],`),n.push(` "contour": [${t.contour.map(e=>N(e,1)).join(`,`)}]`),n.push(` }`)}return n.push(`}`),n.join(`
2
+ `)+`
3
+ `},at=44100,ot=e=>{let t=e>>>0;return()=>{t=t+1831565813>>>0;let e=t;return e=Math.imul(e^e>>>15,e|1),e^=e+Math.imul(e^e>>>7,e|61),((e^e>>>14)>>>0)/4294967296}},st=e=>{let t=2166136261;for(let n=0;n<e.length;n++)t=Math.imul(t^e.charCodeAt(n),16777619);return t>>>0},ct=(e,t,n,r,i)=>{let a=`${e}|${t.pitch}|${Math.round(n*1e3)}|${Math.round(t.velocity*100)}|${r}`,o=i?.get(a);if(o!==void 0)return o;let s=Math.round(t.velocity*100)/100,c=ot(st(a)),l=k(t.pitch),u=pe(e)?he(t.pitch)?Ve(e,t.pitch,s,r,c):new Float32Array:l===void 0?new Float32Array:Be(e,l,n,s,r,c);return i!==void 0&&(i.size>4e3&&i.clear(),i.set(a,u)),u},lt=[1116,1188,1277,1356,1422,1491,1557,1617],ut=[556,441,341,225],dt=23,ft=(e,t,n)=>{let r=t/44100,i=new Float32Array(e.length);for(let t of lt){let a=new Float32Array(Math.round((t+n)*r)),o=0,s=0;for(let t=0;t<e.length;t++){let n=a[o];s=n*.7+s*.3,a[o]=e[t]+s*.84,o=(o+1)%a.length,i[t]+=n}}for(let e of ut){let t=new Float32Array(Math.round((e+n)*r)),a=0;for(let e=0;e<i.length;e++){let n=t[a],r=i[e];t[a]=r+n*.5,i[e]=n-r,a=(a+1)%t.length}}return i},pt=.18,mt=4,ht=2.2,gt=.89,_t=(e,t,n,r)=>{let i=Math.exp(-1/(.004*n)),a=Math.exp(-1/(.15*n)),o=0,s=n=>{let r=Math.max(Math.abs(e[n]),Math.abs(t[n]));return o=r>o?r+(o-r)*i:r+(o-r)*a,o};if(r)for(let t=Math.max(0,e.length-Math.round(n));t<e.length;t++)s(t);let c=e=>Math.abs(e)<gt*.7?e:Math.sign(e)*(gt*.7+gt*.3*Math.tanh((Math.abs(e)-gt*.7)/(gt*.3)));for(let n=0;n<e.length;n++){let r=s(n),i=(r>pt?(r/pt)**(1/mt-1):1)*ht;e[n]=c(e[n]*i),t[n]=c(t[n]*i)}},vt=3,yt=.12,bt=(e,t,n,r,i,a,o)=>{let s=j(t),c=(e.pan+1)*Math.PI/4,l=Math.cos(c)*e.volume,u=Math.sin(c)*e.volume;for(let t of e.notes){let c=ct(e.instrument,t,t.length*s,n,o),d=Math.round(t.at*s*n),f=Math.min(r.length,d+c.length);for(let t=d;t<f;t++){let n=c[t-d];r[t]+=n*l,i[t]+=n*u,a[t]+=n*e.volume*e.reverb}}},xt=(e,t={})=>{let n=t.sampleRate??44100,r=Math.round(Je(e)*n),i=r+Math.round(vt*n),a=new Float32Array(i),o=new Float32Array(i),s=new Float32Array(i);if(e.tracks.forEach((r,i)=>{(t.tracks===void 0?r.mute!==!0:t.tracks.includes(i))&&bt(r,e,n,a,o,s,t.cache)}),s.some(e=>e!==0)){let e=ft(s,n,0),t=ft(s,n,dt);for(let n=0;n<i;n++)a[n]+=e[n]*yt,o[n]+=t[n]*yt}for(let t=0;t<i;t++)a[t]*=e.master,o[t]*=e.master;if(!e.loop){let t=i;for(;t>r&&Math.abs(a[t-1])<1e-4&&Math.abs(o[t-1])<1e-4;)t--;let s={sampleRate:n,left:a.slice(0,t),right:o.slice(0,t)};return e.compressor&&_t(s.left,s.right,n,!1),s}for(let e=r;e<i;e++){let t=(e-r)%r;a[t]+=a[e],o[t]+=o[e]}let c={sampleRate:n,left:a.slice(0,r),right:o.slice(0,r)};return e.compressor&&_t(c.left,c.right,n,!0),c},St=({left:e,right:t})=>{let n=0;for(let r=0;r<e.length;r++)n=Math.max(n,Math.abs(e[r]),Math.abs(t[r]));return n===0?-120:Math.round(20*Math.log10(n)*10)/10},Ct=({left:e,right:t})=>{let n=0;for(let r=0;r<e.length;r++)n+=e[r]*e[r]+t[r]*t[r];let r=Math.sqrt(n/Math.max(1,e.length*2));return r===0?-120:Math.round(20*Math.log10(r)*10)/10},wt={clipping:-.1,quiet:-30,buried:18,follows:.5},Tt=e=>Math.round(e*10)/10,Et=e=>{let t=e.reference;if(t===void 0||t.notes.length===0)return;let n;for(let r of e.tracks){if(x[r.instrument].kind===`drums`)continue;let e=0;for(let n of t.notes){let i=k(n.pitch),a=n.at+t.offset+Math.min(n.length,.25)/2;r.notes.some(e=>e.at<=a&&a<e.at+e.length&&(((k(e.pitch)??-1)-(i??0))%12+12)%12==0)&&e++}let i=Math.round(e/t.notes.length*100)/100;(n===void 0||i>n.share)&&(n={track:r.name,share:i})}return n},Dt=(e,t)=>{let n=[],r=Ke(e.key),i=M(e),a=xt(e,{cache:t}),o=St(a),s=Ct(a),c=e.tracks.map((a,o)=>{let s=x[a.instrument],c=s.kind===`melodic`,l=c?a.notes.flatMap(e=>k(e.pitch)??[]):[];if(a.notes.length===0&&n.push({rule:`empty-track`,severity:`warning`,track:a.name,message:`Has no notes.`}),c&&r!==void 0){let t=a.notes.filter(e=>!qe(r,k(e.pitch)??0));if(t.length>0){let r=t.slice(0,6).map(t=>`${t.pitch} (${Ye(e,t.at,t.at+t.length)})`);n.push({rule:`out-of-key`,severity:`warning`,track:a.name,message:`${t.length} note${t.length===1?` is`:`s are`} outside ${e.key}: ${r.join(`, `)}${t.length>6?`, …`:``}. Fine for a passing color; otherwise move ${t.length===1?`it`:`them`} to a note of the key.`})}}if(c){let[e,t]=s.range,r=l.filter(n=>n<e||n>t);r.length>0&&n.push({rule:`range`,severity:`warning`,track:a.name,message:`${r.length} note${r.length===1?``:`s`} outside what the ${s.label.toLowerCase()} plays well (${A(e)} to ${A(t)}): ${[...new Set(r.map(A))].slice(0,6).join(`, `)}.`})}let u=a.notes.filter(e=>e.at>=i);u.length>0&&n.push({rule:`past-the-end`,severity:`warning`,track:a.name,message:`${u.length} note${u.length===1?` starts`:`s start`} after the song's last bar (${e.bars}) and ${u.length===1?`is`:`are`} never heard. Add bars or move ${u.length===1?`it`:`them`}.`});let d=0,f=[...a.notes].sort((e,t)=>e.at-t.at);for(let e=0;e<f.length;e++)for(let t=e+1;t<f.length&&f[t].at<f[e].at+f[e].length-1e-6;t++)f[t].pitch===f[e].pitch&&d++;d>0&&n.push({rule:`overlap`,severity:`warning`,track:a.name,message:`${d} note${d===1?` starts`:`s start`} while the same note is still held. Shorten the first, or remove one.`});let p=Ct(xt({...e,compressor:!1},{tracks:[o],cache:t}));return{name:a.name,instrument:a.instrument,notes:a.notes.length,loudness:p,range:l.length===0?void 0:[A(Math.min(...l)),A(Math.max(...l))]}});e.tracks.every(e=>e.notes.length===0)?n.push({rule:`silent`,severity:`error`,message:`No track has notes: the song is silent.`}):o>wt.clipping?n.push({rule:`clipping`,severity:`error`,message:`Peaks at +${Tt(Math.max(0,o))} dBFS: it clips and distorts. Lower master by about ${Math.ceil(o+1)} dB (master × ${Tt(10**(-(o+1)/20))}).`}):s<wt.quiet&&n.push({rule:`quiet`,severity:`warning`,message:`${s} dBFS overall: quiet for game music (about -18 to -24 sits well under sound effects). Raise master.`});let l=c.filter((t,n)=>t.notes>0&&e.tracks[n].mute!==!0),u=Math.max(...l.map(e=>e.loudness));for(let e of l)u-e.loudness>wt.buried&&n.push({rule:`buried`,severity:`warning`,track:e.name,message:`${Tt(u-e.loudness)} dB under the loudest track: barely heard. Raise its volume, or drop it.`});let d=Et(e);return d!==void 0&&d.share<wt.follows&&n.push({rule:`off-reference`,severity:`warning`,message:`The closest track (${d.track}) plays ${Math.round(d.share*100)}% of the reference's notes: the melody has wandered from the hum. Keep its notes, or say why not.`}),{verdict:n.some(e=>e.severity===`error`)?`errors`:n.length>0?`warnings`:`clean`,seconds:Tt(Je(e)),bars:e.bars,tempo:e.tempo,key:e.key,peak:o,loudness:s,tracks:c,follows:d,issues:n}},Ot=(e,t,n)=>{let r=e.length;for(let n=1,i=0;n<r;n++){let a=r>>1;for(;i&a;a>>=1)i^=a;i^=a,n<i&&([e[n],e[i]]=[e[i],e[n]],[t[n],t[i]]=[t[i],t[n]])}for(let i=2;i<=r;i<<=1){let a=(n?2:-2)*Math.PI/i,o=Math.cos(a),s=Math.sin(a);for(let n=0;n<r;n+=i){let r=1,a=0;for(let c=0;c<i/2;c++){let l=n+c,u=l+i/2,d=e[u]*r-t[u]*a,f=e[u]*a+t[u]*r;e[u]=e[l]-d,t[u]=t[l]-f,e[l]=e[l]+d,t[l]=t[l]+f;let p=r*o-a*s;a=r*s+a*o,r=p}}}},kt=11025,At=512,jt=64,Mt=70,Nt=1600,Pt=(e,t)=>{let n=Math.max(1,Math.round(t/kt));if(n===1)return{samples:e,rate:t};let r=new Float32Array(Math.floor(e.length/n));for(let t=0;t<r.length;t++){let i=0,a=Math.max(0,t*n-n),o=Math.min(e.length,t*n+n);for(let r=a;r<o;r++)i+=e[r]*(1-Math.abs(r-t*n)/n);r[t]=i/n}return{samples:r,rate:t/n}},Ft=(e,t)=>{let n=Math.floor(t/Mt),r=Math.floor(t/Nt),i=new Float64Array(n+1),a=[];for(let o=0;o+At+n<e.length;o+=jt){let s=0;for(let t=0;t<At;t++)s+=e[o+t]**2;let c=Math.sqrt(s/At);i[0]=1;let l=0,u=-1;for(let t=1;t<=n;t++){let n=0;for(let r=0;r<At;r++){let i=e[o+r]-e[o+r+t];n+=i*i}l+=n,i[t]=l===0?1:n*t/l}for(let e=r;e<=n;e++)if(i[e]<.15){for(;e+1<=n&&i[e+1]<i[e];)e++;u=e;break}if(u<0){let e=r;for(let t=r;t<=n;t++)i[t]<i[e]&&(e=t);i[e]<.35&&(u=e)}let d=0,f=0;if(u>0){let e=i[u-1]??i[u],n=i[u],r=i[u+1]??i[u],a=e+r-2*n==0?0:(e-r)/(2*(e+r-2*n)),o=t/(u+a);d=69+12*Math.log2(o/440),f=1-n}a.push({time:(o+At/2)/t,rms:c,pitch:d,clarity:f})}return a},It=e=>{let t=[...e].sort((e,t)=>e-t);return t.length===0?0:t[Math.floor(t.length/2)]},Lt=(e,t)=>{let n=It(e.map(e=>e.rms).filter(e=>e>0))*.3,r=e.reduce((e,t)=>Math.max(e,t.rms),0),i=Math.max(n,r*.06),a=e.map(e=>e.pitch>0&&e.clarity>.6&&e.rms>i),o=e.map((t,n)=>{if(!a[n])return 0;let r=[];for(let t=Math.max(0,n-3);t<=Math.min(e.length-1,n+3);t++)a[t]&&r.push(e[t].pitch);return It(r)}),s=[],c=jt/t,l,u=e=>{l!==void 0&&e-l.start>=.07&&s.push({start:l.start,end:e,pitch:It(l.pitches)}),l=void 0};for(let t=0;t<e.length;t++){let n=e[t];if(!a[t]){l!==void 0&&++l.quiet*c>.04&&u(n.time-l.quiet*c);continue}let i=o[t],s=e[t-6]?.rms??0,d=l!==void 0&&n.rms>s*2.2&&s<r*.25;l!==void 0&&(Math.abs(i-It(l.pitches))>.7||d)&&u(n.time),l===void 0&&(l={start:n.time,pitches:[],quiet:0}),l.pitches.push(i),l.quiet=0}return u(e[e.length-1]?.time??0),{notes:s,voiced:a}},Rt=e=>{let t=e.reduce((e,t)=>Math.max(e,t.rms),0),n=[];for(let r=4;r<e.length;r++){let i=Math.min(...e.slice(r-4,r).map(e=>e.rms)),a=e[r].rms;a>t*.2&&a>i*2.5&&(n.length===0||e[r].time-n[n.length-1]>.09)&&n.push(e[r].time)}return n},zt=e=>{if(e.length<3)return{tempo:100,confidence:0};let t=[];for(let n=60;n<=180;n+=.5){let r=60/n,i=0;for(let t of e){let n=(t-e[0])/(r/2),a=Math.abs(n-Math.round(n));i+=Math.exp(-((a/.12)**2))*(Math.round(n)%2==0?1:.6)}let a=Math.exp(-.5*(Math.log2(n/105)/.45)**2);t.push({tempo:n,score:i/e.length*a})}t.sort((e,t)=>t.score-e.score);let n=t[0],r=t.find(e=>Math.abs(e.tempo-n.tempo)>4);return{tempo:n.tempo,confidence:r===void 0||n.score===0?1:Math.max(0,Math.min(1,(n.score-r.score)/n.score/.25))}},Bt=[6.35,2.23,3.48,2.33,4.38,4.09,2.52,5.19,2.39,3.66,2.29,2.88],Vt=[6.33,2.68,3.52,5.38,2.6,3.53,2.54,4.75,3.98,2.69,3.34,3.17],Ht=[`C`,`C#`,`D`,`Eb`,`E`,`F`,`F#`,`G`,`Ab`,`A`,`Bb`,`B`],Ut=(e,t)=>{let n=e.reduce((e,t)=>e+t,0)/e.length,r=t.reduce((e,t)=>e+t,0)/t.length,i=0,a=0,o=0;for(let s=0;s<e.length;s++)i+=(e[s]-n)*(t[s]-r),a+=(e[s]-n)**2,o+=(t[s]-r)**2;return a===0||o===0?0:i/Math.sqrt(a*o)},Wt=(e,t,n,r)=>{let i=Array.from({length:12},()=>0),a=4096,o=new Float64Array(a),s=new Float64Array(a);for(let n=0;n+a<=e.length;n+=a/2){for(let t=0;t<a;t++)o[t]=e[n+t]*(.5-.5*Math.cos(2*Math.PI*t/a)),s[t]=0;Ot(o,s,!1);for(let e=1;e<a/2;e++){let n=e*t/a;if(n<65||n>2100)continue;let c=69+12*Math.log2(n/440)-r;i[(Math.round(c)%12+12)%12]+=Math.sqrt(o[e]**2+s[e]**2)}}let c=i.reduce((e,t)=>e+t,0)||1,l=Kt(n,0),u=l.reduce((e,t)=>e+t,0)||1;return i.map((e,t)=>e/c+l[t]/u)},Gt=e=>{let t={score:-2,key:`C major`};for(let n=0;n<12;n++){let r=e.map((t,r)=>e[(r+n)%12]),i=Ut(r,Bt),a=Ut(r,Vt);i>t.score&&(t={score:i,key:`${Ht[n]} major`}),a>t.score&&(t={score:a,key:`${Ht[n]} minor`})}return t},Kt=(e,t)=>{let n=Array.from({length:12},()=>0);for(let r of e)n[(Math.round(r.pitch-t)%12+12)%12]+=r.end-r.start;return n},qt=e=>{if(e.length<3)return 0;let t=0,n=0;for(let r of e){let e=2*Math.PI*(r.pitch-Math.round(r.pitch));t+=Math.cos(e)*(r.end-r.start),n+=Math.sin(e)*(r.end-r.start)}let r=Math.atan2(n,t)/(2*Math.PI);if(Math.abs(r)<.35)return r;let i=r-Math.sign(r);return Gt(Kt(e,i)).score>Gt(Kt(e,r)).score+.05?i:r},P=.25,Jt=(e,t,n={})=>{let{samples:r,rate:i}=Pt(e,t),a=Ft(r,i),{notes:o,voiced:s}=Lt(a,i),c=qt(o),l=o.map(e=>({...e,pitch:e.pitch-c})),u=l.length>=3?l.map(e=>e.start):Rt(a),d=n.tempo===void 0?zt(u):{tempo:n.tempo,confidence:1},f=Math.round(d.tempo),p=60/f,m=u[0]??0,h=e=>Math.max(0,Math.round((e-m)/p/P)*P),g=[];for(let e of l){let t=h(e.start),n=Math.max(t+P,h(e.end)),r=g[g.length-1],i=A(Math.round(e.pitch));r!==void 0&&r.at===t?n-t>r.length&&(g[g.length-1]={at:t,length:n-t,pitch:i}):(r!==void 0&&r.at+r.length>t&&(g[g.length-1]={...r,length:t-r.at}),g.push({at:t,length:n-t,pitch:i}))}for(let e=0;e+1<g.length;e++){let t=g[e],n=g[e+1].at-(t.at+t.length);n>0&&n<=P&&(g[e]={...t,length:t.length+n})}let _=a[a.length-1]?.time??0,ee=Math.max(P,Math.ceil((_-m)/p/P)*P),v=[];for(let e=0;e<ee;e+=.125){let t=m+e*p,n=a.filter((e,n)=>s[n]&&Math.abs(e.time-t)<p*.125/2+.003);v.push(n.length===0?0:Math.round((It(n.map(e=>e.pitch))-c)*10)/10)}let y=l.map(e=>Math.round(e.pitch));return{audio:n.audio,tempo:f,key:Gt(Wt(r,i,l,c)).key,length:ee,notes:g,contour:v,offset:0,start:Math.round(m*1e3)/1e3,found:{seconds:Math.round(e.length/t*100)/100,voiced:Math.round(s.filter(Boolean).length/Math.max(1,s.length)*100)/100,range:y.length===0?void 0:[A(Math.min(...y)),A(Math.max(...y))],tempoConfidence:Math.round(d.confidence*100)/100,tuning:Math.round(c*100)}}},Yt=e=>e.notes.length>0&&e.found.seconds>5&&e.notes.length/e.found.seconds<.4,Xt=e=>{let{found:t,...n}=e;return n},Zt=480,Qt=e=>{let t=[e&127],n=e>>7;for(;n>0;)t.unshift(n&127|128),n>>=7;return t},$t=(e,t)=>[...[...e].map(e=>e.charCodeAt(0)),t.length>>>24&255,t.length>>>16&255,t.length>>>8&255,t.length&255,...t],en=(e,t)=>{let n=[...new TextEncoder().encode(t)];return[255,e,...Qt(n.length),...n]},tn=e=>{let t=[...e].sort((e,t)=>e.tick-t.tick||e.order-t.order),n=[],r=0;for(let e of t)n.push(...Qt(e.tick-r),...e.bytes),r=e.tick;return n.push(0,255,47,0),$t(`MTrk`,n)},nn=e=>{let t=Math.round(6e7/e.tempo),n=Ke(e.key),r={minor:3,"harmonic minor":3,"minor pentatonic":3,blues:3,dorian:10,phrygian:8,lydian:7,mixolydian:5},i=n!==void 0&&r[n.scale]===3,a=n===void 0?0:(n.root+(r[n.scale]??0))%12*7%12,o=a>6?a-12:a,s=[{tick:0,order:0,bytes:en(3,e.about??`Song`)},{tick:0,order:0,bytes:[255,81,3,t>>16&255,t>>8&255,t&255]},{tick:0,order:0,bytes:[255,88,4,e.beatsPerBar,2,24,8]},{tick:0,order:0,bytes:[255,89,2,o&255,+!!i]}],c=[0,1,2,3,4,5,6,7,8,10,11,12,13,14,15],l=0,u=e.tracks.map(e=>{let t=x[e.instrument].kind===`drums`,n=t?9:c[l++%c.length],r=[{tick:0,order:0,bytes:en(3,e.name)},...t?[]:[{tick:0,order:0,bytes:[192|n,x[e.instrument].program]}],{tick:0,order:0,bytes:[176|n,7,Math.min(127,Math.round(e.volume*100))]},{tick:0,order:0,bytes:[176|n,10,Math.round((e.pan+1)*63.5)]}];for(let i of e.notes){let e=t?S.find(e=>e.name===i.pitch)?.gm:k(i.pitch);if(e===void 0)continue;let a=Math.round(i.at*Zt),o=Math.max(a+1,Math.round((i.at+i.length)*Zt)),s=Math.max(1,Math.round(i.velocity*127));r.push({tick:a,order:1,bytes:[144|n,e,s]},{tick:o,order:0,bytes:[128|n,e,0]})}return tn(r)}),d=$t(`MThd`,[0,1,u.length+1>>8&255,u.length+1&255,1,224]);return new Uint8Array([...d,...tn(s),...u.flat()])},rn=[`new`,`approved`,`changes`],an={status:`new`,note:``,comments:[],edits:[]},on=e=>typeof e==`object`&&!!e&&!Array.isArray(e),sn=e=>{let t;try{t=JSON.parse(e)}catch(e){return{ok:!1,error:`not JSON: ${e instanceof Error?e.message:String(e)}`}}if(!on(t))return{ok:!1,error:`a review is {"version": 1, "status": "new", "note": "", "comments": [], "edits": []}`};let n=[],r=rn.find(e=>e===t.status);t.status!==void 0&&r===void 0&&n.push(`status must be one of ${rn.join(`, `)}`);let i=[];for(let[e,r]of(Array.isArray(t.comments)?t.comments:[]).entries())!on(r)||typeof r.id!=`string`||typeof r.text!=`string`||typeof r.from!=`number`||typeof r.to!=`number`?n.push(`comments[${e}] must be {"id", "from", "to", "text"}`):i.push({id:r.id,from:r.from,to:r.to,track:typeof r.track==`string`?r.track:void 0,text:r.text});let a=(Array.isArray(t.edits)?t.edits:[]).filter(e=>typeof e==`string`);return n.length>0?{ok:!1,error:n.join(`; `)}:{ok:!0,value:{status:r??`new`,note:typeof t.note==`string`?t.note:``,comments:i,edits:a}}},cn=e=>JSON.stringify({version:1,...e},null,2)+`
4
+ `,F=e=>`${e.at}|${e.pitch}|${e.length}|${e.velocity}`,I=(e,t)=>`${e} ${t}${e===1?``:`s`}`,ln=(e,t)=>Ye(e,Math.min(...t.map(e=>e.at)),Math.max(...t.map(e=>e.at+e.length))),un=(e,t,n)=>{let r=[],i=n.name;t.name!==n.name&&r.push(`Renamed the track ${t.name} to ${n.name}.`),t.instrument!==n.instrument&&r.push(`${i}: changed the instrument from ${x[t.instrument].label} to ${x[n.instrument].label}.`),Math.abs(t.volume-n.volume)>.005&&r.push(`${i}: volume ${t.volume.toFixed(2)} → ${n.volume.toFixed(2)}.`),Math.abs(t.pan-n.pan)>.005&&r.push(`${i}: pan ${t.pan.toFixed(2)} → ${n.pan.toFixed(2)}.`),Math.abs(t.reverb-n.reverb)>.005&&r.push(`${i}: reverb ${t.reverb.toFixed(2)} → ${n.reverb.toFixed(2)}.`),t.mute===!0!=(n.mute===!0)&&r.push(`${i}: ${n.mute===!0?`muted`:`unmuted`}.`);let a=new Map;for(let e of t.notes)a.set(F(e),(a.get(F(e))??0)+1);let o=[];for(let e of n.notes){let t=a.get(F(e))??0;t>0?a.set(F(e),t-1):o.push(e)}let s=new Map;for(let e of n.notes)s.set(F(e),(s.get(F(e))??0)+1);let c=[];for(let e of t.notes){let t=s.get(F(e))??0;t>0?s.set(F(e),t-1):c.push(e)}return o.length>0&&c.length>0?r.push(`${i}: rewrote notes in ${ln(e,[...o,...c])} (${I(o.length,`note`)} in, ${I(c.length,`note`)} out).`):o.length>0?r.push(`${i}: added ${I(o.length,`note`)} in ${ln(e,o)}.`):c.length>0&&r.push(`${i}: removed ${I(c.length,`note`)} in ${ln(e,c)}.`),r},dn=(e,t)=>{let n=[];e.tempo!==t.tempo&&n.push(`Tempo ${e.tempo} → ${t.tempo} BPM.`),e.key!==t.key&&n.push(`Key ${e.key} → ${t.key}.`),e.bars!==t.bars&&n.push(`Bars ${e.bars} → ${t.bars}.`),e.beatsPerBar!==t.beatsPerBar&&n.push(`Beats per bar ${e.beatsPerBar} → ${t.beatsPerBar}.`),Math.abs(e.master-t.master)>.005&&n.push(`Master volume ${e.master.toFixed(2)} → ${t.master.toFixed(2)}.`),e.loop!==t.loop&&n.push(t.loop?`Made it loop.`:`Made it play once (no loop).`),e.compressor!==t.compressor&&n.push(t.compressor?`Turned the compressor on.`:`Turned the compressor off.`);let r=Math.min(e.tracks.length,t.tracks.length);for(let i=0;i<r;i++)n.push(...un(t,e.tracks[i],t.tracks[i]));for(let e of t.tracks.slice(r))n.push(`Added a track: ${e.name} (${x[e.instrument].label}, ${I(e.notes.length,`note`)}).`);for(let t of e.tracks.slice(r))n.push(`Removed the track ${t.name}.`);return JSON.stringify(e.reference)!==JSON.stringify(t.reference)&&n.push(t.reference===void 0?`Removed the reference.`:`Recorded a new reference: ${t.reference.tempo} BPM, ${t.reference.key}, ${I(t.reference.notes.length,`note`)}.`),n},L=22,fn=26,pn=30,mn=13,hn=22,R={text:`rgba(243, 241, 236, 0.55)`,faint:`rgba(243, 241, 236, 0.06)`,beat:`rgba(243, 241, 236, 0.07)`,bar:`rgba(243, 241, 236, 0.2)`,inKey:`rgba(243, 241, 236, 0.035)`,selection:`rgba(255, 90, 54, 0.12)`,selectionEdge:`rgba(255, 90, 54, 0.7)`,playhead:`#ff5a36`,reference:`rgba(243, 241, 236, 0.75)`,comment:`#f0a843`},gn=(e,t,n)=>{let r=window.devicePixelRatio||1;(e.width!==Math.round(t*r)||e.height!==Math.round(n*r))&&(e.width=Math.round(t*r),e.height=Math.round(n*r)),e.style.width=`${t}px`,e.style.height=`${n}px`;let i=e.getContext(`2d`);if(i!==null)return i.setTransform(r,0,0,r,0,0),i.clearRect(0,0,t,n),i},_n=e=>64+M(e.song)*e.beat+8,vn=(e,t,n)=>{let{song:r}=t,i=M(r);e.font=`600 10.5px JetBrains Mono, ui-monospace, monospace`,e.textBaseline=`middle`;for(let a=0;a<=i;a++){let o=64+a*t.beat,s=a%r.beatsPerBar===0;e.fillStyle=s?R.bar:R.beat,e.fillRect(Math.round(o),s?0:16,1,s?n:6),s&&a<i&&(e.fillStyle=R.text,e.fillText(String(a/r.beatsPerBar+1),o+5,L/2))}e.fillStyle=R.bar,e.fillRect(64,21,i*t.beat,1);for(let n of t.comments){let r=64+n.from*t.beat;e.fillStyle=R.comment,e.beginPath(),e.moveTo(r,21),e.lineTo(r,3),e.lineTo(r+8,6.5),e.lineTo(r,10),e.fill(),e.fillRect(r,19,Math.max(2,(n.to-n.from)*t.beat),2)}},yn=(e,t,n)=>{if(t.selection!==void 0){let r=64+t.selection.from*t.beat,i=(t.selection.to-t.selection.from)*t.beat;e.fillStyle=R.selection,e.fillRect(r,0,i,n),e.fillStyle=R.selectionEdge,e.fillRect(r,0,1,n),e.fillRect(r+i-1,0,1,n)}t.playhead!==void 0&&(e.fillStyle=R.playhead,e.fillRect(Math.round(64+t.playhead*t.beat),0,2,n))},bn=(e,t,n,r,i,a)=>{e.beginPath(),e.roundRect(t,n,Math.max(1,r),i,Math.min(a,r/2,i/2))},xn=e=>L+(e.reference===void 0?0:pn)+e.tracks.length*fn+6,Sn=(e,t)=>{if(t<L)return{kind:`ruler`};let n=L;if(e.reference!==void 0){if(t<n+pn)return{kind:`reference`};n+=pn}let r=Math.floor((t-n)/fn);return r>=0&&r<e.tracks.length?{kind:`track`,index:r}:void 0},Cn=(e,t,n,r,i,a,o)=>{let s=t.song.reference;if(s===void 0)return;e.strokeStyle=R.reference,e.lineWidth=o,e.lineJoin=`round`,e.lineCap=`round`,e.beginPath();let c=!1;s.contour.forEach((o,l)=>{if(o<=0){c=!1;return}let u=64+(s.offset+l*.125)*t.beat,d=n+r-(o-i)/Math.max(1,a-i)*r;c?e.lineTo(u,d):e.moveTo(u,d),c=!0}),e.stroke()},wn=(e,t)=>{let{song:n}=t,r=_n(t),i=xn(n),a=gn(e,r,i);if(a===void 0)return;vn(a,t,i);let o=L;if(a.font=`500 11px Inter, system-ui, sans-serif`,a.textBaseline=`middle`,n.reference!==void 0){let e=n.reference.contour.filter(e=>e>0),r=Math.min(...e,60)-1,i=Math.max(...e,60)+1;a.fillStyle=R.faint,a.fillRect(64,o+2,M(n)*t.beat,26),Cn(a,t,o+5,20,r,i,1.5),a.fillStyle=R.text,a.fillText(`hum`,8,o+pn/2),o+=pn}n.tracks.forEach((e,n)=>{let i=x[e.instrument],s=n===t.track,c=t.heard.includes(n);s&&(a.fillStyle=`rgba(243, 241, 236, 0.07)`,a.fillRect(0,o,r,fn)),a.fillStyle=i.color,a.fillRect(0,o+4,3,18),a.fillStyle=s?`#f3f1ec`:R.text;let l=e.name.length>6?`${e.name.slice(0,6)}…`:e.name;a.fillText(l,8,o+fn/2);let u=e.notes.flatMap(e=>k(e.pitch)??[]),d=Math.min(...u),f=Math.max(...u);a.globalAlpha=c?.95:.25,a.fillStyle=i.color;for(let n of e.notes){let e=64+n.at*t.beat,r=Math.max(2,n.length*t.beat-1),s;if(i.kind===`drums`){let e=S.findIndex(e=>e.name===n.pitch);s=o+3+e/Math.max(1,S.length-1)*17}else{let e=k(n.pitch)??d;s=o+3+(f===d?.5:1-(e-d)/(f-d))*17}a.fillRect(e,s,r,3)}a.globalAlpha=1,o+=fn}),yn(a,t,i)},Tn=(e,t)=>{let n=x[t.instrument];if(n.kind===`drums`)return{drums:!0,count:S.length,height:hn,rowOf:e=>S.findIndex(t=>t.name===e),pitchOf:e=>S[Math.max(0,Math.min(S.length-1,e))].name,label:e=>S[e]?.label??``};let[r,i]=n.range,a=t.notes.flatMap(e=>k(e.pitch)??[]),o=e.reference?.notes.flatMap(e=>k(e.pitch)??[])??[],s=[...a,...o],c=s.length===0?Math.round((r+i)/2/12)*12:Math.round((Math.min(...s)+Math.max(...s))/2),l=Math.min(i,Math.max(...s,c+12)+5),u=l-Math.max(r,Math.min(...s,c-12)-5)+1;return{drums:!1,count:u,height:mn,rowOf:e=>l-(k(e)??l),pitchOf:e=>A(l-Math.max(0,Math.min(u-1,e))),label:e=>A(l-e)}},En=(e,t)=>{let{song:n}=t,r=n.tracks[t.track];if(r===void 0)return;let i=x[r.instrument],a=Tn(n,r),o=_n(t),s=L+a.count*a.height+1,c=gn(e,o,s);if(c===void 0)return;let l=M(n),u=Ke(n.key);for(let e=0;e<a.count;e++){let n=L+e*a.height,r=k(a.pitchOf(e));!a.drums&&u!==void 0&&r!==void 0&&qe(u,r)&&(c.fillStyle=R.inKey,c.fillRect(64,n,l*t.beat,a.height)),(a.drums||r!==void 0&&r%12==0)&&(c.fillStyle=R.beat,c.fillRect(64,n+a.height-1,l*t.beat,1))}if(t.snap<1&&t.beat*t.snap>=6){c.fillStyle=`rgba(243, 241, 236, 0.025)`;for(let e=0;e<l;e+=t.snap)c.fillRect(Math.round(64+e*t.beat),L,1,s-L)}vn(c,t,s),c.font=`500 10px JetBrains Mono, ui-monospace, monospace`,c.textBaseline=`middle`;for(let e=0;e<a.count;e++){let t=L+e*a.height,n=k(a.pitchOf(e)),r=n!==void 0&&[1,3,6,8,10].includes(n%12);c.fillStyle=a.drums?`#1d2026`:r?`#14161a`:`#2a2e35`,c.fillRect(0,t,60,a.height-1),(a.drums||n!==void 0&&(n%12==0||u!==void 0&&n%12===u.root))&&(c.fillStyle=a.drums?R.text:n!==void 0&&u!==void 0&&n%12===u.root?`#f3f1ec`:R.text,c.fillText(a.label(e),5,t+a.height/2))}if(!a.drums&&t.showReference&&n.reference!==void 0){let e=n.reference;c.strokeStyle=`rgba(243, 241, 236, 0.45)`,c.setLineDash([3,3]),c.lineWidth=1;for(let n of e.notes){let r=a.rowOf(n.pitch);r<0||r>=a.count||(bn(c,64+(n.at+e.offset)*t.beat+.5,L+r*a.height+.5,n.length*t.beat-1,a.height-1,3),c.stroke())}c.setLineDash([]);let r=k(a.pitchOf(0))??0;Cn(c,t,L+a.height/2,(a.count-1)*a.height,r-(a.count-1),r,1.25)}r.notes.forEach((e,n)=>{let r=a.rowOf(e.pitch);if(r<0||r>=a.count)return;let o=64+e.at*t.beat,s=L+r*a.height,l=e.length*t.beat,u=n===t.chosen;c.globalAlpha=.45+.55*e.velocity,c.fillStyle=i.color,bn(c,o+.5,s+1,l-1,a.height-2,3),c.fill(),c.globalAlpha=1,u&&(c.strokeStyle=`#f3f1ec`,c.lineWidth=1.5,bn(c,o+.5,s+1,l-1,a.height-2,3),c.stroke()),l>34&&a.height>=13&&!a.drums&&(c.fillStyle=`rgba(14, 16, 19, 0.75)`,c.font=`600 9.5px JetBrains Mono, ui-monospace, monospace`,c.fillText(e.pitch,o+4,s+a.height/2+.5))}),yn(c,t,s)},Dn=(e,t,n,r)=>{let i=e.getBoundingClientRect(),a=t.clientX-i.left,o=t.clientY-i.top;return{x:a,y:o,beat:(a-64)/n.beat,row:Math.floor((o-L)/r.height)}},On=(e,t,n,r)=>{for(let i=e.notes.length-1;i>=0;i--){let a=e.notes[i];if(t.rowOf(a.pitch)===n&&a.at<=r&&r<a.at+a.length)return i}return-1},kn=(e,t)=>Math.round(e/t)*t,An=(e,t)=>Math.floor(e/t+1e-6)*t,jn=(e,t,n)=>{let r=n.view(),i=e.getBoundingClientRect(),a=e=>Math.max(0,Math.min(M(r.song),(e-i.left-64)/r.beat)),o=a(t.clientX),s=!1,c=e=>{let t=a(e.clientX);if(Math.abs(t-o)*r.beat<6&&!s)return;s=!0;let i=r.song.beatsPerBar,c=Math.floor(Math.min(o,t)/i)*i,l=Math.ceil(Math.max(o,t)/i)*i;n.region(c,Math.max(c+i,l))},l=()=>{window.removeEventListener(`pointermove`,c),window.removeEventListener(`pointerup`,l),s||n.seek(o)};window.addEventListener(`pointermove`,c),window.addEventListener(`pointerup`,l)},Mn=(e,t)=>{e.addEventListener(`pointerdown`,n=>{let r=t.view(),i=e.getBoundingClientRect(),a=Sn(r.song,n.clientY-i.top);if(a?.kind===`ruler`)jn(e,n,t);else if(a?.kind===`track`){t.pickTrack(a.index);let e=(n.clientX-i.left-64)/r.beat;e>=0&&t.cue(Math.min(e,M(r.song)))}else a?.kind===`reference`&&t.seek(Math.max(0,(n.clientX-i.left-64)/r.beat))})},Nn=(e,t)=>{e.addEventListener(`contextmenu`,e=>e.preventDefault()),e.addEventListener(`pointerdown`,n=>{let r=t.view(),i=r.song.tracks[r.track];if(i===void 0)return;let a=Tn(r.song,i),o=Dn(e,n,r,a);if(o.y<L)return jn(e,n,t);if(o.row<0||o.row>=a.count)return;if(o.x<64)return t.audition(a.pitchOf(o.row),.8);n.preventDefault();let s=M(r.song),c=On(i,a,o.row,o.beat);if(n.button===2){c>=0&&t.remove(c);return}let l=c,u=`move`,d=0;if(l>=0){let e=i.notes[l];u=64+(e.at+e.length)*r.beat-o.x<Math.min(8,e.length*r.beat/3)?`stretch`:`move`,d=o.beat-e.at,t.choose(l)}else{let e=a.drums?Math.max(r.snap,.25):Pn,n=Math.min(s-r.snap,An(o.beat,r.snap)),i={at:Math.max(0,n),length:Math.min(e,s-n),pitch:a.pitchOf(o.row),velocity:.8};l=t.add(i),t.audition(i.pitch,i.velocity),u=a.drums?`move`:`stretch`,d=0}let f=t.view().song.tracks[r.track]?.notes[l];if(f===void 0)return;let p=f,m=!1;e.setPointerCapture(n.pointerId);let h=n=>{let i=Dn(e,n,r,a),o;if(u===`stretch`){let e=Math.max(f.at+r.snap,kn(i.beat,r.snap));o={...f,length:Math.min(e,s)-f.at}}else{let e=Math.max(0,Math.min(s-f.length,kn(i.beat-d,r.snap)));o={...f,at:e,pitch:a.pitchOf(i.row)}}(o.at!==p.at||o.length!==p.length||o.pitch!==p.pitch)&&(o.pitch!==p.pitch&&t.audition(o.pitch,o.velocity),m=!0,p=o,t.move(l,o,!1))},g=()=>{e.removeEventListener(`pointermove`,h),e.removeEventListener(`pointerup`,g),e.removeEventListener(`pointercancel`,g),a.drums||(Pn=p.length),t.move(l,p,!0),!m&&c>=0&&t.audition(p.pitch,p.velocity)};e.addEventListener(`pointermove`,h),e.addEventListener(`pointerup`,g),e.addEventListener(`pointercancel`,g)}),e.addEventListener(`dblclick`,n=>{let r=t.view(),i=r.song.tracks[r.track];if(i===void 0)return;let a=Tn(r.song,i),o=Dn(e,n,r,a),s=On(i,a,o.row,o.beat);s>=0&&t.remove(s)})},Pn=1,Fn=e=>{let t,n,r,i,a=()=>(t??=new AudioContext,t.resume(),t),o=()=>{i=void 0,e(),n!==void 0&&(i=requestAnimationFrame(o))},s=()=>{if(n===void 0||t===void 0)return;let e=t.currentTime-n.start+n.offset;return n.loop?e%n.length:Math.min(n.length,e)},c=()=>{if(r!==void 0&&(r.onended=null,r.stop(),r=void 0),n!==void 0){for(let e of n.sources)e.onended=null,e.stop();n=void 0}};return{play:(t,r)=>{let l=a(),u=r.at??s()??0;c();let d=t.left.length/t.sampleRate,f=l.createBuffer(2,Math.max(1,t.left.length),t.sampleRate);f.copyToChannel(t.left,0),f.copyToChannel(t.right,1);let p=l.createBufferSource();p.buffer=f,p.loop=r.loop,p.connect(l.destination);let m=[p],h=l.currentTime+.02,g=Math.max(0,Math.min(u,d-.001));if(p.start(h,g),r.extra!==void 0){let e=l.createBufferSource();e.buffer=r.extra,e.connect(l.destination);let t=g+(r.extraAt??0);t<r.extra.duration&&(e.start(h+Math.max(0,-t),Math.max(0,t)),m.push(e))}p.onended=()=>{n?.sources[0]===p&&(n=void 0,e())},n={sources:m,start:h,offset:g,length:d,loop:r.loop},i===void 0&&(i=requestAnimationFrame(o))},stop:()=>{c(),e()},position:s,playAlone:(t,n)=>{let i=a();c(),e();let o=i.createBufferSource();o.buffer=t,o.connect(i.destination),o.onended=()=>{r===o&&(r=void 0,n())},o.start(),r=o},playingAlone:()=>r!==void 0,audition:(e,t)=>{let n=a(),r=n.createBuffer(1,Math.max(1,e.length),t);r.copyToChannel(new Float32Array(e),0);let i=n.createBufferSource();i.buffer=r,i.connect(n.destination),i.start()},decode:e=>a().decodeAudioData(e),context:a}},In=()=>window.isSecureContext&&navigator.mediaDevices?.getUserMedia!==void 0&&typeof MediaRecorder<`u`,Ln=async e=>{let t=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!1,noiseSuppression:!1,autoGainControl:!1}}),n=new MediaRecorder(t),r=[];n.addEventListener(`dataavailable`,e=>r.push(e.data));let i=e.createAnalyser();i.fftSize=1024;let a=e.createMediaStreamSource(t);a.connect(i);let o=new Float32Array(i.fftSize),s=()=>{a.disconnect();for(let e of t.getTracks())e.stop()};return n.start(),{stop:()=>new Promise((e,t)=>{n.addEventListener(`stop`,()=>{s(),new Blob(r,{type:n.mimeType}).arrayBuffer().then(e,t)}),n.stop()}),cancel:()=>{n.stop(),s()},level:()=>{i.getFloatTimeDomainData(o);let e=0;for(let t of o)e=Math.max(e,Math.abs(t));return Math.min(1,e*1.6)}}},Rn=e=>{let t=new Float32Array(e.length);for(let n=0;n<e.numberOfChannels;n++){let r=e.getChannelData(n);for(let n=0;n<t.length;n++)t[n]+=r[n]/e.numberOfChannels}return t},zn=`https://ritmo.typeonce.dev/`,Bn={tempo:100,key:`C major`,beatsPerBar:4,bars:4,loop:!0,master:.8,compressor:!1,tracks:[]},z={songFile:``,name:``,song:Bn,baseline:Bn,review:an,track:0,chosen:void 0,selection:void 0,cue:void 0,snap:.25,zoom:1,showReference:!0,withHum:!1,solo:new Set,draft:``,draftTrack:`all`,recording:void 0,recordStart:0,listening:!1,message:``,saving:`saved`,version:``,loaded:!1},Vn=new Map,B=()=>z.song.tracks[z.track],Hn=()=>{let e=z.song.tracks.map((e,t)=>t);return z.solo.size>0?e.filter(e=>z.solo.has(e)):e.filter(e=>z.song.tracks[e].mute!==!0)},Un=!1,Wn=!1,Gn,Kn="the review server is not running. Start `ritmo-tools music review` again, then reload this page",qn=async(e,t)=>{let n=await fetch(`/api/${e}`,{method:`PUT`,body:t}).catch(()=>{throw Error(Kn)}),r=await n.json();if(!n.ok)throw Error(typeof r==`object`&&r&&`error`in r?String(r.error):n.statusText);return typeof r==`object`&&r&&`version`in r&&typeof r.version==`string`&&(z.version=r.version),r},Jn=async()=>{Gn=void 0;try{Un&&(Un=!1,await qn(`song`,it(z.song))),Wn&&(Wn=!1,await qn(`review`,cn(z.review))),z.saving=Un||Wn?`saving`:`saved`}catch(e){z.saving=`failed`,Q(`Not saved: ${e instanceof Error?e.message:String(e)}`)}Rr()},Yn=e=>{e.song===!0&&(Un=!0,z.review={...z.review,edits:dn(z.baseline,z.song)},Wn=!0),e.review===!0&&(Wn=!0),z.saving=`saving`,Gn!==void 0&&clearTimeout(Gn),Gn=setTimeout(()=>void Jn(),400)},V=se(()=>`${it(z.song)}\u0000${cn(z.review)}`,e=>{let[t=``,n=``]=e.split(`\0`),r=nt(t),i=sn(n);r.ok&&(z.song=r.value),i.ok&&(z.review=i.value),z.chosen=void 0,Yn({song:!0,review:!0}),Ir()},()=>Rr()),H=()=>{Yn({song:!0}),V.schedule(),Ir()},Xn=()=>{Yn({review:!0}),V.schedule(),$()},U=e=>{z.song=e},Zn=(e,t)=>{U({...z.song,tracks:z.song.tracks.map((n,r)=>r===e?t:n)})},Qn=e=>typeof e==`object`&&!!e&&`songText`in e&&typeof e.songText==`string`&&`version`in e&&typeof e.version==`string`,$n=(e,t)=>{let n=nt(e.songText);if(!n.ok)return Q(`The song has problems: ${n.error}`),!1;let r=e.reviewText===null?void 0:sn(e.reviewText),i=t||it(n.value)!==it(z.song);return z.song=n.value,z.review=r?.ok===!0?r.value:an,i&&(z.baseline=n.value,!t&&z.review.edits.length>0&&(z.review={...z.review,edits:[]},Yn({review:!0}))),z.songFile=e.song,z.name=e.name,z.version=e.version,z.track>=z.song.tracks.length&&(z.track=0),i},er=async()=>{let e=await(await fetch(`/api/session`)).json();if(!Qn(e))return Q(`The server sent no song.`);$n(e,!0)&&(V.reset(),V.schedule(),z.loaded=!0,dr(),Ir())},tr=async()=>{if(Gn===void 0&&z.saving!==`saving`&&z.recording===void 0)try{let e=await(await fetch(`/api/version`)).json();if(typeof e!=`object`||!e||!(`version`in e)||e.version===z.version)return;let t=await(await fetch(`/api/session`)).json();if(!Qn(t))return;let n=z.song.reference?.audio;$n(t,!1)?(Q(`The agent changed the song.`),V.schedule(),z.song.reference?.audio!==n&&dr(),Ir()):$()}catch{}};setInterval(()=>void tr(),1500);var W,nr=``,G,K=Fn(()=>Fr()),rr=()=>{let e=JSON.stringify([it(z.song),Hn()]);return(W===void 0||e!==nr)&&(W=xt(z.song,{tracks:Hn(),cache:Vn}),nr=e),W},ir=e=>{let t=z.song.reference,n=K.playingAlone();K.play(rr(),{loop:z.song.loop,at:e,extra:z.withHum?G:void 0,extraAt:(t?.start??0)-(t?.offset??0)*j(z.song)}),n&&zr(),Z()},ar=()=>z.selection===void 0?0:z.selection.from*j(z.song),or=()=>{let e=K.position();e===void 0?(ir(z.cue??ar()),z.cue=void 0):(z.cue=e,K.stop()),X(),Z()},sr=()=>{z.cue=void 0,K.stop(),X(),Z()},cr,lr=()=>{K.position()!==void 0&&(cr!==void 0&&clearTimeout(cr),cr=setTimeout(()=>{cr=void 0,K.position()!==void 0&&ir()},120))},ur=(e,t,n)=>{let r=ct(e,{pitch:t,velocity:n},x[e].kind===`drums`?.2:.45,at,Vn),i=(z.song.tracks[z.track]?.volume??.8)*z.song.master;K.audition(r.map(e=>e*i),at)},dr=async()=>{G=void 0;let e=z.song.reference?.audio;if(e!==void 0){try{let t=await fetch(`/api/audio/${encodeURIComponent(e)}`);t.ok&&(G=await K.decode(await t.arrayBuffer()))}catch{G=void 0}zr(),ei()}},fr=()=>{K.playingAlone()?K.stop():G!==void 0&&K.playAlone(G,zr),zr(),Z()},pr=(e,t,r)=>{let i=URL.createObjectURL(new Blob([e],{type:r}));n(`a`,{attrs:{href:i,download:t}}).click(),setTimeout(()=>URL.revokeObjectURL(i),1e3)},mr=()=>z.name.replace(/\.json$/,``).replace(/\.song$/,``)||`song`,q,hr,gr=()=>{hr!==void 0&&clearTimeout(hr),hr=setTimeout(()=>{hr=void 0,q=Dt(z.song,Vn),ei(),Hr()},500)},_r=async(e,t)=>{z.listening=!0,$();try{let n=await K.decode(e.slice(0)),r=Rn(n);await new Promise(e=>setTimeout(e,30));let i=`${mr()}.song.reference.wav`,a=Jt(r,n.sampleRate,{audio:i}),o=Xt(a);a.notes.length===0&&a.found.tempoConfidence===0?Q(`Heard no clear notes or beat in the ${t}. Try humming louder and closer, one note at a time.`):(await qn(`audio/${encodeURIComponent(i)}`,de(r,n.sampleRate)),G=n,U(z.song.tracks.every(e=>e.notes.length===0)?{...z.song,tempo:a.tempo,key:a.key,bars:Math.max(4,Math.ceil(a.length/(4*z.song.beatsPerBar))*4),reference:o}:{...z.song,reference:o}),z.showReference=!0,H(),Q(a.notes.length===0?`Heard a beat at ${a.tempo} BPM, and no tune. Ask your agent to build the song on that beat.`:Yt(a)?`Heard only ${a.notes.length} notes in ${Math.round(a.found.seconds)} seconds: the listener follows one voice, so a full song or a noisy room loses the tune. Hum the part alone, close to the microphone.`:`Heard ${a.notes.length} notes at ${a.tempo} BPM in ${a.key}. Ask your agent to build the song around it.`))}catch(e){let n=e instanceof Error?e.message:String(e);Q(n===Kn?`Heard the ${t}, but could not keep it: ${n}.`:`Could not listen to the ${t}: ${n}`)}z.listening=!1,$()},vr,yr=()=>{if(vr=void 0,z.recording===void 0)return;let e=document.querySelector(`.record-level span`);e!==null&&(e.style.width=`${Math.round(z.recording.level()*100)}%`);let t=document.querySelector(`.record-time`);t!==null&&(t.textContent=Nr((performance.now()-z.recordStart)/1e3)),vr=requestAnimationFrame(yr)},br=async()=>{if(z.recording!==void 0){let e=z.recording;z.recording=void 0,vr!==void 0&&cancelAnimationFrame(vr),$(),await _r(await e.stop(),`recording`)}else{if(!In())return Q(`Recording needs the page on localhost (or HTTPS). Open it there, or choose an audio file instead.`);try{K.stop(),z.recording=await Ln(K.context()),z.recordStart=performance.now(),$(),yr()}catch(e){Q(`The microphone is not available: ${e instanceof Error?e.message:String(e)}`)}}},xr=n(`input`,{props:{type:`file`,accept:`audio/*,.wav,.mp3,.m4a,.ogg,.webm`},attrs:{hidden:!0},on:{change:()=>{let e=xr.files?.[0];e!==void 0&&e.arrayBuffer().then(t=>_r(t,e.name)),xr.value=``}}}),Sr=document.getElementById(`app`),Cr=n(`header`,{class:`topbar`}),wr=n(`aside`,{class:`sounds tracks`,attrs:{"aria-label":`Tracks`}}),Tr=n(`div`,{class:`sound-view song-view`}),J=n(`div`,{class:`floating playback transport`}),Er=n(`div`,{class:`toast`,attrs:{role:`status`,"aria-live":`polite`}}),Dr=n(`aside`,{class:`inspector`,attrs:{"aria-label":`Settings`}}),Y=n(`div`,{class:`stage`},Tr,J,Er);Sr.append(Cr,n(`main`,{class:`layout lab`},wr,n(`section`,{class:`workspace`},Y),Dr),xr);var Or=n(`div`,{class:`sound-head`}),kr=n(`canvas`,{class:`arrangement`}),Ar=n(`div`,{class:`grid-head`}),jr=n(`canvas`,{class:`note-grid`}),Mr=n(`div`,{class:`song-scroll`},n(`div`,{class:`song-card`},kr),Ar,n(`div`,{class:`song-card grid-card`},jr));Tr.append(Or,Mr);var Nr=e=>`${Math.floor(e/60)}:${String(Math.floor(e%60)).padStart(2,`0`)}`,Pr=()=>{let e=K.position()??z.cue,t=Math.max(320,Mr.clientWidth-2),n=Math.max(8,(t-64-8)/Math.max(1,M(z.song))*z.zoom);return{song:z.song,track:z.track,beat:n,snap:z.snap,selection:z.selection,playhead:e===void 0?void 0:e/j(z.song),showReference:z.showReference,chosen:z.chosen,comments:z.review.comments,heard:Hn()}},X=()=>{let e=Pr();wn(kr,e),B()!==void 0&&En(jr,e)},Fr=()=>{X(),Z()},Ir=()=>{W=void 0,gr(),lr(),$()},Lr={new:`To review`,approved:`Approved`,changes:`Changes asked`},Rr=()=>{let e=z.song;i(Cr,n(`div`,{class:`topbar-start`},n(`div`,{class:`brand`},s(),n(`span`,{},`Music lab`),n(`a`,{class:`by`,title:`Ritmo, the agent-first type safe ECS runtime`,attrs:{href:zn,target:`_blank`,rel:`noopener`}},`by `,n(`b`,{},`ritmo`))),z.loaded&&n(`div`,{class:`file`,title:z.songFile},n(`span`,{class:`file-name`},z.name),n(`span`,{class:`file-meta`},`${e.tempo} BPM · ${e.key} · ${e.bars} bars`))),n(`div`,{class:`topbar-center`}),n(`div`,{class:`topbar-end`},n(`div`,{class:`button-group`},c(`Undo`,()=>V.undo(),{variant:`ghost`,icon:`undo`,iconOnly:!0,disabled:!V.canUndo(),title:`Undo (Ctrl/Cmd+Z)`}),c(`Redo`,()=>V.redo(),{variant:`ghost`,icon:`redo`,iconOnly:!0,disabled:!V.canRedo(),title:`Redo (Ctrl/Cmd+Shift+Z)`})),n(`span`,{class:`divider`}),n(`span`,{class:`save-state ${z.saving}`,title:`Changes are written to the song and its review as you make them`},z.saving===`saved`?o(`check`):z.saving===`failed`?o(`alert`):null,z.saving===`saved`?`Saved`:z.saving===`saving`?`Saving…`:`Not saved`),c(`MIDI`,()=>pr(new Uint8Array(nn(e)),`${mr()}.mid`,`audio/midi`),{variant:`secondary`,icon:`music`,disabled:!z.loaded,title:`Download the notes as a MIDI file, for any music program`}),c(`WAV`,()=>pr(ue(xt(e,{cache:Vn})),`${mr()}.wav`,`audio/wav`),{variant:`primary`,icon:`download`,disabled:!z.loaded,title:`Download the song as a WAV file`})))},zr=()=>{let e=z.song,t=e.reference,r=e=>n(`div`,{class:`sound-row track-row${e.on?` on`:``}`,attrs:{role:`option`,"aria-selected":String(e.on),tabindex:0},on:{click:e.click}},n(`span`,{class:`lane-swatch`,attrs:{style:`--color:${e.color}`}}),n(`span`,{class:`track-text`},n(`span`,{class:`sound-name`},e.name),n(`span`,{class:`track-meta`},e.meta)),e.extra??[]),a=(e,t,r,i)=>n(`button`,{class:`track-toggle${t?` on`:``}`,title:r,attrs:{type:`button`,"aria-pressed":String(t)},on:{click:e=>{e.stopPropagation(),i()}}},e);i(wr,n(`div`,{class:`sounds-head tracks-head`},n(`h2`,{},`Reference`),z.recording===void 0?n(`div`,{class:`button-row`},c(z.listening?`Listening…`:`Record a hum`,()=>void br(),{small:!0,variant:t===void 0?`primary`:`secondary`,icon:`mic`,disabled:z.listening,title:`Hum, whistle, or sing the tune; the agent builds the song around it`}),c(`Audio file`,()=>xr.click(),{small:!0,variant:`ghost`,icon:`open`,disabled:z.listening,title:`A recording or a song to take the tune, tempo, and key from`})):n(`div`,{class:`recording`},n(`span`,{class:`record-dot`}),n(`span`,{class:`record-time`},`0:00`),n(`span`,{class:`record-level`},n(`span`,{})),c(`Stop`,()=>void br(),{small:!0,variant:`primary`,icon:`stop`})),t!==void 0&&r({on:K.playingAlone(),color:`#f3f1ec`,name:`Hum`,meta:`${t.tempo} BPM · ${t.key} · ${t.notes.length} notes`,click:fr,extra:[G!==void 0&&(K.playingAlone()?a(`■`,!0,`Stop the hum`,fr):a(`▶`,!1,`Play the hum alone`,fr))]})),n(`div`,{class:`sound-rows`},n(`h2`,{class:`rows-title`},`Tracks`),e.tracks.length===0&&n(`p`,{class:`hint empty-list`},`No tracks yet. Record a hum and ask your agent for a song, or add a track.`),e.tracks.map((e,t)=>r({on:t===z.track,color:x[e.instrument].color,name:e.name,meta:`${x[e.instrument].label} · ${e.notes.length} notes`,click:()=>Br(t),extra:[a(`M`,e.mute===!0,`Mute this track (saved in the song)`,()=>{Zn(t,{...e,mute:e.mute!==!0||void 0}),H()}),a(`S`,z.solo.has(t),`Hear only this track (and other soloed ones); not saved`,()=>{z.solo.has(t)?z.solo.delete(t):z.solo.add(t),W=void 0,lr(),$()})]})),c(`Add a track`,()=>{U({...z.song,tracks:[...z.song.tracks,{name:`Track ${z.song.tracks.length+1}`,instrument:`piano`,volume:.8,pan:0,reverb:.2,notes:[]}]}),z.track=z.song.tracks.length-1,H()},{small:!0,variant:`ghost`,icon:`plus`,class:`add-track`})))},Br=e=>{e!==z.track&&(z.track=e,z.chosen=void 0,$())},Vr=(e,t=``)=>n(`span`,{class:`stage-pill ${t}`},e),Hr=()=>{let e=z.song,t=q?.follows;i(Or,n(`div`,{class:`sound-title`},n(`h1`,{},mr()),Vr(Lr[z.review.status],z.review.status),Vr(`${e.tempo} BPM`),Vr(e.key),Vr(`${e.bars} bars · ${Nr(Je(e))}`),e.loop&&Vr(`Loops`),t!==void 0&&n(`span`,{class:`stage-pill ${t.share>=.5?`approved`:`changes`}`,title:`${t.track} plays this share of the hum's notes`},`Follows the hum ${Math.round(t.share*100)}%`)),e.about!==void 0&&n(`p`,{class:`sound-about`},e.about))},Ur=()=>{let e=B();if(e===void 0){i(Ar),jr.hidden=!0;return}jr.hidden=!1;let t=x[e.instrument].kind===`drums`;i(Ar,n(`span`,{class:`lane-swatch`,attrs:{style:`--color:${x[e.instrument].color}`}}),n(`h2`,{},e.name),n(`span`,{class:`grid-meta`},x[e.instrument].label),n(`span`,{class:`grid-hint`},t?`Click to add a hit · drag to move · double-click to remove`:`Click to add a note · drag to move · drag its end to stretch · double-click to remove`),n(`span`,{class:`spacer`}),!t&&Wr().reference!==void 0&&h(`Hum`,z.showReference,e=>{z.showReference=e,X()},`Show the hum's notes and pitch line under this track`),l(String(z.snap),[{id:`0.25`,label:`1/16`,title:`Snap to sixteenth notes (a quarter beat)`},{id:`0.5`,label:`1/8`,title:`Snap to eighth notes (half a beat)`},{id:`1`,label:`1/4`,title:`Snap to beats`}],e=>{z.snap=Number(e),Ur(),X()},{label:`Snap`}))},Wr=()=>z.song,Z=()=>{let e=K.position(),t=e!==void 0,r=(e??z.cue??ar())/j(z.song),a=`${Math.floor(r/z.song.beatsPerBar)+1}.${Math.floor(r%z.song.beatsPerBar)+1} / ${z.song.bars}`,o=!t&&z.cue===void 0;(J.dataset.label!==a||J.dataset.on!==String(t)||J.dataset.stopped!==String(o)||J.dataset.hum!==String(z.withHum))&&(J.dataset.label=a,J.dataset.on=String(t),J.dataset.stopped=String(o),J.dataset.hum=String(z.withHum),i(J,c(t?`Pause`:`Play`,or,{variant:`ghost`,icon:t?`pause`:`play`,iconOnly:!0,title:t?`Pause (Space)`:z.cue===void 0?`Play (Space)`:`Play from the marker (Space)`}),c(`Stop`,sr,{variant:`ghost`,icon:`stop`,iconOnly:!0,disabled:o,title:`Stop and go back to the start`}),n(`span`,{class:`frame-counter`,title:`Bar.beat / bars`},a),n(`span`,{class:`divider`}),z.song.reference?.audio!==void 0&&c(`With the hum`,()=>{z.withHum=!z.withHum,t&&ir(),Z()},{variant:`ghost`,icon:`mic`,iconOnly:!0,pressed:z.withHum,class:z.withHum?`on`:``,title:z.withHum?`Playing the hum with the song`:`Play the hum with the song, lined up`}),c(`Zoom out`,()=>{z.zoom=Math.max(1,z.zoom/1.5),X()},{variant:`ghost`,icon:`minus`,iconOnly:!0}),c(`Zoom in`,()=>{z.zoom=Math.min(8,z.zoom*1.5),X()},{variant:`ghost`,icon:`plus`,iconOnly:!0})))},Gr=()=>{z.loaded?(Hr(),Ur(),kr.style.height=`${xn(z.song)}px`,X()):i(Or,n(`div`,{class:`empty-card lab-empty`},n(`div`,{class:`empty-icon`},s()),n(`h2`,{},`Opening the song…`)))},Kr=[{id:`new`,label:`To review`,icon:`sparkle`,title:`Not reviewed yet`},{id:`approved`,label:`Approved`,icon:`check`,title:`It sounds right`},{id:`changes`,label:`Changes`,icon:`alert`,title:`Ask the agent for changes: say what in the note and the comments`}],qr=()=>b({id:`music-review`,title:`Review`,help:'Approve the song, or ask for changes in your own words ("calmer", "more like the hum at the end"). Comments on bars say where. The agent reads it all with `ritmo-tools music notes`, with a list of what you edited by hand.',onToggle:$},l(z.review.status,Kr,e=>{z.review={...z.review,status:e},Xn()},{label:`Status`,class:`fill`}),n(`label`,{class:`input note`},n(`textarea`,{props:{value:z.review.note,rows:3},attrs:{placeholder:`A note on the whole song (optional)`,"aria-label":`Note`},on:{input:e=>{let t=e.currentTarget;t instanceof HTMLTextAreaElement&&(z.review={...z.review,note:t.value},Yn({review:!0}),V.schedule(),Rr())}}})),z.review.edits.length>0&&ie(`Your edits`,n(`ul`,{class:`edits`},z.review.edits.map(e=>n(`li`,{},e))))),Jr=()=>{let e=z.selection,t=e===void 0?void 0:Ye(z.song,e.from,e.to),r=()=>{if(e===void 0||z.draft.trim()===``)return;let t={id:`c${Date.now().toString(36)}`,from:e.from,to:e.to,track:z.draftTrack===`all`?void 0:z.draftTrack,text:z.draft.trim()};z.review={...z.review,comments:[...z.review.comments,t]},z.draft=``,Xn()};return b({id:`music-comments`,title:`Comments`,help:`Drag across the bar numbers to pick bars, then say what should change there. Comments are pinned to those bars (and a track, if you choose one).`,onToggle:$,action:n(`span`,{class:`meta`},String(z.review.comments.length))},e===void 0?_(`Drag across the bar numbers above the tracks to pick bars to comment on.`):ie(`On ${t}`,u(`Track`,m(z.draftTrack,[{id:`all`,label:`Every track`},...z.song.tracks.map(e=>({id:e.name,label:e.name}))],e=>{z.draftTrack=e},`Track`)),n(`label`,{class:`input note`},n(`textarea`,{props:{value:z.draft,rows:2},attrs:{placeholder:`What should change here?`,"aria-label":`Comment`},on:{input:e=>{let t=e.currentTarget;t instanceof HTMLTextAreaElement&&(z.draft=t.value)},keydown:e=>{e.key===`Enter`&&(e.metaKey||e.ctrlKey)&&r()}}})),n(`div`,{class:`button-row`},c(`Add comment`,r,{small:!0,variant:`primary`,icon:`comment`}),c(`Clear bars`,()=>{z.selection=void 0,z.cue=void 0,$()},{small:!0,variant:`ghost`}))),z.review.comments.length>0&&n(`ul`,{class:`comments`},[...z.review.comments].sort((e,t)=>e.from-t.from).map(e=>n(`li`,{on:{click:()=>{z.selection={from:e.from,to:e.to},z.cue=void 0,$()}}},n(`span`,{class:`comment-where`},`${Ye(z.song,e.from,e.to)}${e.track===void 0?``:` · ${e.track}`}`),n(`span`,{class:`comment-text`},e.text),c(`Remove`,()=>{z.review={...z.review,comments:z.review.comments.filter(t=>t.id!==e.id)},Xn()},{variant:`ghost`,icon:`trash`,iconOnly:!0,small:!0})))))},Yr=()=>{let e=z.song,t=Ke(e.key),r=e.key.split(` `)[0]??`C`,i=[`C`,`C#`,`D`,`Eb`,`E`,`F`,`F#`,`G`,`Ab`,`A`,`Bb`,`B`];return b({id:`music-song`,title:`Song`,help:`The whole song: how fast (beats per minute), its key (which notes sound at home), and how long. A looping song repeats without a seam in the game.`,onToggle:$},u(`Tempo`,f(e.tempo,t=>{U({...e,tempo:t}),H()},{min:30,max:300,step:1,label:`Tempo`,suffix:`BPM`})),u(`Key`,n(`div`,{class:`key-picker`},m(i.includes(r)?r:`C`,i.map(e=>({id:e,label:e})),n=>{U({...e,key:`${n} ${t?.scale??`major`}`}),H()},`Key note`),m(t?.scale??`major`,Ge.map(e=>({id:e,label:e})),t=>{U({...e,key:`${r} ${t}`}),H()},`Scale`))),u(`Bars`,f(e.bars,t=>{U({...e,bars:Math.max(1,Math.round(t))}),H()},{min:1,max:256,step:1,label:`Bars`})),h(`Loop`,e.loop,t=>{U({...e,loop:t}),H()},`Game music mostly loops: the end wraps into the start without a seam`),h(`Compressor`,e.compressor,t=>{U({...e,compressor:t}),H()},`Squeezes the whole mix louder and punchier, its peaks held under -1 dBFS: for rock, drum & bass, and electronic songs`),g(`Master`,e.master,{min:0,max:1.5,step:.01},e=>`${Math.round(e*100)}%`,e=>{U({...z.song,master:e})},()=>H(),`The volume over the whole song`))},Xr=()=>{let e=B();if(e===void 0)return!1;let t=z.track,r=(e,n=!0)=>{Zn(t,e),n&&H()};return b({id:`music-track`,title:`Track`,help:`The chosen track: its instrument (try another: the notes stay), how loud it is, where it sits left to right, and how much room echo it has.`,onToggle:$,action:n(`span`,{class:`meta`},`${e.notes.length} notes`)},u(`Name`,p(e.name,t=>r({...e,name:t.trim()===``?e.name:t.trim()}),{label:`Track name`})),u(`Instrument`,m(e.instrument,fe.map(e=>({id:e,label:x[e].label})),t=>{if(x[e.instrument].kind!==x[t].kind)return Q(`Drums and pitched instruments use different notes: add a new track for this one.`);r({...e,instrument:t})},`Instrument`)),_(x[e.instrument].about),g(`Volume`,e.volume,{min:0,max:1.5,step:.01},e=>`${Math.round(e*100)}%`,t=>r({...e,volume:t},!1),()=>H()),g(`Pan`,e.pan,{min:-1,max:1,step:.05},e=>Math.abs(e)<.025?`centre`:e<0?`L ${Math.round(-e*100)}`:`R ${Math.round(e*100)}`,t=>r({...e,pan:t},!1),()=>H()),g(`Room`,e.reverb,{min:0,max:1,step:.01},e=>`${Math.round(e*100)}%`,t=>r({...e,reverb:t},!1),()=>H(),`How much of the track goes to the room's echo`),z.chosen!==void 0&&e.notes[z.chosen]!==void 0&&ie(`Note ${e.notes[z.chosen].pitch}`,g(`Strength`,e.notes[z.chosen].velocity,{min:.05,max:1,step:.05},e=>`${Math.round(e*100)}%`,t=>{let n=z.chosen;n!==void 0&&(r({...e,notes:e.notes.map((e,r)=>r===n?{...e,velocity:t}:e)},!1),X())},()=>H(),`How hard the note is played`)),n(`div`,{class:`button-row`},c(`Remove track`,()=>{U({...z.song,tracks:z.song.tracks.filter((e,n)=>n!==t)}),z.track=Math.max(0,t-1),z.solo.clear(),H()},{small:!0,variant:`danger`,icon:`trash`})))},Zr=()=>{let e=z.song.reference;return b({id:`music-reference`,title:`Reference`,help:`A hum, a whistle, a tapped beat, or a recorded tune. The tool listens to it for its tempo, key, and melody, so the agent can build the song around it. Hum one note at a time, close to the microphone.`,onToggle:$,closed:e!==void 0},e===void 0?[_(`No reference yet. Record a hum (top left) or choose an audio file; the tool hears its tempo, key, and tune.`)]:[u(`Tempo`,n(`span`,{class:`value`},`${e.tempo} BPM`)),u(`Key`,n(`span`,{class:`value`},e.key)),u(`Notes`,n(`span`,{class:`value`},`${e.notes.length} · ${Ye(z.song,0,e.length)}`)),e.tempo!==z.song.tempo&&v(`warn`,`The song is at ${z.song.tempo} BPM and the hum at ${e.tempo}: they will not line up. `,c(`Use ${e.tempo} BPM`,()=>{U({...z.song,tempo:e.tempo}),H()},{small:!0,variant:`secondary`})),n(`div`,{class:`button-row`},c(`Play the hum`,fr,{small:!0,variant:`secondary`,icon:`play`,disabled:G===void 0}),c(`Remove`,()=>{U({...z.song,reference:void 0}),G=void 0,H()},{small:!0,variant:`ghost`,icon:`trash`}))])},Qr=()=>{let e=q?.issues.filter(e=>e.severity===`error`)??[];return b({id:`music-check`,title:`Check`,help:"What can be measured without listening: the same numbers `ritmo-tools music check` gives the agent.",onToggle:$,closed:!0,action:q===void 0?void 0:ee(e.length>0?`Errors`:q.issues.length>0?`Warnings`:`Clean`,e.length>0?`bad`:q.issues.length>0?`warn`:`good`)},q===void 0?_(`Measuring…`):[u(`Loudness`,n(`span`,{class:`value`},`${q.loudness} dBFS`),`Over the whole song: about -18 to -24 sits well under sound effects`),u(`Peak`,n(`span`,{class:`value`},`${q.peak} dBFS`),`The loudest sample: above 0 clips`),q.issues.length===0?v(`good`,`Nothing to fix.`):n(`ul`,{class:`issues`},q.issues.map(e=>n(`li`,{class:e.severity},o(`alert`),n(`span`,{},e.track===void 0?e.message:`${e.track}: ${e.message}`))))])},$r=()=>n(`footer`,{class:`about`},n(`div`,{class:`ruler`,attrs:{"aria-hidden":`true`}}),n(`div`,{class:`about-head`},s(),`Music lab, by Ritmo`),n(`p`,{},`Game music as JSON songs. Agents listen, write, and check them from the command line: `,n(`code`,{},`ritmo-tools music listen`),`, `,n(`code`,{},`ritmo-tools music check`),`, `,n(`code`,{},`ritmo-tools music notes`),`. What you edit, record, and say here is saved to `,n(`code`,{},z.name),` and its review.`)),ei=()=>{let e=Dr.scrollTop;i(Dr,z.loaded&&[qr(),Jr(),Xr(),Yr(),Zr(),Qr()],$r()),Dr.scrollTop=e},ti={view:Pr,add:e=>{let t=B();return t===void 0?-1:(Zn(z.track,{...t,notes:[...t.notes,e]}),z.chosen=t.notes.length,W=void 0,X(),t.notes.length)},move:(e,t,n)=>{let r=B();r!==void 0&&(Zn(z.track,{...r,notes:r.notes.map((n,r)=>r===e?t:n)}),z.chosen=e,n?H():X())},remove:e=>{let t=B();t!==void 0&&(Zn(z.track,{...t,notes:t.notes.filter((t,n)=>n!==e)}),z.chosen=void 0,H())},choose:e=>{z.chosen=e,ei(),X()},audition:(e,t)=>{let n=B();n!==void 0&&ur(n.instrument,e,t)},seek:e=>{z.selection=void 0;let t=Math.floor(e)*j(z.song);K.position()===void 0?(z.cue=t,$()):ir(t)},region:(e,t)=>{z.selection={from:e,to:t},z.cue=void 0,X(),ei(),Z()}};Nn(jr,ti),Mn(kr,{...ti,pickTrack:Br,cue:e=>{K.position()===void 0&&(z.cue=Math.floor(e)*j(z.song),$())}});var ni;function Q(e){z.message=e,ni!==void 0&&clearTimeout(ni),i(Er,n(`span`,{},e),c(`Dismiss`,()=>Er.classList.remove(`on`),{variant:`ghost`,icon:`close`,iconOnly:!0,small:!0})),Er.classList.add(`on`),ni=setTimeout(()=>Er.classList.remove(`on`),6e3)}function $(){Rr(),zr(),Gr(),Z(),ei()}window.addEventListener(`keydown`,e=>{let t=e.target;if(t instanceof HTMLInputElement&&t.type!==`range`&&t.type!==`checkbox`||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement)return;let n=e.metaKey||e.ctrlKey;n&&e.key.toLowerCase()===`z`?(e.preventDefault(),e.shiftKey?V.redo():V.undo()):n&&e.key.toLowerCase()===`y`?(e.preventDefault(),V.redo()):e.key===` `?(e.preventDefault(),or()):(e.key===`Delete`||e.key===`Backspace`)&&z.chosen!==void 0?(e.preventDefault(),ti.remove(z.chosen)):e.key===`Escape`&&(z.chosen=void 0,z.selection=void 0,z.cue=void 0,$())}),window.addEventListener(`resize`,()=>X()),Y.addEventListener(`dragover`,e=>{e.preventDefault(),Y.classList.add(`dropping`)}),Y.addEventListener(`dragleave`,()=>Y.classList.remove(`dropping`)),Y.addEventListener(`drop`,e=>{e.preventDefault(),Y.classList.remove(`dropping`);let t=e.dataTransfer?.files[0];t!==void 0&&t.arrayBuffer().then(e=>_r(e,t.name))}),$(),er();
@@ -0,0 +1 @@
1
+ :root{--ink:#0e1013;--paper:#f3f1ec;--tick:#ff5a36;--graphite:#262a31;--steel:#8a8f98;--line:#d9d5cc;--bg:#efede7;--surface:#faf9f6;--surface-2:#f1efea;--surface-3:#e6e3dc;--border:#e3e0d8;--border-strong:var(--line);--text:var(--ink);--text-2:#4b4f57;--muted:#80858e;--primary:var(--ink);--primary-hover:#262a31;--primary-text:var(--paper);--accent:var(--tick);--accent-soft:#ff5a361f;--accent-text:#fff;--good:#1b7a4e;--good-soft:#1b7a4e17;--warn:#a35a00;--warn-soft:#c478001c;--bad:#c23a2b;--bad-soft:#c23a2b17;--stage:var(--ink);--stage-dots:#f3f1ec0d;--floating:#262a31e6;--floating-border:#f3f1ec14;--shadow-float:0 8px 24px #00000052, 0 0 0 1px var(--floating-border);--focus:0 0 0 2px var(--surface), 0 0 0 4px #ff5a3673;--radius:6px;--radius-lg:10px;--control:28px;--font:"Inter", "InterVariable", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;--mono:"JetBrains Mono", "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}@media (prefers-color-scheme:dark){:root{--bg:#0b0d10;--surface:var(--ink);--surface-2:#171a1f;--surface-3:var(--graphite);--border:#1f2328;--border-strong:#30353d;--text:var(--paper);--text-2:#bdbab3;--muted:var(--steel);--primary:var(--paper);--primary-hover:#fff;--primary-text:var(--ink);--accent-soft:#ff5a3629;--good:#4cc38a;--good-soft:#4cc38a1f;--warn:#f0a843;--warn-soft:#f0a8431f;--bad:#ff7a5c;--bad-soft:#ff7a5c1f;--stage:#08090b;--focus:0 0 0 2px var(--surface), 0 0 0 4px #ff5a368c;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}}*{box-sizing:border-box}html,body{background:var(--bg);height:100%;color:var(--text);font:13px/1.45 var(--font);font-feature-settings:"cv11", "ss01";-webkit-font-smoothing:antialiased;-webkit-tap-highlight-color:transparent;margin:0}#app{flex-direction:column;height:100%;display:flex}[hidden]{display:none!important}h1,h2,h3,p{margin:0}:focus-visible{box-shadow:var(--focus);outline:none}.icon{flex:none;width:16px;height:16px;display:inline-flex}.icon svg{width:100%;height:100%}.btn{height:var(--control);border-radius:var(--radius);color:var(--text);font:500 13px/1 var(--font);white-space:nowrap;cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:1px solid #0000;justify-content:center;align-items:center;gap:6px;padding:0 10px;transition:background-color .12s,border-color .12s,color .12s;display:inline-flex}.btn .icon{width:15px;height:15px}.btn.small{gap:4px;height:24px;padding:0 8px;font-size:12px}.btn.small .icon{width:14px;height:14px}.btn.icon-only{width:var(--control);padding:0}.btn.small.icon-only{width:24px}.btn.block{width:100%}.btn.primary{background:var(--primary);color:var(--primary-text);box-shadow:0 1px 2px #0000001f}.btn.primary:hover:not(:disabled){background:var(--primary-hover)}.btn.secondary{background:var(--surface);border-color:var(--border-strong);box-shadow:0 1px 1px #00000008}.btn.secondary:hover:not(:disabled){background:var(--surface-2)}.btn.ghost{color:var(--text-2)}.btn.ghost:hover:not(:disabled),.btn.ghost.on,.btn.ghost[aria-pressed=true]{background:var(--surface-3);color:var(--text)}.btn:disabled{opacity:.45;cursor:default;box-shadow:none}.segmented{background:var(--surface-3);border-radius:8px;gap:2px;padding:2px;display:inline-flex}.segmented button{height:24px;color:var(--text-2);font:500 12.5px/1 var(--font);white-space:nowrap;cursor:pointer;background:0 0;border:0;border-radius:6px;justify-content:center;align-items:center;gap:6px;padding:0 10px;transition:background-color .12s,color .12s;display:inline-flex}.segmented button:hover{color:var(--text)}.segmented button.on{background:var(--surface);color:var(--text);box-shadow:0 1px 2px #0000001a,0 0 0 .5px #0000000f}.segmented.fill{width:100%;display:flex}.segmented.fill button{flex:1;padding:0 4px}.topbar{border-bottom:1px solid var(--border);background:var(--surface);grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:12px;height:48px;padding:0 12px 0 16px;display:grid}.topbar-start,.topbar-end{align-items:center;gap:8px;min-width:0;display:flex}.topbar-end{justify-content:flex-end}.brand{letter-spacing:-.01em;white-space:nowrap;align-items:center;gap:8px;font-size:13.5px;font-weight:600;display:flex}.mark{width:18px;height:18px;color:var(--text);flex:none;display:inline-flex}.mark svg{width:100%;height:100%}.by{color:var(--muted);font-family:var(--mono);font-size:11.5px;font-weight:500;text-decoration:none}.by:hover{color:var(--text)}.by b{color:var(--text-2);font-weight:600}.file{border-left:1px solid var(--border);align-items:baseline;gap:8px;min-width:0;padding-left:12px;font-size:12.5px;display:flex}.file-name{text-overflow:ellipsis;white-space:nowrap;color:var(--text);font-weight:500;overflow:hidden}.file-meta{color:var(--muted);font-variant-numeric:tabular-nums;flex:none}.button-group{gap:2px;display:flex}.divider{background:var(--border);width:1px;height:20px}.layout{flex:1;grid-template-columns:minmax(0,1fr) 320px;min-height:0;display:grid}.workspace{min-width:0;min-height:0;display:flex}.stage{background-color:var(--stage);background-image:radial-gradient(var(--stage-dots) 1px, transparent 1px);background-size:16px 16px;flex:1;min-height:0;position:relative;overflow:hidden}.stage.dropping:after{content:"";border:2px dashed var(--accent);border-radius:var(--radius-lg);background:var(--accent-soft);pointer-events:none;position:absolute;inset:12px}.view{touch-action:none;cursor:grab;width:100%;height:100%;position:absolute;inset:0}.floating{background:var(--floating);box-shadow:var(--shadow-float);-webkit-backdrop-filter:blur(12px);color:#ececef;border-radius:10px;align-items:center;gap:6px;padding:4px;display:flex;position:absolute}.tools{bottom:16px;left:50%;transform:translate(-50%)}.zoom{gap:0;bottom:16px;right:16px}.floating .segmented{background:0 0;padding:0}.floating .segmented+.segmented{border-left:1px solid #ffffff1a;border-radius:0;padding-left:6px}.floating .segmented button{color:#a8a8b2;height:30px;padding:0 10px}.floating .segmented button:hover{color:#fff;background:#ffffff0f}.floating .segmented button.on{background:var(--accent);color:#fff;box-shadow:none}.floating .btn.ghost{color:#a8a8b2}.floating .btn.ghost:hover:not(:disabled){color:#fff;background:#ffffff14}.zoom-value{text-align:center;font-variant-numeric:tabular-nums;color:#d4d4da;min-width:44px;font-size:12px}.toast{background:var(--floating);max-width:min(520px,100% - 32px);box-shadow:var(--shadow-float);-webkit-backdrop-filter:blur(12px);color:#ececef;opacity:0;pointer-events:none;border-radius:10px;align-items:center;gap:8px;padding:6px 6px 6px 14px;font-size:12.5px;transition:opacity .18s,transform .18s;display:flex;position:absolute;top:16px;left:50%;transform:translate(-50%,-8px)}.toast.on{opacity:1;pointer-events:auto;transform:translate(-50%)}.toast .btn.ghost{color:#a8a8b2}.toast .btn.ghost:hover{color:#fff;background:#ffffff14}.empty{pointer-events:none;place-items:center;padding:24px;display:grid;position:absolute;inset:0}.empty-card{color:#a8a8b2;text-align:center;pointer-events:auto;background:#ffffff05;border:1px dashed #ffffff29;border-radius:14px;flex-direction:column;align-items:center;gap:8px;width:min(420px,100%);padding:32px 28px 24px;display:flex}.empty-icon{color:#ececef;background:#ffffff0f;border-radius:12px;place-items:center;width:44px;height:44px;margin-bottom:4px;display:grid}.empty-icon .icon,.empty-icon .mark{width:22px;height:22px;color:var(--paper)}.empty-card h2{color:#fff;letter-spacing:-.01em;font-size:16px;font-weight:600}.empty-actions{gap:8px;margin-top:12px;display:flex}.empty-actions .btn{height:32px;padding:0 14px}.empty-actions .btn.secondary{color:#ececef;background:#ffffff0f;border-color:#ffffff1f}.empty-actions .btn.secondary:hover{background:#ffffff1a}.empty-note{color:#7c7c85;margin-top:12px;font-size:12px}.inspector{overscroll-behavior:contain;border-left:1px solid var(--border);background:var(--surface);overflow-y:auto}.section{border-bottom:1px solid var(--border)}.section-head{align-items:center;gap:4px;min-height:44px;padding:0 12px 0 8px;display:flex}.section-toggle{border-radius:var(--radius);min-width:0;height:32px;color:var(--text);font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;flex:1;align-items:center;gap:4px;padding:0 4px;display:flex}.section-toggle .icon{width:14px;height:14px;color:var(--muted);transition:transform .15s}.section.closed .section-toggle .icon{transform:rotate(-90deg)}.section-toggle h2{font-family:var(--mono);letter-spacing:-.01em;font-size:12px;font-weight:600}.section-action{align-items:center;gap:6px;display:flex}.section-body{flex-direction:column;gap:8px;padding:0 16px 16px;display:flex}.help{border-radius:var(--radius);background:var(--surface-2);color:var(--text-2);padding:10px 12px;font-size:12px;line-height:1.5}.group{border-top:1px solid var(--border);flex-direction:column;gap:8px;margin-top:4px;padding-top:12px;display:flex}.group h3{color:var(--muted);font-size:11.5px;font-weight:500}.stack{flex-direction:column;gap:8px;display:flex}.meta{color:var(--muted);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:12px}.hint{color:var(--muted);font-size:12px}.summary{color:var(--text-2);font-size:12.5px}.prop{min-height:var(--control);grid-template-columns:92px minmax(0,1fr);align-items:center;gap:8px;display:grid}.prop-label{color:var(--text-2);font-size:12.5px}.prop-control{align-items:center;gap:8px;min-width:0;display:flex}.prop-control>*{flex:1;min-width:0}.pair{grid-template-columns:1fr 1fr;gap:6px;display:grid}.combo{align-items:flex-start;gap:6px;display:flex}.combo>.input{flex:1}.inline-action{min-height:var(--control);color:var(--muted);justify-content:space-between;align-items:center;gap:8px;font-size:12px;display:flex}.input{min-width:0;height:var(--control);border-radius:var(--radius);background:var(--surface-2);border:1px solid #0000;align-items:center;transition:border-color .12s,background-color .12s;display:flex;position:relative}.input:hover{border-color:var(--border-strong)}.input:focus-within{border-color:var(--accent);background:var(--surface);box-shadow:0 0 0 3px var(--accent-soft)}.input input,.input select,.input textarea{width:100%;min-width:0;height:100%;color:var(--text);font:13px/1 var(--font);font-variant-numeric:tabular-nums;background:0 0;border:0;outline:none;flex:1;padding:0 8px}.input input::placeholder,.input textarea::placeholder{color:var(--muted)}.input:has(textarea){height:auto}.input textarea{resize:vertical;line-height:1.4;font-family:var(--mono);padding:6px 8px;font-size:12px}.affix{color:var(--muted);flex:none;padding-left:8px;font-size:11.5px;font-weight:500}.affix+input{padding-left:6px}input+.affix{padding:0 8px 0 0}.input.select select{appearance:none;cursor:pointer;padding-right:28px}.input.select .icon{width:14px;height:14px;color:var(--muted);pointer-events:none;position:absolute;right:8px}.switch-row{min-height:var(--control);color:var(--text-2);cursor:pointer;justify-content:space-between;align-items:center;gap:12px;font-size:12.5px;display:flex}.switch{appearance:none;background:var(--surface-3);width:30px;height:18px;box-shadow:inset 0 0 0 1px var(--border-strong);cursor:pointer;border-radius:999px;flex:none;margin:0;transition:background-color .15s,box-shadow .15s;position:relative}.switch:after{content:"";background:#fff;border-radius:50%;width:14px;height:14px;transition:transform .15s;position:absolute;top:2px;left:2px;box-shadow:0 1px 2px #00000040}.switch:checked{background:var(--primary);box-shadow:none}.switch:checked:after{background:var(--primary-text);transform:translate(12px)}.prop.slider output{min-width:36px;color:var(--text);font-variant-numeric:tabular-nums;text-align:right;flex:none;font-size:12px}.range{--fill:50%;appearance:none;cursor:pointer;background:0 0;height:20px;margin:0}.range::-webkit-slider-runnable-track{background:linear-gradient(to right, var(--primary) var(--fill), var(--surface-3) var(--fill));border-radius:999px;height:4px}.range::-moz-range-track{background:var(--surface-3);border-radius:999px;height:4px}.range::-moz-range-progress{background:var(--primary);border-radius:999px;height:4px}.range::-webkit-slider-thumb{appearance:none;background:#fff;border:0;border-radius:50%;width:14px;height:14px;margin-top:-5px;box-shadow:0 0 0 1px #0000001f,0 1px 3px #00000040}.range::-moz-range-thumb{background:#fff;border:0;border-radius:50%;width:14px;height:14px;box-shadow:0 0 0 1px #0000001f,0 1px 3px #00000040}.range:focus-visible{box-shadow:none}.range:focus-visible::-webkit-slider-thumb{box-shadow:var(--focus)}.badge{white-space:nowrap;border-radius:999px;align-items:center;gap:4px;height:20px;padding:0 7px;font-size:11.5px;font-weight:500;display:inline-flex}.badge .icon{width:12px;height:12px}.badge.neutral{background:var(--surface-3);color:var(--text-2)}.badge.accent{background:var(--accent-soft);color:var(--accent)}.badge.good{background:var(--good-soft);color:var(--good)}.badge.warn{background:var(--warn-soft);color:var(--warn)}.badge.bad{background:var(--bad-soft);color:var(--bad)}.callout{border-radius:var(--radius);gap:8px;padding:8px 10px;font-size:12px;line-height:1.45;display:flex}.callout .icon{width:14px;height:14px;margin-top:1px}.callout.good{background:var(--good-soft);color:var(--good)}.callout.warn{background:var(--warn-soft);color:var(--warn)}.callout.bad{background:var(--bad-soft);color:var(--bad)}.callout.neutral,.callout.accent{background:var(--surface-2);color:var(--text-2)}.issues{flex-direction:column;gap:6px;margin:0;padding:0;font-size:12px;line-height:1.45;list-style:none;display:flex}.issues li{color:var(--text-2);gap:8px;display:flex}.issues .icon{width:14px;height:14px;margin-top:1px}.issues .error .icon{color:var(--bad)}.issues .warning .icon{color:var(--warn)}.chip{background:var(--color);border-radius:4px;flex:none;width:18px;height:18px;display:inline-block;box-shadow:inset 0 0 0 1px #00000024}.chips{gap:4px;display:flex}.swatches{grid-template-columns:repeat(auto-fill,minmax(28px,1fr));gap:6px;display:grid}.swatch{aspect-ratio:1;background:var(--color);cursor:pointer;border:0;border-radius:6px;min-width:0;padding:0;transition:transform .1s,box-shadow .1s,opacity .1s;position:relative;box-shadow:inset 0 0 0 1px #00000024}.swatch:hover{transform:scale(1.06)}.swatch.unused{opacity:.38}.swatch.selected{opacity:1;box-shadow:inset 0 0 0 1px #00000024, 0 0 0 2px var(--surface), 0 0 0 4px var(--accent)}.swatch-lock{background:var(--surface);width:14px;height:14px;color:var(--text);box-shadow:0 0 0 1px var(--border-strong);border-radius:50%;place-items:center;display:grid;position:absolute;bottom:-3px;right:-3px}.swatch-lock .icon{width:9px;height:9px}.swatch.add{box-shadow:inset 0 0 0 1px var(--border-strong);color:var(--muted);background:0 0;place-items:center;display:grid}.swatch.add:hover{color:var(--text);background:var(--surface-2)}.swatch.add .icon{width:14px;height:14px}.color-input{opacity:0;cursor:pointer;border:0;width:100%;height:100%;padding:0;position:absolute;inset:0}.swatch-detail{background:var(--surface-2);border-radius:8px;align-items:center;gap:10px;padding:6px;display:flex}.swatch.big{flex:none;width:32px}.swatch.big:hover{transform:none}.swatch-info{flex-direction:column;flex:1;min-width:0;font-size:12px;display:flex}.swatch-info strong{font-family:var(--mono);text-transform:uppercase;font-size:12px;font-weight:500}.swatch-info span{color:var(--muted)}.proposals{grid-template-columns:repeat(auto-fill,minmax(32px,1fr));gap:10px 8px;padding:3px}.proposal{color:inherit;font:inherit;cursor:pointer;opacity:.4;background:0 0;border:0;flex-direction:column;gap:3px;padding:0;display:flex}.proposal.on{opacity:1}.proposal.on .swatch{box-shadow:inset 0 0 0 1px #00000024, 0 0 0 2px var(--surface), 0 0 0 3.5px var(--accent)}.share{color:var(--muted);text-align:center;font-variant-numeric:tabular-nums;font-size:10.5px}.list{flex-direction:column;margin:0 -8px;display:flex}.list-row{border-radius:var(--radius);align-items:center;gap:8px;min-height:32px;padding:0 8px;display:flex}.list-row:hover{background:var(--surface-2)}.arrow{color:var(--muted);font-size:12px}.list-text{color:var(--text-2);font-variant-numeric:tabular-nums;flex:1;font-size:12px}.preview-box{background:repeating-conic-gradient(var(--surface-3) 0 25%, var(--surface-2) 0 50%) 0 0 / 12px 12px;min-height:120px;box-shadow:inset 0 0 0 1px var(--border);border-radius:8px;place-items:center;padding:16px;display:grid}.preview{max-width:100%;image-rendering:pixelated}.export-main,.export-grid{grid-template-columns:1fr 1fr;gap:6px;display:grid}.intro{border-bottom:1px solid var(--border);flex-direction:column;gap:10px;padding:20px 16px;display:flex}.intro h2{font-size:14px;font-weight:600}.intro ol{color:var(--text-2);flex-direction:column;gap:6px;margin:0;padding-left:18px;font-size:12.5px;display:flex}.intro strong{color:var(--text);font-weight:500}@media (width<=900px){.hide-narrow,.file-meta{display:none}}@media (width<=760px){.topbar{grid-template-rows:48px auto;grid-template-columns:minmax(0,1fr) auto;row-gap:0;height:auto;padding:0 8px 0 12px}.topbar-center{grid-area:2/1/auto/-1;padding-bottom:8px}.topbar-center .segmented{width:100%;display:flex}.topbar-center .segmented button{flex:1}.topbar-center:empty,.file,.topbar-end .btn:not(.icon-only)>span:not(.icon){display:none}.topbar-end .btn:not(.icon-only){width:var(--control);padding:0}.layout{flex-direction:column;display:flex;overflow-y:auto}.workspace{flex:none;height:58vh;min-height:320px}.inspector{border-left:0;overflow:visible}.tools{bottom:10px;left:10px;transform:none}.tools .segmented button>span:not(.icon){display:none}.tools .segmented button{width:36px;padding:0}.zoom{bottom:10px;right:10px}.zoom .zoom-value{display:none}.section-head{min-height:48px}.switch-row,.prop{min-height:34px}}.floating .segmented+.segmented button.on{background:#ffffff24}.about{color:var(--muted);flex-direction:column;gap:8px;padding:16px 16px 24px;font-size:12px;line-height:1.5;display:flex}.about-head{color:var(--text);font-family:var(--mono);align-items:center;gap:8px;font-size:12px;font-weight:600;display:flex}.about-head .mark{width:16px;height:16px}.about code{background:var(--surface-2);color:var(--text-2);font-family:var(--mono);border-radius:4px;padding:1px 4px;font-size:11.5px}.about a{color:var(--text-2);text-decoration:underline;-webkit-text-decoration-color:var(--border-strong);text-decoration-color:var(--border-strong);text-underline-offset:2px}.about a:hover{color:var(--text);-webkit-text-decoration-color:var(--accent);text-decoration-color:var(--accent)}.ruler{background:linear-gradient(to right, var(--border-strong) 1px, transparent 1px) 0 0 / 8px 4px repeat-x, linear-gradient(to right, var(--muted) 1px, transparent 1px) 0 0 / 40px 6px repeat-x;opacity:.8;height:6px}@media (width<=760px){.by{display:none}}.paint-colors{border-left:1px solid #ffffff1a;flex-wrap:wrap;align-items:center;gap:4px;max-width:min(360px,60vw);padding-left:6px;display:flex}.paint-color{background:var(--color);cursor:pointer;border:0;border-radius:4px;width:20px;height:20px;padding:0;box-shadow:inset 0 0 0 1px #ffffff2e}.paint-color.on{box-shadow:0 0 0 2px var(--floating), 0 0 0 4px var(--accent)}.paint-hint{color:#a8a8b2;white-space:nowrap;padding:0 4px;font-size:12px}.topbar-start .segmented.kind{flex:none}.playback{max-width:calc(100% - 32px);bottom:16px;left:50%;transform:translate(-50%)}.playback .btn.ghost.icon-only{width:30px;height:30px}.playback .divider{background:#ffffff1f;height:20px}.frame-counter{text-align:center;font-variant-numeric:tabular-nums;color:#d4d4da;white-space:nowrap;min-width:92px;font-size:12px}.playback .fps .input{color:#ececef;background:#ffffff0f;border-color:#0000;width:84px;height:30px}.playback .fps .input input{color:#ececef}.box-actions{border-left:1px solid #ffffff1a;align-items:center;gap:2px;padding-left:8px;display:flex}.box-actions .paint-hint{margin-right:4px}.frame-list{flex-direction:column;margin:0 -8px;display:flex}.frame-row{border-radius:var(--radius);cursor:pointer;align-items:center;gap:8px;min-height:32px;padding:0 8px;display:flex}.frame-row:hover{background:var(--surface-2)}.frame-row.on{background:var(--accent-soft)}.frame-row.out .frame-number,.frame-row.out .list-text{opacity:.45;text-decoration:line-through}.frame-number{background:var(--surface-3);min-width:22px;height:20px;color:var(--text);font:600 11.5px/1 var(--font);font-variant-numeric:tabular-nums;border-radius:5px;place-items:center;display:inline-grid}.frame-row.on .frame-number{background:var(--accent);color:#fff}@media (width<=760px){.topbar-start .segmented.kind button{padding:0 8px}.topbar-start .segmented.kind button>span:not(.icon){display:none}.playback{flex-wrap:wrap;justify-content:center;bottom:10px;left:10px;right:10px;transform:none}.frame-counter{min-width:0}.stage:has(.playback:not([hidden])) .zoom{display:none}.box-actions .paint-hint{display:none}}.split-seam{width:100%;height:14px;color:var(--muted);font:500 11px/1 var(--font);cursor:pointer;opacity:.45;background:0 0;border:0;align-items:center;gap:6px;margin:0;padding:0 8px;display:flex}.split-seam:before,.split-seam:after{content:"";border-top:1px dashed;flex:1}.split-seam:hover,.split-seam:focus-visible{color:var(--accent);opacity:1}.split-seam .icon{order:1}.split-seam span{order:2}.split-seam:after{order:3}.layout.lab{grid-template-columns:232px minmax(0,1fr) 320px}.sounds{border-right:1px solid var(--border);background:var(--surface);flex-direction:column;min-height:0;display:flex}.sounds-head{border-bottom:1px solid var(--border);padding:10px 10px 8px}.input.search{padding-left:8px}.input.search .icon{width:14px;height:14px;color:var(--muted)}.input.search input::-webkit-search-cancel-button{display:none}.sound-rows{overscroll-behavior:contain;flex:1;padding:6px;position:relative;overflow-y:auto}.empty-list{padding:12px 8px}.sound-row{border-radius:var(--radius);cursor:pointer;-webkit-user-select:none;user-select:none;align-items:center;gap:8px;min-height:30px;padding:0 8px;display:flex}.sound-row:hover{background:var(--surface-2)}.sound-row.on{background:var(--accent-soft)}.sound-name{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-family:var(--mono);color:var(--text);flex:1;font-size:12px;overflow:hidden}.sound-length{color:var(--muted);font-variant-numeric:tabular-nums;font-size:11.5px}.sound-flag .icon{width:13px;height:13px}.sound-flag.warn{color:var(--warn)}.sound-flag.bad{color:var(--bad)}.sound-takes{background:var(--accent);color:#fff;border-radius:999px;place-items:center;min-width:18px;height:16px;padding:0 4px;font-size:10.5px;font-weight:600;display:inline-grid}.dot{border:1.5px solid var(--muted);border-radius:50%;flex:none;width:8px;height:8px}.dot.approved{border-color:var(--good);background:var(--good)}.dot.changes{border-color:var(--warn);background:var(--warn)}.save-state{color:var(--muted);white-space:nowrap;align-items:center;gap:4px;margin-right:6px;font-size:12px;display:inline-flex}.save-state .icon{width:14px;height:14px}.save-state.saved .icon{color:var(--good)}.save-state.failed{color:var(--bad)}.playback .frame-counter{padding-right:8px}.sound-view{color:#ececef;padding:28px 28px 96px;position:absolute;inset:0;overflow-y:auto}.sound-view>*{max-width:980px;margin-left:auto;margin-right:auto}.lab-empty{color:#ececef;margin-top:18vh}.sound-head{flex-direction:column;gap:6px;margin-bottom:18px;display:flex}.sound-title{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.sound-title h1{font-family:var(--mono);letter-spacing:-.02em;color:#f3f1ec;font-size:22px;font-weight:600}.stage-pill{color:#bdbab3;background:#f3f1ec14;border-radius:999px;align-items:center;height:20px;padding:0 8px;font-size:11.5px;font-weight:500;display:inline-flex}.stage-pill.approved{color:#4cc38a;background:#4cc38a29}.stage-pill.changes{color:#f0a843;background:#f0a84329}.stage-pill.tuned{color:#ff7a5c;background:#ff5a3629}.sound-about{color:#bdbab3;font-size:13.5px}.sound-stats{color:#8a8f98;font-family:var(--mono);font-variant-numeric:tabular-nums;flex-wrap:wrap;gap:4px 16px;font-size:11.5px;display:flex}.wave-card{border-radius:var(--radius-lg);box-shadow:0 0 0 1px var(--floating-border);background:#262a318c;flex-direction:column;gap:2px;padding:10px 12px 12px;display:flex}.lane{border-radius:6px;grid-template-columns:76px minmax(0,1fr);align-items:center;gap:10px;min-height:26px;display:grid}.lane.on .lane-label{color:#f3f1ec}.lane.on .lane-track{background:#f3f1ec0f}.lane-label{color:#8a8f98;font-family:var(--mono);align-items:center;gap:6px;font-size:11.5px;display:flex}.lane-swatch{background:var(--color);border-radius:2px;flex:none;width:8px;height:8px}.lane.mix{align-items:stretch;margin-bottom:8px}.lane.mix .lane-label{align-self:center;padding-top:18px}.wave{width:100%;display:block}.wave.big{height:170px}.wave.small{height:64px}.lanes{flex-direction:column;gap:2px;display:flex}.lane-track{border-radius:5px;height:24px;position:relative}.lane-bar{background:color-mix(in srgb, var(--color) 30%, transparent);min-width:4px;box-shadow:inset 2px 0 0 var(--color);color:#ececef;font:11px/1 var(--mono);white-space:nowrap;cursor:pointer;border:0;border-radius:4px;align-items:center;padding:0 6px;display:flex;position:absolute;top:3px;bottom:3px;overflow:hidden}.lane-bar span{text-overflow:ellipsis;overflow:hidden}.lane-bar:hover{background:color-mix(in srgb, var(--color) 45%, transparent)}.takes{margin-top:22px}.takes-head{flex-wrap:wrap;align-items:baseline;gap:4px 10px;margin-bottom:10px;display:flex}.takes-head h2{font-family:var(--mono);color:#f3f1ec;font-size:12px;font-weight:600}.takes-head span{color:#8a8f98;font-size:12px}.take-grid{grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:10px;display:grid}.take{border-radius:var(--radius-lg);box-shadow:0 0 0 1px var(--floating-border);background:#262a318c;flex-direction:column;display:flex;overflow:hidden}.take.playing{box-shadow:0 0 0 1px #f3f1ec4d}.take.picked{box-shadow:0 0 0 2px var(--accent)}.take-play{cursor:pointer;background:0 0;border:0;padding:10px 10px 4px;display:block}.take-play:hover{background:#f3f1ec0a}.take-foot{align-items:center;gap:8px;padding:6px 8px 8px 10px;display:flex}.take-number{color:#f3f1ec;min-width:20px;height:20px;font:600 11.5px/1 var(--font);background:#f3f1ec1a;border-radius:5px;place-items:center;display:inline-grid}.take.picked .take-number{background:var(--accent);color:#fff}.take-label{-webkit-line-clamp:2;color:#bdbab3;-webkit-box-orient:vertical;flex:1;min-width:0;font-size:11.5px;line-height:1.35;display:-webkit-box;overflow:hidden}.take .btn.ghost.take-use{color:#ececef;background:#f3f1ec14}.take .btn.ghost.take-use:hover{background:var(--accent);color:#fff}.take-picked{color:var(--accent);white-space:nowrap;align-items:center;gap:4px;font-size:11.5px;font-weight:600;display:inline-flex}.take-picked .icon{width:13px;height:13px}.value{color:var(--text);font-family:var(--mono);font-variant-numeric:tabular-nums;text-align:right;font-size:12px}.input.note textarea{min-height:64px;font-family:var(--font);font-size:12.5px}.layer-tabs{flex-wrap:wrap;gap:4px;display:flex}.layer-tab{border:1px solid var(--border);border-radius:var(--radius);background:var(--surface);height:24px;color:var(--text-2);font:11.5px/1 var(--mono);cursor:pointer;align-items:center;gap:6px;padding:0 8px;display:inline-flex}.layer-tab .lane-swatch{background:var(--color)}.layer-tab.on{border-color:var(--accent);background:var(--accent-soft);color:var(--text)}.group h3{font-family:var(--mono)}@media (width<=1100px){.layout.lab{grid-template-columns:200px minmax(0,1fr) 300px}}@media (width<=760px){.layout.lab{flex-direction:column;display:flex;overflow-y:auto}.layout.lab>*{flex:none}.layout.lab .workspace{order:1;height:62vh}.sounds{border-right:0;border-bottom:1px solid var(--border);order:2;max-height:40vh}.layout.lab .inspector{order:3}.sound-view{padding:16px 12px 88px}.lane{grid-template-columns:58px minmax(0,1fr);gap:6px}.wave.big{height:130px}.save-state{display:none}}.song-view{flex-direction:column;padding-bottom:76px;display:flex}.song-view>*{width:100%;max-width:none}.song-scroll{overscroll-behavior:contain;flex:1;min-height:0;overflow:auto}.song-card{border-radius:var(--radius-lg);background:#f3f1ec08;width:max-content;min-width:100%;box-shadow:inset 0 0 0 1px #f3f1ec0f}.song-card canvas{touch-action:none;display:block}.arrangement{cursor:pointer}.note-grid{cursor:crosshair}.grid-head{color:#ececef;flex-wrap:wrap;align-items:center;gap:8px;margin:18px 0 8px;display:flex;position:sticky;left:0}.grid-head h2{font-family:var(--mono);margin:0;font-size:13px;font-weight:600}.grid-meta,.grid-hint{color:#f3f1ec80;font-size:12px}.grid-hint{margin-left:6px}.grid-head .spacer{flex:1}.grid-head .segmented{background:#f3f1ec0f}.grid-head .segmented button{color:#f3f1ecb3}.grid-head .segmented button.on{color:#f3f1ec;background:#f3f1ec24}.grid-head .prop.switch,.grid-head .prop{color:#f3f1ecb3;gap:8px;min-height:0;padding:0}.grid-head .prop .prop-label{color:#f3f1ecb3}.tracks-head{flex-direction:column;gap:8px;display:flex}.tracks h2{color:var(--muted);letter-spacing:.04em;text-transform:uppercase;margin:0;font-size:11px;font-weight:600}.rows-title{padding:6px 8px 4px}.button-row{flex-wrap:wrap;gap:6px;display:flex}.track-row{min-height:40px}.track-row .lane-swatch{border-radius:3px;width:8px;height:22px}.track-text{flex-direction:column;flex:1;min-width:0;line-height:1.25;display:flex}.track-meta{color:var(--muted);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.track-toggle{border:1px solid var(--border-strong);width:22px;height:20px;color:var(--muted);font:600 10.5px var(--mono);cursor:pointer;background:0 0;border-radius:5px;place-items:center;padding:0;display:inline-grid}.track-toggle:hover{color:var(--text)}.track-toggle.on{border-color:var(--accent);background:var(--accent);color:#fff}.add-track{margin:6px 2px}.recording{align-items:center;gap:8px;display:flex}.record-dot{background:var(--accent);border-radius:50%;width:10px;height:10px;animation:1s steps(2,end) infinite blink}@keyframes blink{50%{opacity:.25}}.record-time{font:600 12px var(--mono);font-variant-numeric:tabular-nums}.record-level{background:var(--surface-3);border-radius:3px;flex:1;height:6px;overflow:hidden}.record-level span{background:var(--good);width:0;height:100%;display:block}.transport{bottom:16px;left:50%;transform:translate(-50%)}.transport .frame-counter{min-width:64px;font:600 12px var(--mono);font-variant-numeric:tabular-nums;padding:0 6px}.transport .btn.ghost.on{color:var(--accent)}.key-picker{grid-template-columns:64px minmax(0,1fr);gap:6px;display:grid}.edits,.comments{margin:0;padding:0;list-style:none}.edits li{color:var(--text-2);padding:3px 0;font-size:12px}.comments{flex-direction:column;gap:6px;margin-top:10px;display:flex}.comments li{border-radius:var(--radius);background:var(--surface-2);cursor:pointer;grid-template-columns:minmax(0,1fr) auto;gap:2px 6px;padding:8px 6px 8px 10px;display:grid}.comments li .btn{grid-area:1/2/span 2}.comment-where{color:var(--warn);font:600 11px var(--mono)}.comment-text{color:var(--text);font-size:12.5px}@media (width<=760px){.transport{flex-wrap:nowrap;left:50%;right:auto;transform:translate(-50%)}.grid-hint{display:none}.tracks{max-height:none}}