launchpad 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -2
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/README.rdoc +34 -10
- data/Rakefile +1 -50
- data/examples/binary_clock.rb +29 -0
- data/examples/color_picker.rb +1 -1
- data/examples/colors.rb +1 -1
- data/examples/doodle.rb +1 -1
- data/examples/double_buffering.rb +1 -1
- data/examples/drawing_board.rb +1 -1
- data/examples/feedback.rb +1 -1
- data/examples/reset.rb +1 -1
- data/launchpad.gemspec +25 -74
- data/lib/launchpad.rb +2 -1
- data/lib/launchpad/device.rb +94 -63
- data/lib/launchpad/interaction.rb +133 -9
- data/lib/launchpad/logging.rb +27 -0
- data/lib/launchpad/version.rb +1 -1
- data/test/helper.rb +8 -12
- data/test/test_device.rb +117 -111
- data/test/test_interaction.rb +295 -141
- metadata +115 -73
- data/.document +0 -3
- data/examples/setup.rb +0 -5
- data/experiments/wandering_dot.rb +0 -116
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.rdoc
CHANGED
@@ -1,26 +1,46 @@
|
|
1
1
|
= launchpad
|
2
2
|
|
3
|
-
|
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.
|
4
6
|
|
5
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.
|
6
8
|
|
7
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.
|
8
10
|
|
9
11
|
|
12
|
+
== More Info
|
13
|
+
|
14
|
+
If you don't know what launchpad is, visit:
|
15
|
+
|
16
|
+
* Novation's site at http://de.novationmusic.com/products/midi_controller/launchpad
|
17
|
+
* my demo videos for this library at http://www.youtube.com/thomasjachmann
|
18
|
+
* other demos on youtube http://www.youtube.com/results?search_query=novation+launchpad
|
19
|
+
|
20
|
+
If you're into other languages or want to know what goes on behind the scenes MIDI wise, have a look at:
|
21
|
+
|
22
|
+
* Novation's MIDI programmer's reference at {www.novationmusic.com/support/launchpad}[http://www.novationmusic.com/support/launchpad/] (bottom of the page)
|
23
|
+
* Tobi Tobes' port of my gem to processing at http://github.com/rngtng/launchpad
|
24
|
+
|
25
|
+
|
10
26
|
== Requirements
|
11
27
|
|
12
28
|
* Roger B. Dannenberg's {portmidi library}[http://sourceforge.net/projects/portmedia/]
|
13
29
|
* Jan Krutisch's {portmidi gem}[http://github.com/halfbyte/portmidi]
|
14
30
|
|
15
31
|
|
16
|
-
==
|
32
|
+
== Compatibility
|
33
|
+
|
34
|
+
The gem is known to be compatible with the following ruby versions:
|
35
|
+
|
36
|
+
* MRI 1.8.7
|
37
|
+
* MRI 1.9.3
|
38
|
+
* MRI 2.0.0
|
17
39
|
|
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
40
|
|
20
|
-
|
21
|
-
gem tumble
|
41
|
+
== Installation
|
22
42
|
|
23
|
-
|
43
|
+
The gem is hosted on RubyGems[https://rubygems.org/], so in order to use it, you're gonna gem install it:
|
24
44
|
|
25
45
|
gem install launchpad
|
26
46
|
|
@@ -34,7 +54,6 @@ There are two main entry points:
|
|
34
54
|
|
35
55
|
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
56
|
|
37
|
-
require 'rubygems'
|
38
57
|
require 'launchpad/device'
|
39
58
|
|
40
59
|
device = Launchpad::Device.new
|
@@ -46,7 +65,6 @@ This is a simple example (only requiring the device for output) that switches on
|
|
46
65
|
|
47
66
|
This is an interaction example lighting all grid buttons in red when pressed and keeping them lit.
|
48
67
|
|
49
|
-
require 'rubygems'
|
50
68
|
require 'launchpad'
|
51
69
|
|
52
70
|
interaction = Launchpad::Interaction.new
|
@@ -63,15 +81,21 @@ This is an interaction example lighting all grid buttons in red when pressed and
|
|
63
81
|
For more details, see the examples. examples/color_picker.rb is the most complex example with interaction.
|
64
82
|
|
65
83
|
|
66
|
-
==
|
84
|
+
== Future plans
|
67
85
|
|
68
|
-
* interaction responses for presses on single grid buttons/button areas
|
69
86
|
* bitmap rendering
|
70
87
|
* internal tracking of LED states for both buffers
|
71
88
|
|
72
89
|
|
73
90
|
== Changelog
|
74
91
|
|
92
|
+
=== v.0.3.0
|
93
|
+
|
94
|
+
* logging
|
95
|
+
* reworked multi threading for action handling
|
96
|
+
* compatibility with ruby 1.8.7 and 2.0.0
|
97
|
+
* interaction responses for presses on single grid buttons/button areas/columns/rows
|
98
|
+
|
75
99
|
=== v.0.2.2
|
76
100
|
|
77
101
|
* single threading fix: prevent ThreadError when Launchpad::Interaction#stop is called within an action response
|
data/Rakefile
CHANGED
@@ -1,28 +1,4 @@
|
|
1
|
-
require '
|
2
|
-
require 'rake'
|
3
|
-
|
4
|
-
require File.join(File.dirname(__FILE__), 'lib', 'launchpad', 'version')
|
5
|
-
|
6
|
-
begin
|
7
|
-
require 'jeweler'
|
8
|
-
Jeweler::Tasks.new do |gem|
|
9
|
-
gem.name = 'launchpad'
|
10
|
-
gem.summary = 'A gem for accessing novation\'s launchpad programmatically and easily.'
|
11
|
-
gem.description = '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.'
|
12
|
-
gem.email = 'tom.j@gmx.net'
|
13
|
-
gem.homepage = 'http://github.com/thomasjachmann/launchpad'
|
14
|
-
gem.version = Launchpad::VERSION
|
15
|
-
gem.authors = ['Thomas Jachmann']
|
16
|
-
gem.has_rdoc = true
|
17
|
-
gem.add_dependency('portmidi', '>= 0.0.6')
|
18
|
-
gem.add_development_dependency('thoughtbot-shoulda', '>= 0')
|
19
|
-
gem.add_development_dependency('mocha')
|
20
|
-
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
21
|
-
end
|
22
|
-
Jeweler::GemcutterTasks.new
|
23
|
-
rescue LoadError
|
24
|
-
puts 'Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler'
|
25
|
-
end
|
1
|
+
require 'bundler/gem_tasks'
|
26
2
|
|
27
3
|
require 'rake/testtask'
|
28
4
|
Rake::TestTask.new(:test) do |test|
|
@@ -31,29 +7,4 @@ Rake::TestTask.new(:test) do |test|
|
|
31
7
|
test.verbose = true
|
32
8
|
end
|
33
9
|
|
34
|
-
begin
|
35
|
-
require 'rcov/rcovtask'
|
36
|
-
Rcov::RcovTask.new do |test|
|
37
|
-
test.libs << 'test'
|
38
|
-
test.pattern = 'test/**/test_*.rb'
|
39
|
-
test.verbose = true
|
40
|
-
end
|
41
|
-
rescue LoadError
|
42
|
-
task :rcov do
|
43
|
-
abort 'RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov'
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
task :test => :check_dependencies
|
48
|
-
|
49
10
|
task :default => :test
|
50
|
-
|
51
|
-
require 'rake/rdoctask'
|
52
|
-
Rake::RDocTask.new do |rdoc|
|
53
|
-
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
54
|
-
|
55
|
-
rdoc.rdoc_dir = 'rdoc'
|
56
|
-
rdoc.title = "launchpad #{version}"
|
57
|
-
rdoc.rdoc_files.include('README*')
|
58
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
59
|
-
end
|
@@ -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
|
data/examples/color_picker.rb
CHANGED
data/examples/colors.rb
CHANGED
data/examples/doodle.rb
CHANGED
data/examples/drawing_board.rb
CHANGED
data/examples/feedback.rb
CHANGED
data/examples/reset.rb
CHANGED
data/launchpad.gemspec
CHANGED
@@ -1,83 +1,34 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "launchpad/version"
|
5
4
|
|
6
5
|
Gem::Specification.new do |s|
|
7
|
-
s.name
|
8
|
-
s.version
|
9
|
-
|
10
|
-
s.
|
11
|
-
s.
|
12
|
-
s.
|
6
|
+
s.name = "launchpad"
|
7
|
+
s.version = Launchpad::VERSION
|
8
|
+
s.authors = ["Thomas Jachmann"]
|
9
|
+
s.email = ["self@thomasjachmann.com"]
|
10
|
+
s.homepage = "https://github.com/thomasjachmann/launchpad"
|
11
|
+
s.summary = %q{A gem for accessing novation's launchpad programmatically and easily.}
|
13
12
|
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
|
-
s.email = %q{tom.j@gmx.net}
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"LICENSE",
|
17
|
-
"README.rdoc"
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
".document",
|
21
|
-
".gitignore",
|
22
|
-
"LICENSE",
|
23
|
-
"README.rdoc",
|
24
|
-
"Rakefile",
|
25
|
-
"examples/color_picker.rb",
|
26
|
-
"examples/colors.rb",
|
27
|
-
"examples/doodle.rb",
|
28
|
-
"examples/double_buffering.rb",
|
29
|
-
"examples/drawing_board.rb",
|
30
|
-
"examples/feedback.rb",
|
31
|
-
"examples/reset.rb",
|
32
|
-
"examples/setup.rb",
|
33
|
-
"experiments/wandering_dot.rb",
|
34
|
-
"launchpad.gemspec",
|
35
|
-
"lib/launchpad.rb",
|
36
|
-
"lib/launchpad/device.rb",
|
37
|
-
"lib/launchpad/errors.rb",
|
38
|
-
"lib/launchpad/interaction.rb",
|
39
|
-
"lib/launchpad/midi_codes.rb",
|
40
|
-
"lib/launchpad/version.rb",
|
41
|
-
"test/helper.rb",
|
42
|
-
"test/test_device.rb",
|
43
|
-
"test/test_interaction.rb"
|
44
|
-
]
|
45
|
-
s.homepage = %q{http://github.com/thomasjachmann/launchpad}
|
46
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
47
|
-
s.require_paths = ["lib"]
|
48
|
-
s.rubygems_version = %q{1.3.5}
|
49
|
-
s.summary = %q{A gem for accessing novation's launchpad programmatically and easily.}
|
50
|
-
s.test_files = [
|
51
|
-
"test/helper.rb",
|
52
|
-
"test/test_device.rb",
|
53
|
-
"test/test_interaction.rb",
|
54
|
-
"examples/color_picker.rb",
|
55
|
-
"examples/colors.rb",
|
56
|
-
"examples/doodle.rb",
|
57
|
-
"examples/double_buffering.rb",
|
58
|
-
"examples/drawing_board.rb",
|
59
|
-
"examples/feedback.rb",
|
60
|
-
"examples/reset.rb",
|
61
|
-
"examples/setup.rb"
|
62
|
-
]
|
63
13
|
|
64
|
-
|
65
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
66
|
-
s.specification_version = 3
|
14
|
+
s.rubyforge_project = "launchpad"
|
67
15
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
75
|
-
s.add_dependency(%q<mocha>, [">= 0"])
|
76
|
-
end
|
16
|
+
s.add_dependency "portmidi", ">= 0.0.6"
|
17
|
+
s.add_dependency "ffi"
|
18
|
+
s.add_development_dependency "rake"
|
19
|
+
if RUBY_VERSION < "1.9"
|
20
|
+
s.add_development_dependency "minitest"
|
21
|
+
# s.add_development_dependency "ruby-debug"
|
77
22
|
else
|
78
|
-
s.
|
79
|
-
s.
|
80
|
-
s.add_dependency(%q<mocha>, [">= 0"])
|
23
|
+
s.add_development_dependency "minitest-reporters"
|
24
|
+
# s.add_development_dependency "debugger"
|
81
25
|
end
|
82
|
-
|
26
|
+
s.add_development_dependency "mocha"
|
83
27
|
|
28
|
+
# s.has_rdoc = true
|
29
|
+
|
30
|
+
s.files = `git ls-files`.split("\n")
|
31
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
32
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
end
|
data/lib/launchpad.rb
CHANGED
@@ -30,4 +30,5 @@ require 'launchpad/interaction'
|
|
30
30
|
# * <tt>:buffering</tt> (LED is written to buffer, see Launchpad::Device.start_buffering, Launchpad::Device.flush_buffer)
|
31
31
|
# optional, defaults to <tt>:normal</tt>
|
32
32
|
# [+state+] whether the button is pressed or released, <tt>:down/:up</tt>
|
33
|
-
module Launchpad
|
33
|
+
module Launchpad
|
34
|
+
end
|
data/lib/launchpad/device.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'portmidi'
|
2
2
|
|
3
3
|
require 'launchpad/errors'
|
4
|
+
require 'launchpad/logging'
|
4
5
|
require 'launchpad/midi_codes'
|
5
6
|
require 'launchpad/version'
|
6
7
|
|
@@ -11,7 +12,6 @@ module Launchpad
|
|
11
12
|
#
|
12
13
|
# Example:
|
13
14
|
#
|
14
|
-
# require 'rubygems'
|
15
15
|
# require 'launchpad/device'
|
16
16
|
#
|
17
17
|
# device = Launchpad::Device.new
|
@@ -22,8 +22,47 @@ module Launchpad
|
|
22
22
|
# device.change :grid, :x => 4, :y => 4, :red => :high, :green => :low
|
23
23
|
class Device
|
24
24
|
|
25
|
+
include Logging
|
25
26
|
include MidiCodes
|
26
27
|
|
28
|
+
CODE_NOTE_TO_DATA_TYPE = {
|
29
|
+
[Status::ON, SceneButton::SCENE1] => :scene1,
|
30
|
+
[Status::ON, SceneButton::SCENE2] => :scene2,
|
31
|
+
[Status::ON, SceneButton::SCENE3] => :scene3,
|
32
|
+
[Status::ON, SceneButton::SCENE4] => :scene4,
|
33
|
+
[Status::ON, SceneButton::SCENE5] => :scene5,
|
34
|
+
[Status::ON, SceneButton::SCENE6] => :scene6,
|
35
|
+
[Status::ON, SceneButton::SCENE7] => :scene7,
|
36
|
+
[Status::ON, SceneButton::SCENE8] => :scene8,
|
37
|
+
[Status::CC, ControlButton::UP] => :up,
|
38
|
+
[Status::CC, ControlButton::DOWN] => :down,
|
39
|
+
[Status::CC, ControlButton::LEFT] => :left,
|
40
|
+
[Status::CC, ControlButton::RIGHT] => :right,
|
41
|
+
[Status::CC, ControlButton::SESSION] => :session,
|
42
|
+
[Status::CC, ControlButton::USER1] => :user1,
|
43
|
+
[Status::CC, ControlButton::USER2] => :user2,
|
44
|
+
[Status::CC, ControlButton::MIXER] => :mixer
|
45
|
+
}.freeze
|
46
|
+
|
47
|
+
TYPE_TO_NOTE = {
|
48
|
+
:up => ControlButton::UP,
|
49
|
+
:down => ControlButton::DOWN,
|
50
|
+
:left => ControlButton::LEFT,
|
51
|
+
:right => ControlButton::RIGHT,
|
52
|
+
:session => ControlButton::SESSION,
|
53
|
+
:user1 => ControlButton::USER1,
|
54
|
+
:user2 => ControlButton::USER2,
|
55
|
+
:mixer => ControlButton::MIXER,
|
56
|
+
:scene1 => SceneButton::SCENE1,
|
57
|
+
:scene2 => SceneButton::SCENE2,
|
58
|
+
:scene3 => SceneButton::SCENE3,
|
59
|
+
:scene4 => SceneButton::SCENE4,
|
60
|
+
:scene5 => SceneButton::SCENE5,
|
61
|
+
:scene6 => SceneButton::SCENE6,
|
62
|
+
:scene7 => SceneButton::SCENE7,
|
63
|
+
:scene8 => SceneButton::SCENE8
|
64
|
+
}.freeze
|
65
|
+
|
27
66
|
# Initializes the launchpad device. When output capabilities are requested,
|
28
67
|
# the launchpad will be reset.
|
29
68
|
#
|
@@ -39,6 +78,7 @@ module Launchpad
|
|
39
78
|
# optional, <tt>:device_name</tt> will be used if omitted
|
40
79
|
# [<tt>:device_name</tt>] Name of the MIDI device to use,
|
41
80
|
# optional, defaults to "Launchpad"
|
81
|
+
# [<tt>:logger</tt>] [Logger] to be used by this device instance, can be changed afterwards
|
42
82
|
#
|
43
83
|
# Errors raised:
|
44
84
|
#
|
@@ -50,15 +90,26 @@ module Launchpad
|
|
50
90
|
:output => true
|
51
91
|
}.merge(opts || {})
|
52
92
|
|
93
|
+
self.logger = opts[:logger]
|
94
|
+
logger.debug "initializing Launchpad::Device##{object_id} with #{opts.inspect}"
|
95
|
+
|
53
96
|
Portmidi.start
|
54
97
|
|
55
|
-
@input =
|
56
|
-
|
98
|
+
@input = create_device!(Portmidi.input_devices, Portmidi::Input,
|
99
|
+
:id => opts[:input_device_id],
|
100
|
+
:name => opts[:device_name]
|
101
|
+
) if opts[:input]
|
102
|
+
@output = create_device!(Portmidi.output_devices, Portmidi::Output,
|
103
|
+
:id => opts[:output_device_id],
|
104
|
+
:name => opts[:device_name]
|
105
|
+
) if opts[:output]
|
106
|
+
|
57
107
|
reset if output_enabled?
|
58
108
|
end
|
59
109
|
|
60
110
|
# Closes the device - nothing can be done with the device afterwards.
|
61
111
|
def close
|
112
|
+
logger.debug "closing Launchpad::Device##{object_id}"
|
62
113
|
@input.close unless @input.nil?
|
63
114
|
@input = nil
|
64
115
|
@output.close unless @output.nil?
|
@@ -228,6 +279,7 @@ module Launchpad
|
|
228
279
|
end
|
229
280
|
|
230
281
|
# Reads user actions (button presses/releases) that haven't been handled yet.
|
282
|
+
# This is non-blocking, so when nothing happend yet you'll get an empty array.
|
231
283
|
#
|
232
284
|
# Returns:
|
233
285
|
#
|
@@ -249,33 +301,10 @@ module Launchpad
|
|
249
301
|
:timestamp => midi_message[:timestamp],
|
250
302
|
:state => (velocity == 127 ? :down : :up)
|
251
303
|
}
|
252
|
-
data[:type] =
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
when SceneButton::SCENE2 then :scene2
|
257
|
-
when SceneButton::SCENE3 then :scene3
|
258
|
-
when SceneButton::SCENE4 then :scene4
|
259
|
-
when SceneButton::SCENE5 then :scene5
|
260
|
-
when SceneButton::SCENE6 then :scene6
|
261
|
-
when SceneButton::SCENE7 then :scene7
|
262
|
-
when SceneButton::SCENE8 then :scene8
|
263
|
-
else
|
264
|
-
data[:x] = note % 16
|
265
|
-
data[:y] = note / 16
|
266
|
-
:grid
|
267
|
-
end
|
268
|
-
when Status::CC
|
269
|
-
case note
|
270
|
-
when ControlButton::UP then :up
|
271
|
-
when ControlButton::DOWN then :down
|
272
|
-
when ControlButton::LEFT then :left
|
273
|
-
when ControlButton::RIGHT then :right
|
274
|
-
when ControlButton::SESSION then :session
|
275
|
-
when ControlButton::USER1 then :user1
|
276
|
-
when ControlButton::USER2 then :user2
|
277
|
-
when ControlButton::MIXER then :mixer
|
278
|
-
end
|
304
|
+
data[:type] = CODE_NOTE_TO_DATA_TYPE[[code, note]] || :grid
|
305
|
+
if data[:type] == :grid
|
306
|
+
data[:x] = note % 16
|
307
|
+
data[:y] = note / 16
|
279
308
|
end
|
280
309
|
data
|
281
310
|
end
|
@@ -305,16 +334,22 @@ module Launchpad
|
|
305
334
|
#
|
306
335
|
# [Launchpad::NoSuchDeviceError] when device with ID or name specified does not exist
|
307
336
|
# [Launchpad::DeviceBusyError] when device with ID or name specified is busy
|
308
|
-
def
|
337
|
+
def create_device!(devices, device_type, opts)
|
338
|
+
logger.debug "creating #{device_type} with #{opts.inspect}, choosing from portmidi devices #{devices.inspect}"
|
309
339
|
id = opts[:id]
|
310
340
|
if id.nil?
|
311
341
|
name = opts[:name] || 'Launchpad'
|
312
342
|
device = devices.select {|device| device.name == name}.first
|
313
343
|
id = device.device_id unless device.nil?
|
314
344
|
end
|
315
|
-
|
345
|
+
if id.nil?
|
346
|
+
message = "MIDI device #{opts[:id] || opts[:name]} doesn't exist"
|
347
|
+
logger.fatal message
|
348
|
+
raise NoSuchDeviceError.new(message)
|
349
|
+
end
|
316
350
|
device_type.new(id)
|
317
351
|
rescue RuntimeError => e
|
352
|
+
logger.fatal "error creating #{device_type}: #{e.inspect}"
|
318
353
|
raise DeviceBusyError.new(e)
|
319
354
|
end
|
320
355
|
|
@@ -335,7 +370,10 @@ module Launchpad
|
|
335
370
|
#
|
336
371
|
# [Launchpad::NoInputAllowedError] when output is not enabled
|
337
372
|
def input
|
338
|
-
|
373
|
+
if @input.nil?
|
374
|
+
logger.error "trying to read from device that's not been initialized for input"
|
375
|
+
raise NoInputAllowedError
|
376
|
+
end
|
339
377
|
@input.read(16)
|
340
378
|
end
|
341
379
|
|
@@ -365,7 +403,11 @@ module Launchpad
|
|
365
403
|
# MIDI data 2 (velocity)
|
366
404
|
# [<tt>:timestamp</tt>] integer indicating the time when the MIDI message was created
|
367
405
|
def output_messages(messages)
|
368
|
-
|
406
|
+
if @output.nil?
|
407
|
+
logger.error "trying to write to device that's not been initialized for output"
|
408
|
+
raise NoOutputAllowedError
|
409
|
+
end
|
410
|
+
logger.debug "writing messages to launchpad:\n #{messages.join("\n ")}" if logger.debug?
|
369
411
|
@output.write(messages)
|
370
412
|
nil
|
371
413
|
end
|
@@ -389,29 +431,17 @@ module Launchpad
|
|
389
431
|
#
|
390
432
|
# [Launchpad::NoValidGridCoordinatesError] when coordinates aren't within the valid range
|
391
433
|
def note(type, opts)
|
392
|
-
|
393
|
-
|
394
|
-
when :down then ControlButton::DOWN
|
395
|
-
when :left then ControlButton::LEFT
|
396
|
-
when :right then ControlButton::RIGHT
|
397
|
-
when :session then ControlButton::SESSION
|
398
|
-
when :user1 then ControlButton::USER1
|
399
|
-
when :user2 then ControlButton::USER2
|
400
|
-
when :mixer then ControlButton::MIXER
|
401
|
-
when :scene1 then SceneButton::SCENE1
|
402
|
-
when :scene2 then SceneButton::SCENE2
|
403
|
-
when :scene3 then SceneButton::SCENE3
|
404
|
-
when :scene4 then SceneButton::SCENE4
|
405
|
-
when :scene5 then SceneButton::SCENE5
|
406
|
-
when :scene6 then SceneButton::SCENE6
|
407
|
-
when :scene7 then SceneButton::SCENE7
|
408
|
-
when :scene8 then SceneButton::SCENE8
|
409
|
-
else
|
434
|
+
note = TYPE_TO_NOTE[type]
|
435
|
+
if note.nil?
|
410
436
|
x = (opts[:x] || -1).to_i
|
411
437
|
y = (opts[:y] || -1).to_i
|
412
|
-
|
413
|
-
|
438
|
+
if x < 0 || x > 7 || y < 0 || y > 7
|
439
|
+
logger.error "wrong coordinates specified: x=#{x}, y=#{y}"
|
440
|
+
raise NoValidGridCoordinatesError.new("you need to specify valid coordinates (x/y, 0-7, from top left), you specified: x=#{x}, y=#{y}")
|
441
|
+
end
|
442
|
+
note = y * 16 + x
|
414
443
|
end
|
444
|
+
note
|
415
445
|
end
|
416
446
|
|
417
447
|
# Calculates the MIDI data 2 value (velocity) for given brightness and mode values.
|
@@ -433,19 +463,19 @@ module Launchpad
|
|
433
463
|
#
|
434
464
|
# [Launchpad::NoValidBrightnessError] when brightness values aren't within the valid range
|
435
465
|
def velocity(opts)
|
436
|
-
|
466
|
+
if opts.is_a?(Hash)
|
437
467
|
red = brightness(opts[:red] || 0)
|
438
468
|
green = brightness(opts[:green] || 0)
|
439
|
-
16 * green + red
|
469
|
+
color = 16 * green + red
|
470
|
+
flags = case opts[:mode]
|
471
|
+
when :flashing then 8
|
472
|
+
when :buffering then 0
|
473
|
+
else 12
|
474
|
+
end
|
475
|
+
color + flags
|
440
476
|
else
|
441
|
-
opts.to_i
|
442
|
-
end
|
443
|
-
flags = case opts[:mode]
|
444
|
-
when :flashing then 8
|
445
|
-
when :buffering then 0
|
446
|
-
else 12
|
477
|
+
opts.to_i + 12
|
447
478
|
end
|
448
|
-
color + flags
|
449
479
|
end
|
450
480
|
|
451
481
|
# Calculates the integer brightness for given brightness values.
|
@@ -464,6 +494,7 @@ module Launchpad
|
|
464
494
|
when 2, :medium, :med then 2
|
465
495
|
when 3, :high, :hi then 3
|
466
496
|
else
|
497
|
+
logger.error "wrong brightness specified: #{brightness}"
|
467
498
|
raise NoValidBrightnessError.new("you need to specify the brightness as 0/1/2/3, :off/:low/:medium/:high or :off/:lo/:hi, you specified: #{brightness}")
|
468
499
|
end
|
469
500
|
end
|