/** A mono float trace as 16-bit PCM .wav bytes, clipped to [-1, 1]. The * mix is already levelled and at the mix rate, so no resampling or * normalization happens here. */ export function wavBytes(samples: Float32Array, sr: number): Uint8Array { const n = samples.length; const buf = new ArrayBuffer(44 + 2 * n); const view = new DataView(buf); const str = (off: number, s: string): void => { for (let i = 0; i < s.length; i++) view.setUint8(off + i, s.charCodeAt(i)); }; str(0, 'RIFF'); view.setUint32(4, 36 + 2 * n, true); str(8, 'WAVE'); str(12, 'fmt '); view.setUint32(16, 16, true); view.setUint16(20, 1, true); // PCM view.setUint16(22, 1, true); // mono view.setUint32(24, sr, true); view.setUint32(28, 2 * sr, true); view.setUint16(32, 2, true); view.setUint16(34, 16, true); str(36, 'data'); view.setUint32(40, 2 * n, true); for (let i = 0; i < n; i++) { const v = Math.max(-1, Math.min(1, samples[i])); view.setInt16(44 + 2 * i, Math.round(v * 32767), true); } return new Uint8Array(buf); }