or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

audio-playback.mdaudio-reception.mddave-encryption.mdindex.mdnetworking.mdutilities.mdvoice-connections.md
tile.json

utilities.mddocs/

Utility Functions

Helper utilities for state management, dependency reporting, stream probing, gateway adapter integration, and common operations that support the core voice functionality.

Capabilities

State Management

Utility for waiting for specific states in voice connections and audio players.

/**
 * Wait for a voice connection to enter a specific state
 * @param target - VoiceConnection to monitor
 * @param status - Status to wait for
 * @param timeoutOrSignal - Timeout in ms or AbortSignal
 * @returns Promise that resolves when target reaches the desired state
 */
function entersState(
  target: VoiceConnection,
  status: VoiceConnectionStatus,
  timeoutOrSignal: AbortSignal | number
): Promise<VoiceConnection>;

/**
 * Wait for an audio player to enter a specific state
 * @param target - AudioPlayer to monitor  
 * @param status - Status to wait for
 * @param timeoutOrSignal - Timeout in ms or AbortSignal
 * @returns Promise that resolves when target reaches the desired state
 */
function entersState(
  target: AudioPlayer,
  status: AudioPlayerStatus,
  timeoutOrSignal: AbortSignal | number
): Promise<AudioPlayer>;

/**
 * Generic state waiting function
 * @param target - Object with state property to monitor
 * @param status - Status to wait for
 * @param timeoutOrSignal - Timeout in ms or AbortSignal
 * @returns Promise that resolves when target reaches the desired state
 */
function entersState<T extends AudioPlayer | VoiceConnection>(
  target: T,
  status: AudioPlayerStatus | VoiceConnectionStatus,
  timeoutOrSignal: AbortSignal | number
): Promise<T>;

Usage Examples:

import { 
  entersState, 
  VoiceConnectionStatus, 
  AudioPlayerStatus,
  joinVoiceChannel,
  createAudioPlayer 
} from "@discordjs/voice";

// Wait for voice connection to be ready
const connection = joinVoiceChannel({
  channelId: "123456789012345678",
  guildId: "987654321098765432",
  adapterCreator: guild.voiceAdapterCreator,
});

try {
  await entersState(connection, VoiceConnectionStatus.Ready, 30_000);
  console.log("Connection is ready!");
} catch (error) {
  console.error("Connection failed to become ready within 30 seconds");
}

// Wait for audio player to start playing
const player = createAudioPlayer();
const resource = createAudioResource("./music.mp3");

player.play(resource);

try {
  await entersState(player, AudioPlayerStatus.Playing, 5_000);
  console.log("Audio started playing!");
} catch (error) {
  console.error("Audio failed to start within 5 seconds");
}

// Using AbortSignal for cancellation
const controller = new AbortController();

// Cancel after 15 seconds
setTimeout(() => controller.abort(), 15_000);

try {
  await entersState(connection, VoiceConnectionStatus.Ready, controller.signal);
  console.log("Connection ready before timeout");
} catch (error) {
  if (error.name === "AbortError") {
    console.log("State waiting was aborted");
  } else {
    console.error("State waiting failed:", error);
  }
}

Dependency Reporting

Generate comprehensive reports of audio processing dependencies and their versions.

/**
 * Generate a report of dependencies used by @discordjs/voice
 * Useful for debugging audio processing capabilities and issues
 * @returns String report containing version information and capabilities
 */
function generateDependencyReport(): string;

Usage Example:

import { generateDependencyReport } from "@discordjs/voice";

// Generate and log dependency report
const report = generateDependencyReport();
console.log("@discordjs/voice Dependency Report:");
console.log(report);

// Example output:
// @discordjs/voice: 0.19.0
// prism-media: 1.3.5
// @discordjs/opus: 0.9.0 (optional)
// @noble/ciphers: 1.3.0 (for DAVE encryption)
// Node.js: v22.12.0
// FFmpeg: 4.4.2 (detected)
// Available Opus libraries: @discordjs/opus, node-opus
// Available encryption libraries: @noble/ciphers
// Encryption modes: aead_aes256_gcm_rtpsize, aead_xchacha20_poly1305_rtpsize

Stream Probing

Probe audio streams to determine format and Discord compatibility.

/**
 * Attempt to probe a readable stream to determine if it can be demuxed
 * @param stream - Readable stream to probe
 * @param probeSize - Number of bytes to read for probing (default: 1024)
 * @param validator - Function to validate Opus head (default: validateDiscordOpusHead)
 * @returns Promise resolving to probe information
 */
function demuxProbe(
  stream: Readable,
  probeSize?: number,
  validator?: (opusHead: Buffer) => boolean
): Promise<ProbeInfo>;

/**
 * Validate if an Opus head is suitable for Discord voice channels
 * @param opusHead - Opus header buffer to validate
 * @returns true if suitable for Discord (2 channels, 48kHz), false otherwise
 */
function validateDiscordOpusHead(opusHead: Buffer): boolean;

interface ProbeInfo {
  /** Processed readable stream to use (may differ from input) */
  stream: Readable;
  /** Recommended stream type for this audio */
  type: StreamType;
}

Usage Examples:

import { 
  demuxProbe, 
  validateDiscordOpusHead, 
  createAudioResource,
  StreamType 
} from "@discordjs/voice";
import { createReadStream } from "fs";

// Probe unknown audio format
const inputStream = createReadStream("unknown-audio-file.audio");

try {
  const probeInfo = await demuxProbe(inputStream);
  console.log("Detected stream type:", probeInfo.type);
  
  // Use probed stream
  const resource = createAudioResource(probeInfo.stream, {
    inputType: probeInfo.type,
  });
  
  player.play(resource);
} catch (error) {
  console.error("Failed to probe stream:", error);
  
  // Fallback to arbitrary type
  const resource = createAudioResource(inputStream, {
    inputType: StreamType.Arbitrary,
  });
}

// Custom Opus validation
const customValidator = (opusHead) => {
  const channels = opusHead.readUInt8(9);
  const sampleRate = opusHead.readUInt32LE(12);
  
  console.log(`Opus: ${channels} channels, ${sampleRate}Hz`);
  
  // Discord requirements: 2 channels, 48kHz
  return channels === 2 && sampleRate === 48000;
};

const probeInfo = await demuxProbe(stream, 2048, customValidator);

Gateway Adapter Integration

Types and interfaces for integrating with Discord gateway adapters.

/**
 * Function type for creating Discord gateway adapters
 * @param methods - Methods provided by the voice library
 * @returns Methods that the adapter implementer provides
 */
type DiscordGatewayAdapterCreator = (
  methods: DiscordGatewayAdapterLibraryMethods
) => DiscordGatewayAdapterImplementerMethods;

/**
 * Methods provided by @discordjs/voice to adapter implementations
 */
interface DiscordGatewayAdapterLibraryMethods {
  /** Call when adapter can no longer be used */
  destroy(): void;
  
  /**
   * Call when VOICE_SERVER_UPDATE is received
   * @param data - Inner data of VOICE_SERVER_UPDATE payload
   */
  onVoiceServerUpdate(data: GatewayVoiceServerUpdateDispatchData): void;
  
  /**
   * Call when VOICE_STATE_UPDATE is received
   * @param data - Inner data of VOICE_STATE_UPDATE payload
   */
  onVoiceStateUpdate(data: GatewayVoiceStateUpdateDispatchData): void;
}

/**
 * Methods that adapter implementers must provide
 */
interface DiscordGatewayAdapterImplementerMethods {
  /** Called when adapter can safely be destroyed */
  destroy(): void;
  
  /**
   * Send payload to main Discord gateway connection
   * @param payload - Payload to send to gateway
   * @returns false if payload definitely failed to send
   */
  sendPayload(payload: any): boolean;
}

Usage Example:

import { DiscordGatewayAdapterCreator } from "@discordjs/voice";

// Example adapter implementation for discord.js
function createDiscordJSAdapter(guild) {
  return (methods) => {
    // Store library methods
    const libraryMethods = methods;
    
    // Listen for voice events from guild
    guild.client.on('voiceServerUpdate', (update) => {
      if (update.guild?.id === guild.id) {
        libraryMethods.onVoiceServerUpdate(update);
      }
    });
    
    guild.client.on('voiceStateUpdate', (oldState, newState) => {
      if (newState.guild?.id === guild.id && newState.member?.id === guild.client.user?.id) {
        libraryMethods.onVoiceStateUpdate(newState);
      }
    });
    
    // Return implementer methods
    return {
      sendPayload: (payload) => {
        try {
          guild.shard.send(payload);
          return true;
        } catch (error) {
          console.error('Failed to send payload:', error);
          return false;
        }
      },
      destroy: () => {
        // Cleanup listeners if needed
      },
    };
  };
}

// Use the adapter
const connection = joinVoiceChannel({
  channelId: "123456789012345678",
  guildId: "987654321098765432", 
  adapterCreator: createDiscordJSAdapter(guild),
});

Abort Signal Utility

Internal utility for creating abort signals with timeouts.

/**
 * Create an AbortController and signal that aborts after specified time
 * @param delay - Delay in milliseconds before aborting
 * @returns Tuple of [AbortController, AbortSignal]
 */
function abortAfter(delay: number): [AbortController, AbortSignal];

Usage Example:

import { abortAfter } from "@discordjs/voice";

// Create timeout signal
const [controller, signal] = abortAfter(10_000);

// Use with entersState
try {
  await entersState(connection, VoiceConnectionStatus.Ready, signal);
} catch (error) {
  if (error.name === "AbortError") {
    console.log("Operation timed out after 10 seconds");
  }
}

// Manual cancellation
setTimeout(() => {
  controller.abort(); // Cancel early if needed
}, 5_000);

Data Store Utilities

Internal utilities for managing voice connection data and payloads.

/**
 * Create a voice state update payload for the Discord gateway
 * @param config - Join configuration
 * @returns Gateway payload object
 */
function createJoinVoiceChannelPayload(config: JoinConfig): any;

interface JoinConfig {
  /** Channel ID to join (null to leave) */
  channelId: string | null;
  /** Connection group name */
  group: string;
  /** Guild ID */
  guildId: string;
  /** Whether to join deafened */
  selfDeaf: boolean;
  /** Whether to join muted */
  selfMute: boolean;
}

Advanced Utility Usage

Custom State Monitoring

Create custom state monitoring utilities:

import { entersState, VoiceConnectionStatus } from "@discordjs/voice";

async function waitForMultipleStates(targets, status, timeout = 30_000) {
  const promises = targets.map(target => 
    entersState(target, status, timeout)
  );
  
  try {
    const results = await Promise.allSettled(promises);
    const successful = results.filter(r => r.status === 'fulfilled');
    const failed = results.filter(r => r.status === 'rejected');
    
    console.log(`${successful.length}/${targets.length} connections reached ${status}`);
    return { successful: successful.map(r => r.value), failed };
  } catch (error) {
    console.error("Error waiting for states:", error);
    throw error;
  }
}

// Usage
const connections = [connection1, connection2, connection3];
const { successful, failed } = await waitForMultipleStates(
  connections,
  VoiceConnectionStatus.Ready
);

Stream Format Detection

Combine probing with resource creation:

import { demuxProbe, createAudioResource, StreamType } from "@discordjs/voice";

async function createSmartAudioResource(input, options = {}) {
  if (typeof input === 'string' || options.inputType) {
    // File path or type hint provided
    return createAudioResource(input, options);
  }
  
  try {
    // Probe stream to detect format
    const probeInfo = await demuxProbe(input);
    return createAudioResource(probeInfo.stream, {
      ...options,
      inputType: probeInfo.type,
    });
  } catch (error) {
    console.warn("Stream probing failed, using arbitrary type:", error.message);
    return createAudioResource(input, {
      ...options,
      inputType: StreamType.Arbitrary,
    });
  }
}

// Usage
const resource = await createSmartAudioResource(someStream, {
  metadata: { title: "Unknown Format Audio" },
  inlineVolume: true,
});

Debugging and Diagnostics

Use utilities for debugging voice issues:

import { generateDependencyReport, validateDiscordOpusHead } from "@discordjs/voice";

function diagnoseVoiceIssues(connection, player) {
  console.log("=== Voice Diagnostics ===");
  
  // Dependency report
  console.log("\nDependency Report:");
  console.log(generateDependencyReport());
  
  // Connection state
  console.log("\nConnection State:");
  console.log(`Status: ${connection.state.status}`);
  if (connection.state.status === 'ready') {
    console.log(`Networking: ${connection.state.networking.state.code}`);
  }
  
  // Player state
  console.log("\nPlayer State:");
  console.log(`Status: ${player.state.status}`);
  if (player.state.status === 'playing' || player.state.status === 'paused') {
    console.log(`Playback Duration: ${player.state.playbackDuration}ms`);
    console.log(`Resource: ${player.state.resource.constructor.name}`);
  }
  
  console.log("=== End Diagnostics ===");
}

// Usage
diagnoseVoiceIssues(connection, player);