launchpad 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.
- data/README.rdoc +6 -0
- data/Rakefile +1 -1
- data/examples/color_picker.rb +1 -4
- data/launchpad.gemspec +5 -5
- data/lib/launchpad.rb +0 -1
- data/lib/launchpad/device.rb +4 -1
- data/lib/launchpad/interaction.rb +44 -10
- data/lib/launchpad/version.rb +1 -1
- data/test/test_interaction.rb +114 -45
- metadata +3 -3
data/README.rdoc
CHANGED
@@ -67,10 +67,16 @@ For more details, see the examples. examples/color_picker.rb is the most complex
|
|
67
67
|
|
68
68
|
* interaction responses for presses on single grid buttons/button areas
|
69
69
|
* bitmap rendering
|
70
|
+
* internal tracking of LED states for both buffers
|
70
71
|
|
71
72
|
|
72
73
|
== Changelog
|
73
74
|
|
75
|
+
=== v.0.2.1
|
76
|
+
|
77
|
+
* Launchpad::Interaction#close now properly stops interaction first
|
78
|
+
* multi threading: Launchpad::Interaction#start method can be called with :detached => true to allow calling thread to continue
|
79
|
+
|
74
80
|
=== v.0.2.0
|
75
81
|
|
76
82
|
* double buffering (see Launchpad::Device#buffering_mode)
|
data/Rakefile
CHANGED
@@ -14,7 +14,7 @@ begin
|
|
14
14
|
gem.version = Launchpad::VERSION
|
15
15
|
gem.authors = ['Thomas Jachmann']
|
16
16
|
gem.has_rdoc = true
|
17
|
-
gem.add_dependency('portmidi')
|
17
|
+
gem.add_dependency('portmidi', '>= 0.0.6')
|
18
18
|
gem.add_development_dependency('thoughtbot-shoulda', '>= 0')
|
19
19
|
gem.add_development_dependency('mocha')
|
20
20
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
data/examples/color_picker.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'setup')
|
2
2
|
|
3
|
-
|
4
|
-
# @interaction within interactors created by create_interactor_block
|
5
|
-
device = Launchpad::Device.new
|
6
|
-
interaction = Launchpad::Interaction.new(:device => device)
|
3
|
+
interaction = Launchpad::Interaction.new
|
7
4
|
|
8
5
|
# build color arrays for color display views
|
9
6
|
colors_single = [
|
data/launchpad.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{launchpad}
|
8
|
-
s.version = "0.2.
|
8
|
+
s.version = "0.2.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Thomas Jachmann"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2010-01-24}
|
13
13
|
s.description = %q{This gem provides an interface to access novation's launchpad programmatically. LEDs can be lighted and button presses can be evaluated using launchpad's MIDI input/output.}
|
14
14
|
s.email = %q{tom.j@gmx.net}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -66,16 +66,16 @@ Gem::Specification.new do |s|
|
|
66
66
|
s.specification_version = 3
|
67
67
|
|
68
68
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
69
|
-
s.add_runtime_dependency(%q<portmidi>, [">= 0"])
|
69
|
+
s.add_runtime_dependency(%q<portmidi>, [">= 0.0.6"])
|
70
70
|
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
71
71
|
s.add_development_dependency(%q<mocha>, [">= 0"])
|
72
72
|
else
|
73
|
-
s.add_dependency(%q<portmidi>, [">= 0"])
|
73
|
+
s.add_dependency(%q<portmidi>, [">= 0.0.6"])
|
74
74
|
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
75
75
|
s.add_dependency(%q<mocha>, [">= 0"])
|
76
76
|
end
|
77
77
|
else
|
78
|
-
s.add_dependency(%q<portmidi>, [">= 0"])
|
78
|
+
s.add_dependency(%q<portmidi>, [">= 0.0.6"])
|
79
79
|
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
80
80
|
s.add_dependency(%q<mocha>, [">= 0"])
|
81
81
|
end
|
data/lib/launchpad.rb
CHANGED
data/lib/launchpad/device.rb
CHANGED
@@ -145,7 +145,10 @@ module Launchpad
|
|
145
145
|
# * Hash:
|
146
146
|
# [<tt>:red</tt>] brightness of red LED
|
147
147
|
# [<tt>:green</tt>] brightness of green LED
|
148
|
-
# [<tt>:mode</tt>] button mode
|
148
|
+
# [<tt>:mode</tt>] button mode, defaults to <tt>:normal</tt>, one of:
|
149
|
+
# [<tt>:normal/tt>] updates the LEDs for all circumstances (the new value will be written to both buffers)
|
150
|
+
# [<tt>:flashing/tt>] updates the LEDs for flashing (the new values will be written to buffer 0 while the LEDs will be off in buffer 1, see buffering_mode)
|
151
|
+
# [<tt>:buffering/tt>] updates the LEDs for the current update_buffer only
|
149
152
|
# the array consists of 64 colors for the grid buttons,
|
150
153
|
# 8 colors for the scene buttons (top to bottom)
|
151
154
|
# and 8 colors for the top control buttons (left to right),
|
@@ -53,7 +53,13 @@ module Launchpad
|
|
53
53
|
end
|
54
54
|
|
55
55
|
# Closes the interaction's device - nothing can be done with the interaction/device afterwards.
|
56
|
+
#
|
57
|
+
# Errors raised:
|
58
|
+
#
|
59
|
+
# [Launchpad::NoInputAllowedError] when input is not enabled on the interaction's device
|
60
|
+
# [Launchpad::CommunicationError] when anything unexpected happens while communicating with the
|
56
61
|
def close
|
62
|
+
stop
|
57
63
|
@device.close
|
58
64
|
end
|
59
65
|
|
@@ -62,27 +68,55 @@ module Launchpad
|
|
62
68
|
@device.closed?
|
63
69
|
end
|
64
70
|
|
65
|
-
# Starts interacting with the launchpad
|
66
|
-
# the interaction was properly stopped via stop.
|
71
|
+
# Starts interacting with the launchpad. Resets the device when
|
72
|
+
# the interaction was properly stopped via stop or close.
|
73
|
+
#
|
74
|
+
# Optional options hash:
|
75
|
+
#
|
76
|
+
# [<tt>:detached</tt>] <tt>true/false</tt>,
|
77
|
+
# whether to detach the interaction, method is blocking when +false+
|
78
|
+
# optional, defaults to +false+
|
67
79
|
#
|
68
80
|
# Errors raised:
|
69
81
|
#
|
70
82
|
# [Launchpad::NoInputAllowedError] when input is not enabled on the interaction's device
|
83
|
+
# [Launchpad::NoOutputAllowedError] when output is not enabled on the interaction's device
|
71
84
|
# [Launchpad::CommunicationError] when anything unexpected happens while communicating with the launchpad
|
72
|
-
def start
|
85
|
+
def start(opts = nil)
|
86
|
+
opts = {
|
87
|
+
:detached => false
|
88
|
+
}.merge(opts || {})
|
73
89
|
@active = true
|
74
|
-
|
75
|
-
|
76
|
-
|
90
|
+
@reader_thread ||= Thread.new do
|
91
|
+
begin
|
92
|
+
while @active do
|
93
|
+
@device.read_pending_actions.each {|action| respond_to_action(action)}
|
94
|
+
sleep @latency unless @latency <= 0
|
95
|
+
end
|
96
|
+
rescue Portmidi::DeviceError => e
|
97
|
+
raise CommunicationError.new(e)
|
98
|
+
ensure
|
99
|
+
@device.reset
|
100
|
+
end
|
77
101
|
end
|
78
|
-
@
|
79
|
-
rescue Portmidi::DeviceError => e
|
80
|
-
raise CommunicationError.new(e)
|
102
|
+
@reader_thread.join unless opts[:detached]
|
81
103
|
end
|
82
104
|
|
83
|
-
# Stops interacting with the launchpad
|
105
|
+
# Stops interacting with the launchpad.
|
106
|
+
#
|
107
|
+
# Errors raised:
|
108
|
+
#
|
109
|
+
# [Launchpad::NoInputAllowedError] when input is not enabled on the interaction's device
|
110
|
+
# [Launchpad::CommunicationError] when anything unexpected happens while communicating with the
|
84
111
|
def stop
|
85
112
|
@active = false
|
113
|
+
if @reader_thread
|
114
|
+
# run (resume from sleep) and wait for @reader_thread to end
|
115
|
+
@reader_thread.run if @reader_thread.alive?
|
116
|
+
@reader_thread.join
|
117
|
+
@reader_thread = nil
|
118
|
+
end
|
119
|
+
nil
|
86
120
|
end
|
87
121
|
|
88
122
|
# Registers a response to one or more actions.
|
data/lib/launchpad/version.rb
CHANGED
data/test/test_interaction.rb
CHANGED
@@ -16,6 +16,11 @@ class TestInteraction < Test::Unit::TestCase
|
|
16
16
|
assert_equal 'device', Launchpad::Interaction.new(:device_name => 'device').device
|
17
17
|
end
|
18
18
|
|
19
|
+
should 'create device with given input_device_id/output_device_id' do
|
20
|
+
Launchpad::Device.expects(:new).with(:input_device_id => 'in', :output_device_id => 'out', :input => true, :output => true).returns('device')
|
21
|
+
assert_equal 'device', Launchpad::Interaction.new(:input_device_id => 'in', :output_device_id => 'out').device
|
22
|
+
end
|
23
|
+
|
19
24
|
should 'initialize device if given' do
|
20
25
|
assert_equal 'device', Launchpad::Interaction.new(:device => 'device').device
|
21
26
|
end
|
@@ -28,18 +33,17 @@ class TestInteraction < Test::Unit::TestCase
|
|
28
33
|
|
29
34
|
context 'close' do
|
30
35
|
|
31
|
-
should '
|
32
|
-
interaction = Launchpad::Interaction.new
|
33
|
-
|
36
|
+
should 'not be active' do
|
37
|
+
interaction = Launchpad::Interaction.new
|
38
|
+
interaction.start(:detached => true)
|
34
39
|
interaction.close
|
40
|
+
assert !interaction.active
|
35
41
|
end
|
36
42
|
|
37
|
-
should '
|
43
|
+
should 'close device' do
|
38
44
|
interaction = Launchpad::Interaction.new(:device => device = Launchpad::Device.new)
|
45
|
+
device.expects(:close)
|
39
46
|
interaction.close
|
40
|
-
assert_raise Launchpad::NoInputAllowedError do
|
41
|
-
interaction.start
|
42
|
-
end
|
43
47
|
end
|
44
48
|
|
45
49
|
end
|
@@ -57,27 +61,45 @@ class TestInteraction < Test::Unit::TestCase
|
|
57
61
|
|
58
62
|
context 'start' do
|
59
63
|
|
60
|
-
# this is kinda greybox tested, since I couldn't come up with another way to test a loop [thomas, 2009-11-11]
|
61
|
-
|
62
64
|
setup do
|
63
65
|
@interaction = Launchpad::Interaction.new(:device => @device = Launchpad::Device.new)
|
64
66
|
end
|
65
67
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
should 'set active to true' do
|
73
|
-
begin
|
74
|
-
@interaction.start
|
75
|
-
fail 'should raise BreakError'
|
76
|
-
rescue BreakError
|
77
|
-
assert @interaction.active
|
78
|
-
end
|
68
|
+
teardown do
|
69
|
+
begin
|
70
|
+
@interaction.close
|
71
|
+
rescue
|
72
|
+
# ignore, should be handled in tests, this is just to close all the spawned threads
|
79
73
|
end
|
80
|
-
|
74
|
+
end
|
75
|
+
|
76
|
+
# this is kinda greybox tested, since I couldn't come up with another way to test thread handling [thomas, 2010-01-24]
|
77
|
+
should 'set active to true in blocking mode' do
|
78
|
+
t = Thread.new {}
|
79
|
+
Thread.expects(:new).returns(t)
|
80
|
+
@interaction.start
|
81
|
+
assert @interaction.active
|
82
|
+
end
|
83
|
+
|
84
|
+
should 'set active to true in detached mode' do
|
85
|
+
@interaction.start(:detached => true)
|
86
|
+
assert @interaction.active
|
87
|
+
end
|
88
|
+
|
89
|
+
# this is kinda greybox tested, since I couldn't come up with another way to test thread handling [thomas, 2010-01-24]
|
90
|
+
should 'start a new thread and block in blocking mode' do
|
91
|
+
t = Thread.new {}
|
92
|
+
Thread.expects(:new).returns(t)
|
93
|
+
t.expects(:join)
|
94
|
+
@interaction.start
|
95
|
+
end
|
96
|
+
|
97
|
+
# this is kinda greybox tested, since I couldn't come up with another way to test thread handling [thomas, 2010-01-24]
|
98
|
+
should 'start a new thread and return in detached mode' do
|
99
|
+
t = Thread.new {}
|
100
|
+
Thread.expects(:new).returns(t)
|
101
|
+
t.expects(:join).never
|
102
|
+
@interaction.start(:detached => true)
|
81
103
|
end
|
82
104
|
|
83
105
|
should 'raise CommunicationError when Portmidi::DeviceError occurs' do
|
@@ -87,18 +109,16 @@ class TestInteraction < Test::Unit::TestCase
|
|
87
109
|
end
|
88
110
|
end
|
89
111
|
|
112
|
+
# this is kinda greybox tested, since I couldn't come up with another way to test thread handling [thomas, 2010-01-24]
|
90
113
|
should 'call respond_to_action with actions from respond_to_action' do
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
@interaction.start
|
97
|
-
fail 'should raise BreakError'
|
98
|
-
rescue BreakError
|
99
|
-
end
|
114
|
+
@interaction.stubs(:sleep).raises(BreakError)
|
115
|
+
@device.stubs(:read_pending_actions).returns(['message1', 'message2'])
|
116
|
+
@interaction.expects(:respond_to_action).with('message1').once
|
117
|
+
@interaction.expects(:respond_to_action).with('message2').once
|
118
|
+
@interaction.start(:detached => true)
|
100
119
|
end
|
101
120
|
|
121
|
+
# this is kinda greybox tested, since I couldn't come up with another way to test thread handling [thomas, 2010-01-24]
|
102
122
|
context 'sleep' do
|
103
123
|
|
104
124
|
setup do
|
@@ -106,52 +126,101 @@ class TestInteraction < Test::Unit::TestCase
|
|
106
126
|
end
|
107
127
|
|
108
128
|
should 'sleep with default latency of 0.001 when none given' do
|
109
|
-
|
129
|
+
assert_raise BreakError do
|
110
130
|
@interaction.expects(:sleep).with(0.001).raises(BreakError)
|
111
131
|
@interaction.start
|
112
|
-
fail 'should raise BreakError'
|
113
|
-
rescue BreakError
|
114
132
|
end
|
115
133
|
end
|
116
134
|
|
117
135
|
should 'sleep with given latency' do
|
118
|
-
|
136
|
+
assert_raise BreakError do
|
119
137
|
@interaction = Launchpad::Interaction.new(:latency => 4, :device => @device)
|
120
138
|
@interaction.expects(:sleep).with(4).raises(BreakError)
|
121
139
|
@interaction.start
|
122
|
-
fail 'should raise BreakError'
|
123
|
-
rescue BreakError
|
124
140
|
end
|
125
141
|
end
|
126
142
|
|
127
143
|
should 'sleep with absolute value of given negative latency' do
|
128
|
-
|
144
|
+
assert_raise BreakError do
|
129
145
|
@interaction = Launchpad::Interaction.new(:latency => -3.1, :device => @device)
|
130
146
|
@interaction.expects(:sleep).with(3.1).raises(BreakError)
|
131
147
|
@interaction.start
|
132
|
-
fail 'should raise BreakError'
|
133
|
-
rescue BreakError
|
134
148
|
end
|
135
149
|
end
|
136
150
|
|
137
|
-
should 'not sleep when latency is
|
151
|
+
should 'not sleep when latency is 0' do
|
152
|
+
@interaction = Launchpad::Interaction.new(:latency => 0, :device => @device)
|
153
|
+
@interaction.expects(:sleep).never
|
154
|
+
@interaction.start(:detached => true)
|
155
|
+
end
|
138
156
|
|
139
157
|
end
|
140
158
|
|
141
|
-
should 'reset the device after the loop'
|
159
|
+
should 'reset the device after the loop' do
|
160
|
+
@interaction.device.expects(:reset)
|
161
|
+
@interaction.start(:detached => true)
|
162
|
+
@interaction.stop
|
163
|
+
end
|
164
|
+
|
165
|
+
should 'raise NoOutputAllowedError on closed interaction' do
|
166
|
+
@interaction.close
|
167
|
+
assert_raise Launchpad::NoOutputAllowedError do
|
168
|
+
@interaction.start
|
169
|
+
end
|
170
|
+
end
|
142
171
|
|
143
172
|
end
|
144
173
|
|
145
174
|
context 'stop' do
|
146
175
|
|
147
|
-
should 'set active to false' do
|
176
|
+
should 'set active to false in blocking mode' do
|
177
|
+
i = Launchpad::Interaction.new
|
178
|
+
Thread.new do
|
179
|
+
i.start
|
180
|
+
end
|
181
|
+
assert i.active
|
182
|
+
i.stop
|
183
|
+
assert !i.active
|
184
|
+
end
|
185
|
+
|
186
|
+
should 'set active to false in detached mode' do
|
148
187
|
i = Launchpad::Interaction.new
|
149
|
-
i.
|
188
|
+
i.start(:detached => true)
|
150
189
|
assert i.active
|
151
190
|
i.stop
|
152
191
|
assert !i.active
|
153
192
|
end
|
154
193
|
|
194
|
+
should 'be callable anytime' do
|
195
|
+
i = Launchpad::Interaction.new
|
196
|
+
i.stop
|
197
|
+
i.start(:detached => true)
|
198
|
+
i.stop
|
199
|
+
i.stop
|
200
|
+
end
|
201
|
+
|
202
|
+
# this is kinda greybox tested, since I couldn't come up with another way to test thread handling [thomas, 2010-01-24]
|
203
|
+
should 'call run and join on a running reader thread' do
|
204
|
+
t = Thread.new {sleep}
|
205
|
+
Thread.expects(:new).returns(t)
|
206
|
+
t.expects(:run)
|
207
|
+
t.expects(:join)
|
208
|
+
i = Launchpad::Interaction.new
|
209
|
+
i.start(:detached => true)
|
210
|
+
i.stop
|
211
|
+
end
|
212
|
+
|
213
|
+
# this is kinda greybox tested, since I couldn't come up with another way to test tread handling [thomas, 2010-01-24]
|
214
|
+
should 'raise pending exceptions in detached mode' do
|
215
|
+
t = Thread.new {raise BreakError}
|
216
|
+
Thread.expects(:new).returns(t)
|
217
|
+
i = Launchpad::Interaction.new
|
218
|
+
i.start(:detached => true)
|
219
|
+
assert_raise BreakError do
|
220
|
+
i.stop
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
155
224
|
end
|
156
225
|
|
157
226
|
context 'response_to/no_response_to/respond_to' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: launchpad
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Jachmann
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-01-24 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -20,7 +20,7 @@ dependencies:
|
|
20
20
|
requirements:
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version:
|
23
|
+
version: 0.0.6
|
24
24
|
version:
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: thoughtbot-shoulda
|