@typeonce/ritmo-tools 0.17.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.
- package/CHANGELOG.md +50 -0
- package/README.md +304 -24
- package/dist/cli.js +5 -1
- package/dist/cli.js.map +1 -1
- package/dist/internal/version.d.ts +1 -1
- package/dist/internal/version.js +1 -1
- package/dist/music/cli/commands.d.ts +15 -0
- package/dist/music/cli/commands.d.ts.map +1 -0
- package/dist/music/cli/commands.js +180 -0
- package/dist/music/cli/commands.js.map +1 -0
- package/dist/music/cli/io.d.ts +11 -0
- package/dist/music/cli/io.d.ts.map +1 -0
- package/dist/music/cli/io.js +28 -0
- package/dist/music/cli/io.js.map +1 -0
- package/dist/music/cli/serve.d.ts +13 -0
- package/dist/music/cli/serve.d.ts.map +1 -0
- package/dist/music/cli/serve.js +168 -0
- package/dist/music/cli/serve.js.map +1 -0
- package/dist/music/core/audio.d.ts +22 -0
- package/dist/music/core/audio.d.ts.map +1 -0
- package/dist/music/core/audio.js +95 -0
- package/dist/music/core/audio.js.map +1 -0
- package/dist/music/core/check.d.ts +51 -0
- package/dist/music/core/check.d.ts.map +1 -0
- package/dist/music/core/check.js +113 -0
- package/dist/music/core/check.js.map +1 -0
- package/dist/music/core/instruments.d.ts +179 -0
- package/dist/music/core/instruments.d.ts.map +1 -0
- package/dist/music/core/instruments.js +531 -0
- package/dist/music/core/instruments.js.map +1 -0
- package/dist/music/core/listen.d.ts +32 -0
- package/dist/music/core/listen.d.ts.map +1 -0
- package/dist/music/core/listen.js +350 -0
- package/dist/music/core/listen.js.map +1 -0
- package/dist/music/core/midi.d.ts +4 -0
- package/dist/music/core/midi.d.ts.map +1 -0
- package/dist/music/core/midi.js +76 -0
- package/dist/music/core/midi.js.map +1 -0
- package/dist/music/core/render.d.ts +37 -0
- package/dist/music/core/render.d.ts.map +1 -0
- package/dist/music/core/render.js +204 -0
- package/dist/music/core/render.js.map +1 -0
- package/dist/music/core/review.d.ts +41 -0
- package/dist/music/core/review.d.ts.map +1 -0
- package/dist/music/core/review.js +139 -0
- package/dist/music/core/review.js.map +1 -0
- package/dist/music/core/song.d.ts +144 -0
- package/dist/music/core/song.d.ts.map +1 -0
- package/dist/music/core/song.js +291 -0
- package/dist/music/core/song.js.map +1 -0
- package/dist/music/page/assets/index-BfO_jOqP.js +4 -0
- package/dist/music/page/assets/index-Dw14kxkL.css +1 -0
- package/dist/music/page/index.html +19 -0
- package/dist/pixel/cli/commands.d.ts.map +1 -1
- package/dist/pixel/cli/commands.js +122 -46
- package/dist/pixel/cli/commands.js.map +1 -1
- package/dist/pixel/cli/io.d.ts +5 -1
- package/dist/pixel/cli/io.d.ts.map +1 -1
- package/dist/pixel/cli/io.js +16 -1
- package/dist/pixel/cli/io.js.map +1 -1
- package/dist/pixel/core/animation.d.ts +23 -8
- package/dist/pixel/core/animation.d.ts.map +1 -1
- package/dist/pixel/core/animation.js +41 -18
- package/dist/pixel/core/animation.js.map +1 -1
- package/dist/pixel/core/grid.d.ts +3 -2
- package/dist/pixel/core/grid.d.ts.map +1 -1
- package/dist/pixel/core/grid.js +14 -6
- package/dist/pixel/core/grid.js.map +1 -1
- package/dist/pixel/core/preview.d.ts +8 -3
- package/dist/pixel/core/preview.d.ts.map +1 -1
- package/dist/pixel/core/preview.js +39 -25
- package/dist/pixel/core/preview.js.map +1 -1
- package/dist/pixel/core/project.d.ts +27 -0
- package/dist/pixel/core/project.d.ts.map +1 -0
- package/dist/pixel/core/project.js +117 -0
- package/dist/pixel/core/project.js.map +1 -0
- package/dist/pixel/core/recipe.d.ts.map +1 -1
- package/dist/pixel/core/recipe.js +43 -6
- package/dist/pixel/core/recipe.js.map +1 -1
- package/dist/pixel/core/report.d.ts +21 -3
- package/dist/pixel/core/report.d.ts.map +1 -1
- package/dist/pixel/core/report.js +52 -22
- package/dist/pixel/core/report.js.map +1 -1
- package/dist/pixel/core/sheet.d.ts +68 -8
- package/dist/pixel/core/sheet.d.ts.map +1 -1
- package/dist/pixel/core/sheet.js +250 -32
- package/dist/pixel/core/sheet.js.map +1 -1
- package/dist/pixel/core/spritesheet.d.ts +43 -35
- package/dist/pixel/core/spritesheet.d.ts.map +1 -1
- package/dist/pixel/core/spritesheet.js +63 -59
- package/dist/pixel/core/spritesheet.js.map +1 -1
- package/dist/sound/cli/commands.d.ts +25 -0
- package/dist/sound/cli/commands.d.ts.map +1 -0
- package/dist/sound/cli/commands.js +137 -0
- package/dist/sound/cli/commands.js.map +1 -0
- package/dist/sound/cli/io.d.ts +24 -0
- package/dist/sound/cli/io.d.ts.map +1 -0
- package/dist/sound/cli/io.js +51 -0
- package/dist/sound/cli/io.js.map +1 -0
- package/dist/sound/cli/serve.d.ts +11 -0
- package/dist/sound/cli/serve.d.ts.map +1 -0
- package/dist/sound/cli/serve.js +147 -0
- package/dist/sound/cli/serve.js.map +1 -0
- package/dist/sound/core/measure.d.ts +67 -0
- package/dist/sound/core/measure.d.ts.map +1 -0
- package/dist/sound/core/measure.js +138 -0
- package/dist/sound/core/measure.js.map +1 -0
- package/dist/sound/core/recipe.d.ts +143 -0
- package/dist/sound/core/recipe.d.ts.map +1 -0
- package/dist/sound/core/recipe.js +292 -0
- package/dist/sound/core/recipe.js.map +1 -0
- package/dist/sound/core/render.d.ts +37 -0
- package/dist/sound/core/render.d.ts.map +1 -0
- package/dist/sound/core/render.js +317 -0
- package/dist/sound/core/render.js.map +1 -0
- package/dist/sound/core/review.d.ts +52 -0
- package/dist/sound/core/review.d.ts.map +1 -0
- package/dist/sound/core/review.js +89 -0
- package/dist/sound/core/review.js.map +1 -0
- package/dist/sound/core/takes.d.ts +29 -0
- package/dist/sound/core/takes.d.ts.map +1 -0
- package/dist/sound/core/takes.js +109 -0
- package/dist/sound/core/takes.js.map +1 -0
- package/dist/sound/core/wav.d.ts +4 -0
- package/dist/sound/core/wav.d.ts.map +1 -0
- package/dist/sound/core/wav.js +28 -0
- package/dist/sound/core/wav.js.map +1 -0
- package/dist/sound/page/assets/index-Cjun5b92.js +6 -0
- package/dist/sound/page/assets/index-DZm59wkQ.css +1 -0
- package/dist/sound/page/index.html +19 -0
- package/package.json +10 -4
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns a song into stereo samples: every note played by its instrument,
|
|
3
|
+
* placed at its beat, panned, sent to a shared room, and mixed. The same
|
|
4
|
+
* bits come out in Node and in every browser (noise is seeded by the note
|
|
5
|
+
* itself), so the CLI measures exactly what the page plays.
|
|
6
|
+
*
|
|
7
|
+
* A looping song is rendered one pass long, with what rings past its end
|
|
8
|
+
* (a last chord, the room) folded back over its start: played on repeat, it
|
|
9
|
+
* has no seam.
|
|
10
|
+
*/
|
|
11
|
+
import { isDrum, isKit, playDrum, playVoice } from "./instruments.js";
|
|
12
|
+
import { lengthInSeconds, midiOf, secondsPerBeat } from "./song.js";
|
|
13
|
+
export const SAMPLE_RATE = 44100;
|
|
14
|
+
/** A seeded generator of numbers in [0, 1) (mulberry32). */
|
|
15
|
+
export const seeded = (seed) => {
|
|
16
|
+
let state = seed >>> 0;
|
|
17
|
+
return () => {
|
|
18
|
+
state = (state + 0x6d2b79f5) >>> 0;
|
|
19
|
+
let t = state;
|
|
20
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
21
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
22
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
const hash = (text) => {
|
|
26
|
+
let h = 2166136261;
|
|
27
|
+
for (let i = 0; i < text.length; i++)
|
|
28
|
+
h = Math.imul(h ^ text.charCodeAt(i), 16777619);
|
|
29
|
+
return h >>> 0;
|
|
30
|
+
};
|
|
31
|
+
/** One note's samples, from the cache when it was played before. */
|
|
32
|
+
export const noteSamples = (instrument, note, held, rate, cache) => {
|
|
33
|
+
const key = `${instrument}|${note.pitch}|${Math.round(held * 1000)}|${Math.round(note.velocity * 100)}|${rate}`;
|
|
34
|
+
const found = cache?.get(key);
|
|
35
|
+
if (found !== undefined)
|
|
36
|
+
return found;
|
|
37
|
+
const velocity = Math.round(note.velocity * 100) / 100;
|
|
38
|
+
const random = seeded(hash(key));
|
|
39
|
+
const midi = midiOf(note.pitch);
|
|
40
|
+
const samples = isKit(instrument)
|
|
41
|
+
? isDrum(note.pitch)
|
|
42
|
+
? playDrum(instrument, note.pitch, velocity, rate, random)
|
|
43
|
+
: new Float32Array(0)
|
|
44
|
+
: midi === undefined
|
|
45
|
+
? new Float32Array(0)
|
|
46
|
+
: playVoice(instrument, midi, held, velocity, rate, random);
|
|
47
|
+
if (cache !== undefined) {
|
|
48
|
+
if (cache.size > 4000)
|
|
49
|
+
cache.clear();
|
|
50
|
+
cache.set(key, samples);
|
|
51
|
+
}
|
|
52
|
+
return samples;
|
|
53
|
+
};
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// The room: a Freeverb (eight combs and four allpasses a side), the classic small reverb.
|
|
56
|
+
const COMBS = [1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617];
|
|
57
|
+
const ALLPASSES = [556, 441, 341, 225];
|
|
58
|
+
const SPREAD = 23;
|
|
59
|
+
const room = (input, rate, offset) => {
|
|
60
|
+
const scale = rate / 44100;
|
|
61
|
+
const out = new Float32Array(input.length);
|
|
62
|
+
const feedback = 0.84;
|
|
63
|
+
const damp = 0.3;
|
|
64
|
+
for (const size of COMBS) {
|
|
65
|
+
const line = new Float32Array(Math.round((size + offset) * scale));
|
|
66
|
+
let at = 0;
|
|
67
|
+
let store = 0;
|
|
68
|
+
for (let i = 0; i < input.length; i++) {
|
|
69
|
+
const y = line[at];
|
|
70
|
+
store = y * (1 - damp) + store * damp;
|
|
71
|
+
line[at] = input[i] + store * feedback;
|
|
72
|
+
at = (at + 1) % line.length;
|
|
73
|
+
out[i] += y;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (const size of ALLPASSES) {
|
|
77
|
+
const line = new Float32Array(Math.round((size + offset) * scale));
|
|
78
|
+
let at = 0;
|
|
79
|
+
for (let i = 0; i < out.length; i++) {
|
|
80
|
+
const buffered = line[at];
|
|
81
|
+
const x = out[i];
|
|
82
|
+
line[at] = x + buffered * 0.5;
|
|
83
|
+
out[i] = buffered - x;
|
|
84
|
+
at = (at + 1) % line.length;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
};
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// The compressor: one gain for both sides, following the louder one
|
|
91
|
+
const THRESHOLD = 0.18;
|
|
92
|
+
const RATIO = 4;
|
|
93
|
+
const MAKEUP = 2.2;
|
|
94
|
+
const CEILING = 0.89;
|
|
95
|
+
/**
|
|
96
|
+
* Squeezes the mix: above the threshold, every 4 dB in becomes 1 dB out
|
|
97
|
+
* (fast to clamp down, slower to let go), the result is raised back up, and
|
|
98
|
+
* a soft clip holds the peaks under -1 dBFS. A looping song is run through
|
|
99
|
+
* its own end first, so the start is squeezed as it is when the loop comes round.
|
|
100
|
+
*/
|
|
101
|
+
const compress = (left, right, rate, loop) => {
|
|
102
|
+
const attack = Math.exp(-1 / (0.004 * rate));
|
|
103
|
+
const release = Math.exp(-1 / (0.15 * rate));
|
|
104
|
+
let level = 0;
|
|
105
|
+
const follow = (i) => {
|
|
106
|
+
const x = Math.max(Math.abs(left[i]), Math.abs(right[i]));
|
|
107
|
+
level = x > level ? x + (level - x) * attack : x + (level - x) * release;
|
|
108
|
+
return level;
|
|
109
|
+
};
|
|
110
|
+
if (loop)
|
|
111
|
+
for (let i = Math.max(0, left.length - Math.round(rate)); i < left.length; i++)
|
|
112
|
+
follow(i);
|
|
113
|
+
const clip = (x) => (Math.abs(x) < CEILING * 0.7 ? x : Math.sign(x) * (CEILING * 0.7 + CEILING * 0.3 * Math.tanh((Math.abs(x) - CEILING * 0.7) / (CEILING * 0.3))));
|
|
114
|
+
for (let i = 0; i < left.length; i++) {
|
|
115
|
+
const env = follow(i);
|
|
116
|
+
const gain = (env > THRESHOLD ? Math.pow(env / THRESHOLD, 1 / RATIO - 1) : 1) * MAKEUP;
|
|
117
|
+
left[i] = clip(left[i] * gain);
|
|
118
|
+
right[i] = clip(right[i] * gain);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
/** How long the room rings past the last note, in seconds. */
|
|
122
|
+
const TAIL = 3;
|
|
123
|
+
const ROOM_LEVEL = 0.12;
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
/** Places a track's notes into `left` and `right` (and its share of the room into `send`). */
|
|
126
|
+
const addTrack = (track, song, rate, left, right, send, cache) => {
|
|
127
|
+
const beat = secondsPerBeat(song);
|
|
128
|
+
// Constant-power panning: as loud in the middle as at either side.
|
|
129
|
+
const angle = ((track.pan + 1) * Math.PI) / 4;
|
|
130
|
+
const gainLeft = Math.cos(angle) * track.volume;
|
|
131
|
+
const gainRight = Math.sin(angle) * track.volume;
|
|
132
|
+
for (const note of track.notes) {
|
|
133
|
+
const samples = noteSamples(track.instrument, note, note.length * beat, rate, cache);
|
|
134
|
+
const start = Math.round(note.at * beat * rate);
|
|
135
|
+
const end = Math.min(left.length, start + samples.length);
|
|
136
|
+
for (let i = start; i < end; i++) {
|
|
137
|
+
const x = samples[i - start];
|
|
138
|
+
left[i] += x * gainLeft;
|
|
139
|
+
right[i] += x * gainRight;
|
|
140
|
+
send[i] += x * track.volume * track.reverb;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
/** Renders the song (or some of its tracks) as stereo samples, at the song's master volume. */
|
|
145
|
+
export const renderSong = (song, options = {}) => {
|
|
146
|
+
const rate = options.sampleRate ?? SAMPLE_RATE;
|
|
147
|
+
const pass = Math.round(lengthInSeconds(song) * rate);
|
|
148
|
+
const total = pass + Math.round(TAIL * rate);
|
|
149
|
+
const left = new Float32Array(total);
|
|
150
|
+
const right = new Float32Array(total);
|
|
151
|
+
const send = new Float32Array(total);
|
|
152
|
+
song.tracks.forEach((track, index) => {
|
|
153
|
+
const chosen = options.tracks === undefined ? track.mute !== true : options.tracks.includes(index);
|
|
154
|
+
if (chosen)
|
|
155
|
+
addTrack(track, song, rate, left, right, send, options.cache);
|
|
156
|
+
});
|
|
157
|
+
if (send.some((x) => x !== 0)) {
|
|
158
|
+
const wetLeft = room(send, rate, 0);
|
|
159
|
+
const wetRight = room(send, rate, SPREAD);
|
|
160
|
+
for (let i = 0; i < total; i++) {
|
|
161
|
+
left[i] += wetLeft[i] * ROOM_LEVEL;
|
|
162
|
+
right[i] += wetRight[i] * ROOM_LEVEL;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
for (let i = 0; i < total; i++) {
|
|
166
|
+
left[i] *= song.master;
|
|
167
|
+
right[i] *= song.master;
|
|
168
|
+
}
|
|
169
|
+
if (!song.loop) {
|
|
170
|
+
let end = total;
|
|
171
|
+
while (end > pass && Math.abs(left[end - 1]) < 0.0001 && Math.abs(right[end - 1]) < 0.0001)
|
|
172
|
+
end--;
|
|
173
|
+
const out = { sampleRate: rate, left: left.slice(0, end), right: right.slice(0, end) };
|
|
174
|
+
if (song.compressor)
|
|
175
|
+
compress(out.left, out.right, rate, false);
|
|
176
|
+
return out;
|
|
177
|
+
}
|
|
178
|
+
// The tail wraps into the start: the loop's seam is filled by its own end.
|
|
179
|
+
for (let i = pass; i < total; i++) {
|
|
180
|
+
const at = (i - pass) % pass;
|
|
181
|
+
left[at] += left[i];
|
|
182
|
+
right[at] += right[i];
|
|
183
|
+
}
|
|
184
|
+
const out = { sampleRate: rate, left: left.slice(0, pass), right: right.slice(0, pass) };
|
|
185
|
+
if (song.compressor)
|
|
186
|
+
compress(out.left, out.right, rate, true);
|
|
187
|
+
return out;
|
|
188
|
+
};
|
|
189
|
+
/** The loudest sample in dBFS (0 is full scale). */
|
|
190
|
+
export const peakOf = ({ left, right }) => {
|
|
191
|
+
let peak = 0;
|
|
192
|
+
for (let i = 0; i < left.length; i++)
|
|
193
|
+
peak = Math.max(peak, Math.abs(left[i]), Math.abs(right[i]));
|
|
194
|
+
return peak === 0 ? -120 : Math.round(20 * Math.log10(peak) * 10) / 10;
|
|
195
|
+
};
|
|
196
|
+
/** The loudness over the whole song as RMS in dBFS: how loud it feels next to other music. */
|
|
197
|
+
export const loudnessOf = ({ left, right }) => {
|
|
198
|
+
let sum = 0;
|
|
199
|
+
for (let i = 0; i < left.length; i++)
|
|
200
|
+
sum += left[i] * left[i] + right[i] * right[i];
|
|
201
|
+
const rms = Math.sqrt(sum / Math.max(1, left.length * 2));
|
|
202
|
+
return rms === 0 ? -120 : Math.round(20 * Math.log10(rms) * 10) / 10;
|
|
203
|
+
};
|
|
204
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/music/core/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type Decoded, type Song } from "./song.ts";
|
|
2
|
+
export type Status = "new" | "approved" | "changes";
|
|
3
|
+
export declare const statuses: ReadonlyArray<Status>;
|
|
4
|
+
export interface Comment {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
/** The span it is about, in beats. */
|
|
7
|
+
readonly from: number;
|
|
8
|
+
readonly to: number;
|
|
9
|
+
/** The track it is about, by name; none for every track. */
|
|
10
|
+
readonly track?: string | undefined;
|
|
11
|
+
readonly text: string;
|
|
12
|
+
}
|
|
13
|
+
export interface Review {
|
|
14
|
+
readonly status: Status;
|
|
15
|
+
readonly note: string;
|
|
16
|
+
readonly comments: ReadonlyArray<Comment>;
|
|
17
|
+
/** What the person changed by hand since the agent last wrote the song, a line each. */
|
|
18
|
+
readonly edits: ReadonlyArray<string>;
|
|
19
|
+
}
|
|
20
|
+
export declare const emptyReview: Review;
|
|
21
|
+
/** Where a song's review lives. */
|
|
22
|
+
export declare const reviewPath: (songPath: string) => string;
|
|
23
|
+
export declare const decodeReview: (text: string) => Decoded<Review>;
|
|
24
|
+
export declare const encodeReview: (review: Review) => string;
|
|
25
|
+
/** The person's edits, a line each, from the song the agent wrote (`before`) to the song now (`after`). */
|
|
26
|
+
export declare const describeEdits: (before: Song, after: Song) => Array<string>;
|
|
27
|
+
/** What `music notes` prints: the status, the note, comments in song order with their bars, and the edits. */
|
|
28
|
+
export declare const reviewSummary: (song: Song, review: Review) => {
|
|
29
|
+
status: Status;
|
|
30
|
+
note: string;
|
|
31
|
+
comments: {
|
|
32
|
+
where: string;
|
|
33
|
+
from: number;
|
|
34
|
+
to: number;
|
|
35
|
+
track: string | undefined;
|
|
36
|
+
text: string;
|
|
37
|
+
}[];
|
|
38
|
+
edits: readonly string[];
|
|
39
|
+
next: string;
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=review.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../../src/music/core/review.ts"],"names":[],"mappings":"AAWA,OAAO,EAAU,KAAK,OAAO,EAAa,KAAK,IAAI,EAAc,MAAM,WAAW,CAAA;AAElF,MAAM,MAAM,MAAM,GAAG,KAAK,GAAG,UAAU,GAAG,SAAS,CAAA;AACnD,eAAO,MAAM,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAkC,CAAA;AAE7E,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;IACzC,wFAAwF;IACxF,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACtC;AAED,eAAO,MAAM,WAAW,EAAE,MAA6D,CAAA;AAEvF,mCAAmC;AACnC,eAAO,MAAM,UAAU,aAAc,MAAM,KAAG,MAAuG,CAAA;AAIrJ,eAAO,MAAM,YAAY,SAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CAsBzD,CAAA;AAED,eAAO,MAAM,YAAY,WAAY,MAAM,KAAG,MAAmE,CAAA;AA0CjH,2GAA2G;AAC3G,eAAO,MAAM,aAAa,WAAY,IAAI,SAAS,IAAI,KAAG,KAAK,CAAC,MAAM,CAgBrE,CAAA;AAED,8GAA8G;AAC9G,eAAO,MAAM,aAAa,SAAU,IAAI,UAAU,MAAM;;;;;;;;;;;;CAatD,CAAA"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The review: what the person said about a song, kept next to it
|
|
3
|
+
* (`theme.song.json` → `theme.song.review.json`) for the agent to read with
|
|
4
|
+
* `music notes`. The song's status (new, approved, changes asked), a note
|
|
5
|
+
* on the whole, comments pinned to a few bars (and a track), and a summary
|
|
6
|
+
* of the edits the person made by hand since the agent last wrote the song.
|
|
7
|
+
*
|
|
8
|
+
* Edits land in the song itself as they are made; the summary is there so
|
|
9
|
+
* the agent knows what the person changed and keeps it.
|
|
10
|
+
*/
|
|
11
|
+
import { instruments } from "./instruments.js";
|
|
12
|
+
import { barsOf } from "./song.js";
|
|
13
|
+
export const statuses = ["new", "approved", "changes"];
|
|
14
|
+
export const emptyReview = { status: "new", note: "", comments: [], edits: [] };
|
|
15
|
+
/** Where a song's review lives. */
|
|
16
|
+
export const reviewPath = (songPath) => (songPath.endsWith(".json") ? songPath.slice(0, -".json".length) : songPath) + ".review.json";
|
|
17
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18
|
+
export const decodeReview = (text) => {
|
|
19
|
+
let raw;
|
|
20
|
+
try {
|
|
21
|
+
raw = JSON.parse(text);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return { ok: false, error: `not JSON: ${error instanceof Error ? error.message : String(error)}` };
|
|
25
|
+
}
|
|
26
|
+
if (!isRecord(raw))
|
|
27
|
+
return { ok: false, error: 'a review is {"version": 1, "status": "new", "note": "", "comments": [], "edits": []}' };
|
|
28
|
+
const errors = [];
|
|
29
|
+
const status = statuses.find((entry) => entry === raw.status);
|
|
30
|
+
if (raw.status !== undefined && status === undefined)
|
|
31
|
+
errors.push(`status must be one of ${statuses.join(", ")}`);
|
|
32
|
+
const comments = [];
|
|
33
|
+
for (const [i, comment] of (Array.isArray(raw.comments) ? raw.comments : []).entries()) {
|
|
34
|
+
if (!isRecord(comment) || typeof comment.id !== "string" || typeof comment.text !== "string" || typeof comment.from !== "number" || typeof comment.to !== "number") {
|
|
35
|
+
errors.push(`comments[${i}] must be {"id", "from", "to", "text"}`);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
comments.push({ id: comment.id, from: comment.from, to: comment.to, track: typeof comment.track === "string" ? comment.track : undefined, text: comment.text });
|
|
39
|
+
}
|
|
40
|
+
const edits = (Array.isArray(raw.edits) ? raw.edits : []).filter((line) => typeof line === "string");
|
|
41
|
+
if (errors.length > 0)
|
|
42
|
+
return { ok: false, error: errors.join("; ") };
|
|
43
|
+
return { ok: true, value: { status: status ?? "new", note: typeof raw.note === "string" ? raw.note : "", comments, edits } };
|
|
44
|
+
};
|
|
45
|
+
export const encodeReview = (review) => JSON.stringify({ version: 1, ...review }, null, 2) + "\n";
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// What changed between two versions of a song, in words
|
|
48
|
+
const noteKey = (note) => `${note.at}|${note.pitch}|${note.length}|${note.velocity}`;
|
|
49
|
+
const count = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
|
|
50
|
+
const span = (song, notes) => barsOf(song, Math.min(...notes.map((note) => note.at)), Math.max(...notes.map((note) => note.at + note.length)));
|
|
51
|
+
const trackEdits = (song, before, after) => {
|
|
52
|
+
const lines = [];
|
|
53
|
+
const name = after.name;
|
|
54
|
+
if (before.name !== after.name)
|
|
55
|
+
lines.push(`Renamed the track ${before.name} to ${after.name}.`);
|
|
56
|
+
if (before.instrument !== after.instrument)
|
|
57
|
+
lines.push(`${name}: changed the instrument from ${instruments[before.instrument].label} to ${instruments[after.instrument].label}.`);
|
|
58
|
+
if (Math.abs(before.volume - after.volume) > 0.005)
|
|
59
|
+
lines.push(`${name}: volume ${before.volume.toFixed(2)} → ${after.volume.toFixed(2)}.`);
|
|
60
|
+
if (Math.abs(before.pan - after.pan) > 0.005)
|
|
61
|
+
lines.push(`${name}: pan ${before.pan.toFixed(2)} → ${after.pan.toFixed(2)}.`);
|
|
62
|
+
if (Math.abs(before.reverb - after.reverb) > 0.005)
|
|
63
|
+
lines.push(`${name}: reverb ${before.reverb.toFixed(2)} → ${after.reverb.toFixed(2)}.`);
|
|
64
|
+
if ((before.mute === true) !== (after.mute === true))
|
|
65
|
+
lines.push(`${name}: ${after.mute === true ? "muted" : "unmuted"}.`);
|
|
66
|
+
const was = new Map();
|
|
67
|
+
for (const note of before.notes)
|
|
68
|
+
was.set(noteKey(note), (was.get(noteKey(note)) ?? 0) + 1);
|
|
69
|
+
const added = [];
|
|
70
|
+
for (const note of after.notes) {
|
|
71
|
+
const left = was.get(noteKey(note)) ?? 0;
|
|
72
|
+
if (left > 0)
|
|
73
|
+
was.set(noteKey(note), left - 1);
|
|
74
|
+
else
|
|
75
|
+
added.push(note);
|
|
76
|
+
}
|
|
77
|
+
const now = new Map();
|
|
78
|
+
for (const note of after.notes)
|
|
79
|
+
now.set(noteKey(note), (now.get(noteKey(note)) ?? 0) + 1);
|
|
80
|
+
const removed = [];
|
|
81
|
+
for (const note of before.notes) {
|
|
82
|
+
const left = now.get(noteKey(note)) ?? 0;
|
|
83
|
+
if (left > 0)
|
|
84
|
+
now.set(noteKey(note), left - 1);
|
|
85
|
+
else
|
|
86
|
+
removed.push(note);
|
|
87
|
+
}
|
|
88
|
+
if (added.length > 0 && removed.length > 0)
|
|
89
|
+
lines.push(`${name}: rewrote notes in ${span(song, [...added, ...removed])} (${count(added.length, "note")} in, ${count(removed.length, "note")} out).`);
|
|
90
|
+
else if (added.length > 0)
|
|
91
|
+
lines.push(`${name}: added ${count(added.length, "note")} in ${span(song, added)}.`);
|
|
92
|
+
else if (removed.length > 0)
|
|
93
|
+
lines.push(`${name}: removed ${count(removed.length, "note")} in ${span(song, removed)}.`);
|
|
94
|
+
return lines;
|
|
95
|
+
};
|
|
96
|
+
/** The person's edits, a line each, from the song the agent wrote (`before`) to the song now (`after`). */
|
|
97
|
+
export const describeEdits = (before, after) => {
|
|
98
|
+
const lines = [];
|
|
99
|
+
if (before.tempo !== after.tempo)
|
|
100
|
+
lines.push(`Tempo ${before.tempo} → ${after.tempo} BPM.`);
|
|
101
|
+
if (before.key !== after.key)
|
|
102
|
+
lines.push(`Key ${before.key} → ${after.key}.`);
|
|
103
|
+
if (before.bars !== after.bars)
|
|
104
|
+
lines.push(`Bars ${before.bars} → ${after.bars}.`);
|
|
105
|
+
if (before.beatsPerBar !== after.beatsPerBar)
|
|
106
|
+
lines.push(`Beats per bar ${before.beatsPerBar} → ${after.beatsPerBar}.`);
|
|
107
|
+
if (Math.abs(before.master - after.master) > 0.005)
|
|
108
|
+
lines.push(`Master volume ${before.master.toFixed(2)} → ${after.master.toFixed(2)}.`);
|
|
109
|
+
if (before.loop !== after.loop)
|
|
110
|
+
lines.push(after.loop ? "Made it loop." : "Made it play once (no loop).");
|
|
111
|
+
if (before.compressor !== after.compressor)
|
|
112
|
+
lines.push(after.compressor ? "Turned the compressor on." : "Turned the compressor off.");
|
|
113
|
+
// Tracks are matched by position: renaming one is an edit of it, not a new track.
|
|
114
|
+
const shared = Math.min(before.tracks.length, after.tracks.length);
|
|
115
|
+
for (let i = 0; i < shared; i++)
|
|
116
|
+
lines.push(...trackEdits(after, before.tracks[i], after.tracks[i]));
|
|
117
|
+
for (const track of after.tracks.slice(shared))
|
|
118
|
+
lines.push(`Added a track: ${track.name} (${instruments[track.instrument].label}, ${count(track.notes.length, "note")}).`);
|
|
119
|
+
for (const track of before.tracks.slice(shared))
|
|
120
|
+
lines.push(`Removed the track ${track.name}.`);
|
|
121
|
+
if (JSON.stringify(before.reference) !== JSON.stringify(after.reference))
|
|
122
|
+
lines.push(after.reference === undefined ? "Removed the reference." : `Recorded a new reference: ${after.reference.tempo} BPM, ${after.reference.key}, ${count(after.reference.notes.length, "note")}.`);
|
|
123
|
+
return lines;
|
|
124
|
+
};
|
|
125
|
+
/** What `music notes` prints: the status, the note, comments in song order with their bars, and the edits. */
|
|
126
|
+
export const reviewSummary = (song, review) => ({
|
|
127
|
+
status: review.status,
|
|
128
|
+
note: review.note,
|
|
129
|
+
comments: [...review.comments]
|
|
130
|
+
.sort((a, b) => a.from - b.from)
|
|
131
|
+
.map((comment) => ({ where: `${barsOf(song, comment.from, comment.to)}${comment.track === undefined ? "" : `, ${comment.track}`}`, from: comment.from, to: comment.to, track: comment.track, text: comment.text })),
|
|
132
|
+
edits: review.edits,
|
|
133
|
+
next: review.status === "approved"
|
|
134
|
+
? "The person approved the song. Render it (`music render`) for the game."
|
|
135
|
+
: review.comments.length === 0 && review.edits.length === 0 && review.note === ""
|
|
136
|
+
? "Nothing said yet. Wait for the person, or ask them to listen."
|
|
137
|
+
: "Act on each comment in its bars; keep the person's edits (they are already in the song). Then `music check` and tell the person what changed."
|
|
138
|
+
});
|
|
139
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../../src/music/core/review.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,144 @@
|
|
|
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 { type InstrumentId } from "./instruments.ts";
|
|
28
|
+
export type Decoded<A> = {
|
|
29
|
+
readonly ok: true;
|
|
30
|
+
readonly value: A;
|
|
31
|
+
} | {
|
|
32
|
+
readonly ok: false;
|
|
33
|
+
readonly error: string;
|
|
34
|
+
};
|
|
35
|
+
export interface Note {
|
|
36
|
+
/** When it starts, in beats from the song's start. */
|
|
37
|
+
readonly at: number;
|
|
38
|
+
/** How long it is held, in beats. */
|
|
39
|
+
readonly length: number;
|
|
40
|
+
/** A note name (`"A4"`), or a drum name on a drum track (`"kick"`). */
|
|
41
|
+
readonly pitch: string;
|
|
42
|
+
/** How hard it is played, 0 to 1. */
|
|
43
|
+
readonly velocity: number;
|
|
44
|
+
}
|
|
45
|
+
export interface Track {
|
|
46
|
+
readonly name: string;
|
|
47
|
+
readonly instrument: InstrumentId;
|
|
48
|
+
/** 0 to 1.5: how loud the track is in the mix. */
|
|
49
|
+
readonly volume: number;
|
|
50
|
+
/** -1 (left) to 1 (right). */
|
|
51
|
+
readonly pan: number;
|
|
52
|
+
/** How much of the track goes to the room, 0 to 1. */
|
|
53
|
+
readonly reverb: number;
|
|
54
|
+
readonly mute?: boolean | undefined;
|
|
55
|
+
readonly notes: ReadonlyArray<Note>;
|
|
56
|
+
}
|
|
57
|
+
/** A note heard in the reference: a whole semitone, in beats on the song's grid. */
|
|
58
|
+
export interface HeardNote {
|
|
59
|
+
readonly at: number;
|
|
60
|
+
readonly length: number;
|
|
61
|
+
/** A note name. */
|
|
62
|
+
readonly pitch: string;
|
|
63
|
+
}
|
|
64
|
+
/** What `music listen` heard in a hum, a beat, or a recording. */
|
|
65
|
+
export interface Reference {
|
|
66
|
+
/** The audio file it was heard in, next to the song. */
|
|
67
|
+
readonly audio?: string | undefined;
|
|
68
|
+
/** Beats per minute, as heard. */
|
|
69
|
+
readonly tempo: number;
|
|
70
|
+
/** The key it sounds in, as heard: `"D minor"`. */
|
|
71
|
+
readonly key: string;
|
|
72
|
+
/** Its length in beats. */
|
|
73
|
+
readonly length: number;
|
|
74
|
+
/** The melody: each held pitch, rounded to a semitone and to the grid. */
|
|
75
|
+
readonly notes: ReadonlyArray<HeardNote>;
|
|
76
|
+
/** The raw pitch line, one value every eighth of a beat (MIDI note number, fractional; 0 where nothing is sung). */
|
|
77
|
+
readonly contour: ReadonlyArray<number>;
|
|
78
|
+
/** Where on the song the reference starts, in beats (0 by default). */
|
|
79
|
+
readonly offset: number;
|
|
80
|
+
/** Seconds into the audio file where its beat 0 falls (its first note). */
|
|
81
|
+
readonly start: number;
|
|
82
|
+
}
|
|
83
|
+
export interface Song {
|
|
84
|
+
/** What the song is for, in a few words: the agent's intent, shown to the person. */
|
|
85
|
+
readonly about?: string | undefined;
|
|
86
|
+
/** Beats per minute. */
|
|
87
|
+
readonly tempo: number;
|
|
88
|
+
/** The key: a note and a scale, `"A minor"`, `"C major"`, `"D dorian"`. */
|
|
89
|
+
readonly key: string;
|
|
90
|
+
/** Beats in a bar: 4 (most music), 3 (a waltz), or 6. */
|
|
91
|
+
readonly beatsPerBar: number;
|
|
92
|
+
readonly bars: number;
|
|
93
|
+
/** A loop's end wraps into its start, so it repeats without a seam (game music mostly loops). */
|
|
94
|
+
readonly loop: boolean;
|
|
95
|
+
/** The volume over the whole song, 0 to 2. */
|
|
96
|
+
readonly master: number;
|
|
97
|
+
/** Squeezes the whole mix louder and punchier, its peaks held under -1 dBFS: for rock, drum & bass, and electronic songs. Off by default. */
|
|
98
|
+
readonly compressor: boolean;
|
|
99
|
+
readonly tracks: ReadonlyArray<Track>;
|
|
100
|
+
readonly reference?: Reference | undefined;
|
|
101
|
+
}
|
|
102
|
+
/** A note name's MIDI number (`"C4"` is 60, `"A4"` 69), or undefined if it is not one. */
|
|
103
|
+
export declare const midiOf: (pitch: string) => number | undefined;
|
|
104
|
+
/** A MIDI number's note name, sharps for black keys (`61` is `"C#4"`). */
|
|
105
|
+
export declare const nameOf: (midi: number) => string;
|
|
106
|
+
/** A MIDI number's frequency in Hz (A4 = 440). */
|
|
107
|
+
export declare const frequencyOf: (midi: number) => number;
|
|
108
|
+
/** The scales a key may name, as semitones above the key's note. */
|
|
109
|
+
export declare const scales: {
|
|
110
|
+
readonly major: readonly [0, 2, 4, 5, 7, 9, 11];
|
|
111
|
+
readonly minor: readonly [0, 2, 3, 5, 7, 8, 10];
|
|
112
|
+
readonly "harmonic minor": readonly [0, 2, 3, 5, 7, 8, 11];
|
|
113
|
+
readonly dorian: readonly [0, 2, 3, 5, 7, 9, 10];
|
|
114
|
+
readonly phrygian: readonly [0, 1, 3, 5, 7, 8, 10];
|
|
115
|
+
readonly lydian: readonly [0, 2, 4, 6, 7, 9, 11];
|
|
116
|
+
readonly mixolydian: readonly [0, 2, 4, 5, 7, 9, 10];
|
|
117
|
+
readonly "major pentatonic": readonly [0, 2, 4, 7, 9];
|
|
118
|
+
readonly "minor pentatonic": readonly [0, 3, 5, 7, 10];
|
|
119
|
+
readonly blues: readonly [0, 3, 5, 6, 7, 10];
|
|
120
|
+
};
|
|
121
|
+
export type Scale = keyof typeof scales;
|
|
122
|
+
export declare const scaleNames: ReadonlyArray<Scale>;
|
|
123
|
+
export declare const tonics: ReadonlyArray<string>;
|
|
124
|
+
export interface Key {
|
|
125
|
+
/** The key's note, 0 (C) to 11 (B). */
|
|
126
|
+
readonly root: number;
|
|
127
|
+
readonly scale: Scale;
|
|
128
|
+
/** The pitch classes in the key (0 is C). */
|
|
129
|
+
readonly classes: ReadonlyArray<number>;
|
|
130
|
+
}
|
|
131
|
+
/** Reads `"A minor"`, `"F# dorian"`, `"Bb major pentatonic"`. */
|
|
132
|
+
export declare const parseKey: (key: string) => Key | undefined;
|
|
133
|
+
export declare const inKey: (key: Key, midi: number) => boolean;
|
|
134
|
+
export declare const secondsPerBeat: (song: Song) => number;
|
|
135
|
+
export declare const lengthInBeats: (song: Song) => number;
|
|
136
|
+
export declare const lengthInSeconds: (song: Song) => number;
|
|
137
|
+
/** `"bar 3"`, or `"bars 3–4"`: where a span of beats lies, bars counted from 1. */
|
|
138
|
+
export declare const barsOf: (song: Song, from: number, to: number) => string;
|
|
139
|
+
/** Reads a song from parsed JSON, collecting every problem with where it is. */
|
|
140
|
+
export declare const decodeSongValue: (raw: unknown) => Decoded<Song>;
|
|
141
|
+
export declare const decodeSong: (text: string) => Decoded<Song>;
|
|
142
|
+
/** The song as JSON, with every default left out and one note per line. */
|
|
143
|
+
export declare const encodeSong: (song: Song) => string;
|
|
144
|
+
//# sourceMappingURL=song.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"song.d.ts","sourceRoot":"","sources":["../../../src/music/core/song.ts"],"names":[],"mappings":"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"}
|