class LaunchpadMk2::Interaction
This class provides advanced interaction features.
Example:
require 'launchpad_mk2' interaction = Launchpad::Interaction.new interaction.response_to(:grid, :down) do |interaction, action| interaction.device.change(:grid, action.merge(:red => :high)) end interaction.response_to(:mixer, :down) do |interaction, action| interaction.stop end interaction.start
Attributes
Returns whether the Launchpad::Interaction is active or not.
Returns the Launchpad::Device the Launchpad::Interaction acts on.
Public Class Methods
Initializes the interaction.
Optional options hash:
:device
-
Launchpad::Device to act on, optional,
:input_device_id/:output_device_id
will be used if omitted :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”
:latency
-
delay (in s, fractions allowed) between MIDI pulls, optional, defaults to 0.001 (1ms)
:logger
- Logger
-
to be used by this interaction 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/interaction.rb, line 51 def initialize(opts = nil) @reader_thread = nil @device = nil opts ||= {} self.logger = opts[:logger] logger.debug "initializing Launchpad::Interaction##{object_id} with #{opts.inspect}" @device ||= opts[:device] @device ||= Device.new(opts.merge( :input => true, :output => true, :logger => opts[:logger] )) @latency = (opts[:latency] || 0.001).to_f.abs @active = false @action_threads = ThreadGroup.new end
Public Instance Methods
Closes the interaction's device - nothing can be done with the interaction/device afterwards.
Errors raised:
- Launchpad::NoInputAllowedError
-
when input is not enabled on the interaction's device
- Launchpad::CommunicationError
-
when anything unexpected happens while communicating with the
# File lib/launchpad_mk2/interaction.rb, line 85 def close logger.debug "closing Launchpad::Interaction##{object_id}" stop @device.close end
Determines whether this interaction's device has been closed.
# File lib/launchpad_mk2/interaction.rb, line 92 def closed? @device.closed? end
Sets the logger to be used by the current instance and the device.
logger
-
the [Logger] instance
# File lib/launchpad_mk2/interaction.rb, line 74 def logger=(logger) @logger = logger @device.logger = logger if @device end
Deregisters all responses to one or more actions.
Parameters (see Launchpad for values):
types
-
one or an array of button types to respond to, additional value
:all
for actions on all buttons (but not meaning “all responses”), optional, defaults tonil
, meaning “all responses” state
-
button state to respond to, additional value
:both
Optional options hash:
:x
-
x coordinate(s), can contain arrays and ranges, when specified without y coordinate, it's interpreted as a whole column
:y
-
y coordinate(s), can contain arrays and ranges, when specified without x coordinate, it's interpreted as a whole row
# File lib/launchpad_mk2/interaction.rb, line 225 def no_response_to(types = nil, state = :both, opts = nil) logger.debug "removing response to #{types.inspect} for state #{state.inspect}" types = Array(types) Array(state == :both ? %w(down up) : state).each do |current_state| types.each do |type| combined_types(type, opts).each do |combined_type| responses[combined_type][current_state.to_sym].clear end end end nil end
Responds to an action by executing all matching responses, effectively simulating a button press/release.
Parameters (see Launchpad for values):
type
-
type of the button to trigger
state
-
state of the button
Optional options hash (see Launchpad for values):
:x
-
x coordinate
:y
-
y coordinate
# File lib/launchpad_mk2/interaction.rb, line 250 def respond_to(type, state, opts = nil) respond_to_action((opts || {}).merge(:type => type, :state => state)) end
Registers a response to one or more actions.
Parameters (see Launchpad for values):
types
-
one or an array of button types to respond to, additional value
:all
for all buttons state
-
button state to respond to, additional value
:both
Optional options hash:
:exclusive
-
true/false
, whether to deregister all other responses to the specified actions, optional, defaults tofalse
:x
-
x coordinate(s), can contain arrays and ranges, when specified without y coordinate, it's interpreted as a whole column
:y
-
y coordinate(s), can contain arrays and ranges, when specified without x coordinate, it's interpreted as a whole row
Takes a block which will be called when an action matching the parameters occurs.
Block parameters:
interaction
-
the interaction object that received the action
action
-
the action received from Launchpad::Device.read_pending_actions
# File lib/launchpad_mk2/interaction.rb, line 193 def response_to(types = :all, state = :both, opts = nil, &block) logger.debug "setting response to #{types.inspect} for state #{state.inspect} with #{opts.inspect}" types = Array(types) opts ||= {} no_response_to(types, state) if opts[:exclusive] == true Array(state == :both ? %w(down up) : state).each do |current_state| types.each do |type| combined_types(type, opts).each do |combined_type| responses[combined_type][current_state.to_sym] << block end end end nil end
Starts interacting with the launchpad. Resets the device when the interaction was properly stopped via stop or close.
Optional options hash:
:detached
-
true/false
, whether to detach the interaction, method is blocking whenfalse
, optional, defaults tofalse
Errors raised:
- Launchpad::NoInputAllowedError
-
when input is not enabled on the interaction's device
- Launchpad::NoOutputAllowedError
-
when output is not enabled on the interaction's device
- Launchpad::CommunicationError
-
when anything unexpected happens while communicating with the launchpad
# File lib/launchpad_mk2/interaction.rb, line 110 def start(opts = nil) logger.debug "starting Launchpad::Interaction##{object_id}" opts = { :detached => false }.merge(opts || {}) @active = true @reader_thread ||= Thread.new do begin while @active do @device.read_pending_actions.each do |pending_action| action_thread = Thread.new(pending_action) do |action| respond_to_action(action) end @action_threads.add(action_thread) end sleep @latency# if @latency > 0.0 end rescue Portmidi::DeviceError => e logger.fatal "could not read from device, stopping to read actions" raise CommunicationError.new(e) rescue Exception => e logger.fatal "error causing action reading to stop: #{e.inspect}" raise e end end @reader_thread.join unless opts[:detached] end
Stops interacting with the launchpad.
Errors raised:
- Launchpad::NoInputAllowedError
-
when input is not enabled on the interaction's device
- Launchpad::CommunicationError
-
when anything unexpected happens while communicating with the
# File lib/launchpad_mk2/interaction.rb, line 147 def stop logger.debug "stopping Launchpad::Interaction##{object_id}" @active = false if @reader_thread # run (resume from sleep) and wait for @reader_thread to end @reader_thread.run if @reader_thread.alive? @reader_thread.join @reader_thread = nil end ensure @action_threads.list.each do |thread| begin thread.kill thread.join rescue Exception => e logger.error "error when killing action thread: #{e.inspect}" end end nil end
Private Instance Methods
Returns a list of combined types for the type and opts specified. Combined types are just the type, except for grid, where the opts are interpreted and all combinations of x and y coordinates are added as a position suffix.
Example:
combined_types
(:grid, :x => 1..2, y => 2) => [:grid12, :grid22]
Parameters (see Launchpad for values):
type
-
type of the button
Optional options hash:
:x
-
x coordinate(s), can contain arrays and ranges, when specified without y coordinate, it's interpreted as a whole column
:y
-
y coordinate(s), can contain arrays and ranges, when specified without x coordinate, it's interpreted as a whole row
# File lib/launchpad_mk2/interaction.rb, line 295 def combined_types(type, opts = nil) if type.to_sym == :grid && opts x = grid_range(opts[:x]) y = grid_range(opts[:y]) return [:grid] if x.nil? && y.nil? # whole grid x ||= ['-'] # whole row y ||= ['-'] # whole column x.product(y).map {|current_x, current_y| :"grid#{current_x}#{current_y}"} else [type.to_sym] end end
Returns an array of grid positions for a range.
Parameters:
range
-
the range definitions, can be
-
a Fixnum
-
a Range
-
an Array of Fixnum, Range or Array objects
-
# File lib/launchpad_mk2/interaction.rb, line 270 def grid_range(range) return nil if range.nil? Array(range).flatten.map do |pos| pos.respond_to?(:to_a) ? pos.to_a : pos end.flatten.uniq end
Reponds to an action by executing all matching responses.
Parameters:
action
-
hash containing an action from Launchpad::Device.read_pending_actions
# File lib/launchpad_mk2/interaction.rb, line 313 def respond_to_action(action) type = action[:type].to_sym state = action[:state].to_sym actions = [] if type == :grid actions += responses[:"grid#{action[:x]}#{action[:y]}"][state] actions += responses[:"grid#{action[:x]}-"][state] actions += responses[:"grid-#{action[:y]}"][state] end actions += responses[type][state] actions += responses[:all][state] actions.compact.each {|block| block.call(self, action)} nil rescue Exception => e logger.error "error when responding to action #{action.inspect}: #{e.inspect}" raise e end
Returns the hash storing all responses. Keys are button types, values are hashes themselves, keys are :down/:up
, values are arrays of responses.
# File lib/launchpad_mk2/interaction.rb, line 258 def responses @responses ||= Hash.new {|hash, key| hash[key] = {:down => [], :up => []}} end