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
35
# 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.

Returns:

  • (Integer)

    the volume used as a filter to ffmpeg/avconv.

See Also:



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.

Returns:

  • (true, false)

    whether avconv should be used instead of ffmpeg.



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.

Parameters:

  • buf (String)

    An unencoded PCM (s16le) buffer.

  • mult (Float)

    The volume multiplier, 1 for same volume.

Returns:

  • (String)

    The buffer with adjusted volume, s16le again



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

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

Parameters:

  • value (Integer)

    The new bitrate to use, in bits per second (so 64000 if you want 64 kbps)



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

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

#encode(buffer) ⇒ String

Encodes the given buffer using opus.

Parameters:

  • buffer (String)

    An unencoded PCM (s16le) buffer.

Returns:

  • (String)

    A buffer encoded using opus.



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

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.

Parameters:

  • file (String)

    The path or URL to encode.

  • options (String) (defaults to: '')

    ffmpeg options to pass after the -i flag

Returns:

  • (IO)

    the audio, encoded as s16le PCM



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

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.

Parameters:

  • io (IO)

    The stream to encode.

  • options (String) (defaults to: '')

    ffmpeg options to pass after the -i flag

Returns:

  • (IO)

    the audio, encoded as s16le PCM



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

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