surface_master 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +8 -0
- data/Gemfile +13 -0
- data/LICENSE +21 -0
- data/README.md +48 -0
- data/Rakefile +10 -0
- data/debug_tools/Numark_Orbit_Pad_Coloring.mmon +98 -0
- data/debug_tools/OrbitLightingExample.json +662 -0
- data/debug_tools/Orbit_Color_Test.json +662 -0
- data/debug_tools/Orbit_Colors_And_Reset.1.raw +0 -0
- data/debug_tools/Orbit_Colors_And_Reset.1.txt +82 -0
- data/debug_tools/Orbit_Colors_And_Reset.2.raw +0 -0
- data/debug_tools/Orbit_Colors_And_Reset.2.txt +2 -0
- data/debug_tools/Orbit_Colors_And_Reset.3.raw +0 -0
- data/debug_tools/Orbit_Colors_And_Reset.3.txt +82 -0
- data/debug_tools/Orbit_Colors_And_Reset.mmon +93 -0
- data/debug_tools/Orbit_Preset.1.raw +0 -0
- data/debug_tools/Orbit_Preset.1.txt +82 -0
- data/debug_tools/Orbit_Preset.2.raw +0 -0
- data/debug_tools/Orbit_Preset.2.txt +2 -0
- data/debug_tools/Orbit_Preset.mmon +72 -0
- data/debug_tools/compare.sh +12 -0
- data/debug_tools/decode.rb +14 -0
- data/debug_tools/extract_midi_monitor_sample.sh +33 -0
- data/docs/Numark_Orbit_QuickRef.md +50 -0
- data/examples/launchpad_testbed.rb +141 -0
- data/examples/monitor.rb +61 -0
- data/examples/orbit_testbed.rb +62 -0
- data/lib/control_center.rb +26 -0
- data/lib/surface_master/device.rb +90 -0
- data/lib/surface_master/errors.rb +27 -0
- data/lib/surface_master/interaction.rb +133 -0
- data/lib/surface_master/launchpad/device.rb +159 -0
- data/lib/surface_master/launchpad/errors.rb +11 -0
- data/lib/surface_master/launchpad/interaction.rb +86 -0
- data/lib/surface_master/launchpad/midi_codes.rb +51 -0
- data/lib/surface_master/logging.rb +15 -0
- data/lib/surface_master/orbit/device.rb +160 -0
- data/lib/surface_master/orbit/interaction.rb +29 -0
- data/lib/surface_master/orbit/midi_codes.rb +31 -0
- data/lib/surface_master/version.rb +3 -0
- data/mappings/Orbit_Preset.json +662 -0
- data/surface_master.gemspec +26 -0
- data/test/helper.rb +44 -0
- data/test/test_device.rb +530 -0
- data/test/test_interaction.rb +456 -0
- metadata +121 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "surface_master/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "surface_master"
|
7
|
+
s.version = SurfaceMaster::VERSION
|
8
|
+
s.authors = ["Jon Frisby"]
|
9
|
+
s.email = ["jfrisby@mrjoy.com"]
|
10
|
+
s.homepage = "https://github.com/MrJoy/surface_master"
|
11
|
+
s.summary = %q{A gem for accessing various MIDI control surfaces programmatically.}
|
12
|
+
s.description = %q{This gem provides an interface to access Novation's LaunchPad Mark 2, and Numark's Orbit programmatically. LEDs can be lit and button presses can be read.}
|
13
|
+
# TODO: Update docs to give credit to Thomas Jachmann (self@thomasjachmann.com) for his `launchpad` gem.
|
14
|
+
|
15
|
+
s.required_ruby_version = ">= 2.2.0"
|
16
|
+
|
17
|
+
s.add_dependency "portmidi", ">= 0.0.6"
|
18
|
+
s.add_dependency "ffi"
|
19
|
+
|
20
|
+
# s.has_rdoc = true
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'minitest/reporters'
|
6
|
+
MiniTest::Reporters.use!
|
7
|
+
rescue LoadError
|
8
|
+
# ignore when it's not there - must be ruby 1.8
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'mocha/setup'
|
12
|
+
|
13
|
+
require 'launchpad'
|
14
|
+
|
15
|
+
# mock Portmidi for tests
|
16
|
+
module Portmidi
|
17
|
+
|
18
|
+
class Input
|
19
|
+
attr_accessor :device_id
|
20
|
+
def initialize(device_id)
|
21
|
+
self.device_id = device_id
|
22
|
+
end
|
23
|
+
def read(*args); nil; end
|
24
|
+
def close; nil; end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Output
|
28
|
+
attr_accessor :device_id
|
29
|
+
def initialize(device_id)
|
30
|
+
self.device_id = device_id
|
31
|
+
end
|
32
|
+
def write(*args); nil; end
|
33
|
+
def close; nil; end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.input_devices; mock_devices; end
|
37
|
+
def self.output_devices; mock_devices; end
|
38
|
+
def self.start; end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
def mock_devices(opts = {})
|
43
|
+
[Portmidi::Device.new(opts[:id] || 1, 0, 0, opts[:name] || 'Launchpad MK2')]
|
44
|
+
end
|
data/test/test_device.rb
ADDED
@@ -0,0 +1,530 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
describe SurfaceMaster::Launchpad::Device do
|
4
|
+
|
5
|
+
CONTROL_BUTTONS = {
|
6
|
+
:up => 0x68,
|
7
|
+
:down => 0x69,
|
8
|
+
:left => 0x6A,
|
9
|
+
:right => 0x6B,
|
10
|
+
:session => 0x6C,
|
11
|
+
:user1 => 0x6D,
|
12
|
+
:user2 => 0x6E,
|
13
|
+
:mixer => 0x6F
|
14
|
+
}
|
15
|
+
SCENE_BUTTONS = {
|
16
|
+
:scene1 => 0x08,
|
17
|
+
:scene2 => 0x18,
|
18
|
+
:scene3 => 0x28,
|
19
|
+
:scene4 => 0x38,
|
20
|
+
:scene5 => 0x48,
|
21
|
+
:scene6 => 0x58,
|
22
|
+
:scene7 => 0x68,
|
23
|
+
:scene8 => 0x78
|
24
|
+
}
|
25
|
+
COLORS = {
|
26
|
+
nil => 0, 0 => 0, :off => 0,
|
27
|
+
1 => 1, :lo => 1, :low => 1,
|
28
|
+
2 => 2, :med => 2, :medium => 2,
|
29
|
+
3 => 3, :hi => 3, :high => 3
|
30
|
+
}
|
31
|
+
STATES = {
|
32
|
+
:down => 127,
|
33
|
+
:up => 0
|
34
|
+
}
|
35
|
+
|
36
|
+
def expects_output(device, *args)
|
37
|
+
args = [args] unless args.first.is_a?(Array)
|
38
|
+
messages = args.collect {|data| {:message => data, :timestamp => 0}}
|
39
|
+
device.instance_variable_get('@output').expects(:write).with(messages)
|
40
|
+
end
|
41
|
+
|
42
|
+
def stub_input(device, *args)
|
43
|
+
device.instance_variable_get('@input').stubs(:read).returns(args)
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#initialize' do
|
47
|
+
|
48
|
+
it 'tries to initialize both input and output when not specified' do
|
49
|
+
Portmidi.expects(:input_devices).returns(mock_devices)
|
50
|
+
Portmidi.expects(:output_devices).returns(mock_devices)
|
51
|
+
d = Launchpad::Device.new
|
52
|
+
refute_nil d.instance_variable_get('@input')
|
53
|
+
refute_nil d.instance_variable_get('@output')
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'does not try to initialize input when set to false' do
|
57
|
+
Portmidi.expects(:input_devices).never
|
58
|
+
d = Launchpad::Device.new(:input => false)
|
59
|
+
assert_nil d.instance_variable_get('@input')
|
60
|
+
refute_nil d.instance_variable_get('@output')
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'does not try to initialize output when set to false' do
|
64
|
+
Portmidi.expects(:output_devices).never
|
65
|
+
d = Launchpad::Device.new(:output => false)
|
66
|
+
refute_nil d.instance_variable_get('@input')
|
67
|
+
assert_nil d.instance_variable_get('@output')
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'does not try to initialize any of both when set to false' do
|
71
|
+
Portmidi.expects(:input_devices).never
|
72
|
+
Portmidi.expects(:output_devices).never
|
73
|
+
d = Launchpad::Device.new(:input => false, :output => false)
|
74
|
+
assert_nil d.instance_variable_get('@input')
|
75
|
+
assert_nil d.instance_variable_get('@output')
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'initializes the correct input output devices when specified by name' do
|
79
|
+
Portmidi.stubs(:input_devices).returns(mock_devices(:id => 4, :name => 'Launchpad Name'))
|
80
|
+
Portmidi.stubs(:output_devices).returns(mock_devices(:id => 5, :name => 'Launchpad Name'))
|
81
|
+
d = Launchpad::Device.new(:device_name => 'Launchpad Name')
|
82
|
+
assert_equal Portmidi::Input, (input = d.instance_variable_get('@input')).class
|
83
|
+
assert_equal 4, input.device_id
|
84
|
+
assert_equal Portmidi::Output, (output = d.instance_variable_get('@output')).class
|
85
|
+
assert_equal 5, output.device_id
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'initializes the correct input output devices when specified by id' do
|
89
|
+
Portmidi.stubs(:input_devices).returns(mock_devices(:id => 4))
|
90
|
+
Portmidi.stubs(:output_devices).returns(mock_devices(:id => 5))
|
91
|
+
d = Launchpad::Device.new(:input_device_id => 4, :output_device_id => 5, :device_name => 'nonexistant')
|
92
|
+
assert_equal Portmidi::Input, (input = d.instance_variable_get('@input')).class
|
93
|
+
assert_equal 4, input.device_id
|
94
|
+
assert_equal Portmidi::Output, (output = d.instance_variable_get('@output')).class
|
95
|
+
assert_equal 5, output.device_id
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'raises NoSuchDeviceError when requested input device does not exist' do
|
99
|
+
assert_raises Launchpad::NoSuchDeviceError do
|
100
|
+
Portmidi.stubs(:input_devices).returns(mock_devices(:name => 'Launchpad Input'))
|
101
|
+
Launchpad::Device.new
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'raises NoSuchDeviceError when requested output device does not exist' do
|
106
|
+
assert_raises Launchpad::NoSuchDeviceError do
|
107
|
+
Portmidi.stubs(:output_devices).returns(mock_devices(:name => 'Launchpad Output'))
|
108
|
+
Launchpad::Device.new
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'raises DeviceBusyError when requested input device is busy' do
|
113
|
+
assert_raises Launchpad::DeviceBusyError do
|
114
|
+
Portmidi::Input.stubs(:new).raises(RuntimeError)
|
115
|
+
Launchpad::Device.new
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'raises DeviceBusyError when requested output device is busy' do
|
120
|
+
assert_raises Launchpad::DeviceBusyError do
|
121
|
+
Portmidi::Output.stubs(:new).raises(RuntimeError)
|
122
|
+
Launchpad::Device.new
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'stores the logger given' do
|
127
|
+
logger = Logger.new(nil)
|
128
|
+
device = Launchpad::Device.new(:logger => logger)
|
129
|
+
assert_same logger, device.logger
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '#close' do
|
135
|
+
|
136
|
+
it 'does not fail when neither input nor output are there' do
|
137
|
+
Launchpad::Device.new(:input => false, :output => false).close
|
138
|
+
end
|
139
|
+
|
140
|
+
describe 'with input and output devices' do
|
141
|
+
|
142
|
+
before do
|
143
|
+
Portmidi::Input.stubs(:new).returns(@input = mock('input'))
|
144
|
+
Portmidi::Output.stubs(:new).returns(@output = mock('output', :write => nil))
|
145
|
+
@device = Launchpad::Device.new
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'closes input/output and raise NoInputAllowedError/NoOutputAllowedError on subsequent read/write accesses' do
|
149
|
+
@input.expects(:close)
|
150
|
+
@output.expects(:close)
|
151
|
+
@device.close
|
152
|
+
assert_raises Launchpad::NoInputAllowedError do
|
153
|
+
@device.read_pending_actions
|
154
|
+
end
|
155
|
+
assert_raises Launchpad::NoOutputAllowedError do
|
156
|
+
@device.change(:session)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
describe '#closed?' do
|
165
|
+
|
166
|
+
it 'returns true when neither input nor output are there' do
|
167
|
+
assert Launchpad::Device.new(:input => false, :output => false).closed?
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'returns false when initialized with input' do
|
171
|
+
assert !Launchpad::Device.new(:input => true, :output => false).closed?
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'returns false when initialized with output' do
|
175
|
+
assert !Launchpad::Device.new(:input => false, :output => true).closed?
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'returns false when initialized with both but true after calling close' do
|
179
|
+
d = Launchpad::Device.new
|
180
|
+
assert !d.closed?
|
181
|
+
d.close
|
182
|
+
assert d.closed?
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
{
|
188
|
+
:reset => [0xB0, 0x00, 0x00],
|
189
|
+
:flashing_on => [0xB0, 0x00, 0x20],
|
190
|
+
:flashing_off => [0xB0, 0x00, 0x21],
|
191
|
+
:flashing_auto => [0xB0, 0x00, 0x28]
|
192
|
+
}.each do |method, codes|
|
193
|
+
describe "##{method}" do
|
194
|
+
|
195
|
+
it 'raises NoOutputAllowedError when not initialized with output' do
|
196
|
+
assert_raises Launchpad::NoOutputAllowedError do
|
197
|
+
Launchpad::Device.new(:output => false).send(method)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
it "sends #{codes.inspect}" do
|
202
|
+
d = Launchpad::Device.new
|
203
|
+
expects_output(d, *codes)
|
204
|
+
d.send(method)
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe '#test_leds' do
|
211
|
+
|
212
|
+
it 'raises NoOutputAllowedError when not initialized with output' do
|
213
|
+
assert_raises Launchpad::NoOutputAllowedError do
|
214
|
+
Launchpad::Device.new(:output => false).test_leds
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe 'initialized with output' do
|
219
|
+
|
220
|
+
before do
|
221
|
+
@device = Launchpad::Device.new(:input => false)
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'returns nil' do
|
225
|
+
assert_nil @device.test_leds
|
226
|
+
end
|
227
|
+
|
228
|
+
COLORS.merge(nil => 3).each do |name, value|
|
229
|
+
if value == 0
|
230
|
+
it "sends 0xB0, 0x00, 0x00 when given #{name}" do
|
231
|
+
expects_output(@device, 0xB0, 0x00, 0x00)
|
232
|
+
@device.test_leds(value)
|
233
|
+
end
|
234
|
+
else
|
235
|
+
it "sends 0xB0, 0x00, 0x7C + #{value} when given #{name}" do
|
236
|
+
d = Launchpad::Device.new
|
237
|
+
expects_output(@device, 0xB0, 0x00, 0x7C + value)
|
238
|
+
value.nil? ? @device.test_leds : @device.test_leds(value)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
describe '#change' do
|
248
|
+
|
249
|
+
it 'raises NoOutputAllowedError when not initialized with output' do
|
250
|
+
assert_raises Launchpad::NoOutputAllowedError do
|
251
|
+
Launchpad::Device.new(:output => false).change(:up)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
describe 'initialized with output' do
|
256
|
+
|
257
|
+
before do
|
258
|
+
@device = Launchpad::Device.new(:input => false)
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'returns nil' do
|
262
|
+
assert_nil @device.change(:up)
|
263
|
+
end
|
264
|
+
|
265
|
+
describe 'control buttons' do
|
266
|
+
CONTROL_BUTTONS.each do |type, value|
|
267
|
+
it "sends 0xB0, #{value}, 12 when given #{type}" do
|
268
|
+
expects_output(@device, 0xB0, value, 12)
|
269
|
+
@device.change(type)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe 'scene buttons' do
|
275
|
+
SCENE_BUTTONS.each do |type, value|
|
276
|
+
it "sends 0x90, #{value}, 12 when given #{type}" do
|
277
|
+
expects_output(@device, 0x90, value, 12)
|
278
|
+
@device.change(type)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
describe 'grid buttons' do
|
284
|
+
8.times do |x|
|
285
|
+
8.times do |y|
|
286
|
+
it "sends 0x90, #{16 * y + x}, 12 when given :grid, :x => #{x}, :y => #{y}" do
|
287
|
+
expects_output(@device, 0x90, 16 * y + x, 12)
|
288
|
+
@device.change(:grid, :x => x, :y => y)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'raises NoValidGridCoordinatesError if x is not specified' do
|
294
|
+
assert_raises Launchpad::NoValidGridCoordinatesError do
|
295
|
+
@device.change(:grid, :y => 1)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'raises NoValidGridCoordinatesError if x is below 0' do
|
300
|
+
assert_raises Launchpad::NoValidGridCoordinatesError do
|
301
|
+
@device.change(:grid, :x => -1, :y => 1)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'raises NoValidGridCoordinatesError if x is above 7' do
|
306
|
+
assert_raises Launchpad::NoValidGridCoordinatesError do
|
307
|
+
@device.change(:grid, :x => 8, :y => 1)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
it 'raises NoValidGridCoordinatesError if y is not specified' do
|
312
|
+
assert_raises Launchpad::NoValidGridCoordinatesError do
|
313
|
+
@device.change(:grid, :x => 1)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'raises NoValidGridCoordinatesError if y is below 0' do
|
318
|
+
assert_raises Launchpad::NoValidGridCoordinatesError do
|
319
|
+
@device.change(:grid, :x => 1, :y => -1)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
it 'raises NoValidGridCoordinatesError if y is above 7' do
|
324
|
+
assert_raises Launchpad::NoValidGridCoordinatesError do
|
325
|
+
@device.change(:grid, :x => 1, :y => 8)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
describe 'colors' do
|
332
|
+
COLORS.each do |red_key, red_value|
|
333
|
+
COLORS.each do |green_key, green_value|
|
334
|
+
it "sends 0x90, 0, #{16 * green_value + red_value + 12} when given :red => #{red_key}, :green => #{green_key}" do
|
335
|
+
expects_output(@device, 0x90, 0, 16 * green_value + red_value + 12)
|
336
|
+
@device.change(:grid, :x => 0, :y => 0, :red => red_key, :green => green_key)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
it 'raises NoValidBrightnessError if red is below 0' do
|
342
|
+
assert_raises Launchpad::NoValidBrightnessError do
|
343
|
+
@device.change(:grid, :x => 0, :y => 0, :red => -1)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
it 'raises NoValidBrightnessError if red is above 3' do
|
348
|
+
assert_raises Launchpad::NoValidBrightnessError do
|
349
|
+
@device.change(:grid, :x => 0, :y => 0, :red => 4)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'raises NoValidBrightnessError if red is an unknown symbol' do
|
354
|
+
assert_raises Launchpad::NoValidBrightnessError do
|
355
|
+
@device.change(:grid, :x => 0, :y => 0, :red => :unknown)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
it 'raises NoValidBrightnessError if green is below 0' do
|
360
|
+
assert_raises Launchpad::NoValidBrightnessError do
|
361
|
+
@device.change(:grid, :x => 0, :y => 0, :green => -1)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
it 'raises NoValidBrightnessError if green is above 3' do
|
366
|
+
assert_raises Launchpad::NoValidBrightnessError do
|
367
|
+
@device.change(:grid, :x => 0, :y => 0, :green => 4)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
it 'raises NoValidBrightnessError if green is an unknown symbol' do
|
372
|
+
assert_raises Launchpad::NoValidBrightnessError do
|
373
|
+
@device.change(:grid, :x => 0, :y => 0, :green => :unknown)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
end
|
378
|
+
|
379
|
+
describe 'mode' do
|
380
|
+
|
381
|
+
it 'sends color + 12 when nothing given' do
|
382
|
+
expects_output(@device, 0x90, 0, 12)
|
383
|
+
@device.change(:grid, :x => 0, :y => 0, :red => 0, :green => 0)
|
384
|
+
end
|
385
|
+
|
386
|
+
it 'sends color + 12 when given :normal' do
|
387
|
+
expects_output(@device, 0x90, 0, 12)
|
388
|
+
@device.change(:grid, :x => 0, :y => 0, :red => 0, :green => 0, :mode => :normal)
|
389
|
+
end
|
390
|
+
|
391
|
+
it 'sends color + 8 when given :flashing' do
|
392
|
+
expects_output(@device, 0x90, 0, 8)
|
393
|
+
@device.change(:grid, :x => 0, :y => 0, :red => 0, :green => 0, :mode => :flashing)
|
394
|
+
end
|
395
|
+
|
396
|
+
it 'sends color when given :buffering' do
|
397
|
+
expects_output(@device, 0x90, 0, 0)
|
398
|
+
@device.change(:grid, :x => 0, :y => 0, :red => 0, :green => 0, :mode => :buffering)
|
399
|
+
end
|
400
|
+
|
401
|
+
end
|
402
|
+
|
403
|
+
end
|
404
|
+
|
405
|
+
end
|
406
|
+
|
407
|
+
describe '#change_all' do
|
408
|
+
|
409
|
+
it 'raises NoOutputAllowedError when not initialized with output' do
|
410
|
+
assert_raises Launchpad::NoOutputAllowedError do
|
411
|
+
Launchpad::Device.new(:output => false).change_all
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
describe 'initialized with output' do
|
416
|
+
|
417
|
+
before do
|
418
|
+
@device = Launchpad::Device.new(:input => false)
|
419
|
+
end
|
420
|
+
|
421
|
+
it 'returns nil' do
|
422
|
+
assert_nil @device.change_all([0])
|
423
|
+
end
|
424
|
+
|
425
|
+
it 'fills colors with 0, set grid layout to XY and flush colors' do
|
426
|
+
expects_output(@device, 0xB0, 0, 0x01)
|
427
|
+
expects_output(@device, *([[0x92, 17, 17]] * 20 + [[0x92, 12, 12]] * 20))
|
428
|
+
@device.change_all([5] * 40)
|
429
|
+
end
|
430
|
+
|
431
|
+
it 'cuts off exceeding colors, set grid layout to XY and flush colors' do
|
432
|
+
expects_output(@device, 0xB0, 0, 0x01)
|
433
|
+
expects_output(@device, *([[0x92, 17, 17]] * 40))
|
434
|
+
@device.change_all([5] * 100)
|
435
|
+
end
|
436
|
+
|
437
|
+
end
|
438
|
+
|
439
|
+
end
|
440
|
+
|
441
|
+
describe '#buffering_mode' do
|
442
|
+
|
443
|
+
it 'raises NoOutputAllowedError when not initialized with output' do
|
444
|
+
assert_raises Launchpad::NoOutputAllowedError do
|
445
|
+
Launchpad::Device.new(:output => false).buffering_mode
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
{
|
450
|
+
nil => [0xB0, 0x00, 0x20],
|
451
|
+
{} => [0xB0, 0x00, 0x20],
|
452
|
+
{:display_buffer => 1} => [0xB0, 0x00, 0x21],
|
453
|
+
{:update_buffer => 1} => [0xB0, 0x00, 0x24],
|
454
|
+
{:copy => true} => [0xB0, 0x00, 0x30],
|
455
|
+
{:flashing => true} => [0xB0, 0x00, 0x28],
|
456
|
+
{
|
457
|
+
:display_buffer => 1,
|
458
|
+
:update_buffer => 1,
|
459
|
+
:copy => true,
|
460
|
+
:flashing => true
|
461
|
+
} => [0xB0, 0x00, 0x3D]
|
462
|
+
}.each do |opts, codes|
|
463
|
+
it "sends #{codes.inspect} when called with #{opts.inspect}" do
|
464
|
+
d = Launchpad::Device.new
|
465
|
+
expects_output(d, *codes)
|
466
|
+
d.buffering_mode(opts)
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
end
|
471
|
+
|
472
|
+
describe '#read_pending_actions' do
|
473
|
+
|
474
|
+
it 'raises NoInputAllowedError when not initialized with input' do
|
475
|
+
assert_raises Launchpad::NoInputAllowedError do
|
476
|
+
Launchpad::Device.new(:input => false).read_pending_actions
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
describe 'initialized with input' do
|
481
|
+
|
482
|
+
before do
|
483
|
+
@device = Launchpad::Device.new(:output => false)
|
484
|
+
end
|
485
|
+
|
486
|
+
describe 'control buttons' do
|
487
|
+
CONTROL_BUTTONS.each do |type, value|
|
488
|
+
STATES.each do |state, velocity|
|
489
|
+
it "builds proper action for control button #{type}, #{state}" do
|
490
|
+
stub_input(@device, {:timestamp => 0, :message => [0xB0, value, velocity]})
|
491
|
+
assert_equal [{:timestamp => 0, :state => state, :type => type}], @device.read_pending_actions
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
describe 'scene buttons' do
|
498
|
+
SCENE_BUTTONS.each do |type, value|
|
499
|
+
STATES.each do |state, velocity|
|
500
|
+
it "builds proper action for scene button #{type}, #{state}" do
|
501
|
+
stub_input(@device, {:timestamp => 0, :message => [0x90, value, velocity]})
|
502
|
+
assert_equal [{:timestamp => 0, :state => state, :type => type}], @device.read_pending_actions
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
describe '#grid buttons' do
|
509
|
+
8.times do |x|
|
510
|
+
8.times do |y|
|
511
|
+
STATES.each do |state, velocity|
|
512
|
+
it "builds proper action for grid button #{x},#{y}, #{state}" do
|
513
|
+
stub_input(@device, {:timestamp => 0, :message => [0x90, 16 * y + x, velocity]})
|
514
|
+
assert_equal [{:timestamp => 0, :state => state, :type => :grid, :x => x, :y => y}], @device.read_pending_actions
|
515
|
+
end
|
516
|
+
end
|
517
|
+
end
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
it 'builds proper actions for multiple pending actions' do
|
522
|
+
stub_input(@device, {:timestamp => 1, :message => [0x90, 0, 127]}, {:timestamp => 2, :message => [0xB0, 0x68, 0]})
|
523
|
+
assert_equal [{:timestamp => 1, :state => :down, :type => :grid, :x => 0, :y => 0}, {:timestamp => 2, :state => :up, :type => :up}], @device.read_pending_actions
|
524
|
+
end
|
525
|
+
|
526
|
+
end
|
527
|
+
|
528
|
+
end
|
529
|
+
|
530
|
+
end
|