libatem 0.0.1
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 +7 -0
- data/README.md +39 -0
- data/lib/atem.rb +18 -0
- data/lib/atem/network.rb +186 -0
- data/lib/atem/switcher.rb +158 -0
- data/lib/atem/switcher/input.rb +63 -0
- data/lib/atem/switcher/input/audio.rb +59 -0
- data/lib/atem/switcher/input_collection.rb +42 -0
- metadata +51 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: bc36e2d5444044ba6b674c2cb93c3779497b59cb
|
|
4
|
+
data.tar.gz: 7d977b1f3cee724021e8c38c0a3b82df6cbf3bfc
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 73624d5e7736f99017e16d2d843f2dbeaeb8a77346ebde9d3880498e5c2d0d51e5a5277d33d915f89192530a9a7b95c693a678f3dd6123c3076cf00cd1793d14
|
|
7
|
+
data.tar.gz: 1c2e7bb4cdacba2e24cb2827465f02f84205915a80018b82362610a960b37cbec2b64f028c2c1d660153f5906e49ca060883405ef919a37115f3992fe2b11239
|
data/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# <img src="https://raw.githubusercontent.com/InsanityRadio/OnAirController/master/doc/headphones_dark.png" align="left" height=48 /> libatem
|
|
2
|
+
|
|
3
|
+
libatem is a Ruby library that can control ATEM switchers.
|
|
4
|
+
|
|
5
|
+
# Basic usage
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
switcher = ATEM.connect("10.32.0.199")
|
|
9
|
+
|
|
10
|
+
switcher.inputs
|
|
11
|
+
|
|
12
|
+
=> []
|
|
13
|
+
|
|
14
|
+
switcher.inputs[1]
|
|
15
|
+
switcher.inputs['Camera 1']
|
|
16
|
+
|
|
17
|
+
switcher.inputs[0].preview
|
|
18
|
+
switcher.inputs[1].program
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Audio!
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
# Just a warning: this generates quite a bit of traffic from the ATEM
|
|
28
|
+
switcher.use_audio_levels = true
|
|
29
|
+
|
|
30
|
+
switcher.inputs['Camera 1'].audio.level
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
# TODO:
|
|
35
|
+
|
|
36
|
+
- Add more features/command support!
|
|
37
|
+
- Smoke & unit test, improve stability
|
|
38
|
+
- Audio
|
|
39
|
+
- Create Gem
|
data/lib/atem.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require_relative 'atem/network'
|
|
2
|
+
require_relative 'atem/switcher'
|
|
3
|
+
require_relative 'atem/switcher/input'
|
|
4
|
+
require_relative 'atem/switcher/input_collection'
|
|
5
|
+
require_relative 'atem/switcher/input/audio'
|
|
6
|
+
|
|
7
|
+
module ATEM
|
|
8
|
+
|
|
9
|
+
def self.connect ip, port = 9910
|
|
10
|
+
|
|
11
|
+
s = Switcher.new({ :ip => ip, :port => port, :uid => 0x1337 })
|
|
12
|
+
s.connect
|
|
13
|
+
|
|
14
|
+
s
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
data/lib/atem/network.rb
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
require 'socket'
|
|
2
|
+
|
|
3
|
+
class String
|
|
4
|
+
|
|
5
|
+
def to_hex
|
|
6
|
+
self.bytes.map{|a|"0x" + a.to_s(16)}.join(" ")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
module ATEM
|
|
13
|
+
|
|
14
|
+
class Network
|
|
15
|
+
|
|
16
|
+
module Packet
|
|
17
|
+
NOP = 0x00
|
|
18
|
+
ACK_REQ = 0x01
|
|
19
|
+
HELLO = 0x02
|
|
20
|
+
RESEND = 0x04
|
|
21
|
+
UNDEFINED = 0x08
|
|
22
|
+
ACK = 0x10
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class Retry < StandardError
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
@@SIZE_OF_HEADER = 0x0c
|
|
29
|
+
|
|
30
|
+
@package_id = 0
|
|
31
|
+
|
|
32
|
+
def initialize ip, port, uid = 0x1337
|
|
33
|
+
|
|
34
|
+
@socket = UDPSocket.new
|
|
35
|
+
@socket.bind "0.0.0.0", 9100
|
|
36
|
+
|
|
37
|
+
@ip = ip
|
|
38
|
+
@port = port
|
|
39
|
+
@uid = uid
|
|
40
|
+
@package_id = 0
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def disconnect
|
|
45
|
+
@socket.close
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def << data
|
|
49
|
+
|
|
50
|
+
bitmask, ack_id, payload = data
|
|
51
|
+
|
|
52
|
+
bitmask = bitmask << 11
|
|
53
|
+
bitmask |= (payload.length + @@SIZE_OF_HEADER)
|
|
54
|
+
|
|
55
|
+
package_id = 0
|
|
56
|
+
if (bitmask & (Packet::HELLO | Packet::ACK)) != 0 and @ready and payload.length != 0
|
|
57
|
+
# p "SENDING PACKAGE"
|
|
58
|
+
@package_id += 1
|
|
59
|
+
package_id = @package_id
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
packet = [bitmask, @uid, ack_id, 0, package_id].pack("S>S>S>L>S>")
|
|
63
|
+
packet += payload
|
|
64
|
+
|
|
65
|
+
# print "TX(#{packet.length}, #{@package_id}, #{ack_id})"; p packet.to_hex
|
|
66
|
+
@socket.send packet, 0, @ip, @port
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def send! cmd, payload
|
|
71
|
+
|
|
72
|
+
raise "Invalid command" if cmd.bytes.length != 4
|
|
73
|
+
|
|
74
|
+
size = cmd.length + payload.length + 4
|
|
75
|
+
datagram = [size, 0, 0].pack("S>CC") + cmd + payload
|
|
76
|
+
|
|
77
|
+
self << [Packet::ACK_REQ, @ack_id, datagram]
|
|
78
|
+
self.receive
|
|
79
|
+
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def hello
|
|
83
|
+
|
|
84
|
+
self << [0x02, 0x0, [0x01000000, 0x00].pack("L>L>")]
|
|
85
|
+
self.receive_until_ready
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def receive
|
|
90
|
+
|
|
91
|
+
packets = []
|
|
92
|
+
next_packet = nil
|
|
93
|
+
|
|
94
|
+
begin
|
|
95
|
+
|
|
96
|
+
begin
|
|
97
|
+
data, _ = @socket.recvfrom(2048)
|
|
98
|
+
rescue
|
|
99
|
+
p "ERR"
|
|
100
|
+
return []
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# print "RX(#{data.length}) "; p data.to_hex
|
|
104
|
+
|
|
105
|
+
bitmask, size, uid, ack_id, _, package_id = data.unpack("CXS>S>S>LS>")
|
|
106
|
+
@uid = uid
|
|
107
|
+
|
|
108
|
+
bitmask = bitmask >> 3
|
|
109
|
+
size &= 0x07FF
|
|
110
|
+
|
|
111
|
+
# print "RX HEADER: "
|
|
112
|
+
# p [bitmask, size, uid, ack_id, package_id]
|
|
113
|
+
|
|
114
|
+
@ack_id = package_id
|
|
115
|
+
|
|
116
|
+
packet = [ack_id, bitmask, package_id, data[ @@SIZE_OF_HEADER .. -1 ]]
|
|
117
|
+
|
|
118
|
+
packets += handle(packet)
|
|
119
|
+
|
|
120
|
+
# raise Retry
|
|
121
|
+
|
|
122
|
+
# rescue Retry
|
|
123
|
+
# retry if next_packet and next_packet.length >= @@SIZE_OF_HEADER
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
packets
|
|
127
|
+
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def receive_until_ready
|
|
131
|
+
|
|
132
|
+
packets = []
|
|
133
|
+
while !@ready
|
|
134
|
+
packets += receive
|
|
135
|
+
end
|
|
136
|
+
packets
|
|
137
|
+
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
private
|
|
141
|
+
|
|
142
|
+
def handle packet
|
|
143
|
+
|
|
144
|
+
bitmask = packet[1]
|
|
145
|
+
|
|
146
|
+
if (bitmask & Packet::HELLO) == Packet::HELLO
|
|
147
|
+
|
|
148
|
+
@ready = false
|
|
149
|
+
self << [Packet::ACK, 0x0, '']
|
|
150
|
+
|
|
151
|
+
elsif ((bitmask & Packet::ACK_REQ) == Packet::ACK_REQ) and (@ready or (!@ready and packet[3].length == 0))
|
|
152
|
+
|
|
153
|
+
self << [Packet::ACK, packet[2], '']
|
|
154
|
+
@ready = true
|
|
155
|
+
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
data = packet[3]
|
|
159
|
+
packets = []
|
|
160
|
+
|
|
161
|
+
while data != nil and data.length > 0
|
|
162
|
+
|
|
163
|
+
packet, data = payload(data)
|
|
164
|
+
packets << packet
|
|
165
|
+
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
packets
|
|
169
|
+
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def payload packet
|
|
173
|
+
|
|
174
|
+
size = packet.unpack("S>")[0]
|
|
175
|
+
pack = packet[4..size-1]
|
|
176
|
+
packet = packet[size..-1]
|
|
177
|
+
|
|
178
|
+
command = pack.slice!(0, 4)
|
|
179
|
+
|
|
180
|
+
[[command, pack], packet]
|
|
181
|
+
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
end
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
module ATEM
|
|
2
|
+
|
|
3
|
+
class Switcher
|
|
4
|
+
|
|
5
|
+
attr_reader :version, :product, :topology, :video_mode, :master
|
|
6
|
+
|
|
7
|
+
def initialize config
|
|
8
|
+
@config = config
|
|
9
|
+
@inputs = ATEM::Switcher::InputCollection.new self
|
|
10
|
+
@_audio_by_index = []
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def connect
|
|
14
|
+
|
|
15
|
+
@airtower = ATEM::Network.new @config[:ip], @config[:port], @config[:uid]
|
|
16
|
+
|
|
17
|
+
response = @airtower.hello
|
|
18
|
+
# @airtower.send! "FTSU", "\x0" * 12
|
|
19
|
+
response.each { | c | handle c }
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# YIKES!
|
|
24
|
+
def handle packet
|
|
25
|
+
|
|
26
|
+
case packet[0]
|
|
27
|
+
when "_ver"
|
|
28
|
+
|
|
29
|
+
@version = packet[1].unpack("S>S>")
|
|
30
|
+
|
|
31
|
+
when "_pin"
|
|
32
|
+
|
|
33
|
+
@product = packet[1].unpack("a20")[0]
|
|
34
|
+
|
|
35
|
+
when "_top"
|
|
36
|
+
|
|
37
|
+
top = ["MEs", "Sources", "Colour Generators", "AUX busses", "DSKs", "Stingers", "DVEs", "SuperSources"]
|
|
38
|
+
@topology = Hash[top.zip(packet[1].unpack("CCCCCCCC"))]
|
|
39
|
+
|
|
40
|
+
when "VidM"
|
|
41
|
+
|
|
42
|
+
@video_mode = packet[1].unpack("C")
|
|
43
|
+
|
|
44
|
+
when "InPr"
|
|
45
|
+
|
|
46
|
+
input = ATEM::Switcher::Input.from packet[1], self, ATEM::Switcher::Input::Type::VIDEO
|
|
47
|
+
@inputs.add input
|
|
48
|
+
|
|
49
|
+
when "AMIP"
|
|
50
|
+
|
|
51
|
+
audio_id = packet[1].unpack("S>")[0] #("S>CxxxCCCxS>s>")
|
|
52
|
+
|
|
53
|
+
input = @inputs[audio_id]
|
|
54
|
+
|
|
55
|
+
if !@inputs[audio_id]
|
|
56
|
+
input = ATEM::Switcher::Input.new self
|
|
57
|
+
input.init audio_id
|
|
58
|
+
@inputs.add(input)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
input.type |= ATEM::Switcher::Input::Type::AUDIO
|
|
62
|
+
input.audio = ATEM::Switcher::Input::Audio.from packet[1], self, input
|
|
63
|
+
|
|
64
|
+
when "AMLv"
|
|
65
|
+
|
|
66
|
+
master = {}
|
|
67
|
+
sources, master[:left], master[:right], master[:left_peak], master[:right_peak],
|
|
68
|
+
monitor = packet[1].unpack("S>xxl>l>l>l>l>")
|
|
69
|
+
|
|
70
|
+
@master = master
|
|
71
|
+
start_offset = 38 + sources * 2
|
|
72
|
+
|
|
73
|
+
(0..sources-1).each do | source |
|
|
74
|
+
|
|
75
|
+
source_id = packet[1][(36 + source * 2)..-1].unpack("S>")[0]
|
|
76
|
+
|
|
77
|
+
levels = {}
|
|
78
|
+
|
|
79
|
+
levels[:left], levels[:right], levels[:left_peak], levels[:right_peak] =
|
|
80
|
+
packet[1][(start_offset + source * 16)..-1].unpack("l>l>l>l>")
|
|
81
|
+
|
|
82
|
+
@inputs[source_id].audio.levels = levels
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def disconnect
|
|
91
|
+
|
|
92
|
+
@airtower.disconnect
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def inputs
|
|
97
|
+
@inputs
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def multithreading
|
|
101
|
+
@thread != nil
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def multithreading= enabled
|
|
105
|
+
|
|
106
|
+
@thread.kill if @thread
|
|
107
|
+
@thread = nil
|
|
108
|
+
return if !enabled
|
|
109
|
+
|
|
110
|
+
Thread.abort_on_exception = true
|
|
111
|
+
@thread = Thread.new do
|
|
112
|
+
|
|
113
|
+
loop do
|
|
114
|
+
|
|
115
|
+
packets = @airtower.receive
|
|
116
|
+
packets.each do | packet |
|
|
117
|
+
handle packet
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def use_audio_levels
|
|
127
|
+
@use_audio_levels
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def use_audio_levels= enabled
|
|
131
|
+
|
|
132
|
+
self.multithreading = true if !@thread
|
|
133
|
+
@airtower.send! "SALN", [enabled ? 1 : 0].pack("C") + "\0\0\0"
|
|
134
|
+
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def reset_audio_peaks
|
|
138
|
+
|
|
139
|
+
@inputs.each do | id, input |
|
|
140
|
+
|
|
141
|
+
puts "Resetting #{input.name}" if input.audio != nil
|
|
142
|
+
@airtower.send! "RAMP", [2, 0, input.audio.id, 1, 0, 0, 0].pack("CCS>CCCC") if input.audio != nil
|
|
143
|
+
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def preview id
|
|
149
|
+
@airtower.send! "CPvI", [0, 0, id].pack("CCS>")
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def program id
|
|
153
|
+
@airtower.send! "CPgI", [0, 0, id].pack("CCS>")
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module ATEM
|
|
2
|
+
|
|
3
|
+
class Switcher
|
|
4
|
+
|
|
5
|
+
class Input
|
|
6
|
+
|
|
7
|
+
attr_reader :switcher, :id, :name, :short_name, :quick_init
|
|
8
|
+
attr_accessor :type, :audio
|
|
9
|
+
|
|
10
|
+
module Type
|
|
11
|
+
VIDEO = 0x01
|
|
12
|
+
AUDIO = 0x02
|
|
13
|
+
AUDIO_VIDEO = 0x03
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.from packet, switcher, type
|
|
17
|
+
|
|
18
|
+
input = self.new switcher
|
|
19
|
+
input.init_from packet
|
|
20
|
+
|
|
21
|
+
input.type = type
|
|
22
|
+
|
|
23
|
+
input
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def initialize switcher
|
|
28
|
+
|
|
29
|
+
@switcher = switcher
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def init_from packet
|
|
34
|
+
|
|
35
|
+
@id, @name, @short_name, @supported, @ext_port_type, @port_type, @availability =
|
|
36
|
+
packet.unpack("S>Z20Z4xCxCCxC")
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def init id, name = nil, short_name = nil
|
|
41
|
+
|
|
42
|
+
@id = id
|
|
43
|
+
@name = name or "Input #{@id}"
|
|
44
|
+
@short_name = short_name or "#{@id.to_s.rjust(4, "0")}"
|
|
45
|
+
@quick_init = true
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
######
|
|
50
|
+
|
|
51
|
+
def preview
|
|
52
|
+
@switcher.preview @id
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def program
|
|
56
|
+
@switcher.program @id
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module ATEM
|
|
2
|
+
|
|
3
|
+
class Switcher
|
|
4
|
+
|
|
5
|
+
class Input
|
|
6
|
+
|
|
7
|
+
class Audio
|
|
8
|
+
|
|
9
|
+
attr_reader :switcher, :input, :id, :type, :media_player, :plug, :mix, :gain, :balance
|
|
10
|
+
attr_accessor :levels
|
|
11
|
+
|
|
12
|
+
def self.from packet, switcher, input
|
|
13
|
+
|
|
14
|
+
audio = self.new switcher, input
|
|
15
|
+
audio.init_from packet
|
|
16
|
+
audio
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize switcher, input
|
|
21
|
+
|
|
22
|
+
@switcher = switcher
|
|
23
|
+
@input = input
|
|
24
|
+
@level = 0
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def init_from packet
|
|
29
|
+
|
|
30
|
+
@id, @type, @media_player, @plug, @mix, @gain, @balance =
|
|
31
|
+
packet.unpack("S>CxxxCCCxS>s>")
|
|
32
|
+
|
|
33
|
+
# Now we have the right data, we can name the input itself
|
|
34
|
+
if @input.quick_init
|
|
35
|
+
|
|
36
|
+
values = {
|
|
37
|
+
1001 => ['XLR', 'XLR0'],
|
|
38
|
+
1101 => ['AES/EBU', 'AES3'],
|
|
39
|
+
1201 => ['RCA', 'RCA_'],
|
|
40
|
+
}
|
|
41
|
+
@input.init @input.id, values[@input.id][0], values[@input.id][1]
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
#####
|
|
48
|
+
|
|
49
|
+
def level
|
|
50
|
+
(@levels[:left] + @levels[:right]) / 2
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module ATEM
|
|
2
|
+
|
|
3
|
+
class Switcher
|
|
4
|
+
|
|
5
|
+
class InputCollection
|
|
6
|
+
|
|
7
|
+
include Enumerable
|
|
8
|
+
|
|
9
|
+
attr_reader :switcher
|
|
10
|
+
|
|
11
|
+
def initialize switcher
|
|
12
|
+
@switcher = switcher
|
|
13
|
+
@inputs = {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def add input
|
|
17
|
+
|
|
18
|
+
@inputs[input.id] = input
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def [] index
|
|
23
|
+
|
|
24
|
+
return @inputs[index] if @inputs[index]
|
|
25
|
+
|
|
26
|
+
@inputs.each do | a, input |
|
|
27
|
+
|
|
28
|
+
return input if input.name == index or input.short_name.downcase == index.downcase
|
|
29
|
+
|
|
30
|
+
end if index.is_a? String
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def each(&block)
|
|
35
|
+
@inputs.each(&block)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: libatem
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Jamie Woods
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2017-07-02 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: A library that allows control of Blackmagic Design ATEM switchers through
|
|
14
|
+
Ruby
|
|
15
|
+
email: gnitupmoc@insanityradiodotcom
|
|
16
|
+
executables: []
|
|
17
|
+
extensions: []
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- lib/atem/network.rb
|
|
21
|
+
- lib/atem/switcher/input/audio.rb
|
|
22
|
+
- lib/atem/switcher/input.rb
|
|
23
|
+
- lib/atem/switcher/input_collection.rb
|
|
24
|
+
- lib/atem/switcher.rb
|
|
25
|
+
- lib/atem.rb
|
|
26
|
+
- README.md
|
|
27
|
+
homepage: https://tech.insanityradio.com/
|
|
28
|
+
licenses:
|
|
29
|
+
- MIT
|
|
30
|
+
metadata: {}
|
|
31
|
+
post_install_message:
|
|
32
|
+
rdoc_options: []
|
|
33
|
+
require_paths:
|
|
34
|
+
- lib
|
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - '>='
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
41
|
+
requirements:
|
|
42
|
+
- - '>='
|
|
43
|
+
- !ruby/object:Gem::Version
|
|
44
|
+
version: '0'
|
|
45
|
+
requirements: []
|
|
46
|
+
rubyforge_project:
|
|
47
|
+
rubygems_version: 2.0.14.1
|
|
48
|
+
signing_key:
|
|
49
|
+
specification_version: 4
|
|
50
|
+
summary: ATEM for Ruby
|
|
51
|
+
test_files: []
|