Skip to content

Commit

Permalink
extract methods
Browse files Browse the repository at this point in the history
  • Loading branch information
arirusso committed Sep 18, 2014
1 parent afd56bb commit 9d6fd0a
Showing 1 changed file with 57 additions and 30 deletions.
87 changes: 57 additions & 30 deletions lib/diamond/arpeggiator.rb
Expand Up @@ -14,13 +14,14 @@ class Arpeggiator
# @option options [Fixnum] :gate Duration of the arpeggiated notes. The value is a percentage based on the rate. If the rate is 4, then a gate of 100 is equal to a quarter note. (default: 75) must be 1..500
# @option options [Fixnum] :interval Increment (pattern) over (interval) scale degrees (range) times. May be positive or negative. (default: 12)
# @option options [Array<UniMIDI::Input, UniMIDI::Output>, UniMIDI::Input, UniMIDI::Output] :midi MIDI devices to use
# @option options [Array<Hash>] :midi_control A user-defined mapping of MIDI cc to arpeggiator params
# @option options [Fixnum] :tx_channel Send output messages to the given MIDI channel despite what channel the input notes were intended for.
# @option options [Fixnum] :pattern_offset Begin on the nth note of the sequence (but not omit any notes). (default: 0)
# @option options [String, Pattern] :pattern Computes the contour of the arpeggiated melody. Can be the name of a pattern or a pattern object.
# @option options [Fixnum] :range Increment the (pattern) over (interval) scale degrees (range) times. Must be positive (abs will be used). (default: 3)
# @option options [Fixnum] :rate How fast the arpeggios will be played. Must be positive (abs will be used). (default: 8, eighth note.) must be 0..resolution
# @option options [Fixnum] :resolution Numeric resolution for rhythm (default: 128)
# @option options [Hash] :osc_map A map of OSC addresses and properties
# @option options [Hash] :osc_control A user-defined map of OSC addresses and properties to arpeggiator params
# @option options [Fixnum] :osc_port The port to listen for OSC on
def initialize(options = {}, &block)
devices = MIDIInstrument::Device.partition(options[:midi])
Expand All @@ -29,8 +30,12 @@ def initialize(options = {}, &block)
@sequence = Sequence.new
@parameter = SequenceParameters.new(@sequence, resolution, options) { @sequence.mark_changed }
@sequencer = Sequencer.new
initialize_midi(devices, options)
initialize_osc(options)

initialize_midi_devices(devices, options)
initialize_midi_output
initialize_midi_note_control
initialize_midi_parameter_control(options[:midi_control]) if !options[:midi_control].nil?
initialize_osc_parameter_control(options[:osc_control], :port => options[:port]) if !options[:osc_control].nil?
end

# Add MIDI input notes
Expand All @@ -51,6 +56,34 @@ def remove(*args)

private

# @param [Hash] options
# @option options [Hash] :osc_map A map of OSC addresses and properties
# @option options [Fixnum] :osc_port The port to listen for OSC on
def initialize_osc_parameter_control(map, options = {})
@osc_controller = OSC::Controller.new(self, map, :port => options[:port])
@osc_controller.start
end

# Initialize a user-defined map of control change messages
# @param [Array<Hash>] map
# @return [Boolean]
def initialize_midi_parameter_control(map)
from_range = 0..127
@midi.input.receive(:class => MIDIMessage::ControlChange) do |event|
message = event[:message]
if @midi.input.channel.nil? || @midi.input.channel == message.channel
index = message.index
mapping = map.find { |mapping| mapping[:index] == index }
property = mapping[:property]
to_range = SequenceParameters::RANGE[property]
value = message.value
value = Scale.transform(value).from(from_range).to(to_range)
puts "MIDI: Arpeggiator #{property}= #{value}"
@parameter.send("#{property}=", value)
end
end
end

# Emit any note off messages that are currently pending in the queue. The clock triggers this
# when stopping or pausing
# @return [Array<MIDIMessage::NoteOff>]
Expand All @@ -64,44 +97,38 @@ def emit_pending_note_offs
# @param [Hash] devices
# @param [Hash] options
# @option options [Fixnum] :channel The receive channel (also: :rx_channel)
# @option options [Array<Hash>] :midi_control Specify a user defined control change message map
# @option options [Fixnum] :tx_channel The transmit channel
# @return [Boolean]
def initialize_midi(devices, options = {})
def initialize_midi_devices(devices, options = {})
@midi = MIDIInstrument::Node.new
initialize_midi_input(devices[:input], options[:rx_channel] || options[:channel])
initialize_midi_output(devices[:output], options[:tx_channel])
true
@midi.input.devices.concat(devices[:input])
@midi.input.channel = options[:rx_channel] || options[:channel]
@midi.output.devices.concat(devices[:output])
@midi.output.channel = options[:tx_channel]
@midi
end

# @param [Hash] options
# @option options [Hash] :osc_map A map of OSC addresses and properties
# @option options [Fixnum] :osc_port The port to listen for OSC on
def initialize_osc(options = {})
if !options[:osc_map].nil?
@osc = OSC::Controller.new(self, options[:osc_map], :port => options[:osc_port])
@osc.start
# Initialize adding and removing MIDI notes from the sequence
def initialize_midi_note_control
@midi.input.receive(:class => MIDIMessage::NoteOn) do |event|
message = event[:message]
if @midi.input.channel.nil? || @midi.input.channel == message.channel
@sequence.add(message)
end
end
@midi.input.receive(:class => MIDIMessage::NoteOff) do |event|
message = event[:message]
if @midi.input.channel.nil? || @midi.input.channel == message.channel
@sequence.remove(message)
end
end
end

# Initialize MIDI input, adding and removing notes from the sequence
# @param [Array<UniMIDI::Input>] inputs
# @param [Fixnum] channel
# @return [Boolean]
def initialize_midi_input(inputs, channel)
@midi.input.devices.concat(inputs)
@midi.input.channel = channel
@midi.input.receive(:class => MIDIMessage::NoteOn) { |event| @sequence.add(event[:message]) }
@midi.input.receive(:class => MIDIMessage::NoteOff) { |event| @sequence.remove(event[:message]) }
true
end

# Initialize MIDI output, enabling the sequencer to emit notes
# @param [Array<UniMIDI::Output>] outputs
# @param [Fixnum] channel
# @return [Boolean]
def initialize_midi_output(outputs, channel)
@midi.output.devices.concat(outputs)
@midi.output.channel = channel
def initialize_midi_output
@sequencer.event.perform << proc do |data|
@midi.output.puts(data) unless data.empty?
end
Expand Down

0 comments on commit 9d6fd0a

Please sign in to comment.