Files
discord.js/docs/topics/voice.md
Amish Shah 6ab46491c8 Add internal support for @discordjs/opus to v11 (#3700)
* Add internal support for @discordjs/opus

* Remove redundant try/catch

* fix: use setBitrate method in @discordjs/opus

* chore: tidy up opus imports

* fix: correct imports for DiscordJsOpusEngine

* chore: update docs to prefer @discordjs/opus

* chore: bump prism-media to 0.0.4 to allow ffmpeg-static
2020-01-17 20:58:49 +00:00

4.1 KiB

Introduction to Voice

Voice in discord.js can be used for many things, such as music bots, recording or relaying audio.

In discord.js, you can use voice by connecting to a VoiceChannel to obtain a VoiceConnection, where you can start streaming and receiving audio.

To get started, make sure you have:

  • FFmpeg - npm install ffmpeg-binaries
  • an opus encoder, choose one from below:
    • npm install opusscript
    • npm install @discordjs/opus
  • a good network connection

The preferred opus engine is @discordjs/opus, as it performs significantly better than opusscript. When both are available, discord.js will automatically choose @discordjs/opus. Using opusscript is only recommended for development environments where @discordjs/opus is tough to get working. For production bots, using @discordjs/opus should be considered a necessity, especially if they're going to be running on multiple servers.

Joining a voice channel

The example below reacts to a message and joins the sender's voice channel, catching any errors. This is important as it allows us to obtain a VoiceConnection that we can start to stream audio with.

const Discord = require('discord.js');
const client = new Discord.Client();

client.login('token here');

client.on('message', message => {
  // Voice only works in guilds, if the message does not come from a guild,
  // we ignore it
  if (!message.guild) return;

  if (message.content === '/join') {
    // Only try to join the sender's voice channel if they are in one themselves
    if (message.member.voiceChannel) {
      message.member.voiceChannel.join()
        .then(connection => { // Connection is an instance of VoiceConnection
          message.reply('I have successfully connected to the channel!');
        })
        .catch(console.log);
    } else {
      message.reply('You need to join a voice channel first!');
    }
  }
});

Streaming to a Voice Channel

In the previous example, we looked at how to join a voice channel in order to obtain a VoiceConnection. Now that we have obtained a voice connection, we can start streaming audio to it. The following example shows how to stream an mp3 file:

Playing a file:

// To play a file, we need to give an absolute path to it
const dispatcher = connection.playFile('C:/Users/Discord/Desktop/myfile.mp3');

Your file doesn't have to be just an mp3; ffmpeg can convert videos and audios of many formats.

The dispatcher variable is an instance of a StreamDispatcher, which manages streaming a specific resource to a voice channel. We can do many things with the dispatcher, such as finding out when the stream ends or changing the volume:

dispatcher.on('end', () => {
  // The song has finished
});

dispatcher.on('error', e => {
  // Catch any errors that may arise
  console.log(e);
});

dispatcher.setVolume(0.5); // Set the volume to 50%
dispatcher.setVolume(1); // Set the volume back to 100%

console.log(dispatcher.time); // The time in milliseconds that the stream dispatcher has been playing for

dispatcher.pause(); // Pause the stream
dispatcher.resume(); // Carry on playing

dispatcher.end(); // End the dispatcher, emits 'end' event

If you have an existing ReadableStream, this can also be used:

Playing a ReadableStream:

connection.playStream(myReadableStream);

// If you don't want to use absolute paths, you can use
// fs.createReadStream to circumvent it

const fs = require('fs');
const stream = fs.createReadStream('./test.mp3');
connection.playStream(stream);

It's important to note that creating a readable stream to a file is less efficient than simply using connection.playFile().

Playing anything else:

For anything else, such as a URL to a file, you can use connection.playArbitraryInput(). You should consult the ffmpeg protocol documentation to see what you can use this for.

// Play an mp3 from a URL
connection.playArbitraryInput('http://mysite.com/sound.mp3');

Again, playing a file from a URL like this is more performant than creating a ReadableStream to the file.

Advanced Topics

soon™️