Class: Discordrb::Voice::Encoder

Inherits:
Object
  • Object
show all
Defined in:
lib/discordrb/voice/encoder.rb

Overview

This class conveniently abstracts opus and ffmpeg/avconv, for easy implementation of voice sending. It's not very useful for most users, but I guess it can be useful sometimes.

Constant Summary collapse

OPUS_SILENCE =

One frame of complete silence Opus encoded

[0xF8, 0xFF, 0xFE].pack('C*').freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeEncoder

Create a new encoder

Raises:

  • (LoadError)

26
27
28
29
30
31
32
33
34
# File 'lib/discordrb/voice/encoder.rb', line 26

def initialize
  sample_rate = 48_000
  frame_size = 960
  channels = 2
  @filter_volume = 1

  raise LoadError, 'Opus unavailable - voice not supported! Please install opus for voice support to work.' unless OPUS_AVAILABLE
  @opus = Opus::Encoder.new(sample_rate, frame_size, channels)
end

Instance Attribute Details

#filter_volumeInteger

Returns the volume used as a filter to ffmpeg/avconv.


23
24
25
# File 'lib/discordrb/voice/encoder.rb', line 23

def filter_volume
  @filter_volume
end

#use_avconvtrue, false

Whether or not avconv should be used instead of ffmpeg. If possible, it is recommended to use ffmpeg instead, as it is better supported.


19
20
21
# File 'lib/discordrb/voice/encoder.rb', line 19

def use_avconv
  @use_avconv
end

Instance Method Details

#adjust_volume(buf, mult) ⇒ String

Adjusts the volume of a given buffer of s16le PCM data.


56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/discordrb/voice/encoder.rb', line 56

def adjust_volume(buf, mult)
  # We don't need to adjust anything if the buf is nil so just return in that case
  return unless buf

  # buf is s16le so use 's<' for signed, 16 bit, LE
  result = buf.unpack('s<*').map do |sample|
    sample *= mult

    # clamp to s16 range
    [32_767, [-32_768, sample].max].min
  end

  # After modification, make it s16le again
  result.pack('s<*')
end

#bitrate=(value) ⇒ Object

Set the opus encoding bitrate


38
39
40
# File 'lib/discordrb/voice/encoder.rb', line 38

def bitrate=(value)
  @opus.bitrate = value
end

#encode(buffer) ⇒ String

Encodes the given buffer using opus.


45
46
47
# File 'lib/discordrb/voice/encoder.rb', line 45

def encode(buffer)
  @opus.encode(buffer, 1920)
end

#encode_file(file, options = '') ⇒ IO

Encodes a given file (or rather, decodes it) using ffmpeg. This accepts pretty much any format, even videos with an audio track. For a list of supported formats, see https://ffmpeg.org/general.html#Audio-Codecs. It even accepts URLs, though encoding them is pretty slow - I recommend to make a stream of it and then use #encode_io instead.


78
79
80
81
# File 'lib/discordrb/voice/encoder.rb', line 78

def encode_file(file, options = '')
  command = "#{ffmpeg_command} -loglevel 0 -i \"#{file}\" #{options} -f s16le -ar 48000 -ac 2 #{filter_volume_argument} pipe:1"
  IO.popen(command)
end

#encode_io(io, options = '') ⇒ IO

Encodes an arbitrary IO audio stream using ffmpeg. Accepts pretty much any media format, even videos with audio tracks. For a list of supported audio formats, see https://ffmpeg.org/general.html#Audio-Codecs.


88
89
90
91
92
93
# File 'lib/discordrb/voice/encoder.rb', line 88

def encode_io(io, options = '')
  ret_io, writer = IO.pipe
  command = "#{ffmpeg_command} -loglevel 0 -i - #{options} -f s16le -ar 48000 -ac 2 #{filter_volume_argument} pipe:1"
  spawn(command, in: io, out: writer)
  ret_io
end