80c0b6dPlate dynamics lab: plates in realizable mounts under gravity, explicit finite differences on WebGPUJeremy Magland 1/**
2 * Encode a mono signal as a 16-bit PCM RIFF/WAVE file. The input is expected
3 * to be peak-normalized already (see `prepareAudio`); values outside [-1, 1]
4 * are clipped rather than wrapped. Non-finite samples are written as silence.
5 */
6export function encodeWav(samples: ArrayLike<number>, fs: number): Uint8Array {
7 const n = samples.length;
8 const bytesPerSample = 2;
9 const dataBytes = n * bytesPerSample;
10 const buf = new ArrayBuffer(44 + dataBytes);
11 const v = new DataView(buf);
12 const ascii = (off: number, s: string) => {
13 for (let i = 0; i < s.length; i++) v.setUint8(off + i, s.charCodeAt(i));
14 };
15 ascii(0, "RIFF");
16 v.setUint32(4, 36 + dataBytes, true);
17 ascii(8, "WAVE");
18 ascii(12, "fmt ");
19 v.setUint32(16, 16, true); // fmt chunk size
20 v.setUint16(20, 1, true); // PCM
21 v.setUint16(22, 1, true); // mono
22 v.setUint32(24, fs, true);
23 v.setUint32(28, fs * bytesPerSample, true); // byte rate
24 v.setUint16(32, bytesPerSample, true); // block align
25 v.setUint16(34, 16, true); // bits per sample
26 ascii(36, "data");
27 v.setUint32(40, dataBytes, true);
28 let off = 44;
29 for (let i = 0; i < n; i++, off += 2) {
30 const x = samples[i];
31 const c = Number.isFinite(x) ? Math.max(-1, Math.min(1, x)) : 0;
32 v.setInt16(off, Math.round(c < 0 ? c * 32768 : c * 32767), true);
33 }
34 return new Uint8Array(buf);
35}