Audio
PCM16 format, sample rates, binary vs base64 encoding, and returning audio.
Format and sample rates
Audio is PCM16 little-endian, mono in both directions, with no headers and no container. Fonada can send audio at 8000 Hz or 16000 Hz; 16 kHz is preferable for recognition accuracy where the telephony path supports it.
For audio you send back, the rate must be agreed in advance and must match output_sample_rate in session_start — Fonada does not detect the rate from the stream. Sending a different rate is not an error, but it produces audio that sounds too fast or too slow.
Which rates Fonada accepts depends on the rate of the call itself, because Fonada downsamples but never upsamples:
| Call runs at | Fonada accepts from you |
|---|---|
| 16000 Hz | 16000 Hz, or 24000 / 48000 Hz (Fonada downsamples) |
| 8000 Hz | 8000 Hz only |
So if your TTS emits 24 kHz or 48 kHz, there is no need to resample on your side for a 16 kHz call. On an 8 kHz call, you must send 8 kHz.
Binary (default, recommended)
Audio travels as raw PCM bytes in WebSocket binary frames; control messages travel as text frames. Distinguishing them is a matter of checking the frame type. This is the most efficient option.
Base64
If your stack cannot handle binary WebSocket frames, Fonada wraps audio in JSON text frames:
{ "type": "media", "payload": "<base64-encoded PCM16 LE mono>" }This applies in both directions, and all control messages stay exactly the same. Because audio and control now share the text frame type, dispatch on the type field — treat media as audio and everything else as control.
Fonada's receive path is deliberately tolerant: whichever encoding is configured, Fonada accepts audio from you as either binary frames or media text frames. The setting governs only what Fonada sends, so you cannot break audio to the caller by choosing the other form.
Trade-offs before choosing base64:
- ~33% more bandwidth for audio. At 16 kHz a call rises from ~64 kB/s to ~85 kB/s across both directions.
- Slightly more CPU on both sides — measured at well under 0.1% of a core per call.
- Chunk sizes still refer to raw PCM bytes before encoding, so the text frame is ~a third larger than quoted: 320 bytes of PCM becomes an ~428-character payload.
Sending audio back
Send audio only when there is something for the end user to hear. During silence, send nothing — silence is generated by Fonada. Frames may be any size; Fonada re-chunks them into the 20 ms frames the telephony layer requires.