sync to avoid exhausting the audio bufferThe API allows you to buffer up to 30MB of audio data at any given time. The buffer size is chosen to be large enough that you don’t need to worry about it in most normal use cases — any individual audio file will comfortably fit inside the 30MB limit. However, this can become an issue if your application tries to play multiple large audio files in succession, or if you play the same audio file repeatedly in a loop.
In short: you rarely need sync. It’s not needed for a
single audio file, or for small chunks sent back and forth like in a live AI
voice conversation. Only use it when sending large amounts of audio at once,
such as looping playback or several large files in a row.
In the following example, the application naively plays the same file again and again, looping forever. This will quickly fill the API’s audio buffer and cause the call to end with an error:
# NB: This code does not work
while True:
with open("audio.wav", "rb") as f:
await elks_ws.send(json.dumps({
"t": "sending",
"format": "wav"
}))
for chunk in iter(lambda: f.read(32 * 1024), b""):
await ws.send(json.dumps({
"t": "audio",
"data": base64.b64encode(chunk).decode()
}))
Instead of writing complex timing code in your application, or guessing how
much data is in the API’s buffer at any given moment, you can use the sync
message to synchronize the state between the API and your application.
The sync message acts as a “checkpoint” of sorts that lets your application
know that the API has processed all messages up until that point. When the
API receives a sync message it will reply back with its own sync —
but only after all previous messages have been processed. In other words, if
you send a sync after sending some audio, the API will not respond to it
until all audio sent before the sync has been played.
You can use this to ensure your application doesn’t send “too much” audio at once, as in the following example.
syncThis example plays a WAV file in a loop until the user hangs up.
async def looped_playback(ws):
# Get the call metadata from the hello message
hello = json.loads(await ws.recv())
print(f"Received {hello['to']} <- {hello['from']} ({hello['callid']})")
while True:
# Tell the API the format we'll be sending audio in
await ws.send(json.dumps({
"t": "sending",
"format": "wav"
}))
# Send the WAV file bytes in chunks
with open("audio.wav", "rb") as f:
for chunk in iter(lambda: f.read(32 * 1024), b""):
await ws.send(json.dumps({
"t": "audio",
"data": base64.b64encode(chunk).decode()
}))
# Send the sync message
await ws.send(json.dumps({
"t": "sync"
}))
# Wait for the response from the API.
msg = json.loads(await ws.recv())
# Exit if the call has ended. Else, play the file again.
if msg["t"] == "bye":
print("Call ended:", msg["message"])
break