All audio is mono float32 in [-1.0, 1.0], little-endian, at the rate in
config.SAMPLE_RATE (16 kHz). Two transports share this sample format.
Duplex, one denoised chunk returned per input chunk.
- Handshake (client → server, TEXT/JSON):
{ "chunk_samples": 320, "vad": true } - Ack (server → client, TEXT/JSON):
On a bad handshake the server replies
{ "ok": true, "sample_rate": 16000, "chunk_samples": 320, "enhancer": "spectral-gate", "vad": true }{ "ok": false, "error": "..." }and closes. - Audio (client → server, BINARY): exactly
chunk_samplesfloat32 samples (chunk_samples * 4bytes) per message. - Audio (server → client, BINARY): exactly
chunk_samplesdenoised float32 samples per input chunk. A wrong-sized input gets a TEXT error frame instead. - The client closes the socket (or sends TEXT
"bye") to end the stream.
With vad: true the server gates clearly-silent input chunks to zero on output,
suppressing residual noise during pauses.
| Request body | raw float32 PCM (len % 4 == 0) |
| Request header | X-Sample-Rate: 16000 |
| Response | audio/wav (denoised, mono int16 WAV) |
| Errors | 400 if the body is empty or not float32-aligned |
{ "ok": true, "service": "denoise-stream", "version": "0.1.0",
"sample_rate_hz": 16000, "chunk_samples": 320, "enhancer": "spectral-gate" }A self-contained length-prefixed format used by the low-level transport and the unit tests. Little-endian throughout:
header := magic[8 bytes = "DNSTREAM"] sample_rate[uint32] chunk_samples[uint32] (16 bytes)
frame := num_floats[uint32] payload[num_floats * 4 bytes]
EOS := num_floats == 0 (no payload)
The 8-byte magic lets a mismatched endpoint fail fast with a clear error rather than misinterpreting bytes as audio.