surface_master 0.2.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|