lib/launchpad/interaction.rb in launchpad-0.1.0 vs lib/launchpad/interaction.rb in launchpad-0.1.1
- old
+ new
@@ -1,27 +1,76 @@
require 'launchpad/device'
module Launchpad
+ # This class provides advanced interaction features.
+ #
+ # Example:
+ #
+ # require 'rubygems'
+ # require 'launchpad'
+ #
+ # 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
class Interaction
- attr_reader :device, :active
+ # Returns the Launchpad::Device the Launchpad::Interaction acts on.
+ attr_reader :device
- # Initializes the launchpad interaction
- # {
- # :device => Launchpad::Device instance, optional
- # :device_name => Name of the MIDI device to use, optional, defaults to Launchpad, ignored when :device is specified
- # :latency => delay (in s, fractions allowed) between MIDI pulls, optional, defaults to 0.001
- # }
+ # Returns whether the Launchpad::Interaction is active or not.
+ attr_reader :active
+
+ # Initializes the interaction.
+ #
+ # Optional options hash:
+ #
+ # [<tt>:device</tt>] Launchpad::Device to act on,
+ # optional, <tt>:input_device_id/:output_device_id</tt> will be used if omitted
+ # [<tt>:input_device_id</tt>] ID of the MIDI input device to use,
+ # optional, <tt>:device_name</tt> will be used if omitted
+ # [<tt>:output_device_id</tt>] ID of the MIDI output device to use,
+ # optional, <tt>:device_name</tt> will be used if omitted
+ # [<tt>:device_name</tt>] Name of the MIDI device to use,
+ # optional, defaults to "Launchpad"
+ # [<tt>:latency</tt>] delay (in s, fractions allowed) between MIDI pulls,
+ # optional, defaults to 0.001 (1ms)
+ #
+ # 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
def initialize(opts = nil)
opts ||= {}
@device = opts[:device] || Device.new(opts.merge(:input => true, :output => true))
@latency = (opts[:latency] || 0.001).to_f.abs
@active = false
end
- # Starts interacting with the launchpad, blocking
+ # Closes the interaction's device - nothing can be done with the interaction/device afterwards.
+ def close
+ @device.close
+ end
+
+ # Determines whether this interaction's device has been closed.
+ def closed?
+ @device.closed?
+ end
+
+ # Starts interacting with the launchpad, blocking. Resets the device when
+ # the interaction was properly stopped via stop.
+ #
+ # Errors raised:
+ #
+ # [Launchpad::NoInputAllowedError] when input is not enabled on the interaction's device
+ # [Launchpad::CommunicationError] when anything unexpected happens while communicating with the launchpad
def start
@active = true
while @active do
@device.read_pending_actions.each {|action| respond_to_action(action)}
sleep @latency unless @latency <= 0
@@ -29,60 +78,97 @@
@device.reset
rescue Portmidi::DeviceError => e
raise CommunicationError.new(e)
end
- # Stops interacting with the launchpad
+ # Stops interacting with the launchpad and resets it.
def stop
@active = false
end
- # Registers a response to one or more actions
- # types => the type of action to respond to, one or more of :all, :grid, :up, :down, :left, :right, :session, :user1, :user2, :mixer, :scene1 - :scene8, optional, defaults to :all
- # state => which state transition to respond to, one of :down, :up, :both, optional, defaults to :both
- # opts => {
- # :exclusive => whether all other responses to the given types shall be deregistered first
- # }
+ # 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 <tt>:all</tt> for all buttons
+ # [+state+] button state to respond to,
+ # additional value <tt>:both</tt>
+ #
+ # Optional options hash:
+ #
+ # [<tt>:exclusive</tt>] <tt>true/false</tt>,
+ # whether to deregister all other responses to the specified actions,
+ # optional, defaults to +false+
+ #
+ # 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
def response_to(types = :all, state = :both, opts = nil, &block)
types = Array(types)
opts ||= {}
no_response_to(types, state) if opts[:exclusive] == true
Array(state == :both ? %w(down up) : state).each do |state|
types.each {|type| responses[type.to_sym][state.to_sym] << block}
end
+ nil
end
- # Deregisters all responses to one or more actions
- # type => the type of response to clear, one or more of :all (not meaning "all responses" but "responses registered for type :all"), :grid, :up, :down, :left, :right, :session, :user1, :user2, :mixer, :scene1 - :scene8, optional, defaults to nil (meaning "all responses")
- # state => which state transition to not respond to, one of :down, :up, :both, optional, defaults to :both
+ # 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 <tt>:all</tt> for actions on all buttons
+ # (but not meaning "all responses"),
+ # optional, defaults to +nil+, meaning "all responses"
+ # [+state+] button state to respond to,
+ # additional value <tt>:both</tt>
def no_response_to(types = nil, state = :both)
types = Array(types)
Array(state == :both ? %w(down up) : state).each do |state|
types.each {|type| responses[type.to_sym][state.to_sym].clear}
end
+ nil
end
- # Responds to an action by executing all matching responses
- # type => the type of action to respond to, one of :grid, :up, :down, :left, :right, :session, :user1, :user2, :mixer, :scene1 - :scene8
- # state => which state transition to respond to, one of :down, :up
- # opts => {
- # :x => x coordinate (0 based from top left)
- # :y => y coordinate (0 based from top left)
- # }, unused unless type is :grid
+ # 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):
+ #
+ # [<tt>:x</tt>] x coordinate
+ # [<tt>:y</tt>] y coordinate
def respond_to(type, state, opts = nil)
respond_to_action((opts || {}).merge(:type => type, :state => state))
end
private
+ # Returns the hash storing all responses. Keys are button types, values are
+ # hashes themselves, keys are <tt>:down/:up</tt>, values are arrays of responses.
def responses
@responses ||= Hash.new {|hash, key| hash[key] = {:down => [], :up => []}}
end
+ # Reponds to an action by executing all matching responses.
+ #
+ # Parameters:
+ #
+ # [+action+] hash containing an action from Launchpad::Device.read_pending_actions
def respond_to_action(action)
type = action[:type].to_sym
state = action[:state].to_sym
(responses[type][state] + responses[:all][state]).each {|block| block.call(self, action)}
+ nil
end
end
end
\ No newline at end of file