Two clocks, one voice loop
Discord speaks Opus at 48 kHz stereo; the Gemini Live API speaks raw PCM at 24 kHz mono. Bridging them in real time is less about audio theory than about deciding who is allowed to wait.
Text in, text out is forgiving: a slow response is a slow response. Voice is not. Two systems are producing and consuming audio continuously, at different sample rates, in different encodings, and neither pauses politely while the other catches up.
The mismatch
Discord delivers voice as Opus, 48 kHz stereo. The Gemini Live API expects raw PCM at 24 kHz mono and answers in the same shape. So every inbound frame is decoded and downsampled, and every outbound frame is upsampled and re-encoded, continuously, in both directions, for as long as the session is open.
Turn-taking is the real feature
The interesting problem is not conversion, it is knowing when someone has stopped speaking. Push-to-talk answers it by making the user do the work. Voice activity detection answers it by watching the stream, which is what makes the exchange feel like a conversation rather than a radio protocol.
Sessions are state, and state leaks
A live session holds a voice connection, decoder and encoder pipelines, and an open API stream. The same command that starts it has to tear all of it down, including when the user simply leaves the channel. Anything that opens per session must close per session, or the bot survives the conversation and the memory does too.
Real-time work rewards being explicit about which side is allowed to wait, and about what has to be released when the conversation ends.