> ## Documentation Index
> Fetch the complete documentation index at: https://developer.suki.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# WAV Header Streamed as Audio Data

> Strip WAV headers before Base64 PCM streaming for Ambient and Dictation

If you Base64-encode a whole `.wav` file and stream it, the RIFF header is treated as audio. Transcription quality drops, and the stream still looks valid.

Ambient and Dictation expect raw PCM (LINEAR16 / PCM\_S16LE), not a WAV container. Strip the header or decode to raw PCM before Base64 encoding.

## Common causes

* Encoding the full WAV file without removing the header.
* Test harnesses that treat WAV as raw PCM.
* Sending container bytes instead of PCM samples.

## Fix

<Steps>
  <Step title="Strip the RIFF Header">
    For a standard RIFF WAV, remove the **44-byte** header when the buffer starts with `RIFF` and has `WAVE` at offset 8, or decode the file to raw PCM first.
  </Step>

  <Step title="Encode Only PCM Samples">
    Base64-encode raw mono **16 kHz** LINEAR16 / PCM\_S16LE with standard Base64 ([RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648)). Do not use hex or URL-safe Base64.
  </Step>

  <Step title="Use the Correct Field">
    On ambient `/ws/stream`, put Base64 PCM in **`data`**. On Dictation `/ws/transcribe`, put Base64 PCM in **`audioData`**.
  </Step>
</Steps>

```python theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
WAV_HEADER_BYTES = 44  # Standard WAV header size to strip

def strip_wav_header(raw: bytes) -> bytes:
    """If the file is WAV, drop the 44-byte header and keep only raw PCM bytes."""
    if len(raw) >= 12 and raw[:4] == b"RIFF" and raw[8:12] == b"WAVE":
        return raw[WAV_HEADER_BYTES:]
    return raw
```

```typescript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const WAV_HEADER_BYTES = 44; // Standard WAV header size to strip

/** If the file is WAV, drop the 44-byte header and keep only raw PCM bytes. */
function stripWavHeader(buf: Uint8Array): Uint8Array {
  if (
    buf.length >= 12 &&
    buf[0] === 0x52 &&
    buf[1] === 0x49 &&
    buf[2] === 0x46 &&
    buf[3] === 0x46 &&
    buf[8] === 0x57 &&
    buf[9] === 0x41 &&
    buf[10] === 0x56 &&
    buf[11] === 0x45
  ) {
    return buf.subarray(WAV_HEADER_BYTES);
  }
  return buf;
}
```

<Tip>
  If the buffer is already raw PCM, do not strip anything. Sending WAV headers as PCM hurts recognition and makes debugging harder.
</Tip>

## Next steps

<Icon icon="file-lines" iconType="solid" /> **[Build an ambient streaming client](/documentation/tutorials/ambient-websocket-code-example)** - Full client that strips WAV before stream

<Icon icon="file-lines" iconType="solid" /> **[Build a Dictation streaming client](/documentation/tutorials/dictation-websocket-code-example)** - Same strip helper for `/ws/transcribe`

<Icon icon="file-lines" iconType="solid" /> **[Ambient WebSocket disconnects](/documentation/troubleshooting/websocket-disconnects-ambient)** - Frame format and end marker issues

<Icon icon="file-lines" iconType="solid" /> **[Dictation WebSocket handshake](/documentation/troubleshooting/dictation-websocket-handshake)** - `audioData` vs ambient `data`

<Icon icon="file-lines" iconType="solid" /> **[Empty notes after end session](/documentation/troubleshooting/empty-notes-after-end-session)** - Poll status after a correct stream end
