Nostrum.Voice (Nostrum v0.5.0-rc1) View Source
Interface for playing audio through Discord's voice channels.
Using Discord Voice Channels
To play sound in Discord with Nostrum, you'll need ffmpeg
to be installed.
If you don't have the executable ffmpeg
in the path, the absolute path may
be configured through config keys :nostrum, :ffmpeg
. If you don't want to use
ffmpeg, read on to the next section.
A bot may be connected to at most one voice channel per guild. For this reason, most of the functions in this module take a guild id, and the resulting action will be performed in the given guild's voice channel that the bot is connected to.
The primary Discord gateway responsible for all text based communication relies on one websocket connection per shard, where small bots typically only have one shard. The Discord voice gateways work by establishing a websocket connection per guild/channel. After some handshaking on this connection, audio data can be sent over UDP/RTP. Behind the scenes the voice websocket connections are implemented nearly the same way the main shard websocket connections are, and require no developer intervention.
Voice Without FFmpeg
If you wish to BYOE (Bring Your Own Encoder), there are a few options.
- Use
:raw
astype
forNostrum.Voice.play/4
- Provide the complete list of opus frames as the input
- Use
:raw_s
astype
forNostrum.Voice.play/4
- Provide a stateful enumerable of opus frames as input (think GenServer wrapped in
Stream.unfold/2
)
- Provide a stateful enumerable of opus frames as input (think GenServer wrapped in
- Use lower level functions to send opus frames at your leisure
- Send packets on your own time using
Nostrum.Voice.send_frames/2
- Send packets on your own time using
Link to this section Summary
Functions
Returns a specification to start this module under a supervisor.
Low-level. Manually connect to voice websockets gateway.
Gets the id of the voice channel that the bot is connected to.
Joins or moves the bot to a voice channel.
Leaves the voice channel of the given guild id.
Listen for incoming voice RTP packets.
Pauses the current sound being played in a voice channel.
Plays sound in the voice channel the bot is in.
Checks if the bot is playing sound in a voice channel.
Checks if the connection is up and ready to play audio.
Resumes playing the current paused sound in a voice channel.
Low-level. Send pre-encoded audio packets directly.
Low-level. Set speaking flag in voice channel.
Stops the current sound being played in a voice channel.
Link to this section Functions
Returns a specification to start this module under a supervisor.
See Supervisor
.
Specs
connect_to_gateway(Nostrum.Struct.Guild.id()) :: :ok | {:error, String.t()}
Low-level. Manually connect to voice websockets gateway.
This function should only be called if config option :voice_auto_connect
is set to false
.
By default Nostrum will automatically create a voice gateway when joining a channel.
Specs
get_channel_id(Nostrum.Struct.Guild.id()) :: Nostrum.Struct.Channel.id()
Gets the id of the voice channel that the bot is connected to.
Parameters
guild_id
- ID of guild that the resultant channel belongs to.
Returns the channel_id
for the channel the bot is connected to, otherwise nil
.
Examples
iex> Nostrum.Voice.join_channel(123456789, 420691337)
iex> Nostrum.Voice.get_channel(123456789)
420691337
iex> Nostrum.Voice.leave_channel(123456789)
iex> Nostrum.Voice.get_channel(123456789)
nil
join_channel(guild_id, channel_id, self_mute \\ false, self_deaf \\ false)
View SourceSpecs
join_channel( Nostrum.Struct.Guild.id(), Nostrum.Struct.Channel.id(), boolean(), boolean() ) :: no_return() | :ok
Joins or moves the bot to a voice channel.
This function is equivalent to Nostrum.Api.update_voice_state/4
.
Specs
leave_channel(Nostrum.Struct.Guild.id()) :: no_return() | :ok
Leaves the voice channel of the given guild id.
This function is equivalent to calling Nostrum.Api.update_voice_state(guild_id, nil)
.
Specs
listen(Nostrum.Struct.Guild.id(), pos_integer()) :: [{binary(), binary()}] | {:error, String.t()}
Listen for incoming voice RTP packets.
Parameters
guild_id
- ID of guild that the bot is listening to.num_packets
- Number of packets to wait for.
Returns a list of 2-element tuples in the form {rtp_header, opus_packet}
.
This function will block until the specified number of packets is received.
Specs
pause(Nostrum.Struct.Guild.id()) :: :ok | {:error, String.t()}
Pauses the current sound being played in a voice channel.
The bot must be connected to a voice channel in the guild specified.
Parameters
guild_id
- ID of guild whose voice channel the sound will be paused in.
Returns {:error, reason}
if unable to pause or no sound is playing, else :ok
.
This function is similar to stop/1
, except that the sound may be
resumed after being paused.
Examples
iex> Nostrum.Voice.join_channel(123456789, 420691337)
iex> Nostrum.Voice.play(123456789, "~/files/twelve_hour_loop_of_waterfall_sounds.mp3")
iex> Nostrum.Voice.pause(123456789)
Specs
play( Nostrum.Struct.Guild.id(), String.t() | binary() | iodata() | Enum.t(), :url | :pipe | :ytdl | :stream | :raw | :raw_s, keyword() ) :: :ok | {:error, String.t()}
Plays sound in the voice channel the bot is in.
The bot must be connected to a voice channel in the guild specified.
Parameters
guild_id
- ID of guild whose voice channel the sound will be played in.input
- Audio to be played. Type ofinput
determined bytype
parameter.type
- Type of input (defaults to:url
).:url
Input will be any url thatffmpeg
can read.:pipe
Input will be data that is piped to stdin offfmpeg
.:ytdl
Input will be url foryoutube-dl
, which gets automatically piped toffmpeg
.:stream
Input will be livestream url forstreamlink
, which gets automatically piped toffmpeg
.:raw
Input will be an enumarable of raw opus frames. This bypassesffmpeg
and all options.:raw_s
Same as:raw
but input must be stateful, i.e. callingEnum.take/2
on input is not idempotent.
options
- See options section below.
Returns {:error, reason}
if unable to play or a sound is playing, else :ok
.
Options
:start_pos
(string) - The start position of the audio to be played. Defaults to beginning.:duration
(string) - The duration to of the audio to be played . Defaults to entire duration.:realtime
(boolean) - Make ffmpeg process the input in realtime instead of as fast as possible. Defaults to true.:volume
(number) - The output volume of the audio. Default volume is 1.0.:filter
(string) - Filter(s) to be applied to the audio. No filters applied by default.
The values of :start_pos
and :duration
can be any time duration that ffmpeg can read.
The :filter
can be used multiple times in a single call (see examples).
The values of :filter
can be any audio filters that ffmpeg can read.
Filters will be applied in order and can be as complex as you want. The world is your oyster!
Note that using the :volume
option is shortcut for the "volume" filter, and will be added to the end of the filter chain, acting as a master volume.
Volume values between 0.0
and 1.0
act as standard oparating range where 0
is off and 1
is max.
Values greater than 1.0
will add saturation and distortion to the audio.
Negative values act the same as their position but reverse the polarity of the waveform.
Having all the ffmpeg audio filters available is extremely powerful so it may be worth learning some of them for your use cases.
If you use any filters to increase the playback speed of your audio, it's recommended to set the :realtime
option to false
because realtime processing is relative to the original playback speed.
Examples
iex> Nostrum.Voice.join_channel(123456789, 420691337)
iex> Nostrum.Voice.play(123456789, "~/music/FavoriteSong.mp3", :url)
iex> Nostrum.Voice.play(123456789, "~/music/NotFavoriteButStillGoodSong.mp3", :url, volume: 0.5)
iex> Nostrum.Voice.play(123456789, "~/music/ThisWillBeHeavilyDistorted.mp3", :url, volume: 1000)
iex> Nostrum.Voice.join_channel(123456789, 420691337)
iex> raw_data = File.read!("~/music/sound_effect.wav")
iex> Nostrum.Voice.play(123456789, raw_data, :pipe)
iex> Nostrum.Voice.join_channel(123456789, 420691337)
iex> Nostrum.Voice.play(123456789, "https://www.youtube.com/watch?v=b4RJ-QGOtw4", :ytdl,
...> realtime: true, start_pos: "0:17", duration: "30")
iex> Nostrum.Voice.play(123456789, "https://www.youtube.com/watch?v=0ngcL_5ekXo", :ytdl,
...> filter: "lowpass=f=1200", filter: "highpass=f=300", filter: "asetrate=44100*0.5")
iex> Nostrum.Voice.join_channel(123456789, 420691337)
iex> Nostrum.Voice.play(123456789, "https://www.twitch.tv/pestily", :stream)
iex> Nostrum.Voice.play(123456789, "https://youtu.be/LN4r-K8ZP5Q", :stream)
Specs
playing?(Nostrum.Struct.Guild.id()) :: boolean()
Checks if the bot is playing sound in a voice channel.
Parameters
guild_id
- ID of guild to check if audio being played.
Returns true
if the bot is currently being played in a voice channel, otherwise false
.
Examples
iex> Nostrum.Voice.join_channel(123456789, 420691337)
iex> Nostrum.Voice.play(123456789, "https://a-real-site.biz/RickRoll.m4a")
iex> Nostrum.Voice.playing?(123456789)
true
iex> Nostrum.Voice.pause(123456789)
iex> Nostrum.Voice.playing?(123456789)
false
Specs
ready?(Nostrum.Struct.Guild.id()) :: boolean()
Checks if the connection is up and ready to play audio.
Parameters
guild_id
- ID of guild to check if voice connection is up.
Returns true
if the bot is connected to a voice channel, otherwise false
.
This function does not check if audio is already playing. For that, use playing?/1
.
Examples
iex> Nostrum.Voice.join_channel(123456789, 420691337)
iex> Nostrum.Voice.ready?(123456789)
true
iex> Nostrum.Voice.leave_channel(123456789)
iex> Nostrum.Voice.ready?(123456789)
false
Specs
resume(Nostrum.Struct.Guild.id()) :: :ok | {:error, String.t()}
Resumes playing the current paused sound in a voice channel.
The bot must be connected to a voice channel in the guild specified.
Parameters
guild_id
- ID of guild whose voice channel the sound will be resumed in.
Returns {:error, reason}
if unable to resume or no sound has been paused, otherwise returns :ok
.
This function is used to resume a sound that had previously been paused.
Examples
iex> Nostrum.Voice.join_channel(123456789, 420691337)
iex> Nostrum.Voice.play(123456789, "~/stuff/Toto - Africa (Bass Boosted)")
iex> Nostrum.Voice.pause(123456789)
iex> Nostrum.Voice.resume(123456789)
Specs
send_frames(Nostrum.Struct.Guild.id(), [binary()]) :: :ok | {:error, String.t()}
Low-level. Send pre-encoded audio packets directly.
Speaking should be set to true via Nostrum.Voice.set_is_speaking/2
before sending frames.
Opus frames will be encrypted and prefixed with the appropriate RTP header and sent immediately.
The length of frames
depends on how often you wish to send a sequence of frames.
A single frame contains 20ms of audio. Sending more than 50 frames (1 second of audio)
in a single function call may result in inconsistent playback rates.
Nostrum.Voice.playing?/1
will not return accurate values when using send_frames/2
instead of Nostrum.Voice.play/4
Specs
set_is_speaking(Nostrum.Struct.Guild.id(), boolean()) :: :ok
Low-level. Set speaking flag in voice channel.
This function does not need to be called unless you are sending audio frames
directly using Nostrum.Voice.send_frames/2
.
Specs
stop(Nostrum.Struct.Guild.id()) :: :ok | {:error, String.t()}
Stops the current sound being played in a voice channel.
The bot must be connected to a voice channel in the guild specified.
Parameters
guild_id
- ID of guild whose voice channel the sound will be stopped in.
Returns {:error, reason}
if unable to stop or no sound is playing, else :ok
.
If a sound has finished playing, this function does not need to be called to start playing another sound.
Examples
iex> Nostrum.Voice.join_channel(123456789, 420691337)
iex> Nostrum.Voice.play(123456789, "http://brandthill.com/files/weird_dubstep_noises.mp3")
iex> Nostrum.Voice.stop(123456789)