class LaunchpadMk2::Device
This class is used to exchange data with the launchpad. It provides methods to light LEDs and to get information about button presses/releases.
Example:
require 'launchpad_mk2/device' device = Launchpad::Device.new device.test_leds sleep 1 device.reset sleep 1 device.change :grid, :x => 4, :y => 4, :red => :high, :green => :low
Constants
- CODE_NOTE_TO_DATA_TYPE
- MK2_DEVICE_NAME
- SYSEX_FOOTER
- SYSEX_HEADER
- TYPE_TO_NOTE
Public Class Methods
Initializes the launchpad device. When output capabilities are requested, the launchpad will be reset.
Optional options hash:
:input
-
whether to use MIDI input for user interaction,
true/false
, optional, defaults totrue
:output
-
whether to use MIDI output for data display,
true/false
, optional, defaults totrue
:input_device_id
-
ID of the MIDI input device to use, optional,
:device_name
will be used if omitted :output_device_id
-
ID of the MIDI output device to use, optional,
:device_name
will be used if omitted :device_name
-
Name of the MIDI device to use, optional, defaults to “Launchpad”
:logger
- Logger
-
to be used by this device instance, can be changed afterwards
Errors raised:
- Launchpad::NoSuchDeviceError
-
when device with ID or name specified does not exist
- Launchpad::DeviceBusyError
-
when device with ID or name specified is busy
# File lib/launchpad_mk2/device.rb, line 89 def initialize(opts = nil) @input = nil @output = nil opts = { :input => true, :output => true }.merge(opts || {}) self.logger = opts[:logger] logger.debug "initializing Launchpad::Device##{object_id} with #{opts.inspect}" Portmidi.start @input = create_device!(Portmidi.input_devices, Portmidi::Input, :id => opts[:input_device_id], :name => opts[:device_name] ) if opts[:input] @output = create_device!(Portmidi.output_devices, Portmidi::Output, :id => opts[:output_device_id], :name => opts[:device_name] ) if opts[:output] end
Public Instance Methods
Changes a single LED.
Parameters (see Launchpad for values):
type
-
type of the button to change
Optional options hash (see Launchpad for values):
:x
-
x coordinate
:y
-
y coordinate
color
-
color of the LED (value between 0 and 127 inclusive) optional, defaults to
:off
Errors raised:
- Launchpad::NoValidGridCoordinatesError
-
when coordinates aren't within the valid range
- Launchpad::NoValidColorError
-
when color value isn't within the valid range
- Launchpad::NoOutputAllowedError
-
when output is not enabled
# File lib/launchpad_mk2/device.rb, line 154 def change(type, opts = nil) opts ||= {} status = %w(up down left right session user1 user2 mixer).include?(type.to_s) ? Status::CC : Status::ON output(status, note(type, opts), velocity(opts)) end
Closes the device - nothing can be done with the device afterwards.
# File lib/launchpad_mk2/device.rb, line 113 def close logger.debug "closing Launchpad::Device##{object_id}" @input.close unless @input.nil? @input = nil @output.close unless @output.nil? @output = nil end
Determines whether this device has been closed.
# File lib/launchpad_mk2/device.rb, line 122 def closed? !(input_enabled? || output_enabled?) end
# File lib/launchpad_mk2/device.rb, line 175 def flash1(x, y, color_key) note = note(:grid, {:x => x, :y => y}) output_sysex(SYSEX_HEADER + [35, 0, note, color_key] + SYSEX_FOOTER) end
# File lib/launchpad_mk2/device.rb, line 180 def flashn(notes, color_key) notes.each { |coord| note = note(:grid, {:x => coord[0], :y => coord[1]}) output_sysex(SYSEX_HEADER + [35, 0, note, color_key, 0] + SYSEX_FOOTER) } end
Determines whether this device can be used to read input.
# File lib/launchpad_mk2/device.rb, line 127 def input_enabled? !@input.nil? end
# File lib/launchpad_mk2/device.rb, line 217 def light1_column(column_key, color_key) output_sysex(SYSEX_HEADER + [12, column_key, color_key] + SYSEX_FOOTER) end
# File lib/launchpad_mk2/device.rb, line 227 def light1_row(row_key, color_key) output_sysex(SYSEX_HEADER + [13, row_key, color_key] + SYSEX_FOOTER) end
# File lib/launchpad_mk2/device.rb, line 203 def light_all(color_key) output_sysex(SYSEX_HEADER + [14, color_key] + SYSEX_FOOTER) end
# File lib/launchpad_mk2/device.rb, line 211 def lightn_column(column_keys, color_key) column_keys.each { |column_key| output_sysex(SYSEX_HEADER + [12, column_key, color_key] + SYSEX_FOOTER) } end
# File lib/launchpad_mk2/device.rb, line 221 def lightn_row(rows_keys, color_key) rows_keys.each { |row_key| output_sysex(SYSEX_HEADER + [13, row_key, color_key] + SYSEX_FOOTER) } end
Determines whether this device can be used to output data.
# File lib/launchpad_mk2/device.rb, line 132 def output_enabled? !@output.nil? end
# File lib/launchpad_mk2/device.rb, line 163 def pulse1(x, y, color_key) note = note(:grid, {:x => x, :y => y}) output_sysex(SYSEX_HEADER + [40, 0, note, color_key] + SYSEX_FOOTER) end
# File lib/launchpad_mk2/device.rb, line 168 def pulsen(notes, color_key) notes.each { |coord| note = note(:grid, {:x => coord[0], :y => coord[1]}) output_sysex(SYSEX_HEADER + [40, 0, note, color_key] + SYSEX_FOOTER) } end
Reads user actions (button presses/releases) that haven't been handled yet. This is non-blocking, so when nothing happend yet you'll get an empty array.
Returns:
an array of hashes with (see Launchpad for values):
:timestamp
-
integer indicating the time when the action occured
:state
-
state of the button after action
:type
-
type of the button
:x
-
x coordinate
:y
-
y coordinate
Errors raised:
- Launchpad::NoInputAllowedError
-
when input is not enabled
# File lib/launchpad_mk2/device.rb, line 247 def read_pending_actions Array(input).collect do |midi_message| (code, note, velocity) = midi_message[:message] data = { :timestamp => midi_message[:timestamp], :state => (velocity == 127 ? :down : :up) } data[:type] = CODE_NOTE_TO_DATA_TYPE[[code, note]] || :grid if data[:type] == :grid data[:x] = (note % 10) - 1 data[:y] = (note / 10) - 1 end data end end
# File lib/launchpad_mk2/device.rb, line 207 def reset_all() light_all(0) end
# File lib/launchpad_mk2/device.rb, line 187 def scroll(color_key, text, mode) output_sysex(SYSEX_HEADER + [20, color_key, mode] + text.chars.map(&:ord) + SYSEX_FOOTER) end
# File lib/launchpad_mk2/device.rb, line 191 def scroll_forever(color_key, text) scroll(color_key, text, 1) end
# File lib/launchpad_mk2/device.rb, line 195 def scroll_once(color_key, text) scroll(color_key, text, 0) end
# File lib/launchpad_mk2/device.rb, line 199 def scroll_stop() output_sysex(SYSEX_HEADER + [20] + SYSEX_FOOTER) end
Private Instance Methods
# File lib/launchpad_mk2/device.rb, line 425 def color(color_key) if color_key.nil? 0 else if (not (color_key.is_a? Integer)) logger.error "wrong color specified: color_key=#{color_key}" raise NoValidColorError.new("you need to specify a valid color (0-127), you specified: color_key=#{color_key}") end if color_key < 0 || color_key > 127 logger.error "wrong color specified: color_key=#{color_key}" raise NoValidColorError.new("you need to specify a valid color (0-127), you specified: color_key=#{color_key}") end color_key end end
Creates input/output devices.
Parameters:
devices
-
array of portmidi devices
- +device_type
-
class to instantiate (
Portmidi::Input/Portmidi::Output
)
Options hash:
:id
-
id of the MIDI device to use
:name
-
name of the MIDI device to use, only used when
:id
is not specified, defaults to “Launchpad”
Returns:
newly created device
Errors raised:
- Launchpad::NoSuchDeviceError
-
when device with ID or name specified does not exist
- Launchpad::DeviceBusyError
-
when device with ID or name specified is busy
# File lib/launchpad_mk2/device.rb, line 287 def create_device!(devices, device_type, opts) logger.debug "creating #{device_type} with #{opts.inspect}, choosing from portmidi devices #{devices.inspect}" id = opts[:id] if id.nil? name = opts[:name] || MK2_DEVICE_NAME device = devices.select {|current_device| current_device.name == name}.first id = device.device_id unless device.nil? end if id.nil? message = "MIDI device #{opts[:id] || opts[:name]} doesn't exist" logger.fatal message raise NoSuchDeviceError.new(message) end device_type.new(id) rescue RuntimeError => e logger.fatal "error creating #{device_type}: #{e.inspect}" raise DeviceBusyError.new(e) end
Reads input from the MIDI device.
Returns:
an array of hashes with:
:message
-
an array of MIDI status code, MIDI data 1 (note), MIDI data 2 (velocity) and a fourth value
:timestamp
-
integer indicating the time when the MIDI message was created
Errors raised:
- Launchpad::NoInputAllowedError
-
when output is not enabled
# File lib/launchpad_mk2/device.rb, line 322 def input if @input.nil? logger.error "trying to read from device that's not been initialized for input" raise NoInputAllowedError end @input.read(16) end
Creates a MIDI message.
Parameters:
status
-
MIDI status code
data1
-
MIDI data 1 (note)
data2
-
MIDI data 2 (velocity)
Returns:
an array with:
:message
-
an array of MIDI status code, MIDI data 1 (note), MIDI data 2 (velocity)
:timestamp
-
integer indicating the time when the MIDI message was created, in this case 0
# File lib/launchpad_mk2/device.rb, line 460 def message(status, data1, data2) {:message => [status, data1, data2], :timestamp => 0} end
Calculates the MIDI data 1 value (note) for a button.
Parameters (see Launchpad for values):
type
-
type of the button
Options hash:
:x
-
x coordinate
:y
-
y coordinate
Returns:
integer to be used for MIDI data 1
Errors raised:
- Launchpad::NoValidGridCoordinatesError
-
when coordinates aren't within the valid range
# File lib/launchpad_mk2/device.rb, line 393 def note(type, opts) note = TYPE_TO_NOTE[type] if note.nil? x = (opts[:x] || -1).to_i y = (opts[:y] || -1).to_i if x < 0 || x > 7 || y < 0 || y > 7 logger.error "wrong coordinates specified: x=#{x}, y=#{y}" raise NoValidGridCoordinatesError.new("you need to specify valid coordinates (x/y, 0-7, from top left), you specified: x=#{x}, y=#{y}") end note = (y + 1) * 10 + (x + 1) end note end
Writes data to the MIDI device.
Parameters:
status
-
MIDI status code
data1
-
MIDI data 1 (note)
data2
-
MIDI data 2 (velocity)
Errors raised:
- Launchpad::NoOutputAllowedError
-
when output is not enabled
# File lib/launchpad_mk2/device.rb, line 341 def output(status, data1, data2) output_messages([message(status, data1, data2)]) end
Writes several messages to the MIDI device.
Parameters:
messages
-
an array of hashes (usually created with message) with:
:message
-
an array of MIDI status code, MIDI data 1 (note), MIDI data 2 (velocity)
:timestamp
-
integer indicating the time when the MIDI message was created
# File lib/launchpad_mk2/device.rb, line 355 def output_messages(messages) if @output.nil? logger.error "trying to write to device that's not been initialized for output" raise NoOutputAllowedError end logger.debug "writing messages to launchpad:\n #{messages.join("\n ")}" if logger.debug? @output.write(messages) nil end
# File lib/launchpad_mk2/device.rb, line 365 def output_sysex(messages) if @output.nil? logger.error "trying to write to device that's not been initialized for output" raise NoOutputAllowedError end logger.debug "writing sysex to launchpad:\n #{messages.join("\n ")}" if logger.debug? @output.write_sysex(messages) nil end
Calculates the MIDI data 2 value (velocity) for given brightness and mode values.
Options hash:
color
-
color of the LED (value between 0 and 127 inclusive) optional, defaults to
:off
Returns:
integer to be used for MIDI data 2
# File lib/launchpad_mk2/device.rb, line 417 def velocity(opts) if opts.is_a?(Hash) color = color(opts[:color]) || 0 else opts.to_i + 12 end end