Streaming
Start playing while the rest is still being generated: over HTTP with stream: true, or over one WebSocket connection for many sentences.
HTTP streaming
With stream: true the body starts with a 44-byte WAV header, followed by raw PCM16 as it is generated: little-endian, mono, 48 kHz on Alania-2 and 24 kHz on Alania-1. The length of a stream is not known in advance, so the header's length fields are 0xFFFFFFFF.
- Streams accept
wav,pcm,mulawandalaw.pcmis raw samples without a header; the rate is inContent-Type: audio/L16; rate=48000. For phone lines see Telephony (8 kHz). - Network chunks may split a sample: carry a leftover byte over to the next chunk.
- Long text is split at sentence boundaries, with 250 ms of silence between parts.
x-audio-secondsis not sent on a stream: it is not known until the audio ends.
48 kHz playback in the browser
The Browser tab of the HTTP streaming example schedules each chunk with Web Audio. What matters:
- Create buffers at the right rate:
createBuffer(1, n, 48000). Read the rate from byte 24 of the WAV header, or fromspeech_start.sample_rateon a WebSocket; Alania-1 is 24 kHz. new AudioContext({ sampleRate: 48000 })avoids needless resampling; if the device runs at another rate, the browser converts.- Schedule each chunk at the end of the previous one; a 50 ms margin on the first avoids a stutter.
- An
AudioContextonly plays after a user gesture; start the request from a click.
Latency
On Alania-2, a streamed request gets its first audio in about 190 ms (median, measured through the public API with the network included). What you measure depends on your network and distance to the servers. The x-queue-wait-ms header reports the time the request waited for a generation slot.
WebSocket
wss://voice.patientdesk.ai/v1/audio/speech/streamFor an application that speaks many sentences in a row, such as a voice agent, use one connection. Each speak message returns one audio stream and the connection stays open. Authenticate with ?key= or ?token=; ?model=alania-v1 makes Alania-1 the connection's default model. For a phone line, ?encoding=mulaw (or alaw) sends the audio as 8 kHz G.711; ?sample_rate=16000 and the like choose another rate.
Messages you send
speakJSONinputis required. Every field of the HTTP body works:model,voice,instructions,voice_description,reference_audio(base64),reference_text,consent,cfg,temperature,seed,sample_rate;encoding(pcm_s16le,mulaw,alaw) overrides the connection's output for this message. Messages are handled in order, one at a time; onlycanceltakes effect at once.cancelJSON- Stops the
speakin progress at once: its audio ends, itsspeech_endarrives with"canceled": true, and the connection stays open; the next queuedspeakstarts in its turn. Eachcancelends the oldestspeaknot yet ended, so onecancelper outstandingspeaksilences them all. With none outstanding it does nothing. See Barge-in. stopJSON- Answered with
stopped, then the connection closes.closedoes the same.
Messages you receive
readyJSONmodel,voice,sample_rate,encoding,models,usage. The connection is ready;usageis your remaining allowance.speech_startJSONcharacters,model,voice,sample_rate,encoding(pcm_s16le,mulaworalaw),disclosure. Arrives before the audio of eachspeak.binaryPCM16 · G.711- Audio, mono: PCM16, or one byte a sample when
encodingismulaw/alaw. On Alania-2 messages are usually 0.16 seconds (7,680 samples at 48 kHz); the last one may be shorter. speech_endJSONaudio_s,infer_ms;canceled: truewhen acancelstopped it. Thisspeakis done.stoppedJSON- The reply to
stop. errorJSONmessage,code,category,retry_after. After an unknown message type the connection stays open; after any other error it closes.
Barge-in
- When the caller talks over the agent, send one
{"type":"cancel"}for eachspeakyou sent that has nospeech_endyet, and stop playing what you have buffered. Audio already on its way before thecancelmay still arrive; drop binary messages until the nextspeech_start. - A
speakis charged once itsspeech_startis sent: all of itsinputcharacters, even when acancelor a closed connection cuts it short. Aspeakcanceled before itsspeech_start(one still queued, say) gets only aspeech_end(audio_s: 0,canceled: true) and is not charged. - Closing the connection also stops the
speakin progress, but the next reply then needs a new connection.cancelkeeps it open.
Compressed formats
For non-streaming requests response_format can be mp3, opus (Ogg), flac or aac, which saves bandwidth for mobile apps. They cannot be streamed: half a container file is not playable, so they are refused with a 400.
Telephony (8 kHz)
A phone line carries 8 kHz G.711. response_format: "mulaw" (μ-law, North America and Japan) or "alaw" (A-law, Türkiye and Europe) returns audio in that form directly, headerless, one byte a sample; there is nothing to resample. It works on streams and on the WebSocket too.
sample_ratepicks another rate: 8000, 16000, 22050, 24000, 44100 or 48000 (up to the model's own). G.711 defaults to 8000, everything else to the model's rate. For example"response_format": "pcm", "sample_rate": 16000gives 16 kHz PCM16.- The conversion runs on the server as the audio is made and adds no time to the first audio. 8 kHz μ-law is 12 times less data than 48 kHz PCM16.
Content-Typestates the format and rate:audio/PCMU; rate=8000,audio/PCMA; rate=8000,audio/L16; rate=16000.