mumble-ruby 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e46d3df87918f5012997b654c5f47a78ed4e19e3
4
- data.tar.gz: 345f9359d29321c10a4a69cceec83c9468d1d8af
3
+ metadata.gz: e8dc2810d57d5afb6c1f6c7ad838e296c0f5b074
4
+ data.tar.gz: 9239fee6276e50cbbad6fdc773556d5abbb4ead4
5
5
  SHA512:
6
- metadata.gz: c3f904facc7c604f718701924fa1b5809e1268eb442b55906800efcfb78c13dea30c324bc3ff432435ac2343c4e7d7a54d573cd70e419538c3e72ec35e1abf5e
7
- data.tar.gz: 0628cfddb4b8e04c2bb34f18e9c17de8a29d151c7371af35918b022346f793a9e23fe5d38decd448160a18f6abe59ce2a9fe930dacb7e37bf9312ff3b4fb703c
6
+ metadata.gz: 0c2ef16226e6063220e1ddfa34f94f673240ae7bbecc09873bd0784771601e5ee7822bfa998d75e0d48f1ba6b85f7673d6f1348dd816a3aa8bc63b3fe9f07e69
7
+ data.tar.gz: 37a828c91ea9df41015fb3be82bedc85d9e168bcf86ef2f2e8b7444788cee2a29582b7c710ae66a775ec2dbfaf610c2534db5fe506b353048badebf20a00b7c1
@@ -1 +1 @@
1
- ruby-2.1.0
1
+ ruby-2.1.1
@@ -16,10 +16,15 @@ There is huge room for improvement in this library and I am willing to accept al
16
16
 
17
17
  * Ruby >= 2.1.0
18
18
  * OPUS Audio Codec
19
- * Murmur server > 1.2.4
19
+ * Murmur server > 1.2.4 -- NOTE: mumble-ruby will not be able to stream audio to servers that don't support OPUS anymore. I haven't looked into backwards-compatability with CELT.
20
20
 
21
21
  == RECENT CHANGES:
22
22
 
23
+ * Merged changes for proper user/channel objects
24
+ * Merged changes for recording feature
25
+ * Added half-broken support for playing files (Something is wrong, wouldn't recommend using it)
26
+ * Bit of refactoring and renaming
27
+
23
28
  * Added OPUS support
24
29
  * Added more configuration options
25
30
  * Added image text messages
@@ -62,23 +67,25 @@ There is huge room for improvement in this library and I am willing to accept al
62
67
  cli.connect
63
68
  # => #<Thread:0x000000033d7388 run>
64
69
 
65
- # Mute and Deafen yourself
66
- cli.mute
67
- cli.deafen
70
+ # Mute and Deafen yourself after connecting
71
+ cli.on_connect do
72
+ cli.me.mute
73
+ cli.me.deafen
74
+ end
68
75
 
69
- # Join the channel titled "Chillen" (this returns the number of bytes written to the socket)
76
+ # Join the channel titled "Chillen" (this will return a channel object for that channel)
70
77
  cli.join_channel('Chillen')
71
- # => 11
72
78
 
73
79
  # Get a list of channels
74
80
  cli.channels
75
- # Returns a hash of channel_id: ChannelState Messages
81
+ # Returns a hash of channel_id: Channel objects
76
82
 
77
83
  # Join Channel using ID
78
84
  cli.join_channel(0)
79
85
 
80
- # Join Channel using ChannelState Message
86
+ # Join Channel using Channel object
81
87
  cli.join_channel(cli.channels[0])
88
+ cli.channels[0].join
82
89
 
83
90
  # Get a list of users
84
91
  cli.users
@@ -86,14 +93,20 @@ There is huge room for improvement in this library and I am willing to accept al
86
93
 
87
94
  # Text user
88
95
  cli.text_user('perrym5', "Hello there, I'm a robot!")
89
- # => 35
90
96
 
91
97
  # Text an image to a channel
92
98
  cli.text_channel_img('Chillen', '/path/to/image.jpg')
93
99
 
94
100
  # Start streaming from a FIFO queue of raw PCM data
95
- cli.stream_raw_audio('/tmp/mpd.fifo')
96
- # => #<Mumble::AudioStream ...>
101
+ cli.player.stream_named_pipe('/tmp/mpd.fifo')
102
+
103
+ # EXPERIMENTAL: Recording feature
104
+ cli.recorder.start('/home/matt/record.wav')
105
+ sleep(2)
106
+ cli.recorder.stop
107
+
108
+ # EXPERIMENTAL: Play wav files
109
+ cli.player.play_file('/home/matt/record.wav')
97
110
 
98
111
  # Safely disconnect
99
112
  cli.disconnect
@@ -1,13 +1,19 @@
1
1
  require 'opus-ruby'
2
- require 'active_support/inflector.rb'
2
+ require 'active_support/inflector'
3
3
  require 'mumble-ruby/version'
4
- require 'mumble-ruby/messages.rb'
5
- require 'mumble-ruby/connection.rb'
6
- require 'mumble-ruby/client.rb'
7
- require 'mumble-ruby/audio_stream.rb'
8
- require 'mumble-ruby/packet_data_stream.rb'
9
- require 'mumble-ruby/img_reader.rb'
10
- require 'mumble-ruby/cert_manager.rb'
4
+ require 'mumble-ruby/thread_tools'
5
+ require 'mumble-ruby/messages'
6
+ require 'mumble-ruby/connection'
7
+ require 'mumble-ruby/model'
8
+ require 'mumble-ruby/user'
9
+ require 'mumble-ruby/channel'
10
+ require 'mumble-ruby/client'
11
+ require 'mumble-ruby/audio_player'
12
+ require 'mumble-ruby/packet_data_stream'
13
+ require 'mumble-ruby/img_reader'
14
+ require 'mumble-ruby/cert_manager'
15
+ require 'mumble-ruby/audio_recorder'
16
+ require 'hashie'
11
17
 
12
18
  module Mumble
13
19
  DEFAULTS = {
@@ -0,0 +1,102 @@
1
+ require 'wavefile'
2
+
3
+ module Mumble
4
+ class AudioPlayer
5
+ include ThreadTools
6
+ COMPRESSED_SIZE = 960
7
+
8
+ def initialize(type, connection, sample_rate, bitrate)
9
+ @packet_header = (type << 5).chr
10
+ @conn = connection
11
+ @pds = PacketDataStream.new
12
+ @queue = Queue.new
13
+ @wav_format = WaveFile::Format.new :mono, :pcm_16, sample_rate
14
+
15
+ create_encoder sample_rate, bitrate
16
+ end
17
+
18
+ def volume
19
+ @volume ||= 100
20
+ end
21
+
22
+ def volume=(volume)
23
+ @volume = volume
24
+ end
25
+
26
+ def playing?
27
+ @playing ||= false
28
+ end
29
+
30
+ def play_file(file)
31
+ unless playing?
32
+ @file = WaveFile::Reader.new(file, @wav_format)
33
+ Thread.new { bounded_produce }
34
+ @playing = true
35
+ end
36
+ end
37
+
38
+ def stream_named_pipe(pipe)
39
+ unless playing?
40
+ @file = File.open(pipe, 'rb')
41
+ spawn_thread :produce, :consume
42
+ @playing = true
43
+ end
44
+ end
45
+
46
+ def stop
47
+ if playing?
48
+ kill_threads
49
+ @encoder.reset
50
+ @file.close unless @file.closed?
51
+ @playing = false
52
+ end
53
+ end
54
+
55
+ private
56
+ def create_encoder(sample_rate, bitrate)
57
+ @encoder = Opus::Encoder.new sample_rate, sample_rate / 100, 1
58
+ @encoder.vbr_rate = 0 # CBR
59
+ @encoder.bitrate = bitrate
60
+ end
61
+
62
+ def change_volume(pcm_data)
63
+ pcm_data.unpack('s*').map { |s| s * (volume / 100.0) }.pack('s*')
64
+ end
65
+
66
+ def bounded_produce
67
+ @file.each_buffer(@encoder.frame_size) do |buffer|
68
+ encode_sample buffer.samples.pack('s*')
69
+ consume
70
+ end
71
+
72
+ stop
73
+ end
74
+
75
+ def produce
76
+ encode_sample @file.read(@encoder.frame_size * 2)
77
+ end
78
+
79
+ def encode_sample(sample)
80
+ pcm_data = change_volume sample
81
+ @queue << @encoder.encode(pcm_data, COMPRESSED_SIZE)
82
+ end
83
+
84
+ def consume
85
+ @seq ||= 0
86
+ @seq %= 1000000 # Keep sequence number reasonable for long runs
87
+
88
+ @pds.rewind
89
+ @seq += 1
90
+ @pds.put_int @seq
91
+
92
+ frame = @queue.pop
93
+ @pds.put_int frame.size
94
+ @pds.append_block frame
95
+
96
+ size = @pds.size
97
+ @pds.rewind
98
+ data = [@packet_header, @pds.get_block(size)].flatten.join
99
+ @conn.send_udp_packet data
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,79 @@
1
+ require 'wavefile'
2
+ require 'thread'
3
+
4
+ module Mumble
5
+ class AudioRecorder
6
+ include ThreadTools
7
+
8
+ def initialize(client, sample_rate)
9
+ @client = client
10
+ @wav_format = WaveFile::Format.new(:mono, :pcm_16, sample_rate)
11
+ @pds = PacketDataStream.new
12
+ @pds_lock = Mutex.new
13
+
14
+ @decoders = Hash.new do |h, k|
15
+ h[k] = Opus::Decoder.new sample_rate, sample_rate / 100, 1
16
+ end
17
+
18
+ @queues = Hash.new do |h, k|
19
+ h[k] = Queue.new
20
+ end
21
+ end
22
+
23
+ def recording?
24
+ @recording ||= false
25
+ end
26
+
27
+ def start(file)
28
+ unless recording?
29
+ @file = WaveFile::Writer.new(file, @wav_format)
30
+ @callback = @client.on_udp_tunnel { |msg| process_udp_tunnel msg }
31
+ spawn_thread :write_audio
32
+ @recording = true
33
+ end
34
+ end
35
+
36
+ def stop
37
+ if recording?
38
+ @client.remove_callback :udp_tunnel, @callback
39
+ kill_threads
40
+ @decoders.values.each &:destroy
41
+ @decoders.clear
42
+ @queues.clear
43
+ @file.close
44
+ @recording = false
45
+ end
46
+ end
47
+
48
+ private
49
+ def process_udp_tunnel(message)
50
+ @pds_lock.synchronize do
51
+ @pds.rewind
52
+ @pds.append_block message.packet[1..-1]
53
+
54
+ @pds.rewind
55
+ source = @pds.get_int
56
+ seq = @pds.get_int
57
+ len = @pds.get_int
58
+ opus = @pds.get_block len
59
+
60
+ @queues[source] << @decoders[source].decode(opus.join)
61
+ end
62
+ end
63
+
64
+ # TODO: Better audio stream merge with normalization
65
+ def write_audio
66
+ pcms = @queues.values
67
+ .reject { |q| q.empty? } # Remove empty queues
68
+ .map { |q| q.pop.unpack 's*' } # Grab the top element of each queue and expand
69
+
70
+ head, *tail = pcms
71
+ if head
72
+ samples = head.zip(*tail)
73
+ .map { |pcms| pcms.reduce(:+) / pcms.size } # Average together all the columns of the matrix (merge audio streams)
74
+ .flatten # Flatten the resulting 1d matrix
75
+ @file.write WaveFile::Buffer.new(samples, @wav_format)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,48 @@
1
+ module Mumble
2
+ class Channel < Model
3
+ attribute :channel_id do
4
+ self.data.fetch('channel_id', 0)
5
+ end
6
+ attribute :name
7
+ attribute :parent_id do
8
+ self.data['parent']
9
+ end
10
+ attribute :links do
11
+ self.data.fetch('links', [])
12
+ end
13
+
14
+ def parent
15
+ client.channels[parent_id]
16
+ end
17
+
18
+ def children
19
+ client.channels.values.select do |channel|
20
+ channel.parent_id == channel_id
21
+ end
22
+ end
23
+
24
+ def linked_channels
25
+ links.map do |channel_id|
26
+ client.channels[channel_id]
27
+ end
28
+ end
29
+
30
+ def users
31
+ client.users.values.select do |user|
32
+ user.channel_id == channel_id
33
+ end
34
+ end
35
+
36
+ def join
37
+ client.join_channel(self)
38
+ end
39
+
40
+ def send_text(string)
41
+ client.text_channel(self, string)
42
+ end
43
+
44
+ def send_image(file)
45
+ client.text_channel_img(self, file)
46
+ end
47
+ end
48
+ end
@@ -1,4 +1,3 @@
1
- require 'thread'
2
1
  require 'hashie'
3
2
 
4
3
  module Mumble
@@ -7,14 +6,14 @@ module Mumble
7
6
  class NoSupportedCodec < StandardError; end
8
7
 
9
8
  class Client
10
- attr_reader :users, :channels, :connected
9
+ include ThreadTools
10
+ attr_reader :users, :channels
11
11
 
12
12
  CODEC_OPUS = 4
13
13
 
14
14
  def initialize(host, port=64738, username="RubyClient", password="")
15
15
  @users, @channels = {}, {}
16
16
  @callbacks = Hash.new { |h, k| h[k] = [] }
17
- @connected = false
18
17
 
19
18
  @config = Mumble.configuration.dup.tap do |c|
20
19
  c.host = host
@@ -26,91 +25,69 @@ module Mumble
26
25
  end
27
26
 
28
27
  def connect
29
- cert_manager = CertManager.new(@config.username, @config.ssl_cert_opts)
30
28
  @conn = Connection.new @config.host, @config.port, cert_manager
31
29
  @conn.connect
32
30
 
33
- create_encoder
31
+ init_callbacks
34
32
  version_exchange
35
33
  authenticate
36
- init_callbacks
37
34
 
38
- @read_thread = spawn_thread :read
39
- @ping_thread = spawn_thread :ping
35
+ spawn_threads :read, :ping
36
+ connected? # just to get a nice return value
40
37
  end
41
38
 
42
39
  def disconnect
43
- @encoder.destroy
44
- @read_thread.kill
45
- @ping_thread.kill
40
+ kill_threads
46
41
  @conn.disconnect
47
42
  @connected = false
48
43
  end
49
44
 
50
- def me
51
- users[@session]
45
+ def connected?
46
+ @connected ||= false
52
47
  end
53
48
 
54
- def current_channel
55
- channels[me.channel_id]
49
+ def cert_manager
50
+ @cert_manager ||= CertManager.new @config.username, @config.ssl_cert_opts
56
51
  end
57
52
 
58
- def stream_raw_audio(file)
53
+ def recorder
59
54
  raise NoSupportedCodec unless @codec
60
- AudioStream.new(@codec, 0, @encoder, file, @conn)
55
+ @recorder ||= AudioRecorder.new self, @config.sample_rate
61
56
  end
62
57
 
63
- Messages.all_types.each do |msg_type|
64
- define_method "on_#{msg_type}" do |&block|
65
- @callbacks[msg_type] << block
66
- end
67
-
68
- define_method "send_#{msg_type}" do |opts|
69
- @conn.send_message(msg_type, opts)
70
- end
71
- end
72
-
73
- def mute(bool=true)
74
- send_user_state self_mute: bool
58
+ def player
59
+ raise NoSupportedCodec unless @codec
60
+ @audio_streamer ||= AudioPlayer.new @codec, @conn, @config.sample_rate, @config.bitrate
75
61
  end
76
62
 
77
- def deafen(bool=true)
78
- send_user_state self_deaf: bool
63
+ def me
64
+ users[@session]
79
65
  end
80
66
 
81
67
  def join_channel(channel)
82
- send_user_state({
83
- session: me.session,
84
- channel_id: channel_id(channel)
85
- })
68
+ id = channel_id channel
69
+ send_user_state(session: @session, channel_id: id)
70
+ channels[id]
86
71
  end
87
72
 
88
73
  def text_user(user, string)
89
- send_text_message({
90
- session: [user_session(user)],
91
- message: string
92
- })
74
+ session = user_session user
75
+ send_text_message(session: [user_session(user)], message: string)
76
+ users[session]
93
77
  end
94
78
 
95
79
  def text_user_img(user, file)
96
- img = ImgReader.new file
97
- text_user(user, img.to_msg)
80
+ text_user(user, ImgReader.msg_from_file(file))
98
81
  end
99
82
 
100
83
  def text_channel(channel, string)
101
- send_text_message({
102
- channel_id: [channel_id(channel)],
103
- message: string
104
- })
84
+ id = channel_id channel
85
+ send_text_message(channel_id: [id], message: string)
86
+ channels[id]
105
87
  end
106
88
 
107
89
  def text_channel_img(channel, file)
108
- img = ImgReader.new file
109
- text_channel(channel, img.to_msg)
110
- end
111
-
112
- def user_stats(user)
113
- send_user_stats session: user_session(user)
90
+ text_channel(channel, ImgReader.msg_from_file(file))
114
91
  end
115
92
 
116
93
  def find_user(name)
@@ -125,11 +102,21 @@ module Mumble
125
102
  @callbacks[:connected] << block
126
103
  end
127
104
 
128
- private
129
- def spawn_thread(sym)
130
- Thread.new { loop { send sym } }
105
+ def remove_callback(symbol, callback)
106
+ @callbacks[symbol].delete callback
107
+ end
108
+
109
+ Messages.all_types.each do |msg_type|
110
+ define_method "on_#{msg_type}" do |&block|
111
+ @callbacks[msg_type] << block
112
+ end
113
+
114
+ define_method "send_#{msg_type}" do |opts|
115
+ @conn.send_message(msg_type, opts)
116
+ end
131
117
  end
132
118
 
119
+ private
133
120
  def read
134
121
  message = @conn.read_message
135
122
  sym = message.class.to_s.demodulize.underscore.to_sym
@@ -153,9 +140,9 @@ module Mumble
153
140
  end
154
141
  on_channel_state do |message|
155
142
  if channel = channels[message.channel_id]
156
- channel.merge! message.to_hash
143
+ channel.update message.to_hash
157
144
  else
158
- channels[message.channel_id] = Hashie::Mash.new(message.to_hash)
145
+ channels[message.channel_id] = Channel.new(self, message.to_hash)
159
146
  end
160
147
  end
161
148
  on_channel_remove do |message|
@@ -163,9 +150,9 @@ module Mumble
163
150
  end
164
151
  on_user_state do |message|
165
152
  if user = users[message.session]
166
- user.merge! message.to_hash
153
+ user.update(message.to_hash)
167
154
  else
168
- users[message.session] = Hashie::Mash.new(message.to_hash)
155
+ users[message.session] = User.new(self, message.to_hash)
169
156
  end
170
157
  end
171
158
  on_user_remove do |message|
@@ -176,15 +163,9 @@ module Mumble
176
163
  end
177
164
  end
178
165
 
179
- def create_encoder
180
- @encoder = Opus::Encoder.new @config.sample_rate, @config.sample_rate / 100, 1
181
- @encoder.vbr_rate = 0 # CBR
182
- @encoder.bitrate = @config.bitrate
183
- end
184
-
185
166
  def version_exchange
186
167
  send_version({
187
- version: encode_version(1, 2, 5),
168
+ version: encode_version(1, 2, 7),
188
169
  release: "mumble-ruby #{Mumble::VERSION}",
189
170
  os: %x{uname -s}.strip,
190
171
  os_version: %x{uname -v}.strip
@@ -14,26 +14,24 @@ module Mumble
14
14
  end
15
15
 
16
16
  class ImgReader
17
- attr_reader :file
18
- FORMATS = %w(png jpg jpeg svg)
17
+ class << self
18
+ FORMATS = %w(png jpg jpeg svg)
19
19
 
20
- def initialize(file)
21
- @file = file
22
- raise LoadError.new("#{file} not found") unless File.exists? file
23
- raise UnsupportedImgFormat unless FORMATS.include? ext
24
- raise ImgTooLarge unless File.size(file) <= 128 * 1024
25
- end
26
-
27
- def ext
28
- @ext ||= File.extname(@file)[1..-1]
29
- end
20
+ def msg_from_file(file)
21
+ @@file = file
22
+ @@ext = File.extname(@@file)[1..-1]
23
+ validate_file
30
24
 
31
- def data
32
- @data ||= File.read @file
33
- end
25
+ data = File.read @@file
26
+ "<img src='data:image/#{@@ext};base64,#{Base64.encode64(data)}'/>"
27
+ end
34
28
 
35
- def to_msg
36
- "<img src='data:image/#{ext};base64,#{Base64.encode64(data)}'/>"
29
+ private
30
+ def validate_file
31
+ raise LoadError.new("#{@@file} not found") unless File.exists? @@file
32
+ raise UnsupportedImgFormat unless FORMATS.include? @@ext
33
+ raise ImgTooLarge unless File.size(@@file) <= 128 * 1024
34
+ end
37
35
  end
38
36
  end
39
37
  end
@@ -0,0 +1,48 @@
1
+ require 'forwardable'
2
+
3
+ module Mumble
4
+ class Model
5
+ extend ::Forwardable
6
+
7
+ class << self
8
+ def attribute(name, &block)
9
+ attributes << name
10
+ define_method(name) do
11
+ if block_given?
12
+ self.instance_eval(&block)
13
+ else
14
+ @data[name.to_s]
15
+ end
16
+ end
17
+ end
18
+
19
+ def attributes
20
+ @attributes ||= []
21
+ end
22
+ end
23
+
24
+ def initialize(client, data)
25
+ @client = client
26
+ @data = data
27
+ end
28
+
29
+ def update(data)
30
+ @data.merge!(data)
31
+ end
32
+
33
+ def inspect
34
+ attrs = self.class.attributes.map do |attr|
35
+ [attr, send(attr)].join("=")
36
+ end.join(" ")
37
+ %Q{#<#{self.class.name} #{attrs}>}
38
+ end
39
+
40
+ protected def data
41
+ @data
42
+ end
43
+
44
+ protected def client
45
+ @client
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,24 @@
1
+ module Mumble
2
+ module ThreadTools
3
+ class DuplicateThread < StandardError; end
4
+
5
+ protected
6
+ def spawn_thread(sym)
7
+ raise DuplicateThread if threads.has_key? sym
8
+ threads[sym] = Thread.new { loop { send sym } }
9
+ end
10
+
11
+ def spawn_threads(*symbols)
12
+ symbols.map { |sym| spawn_thread sym }
13
+ end
14
+
15
+ def kill_threads
16
+ threads.values.map(&:kill)
17
+ threads.clear
18
+ end
19
+
20
+ def threads
21
+ @threads ||= {}
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,46 @@
1
+ module Mumble
2
+ class User < Model
3
+ attribute :session
4
+ attribute :actor
5
+ attribute :name
6
+ attribute :channel_id
7
+ attribute :hash
8
+ attribute :comment
9
+ attribute :mute
10
+ attribute :deaf
11
+ attribute :self_mute
12
+ attribute :self_deaf
13
+
14
+ def current_channel
15
+ client.channels[channel_id]
16
+ end
17
+
18
+ def send_text(string)
19
+ client.text_user(self, string)
20
+ end
21
+
22
+ def send_image(file)
23
+ client.text_user_img(self, file)
24
+ end
25
+
26
+ def mute(bool=true)
27
+ client.send_user_state self_mute: bool
28
+ end
29
+
30
+ def deafen(bool=true)
31
+ client.send_user_state self_deaf: bool
32
+ end
33
+
34
+ def muted?
35
+ !!mute || !!self_mute
36
+ end
37
+
38
+ def deafened?
39
+ !!deaf || !!self_deaf
40
+ end
41
+
42
+ def stats
43
+ client.send_user_stats session: session
44
+ end
45
+ end
46
+ end
@@ -1,3 +1,3 @@
1
1
  module Mumble
2
- VERSION = "1.0.2"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -19,4 +19,5 @@ Gem::Specification.new do |gem|
19
19
  gem.add_dependency "ruby_protobuf"
20
20
  gem.add_dependency "hashie"
21
21
  gem.add_dependency "opus-ruby"
22
+ gem.add_dependency "wavefile"
22
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mumble-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Perry
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-26 00:00:00.000000000 Z
11
+ date: 2014-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: wavefile
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  description: Ruby API for interacting with a mumble server
70
84
  email:
71
85
  - perrym5@rpi.edu
@@ -81,14 +95,19 @@ files:
81
95
  - README.rdoc
82
96
  - Rakefile
83
97
  - lib/mumble-ruby.rb
84
- - lib/mumble-ruby/audio_stream.rb
98
+ - lib/mumble-ruby/audio_player.rb
99
+ - lib/mumble-ruby/audio_recorder.rb
85
100
  - lib/mumble-ruby/cert_manager.rb
101
+ - lib/mumble-ruby/channel.rb
86
102
  - lib/mumble-ruby/client.rb
87
103
  - lib/mumble-ruby/connection.rb
88
104
  - lib/mumble-ruby/img_reader.rb
89
105
  - lib/mumble-ruby/messages.rb
106
+ - lib/mumble-ruby/model.rb
90
107
  - lib/mumble-ruby/mumble.proto
91
108
  - lib/mumble-ruby/packet_data_stream.rb
109
+ - lib/mumble-ruby/thread_tools.rb
110
+ - lib/mumble-ruby/user.rb
92
111
  - lib/mumble-ruby/version.rb
93
112
  - mumble-ruby.gemspec
94
113
  homepage: http://www.github.com/perrym5/mumble-ruby
@@ -1,67 +0,0 @@
1
- module Mumble
2
- class AudioStream
3
- attr_reader :volume
4
-
5
- def initialize(type, target, encoder, file, connection)
6
- @type = type
7
- @target = target
8
- @encoder = encoder
9
- @file = File.open(file, 'rb')
10
- @conn = connection
11
- @seq = 0
12
- @compressed_size = 960
13
- @pds = PacketDataStream.new
14
- @volume = 1.0
15
-
16
- @queue = Queue.new
17
- @producer = spawn_thread :produce
18
- @consumer = spawn_thread :consume
19
- end
20
-
21
- def volume=(volume)
22
- @volume = volume / 100.0
23
- end
24
-
25
- def stop
26
- @producer.kill
27
- @consumer.kill
28
- @file.close
29
- end
30
-
31
- private
32
- def change_volume(pcm_data)
33
- pcm_data.unpack('s*').map { |s| s * @volume }.pack('s*')
34
- end
35
-
36
- def packet_header
37
- ((@type << 5) | @target).chr
38
- end
39
-
40
- def produce
41
- pcm_data = change_volume @file.read(@encoder.frame_size * 2)
42
- @queue << @encoder.encode(pcm_data, @compressed_size)
43
- end
44
-
45
- def consume
46
- @seq %= 1000000 # Keep sequence number reasonable for long runs
47
-
48
- @pds.rewind
49
- @seq += 1
50
- @pds.put_int @seq
51
-
52
- frame = @queue.pop
53
- len = frame.size
54
- @pds.put_int len
55
- @pds.append_block frame
56
-
57
- size = @pds.size
58
- @pds.rewind
59
- data = [packet_header, @pds.get_block(size)].flatten.join
60
- @conn.send_udp_packet data
61
- end
62
-
63
- def spawn_thread(sym)
64
- Thread.new { loop { send sym } }
65
- end
66
- end
67
- end