A phone line with Twilio
Answer a call, transcribe the caller live with Duyu and reply in Alania's voice. Audio stays 8 kHz μ-law both ways; nothing to resample.
How it fits together
- Twilio answers the call and sends the caller's audio to your server over a Media Stream WebSocket: 20 ms frames of base64 8 kHz μ-law.
- Your server turns μ-law into PCM16 and sends it to Duyu's live stream with
sampleRate: 8000. - Duyu sends a
finalwhen a sentence ends. You decide what to say back: rules, an LLM, or both. - The reply goes to an Alania WebSocket opened with
?encoding=mulawas aspeak. Every binary message that comes back is already 8 kHz μ-law and goes straight back to Twilio.
TwiML
Return this TwiML from your Twilio number's incoming-call webhook. url is the public WSS address of the bridge below.
XML
The bridge
Each call opens two connections and keeps them for the whole call: one to Duyu (listening) and one to Alania (speaking). answer() is your agent.
bridge.js
- Barge-in. When the caller talks over Alania, Duyu sends
segment_start. The bridge sends Twilio aclearto drop buffered audio and discards the rest of that reply. - Order. Alania handles
speakmessages one at a time, in order. Messages sent before the connection isreadywait in a queue. - Chunk size. Twilio sends 20 ms; the bridge forwards 60 ms at a time (Duyu expects 40–100 ms chunks).
Latency
| Step | Time | Knob |
|---|---|---|
| End of sentence | ~500 ms of silence | eager_final: true moves it about 120 ms earlier (on in the bridge). |
| Your agent | up to you | Stream the LLM reply and send each sentence as its own speak. |
| First audio | ~190 ms | The μ-law conversion runs on the server and adds nothing. |