mrjoy-launchpad 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f8c2eaea477fc7c3dd5587186a3ee2846705f14e
4
+ data.tar.gz: a5e36f0a19dd060bb7a48fd2414d4c05796981b8
5
+ SHA512:
6
+ metadata.gz: 4435b65556c71612909b4cd6ff980f506085d8a3fff825619d4e7ce72e9627b346bfae8f9865c86c9cb1c36580a37485991cc74331051aaf5ce8437d0b0062b5
7
+ data.tar.gz: c93a479be6b78023c09c9deedc2eb5818577a6cd7facb50cca3dd3fb77a6c5d8a5156ba312dc5dd30285a80d27354566318b027b3490cd32aea48ff1d9c99166
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.3
5
+ - 2.0.0
6
+ before_install:
7
+ - sudo apt-get update
8
+ - sudo apt-get install libportmidi-dev
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ #ruby-gemset=launchpad
2
+ ruby "2.2.2"
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in launchpad.gemspec
6
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Thomas Jachmann
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,136 @@
1
+ = launchpad
2
+
3
+ {<img src="https://travis-ci.org/thomasjachmann/launchpad.png?branch=master" alt="Build Status" />}[https://travis-ci.org/thomasjachmann/launchpad]
4
+
5
+ This gem provides a ruby 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.
6
+
7
+ The interfaces should be rather stable now (sorry, I changed quite a bit since the last release), 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.
8
+
9
+ 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.
10
+
11
+ == This Fork
12
+
13
+ * Added ability to handle inputs without spawning a thread per input.
14
+ * Modify to support for LaunchPad Mk 2. Note that this breaks Mk 1 support!
15
+ * Recognize identity string from this model.
16
+ * Fix X/Y coordinates for buttons.
17
+ * Add support for RGB colors.
18
+
19
+
20
+ == More Info
21
+
22
+ If you don't know what launchpad is, visit:
23
+
24
+ * Novation's site at http://de.novationmusic.com/products/midi_controller/launchpad
25
+ * my demo videos for this library at http://www.youtube.com/thomasjachmann
26
+ * other demos on youtube http://www.youtube.com/results?search_query=novation+launchpad
27
+
28
+ If you're into other languages or want to know what goes on behind the scenes MIDI wise, have a look at:
29
+
30
+ * Novation's MIDI programmer's reference at {www.novationmusic.com/support/launchpad}[http://www.novationmusic.com/support/launchpad/] (bottom of the page)
31
+ * Tobi Tobes' port of my gem to processing at http://github.com/rngtng/launchpad
32
+
33
+
34
+ == Requirements
35
+
36
+ * Roger B. Dannenberg's {portmidi library}[http://sourceforge.net/projects/portmedia/]
37
+ * Jan Krutisch's {portmidi gem}[http://github.com/halfbyte/portmidi]
38
+
39
+
40
+ == Compatibility
41
+
42
+ The gem is known to be compatible with the following ruby versions:
43
+
44
+ * MRI 1.8.7
45
+ * MRI 1.9.3
46
+ * MRI 2.0.0
47
+ * MRI 2.2.2
48
+
49
+
50
+ == Installation
51
+
52
+ The gem is hosted on RubyGems[https://rubygems.org/], so in order to use it, you're gonna gem install it:
53
+
54
+ gem install launchpad
55
+
56
+
57
+ == Usage
58
+
59
+ There are two main entry points:
60
+
61
+ * require 'launchpad/device', providing Launchpad::Device, which handles all the basic input/output stuff
62
+ * require 'launchpad/interaction' or just 'launchpad', additionally providing Launchpad::Interaction, which lets you respond to actions (button presses/releases)
63
+
64
+ 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).
65
+
66
+ require 'launchpad/device'
67
+
68
+ device = Launchpad::Device.new
69
+ device.test_leds
70
+ sleep 1
71
+ device.reset
72
+ sleep 1
73
+ device.change :grid, :x => 4, :y => 4, :red => :high, :green => :low
74
+
75
+ This is an interaction example lighting all grid buttons in red when pressed and keeping them lit.
76
+
77
+ require 'launchpad'
78
+
79
+ interaction = Launchpad::Interaction.new
80
+ interaction.response_to(:grid, :down) do |interaction, action|
81
+ interaction.device.change(:grid, action.merge(:red => :high))
82
+ end
83
+ interaction.response_to(:mixer, :down) do |interaction, action|
84
+ interaction.stop
85
+ end
86
+
87
+ interaction.start
88
+
89
+
90
+ For more details, see the examples. examples/color_picker.rb is the most complex example with interaction.
91
+
92
+
93
+ == Future plans
94
+
95
+ * bitmap rendering
96
+ * internal tracking of LED states for both buffers
97
+
98
+
99
+ == Changelog
100
+
101
+ === v.0.3.0
102
+
103
+ * logging
104
+ * reworked multi threading for action handling
105
+ * compatibility with ruby 1.8.7 and 2.0.0
106
+ * interaction responses for presses on single grid buttons/button areas/columns/rows
107
+
108
+ === v.0.2.2
109
+
110
+ * single threading fix: prevent ThreadError when Launchpad::Interaction#stop is called within an action response
111
+
112
+ === v.0.2.1
113
+
114
+ * Launchpad::Interaction#close now properly stops interaction first
115
+ * multi threading: Launchpad::Interaction#start method can be called with :detached => true to allow calling thread to continue
116
+
117
+ === v.0.2.0
118
+
119
+ * double buffering (see Launchpad::Device#buffering_mode)
120
+ * don't update grid button 0,0 before change_all (in order to reset rapid update pointer), use MIDI message without visual effect
121
+ * (at least) doubled the speed of change_all by not sending each message individually but sending them in one go (as an array)
122
+
123
+ === v0.1.1
124
+
125
+ * ability to close device/interaction to free portmidi resources
126
+ * ability to initialize devices using device ids as well as device names
127
+ * complete documentation for http://rdoc.info/projects/thomasjachmann/launchpad
128
+
129
+ === v0.1.0
130
+
131
+ * first feature complete version with kinda stable API
132
+
133
+
134
+ == Copyright
135
+
136
+ Copyright (c) 2009 Thomas Jachmann. See LICENSE for details.
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.pattern = 'test/**/test_*.rb'
7
+ test.verbose = true
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,29 @@
1
+ require 'launchpad'
2
+
3
+ device = Launchpad::Device.new
4
+
5
+ on = { :red => :high, :green => :off }
6
+ off = { :red => :off, :green => :lo }
7
+
8
+ digit_map = [
9
+ [off, off, off, off],
10
+ [on , off, off, off],
11
+ [off, on , off, off],
12
+ [on , on , off, off],
13
+ [off, off, on , off],
14
+ [on , off, on , off],
15
+ [off, on , on , off],
16
+ [on , on , on , off],
17
+ [off, off, off, on ],
18
+ [on , off, off, on ]
19
+ ]
20
+
21
+ while true do
22
+ Time.now.strftime('%H%M%S').split('').each_with_index do |digit, x|
23
+ digit_map[digit.to_i].each_with_index do |color, y|
24
+ device.change :grid, color.merge(:x => x, :y => (7 - y))
25
+ end
26
+ end
27
+
28
+ sleep 0.25
29
+ end
@@ -0,0 +1,96 @@
1
+ require 'launchpad'
2
+
3
+ interaction = Launchpad::Interaction.new
4
+
5
+ # build color arrays for color display views
6
+ colors_single = [
7
+ [ 0, 1, 2, 3, 0, 0, 0, 0],
8
+ [16, 17, 18, 19, 0, 0, 0, 0],
9
+ [32, 33, 34, 35, 0, 0, 0, 0],
10
+ [48, 49, 50, 51, 0, 0, 0, 0],
11
+ [0] * 8,
12
+ [0] * 8,
13
+ [0] * 8,
14
+ [0] * 8,
15
+ [0] * 8
16
+ ]
17
+ colors_double = [
18
+ [ 0, 0, 1, 1, 2, 2, 3, 3],
19
+ [ 0, 0, 1, 1, 2, 2, 3, 3],
20
+ [16, 16, 17, 17, 18, 18, 19, 19],
21
+ [16, 16, 17, 17, 18, 18, 19, 19],
22
+ [32, 32, 33, 33, 34, 34, 35, 35],
23
+ [32, 32, 33, 33, 34, 34, 35, 35],
24
+ [48, 48, 49, 49, 50, 50, 51, 51],
25
+ [48, 48, 49, 49, 50, 50, 51, 51],
26
+ [0] * 8
27
+ ]
28
+ colors_mirrored = [
29
+ [ 0, 1, 2, 3, 3, 2, 1, 0],
30
+ [16, 17, 18, 19, 19, 18, 17, 16],
31
+ [32, 33, 34, 35, 35, 34, 33, 32],
32
+ [48, 49, 50, 51, 51, 50, 49, 48],
33
+ [48, 49, 50, 51, 51, 50, 49, 48],
34
+ [32, 33, 34, 35, 35, 34, 33, 32],
35
+ [16, 17, 18, 19, 19, 18, 17, 16],
36
+ [ 0, 1, 2, 3, 3, 2, 1, 0],
37
+ [0] * 8
38
+ ]
39
+
40
+ # setup color display views
41
+ def display_color_view(colors)
42
+ lambda do |interaction, action|
43
+ # set color
44
+ interaction.device.change_all(colors)
45
+ # register mute interactor on scene buttons
46
+ interaction.response_to(%w(scene1 scene2 scene3 scene4 scene5 scene6 scene7 scene8), :down, :exclusive => true, &@mute)
47
+ end
48
+ end
49
+ interaction.response_to(:up, :down, &display_color_view(colors_single + [48, 16, 16, 16]))
50
+ interaction.response_to(:down, :down, &display_color_view(colors_double + [16, 48, 16, 16]))
51
+ interaction.response_to(:left, :down, &display_color_view(colors_mirrored + [16, 16, 48, 16]))
52
+
53
+ # setup color picker view
54
+ def display_color(opts)
55
+ lambda do |interaction, action|
56
+ @red = opts[:red] if opts[:red]
57
+ @green = opts[:green] if opts[:green]
58
+ colors = [(@green * 16 + @red)] * 64
59
+ 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]
60
+ interaction.device.change_all(colors + scenes + [16, 16, 16, 48])
61
+ end
62
+ end
63
+ interaction.response_to(:right, :down) do |interaction, action|
64
+ @red = 0
65
+ @green = 0
66
+ # register color picker interactors on scene buttons
67
+ interaction.response_to(:scene1, :down, :exclusive => true, &display_color(:red => 3))
68
+ interaction.response_to(:scene2, :down, :exclusive => true, &display_color(:red => 2))
69
+ interaction.response_to(:scene3, :down, :exclusive => true, &display_color(:red => 1))
70
+ interaction.response_to(:scene4, :down, :exclusive => true, &display_color(:red => 0))
71
+ interaction.response_to(:scene5, :down, :exclusive => true, &display_color(:green => 3))
72
+ interaction.response_to(:scene6, :down, :exclusive => true, &display_color(:green => 2))
73
+ interaction.response_to(:scene7, :down, :exclusive => true, &display_color(:green => 1))
74
+ interaction.response_to(:scene8, :down, :exclusive => true, &display_color(:green => 0))
75
+ # display color
76
+ interaction.respond_to(:scene8, :down)
77
+ end
78
+
79
+ # mixer button terminates interaction on button up
80
+ interaction.response_to(:mixer) do |interaction, action|
81
+ interaction.device.change(:mixer, :red => action[:state] == :down ? :hi : :off)
82
+ interaction.stop if action[:state] == :up
83
+ end
84
+
85
+ # setup mute display interactors on all unused buttons
86
+ @mute = display_color_view([0] * 72 + [16, 16, 16, 16])
87
+ interaction.response_to(%w(session user1 user2 grid), :down, &@mute)
88
+
89
+ # display mute view
90
+ interaction.respond_to(:session, :down)
91
+
92
+ # start interacting
93
+ interaction.start
94
+
95
+ # sleep so that the messages can be sent before the program terminates
96
+ sleep 0.1
@@ -0,0 +1,21 @@
1
+ require 'launchpad'
2
+
3
+ device = Launchpad::Device.new(:input => false, :output => true)
4
+
5
+ pos_x = pos_y = 0
6
+ 4.times do |red|
7
+ 4.times do |green|
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
12
+ pos_y += 1
13
+ # sleep, otherwise the connection drops some messages - WTF?
14
+ sleep 0.01
15
+ end
16
+ pos_x += 1
17
+ pos_y = 0
18
+ end
19
+
20
+ # sleep so that the messages can be sent before the program terminates
21
+ sleep 0.1
@@ -0,0 +1,68 @@
1
+ require 'launchpad'
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,104 @@
1
+ require 'launchpad'
2
+
3
+ interaction = Launchpad::Interaction.new
4
+
5
+ # store and change button states, ugly but well...
6
+ @button_states = [
7
+ [false, false, false, false, false, false, false, false],
8
+ [false, false, false, false, false, false, false, false],
9
+ [[false, false], [false, false], [false, false], [false, false], [false, false], [false, false], [false, false], [false, false]],
10
+ [[false, false], [false, false], [false, false], [false, false], [false, false], [false, false], [false, false], [false, false]],
11
+ [[false, false], [false, false], [false, false], [false, false], [false, false], [false, false], [false, false], [false, false]],
12
+ [[false, false], [false, false], [false, false], [false, false], [false, false], [false, false], [false, false], [false, false]],
13
+ [[false, false], [false, false], [false, false], [false, false], [false, false], [false, false], [false, false], [false, false]],
14
+ [[false, false], [false, false], [false, false], [false, false], [false, false], [false, false], [false, false], [false, false]]
15
+ ]
16
+ def change_button_state(action)
17
+ if action[:y] > 1
18
+ which = @active_buffer_button == :user2 ? 1 : 0
19
+ @button_states[action[:y]][action[:x]][which] = !@button_states[action[:y]][action[:x]][which]
20
+ else
21
+ @button_states[action[:y]][action[:x]] = !@button_states[action[:y]][action[:x]]
22
+ end
23
+ end
24
+
25
+ # setup grid buttons to:
26
+ # * set LEDs in normal mode on the first row
27
+ # * set LEDs in flashing mode on the second row
28
+ # * set LEDs in buffering mode on all other rows
29
+ interaction.response_to(:grid, :down) do |interaction, action|
30
+ color = change_button_state(action) ? @color : {}
31
+ case action[:y]
32
+ when 0
33
+ interaction.device.change(:grid, action.merge(color))
34
+ when 1
35
+ interaction.device.buffering_mode(:flashing => false, :display_buffer => 1, :update_buffer => 0)
36
+ interaction.device.change(:grid, action.merge(color).merge(:mode => :flashing))
37
+ interaction.respond_to(@active_buffer_button, :down)
38
+ else
39
+ interaction.device.change(:grid, action.merge(color).merge(:mode => :buffering))
40
+ end
41
+ end
42
+
43
+ # green feedback for buffer buttons
44
+ interaction.response_to([:session, :user1, :user2], :down) do |interaction, action|
45
+ case @active_buffer_button = action[:type]
46
+ when :session
47
+ interaction.device.buffering_mode(:flashing => true)
48
+ when :user1
49
+ interaction.device.buffering_mode(:display_buffer => 0, :update_buffer => 0)
50
+ when :user2
51
+ interaction.device.buffering_mode(:display_buffer => 1, :update_buffer => 1)
52
+ end
53
+ interaction.device.change(:session, :red => @active_buffer_button == :session ? :hi : :lo, :green => @active_buffer_button == :session ? :hi : :lo)
54
+ interaction.device.change(:user1, :red => @active_buffer_button == :user1 ? :hi : :lo, :green => @active_buffer_button == :user1 ? :hi : :lo)
55
+ interaction.device.change(:user2, :red => @active_buffer_button == :user2 ? :hi : :lo, :green => @active_buffer_button == :user2 ? :hi : :lo)
56
+ end
57
+
58
+ # setup color picker
59
+ def display_color(opts)
60
+ lambda do |interaction, action|
61
+ @red = opts[:red] if opts[:red]
62
+ @green = opts[:green] if opts[:green]
63
+ if @red == 0 && @green == 0
64
+ @red = 1 if opts[:red]
65
+ @green = 1 if opts[:green]
66
+ end
67
+ @color = {:red => @red, :green => @green}
68
+ on = {:red => 3, :green => 3}
69
+ interaction.device.change(:scene1, @red == 3 ? on : {:red => 3})
70
+ interaction.device.change(:scene2, @red == 2 ? on : {:red => 2})
71
+ interaction.device.change(:scene3, @red == 1 ? on : {:red => 1})
72
+ interaction.device.change(:scene4, @red == 0 ? on : {:red => 0})
73
+ interaction.device.change(:scene5, @green == 3 ? on : {:green => 3})
74
+ interaction.device.change(:scene6, @green == 2 ? on : {:green => 2})
75
+ interaction.device.change(:scene7, @green == 1 ? on : {:green => 1})
76
+ interaction.device.change(:scene8, @green == 0 ? on : {:green => 0})
77
+ end
78
+ end
79
+ # register color picker interactors on scene buttons
80
+ interaction.response_to(:scene1, :down, :exclusive => true, &display_color(:red => 3))
81
+ interaction.response_to(:scene2, :down, :exclusive => true, &display_color(:red => 2))
82
+ interaction.response_to(:scene3, :down, :exclusive => true, &display_color(:red => 1))
83
+ interaction.response_to(:scene4, :down, :exclusive => true, &display_color(:red => 0))
84
+ interaction.response_to(:scene5, :down, :exclusive => true, &display_color(:green => 3))
85
+ interaction.response_to(:scene6, :down, :exclusive => true, &display_color(:green => 2))
86
+ interaction.response_to(:scene7, :down, :exclusive => true, &display_color(:green => 1))
87
+ interaction.response_to(:scene8, :down, :exclusive => true, &display_color(:green => 0))
88
+ # pick green
89
+ interaction.respond_to(:scene5, :down)
90
+
91
+ # mixer button terminates interaction on button up
92
+ interaction.response_to(:mixer) do |interaction, action|
93
+ interaction.device.change(:mixer, :red => action[:state] == :down ? :hi : :off)
94
+ interaction.stop if action[:state] == :up
95
+ end
96
+
97
+ # start in auto flashing mode
98
+ interaction.respond_to(:session, :down)
99
+
100
+ # start interacting
101
+ interaction.start
102
+
103
+ # sleep so that the messages can be sent before the program terminates
104
+ sleep 0.1