Skip to content

VoiceConnection & AudioPlayer

@lunibee/voice provides low-level Discord voice support. VoiceConnection manages the Voice Gateway session, while AudioPlayer drives audio streaming into a voice channel.

[!NOTE] Voice support is at a lower abstraction level than the main Client. You must wire UDP transport yourself or use a higher-level adapter.

import {
VoiceConnection,
VoiceConnectionState,
AudioPlayer,
AudioStream,
VoiceReceiver,
SpeakingFlags,
} from "@lunibee/voice";

Manages the Discord Voice Gateway WebSocket session for a single guild.

new VoiceConnection(guildId: string)
// or with full options:
new VoiceConnection({ guildId, endpoint, token, sessionId, selfDeaf, selfMute })
Property Type Description
state VoiceConnectionState Current connection state.
guildId string The guild this connection belongs to.
State Description
Disconnected Not connected.
Connecting Waiting for READY/SESSION_DESCRIPTION.
Ready Fully connected, audio can be played.
Destroyed Permanently closed.

Starts the Voice WebSocket connection.

await connection.connect(voiceEndpoint, voiceToken, sessionId);

Attaches a VoiceUdpTransport for sending audio packets.

connection.attachUdp(myUdpTransport);

Updates the speaking state.

connection.setSpeaking(SpeakingFlags.Microphone);
connection.setSpeaking(0); // stop speaking

Closes and destroys the connection.

on(event, listener) / off(event, listener)

Section titled “on(event, listener) / off(event, listener)”
Event Args Description
"ready" (ssrc: number) Connection is fully established.
"stateChange" (prev, next) State transition.
"sessionDescription" (data) Encryption key/mode received.

Drives audio from a ReadableStream<Uint8Array> into the voice connection.

new AudioPlayer()
Property Type Description
state AudioPlayerState "idle", "playing", "paused", "stopped".

Starts playing an AudioStream.

const stream = new AudioStream(readableStream, { frameSize: 960 });
player.play(stream);

Pauses playback.

Resumes from paused state.

Stops playback and enters "stopped" state.

Event Args Description
"start" () Playback began.
"pause" () Playback paused.
"resume" () Playback resumed.
"finish" () Audio stream finished naturally.
"stop" () Manually stopped.
"error" (error) Playback error.

Wraps a ReadableStream<Uint8Array> for use with AudioPlayer.

import { AudioStream } from "@lunibee/voice";
import { createReadStream } from "node:fs";
const fileStream = createReadStream("./music.pcm");
const audio = new AudioStream(Readable.toWeb(fileStream), { frameSize: 960 });

Receives and demultiplexes incoming voice audio by SSRC/user ID.

const receiver = new VoiceReceiver(connection);
const userStream = await receiver.subscribe(userId);
// userStream: ReadableStream<Uint8Array>

Flag Value Description
SpeakingFlags.Microphone 1 Normal microphone speaking.
SpeakingFlags.Soundshare 2 Application audio (screen share).
SpeakingFlags.Priority 4 Priority speaker.

import { VoiceConnection, AudioPlayer, AudioStream, SpeakingFlags } from "@lunibee/voice";
// Get voice state from a Gateway event:
client.on(ClientEvent.VoiceStateUpdate, async (data) => {
// Use VoiceStateUpdate + VoiceServerUpdate to get endpoint/token/sessionId...
});
// Once you have voice credentials:
const connection = new VoiceConnection({ guildId });
await connection.connect(endpoint, token, sessionId);
connection.on("ready", async (ssrc) => {
const player = new AudioPlayer();
const stream = new AudioStream(audioReadableStream, { frameSize: 960 });
connection.setSpeaking(SpeakingFlags.Microphone);
player.play(stream);
player.on("finish", () => {
connection.setSpeaking(0);
connection.destroy();
});
});