{"record":{"id":"f9f6f644663c6f12","repo":"heygen-com/hyperframes","slug":"unsupported-wav-format-format-bits-bit-pa","errorCode":null,"errorMessage":"Unsupported WAV format ${format}/${bits}-bit: ${path}","messagePattern":"Unsupported WAV format (.+?)/(.+?)-bit: (.+?)","errorType":"exception","errorClass":"AudioFxRenderError","httpStatus":null,"severity":"error","filePath":"packages/engine/src/services/audioFxRender.ts","lineNumber":97,"sourceCode":"/** Interleaved samples as floats, for the two formats the mixer emits upstream. */\nfunction decodeSamples(data: Buffer, format: number, bits: number, path: string): Float32Array {\n  if (format === 3 && bits === 32) {\n    const n = Math.floor(data.length / 4);\n    // A Float32Array view demands a 4-aligned offset, and chunk layouts that put\n    // `data` on an odd boundary (an 18-byte fmt plus a fact chunk, which\n    // ffmpeg's pcm_f32le writes) would otherwise throw RangeError. Copy then.\n    if (data.byteOffset % 4 === 0) return new Float32Array(data.buffer, data.byteOffset, n);\n    const copied = new Float32Array(n);\n    for (let i = 0; i < n; i++) copied[i] = data.readFloatLE(i * 4);\n    return copied;\n  }\n  if (format === 1 && bits === 16) {\n    const n = Math.floor(data.length / 2);\n    const out = new Float32Array(n);\n    for (let i = 0; i < n; i++) out[i] = data.readInt16LE(i * 2) / 32768;\n    return out;\n  }\n  throw new AudioFxRenderError(`Unsupported WAV format ${format}/${bits}-bit: ${path}`);\n}\n\n/**\n * Write 16-bit PCM, interleaved, preserving the channel count.\n *\n * 16-bit rather than the float32 this used to emit: the very next step in the\n * mixer bakes the volume envelope into the samples, and that baker accepts only\n * 16-bit PCM. Emitting float meant enabling any effect silently downgraded a\n * track's volume automation to the ffmpeg expression path, which is capped at 32\n * straight segments — so a curved envelope was quantised and a dense one could\n * fall back to rendering at base volume.\n */\nexport function writeWav(\n  path: string,\n  samples: Float32Array,\n  sampleRate: number,\n  channels = 1,\n): void {","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/engine/src/services/audioFxRender.ts#L79-L115","documentation":"Thrown by decodeSamples() when a WAV file's format/bits-per-sample combination is not one of the two supported decodings: IEEE float (format=3, 32-bit) or PCM (format=1, 16-bit). The audio FX pipeline only accepts samples in those two layouts because the upstream mixer writes 16-bit PCM or float32 and the baker that follows accepts only 16-bit. Any other combination — 24-bit PCM, 32-bit integer PCM, or extensible (0xFF) — has no decode path.","triggerScenarios":"readWav() or applyAudioFxChain() is called on a WAV whose fmt chunk reports a format code and bit depth outside {3/32, 1/16}. The check fires after RIFF/data-chunk parsing succeeds, so the file is a valid WAV with an unsupported sample encoding.","commonSituations":"Author supplies a custom audio asset exported at 24-bit PCM (common in DAWs). An upstream tool re-encodes to 32-bit integer PCM instead of float. A WAV uses WAVE_FORMAT_EXTENSIBLE (0xFF) with a valid sub-format that the parser doesn't unwrap.","solutions":["Re-export or transcode the source WAV to 16-bit PCM or 32-bit float: ffmpeg -i input.wav -c:a pcm_s16le output.wav","If the file is WAVE_FORMAT_EXTENSIBLE, unwrap it to its base format: ffmpeg -i input.wav -c:a pcm_f32le output.wav","Verify with ffprobe: ffprobe -show_entries stream=sample_fmt,bits_per_raw_sample input.wav — sample_fmt s16 or f32le are accepted.","If you control the upstream render step, ensure writeWav() (which emits 16-bit PCM) is the only writer feeding the FX chain."],"exampleFix":"// before: source WAV is 24-bit PCM\n// after: transcode before passing to the pipeline\nimport { execSync } from 'child_process';\nexecSync('ffmpeg -y -i track.wav -c:a pcm_s16le track-16.wav');\nawait applyAudioFxChain('track-16.wav', chain, outWav, opts);","handlingStrategy":"validation","validationCode":"import { readFileSync } from 'fs';\n\nfunction isSupportedWav(path: string): boolean {\n  const buf = readFileSync(path);\n  if (buf.length < 44 || buf.toString('ascii', 0, 4) !== 'RIFF') return false;\n  // format code at offset 20, bits at offset 34\n  const format = buf.readUInt16LE(20);\n  const bits = buf.readUInt16LE(34);\n  return (format === 3 && bits === 32) || (format === 1 && bits === 16);\n}\n\n// before calling applyAudioFxChain:\nif (!isSupportedWav(inputWav)) {\n  // transcode or skip\n}","typeGuard":null,"tryCatchPattern":"try {\n  await applyAudioFxChain(inputWav, chain, outWav, opts);\n} catch (err) {\n  if (err instanceof AudioFxRenderError && err.message.includes('Unsupported WAV format')) {\n    // transcode and retry, or fall back to dry signal with a warning\n  }\n  throw err;\n}","preventionTips":["Standardize all track WAVs to 16-bit PCM at the upstream render step.","Validate WAV headers with ffprobe before passing to the FX pipeline.","Document the two accepted format/bit-depth combinations in the audio module's README."],"tags":["audio","wav","codec","ffmpeg","validation"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}