surface_master 0.2.1 → 0.4.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 +4 -4
- data/CHANGELOG.md +17 -0
- data/docs/Numark_Orbit_QuickRef.md +5 -0
- data/examples/{launchpad_testbed.rb → launchpad_playground.rb} +61 -21
- data/examples/{orbit_testbed.rb → orbit_device.rb} +0 -6
- data/examples/orbit_interaction.rb +71 -0
- data/examples/orbit_playground.rb +135 -0
- data/examples/{monitor.rb → system_monitor.rb} +2 -2
- data/lib/surface_master/device.rb +5 -2
- data/lib/surface_master/interaction.rb +15 -53
- data/lib/surface_master/launchpad/device.rb +1 -1
- data/lib/surface_master/orbit/device.rb +146 -98
- data/lib/surface_master/orbit/interaction.rb +62 -12
- data/lib/surface_master/orbit/midi_codes.rb +19 -19
- data/lib/surface_master/version.rb +1 -1
- data/lib/surface_master.rb +1 -0
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2b72c0db5f531114165b1bf813b7723de8daa62
|
4
|
+
data.tar.gz: 816e060bcc90c233e3fe80f994295a0d04222334
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d7e478d03cb7f4bfbb8dd29c527c374d4ad798054b04a3a9c6326d4ed42e56cd971c2830642715df5e9d851de8631342f6d02f215b4cd6c0394ec83e6af7206
|
7
|
+
data.tar.gz: 3252e00acc136cf55dee085e073266f65f7cdb63494f84ddcead0fb6bac150e73598fa9e922fc8aefdc58ec6cb9e5e7a608b6c8a4798e2b0141bfc66b8ca0147
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# Changes
|
2
2
|
|
3
|
+
## v0.4.0
|
4
|
+
|
5
|
+
* Jettison threaded input handling.
|
6
|
+
* More Numark Orbit handling (output is still a problem).
|
7
|
+
|
8
|
+
|
9
|
+
## v0.3.0
|
10
|
+
|
11
|
+
* __BREAKING CHANGE__: Change Orbit interface to more closely follow conventions established with Novation Launchpad driver.
|
12
|
+
* Add preliminary `Interaction` class for Numark Orbit.
|
13
|
+
* See `examples/orbit_interaction.rb`.
|
14
|
+
* Fix exclusive binding in `Interaction` to not nuke coarser bindings entirely.
|
15
|
+
* Rename `examples/orbit_testbed.rb` to `examples/orbit_device.rb`.
|
16
|
+
* Rename `examples/monitor.rb` to `examples/system_monitor.rb`.
|
17
|
+
* Rename `examples/launchpad_testbed.rb` to `examples/launchpad_playground.rb`.
|
18
|
+
|
19
|
+
|
3
20
|
## v0.2.1
|
4
21
|
|
5
22
|
* Missed a file rename.
|
@@ -1,5 +1,10 @@
|
|
1
1
|
# Numark Orbit Quick Reference
|
2
2
|
|
3
|
+
## Mappings
|
4
|
+
|
5
|
+
Use the file `mappings/Orbit_Preset.json`, and be sure to hit `Send` several times, as it sometimes does not actually take.
|
6
|
+
|
7
|
+
|
3
8
|
## Low Battery Indicator
|
4
9
|
|
5
10
|
Any blinking `Pad Bank Selectors` indicates the battery is low.
|
@@ -1,32 +1,52 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file provides an example of using the Novation Launchpad Mark 2 using
|
4
|
+
# event handlers to respond to buttons, and batch LED updates to update every
|
5
|
+
# button on the board quite rapidly.
|
6
|
+
#
|
7
|
+
# Controls:
|
8
|
+
#
|
9
|
+
# * Press any grid button and observe that the color changes to white while it's held. Any number
|
10
|
+
# of pads may be pressed simultaneously.
|
11
|
+
# * The control buttons on the right will each channel-flip a quadrant of the board. They act as
|
12
|
+
# toggles, to pressing them again undoes the effect.
|
13
|
+
# * The `Mixer` button will terminate the simulation.
|
14
|
+
#
|
15
|
+
# Effects:
|
16
|
+
#
|
17
|
+
# * The color of a pad is applied in layers additively, with values clamped to white at the end:
|
18
|
+
# 1. The base color for each grid pad is defined by its position with the color getting more
|
19
|
+
# green on the X axis, and more blue along the Y axis. See `SCALE` for how steep the change
|
20
|
+
# is.
|
21
|
+
# 2. Each quadrant adds in a specific color (see `QUADRANTS` below).
|
22
|
+
# 3. The red/green/blue channels may be rotated for any given quadrant, if the relevant toggle
|
23
|
+
# is active.
|
24
|
+
# 4. A sine-wave is applied to one or more channels (see `TIME_SCALE` for the speed of the sine
|
25
|
+
# wave).
|
26
|
+
# 5. If a particular pad is pressed, the color is set to white.
|
27
|
+
#
|
28
|
+
# TODO: Input handling seems to interfere with frame rendering, as our FPS seems to "jump" to
|
29
|
+
# TODO: insane values when a button is pressed.
|
30
|
+
#
|
2
31
|
require "rubygems"
|
3
32
|
require "bundler/setup"
|
4
33
|
Bundler.require(:default, :development)
|
5
34
|
|
6
35
|
require "surface_master"
|
7
36
|
|
8
|
-
#
|
9
|
-
|
10
|
-
# Set all: F0h 00h 20h 29h 02h 18h 0Eh <Colour> F7h
|
11
|
-
# Set row: F0h 00h 20h 29h 02h 18h 0Dh <Row> <Colour> F7h
|
12
|
-
# Set col: F0h 00h 20h 29h 02h 18h 0Ch <Column> <Colour> F7h
|
13
|
-
|
14
|
-
# Configuration
|
15
|
-
SCALE = 2
|
37
|
+
# Animation Configuration
|
38
|
+
SCALE = [2, 2]
|
16
39
|
TIME_SCALE = 4.0
|
40
|
+
TIME_MASK = { red: 0x3F, green: 0x00, blue: 0x00 }
|
41
|
+
QUADRANTS = [[{ red: 0x2F, green: 0x00, blue: 0x00 }, { red: 0x00, green: 0x2F, blue: 0x00 }],
|
42
|
+
[{ red: 0x00, green: 0x00, blue: 0x2F }, { red: 0x2F, green: 0x2F, blue: 0x00 }]]
|
17
43
|
|
18
44
|
# State
|
19
|
-
QUADRANTS = [
|
20
|
-
[{ red: 0x2F, green: 0x00, blue: 0x00 }, { red: 0x00, green: 0x2F, blue: 0x00 }],
|
21
|
-
[{ red: 0x00, green: 0x00, blue: 0x2F }, { red: 0x2F, green: 0x2F, blue: 0x00 }],
|
22
|
-
]
|
23
45
|
FLIPPED = [[false, false], [false, false]]
|
24
46
|
PRESSED = (0..7).map { |_x| (0..7).map { |_y| false } }
|
25
47
|
NOW = [Time.now.to_f]
|
26
48
|
|
27
49
|
# Helpers
|
28
|
-
CC = %i(up down left right session user1 user2 scene1 scene2 scene3 scene4 scene5 scene6 scene7
|
29
|
-
scene8)
|
30
50
|
GRID = (0..7).map { |x| (0..7).map { |y| { grid: [x, y] } } }.flatten
|
31
51
|
WHITE = { red: 0x3F, green: 0x3F, blue: 0x3F }
|
32
52
|
BLACK = { red: 0x00, green: 0x00, blue: 0x00 }
|
@@ -41,15 +61,15 @@ end
|
|
41
61
|
|
42
62
|
def positional_color(x, y)
|
43
63
|
{ red: 0x00,
|
44
|
-
green: (x * SCALE),
|
45
|
-
blue: (y * SCALE) }
|
64
|
+
green: (x * SCALE[0]),
|
65
|
+
blue: (y * SCALE[1]) }
|
46
66
|
end
|
47
67
|
|
48
68
|
def temporal_color(_x, _y)
|
49
69
|
s_t = (Math.sin(NOW[0] * TIME_SCALE) * 0.5) + 0.5
|
50
|
-
{ red: (s_t *
|
51
|
-
green:
|
52
|
-
blue:
|
70
|
+
{ red: (s_t * TIME_MASK[:red]).round,
|
71
|
+
green: (s_t * TIME_MASK[:green]).round,
|
72
|
+
blue: (s_t * TIME_MASK[:blue]).round }
|
53
73
|
end
|
54
74
|
|
55
75
|
def clamp_color(color)
|
@@ -155,18 +175,38 @@ interaction.response_to(:mixer, :down) do |_interaction, _action|
|
|
155
175
|
interaction.stop
|
156
176
|
end
|
157
177
|
interaction.change(red: 0x03, green: 0x00, blue: 0x00, cc: :mixer)
|
158
|
-
BTN_COL = { red: 0x03, green: 0x03, blue: 0x03
|
178
|
+
BTN_COL = { red: 0x03, green: 0x03, blue: 0x03 }
|
159
179
|
interaction.changes(%i(scene1 scene2 scene3 scene4).map { |cc| BTN_COL.merge(cc: cc) })
|
160
180
|
|
161
181
|
init_board(interaction)
|
162
182
|
input_thread = Thread.new do
|
163
183
|
interaction.start
|
164
184
|
end
|
165
|
-
|
185
|
+
cumulative_time = 0.0
|
186
|
+
frame_times = []
|
187
|
+
min_frame_time = Float::INFINITY
|
188
|
+
max_frame_time = 0.0
|
189
|
+
animation_thread = Thread.new do
|
166
190
|
loop do
|
167
191
|
begin
|
168
192
|
NOW[0] = Time.now.to_f
|
169
193
|
init_board(interaction)
|
194
|
+
elapsed = Time.now.to_f - NOW[0]
|
195
|
+
cumulative_time += elapsed
|
196
|
+
min_frame_time = elapsed if elapsed < min_frame_time
|
197
|
+
max_frame_time = elapsed if elapsed > max_frame_time
|
198
|
+
frame_times.push(elapsed)
|
199
|
+
frame_times.shift if frame_times.length > 60
|
200
|
+
|
201
|
+
# Show avg FPS once per second.
|
202
|
+
if cumulative_time >= 1.0
|
203
|
+
cumulative_time = 0.0
|
204
|
+
avg_frame_time = frame_times.inject(0.0) { |a, e| a + e } / frame_times.length
|
205
|
+
avg_rate = (1.0 / avg_frame_time)
|
206
|
+
max_rate = (1.0 / min_frame_time)
|
207
|
+
min_rate = (1.0 / max_frame_time)
|
208
|
+
puts "FPS: Average = %0.1f, Min = %0.1f, Max = %0.1f" % [avg_rate, min_rate, max_rate]
|
209
|
+
end
|
170
210
|
rescue StandardError => e
|
171
211
|
puts e.inspect
|
172
212
|
puts e.backtrace.join("\n")
|
@@ -1,16 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
# require "bignum"
|
3
2
|
require "rubygems"
|
4
3
|
require "bundler/setup"
|
5
4
|
Bundler.require(:default, :development)
|
6
5
|
|
7
6
|
require "surface_master"
|
8
7
|
|
9
|
-
# Monkey-patching to make debugging easier.
|
10
|
-
class Fixnum
|
11
|
-
def to_hex; "%02X" % self; end
|
12
|
-
end
|
13
|
-
|
14
8
|
SurfaceMaster.init!
|
15
9
|
device = SurfaceMaster::Orbit::Device.new
|
16
10
|
loop do
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "rubygems"
|
3
|
+
require "bundler/setup"
|
4
|
+
Bundler.require(:default, :development)
|
5
|
+
|
6
|
+
require "surface_master"
|
7
|
+
|
8
|
+
SurfaceMaster.init!
|
9
|
+
interaction = SurfaceMaster::Orbit::Interaction.new
|
10
|
+
interaction.response_to(:pad, :down) do |_inter, action|
|
11
|
+
puts "PAD DOWN: #{action.inspect}"
|
12
|
+
end
|
13
|
+
interaction.response_to(:pad, :up) do |_inter, action|
|
14
|
+
puts "PAD UP: #{action.inspect}"
|
15
|
+
end
|
16
|
+
interaction.response_to(:pad, :up, button: 1..4) do |_inter, action|
|
17
|
+
puts "LEFT COLUMN PAD UP: #{action.inspect}"
|
18
|
+
end
|
19
|
+
interaction.response_to(:pad, :up, button: 5..8, bank: 4, exclusive: true) do |_inter, action|
|
20
|
+
puts "SECOND-FROM-LEFT COLUMN PAD UP: #{action.inspect}"
|
21
|
+
end
|
22
|
+
|
23
|
+
interaction.response_to(:vknob, :update) do |_inter, action|
|
24
|
+
puts "ANY KNOB, ANY BANK (EXCEPT 2) TURNED: #{action.inspect}"
|
25
|
+
end
|
26
|
+
interaction.response_to(:vknob, :update, vknob: 3) do |_inter, action|
|
27
|
+
puts "KNOB 3, ANY BANK (EXCEPT 2) TURNED: #{action.inspect}"
|
28
|
+
end
|
29
|
+
interaction.response_to(:vknob, :update, bank: 2, exclusive: true) do |_inter, action|
|
30
|
+
puts "ANY KNOB, BANK 2 TURNED: #{action.inspect}"
|
31
|
+
end
|
32
|
+
interaction.response_to(:vknob, :update, bank: 4, vknob: 1) do |_inter, action|
|
33
|
+
puts "KNOB 1, BANK 4 TURNED: #{action.inspect}"
|
34
|
+
end
|
35
|
+
|
36
|
+
interaction.response_to(:accelerometer, :tilt) do |_inter, action|
|
37
|
+
puts "ANY AXIS TILT: #{action.inspect}"
|
38
|
+
end
|
39
|
+
interaction.response_to(:accelerometer, :tilt, axis: :x) do |_inter, action|
|
40
|
+
puts "X-AXIS TILT: #{action.inspect}"
|
41
|
+
end
|
42
|
+
|
43
|
+
interaction.response_to(:vknobs, :down) do |_inter, action|
|
44
|
+
puts "ANY VKNOB SELECTOR DOWN: #{action.inspect}"
|
45
|
+
end
|
46
|
+
interaction.response_to(:vknobs, :down, button: 2) do |_inter, action|
|
47
|
+
puts "VKNOB 2 SELECTOR DOWN: #{action.inspect}"
|
48
|
+
end
|
49
|
+
|
50
|
+
interaction.response_to(:banks, :down) do |_inter, action|
|
51
|
+
puts "ANY BANK SELECTOR DOWN: #{action.inspect}"
|
52
|
+
end
|
53
|
+
interaction.response_to(:banks, :down, button: 3) do |_inter, action|
|
54
|
+
puts "BANK 3 SELECTOR DOWN: #{action.inspect}"
|
55
|
+
end
|
56
|
+
|
57
|
+
interaction.response_to(:shoulder, :down) do |_inter, action|
|
58
|
+
puts "ANY SHOULDER DOWN: #{action.inspect}"
|
59
|
+
end
|
60
|
+
interaction.response_to(:shoulder, :up) do |_inter, action|
|
61
|
+
puts "ANY SHOULDER UP: #{action.inspect}"
|
62
|
+
end
|
63
|
+
interaction.response_to(:shoulder, :down, button: :left) do |_inter, action|
|
64
|
+
puts "LEFT SHOULDER DOWN: #{action.inspect}"
|
65
|
+
end
|
66
|
+
interaction.response_to(:shoulder, :up, button: :left) do |_inter, action|
|
67
|
+
puts "LEFT SHOULDER UP: #{action.inspect}"
|
68
|
+
end
|
69
|
+
|
70
|
+
puts "Starting input loop..."
|
71
|
+
interaction.start
|
@@ -0,0 +1,135 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Cycle Numark Orbit colors through a wheel.
|
3
|
+
#
|
4
|
+
# IMPORTANT: Set `MODE` below to `:wired`, or `:wireless`, as appropriate to
|
5
|
+
# IMPORTANT: how your Numark Orbit is connected!
|
6
|
+
#
|
7
|
+
# NOTE: If the lights do not blank out when this starts, your device is in a
|
8
|
+
# NOTE: bad state. Push a config to it from the Numark Orbit Editor. If that
|
9
|
+
# NOTE: doesn't work, power-cycle it and try again!
|
10
|
+
require "rubygems"
|
11
|
+
require "bundler/setup"
|
12
|
+
Bundler.require(:default, :development)
|
13
|
+
|
14
|
+
require "surface_master"
|
15
|
+
|
16
|
+
device = SurfaceMaster::Orbit::Device.new
|
17
|
+
# The device seems to be notably less able to accept updates over wireless, and
|
18
|
+
# perhaps because CoreMIDI has no backpressure, we can easily wind up hosed.
|
19
|
+
# Before settling on new values here, run the process for *several full minutes*
|
20
|
+
# and make sure the device continues accepting updates at the end!
|
21
|
+
#
|
22
|
+
# TODO: More thorough stress testing.
|
23
|
+
#
|
24
|
+
# TODO: Can we determine if the connection is wired/wireless automatically?
|
25
|
+
#
|
26
|
+
# TODO: Can we safely get input simultaneously?
|
27
|
+
MODE = :wired
|
28
|
+
|
29
|
+
CONFIGS = { wireless: { delay: 0.75, offset: 0x03, use_read: true, read_delay: 0.1 },
|
30
|
+
wired: { delay: 0.1, offset: 0x01, use_read: false, read_delay: 0 } }
|
31
|
+
MAPPINGS = [0x03, 0x01, 0x70,
|
32
|
+
|
33
|
+
0x00, 0x00, 0x00,
|
34
|
+
0x00, 0x04, 0x04,
|
35
|
+
0x00, 0x08, 0x08,
|
36
|
+
0x00, 0x0C, 0x0C,
|
37
|
+
0x00, 0x01, 0x01,
|
38
|
+
0x00, 0x05, 0x05,
|
39
|
+
0x00, 0x09, 0x09,
|
40
|
+
0x00, 0x0D, 0x0D,
|
41
|
+
0x00, 0x02, 0x02,
|
42
|
+
0x00, 0x06, 0x06,
|
43
|
+
0x00, 0x0A, 0x0A,
|
44
|
+
0x00, 0x0E, 0x0E,
|
45
|
+
0x00, 0x03, 0x03,
|
46
|
+
0x00, 0x07, 0x07,
|
47
|
+
0x00, 0x0B, 0x0B,
|
48
|
+
0x00, 0x0F, 0x0F,
|
49
|
+
0x01, 0x00, 0x10,
|
50
|
+
0x01, 0x04, 0x14,
|
51
|
+
0x01, 0x08, 0x18,
|
52
|
+
0x01, 0x0C, 0x1C,
|
53
|
+
0x01, 0x01, 0x11,
|
54
|
+
0x01, 0x05, 0x15,
|
55
|
+
0x01, 0x09, 0x19,
|
56
|
+
0x01, 0x0D, 0x1D,
|
57
|
+
0x01, 0x02, 0x12,
|
58
|
+
0x01, 0x06, 0x16,
|
59
|
+
0x01, 0x0A, 0x1A,
|
60
|
+
0x01, 0x0E, 0x1E,
|
61
|
+
0x01, 0x03, 0x13,
|
62
|
+
0x01, 0x07, 0x17,
|
63
|
+
0x01, 0x0B, 0x1B,
|
64
|
+
0x01, 0x0F, 0x1F,
|
65
|
+
0x02, 0x00, 0x20,
|
66
|
+
0x02, 0x04, 0x24,
|
67
|
+
0x02, 0x08, 0x28,
|
68
|
+
0x02, 0x0C, 0x2C,
|
69
|
+
0x02, 0x01, 0x21,
|
70
|
+
0x02, 0x05, 0x25,
|
71
|
+
0x02, 0x09, 0x29,
|
72
|
+
0x02, 0x0D, 0x2D,
|
73
|
+
0x02, 0x02, 0x22,
|
74
|
+
0x02, 0x06, 0x26,
|
75
|
+
0x02, 0x0A, 0x2A,
|
76
|
+
0x02, 0x0E, 0x2E,
|
77
|
+
0x02, 0x03, 0x23,
|
78
|
+
0x02, 0x07, 0x27,
|
79
|
+
0x02, 0x0B, 0x2B,
|
80
|
+
0x02, 0x0F, 0x2F,
|
81
|
+
0x03, 0x00, 0x30,
|
82
|
+
0x03, 0x04, 0x34,
|
83
|
+
0x03, 0x08, 0x38,
|
84
|
+
0x03, 0x0C, 0x3C,
|
85
|
+
0x03, 0x01, 0x31,
|
86
|
+
0x03, 0x05, 0x35,
|
87
|
+
0x03, 0x09, 0x39,
|
88
|
+
0x03, 0x0D, 0x3D,
|
89
|
+
0x03, 0x02, 0x32,
|
90
|
+
0x03, 0x06, 0x36,
|
91
|
+
0x03, 0x0A, 0x3A,
|
92
|
+
0x03, 0x0E, 0x3E,
|
93
|
+
0x03, 0x03, 0x33,
|
94
|
+
0x03, 0x07, 0x37,
|
95
|
+
0x03, 0x0B, 0x3B,
|
96
|
+
0x03, 0x0F, 0x3F,
|
97
|
+
|
98
|
+
0x00, 0x00, 0x01,
|
99
|
+
0x00, 0x02, 0x00,
|
100
|
+
0x03, 0x00, 0x00,
|
101
|
+
0x01, 0x01, 0x01,
|
102
|
+
0x02, 0x01, 0x03,
|
103
|
+
0x01, 0x00, 0x02,
|
104
|
+
0x01, 0x02, 0x02,
|
105
|
+
0x02, 0x03, 0x02,
|
106
|
+
0x00, 0x03, 0x01,
|
107
|
+
0x03, 0x02, 0x03,
|
108
|
+
0x03, 0x03, 0x0C,
|
109
|
+
0x00, 0x0D, 0x00,
|
110
|
+
0x0C, 0x00, 0x0D,
|
111
|
+
0x00, 0x0C, 0x00,
|
112
|
+
0x0D, 0x00, 0x0C,
|
113
|
+
0x00, 0x0D, 0x00]
|
114
|
+
READ_STATE = [0x01, 0x00, 0x00]
|
115
|
+
|
116
|
+
delay = CONFIGS[MODE][:delay]
|
117
|
+
offset = CONFIGS[MODE][:offset]
|
118
|
+
use_read = CONFIGS[MODE][:use_read]
|
119
|
+
read_delay = CONFIGS[MODE][:read_delay]
|
120
|
+
|
121
|
+
sleep delay
|
122
|
+
puts "Starting..."
|
123
|
+
indices = (0..63).map { |n| 5 + (3 * n) }
|
124
|
+
loop do
|
125
|
+
indices.each do |i|
|
126
|
+
MAPPINGS[i] = (MAPPINGS[i] + offset) % 0x3F
|
127
|
+
end
|
128
|
+
device.sysex!(*MAPPINGS)
|
129
|
+
if use_read
|
130
|
+
sleep read_delay
|
131
|
+
device.sysex!(*READ_STATE)
|
132
|
+
end
|
133
|
+
printf "."
|
134
|
+
sleep delay
|
135
|
+
end
|
@@ -39,14 +39,14 @@ end
|
|
39
39
|
|
40
40
|
interaction = SurfaceMaster::Launchpad::Interaction.new
|
41
41
|
cpu_bar = Bar.new(interaction, 0, red: 0x3F, green: 0x00, blue: 0x00)
|
42
|
-
io_bar = Bar.new(interaction,
|
42
|
+
io_bar = Bar.new(interaction, 1, red: 0x00, green: 0x3F, blue: 0x00)
|
43
43
|
monitor = Thread.new do
|
44
44
|
loop do
|
45
45
|
fields = `iostat -c 2 disk0`.split(/\n/).last.strip.split(/\s+/)
|
46
46
|
cpu_pct = 100 - fields[-4].to_i
|
47
47
|
cpu_usage = ((cpu_pct / 100.0) * 8.0).round.to_i
|
48
48
|
|
49
|
-
disk_pct = (fields[2].to_f / 750.0) * 100.0
|
49
|
+
disk_pct = ((fields[2].to_f / 750.0) * 100.0).round.to_i
|
50
50
|
disk_usage = ((disk_pct / 100.0) * 8.0).round.to_i
|
51
51
|
|
52
52
|
puts "I/O=#{disk_pct}%, CPU=#{cpu_pct}%"
|
@@ -51,10 +51,13 @@ module SurfaceMaster
|
|
51
51
|
fail NoOutputAllowedError unless output_enabled?
|
52
52
|
msg = sysex_msg(payload)
|
53
53
|
logger.debug { "#{msg.length}: 0x#{msg.map { |b| '%02X' % b }.join(' ')}" }
|
54
|
-
@output.write_sysex(msg)
|
54
|
+
result = @output.write_sysex(msg)
|
55
|
+
if result != 0
|
56
|
+
puts "Sysex Error: #{Portmidi::PM_Map.Pm_GetErrorText(result)}"
|
57
|
+
end
|
58
|
+
result
|
55
59
|
end
|
56
60
|
|
57
|
-
|
58
61
|
def create_input_device(opts)
|
59
62
|
return nil unless opts[:input]
|
60
63
|
create_device(Portmidi.input_devices,
|
@@ -12,14 +12,11 @@ module SurfaceMaster
|
|
12
12
|
self.logger = opts[:logger]
|
13
13
|
logger.debug "Initializing #{self.class}##{object_id} with #{opts.inspect}"
|
14
14
|
|
15
|
-
@use_threads = opts[:use_threads] || true
|
16
15
|
@device = opts[:device] || @device_class.new(opts.merge(input: true,
|
17
16
|
output: true,
|
18
17
|
logger: opts[:logger]))
|
19
18
|
@latency = (opts[:latency] || 0.001).to_f.abs
|
20
19
|
@active = false
|
21
|
-
|
22
|
-
@action_threads = ThreadGroup.new
|
23
20
|
end
|
24
21
|
|
25
22
|
def change(opts); @device.change(opts); end
|
@@ -33,36 +30,29 @@ module SurfaceMaster
|
|
33
30
|
|
34
31
|
def closed?; @device.closed?; end
|
35
32
|
|
36
|
-
def start
|
33
|
+
def start
|
37
34
|
logger.debug "Starting #{self.class}##{object_id}"
|
38
35
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
36
|
+
@active = true
|
37
|
+
guard_input_and_reset_at_end! do
|
38
|
+
while @active
|
39
|
+
@device.read.each { |action| respond_to_action(action) }
|
40
|
+
sleep @latency if @latency && @latency > 0.0
|
41
|
+
end
|
42
|
+
end
|
44
43
|
end
|
45
44
|
|
46
45
|
def stop
|
47
46
|
logger.debug "Stopping #{self.class}##{object_id}"
|
48
47
|
@active = false
|
49
|
-
if @reader_thread
|
50
|
-
# run (resume from sleep) and wait for @reader_thread to end
|
51
|
-
@reader_thread.run if @reader_thread.alive?
|
52
|
-
@reader_thread.join
|
53
|
-
@reader_thread = nil
|
54
|
-
end
|
55
|
-
ensure
|
56
|
-
kill_action_threads!
|
57
|
-
nil
|
58
48
|
end
|
59
49
|
|
60
50
|
def response_to(types = :all, state = :both, opts = nil, &block)
|
61
51
|
logger.debug "Setting response to #{types.inspect} for state #{state.inspect} with"\
|
62
52
|
" #{opts.inspect}"
|
63
|
-
types
|
64
|
-
opts
|
65
|
-
no_response_to(types, state) if opts[:exclusive] == true
|
53
|
+
types = Array(types)
|
54
|
+
opts ||= {}
|
55
|
+
no_response_to(types, state, opts) if opts[:exclusive] == true
|
66
56
|
expand_states(state).each do |st|
|
67
57
|
add_response_for_state!(types, opts, st, block)
|
68
58
|
end
|
@@ -71,7 +61,8 @@ module SurfaceMaster
|
|
71
61
|
|
72
62
|
def no_response_to(types = nil, state = :both, opts = nil)
|
73
63
|
logger.debug "Removing response to #{types.inspect} for state #{state.inspect}"
|
74
|
-
types
|
64
|
+
types = Array(types)
|
65
|
+
opts ||= {}
|
75
66
|
expand_states(state).each do |st|
|
76
67
|
clear_responses_for_state!(types, opts, st)
|
77
68
|
end
|
@@ -84,15 +75,8 @@ module SurfaceMaster
|
|
84
75
|
|
85
76
|
protected
|
86
77
|
|
87
|
-
def
|
88
|
-
|
89
|
-
guard_input_and_reset_at_end! do
|
90
|
-
while @active
|
91
|
-
@device.read.each { |action| handle_action(action) }
|
92
|
-
sleep @latency if @latency && @latency > 0.0
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
78
|
+
def expand(list)
|
79
|
+
list.map { |ll| ll.respond_to?(:to_a) ? ll.to_a : ll }.flatten
|
96
80
|
end
|
97
81
|
|
98
82
|
def guard_input_and_reset_at_end!(&block)
|
@@ -107,17 +91,6 @@ module SurfaceMaster
|
|
107
91
|
@device.reset!
|
108
92
|
end
|
109
93
|
|
110
|
-
def kill_action_threads!
|
111
|
-
@action_threads.list.each do |thread|
|
112
|
-
begin
|
113
|
-
thread.kill
|
114
|
-
thread.join
|
115
|
-
rescue StandardException => e # TODO: RuntimeError, Exception, or this?
|
116
|
-
logger.error "Error when killing action thread: #{e.inspect}"
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
94
|
def add_response_for_state!(types, opts, state, block)
|
122
95
|
response_groups_for(types, opts, state) do |responses|
|
123
96
|
responses << block
|
@@ -138,17 +111,6 @@ module SurfaceMaster
|
|
138
111
|
|
139
112
|
def expand_states(state); Array(state == :both ? %i(down up) : state); end
|
140
113
|
|
141
|
-
def handle_action(action)
|
142
|
-
if @use_threads
|
143
|
-
action_thread = Thread.new(action) do |act|
|
144
|
-
respond_to_action(act)
|
145
|
-
end
|
146
|
-
@action_threads.add(action_thread)
|
147
|
-
else
|
148
|
-
respond_to_action(action)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
114
|
def responses
|
153
115
|
# TODO: Generalize for arbitrary actions...
|
154
116
|
@responses ||= Hash.new { |hash, key| hash[key] = { down: [], up: [] } }
|
@@ -11,11 +11,52 @@ module SurfaceMaster
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def reset!
|
14
|
-
#
|
15
|
-
|
16
|
-
|
14
|
+
# Skip Sysex begin, vendor header, command code, aaaaand sysex end --
|
15
|
+
# this will let us compare command vs. response payloads to determine
|
16
|
+
# if the state of the device is what we want. Of course, sometimes it
|
17
|
+
# lies, but we can't do much about that.
|
18
|
+
expected_state = MAPPINGS[6..-2]
|
19
|
+
sysex!(MAPPINGS)
|
20
|
+
sleep 0.1
|
21
|
+
sysex!(READ_STATE)
|
22
|
+
current_state = nil
|
23
|
+
started_at = Time.now.to_f
|
24
|
+
attempts = 1
|
25
|
+
loop do
|
26
|
+
# TODO: It appears that accessing `buffer` is HIGHLY unsafe! We may
|
27
|
+
# TODO: be OK if everyone's waiting on us to come back from this
|
28
|
+
# TODO: method before they begin clamoring for input, but that's just
|
29
|
+
# TODO: a guess right now.
|
30
|
+
if @input.buffer.length == 0
|
31
|
+
elapsed = Time.now.to_f - started_at
|
32
|
+
if elapsed > 4.0
|
33
|
+
logger.warn { "Timeout fetching state of Numark Orbit!" }
|
34
|
+
break
|
35
|
+
elsif elapsed > (1.0 * attempts)
|
36
|
+
logger.warn { "Asking for current state of Numark Orbit again!" }
|
37
|
+
attempts += 1
|
38
|
+
@output.puts(READ_STATE)
|
39
|
+
next
|
40
|
+
end
|
41
|
+
sleep 0.01
|
42
|
+
next
|
43
|
+
end
|
44
|
+
raw = @input.gets
|
45
|
+
current_state = raw.find { |ii| ii[:data][0] == 0xF0 }
|
46
|
+
break unless current_state.nil?
|
47
|
+
end
|
48
|
+
|
49
|
+
return unless current_state
|
50
|
+
|
51
|
+
current_state = current_state[:data][6..-2].dup
|
52
|
+
logger.debug { "Got state info from Numark Orbit!" }
|
53
|
+
if expected_state != current_state
|
54
|
+
logger.error { "UH OH! Numark Orbit state didn't match what we sent!" }
|
55
|
+
logger.error { "Expected: #{expected_state.inspect}" }
|
56
|
+
logger.error { "Got: #{current_state.inspect}" }
|
57
|
+
else
|
58
|
+
logger.debug { "Your Numark Orbit should be in the right state now." }
|
17
59
|
end
|
18
|
-
sysex!(0x01, 0x00, 0x00)
|
19
60
|
end
|
20
61
|
|
21
62
|
def read
|
@@ -26,133 +67,140 @@ module SurfaceMaster
|
|
26
67
|
|
27
68
|
protected
|
28
69
|
|
29
|
-
MAPPINGS =
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
70
|
+
MAPPINGS = [0x03, 0x01, 0x70,
|
71
|
+
|
72
|
+
0x00, 0x00, 0x00,
|
73
|
+
0x00, 0x04, 0x00,
|
74
|
+
0x00, 0x08, 0x00,
|
75
|
+
0x00, 0x0C, 0x00,
|
76
|
+
0x00, 0x01, 0x00,
|
77
|
+
0x00, 0x05, 0x00,
|
78
|
+
0x00, 0x09, 0x00,
|
79
|
+
0x00, 0x0D, 0x00,
|
80
|
+
0x00, 0x02, 0x00,
|
81
|
+
0x00, 0x06, 0x00,
|
82
|
+
0x00, 0x0A, 0x00,
|
83
|
+
0x00, 0x0E, 0x00,
|
84
|
+
0x00, 0x03, 0x00,
|
85
|
+
0x00, 0x07, 0x00,
|
86
|
+
0x00, 0x0B, 0x00,
|
87
|
+
0x00, 0x0F, 0x00,
|
88
|
+
0x01, 0x00, 0x00,
|
89
|
+
0x01, 0x04, 0x00,
|
90
|
+
0x01, 0x08, 0x00,
|
91
|
+
0x01, 0x0C, 0x00,
|
92
|
+
0x01, 0x01, 0x00,
|
93
|
+
0x01, 0x05, 0x00,
|
94
|
+
0x01, 0x09, 0x00,
|
95
|
+
0x01, 0x0D, 0x00,
|
96
|
+
0x01, 0x02, 0x00,
|
97
|
+
0x01, 0x06, 0x00,
|
98
|
+
0x01, 0x0A, 0x00,
|
99
|
+
0x01, 0x0E, 0x00,
|
100
|
+
0x01, 0x03, 0x00,
|
101
|
+
0x01, 0x07, 0x00,
|
102
|
+
0x01, 0x0B, 0x00,
|
103
|
+
0x01, 0x0F, 0x00,
|
104
|
+
0x02, 0x00, 0x00,
|
105
|
+
0x02, 0x04, 0x00,
|
106
|
+
0x02, 0x08, 0x00,
|
107
|
+
0x02, 0x0C, 0x00,
|
108
|
+
0x02, 0x01, 0x00,
|
109
|
+
0x02, 0x05, 0x00,
|
110
|
+
0x02, 0x09, 0x00,
|
111
|
+
0x02, 0x0D, 0x00,
|
112
|
+
0x02, 0x02, 0x00,
|
113
|
+
0x02, 0x06, 0x00,
|
114
|
+
0x02, 0x0A, 0x00,
|
115
|
+
0x02, 0x0E, 0x00,
|
116
|
+
0x02, 0x03, 0x00,
|
117
|
+
0x02, 0x07, 0x00,
|
118
|
+
0x02, 0x0B, 0x00,
|
119
|
+
0x02, 0x0F, 0x00,
|
120
|
+
0x03, 0x00, 0x00,
|
121
|
+
0x03, 0x04, 0x00,
|
122
|
+
0x03, 0x08, 0x00,
|
123
|
+
0x03, 0x0C, 0x00,
|
124
|
+
0x03, 0x01, 0x00,
|
125
|
+
0x03, 0x05, 0x00,
|
126
|
+
0x03, 0x09, 0x00,
|
127
|
+
0x03, 0x0D, 0x00,
|
128
|
+
0x03, 0x02, 0x00,
|
129
|
+
0x03, 0x06, 0x00,
|
130
|
+
0x03, 0x0A, 0x00,
|
131
|
+
0x03, 0x0E, 0x00,
|
132
|
+
0x03, 0x03, 0x00,
|
133
|
+
0x03, 0x07, 0x00,
|
134
|
+
0x03, 0x0B, 0x00,
|
135
|
+
0x03, 0x0F, 0x00,
|
136
|
+
|
137
|
+
0x00, 0x00, 0x01,
|
138
|
+
0x00, 0x02, 0x00,
|
139
|
+
0x03, 0x00, 0x00,
|
140
|
+
0x01, 0x01, 0x01,
|
141
|
+
0x02, 0x01, 0x03,
|
142
|
+
0x01, 0x00, 0x02,
|
143
|
+
0x01, 0x02, 0x02,
|
144
|
+
0x02, 0x03, 0x02,
|
145
|
+
0x00, 0x03, 0x01,
|
146
|
+
0x03, 0x02, 0x03,
|
147
|
+
0x03, 0x03, 0x0C,
|
148
|
+
0x00, 0x0D, 0x00,
|
149
|
+
0x0C, 0x00, 0x0D,
|
150
|
+
0x00, 0x0C, 0x00,
|
151
|
+
0x0D, 0x00, 0x0C,
|
152
|
+
0x00, 0x0D, 0x00]
|
153
|
+
READ_STATE = [0x01, 0x00, 0x00]
|
110
154
|
|
111
155
|
def sysex_prefix; @sysex_prefix ||= super + [0x00, 0x01, 0x3F, 0x2B]; end
|
112
156
|
|
113
157
|
def decode_shoulder(decoded, note, _velocity)
|
114
|
-
decoded[:control].merge
|
158
|
+
decoded[:control] = decoded[:control].merge(SurfaceMaster::Orbit::Device::SHOULDERS[note])
|
115
159
|
decoded
|
116
160
|
end
|
117
161
|
|
118
162
|
def decode_pad(decoded, note, _velocity)
|
119
|
-
decoded[:control][:button
|
163
|
+
decoded[:control] = decoded[:control].merge(button: note + 1)
|
120
164
|
decoded
|
121
165
|
end
|
122
166
|
|
123
167
|
def decode_knob(decoded, note, velocity)
|
124
|
-
decoded[:control][:bank
|
125
|
-
decoded[:value]
|
168
|
+
decoded[:control] = decoded[:control].merge(bank: note + 1)
|
169
|
+
decoded[:value] = velocity
|
126
170
|
decoded
|
127
171
|
end
|
128
172
|
|
129
173
|
def decode_control(decoded, note, velocity)
|
130
|
-
|
131
|
-
|
132
|
-
decoded
|
174
|
+
decoded = decoded.merge(SurfaceMaster::Orbit::Device::SELECTORS[note])
|
175
|
+
decoded[:control] = { button: velocity }
|
176
|
+
decoded
|
177
|
+
end
|
178
|
+
|
179
|
+
def decode_accelerometer(decoded, _note, velocity)
|
180
|
+
decoded[:value] = velocity
|
133
181
|
decoded
|
134
182
|
end
|
135
183
|
|
136
184
|
def enrich_decoded_message(decoded, note, velocity, timestamp)
|
137
185
|
case decoded[:type]
|
138
|
-
when :shoulder
|
139
|
-
when :pad
|
140
|
-
when :
|
141
|
-
when :
|
186
|
+
when :shoulder then decoded = decode_shoulder(decoded, note, velocity)
|
187
|
+
when :pad then decoded = decode_pad(decoded, note, velocity)
|
188
|
+
when :vknob then decoded = decode_knob(decoded, note, velocity)
|
189
|
+
when :accelerometer then decoded = decode_accelerometer(decoded, note, velocity)
|
190
|
+
else decoded = decode_control(decoded, note, velocity)
|
142
191
|
end
|
143
192
|
decoded[:timestamp] = timestamp
|
144
193
|
decoded
|
145
194
|
end
|
146
195
|
|
147
196
|
def decode_input(input)
|
148
|
-
# puts [input[:code].to_hex, input[:note].to_hex, input[:velocity].to_hex].join(" ")
|
149
197
|
note = input[:note]
|
150
198
|
velocity = input[:velocity]
|
151
199
|
code_high = input[:code] & 0xF0
|
152
200
|
code_low = input[:code] & 0x0F
|
153
201
|
raw = SurfaceMaster::Orbit::Device::CONTROLS[code_high]
|
154
202
|
raw = raw[code_low] if raw
|
155
|
-
raw = enrich_decoded_message(raw, note, velocity, input[:timestamp]) if raw
|
203
|
+
raw = enrich_decoded_message(raw.dup, note, velocity, input[:timestamp]) if raw
|
156
204
|
raw
|
157
205
|
end
|
158
206
|
end
|
@@ -7,24 +7,74 @@ module SurfaceMaster
|
|
7
7
|
super(opts)
|
8
8
|
end
|
9
9
|
|
10
|
-
|
10
|
+
protected
|
11
11
|
|
12
|
+
def combined_types(type, opts = nil)
|
13
|
+
tmp = case type
|
14
|
+
when :shoulder
|
15
|
+
[:"#{type}-#{opts[:button]}"]
|
16
|
+
when :accelerometer
|
17
|
+
[:"#{type}-#{opts[:axis]}"]
|
18
|
+
when :vknob
|
19
|
+
knobs = opts[:vknob].nil? ? [1..4] : [opts[:vknob]]
|
20
|
+
banks = opts[:bank].nil? ? [1..4] : [opts[:bank]]
|
21
|
+
|
22
|
+
expand(knobs).product(expand(banks)).map { |k, b| :"#{type}-#{k}-#{b}" }
|
23
|
+
when :vknobs, :banks
|
24
|
+
buttons = opts[:button].nil? ? [1..4] : [opts[:button]]
|
25
|
+
|
26
|
+
expand(buttons).map { |b| [:"#{type}-#{b}"] }
|
27
|
+
when :pad
|
28
|
+
banks = opts[:bank].nil? ? [1..4] : [opts[:bank]]
|
29
|
+
buttons = opts[:button].nil? ? [1..16] : [opts[:button]]
|
30
|
+
|
31
|
+
expand(buttons).product(expand(banks)).map { |p, b| :"#{type}-#{p}-#{b}" }
|
32
|
+
else
|
33
|
+
[type]
|
34
|
+
end
|
35
|
+
tmp.flatten.compact
|
36
|
+
end
|
37
|
+
|
38
|
+
def responses_hash
|
39
|
+
{ down: [],
|
40
|
+
up: [],
|
41
|
+
update: [],
|
42
|
+
tilt: [] }
|
43
|
+
end
|
44
|
+
|
45
|
+
def responses
|
46
|
+
@responses ||= Hash.new { |hash, key| hash[key] = responses_hash }
|
47
|
+
end
|
48
|
+
|
49
|
+
# TODO: Allow catching ranges of pads...
|
50
|
+
#
|
51
|
+
# TODO: Allow differentiating on bank/vknob/shoulder button...
|
12
52
|
def respond_to_action(action)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
# actions += responses[:"grid#{action[:x]}#{action[:y]}"][state]
|
18
|
-
# actions += responses[:"grid#{action[:x]}-"][state]
|
19
|
-
# actions += responses[:"grid-#{action[:y]}"][state]
|
20
|
-
# end
|
21
|
-
# actions += responses[type][state]
|
22
|
-
# actions += responses[:all][state]
|
23
|
-
# actions.compact.each {|block| block.call(self, action)}
|
53
|
+
mappings_for_action(action).each do |block|
|
54
|
+
block.call(self, action)
|
55
|
+
end
|
56
|
+
nil
|
24
57
|
rescue Exception => e # TODO: StandardException, RuntimeError, or Exception?
|
25
58
|
logger.error "Error when responding to action #{action.inspect}: #{e.inspect}"
|
26
59
|
raise e
|
27
60
|
end
|
61
|
+
|
62
|
+
def mappings_for_action(action)
|
63
|
+
combined_types = combined_types(action[:type].to_sym, action[:control])
|
64
|
+
state = action[:state].to_sym
|
65
|
+
actions = []
|
66
|
+
actions += combined_types.map { |ct| responses[ct][state] }
|
67
|
+
actions += responses[:all][state]
|
68
|
+
actions.flatten.compact
|
69
|
+
end
|
70
|
+
|
71
|
+
def expand_states(state)
|
72
|
+
case state
|
73
|
+
when :both then %i(down up)
|
74
|
+
when :all then responses_hash.keys
|
75
|
+
else Array(state)
|
76
|
+
end
|
77
|
+
end
|
28
78
|
end
|
29
79
|
end
|
30
80
|
end
|
@@ -6,28 +6,28 @@ module SurfaceMaster
|
|
6
6
|
module MIDICodes
|
7
7
|
# TODO: Use a lib to do a deep-freeze.
|
8
8
|
# rubocop:disable Metrics/LineLength
|
9
|
-
CONTROLS = { 0x90 => { 0x00 => { type: :pad,
|
10
|
-
0x01 => { type: :pad,
|
11
|
-
0x02 => { type: :pad,
|
12
|
-
0x03 => { type: :pad,
|
13
|
-
0x0F => { type: :shoulder,
|
14
|
-
0x80 => { 0x00 => { type: :pad,
|
15
|
-
0x01 => { type: :pad,
|
16
|
-
0x02 => { type: :pad,
|
17
|
-
0x03 => { type: :pad,
|
18
|
-
0x0F => { type: :shoulder,
|
19
|
-
0xB0 => { 0x00 => { type: :
|
20
|
-
0x01 => { type: :
|
21
|
-
0x02 => { type: :
|
22
|
-
0x03 => { type: :
|
23
|
-
0x0C => { type: :accelerometer,
|
24
|
-
0x0D => { type: :accelerometer,
|
25
|
-
0x0F => {
|
9
|
+
CONTROLS = { 0x90 => { 0x00 => { type: :pad, state: :down, control: { bank: 1 } },
|
10
|
+
0x01 => { type: :pad, state: :down, control: { bank: 2 } },
|
11
|
+
0x02 => { type: :pad, state: :down, control: { bank: 3 } },
|
12
|
+
0x03 => { type: :pad, state: :down, control: { bank: 4 } },
|
13
|
+
0x0F => { type: :shoulder, state: :down, control: {} } },
|
14
|
+
0x80 => { 0x00 => { type: :pad, state: :up, control: { bank: 1 } },
|
15
|
+
0x01 => { type: :pad, state: :up, control: { bank: 2 } },
|
16
|
+
0x02 => { type: :pad, state: :up, control: { bank: 3 } },
|
17
|
+
0x03 => { type: :pad, state: :up, control: { bank: 4 } },
|
18
|
+
0x0F => { type: :shoulder, state: :up, control: {} } },
|
19
|
+
0xB0 => { 0x00 => { type: :vknob, state: :update, control: { vknob: 1 } },
|
20
|
+
0x01 => { type: :vknob, state: :update, control: { vknob: 2 } },
|
21
|
+
0x02 => { type: :vknob, state: :update, control: { vknob: 3 } },
|
22
|
+
0x03 => { type: :vknob, state: :update, control: { vknob: 4 } },
|
23
|
+
0x0C => { type: :accelerometer, state: :tilt, control: { axis: :x } },
|
24
|
+
0x0D => { type: :accelerometer, state: :tilt, control: { axis: :y } },
|
25
|
+
0x0F => { state: :down } } }.freeze
|
26
26
|
# rubocop:enable Metrics/LineLength
|
27
27
|
SHOULDERS = { 0x03 => { button: :left },
|
28
28
|
0x04 => { button: :right } }.freeze
|
29
|
-
SELECTORS = { 0x01 => {
|
30
|
-
0x02 => {
|
29
|
+
SELECTORS = { 0x01 => { type: :banks },
|
30
|
+
0x02 => { type: :vknobs } }.freeze
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
data/lib/surface_master.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: surface_master
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Frisby
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-09-
|
11
|
+
date: 2015-09-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: portmidi
|
@@ -73,9 +73,11 @@ files:
|
|
73
73
|
- debug_tools/decode.rb
|
74
74
|
- debug_tools/extract_midi_monitor_sample.sh
|
75
75
|
- docs/Numark_Orbit_QuickRef.md
|
76
|
-
- examples/
|
77
|
-
- examples/
|
78
|
-
- examples/
|
76
|
+
- examples/launchpad_playground.rb
|
77
|
+
- examples/orbit_device.rb
|
78
|
+
- examples/orbit_interaction.rb
|
79
|
+
- examples/orbit_playground.rb
|
80
|
+
- examples/system_monitor.rb
|
79
81
|
- lib/surface_master.rb
|
80
82
|
- lib/surface_master/device.rb
|
81
83
|
- lib/surface_master/errors.rb
|