surface_master 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +107 -0
- data/.travis.yml +2 -3
- data/CHANGELOG.md +16 -0
- data/Gemfile +5 -1
- data/Rakefile +73 -5
- data/debug_tools/decode.rb +1 -1
- data/examples/launchpad_testbed.rb +67 -28
- data/examples/monitor.rb +23 -11
- data/examples/orbit_testbed.rb +1 -42
- data/lib/surface_master/device.rb +40 -39
- data/lib/surface_master/interaction.rb +81 -55
- data/lib/surface_master/launchpad/device.rb +70 -58
- data/lib/surface_master/launchpad/errors.rb +8 -9
- data/lib/surface_master/launchpad/interaction.rb +24 -42
- data/lib/surface_master/orbit/device.rb +85 -85
- data/lib/surface_master/orbit/interaction.rb +1 -0
- data/lib/surface_master/orbit/midi_codes.rb +3 -1
- data/lib/surface_master/version.rb +2 -1
- data/lib/{control_center.rb → surface_master.rb} +0 -0
- data/surface_master.gemspec +7 -5
- data/test/helper.rb +16 -17
- data/test/test_device.rb +167 -355
- data/test/test_interaction.rb +177 -188
- metadata +5 -3
@@ -1,5 +1,6 @@
|
|
1
1
|
module SurfaceMaster
|
2
|
-
# Base class for event-based drivers. Sub-classes should extend the constructor, and implement
|
2
|
+
# Base class for event-based drivers. Sub-classes should extend the constructor, and implement
|
3
|
+
# `respond_to_action`, etc.
|
3
4
|
class Interaction
|
4
5
|
include Logging
|
5
6
|
|
@@ -12,10 +13,9 @@ module SurfaceMaster
|
|
12
13
|
logger.debug "Initializing #{self.class}##{object_id} with #{opts.inspect}"
|
13
14
|
|
14
15
|
@use_threads = opts[:use_threads] || true
|
15
|
-
@device = opts[:device]
|
16
|
-
|
17
|
-
|
18
|
-
logger: opts[:logger]))
|
16
|
+
@device = opts[:device] || @device_class.new(opts.merge(input: true,
|
17
|
+
output: true,
|
18
|
+
logger: opts[:logger]))
|
19
19
|
@latency = (opts[:latency] || 0.001).to_f.abs
|
20
20
|
@active = false
|
21
21
|
|
@@ -36,35 +36,10 @@ module SurfaceMaster
|
|
36
36
|
def start(opts = nil)
|
37
37
|
logger.debug "Starting #{self.class}##{object_id}"
|
38
38
|
|
39
|
-
opts
|
39
|
+
opts = { detached: false }.merge(opts || {})
|
40
|
+
@active = true
|
41
|
+
@reader_thread ||= create_reader_thread
|
40
42
|
|
41
|
-
@active = true
|
42
|
-
|
43
|
-
@reader_thread ||= Thread.new do
|
44
|
-
begin
|
45
|
-
while @active do
|
46
|
-
@device.read.each do |action|
|
47
|
-
if @use_threads
|
48
|
-
action_thread = Thread.new(action) do |act|
|
49
|
-
respond_to_action(act)
|
50
|
-
end
|
51
|
-
@action_threads.add(action_thread)
|
52
|
-
else
|
53
|
-
respond_to_action(action)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
sleep @latency# if @latency > 0.0
|
57
|
-
end
|
58
|
-
rescue Portmidi::DeviceError => e
|
59
|
-
logger.fatal "Could not read from device, stopping reader!"
|
60
|
-
raise SurfaceMaster::CommunicationError.new(e)
|
61
|
-
rescue Exception => e
|
62
|
-
logger.fatal "Unkown error, stopping reader: #{e.inspect}"
|
63
|
-
raise e
|
64
|
-
ensure
|
65
|
-
@device.reset!
|
66
|
-
end
|
67
|
-
end
|
68
43
|
@reader_thread.join unless opts[:detached]
|
69
44
|
end
|
70
45
|
|
@@ -78,28 +53,18 @@ module SurfaceMaster
|
|
78
53
|
@reader_thread = nil
|
79
54
|
end
|
80
55
|
ensure
|
81
|
-
|
82
|
-
begin
|
83
|
-
thread.kill
|
84
|
-
thread.join
|
85
|
-
rescue StandardException => e # TODO: RuntimeError, Exception, or this?
|
86
|
-
logger.error "Error when killing action thread: #{e.inspect}"
|
87
|
-
end
|
88
|
-
end
|
56
|
+
kill_action_threads!
|
89
57
|
nil
|
90
58
|
end
|
91
59
|
|
92
60
|
def response_to(types = :all, state = :both, opts = nil, &block)
|
93
|
-
logger.debug "Setting response to #{types.inspect} for state #{state.inspect} with
|
61
|
+
logger.debug "Setting response to #{types.inspect} for state #{state.inspect} with"\
|
62
|
+
" #{opts.inspect}"
|
94
63
|
types = Array(types)
|
95
64
|
opts ||= {}
|
96
65
|
no_response_to(types, state) if opts[:exclusive] == true
|
97
|
-
|
98
|
-
types
|
99
|
-
combined_types(type, opts).each do |combined_type|
|
100
|
-
responses[combined_type][st] << block
|
101
|
-
end
|
102
|
-
end
|
66
|
+
expand_states(state).each do |st|
|
67
|
+
add_response_for_state!(types, opts, st, block)
|
103
68
|
end
|
104
69
|
nil
|
105
70
|
end
|
@@ -107,12 +72,8 @@ module SurfaceMaster
|
|
107
72
|
def no_response_to(types = nil, state = :both, opts = nil)
|
108
73
|
logger.debug "Removing response to #{types.inspect} for state #{state.inspect}"
|
109
74
|
types = Array(types)
|
110
|
-
|
111
|
-
types
|
112
|
-
combined_types(type, opts).each do |combined_type|
|
113
|
-
responses[combined_type][st].clear
|
114
|
-
end
|
115
|
-
end
|
75
|
+
expand_states(state).each do |st|
|
76
|
+
clear_responses_for_state!(types, opts, st)
|
116
77
|
end
|
117
78
|
nil
|
118
79
|
end
|
@@ -123,11 +84,76 @@ module SurfaceMaster
|
|
123
84
|
|
124
85
|
protected
|
125
86
|
|
87
|
+
def create_reader_thread
|
88
|
+
Thread.new do
|
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
|
96
|
+
end
|
97
|
+
|
98
|
+
def guard_input_and_reset_at_end!(&block)
|
99
|
+
block.call
|
100
|
+
rescue Portmidi::DeviceError => e
|
101
|
+
logger.fatal "Could not read from device, stopping reader!"
|
102
|
+
raise SurfaceMaster::CommunicationError, e
|
103
|
+
rescue Exception => e
|
104
|
+
logger.fatal "Unkown error, stopping reader: #{e.inspect}"
|
105
|
+
raise e
|
106
|
+
ensure
|
107
|
+
@device.reset!
|
108
|
+
end
|
109
|
+
|
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
|
+
def add_response_for_state!(types, opts, state, block)
|
122
|
+
response_groups_for(types, opts, state) do |responses|
|
123
|
+
responses << block
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def clear_responses_for_state!(types, opts, state)
|
128
|
+
response_groups_for(types, opts, state, &:clear)
|
129
|
+
end
|
130
|
+
|
131
|
+
def response_groups_for(types, opts, state, &block)
|
132
|
+
types.each do |type|
|
133
|
+
combined_types(type, opts).each do |combined_type|
|
134
|
+
block.call(responses[combined_type][state])
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def expand_states(state); Array(state == :both ? %i(down up) : state); end
|
140
|
+
|
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
|
+
|
126
152
|
def responses
|
127
153
|
# TODO: Generalize for arbitrary actions...
|
128
154
|
@responses ||= Hash.new { |hash, key| hash[key] = { down: [], up: [] } }
|
129
155
|
end
|
130
156
|
|
131
|
-
def respond_to_action(
|
157
|
+
def respond_to_action(_action); end
|
132
158
|
end
|
133
159
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module SurfaceMaster
|
2
2
|
module Launchpad
|
3
|
+
# Low-level interface to Novation Launchpad Mark 2 control surface.
|
3
4
|
class Device < SurfaceMaster::Device
|
4
5
|
include MIDICodes
|
5
6
|
|
@@ -54,76 +55,101 @@ module SurfaceMaster
|
|
54
55
|
# TODO: Support more of the LaunchPad Mark 2's functionality.
|
55
56
|
|
56
57
|
def change(opts = nil)
|
58
|
+
fail NoOutputAllowedError unless output_enabled?
|
59
|
+
|
57
60
|
opts ||= {}
|
58
61
|
command, payload = color_payload(opts)
|
59
62
|
sysex!(command, payload[:led], payload[:color])
|
63
|
+
nil
|
60
64
|
end
|
61
65
|
|
62
66
|
def changes(values)
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
msg_by_command.each do |command, payloads|
|
69
|
-
# The documented batch size for RGB LED updates is 80. The docs lie, at least on my current
|
70
|
-
# firmware version -- anything above 62 crashes the device hard.
|
67
|
+
fail NoOutputAllowedError unless output_enabled?
|
68
|
+
|
69
|
+
organize_commands(values).each do |command, payloads|
|
70
|
+
# The documented batch size for RGB LED updates is 80. The docs lie, at least on my
|
71
|
+
# current firmware version -- anything above 62 crashes the device hard.
|
71
72
|
while (slice = payloads.shift(62)).length > 0
|
72
|
-
|
73
|
+
messages = slice.map { |payload| [payload[:led], payload[:color]] }
|
74
|
+
sysex!(command, *messages)
|
73
75
|
end
|
74
76
|
end
|
77
|
+
nil
|
75
78
|
end
|
76
79
|
|
77
80
|
def read
|
81
|
+
fail NoInputAllowedError unless input_enabled?
|
78
82
|
super.collect do |input|
|
79
|
-
note
|
80
|
-
input[:type]
|
81
|
-
if input[:type] == :grid
|
82
|
-
|
83
|
-
input[:x] = note % 10
|
84
|
-
input[:y] = note / 10
|
85
|
-
end
|
83
|
+
note = input.delete(:note)
|
84
|
+
input[:type] = CODE_NOTE_TO_TYPE[[input.delete(:code), note]] || :grid
|
85
|
+
input[:x], input[:y] = decode_grid_coord(note) if input[:type] == :grid
|
86
|
+
input.delete(:velocity)
|
86
87
|
input
|
87
88
|
end
|
88
89
|
end
|
89
90
|
|
90
91
|
protected
|
91
92
|
|
93
|
+
def organize_commands(values)
|
94
|
+
msg_by_command = {}
|
95
|
+
values.each do |value|
|
96
|
+
command, payload = color_payload(value)
|
97
|
+
(msg_by_command[command] ||= []) << payload
|
98
|
+
end
|
99
|
+
msg_by_command
|
100
|
+
end
|
101
|
+
|
102
|
+
def decode_grid_coord(note)
|
103
|
+
note -= 11
|
104
|
+
x = note % 10
|
105
|
+
y = note / 10
|
106
|
+
[x, y]
|
107
|
+
end
|
108
|
+
|
92
109
|
def layout!(mode); sysex!(0x22, mode); end
|
93
110
|
def sysex_prefix; @sysex_prefix ||= super + [0x00, 0x20, 0x29, 0x02, 0x18]; end
|
94
111
|
|
95
112
|
def decode_led(opts)
|
96
113
|
case
|
97
|
-
when opts[:cc]
|
98
|
-
|
99
|
-
when opts[:
|
100
|
-
|
101
|
-
[:all, nil]
|
102
|
-
else
|
103
|
-
[:grid, (opts[:grid][1] * 10) + opts[:grid][0] + 11]
|
104
|
-
end
|
105
|
-
when opts[:column]
|
106
|
-
[:column, opts[:column]]
|
107
|
-
when opts[:row]
|
108
|
-
[:row, opts[:row]]
|
114
|
+
when opts[:cc] then [:cc, TYPE_TO_NOTE[opts[:cc]]]
|
115
|
+
when opts[:grid] then decode_grid_led(opts)
|
116
|
+
when opts[:column] then [:column, opts[:column]]
|
117
|
+
when opts[:row] then [:row, opts[:row]]
|
109
118
|
end
|
119
|
+
rescue
|
120
|
+
raise SurfaceMaster::Launchpad::NoValidGridCoordinatesError
|
110
121
|
end
|
111
122
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
type, led = decode_led(opts)
|
119
|
-
command = TYPE_TO_COMMAND[type]
|
120
|
-
case type
|
121
|
-
when :cc, :grid
|
122
|
-
color = [opts[:red] || 0x00, opts[:green] || 0x00, opts[:blue] || 0x00]
|
123
|
-
when :column, :row, :all
|
124
|
-
color = opts[:color] || 0x00
|
123
|
+
def decode_grid_led(opts)
|
124
|
+
if opts[:grid] == :all
|
125
|
+
[:all, nil]
|
126
|
+
else
|
127
|
+
check_xy_values!(opts[:grid])
|
128
|
+
[:grid, (opts[:grid][1] * 10) + opts[:grid][0] + 11]
|
125
129
|
end
|
126
|
-
|
130
|
+
end
|
131
|
+
|
132
|
+
def check_xy_values!(xy_pair)
|
133
|
+
x = xy_pair[0]
|
134
|
+
y = xy_pair[1]
|
135
|
+
return unless xy_pair.length != 2 ||
|
136
|
+
!coord_in_range?(x) ||
|
137
|
+
!coord_in_range?(y)
|
138
|
+
|
139
|
+
fail SurfaceMaster::Launchpad::NoValidGridCoordinatesError
|
140
|
+
end
|
141
|
+
|
142
|
+
def coord_in_range?(val); val && val >= 0 && val <= 7; end
|
143
|
+
|
144
|
+
def color_payload(opts)
|
145
|
+
# Hard-coded to single-LED RGB update right now.
|
146
|
+
# For paletted changes, commands available include:
|
147
|
+
# 0x0C -> Column
|
148
|
+
# 0x0D -> Row
|
149
|
+
# 0x0E -> All LEDs
|
150
|
+
[0x0B,
|
151
|
+
{ led: decode_led(opts),
|
152
|
+
color: [opts[:red] || 0x00, opts[:green] || 0x00, opts[:blue] || 0x00] }]
|
127
153
|
end
|
128
154
|
|
129
155
|
def output!(status, data1, data2)
|
@@ -133,27 +159,13 @@ module SurfaceMaster
|
|
133
159
|
def outputs!(*messages)
|
134
160
|
messages = Array(messages)
|
135
161
|
if @output.nil?
|
136
|
-
logger.error "trying to write to device
|
137
|
-
|
162
|
+
logger.error "trying to write to device not open for output"
|
163
|
+
fail SurfaceMaster::NoOutputAllowedError
|
138
164
|
end
|
139
165
|
logger.debug "writing messages to launchpad:\n #{messages.join("\n ")}" if logger.debug?
|
140
166
|
@output.write(messages)
|
141
167
|
nil
|
142
168
|
end
|
143
|
-
|
144
|
-
def note(type, opts)
|
145
|
-
note = TYPE_TO_NOTE[type]
|
146
|
-
if note.nil?
|
147
|
-
x = (opts[:x] || -1).to_i
|
148
|
-
y = (opts[:y] || -1).to_i
|
149
|
-
if x < 0 || x > 7 || y < 0 || y > 7
|
150
|
-
logger.error "wrong coordinates specified: x=#{x}, y=#{y}"
|
151
|
-
raise NoValidGridCoordinatesError.new("you need to specify valid coordinates (x/y, 0-7, from top left), you specified: x=#{x}, y=#{y}")
|
152
|
-
end
|
153
|
-
note = y * 10 + x
|
154
|
-
end
|
155
|
-
note
|
156
|
-
end
|
157
169
|
end
|
158
170
|
end
|
159
171
|
end
|
@@ -1,11 +1,10 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
1
|
+
module SurfaceMaster
|
2
|
+
module Launchpad
|
3
|
+
# Generic launchpad error.
|
4
|
+
class LaunchpadError < SurfaceMaster::GenericError; end
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
# Error raised when wrong brightness was specified.
|
10
|
-
class NoValidBrightnessError < LaunchpadError; end
|
6
|
+
# Error raised when <tt>x/y</tt> coordinates outside of the grid
|
7
|
+
# or none were specified.
|
8
|
+
class NoValidGridCoordinatesError < LaunchpadError; end
|
9
|
+
end
|
11
10
|
end
|
@@ -1,43 +1,13 @@
|
|
1
1
|
module SurfaceMaster
|
2
2
|
module Launchpad
|
3
|
+
# Higher-level interface to Novation Launchpad Mark 2, providing an input
|
4
|
+
# handling loop and event-hooks for input events.
|
3
5
|
class Interaction < SurfaceMaster::Interaction
|
4
6
|
def initialize(opts = nil)
|
5
7
|
@device_class = Device
|
6
8
|
super(opts)
|
7
9
|
end
|
8
10
|
|
9
|
-
# def response_to(types = :all, state = :both, opts = nil, &block)
|
10
|
-
# logger.debug "setting response to #{types.inspect} for state #{state.inspect} with #{opts.inspect}"
|
11
|
-
# types = Array(types)
|
12
|
-
# opts ||= {}
|
13
|
-
# no_response_to(types, state) if opts[:exclusive] == true
|
14
|
-
# Array(state == :both ? %i(down up) : state).each do |st|
|
15
|
-
# types.each do |type|
|
16
|
-
# combined_types(type, opts).each do |combined_type|
|
17
|
-
# responses[combined_type][st] << block
|
18
|
-
# end
|
19
|
-
# end
|
20
|
-
# end
|
21
|
-
# nil
|
22
|
-
# end
|
23
|
-
|
24
|
-
# def no_response_to(types = nil, state = :both, opts = nil)
|
25
|
-
# logger.debug "removing response to #{types.inspect} for state #{state.inspect}"
|
26
|
-
# types = Array(types)
|
27
|
-
# Array(state == :both ? %i(down up) : state).each do |st|
|
28
|
-
# types.each do |type|
|
29
|
-
# combined_types(type, opts).each do |combined_type|
|
30
|
-
# responses[combined_type][st].clear
|
31
|
-
# end
|
32
|
-
# end
|
33
|
-
# end
|
34
|
-
# nil
|
35
|
-
# end
|
36
|
-
|
37
|
-
# def respond_to(type, state, opts = nil)
|
38
|
-
# respond_to_action((opts || {}).merge(type: type, state: state))
|
39
|
-
# end
|
40
|
-
|
41
11
|
protected
|
42
12
|
|
43
13
|
def responses
|
@@ -56,8 +26,8 @@ module SurfaceMaster
|
|
56
26
|
x = grid_range(opts[:x])
|
57
27
|
y = grid_range(opts[:y])
|
58
28
|
return [:grid] if x.nil? && y.nil? # whole grid
|
59
|
-
x ||= [
|
60
|
-
y ||= [
|
29
|
+
x ||= ["-"] # whole row
|
30
|
+
y ||= ["-"] # whole column
|
61
31
|
x.product(y).map { |xx, yy| :"grid#{xx}#{yy}" }
|
62
32
|
else
|
63
33
|
[type.to_sym]
|
@@ -65,21 +35,33 @@ module SurfaceMaster
|
|
65
35
|
end
|
66
36
|
|
67
37
|
def respond_to_action(action)
|
38
|
+
mappings_for_action(action).each do |block|
|
39
|
+
block.call(self, action)
|
40
|
+
end
|
41
|
+
nil
|
42
|
+
rescue Exception => e # TODO: StandardException, RuntimeError, or Exception?
|
43
|
+
logger.error "Error when responding to action #{action.inspect}: #{e.inspect}"
|
44
|
+
raise e
|
45
|
+
end
|
46
|
+
|
47
|
+
def mappings_for_action(action)
|
68
48
|
type = action[:type].to_sym
|
69
49
|
state = action[:state].to_sym
|
70
50
|
actions = []
|
71
51
|
if type == :grid
|
72
|
-
actions +=
|
73
|
-
actions += responses[:"grid#{action[:x]}-"][state]
|
74
|
-
actions += responses[:"grid-#{action[:y]}"][state]
|
52
|
+
actions += mappings_for_grid_action(state, action[:x], action[:y])
|
75
53
|
end
|
76
54
|
actions += responses[type][state]
|
77
55
|
actions += responses[:all][state]
|
78
|
-
actions.compact
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
56
|
+
actions.compact
|
57
|
+
end
|
58
|
+
|
59
|
+
def mappings_for_grid_action(state, x, y)
|
60
|
+
actions = []
|
61
|
+
actions += responses[:"grid#{x}#{y}"][state]
|
62
|
+
actions += responses[:"grid#{x}-"][state]
|
63
|
+
actions += responses[:"grid-#{y}"][state]
|
64
|
+
actions
|
83
65
|
end
|
84
66
|
end
|
85
67
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module SurfaceMaster
|
2
2
|
module Orbit
|
3
|
+
# Low-level interface to Numark Orbit wireless MIDI control surface.
|
3
4
|
class Device < SurfaceMaster::Device
|
4
5
|
include MIDICodes
|
5
6
|
|
@@ -11,91 +12,8 @@ module SurfaceMaster
|
|
11
12
|
|
12
13
|
def reset!
|
13
14
|
# TODO: This... doesn't appear to work. At all.
|
14
|
-
|
15
|
-
|
16
|
-
0x00, 0x00, 0x00,
|
17
|
-
0x00, 0x04, 0x00,
|
18
|
-
0x00, 0x08, 0x00,
|
19
|
-
0x00, 0x0C, 0x00,
|
20
|
-
0x00, 0x01, 0x00,
|
21
|
-
0x00, 0x05, 0x00,
|
22
|
-
0x00, 0x09, 0x00,
|
23
|
-
0x00, 0x0D, 0x00,
|
24
|
-
0x00, 0x02, 0x00,
|
25
|
-
0x00, 0x06, 0x00,
|
26
|
-
0x00, 0x0A, 0x00,
|
27
|
-
0x00, 0x0E, 0x00,
|
28
|
-
0x00, 0x03, 0x00,
|
29
|
-
0x00, 0x07, 0x00,
|
30
|
-
0x00, 0x0B, 0x00,
|
31
|
-
0x00, 0x0F, 0x00,
|
32
|
-
0x01, 0x00, 0x00,
|
33
|
-
0x01, 0x04, 0x00,
|
34
|
-
0x01, 0x08, 0x00,
|
35
|
-
0x01, 0x0C, 0x00,
|
36
|
-
0x01, 0x01, 0x00,
|
37
|
-
0x01, 0x05, 0x00,
|
38
|
-
0x01, 0x09, 0x00,
|
39
|
-
0x01, 0x0D, 0x00,
|
40
|
-
0x01, 0x02, 0x00,
|
41
|
-
0x01, 0x06, 0x00,
|
42
|
-
0x01, 0x0A, 0x00,
|
43
|
-
0x01, 0x0E, 0x00,
|
44
|
-
0x01, 0x03, 0x00,
|
45
|
-
0x01, 0x07, 0x00,
|
46
|
-
0x01, 0x0B, 0x00,
|
47
|
-
0x01, 0x0F, 0x00,
|
48
|
-
0x02, 0x00, 0x00,
|
49
|
-
0x02, 0x04, 0x00,
|
50
|
-
0x02, 0x08, 0x00,
|
51
|
-
0x02, 0x0C, 0x00,
|
52
|
-
0x02, 0x01, 0x00,
|
53
|
-
0x02, 0x05, 0x00,
|
54
|
-
0x02, 0x09, 0x00,
|
55
|
-
0x02, 0x0D, 0x00,
|
56
|
-
0x02, 0x02, 0x00,
|
57
|
-
0x02, 0x06, 0x00,
|
58
|
-
0x02, 0x0A, 0x00,
|
59
|
-
0x02, 0x0E, 0x00,
|
60
|
-
0x02, 0x03, 0x00,
|
61
|
-
0x02, 0x07, 0x00,
|
62
|
-
0x02, 0x0B, 0x00,
|
63
|
-
0x02, 0x0F, 0x00,
|
64
|
-
0x03, 0x00, 0x00,
|
65
|
-
0x03, 0x04, 0x00,
|
66
|
-
0x03, 0x08, 0x00,
|
67
|
-
0x03, 0x0C, 0x00,
|
68
|
-
0x03, 0x01, 0x00,
|
69
|
-
0x03, 0x05, 0x00,
|
70
|
-
0x03, 0x09, 0x00,
|
71
|
-
0x03, 0x0D, 0x00,
|
72
|
-
0x03, 0x02, 0x00,
|
73
|
-
0x03, 0x06, 0x00,
|
74
|
-
0x03, 0x0A, 0x00,
|
75
|
-
0x03, 0x0E, 0x00,
|
76
|
-
0x03, 0x03, 0x00,
|
77
|
-
0x03, 0x07, 0x00,
|
78
|
-
0x03, 0x0B, 0x00,
|
79
|
-
0x03, 0x0F, 0x00,
|
80
|
-
0x00, 0x00, 0x01,
|
81
|
-
0x00, 0x02, 0x00,
|
82
|
-
0x03, 0x00, 0x00,
|
83
|
-
0x01, 0x01, 0x01,
|
84
|
-
0x02, 0x01, 0x03,
|
85
|
-
0x01, 0x00, 0x02,
|
86
|
-
0x01, 0x02, 0x02,
|
87
|
-
0x02, 0x03, 0x02,
|
88
|
-
0x00, 0x03, 0x01,
|
89
|
-
0x03, 0x02, 0x03,
|
90
|
-
0x03, 0x03, 0x0C,
|
91
|
-
0x00, 0x0D, 0x00,
|
92
|
-
0x0C, 0x00, 0x0D,
|
93
|
-
0x00, 0x0C, 0x00,
|
94
|
-
0x0D, 0x00, 0x0C,
|
95
|
-
0x00, 0x0D, 0x00]
|
96
|
-
|
97
|
-
if (result = sysex!(*mappings)) != 0
|
98
|
-
raise "Expected success (0) setting mappings, got: #{result}"
|
15
|
+
if (result = sysex!(*MAPPINGS)) != 0
|
16
|
+
fail "Expected success (0) setting mappings, got: #{result}"
|
99
17
|
end
|
100
18
|
sysex!(0x01, 0x00, 0x00)
|
101
19
|
end
|
@@ -108,6 +26,88 @@ module SurfaceMaster
|
|
108
26
|
|
109
27
|
protected
|
110
28
|
|
29
|
+
MAPPINGS = [0x03, 0x01, 0x70,
|
30
|
+
0x00, 0x00, 0x00,
|
31
|
+
0x00, 0x04, 0x00,
|
32
|
+
0x00, 0x08, 0x00,
|
33
|
+
0x00, 0x0C, 0x00,
|
34
|
+
0x00, 0x01, 0x00,
|
35
|
+
0x00, 0x05, 0x00,
|
36
|
+
0x00, 0x09, 0x00,
|
37
|
+
0x00, 0x0D, 0x00,
|
38
|
+
0x00, 0x02, 0x00,
|
39
|
+
0x00, 0x06, 0x00,
|
40
|
+
0x00, 0x0A, 0x00,
|
41
|
+
0x00, 0x0E, 0x00,
|
42
|
+
0x00, 0x03, 0x00,
|
43
|
+
0x00, 0x07, 0x00,
|
44
|
+
0x00, 0x0B, 0x00,
|
45
|
+
0x00, 0x0F, 0x00,
|
46
|
+
0x01, 0x00, 0x00,
|
47
|
+
0x01, 0x04, 0x00,
|
48
|
+
0x01, 0x08, 0x00,
|
49
|
+
0x01, 0x0C, 0x00,
|
50
|
+
0x01, 0x01, 0x00,
|
51
|
+
0x01, 0x05, 0x00,
|
52
|
+
0x01, 0x09, 0x00,
|
53
|
+
0x01, 0x0D, 0x00,
|
54
|
+
0x01, 0x02, 0x00,
|
55
|
+
0x01, 0x06, 0x00,
|
56
|
+
0x01, 0x0A, 0x00,
|
57
|
+
0x01, 0x0E, 0x00,
|
58
|
+
0x01, 0x03, 0x00,
|
59
|
+
0x01, 0x07, 0x00,
|
60
|
+
0x01, 0x0B, 0x00,
|
61
|
+
0x01, 0x0F, 0x00,
|
62
|
+
0x02, 0x00, 0x00,
|
63
|
+
0x02, 0x04, 0x00,
|
64
|
+
0x02, 0x08, 0x00,
|
65
|
+
0x02, 0x0C, 0x00,
|
66
|
+
0x02, 0x01, 0x00,
|
67
|
+
0x02, 0x05, 0x00,
|
68
|
+
0x02, 0x09, 0x00,
|
69
|
+
0x02, 0x0D, 0x00,
|
70
|
+
0x02, 0x02, 0x00,
|
71
|
+
0x02, 0x06, 0x00,
|
72
|
+
0x02, 0x0A, 0x00,
|
73
|
+
0x02, 0x0E, 0x00,
|
74
|
+
0x02, 0x03, 0x00,
|
75
|
+
0x02, 0x07, 0x00,
|
76
|
+
0x02, 0x0B, 0x00,
|
77
|
+
0x02, 0x0F, 0x00,
|
78
|
+
0x03, 0x00, 0x00,
|
79
|
+
0x03, 0x04, 0x00,
|
80
|
+
0x03, 0x08, 0x00,
|
81
|
+
0x03, 0x0C, 0x00,
|
82
|
+
0x03, 0x01, 0x00,
|
83
|
+
0x03, 0x05, 0x00,
|
84
|
+
0x03, 0x09, 0x00,
|
85
|
+
0x03, 0x0D, 0x00,
|
86
|
+
0x03, 0x02, 0x00,
|
87
|
+
0x03, 0x06, 0x00,
|
88
|
+
0x03, 0x0A, 0x00,
|
89
|
+
0x03, 0x0E, 0x00,
|
90
|
+
0x03, 0x03, 0x00,
|
91
|
+
0x03, 0x07, 0x00,
|
92
|
+
0x03, 0x0B, 0x00,
|
93
|
+
0x03, 0x0F, 0x00,
|
94
|
+
0x00, 0x00, 0x01,
|
95
|
+
0x00, 0x02, 0x00,
|
96
|
+
0x03, 0x00, 0x00,
|
97
|
+
0x01, 0x01, 0x01,
|
98
|
+
0x02, 0x01, 0x03,
|
99
|
+
0x01, 0x00, 0x02,
|
100
|
+
0x01, 0x02, 0x02,
|
101
|
+
0x02, 0x03, 0x02,
|
102
|
+
0x00, 0x03, 0x01,
|
103
|
+
0x03, 0x02, 0x03,
|
104
|
+
0x03, 0x03, 0x0C,
|
105
|
+
0x00, 0x0D, 0x00,
|
106
|
+
0x0C, 0x00, 0x0D,
|
107
|
+
0x00, 0x0C, 0x00,
|
108
|
+
0x0D, 0x00, 0x0C,
|
109
|
+
0x00, 0x0D, 0x00]
|
110
|
+
|
111
111
|
def sysex_prefix; @sysex_prefix ||= super + [0x00, 0x01, 0x3F, 0x2B]; end
|
112
112
|
|
113
113
|
def decode_shoulder(decoded, note, _velocity)
|