launchpad 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,23 +1,70 @@
1
1
  = launchpad
2
2
 
3
- 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.
3
+ This gem provides an interface to access novation's launchpad programmatically. LEDs can be lighted and button presses can be responded to. Internally, launchpad's MIDI input/output is used to accomplish this.
4
4
 
5
- This is the first version, nothing sophisticated, but you can access the launchpad already. In and out. The API might change quite a bit during the next releases, so don't rely on it, this is work in progress. If you need anything or think the interface could be improved in any way, please contact me.
5
+ The interfaces should be rather stable now, so experiment with them and comment on their usability. This still is work in progress. If you need anything or think the interfaces could be improved in any way, please contact me.
6
6
 
7
- Sometimes, the launchpad won't react to anything. Don't despair, just dis- and reconnect the thing. It seems that some (unexpected) MIDI signals make it hickup.
7
+ Sometimes, the launchpad won't react to anything or react to/light up the wrong LEDs. Don't despair, just dis- and reconnect the thing. It seems that some (unexpected) MIDI signals make it hickup.
8
8
 
9
9
 
10
10
  == Requirements
11
11
 
12
- * Jan Krutisch's portmidi gem
13
- * Roger B. Dannenberg's portmidi library
12
+ * Roger B. Dannenberg's {portmidi library}[http://sourceforge.net/projects/portmedia/]
13
+ * Jan Krutisch's {portmidi gem}[http://github.com/halfbyte/portmidi]
14
+
15
+
16
+ == Installation
17
+
18
+ The gem is hosted on Gemcutter[http://gemcutter.org/], so in order to use it, you're gonna install the gemcutter gem (for details, see their site):
19
+
20
+ gem install gemcutter
21
+ gem tumble
22
+
23
+ After that, it's a simple gem install:
24
+
25
+ gem install launchpad
26
+
27
+
28
+ == Usage
29
+
30
+ There are two main entry points:
31
+
32
+ * require 'launchpad/device', providing Launchpad::Device, which handles all the basic input/output stuff
33
+ * require 'launchpad/interaction' or just 'launchpad', additionally providing Launchpad::Interaction, which lets you respond to actions (button presses/releases)
34
+
35
+ This is a simple example (only requiring the device for output) that switches on all LEDs (for testing), resets the launchpad again and then lights the grid button at position 4/4 (from top left).
36
+
37
+ require 'rubygems'
38
+ require 'launchpad/device'
39
+
40
+ device = Launchpad::Device.new
41
+ device.test_leds
42
+ sleep 1
43
+ device.reset
44
+ sleep 1
45
+ device.change :grid, :x => 4, :y => 4, :red => :high, :green => :low
46
+
47
+ This is an interaction example lighting all grid buttons in red when pressed and keeping them lit.
48
+
49
+ require 'rubygems'
50
+ require 'launchpad'
51
+
52
+ interaction = Launchpad::Interaction.new
53
+ interaction.response_to(:grid, :down) do |interaction, action|
54
+ interaction.device.change(:grid, action.merge(:red => :high))
55
+ end
56
+
57
+ interaction.start
58
+
59
+
60
+ For more details, see the examples. examples/color_picker.rb is the most complex example with interaction.
14
61
 
15
62
 
16
63
  == Near future plans
17
64
 
18
- * dedicated interaction with control buttons (top and right row)
19
- * listeners for presses on single buttons/button areas
20
- * double buffering (already there but has some glitches)
65
+ * close devices properly as soon as halfbyte's pulled my changes to portmidi
66
+ * interaction responses for presses on single grid buttons/button areas
67
+ * double buffering
21
68
  * bitmap rendering
22
69
 
23
70
 
data/Rakefile CHANGED
@@ -14,7 +14,8 @@ begin
14
14
  gem.version = Launchpad::VERSION
15
15
  gem.authors = ['Thomas Jachmann']
16
16
  gem.add_dependency('portmidi')
17
- #gem.add_development_dependency 'thoughtbot-shoulda', '>= 0'
17
+ gem.add_development_dependency('thoughtbot-shoulda', '>= 0')
18
+ gem.add_development_dependency('mocha')
18
19
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
19
20
  end
20
21
  Jeweler::GemcutterTasks.new
@@ -0,0 +1,99 @@
1
+ require File.join(File.dirname(__FILE__), 'setup')
2
+
3
+ # need to declare as instance variables here for being able to access
4
+ # @interaction within interactors created by create_interactor_block
5
+ device = Launchpad::Device.new
6
+ interaction = Launchpad::Interaction.new(:device => device)
7
+
8
+ # build color arrays for color display views
9
+ colors_single = [
10
+ [ 0, 1, 2, 3, 0, 0, 0, 0],
11
+ [16, 17, 18, 19, 0, 0, 0, 0],
12
+ [32, 33, 34, 35, 0, 0, 0, 0],
13
+ [48, 49, 50, 51, 0, 0, 0, 0],
14
+ [0] * 8,
15
+ [0] * 8,
16
+ [0] * 8,
17
+ [0] * 8,
18
+ [0] * 8
19
+ ]
20
+ colors_double = [
21
+ [ 0, 0, 1, 1, 2, 2, 3, 3],
22
+ [ 0, 0, 1, 1, 2, 2, 3, 3],
23
+ [16, 16, 17, 17, 18, 18, 19, 19],
24
+ [16, 16, 17, 17, 18, 18, 19, 19],
25
+ [32, 32, 33, 33, 34, 34, 35, 35],
26
+ [32, 32, 33, 33, 34, 34, 35, 35],
27
+ [48, 48, 49, 49, 50, 50, 51, 51],
28
+ [48, 48, 49, 49, 50, 50, 51, 51],
29
+ [0] * 8
30
+ ]
31
+ colors_mirrored = [
32
+ [ 0, 1, 2, 3, 3, 2, 1, 0],
33
+ [16, 17, 18, 19, 19, 18, 17, 16],
34
+ [32, 33, 34, 35, 35, 34, 33, 32],
35
+ [48, 49, 50, 51, 51, 50, 49, 48],
36
+ [48, 49, 50, 51, 51, 50, 49, 48],
37
+ [32, 33, 34, 35, 35, 34, 33, 32],
38
+ [16, 17, 18, 19, 19, 18, 17, 16],
39
+ [ 0, 1, 2, 3, 3, 2, 1, 0],
40
+ [0] * 8
41
+ ]
42
+
43
+ # setup color display views
44
+ def display_color_view(colors)
45
+ lambda do |interaction, action|
46
+ # set color
47
+ interaction.device.change_all(colors)
48
+ # register mute interactor on scene buttons
49
+ interaction.response_to(%w(scene1 scene2 scene3 scene4 scene5 scene6 scene7 scene8), :down, :exclusive => true, &@mute)
50
+ end
51
+ end
52
+ interaction.response_to(:up, :down, &display_color_view(colors_single + [48, 16, 16, 16]))
53
+ interaction.response_to(:down, :down, &display_color_view(colors_double + [16, 48, 16, 16]))
54
+ interaction.response_to(:left, :down, &display_color_view(colors_mirrored + [16, 16, 48, 16]))
55
+
56
+ # setup color picker view
57
+ def display_color(opts)
58
+ lambda do |interaction, action|
59
+ @red = opts[:red] if opts[:red]
60
+ @green = opts[:green] if opts[:green]
61
+ colors = [(@green * 16 + @red)] * 64
62
+ scenes = [@red == 3 ? 51 : 3, @red == 2 ? 51 : 2, @red == 1 ? 51 : 1, @red == 0 ? 51 : 0, @green == 3 ? 51 : 48, @green == 2 ? 51 : 32, @green == 1 ? 51 : 16, @green == 0 ? 51 : 0]
63
+ interaction.device.change_all(colors + scenes + [16, 16, 16, 48])
64
+ end
65
+ end
66
+ interaction.response_to(:right, :down) do |interaction, action|
67
+ @red = 0
68
+ @green = 0
69
+ # register color picker interactors on scene buttons
70
+ interaction.response_to(:scene1, :down, :exclusive => true, &display_color(:red => 3))
71
+ interaction.response_to(:scene2, :down, :exclusive => true, &display_color(:red => 2))
72
+ interaction.response_to(:scene3, :down, :exclusive => true, &display_color(:red => 1))
73
+ interaction.response_to(:scene4, :down, :exclusive => true, &display_color(:red => 0))
74
+ interaction.response_to(:scene5, :down, :exclusive => true, &display_color(:green => 3))
75
+ interaction.response_to(:scene6, :down, :exclusive => true, &display_color(:green => 2))
76
+ interaction.response_to(:scene7, :down, :exclusive => true, &display_color(:green => 1))
77
+ interaction.response_to(:scene8, :down, :exclusive => true, &display_color(:green => 0))
78
+ # display color
79
+ interaction.respond_to(:scene8, :down)
80
+ end
81
+
82
+ # mixer button terminates interaction on button up
83
+ interaction.response_to(:mixer) do |interaction, action|
84
+ interaction.device.change(:mixer, :red => action[:state] == :down ? :hi : :off)
85
+ interaction.stop if action[:state] == :up
86
+ end
87
+
88
+ # setup mute display interactors on all unused buttons
89
+ @mute = display_color_view([0] * 72 + [16, 16, 16, 16])
90
+ interaction.response_to(%w(session user1 user2 grid), :down, &@mute)
91
+
92
+ # display mute view
93
+ interaction.respond_to(:session, :down)
94
+
95
+ # start interacting
96
+ interaction.start
97
+
98
+ # sleep so that the messages can be sent before the program terminates
99
+ sleep 0.1
data/examples/colors.rb CHANGED
@@ -1,20 +1,21 @@
1
1
  require File.join(File.dirname(__FILE__), 'setup')
2
2
 
3
- l = Launchpad.new(:input => false, :output => true)
4
-
5
- sleep 1
3
+ device = Launchpad::Device.new(:input => false, :output => true)
6
4
 
7
5
  pos_x = pos_y = 0
8
6
  4.times do |red|
9
7
  4.times do |green|
10
- l.single :x => pos_x, :y => pos_y, :red => red, :green => green, :mode => :buffering
11
- l.single :x => 7 - pos_x, :y => pos_y, :red => red, :green => green, :mode => :buffering
12
- l.single :x => pos_x, :y => 7 - pos_y, :red => red, :green => green, :mode => :buffering
13
- l.single :x => 7 - pos_x, :y => 7 - pos_y, :red => red, :green => green, :mode => :buffering
8
+ device.change :grid, :x => pos_x, :y => pos_y, :red => red, :green => green
9
+ device.change :grid, :x => 7 - pos_x, :y => pos_y, :red => red, :green => green
10
+ device.change :grid, :x => pos_x, :y => 7 - pos_y, :red => red, :green => green
11
+ device.change :grid, :x => 7 - pos_x, :y => 7 - pos_y, :red => red, :green => green
14
12
  pos_y += 1
13
+ # sleep, otherwise the connection drops some messages - WTF?
14
+ sleep 0.01
15
15
  end
16
16
  pos_x += 1
17
17
  pos_y = 0
18
18
  end
19
19
 
20
- sleep 1
20
+ # sleep so that the messages can be sent before the program terminates
21
+ sleep 0.1
@@ -0,0 +1,68 @@
1
+ require File.join(File.dirname(__FILE__), 'setup')
2
+
3
+ interaction = Launchpad::Interaction.new
4
+
5
+ current_color = {
6
+ :red => :hi,
7
+ :green => :hi,
8
+ :mode => :normal
9
+ }
10
+
11
+ def update_scene_buttons(d, color)
12
+ on = {:red => :hi, :green => :hi}
13
+ d.change(:scene1, color[:red] == :hi ? on : {:red => :hi})
14
+ d.change(:scene2, color[:red] == :med ? on : {:red => :med})
15
+ d.change(:scene3, color[:red] == :lo ? on : {:red => :lo})
16
+ d.change(:scene4, color[:red] == :off ? on : {:red => :off})
17
+ d.change(:scene5, color[:green] == :hi ? on : {:green => :hi})
18
+ d.change(:scene6, color[:green] == :med ? on : {:green => :med})
19
+ d.change(:scene7, color[:green] == :lo ? on : {:green => :lo})
20
+ d.change(:scene8, color[:green] == :off ? on : {:green => :off})
21
+ d.change(:user1, :green => color[:mode] == :normal ? :lo : :hi, :mode => :flashing)
22
+ d.change(:user2, :green => color[:mode] == :normal ? :hi : :lo)
23
+ end
24
+
25
+ def choose_color(color, opts)
26
+ lambda do |interaction, action|
27
+ color.update(opts)
28
+ update_scene_buttons(interaction.device, color)
29
+ end
30
+ end
31
+
32
+ # register color picker interactors on scene buttons
33
+ interaction.response_to(:scene1, :down, &choose_color(current_color, :red => :hi))
34
+ interaction.response_to(:scene2, :down, &choose_color(current_color, :red => :med))
35
+ interaction.response_to(:scene3, :down, &choose_color(current_color, :red => :lo))
36
+ interaction.response_to(:scene4, :down, &choose_color(current_color, :red => :off))
37
+ interaction.response_to(:scene5, :down, &choose_color(current_color, :green => :hi))
38
+ interaction.response_to(:scene6, :down, &choose_color(current_color, :green => :med))
39
+ interaction.response_to(:scene7, :down, &choose_color(current_color, :green => :lo))
40
+ interaction.response_to(:scene8, :down, &choose_color(current_color, :green => :off))
41
+
42
+ # register mode picker interactors on user buttons
43
+ interaction.response_to(:user1, :down, &choose_color(current_color, :mode => :flashing))
44
+ interaction.response_to(:user2, :down, &choose_color(current_color, :mode => :normal))
45
+
46
+ # update scene buttons and start flashing
47
+ update_scene_buttons(interaction.device, current_color)
48
+ interaction.device.flashing_auto
49
+
50
+ # feedback for grid buttons
51
+ interaction.response_to(:grid, :down) do |interaction, action|
52
+ #coord = 16 * action[:y] + action[:x]
53
+ #brightness = flags[coord] ? :off : :hi
54
+ #flags[coord] = !flags[coord]
55
+ interaction.device.change(:grid, action.merge(current_color))
56
+ end
57
+
58
+ # mixer button terminates interaction on button up
59
+ interaction.response_to(:mixer) do |interaction, action|
60
+ interaction.device.change(:mixer, :red => action[:state] == :down ? :hi : :off)
61
+ interaction.stop if action[:state] == :up
62
+ end
63
+
64
+ # start interacting
65
+ interaction.start
66
+
67
+ # sleep so that the messages can be sent before the program terminates
68
+ sleep 0.1
@@ -0,0 +1,25 @@
1
+ require File.join(File.dirname(__FILE__), 'setup')
2
+
3
+ interaction = Launchpad::Interaction.new
4
+
5
+ flags = Hash.new(false)
6
+
7
+ # yellow feedback for grid buttons
8
+ interaction.response_to(:grid, :down) do |interaction, action|
9
+ coord = 16 * action[:y] + action[:x]
10
+ brightness = flags[coord] ? :off : :hi
11
+ flags[coord] = !flags[coord]
12
+ interaction.device.change(:grid, action.merge(:red => brightness, :green => brightness))
13
+ end
14
+
15
+ # mixer button terminates interaction on button up
16
+ interaction.response_to(:mixer) do |interaction, action|
17
+ interaction.device.change(:mixer, :red => action[:state] == :down ? :hi : :off)
18
+ interaction.stop if action[:state] == :up
19
+ end
20
+
21
+ # start interacting
22
+ interaction.start
23
+
24
+ # sleep so that the messages can be sent before the program terminates
25
+ sleep 0.1
data/examples/feedback.rb CHANGED
@@ -1,5 +1,34 @@
1
1
  require File.join(File.dirname(__FILE__), 'setup')
2
2
 
3
- Launchpad.start do |l, x, y, state|
4
- l.single(:x => x, :y => y, :red => state ? 3 : 0)
3
+ interaction = Launchpad::Interaction.new
4
+
5
+ def brightness(action)
6
+ action[:state] == :down ? :hi : :off
7
+ end
8
+
9
+ # yellow feedback for grid buttons
10
+ interaction.response_to(:grid) do |interaction, action|
11
+ b = brightness(action)
12
+ interaction.device.change(:grid, action.merge(:red => b, :green => b))
5
13
  end
14
+
15
+ # red feedback for top control buttons
16
+ interaction.response_to([:up, :down, :left, :right, :session, :user1, :user2, :mixer]) do |interaction, action|
17
+ interaction.device.change(action[:type], :red => brightness(action))
18
+ end
19
+
20
+ # green feedback for scene buttons
21
+ interaction.response_to([:scene1, :scene2, :scene3, :scene4, :scene5, :scene6, :scene7, :scene8]) do |interaction, action|
22
+ interaction.device.change(action[:type], :green => brightness(action))
23
+ end
24
+
25
+ # mixer button terminates interaction on button up
26
+ interaction.response_to(:mixer, :up) do |interaction, action|
27
+ interaction.stop
28
+ end
29
+
30
+ # start interacting
31
+ interaction.start
32
+
33
+ # sleep so that the messages can be sent before the program terminates
34
+ sleep 0.1
data/examples/reset.rb ADDED
@@ -0,0 +1,6 @@
1
+ require File.join(File.dirname(__FILE__), 'setup')
2
+
3
+ Launchpad::Device.new.reset
4
+
5
+ # sleep so that the messages can be sent before the program terminates
6
+ sleep 0.1
data/examples/setup.rb CHANGED
@@ -1,3 +1,5 @@
1
1
  # normally, this is done by rubygems (or whatever you use for your library management)
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
2
4
  require 'rubygems'
3
- require File.join(File.dirname(__FILE__), '..', 'lib', 'launchpad')
5
+ require 'launchpad'
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.0.2"
8
+ s.version = "0.1.0"
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{2009-11-08}
12
+ s.date = %q{2009-11-13}
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 = [
@@ -22,15 +22,24 @@ Gem::Specification.new do |s|
22
22
  "LICENSE",
23
23
  "README.rdoc",
24
24
  "Rakefile",
25
+ "examples/color_picker.rb",
25
26
  "examples/colors.rb",
27
+ "examples/doodle.rb",
28
+ "examples/drawing_board.rb",
26
29
  "examples/feedback.rb",
30
+ "examples/reset.rb",
27
31
  "examples/setup.rb",
28
32
  "experiments/wandering_dot.rb",
29
33
  "launchpad.gemspec",
30
34
  "lib/launchpad.rb",
35
+ "lib/launchpad/device.rb",
36
+ "lib/launchpad/errors.rb",
37
+ "lib/launchpad/interaction.rb",
38
+ "lib/launchpad/midi_codes.rb",
31
39
  "lib/launchpad/version.rb",
32
40
  "test/helper.rb",
33
- "test/test_launchpad-gem.rb"
41
+ "test/test_device.rb",
42
+ "test/test_interaction.rb"
34
43
  ]
35
44
  s.homepage = %q{http://github.com/thomasjachmann/launchpad}
36
45
  s.rdoc_options = ["--charset=UTF-8"]
@@ -39,9 +48,14 @@ Gem::Specification.new do |s|
39
48
  s.summary = %q{A gem for accessing novation's launchpad programmatically and easily.}
40
49
  s.test_files = [
41
50
  "test/helper.rb",
42
- "test/test_launchpad-gem.rb",
51
+ "test/test_device.rb",
52
+ "test/test_interaction.rb",
53
+ "examples/color_picker.rb",
43
54
  "examples/colors.rb",
55
+ "examples/doodle.rb",
56
+ "examples/drawing_board.rb",
44
57
  "examples/feedback.rb",
58
+ "examples/reset.rb",
45
59
  "examples/setup.rb"
46
60
  ]
47
61
 
@@ -51,11 +65,17 @@ Gem::Specification.new do |s|
51
65
 
52
66
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
53
67
  s.add_runtime_dependency(%q<portmidi>, [">= 0"])
68
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
69
+ s.add_development_dependency(%q<mocha>, [">= 0"])
54
70
  else
55
71
  s.add_dependency(%q<portmidi>, [">= 0"])
72
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
73
+ s.add_dependency(%q<mocha>, [">= 0"])
56
74
  end
57
75
  else
58
76
  s.add_dependency(%q<portmidi>, [">= 0"])
77
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
78
+ s.add_dependency(%q<mocha>, [">= 0"])
59
79
  end
60
80
  end
61
81
 
data/lib/launchpad.rb CHANGED
@@ -1,167 +1,2 @@
1
- require 'portmidi'
2
-
3
- class Launchpad
4
-
5
- class LaunchpadError < StandardError; end
6
- class NoInputAllowed < LaunchpadError; end
7
- class NoOutputAllowed < LaunchpadError; end
8
- class NoLocationError < LaunchpadError; end
9
- class CommunicationError < LaunchpadError
10
- attr_accessor :source
11
- def initialize(e)
12
- super(e.portmidi_error)
13
- self.source = e
14
- end
15
- end
16
-
17
- OFF = 0x80
18
- ON = 0x90
19
- CC = 0xB0
20
-
21
- def initialize(opts = nil)
22
- opts = {
23
- :device_name => 'Launchpad',
24
- :input => true,
25
- :output => true
26
- }.merge(opts || {})
27
- Portmidi.start
28
- if opts[:input]
29
- input_device = Portmidi.input_devices.select {|device| device.name == opts[:device_name]}.first
30
- @input = Portmidi::Input.new(input_device.device_id)
31
- end
32
- if opts[:output]
33
- output_device = Portmidi.output_devices.select {|device| device.name == opts[:device_name]}.first
34
- @output = Portmidi::Output.new(output_device.device_id)
35
- reset
36
- end
37
- @buffering = false
38
- end
39
-
40
- def self.start(opts = nil, &block)
41
- opts ||= {}
42
- latency = (opts.delete(:latency) || 0.001).to_f
43
- launchpad = Launchpad.new(opts.merge({:input => true, :output => true}))
44
- loop do
45
- messages = launchpad.input
46
- if messages
47
- messages.each do |message|
48
- message = parse_message(message)
49
- if message[:code] == ON
50
- block.call(launchpad, message[:x], message[:y], message[:state])
51
- end
52
- end
53
- end
54
- sleep latency
55
- end
56
- rescue Portmidi::DeviceError => e
57
- raise CommunicationError.new(e)
58
- ensure
59
- launchpad.reset if launchpad
60
- end
61
-
62
- # Reset the launchpad - all settings are reset and all LEDs are switched off
63
- def reset
64
- output(CC, 0x00, 0x00)
65
- end
66
-
67
- # Light all LEDs (for testing purposes)
68
- # takes an optional parameter brightness (1-3, defaults to 3)
69
- def light_all(brightness = 3)
70
- output(CC, 0x00, 124 + min_max_color(brightness, false))
71
- end
72
-
73
- def start_buffering
74
- output(CC, 0x00, 0x31)
75
- @buffering = true
76
- end
77
-
78
- def flush_buffer(end_buffering = true)
79
- output(CC, 0x00, 0x34)
80
- if end_buffering
81
- output(CC, 0x00, 0x30)
82
- @buffering = false
83
- end
84
- end
85
-
86
- # Switches a single LED
87
- # * :x => x coordinate (0 based from top left, mandatory)
88
- # * :y => y coordinate (0 based from top left, mandatory)
89
- # * :red => brightness of red LED (0-3, optional, defaults to 0)
90
- # * :green => brightness of red LED (0-3, optional, defaults to 0)
91
- # * :mode => button behaviour (:normal, :flashing, :buffering, optional, defaults to :normal)
92
- def single(opts)
93
- location = location(opts)
94
- velocity = velocity(opts)
95
- output(ON, location, velocity)
96
- end
97
-
98
- def multi(*velocities)
99
- output(CC, 0x01, 0x00)
100
- output(0x92, *velocities)
101
- end
102
-
103
- # Switches LEDs marked as flashing on (when using custom timer for flashing)
104
- def custom_flashing_on
105
- output(CC, 0x00, 0x20)
106
- end
107
-
108
- # Switches LEDs marked as flashing off (when using custom timer for flashing)
109
- def custom_flashing_off
110
- output(CC, 0x00, 0x21)
111
- end
112
-
113
- # Starts flashing LEDs marked as flashing automatically
114
- def start_auto_flashing
115
- output(CC, 0x00, 0x28)
116
- end
117
-
118
- # Stops flashing LEDs marked as flashing automatically (turning them on)
119
- alias_method :stop_auto_flashing, :custom_flashing_on
120
-
121
- def coordinates(location)
122
- [location % 16, location / 16]
123
- end
124
-
125
- def input
126
- raise NoInputAllowed if @input.nil?
127
- @input.read(16)
128
- end
129
-
130
- def output(*args)
131
- raise NoOutputAllowed if @output.nil?
132
- @output.write([{:message => args, :timestamp => 0}])
133
- end
134
-
135
- private
136
-
137
- def self.parse_message(message)
138
- message = message[:message]
139
- {
140
- :code => message[0],
141
- :x => message[1] % 16,
142
- :y => message[1] / 16,
143
- :state => message[2] == 127
144
- }
145
- end
146
-
147
- def location(opts)
148
- raise NoLocationError.new('you need to specify a location (x/y, 0 based from top left)') if (y = opts[:y]).nil? || (x = opts[:x]).nil?
149
- y * 16 + x
150
- end
151
-
152
- def velocity(opts)
153
- red = min_max_color(opts[:red] || 0)
154
- green = min_max_color(opts[:green] || 0)
155
- flags = case opts[:mode]
156
- when :flashing then 8
157
- when :buffering then 0
158
- else 12
159
- end
160
- (16 * (green)) + red + flags
161
- end
162
-
163
- def min_max_color(color, with_off = true)
164
- [[with_off ? 0 : 1, color.to_i].max, 3].min
165
- end
166
-
167
- end
1
+ require 'launchpad/device'
2
+ require 'launchpad/interaction'