Skip to main content
The AudioSendHandler interface is used to provide audio data to JDA for transmission to Discord voice channels.
Audio transmission requires the DAVE (Discord Audio Video Encryption) protocol. See the Discord documentation for details.

Audio Format

JDA expects audio in the following format (unless using pre-encoded Opus):
  • Sample Rate: 48 KHz
  • Bit Depth: 16-bit
  • Channels: Stereo (2 channels)
  • Encoding: Signed BigEndian PCM
This is defined by the constant:
AudioFormat INPUT_FORMAT = new AudioFormat(48000f, 16, 2, true, true);

Methods

canProvide

Indicates whether audio data is available to send.
boolean canProvide()
returns
boolean
true if audio data is ready to be provided
Description:
  • JDA calls this method before attempting to retrieve audio data
  • Return true to indicate audio is ready, false otherwise
  • This is checked each time JDA attempts to send audio
  • Can be used to dynamically start/stop audio transmission
Example:
@Override
public boolean canProvide() {
    // Return true if audio queue has data
    return !audioQueue.isEmpty();
}

provide20MsAudio

Provides 20 milliseconds of audio data.
ByteBuffer provide20MsAudio()
returns
ByteBuffer
A ByteBuffer containing 20ms of audio data, or null if no data available
Requirements:
  • Must return an array-backed ByteBuffer
  • Use ByteBuffer.allocate(int) or ByteBuffer.wrap(byte[])
  • Must contain exactly 20 milliseconds of audio
  • Audio format: 48KHz 16-bit stereo signed BigEndian PCM (unless isOpus() returns true)
For best performance, load audio data beforehand or in parallel. Loading from disk when this method is called may cause latency issues.
Example (PCM audio):
@Override
public ByteBuffer provide20MsAudio() {
    if (audioQueue.isEmpty()) {
        return null;
    }
    
    byte[] audioData = audioQueue.poll(); // 20ms of PCM audio
    return ByteBuffer.wrap(audioData);
}
Example (pre-encoded Opus):
@Override
public ByteBuffer provide20MsAudio() {
    if (opusPackets.isEmpty()) {
        return null;
    }
    
    byte[] opusPacket = opusPackets.poll(); // Pre-encoded Opus packet
    return ByteBuffer.wrap(opusPacket);
}

@Override
public boolean isOpus() {
    return true; // Indicate we're providing Opus
}

isOpus

Indicates whether the audio data is pre-encoded in Opus format.
default boolean isOpus() {
    return false;
}
returns
boolean
true if providing pre-encoded Opus audio, false for PCM (default)
Description:
  • Return true if provide20MsAudio() returns pre-encoded Opus packets
  • Return false (default) if providing raw PCM audio
  • When true, JDA will not encode the audio and will send it as-is to Discord

Implementation Example

Here’s a complete example of an AudioSendHandler:
public class MyAudioSendHandler implements AudioSendHandler {
    private final Queue<byte[]> audioQueue = new ConcurrentLinkedQueue<>();
    
    // Method to add audio data to the queue
    public void queueAudio(byte[] audioData) {
        audioQueue.offer(audioData);
    }
    
    @Override
    public boolean canProvide() {
        return !audioQueue.isEmpty();
    }
    
    @Override
    public ByteBuffer provide20MsAudio() {
        byte[] data = audioQueue.poll();
        if (data == null) {
            return null;
        }
        return ByteBuffer.wrap(data);
    }
    
    @Override
    public boolean isOpus() {
        return false; // Providing PCM audio
    }
}

Using with AudioManager

// Create and register the handler
MyAudioSendHandler sendHandler = new MyAudioSendHandler();
AudioManager audioManager = guild.getAudioManager();
audioManager.setSendingHandler(sendHandler);

// Connect to a voice channel
VoiceChannel channel = guild.getVoiceChannelById("123456789");
audioManager.openAudioConnection(channel);

// Queue audio data (in 20ms chunks)
sendHandler.queueAudio(audio20msData);
JDA recommends using LavaPlayer as your AudioSendHandler implementation. It provides a complete audio playback system with support for various audio sources.See the LavaPlayer JDA demo for integration examples.

Audio Data Size

For 20ms of PCM audio at 48KHz 16-bit stereo:
  • Sample rate: 48,000 samples per second
  • Duration: 20ms = 0.02 seconds
  • Samples for 20ms: 48,000 × 0.02 = 960 samples
  • Channels: 2 (stereo)
  • Bytes per sample: 2 (16-bit)
  • Total bytes: 960 × 2 × 2 = 3,840 bytes

Build docs developers (and LLMs) love