Skip to content

DiscordVoice

Duplex Discord voice-channel provider ('input' + 'output').

Defined in: src/providers/io/discord/DiscordVoice.ts:425

Duplex Discord voice-channel provider ('input' + 'output').

Remarks

DiscordVoice captures speech from users in a Discord voice channel and plays synthesized speech back into it, implementing both AudioInputProvider and AudioOutputProvider in one class.

Input: the connection’s receiver notifies when a user starts speaking (receiver.speaking.on('start')); the provider subscribes to that user’s Opus packet stream (ending after DiscordVoiceConfig.silenceDurationMs of silence), decodes it with prism-media (rate: 48000, channels: 2, frameSize: 960), downmixes interleaved stereo to mono by averaging each left/right pair, and emits linear16 mono 48 kHz AudioChunks.

Output: TTS chunks are queued by enqueue(). flush() concatenates the queue, converts it to 48 kHz stereo s16le PCM (linear-interpolation resampling for other rates, mono duplicated into both channels), wraps it in an audio resource with StreamType.Raw, and plays it via a shared AudioPlayer. flush() resolves when the player transitions back to AudioPlayerStatus.Idle. Because the audio is fed as raw PCM, no ffmpeg is required.

Duplex stop() semantics: a duplex provider has a single stop() serving both role interfaces. DiscordVoice.stop() implements the output contract (barge-in): it clears queued audio and calls player.stop(true), leaving capture running so the interrupting user is still heard. To end capture, call stopCapture(), detach(), or dispose(). Likewise pause()/resume() primarily gate input emission (the turn-taking system uses them to mute capture during agent speech); pause() only pauses the player when audio is actually playing.

Data-flow diagram:

speaking('start', userId)
       │
       ▼
receiver.subscribe(userId) ──Opus──> prism opus.Decoder ──PCM 48k/2ch──┐
                                                                       │
             onAudio(chunk: linear16 48k mono) <──stereo→mono downmix──┘

enqueue(chunk) ─> queue ─flush()─> 48k stereo s16le ─> createAudioResource(Raw)
                                                             │
                             player.play(resource) <─────────┘
                                   │
                   AudioPlayerStatus.Idle ─> flush() resolves, onPlaybackEnd

Example

import { DiscordVoice } from 'composite-voice';
import { joinVoiceChannel } from '@discordjs/voice';

const discord = new DiscordVoice({ debug: true });
await discord.initialize();

discord.attach(joinVoiceChannel({
  channelId, guildId, adapterCreator: guild.voiceAdapterCreator,
  selfDeaf: false,
}));

discord.onAudio((chunk) => {
  console.log(`captured ${chunk.data.byteLength} bytes`);
});
discord.start();

See

Implements

Constructors

Constructor

new DiscordVoice(config?): DiscordVoice;

Defined in: src/providers/io/discord/DiscordVoice.ts:530

Creates a new DiscordVoice instance.

Parameters

ParameterTypeDescription
configDiscordVoiceConfigProvider configuration. All fields are optional; a connection can be supplied later via attach().

Returns

DiscordVoice

Remarks

Construction is side-effect free. The @discordjs/voice and prism-media peer dependencies are only imported when initialize() is called.

Example

const discord = new DiscordVoice({ silenceDurationMs: 800 });

Properties

PropertyModifierTypeDefault valueDescriptionDefined in
rolesreadonlyreadonly ProviderRole[]undefinedPipeline roles covered by this provider. Remarks DiscordVoice is a duplex provider covering both the 'input' and 'output' slots. Separate STT, LLM, and TTS providers are still required.src/providers/io/discord/DiscordVoice.ts:442
typereadonlyProviderType'websocket'Communication type for this provider. Remarks 'websocket' — the provider operates over Discord’s persistent voice gateway connection (owned by the attached VoiceConnection).src/providers/io/discord/DiscordVoice.ts:433

Methods

attach()

attach(connection): void;

Defined in: src/providers/io/discord/DiscordVoice.ts:649

Attach a Discord VoiceConnection to the provider.

Parameters

ParameterTypeDescription
connectionDiscordVoiceConnectionThe voice connection to attach.

Returns

void

Remarks

The application creates the connection with joinVoiceChannel() from @discordjs/voice (join with selfDeaf: false, or the bot receives no audio). Attaching subscribes the internal audio player to the connection and registers the speaking listener on its receiver. Calling attach() again with a different connection detaches the previous one first.

May be called before initialize(); the connection is then wired during initialization.

Example

const connection = joinVoiceChannel({
  channelId: channel.id,
  guildId: guild.id,
  adapterCreator: guild.voiceAdapterCreator,
  selfDeaf: false,
});
discord.attach(connection);

configure()

configure(metadata): void;

Defined in: src/providers/io/discord/DiscordVoice.ts:865

Configure the output with the audio format the TTS will produce.

Parameters

ParameterTypeDescription
metadataAudioMetadataFormat description of the incoming TTS audio.

Returns

void

Remarks

DiscordVoice accepts linear16 PCM only (mono or stereo, any sample rate) — the provider resamples to 48 kHz and expands mono to stereo itself. Compressed formats (mp3, opus, mulaw, alaw) are rejected because decoding them server-side would require ffmpeg.

Throws

ConfigurationError if metadata.encoding is not 'linear16' — configure your TTS accordingly, e.g. new DeepgramTTS({ encoding: 'linear16', sampleRate: 24000 }).

Implementation of

AudioOutputProvider.configure


detach()

detach(): void;

Defined in: src/providers/io/discord/DiscordVoice.ts:676

Detach the current voice connection, if any.

Returns

void

Remarks

Destroys all active receive streams, removes the speaking listener, and unsubscribes the audio player from the connection. The provider can be re-attached to the same or a different connection afterwards. The connection itself is left open — destroying it is the application’s job.


dispose()

dispose(): Promise<void>;

Defined in: src/providers/io/discord/DiscordVoice.ts:590

Dispose of the provider and release all resources.

Returns

Promise<void>

Remarks

Detaches the connection (destroying receive streams and unsubscribing the player), stops playback, settles any pending flush promises, and clears all state. The instance can be re-initialized afterwards.

Implementation of

AudioOutputProvider.dispose


enqueue()

enqueue(chunk): void;

Defined in: src/providers/io/discord/DiscordVoice.ts:884

Enqueue a TTS audio chunk for the next flush().

Parameters

ParameterTypeDescription
chunkAudioChunkAudio data to queue for playback.

Returns

void

Remarks

Chunks are buffered until flush() builds a single playback resource from them. A per-chunk AudioChunk.metadata is honored as a fallback when configure() was not called.

Implementation of

AudioOutputProvider.enqueue


flush()

flush(): Promise<void>;

Defined in: src/providers/io/discord/DiscordVoice.ts:905

Play all queued audio into the channel and wait for playback to finish.

Returns

Promise<void>

Remarks

Concatenates the queued chunks, converts them to 48 kHz stereo s16le PCM (linear-interpolation resampling via resamplePcm for other rates; mono duplicated into both channels), wraps the result in a Readable played as StreamType.Raw, and resolves once the player transitions to AudioPlayerStatus.Idle. Resolves immediately when nothing is queued. A stop() (barge-in) also settles the promise, because player.stop(true) forces the Idle transition.

Throws

ConfigurationError if no format is known (neither configure() nor per-chunk metadata) or the format is not linear16.

Throws

ProviderInitializationError via initialize rules if the provider is not initialized.

Implementation of

AudioOutputProvider.flush


getMetadata()

getMetadata(): AudioMetadata;

Defined in: src/providers/io/discord/DiscordVoice.ts:839

Get the audio format metadata for captured audio.

Returns

AudioMetadata

{ encoding: 'linear16', sampleRate: 48000, channels: 1, bitDepth: 16 }

Remarks

Discord voice is Opus at 48 kHz stereo on the wire; after decoding and downmixing, the provider emits linear16 mono at 48 kHz. The pipeline uses this to auto-configure the STT provider.

Implementation of

AudioInputProvider.getMetadata


initialize()

initialize(): Promise<void>;

Defined in: src/providers/io/discord/DiscordVoice.ts:555

Initialize the provider: load peer dependencies and create the player.

Returns

Promise<void>

Remarks

Dynamically imports @discordjs/voice and prism-media, creates the shared AudioPlayer, wires its status events, and attaches the connection from DiscordVoiceConfig.connection (or one supplied earlier via attach()). Safe to call again after dispose(). If already initialized, this is a no-op.

Throws

ProviderInitializationError if @discordjs/voice or prism-media is not installed (with install instructions).

Implementation of

AudioOutputProvider.initialize


isActive()

isActive(): boolean;

Defined in: src/providers/io/discord/DiscordVoice.ts:810

Check whether input capture is active.

Returns

boolean

true when started and not paused.

Implementation of

AudioInputProvider.isActive


isPlaying()

isPlaying(): boolean;

Defined in: src/providers/io/discord/DiscordVoice.ts:953

Check whether audio is currently being played into the channel.

Returns

boolean

true between the player’s Playing and Idle transitions.

Implementation of

AudioOutputProvider.isPlaying


isReady()

isReady(): boolean;

Defined in: src/providers/io/discord/DiscordVoice.ts:617

Check whether the provider has been initialized.

Returns

boolean

true when initialize has completed and dispose has not yet been called.

Implementation of

AudioOutputProvider.isReady


onAudio()

onAudio(callback): void;

Defined in: src/providers/io/discord/DiscordVoice.ts:825

Register a callback to receive captured audio chunks.

Parameters

ParameterTypeDescription
callback(chunk) => voidFunction invoked with each AudioChunk (linear16, 48 kHz, mono) while capture is active.

Returns

void

Remarks

Only one callback can be registered at a time; subsequent calls replace the previous callback. Must be called before start().

Implementation of

AudioInputProvider.onAudio


onPlaybackEnd()

onPlaybackEnd(callback): void;

Defined in: src/providers/io/discord/DiscordVoice.ts:973

Register a callback fired when the queue fully drains.

Parameters

ParameterTypeDescription
callback() => voidInvoked when the player returns to Idle after having played audio.

Returns

void

Implementation of

AudioOutputProvider.onPlaybackEnd


onPlaybackError()

onPlaybackError(callback): void;

Defined in: src/providers/io/discord/DiscordVoice.ts:982

Register a callback fired when the player reports an error.

Parameters

ParameterTypeDescription
callback(error) => voidInvoked with the underlying error.

Returns

void

Implementation of

AudioOutputProvider.onPlaybackError


onPlaybackStart()

onPlaybackStart(callback): void;

Defined in: src/providers/io/discord/DiscordVoice.ts:963

Register a callback fired when audio delivery begins after being idle.

Parameters

ParameterTypeDescription
callback() => voidInvoked on the player’s Idle/BufferingPlaying transition.

Returns

void

Implementation of

AudioOutputProvider.onPlaybackStart


pause()

pause(): void;

Defined in: src/providers/io/discord/DiscordVoice.ts:781

Pause audio processing.

Returns

void

Remarks

Gates input emission (decoded chunks are silently dropped) — the turn-taking system calls this to mute capture while the agent speaks. If audio is currently playing, the player is paused as well. Resume with resume().

Implementation of

AudioOutputProvider.pause


resume()

resume(): void;

Defined in: src/providers/io/discord/DiscordVoice.ts:798

Resume audio processing after a pause.

Returns

void

Remarks

Re-opens the input emission gate and unpauses the player.

See

pause

Implementation of

AudioOutputProvider.resume


start()

start(): void;

Defined in: src/providers/io/discord/DiscordVoice.ts:719

Start capturing audio from users speaking in the channel.

Returns

void

Remarks

After calling start(), each speaking('start') event triggers a per-user Opus subscription and decoded chunks are delivered to the callback registered with onAudio(). Requires an attached connection to have any effect.

Implementation of

AudioInputProvider.start


stop()

stop(): void;

Defined in: src/providers/io/discord/DiscordVoice.ts:744

Stop the provider — barge-in while the agent is speaking, capture halt otherwise.

Returns

void

Remarks

A duplex provider exposes a single stop() for both role interfaces, so DiscordVoice dispatches on playback state:

  • Barge-in (output.stop()) — while audio is queued or the player is delivering, the pending output queue is cleared and player.stop(true) forces the player to Idle, settling any in-flight flush(). Capture deliberately survives: the orchestrator invokes this synchronously when a user interrupts the agent, and the interrupting speech must be transcribed.
  • Stop listening (input.stop()) — with nothing queued or playing, behaves as stopCapture().

detach() and dispose() stop both sides unconditionally.

Implementation of

AudioOutputProvider.stop


stopCapture()

stopCapture(): void;

Defined in: src/providers/io/discord/DiscordVoice.ts:701

Stop audio capture without touching playback.

Returns

void

Remarks

The input-role counterpart to the barge-in stop(): destroys all active receive streams and closes the emission gate, while the speaking listener stays attached so start() can restart capture at any time.

See

stop for the playback (barge-in) stop

Implementation of

AudioInputProvider.stopCapture


stopPlayback()

stopPlayback(): void;

Defined in: src/providers/io/discord/DiscordVoice.ts:766

Stop playback, leaving capture running.

Returns

void

Remarks

The pipeline calls this for barge-in, so the provider never has to infer which side was meant. Capture stays open deliberately: barge-in fires because someone started talking, and that speech has to reach STT.

Safe when nothing is playing — barge-in is also raised while the agent is still thinking, and there is simply no audio to drop yet.

Implementation of

AudioOutputProvider.stopPlayback

© 2026 CompositeVoice. All rights reserved.

Font size
Contrast
Motion
Transparency