View Source Nostrum.Voice (Nostrum v0.5.0)
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
:rawastypeforNostrum.Voice.play/4- Provide the complete list of opus frames as the input
- Use
:raw_sastypeforNostrum.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 ofinputdetermined bytypeparameter.type- Type of input (defaults to:url).:urlInput will be any url thatffmpegcan read.:pipeInput will be data that is piped to stdin offfmpeg.:ytdlInput will be url foryoutube-dl, which gets automatically piped toffmpeg.:streamInput will be livestream url forstreamlink, which gets automatically piped toffmpeg.:rawInput will be an enumarable of raw opus frames. This bypassesffmpegand all options.:raw_sSame as:rawbut input must be stateful, i.e. callingEnum.take/2on 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)