Source code
Revision control
Copy as Markdown
Other Tools
// Shared crashtest helper: build a PCM WAV file as an ArrayBuffer.
function buildWav(channels, rate, seconds) {
const PCM_FORMAT = 1; // WAVE_FORMAT_PCM
const BYTES_PER_SAMPLE = 2; // 16-bit samples
const BITS_PER_SAMPLE = 8 * BYTES_PER_SAMPLE;
const FMT_CHUNK_BYTES = 16; // size of a PCM "fmt " chunk body
const HEADER_BYTES = 44; // canonical PCM WAV header length
const RIFF_FIELDS_BEFORE_SIZE = 8; // "RIFF" + the size field itself
const blockAlign = channels * BYTES_PER_SAMPLE;
const byteRate = rate * blockAlign;
const frames = Math.floor(rate * seconds);
const dataSize = frames * blockAlign;
const buf = new ArrayBuffer(HEADER_BYTES + dataSize);
const v = new DataView(buf);
// Sequential writer so the header reads top-to-bottom instead of by offset.
let pos = 0;
const tag = s => { for (let i = 0; i < s.length; i++) v.setUint8(pos++, s.charCodeAt(i)); };
const u16 = n => { v.setUint16(pos, n, true); pos += 2; };
const u32 = n => { v.setUint32(pos, n, true); pos += 4; };
const s16 = n => { v.setInt16(pos, n, true); pos += 2; };
// RIFF chunk descriptor.
tag("RIFF");
u32(HEADER_BYTES - RIFF_FIELDS_BEFORE_SIZE + dataSize); // bytes after this field
tag("WAVE");
// "fmt " sub-chunk.
tag("fmt ");
u32(FMT_CHUNK_BYTES);
u16(PCM_FORMAT);
u16(channels);
u32(rate);
u32(byteRate);
u16(blockAlign);
u16(BITS_PER_SAMPLE);
// "data" sub-chunk.
tag("data");
u32(dataSize);
// Interleaved 16-bit PCM: a quiet sine, identical across every channel.
const AMPLITUDE = 12000;
const SINE_STEP = 0.05;
for (let f = 0; f < frames; f++) {
const sample = Math.round(Math.sin(f * SINE_STEP) * AMPLITUDE);
for (let c = 0; c < channels; c++) { s16(sample); }
}
return buf;
}