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_HEADER
TYPE_TO_NOTE

Public Class Methods

new(opts = nil) click to toggle source

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 to true

:output

whether to use MIDI output for data display, true/false, optional, defaults to true

: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

change(type, opts = nil) click to toggle source

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
close() click to toggle source

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
closed?() click to toggle source

Determines whether this device has been closed.

# File lib/launchpad_mk2/device.rb, line 122
def closed?
  !(input_enabled? || output_enabled?)
end
flash1(x, y, color_key) click to toggle source
# 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
flashn(notes, color_key) click to toggle source
# 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
input_enabled?() click to toggle source

Determines whether this device can be used to read input.

# File lib/launchpad_mk2/device.rb, line 127
def input_enabled?
  !@input.nil?
end
light1_column(column_key, color_key) click to toggle source
# 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
light1_row(row_key, color_key) click to toggle source
# 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
light_all(color_key) click to toggle source
# File lib/launchpad_mk2/device.rb, line 203
def light_all(color_key)
  output_sysex(SYSEX_HEADER + [14, color_key] + SYSEX_FOOTER)
end
lightn_column(column_keys, color_key) click to toggle source
# 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
lightn_row(rows_keys, color_key) click to toggle source
# 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
output_enabled?() click to toggle source

Determines whether this device can be used to output data.

# File lib/launchpad_mk2/device.rb, line 132
def output_enabled?
  !@output.nil?
end
pulse1(x, y, color_key) click to toggle source
# 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
pulsen(notes, color_key) click to toggle source
# 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
read_pending_actions() click to toggle source

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
reset_all() click to toggle source
# File lib/launchpad_mk2/device.rb, line 207
def reset_all()
  light_all(0)
end
scroll(color_key, text, mode) click to toggle source
# 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
scroll_forever(color_key, text) click to toggle source
# File lib/launchpad_mk2/device.rb, line 191
def scroll_forever(color_key, text)
  scroll(color_key, text, 1)
end
scroll_once(color_key, text) click to toggle source
# File lib/launchpad_mk2/device.rb, line 195
def scroll_once(color_key, text)
  scroll(color_key, text, 0)
end
scroll_stop() click to toggle source
# File lib/launchpad_mk2/device.rb, line 199
def scroll_stop()
  output_sysex(SYSEX_HEADER + [20] + SYSEX_FOOTER)
end

Private Instance Methods

color(color_key) click to toggle source
# 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
create_device!(devices, device_type, opts) click to toggle source

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
input() click to toggle source

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
message(status, data1, data2) click to toggle source

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
note(type, opts) click to toggle source

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
output(status, data1, data2) click to toggle source

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
output_messages(messages) click to toggle source

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
output_sysex(messages) click to toggle source
# 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
velocity(opts) click to toggle source

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