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