class PM::Connection

A Connection connects an InputInstrument to an OutputInstrument. Whenever MIDI data arrives at the InputInstrument it is optionally modified or filtered, then the remaining modified data is sent to the OutputInstrument.

Attributes

bank[RW]
filter[RW]
input[RW]
input_chan[RW]
output[RW]
output_chan[RW]
pc_prog[RW]
xpose[RW]
zone[RW]

Public Class Methods

new(input, input_chan, output, output_chan, filter=nil, opts={}) click to toggle source

If input_chan is nil than all messages from input will be sent to output.

All channels (input_chan, output_chan, etc.) are 1-based here but are turned into 0-based channels for later use.

# File lib/patchmaster/connection.rb, line 19
def initialize(input, input_chan, output, output_chan, filter=nil, opts={})
  @input, @input_chan, @output, @output_chan, @filter = input, input_chan, output, output_chan, filter
  @bank, @pc_prog, @zone, @xpose = opts[:bank], opts[:pc_prog], opts[:zone], opts[:xpose]

  @input_chan -= 1 if @input_chan
  @output_chan -= 1 if @output_chan
end

Public Instance Methods

accept_from_input?(bytes) click to toggle source
# File lib/patchmaster/connection.rb, line 42
def accept_from_input?(bytes)
  return true if @input_chan == nil
  return true unless bytes.channel?
  bytes.channel == @input_chan
end
inside_zone?(note) click to toggle source

Returns true if the +@zone+ is nil (allowing all notes throught) or if +@zone+ is a Range and note is inside +@zone+.

# File lib/patchmaster/connection.rb, line 50
def inside_zone?(note)
  @zone == nil || @zone.include?(note)
end
midi_in(bytes) click to toggle source

The workhorse. Ignore bytes that aren't from our input, or are outside the zone. Change to output channel. Filter.

Note that running bytes are not handled, but unimidi doesn't seem to use them anyway.

Finally, we go through gyrations to avoid duping bytes unless they are actually modified in some way.

# File lib/patchmaster/connection.rb, line 62
def midi_in(bytes)
  return unless accept_from_input?(bytes)

  bytes_duped = false

  high_nibble = bytes.high_nibble
  case high_nibble
  when NOTE_ON, NOTE_OFF, POLY_PRESSURE
    return unless inside_zone?(bytes[1])

    if bytes[0] != high_nibble + @output_chan || (@xpose && @xpose != 0)
      bytes = bytes.dup
      bytes_duped = true
    end

    bytes[0] = high_nibble + @output_chan
    bytes[1] = ((bytes[1] + @xpose) & 0xff) if @xpose
  when CONTROLLER, PROGRAM_CHANGE, CHANNEL_PRESSURE, PITCH_BEND
    if bytes[0] != high_nibble + @output_chan
      bytes = bytes.dup
      bytes_duped = true
      bytes[0] = high_nibble + @output_chan
    end
  end

  # We can't tell if a filter will modify the bytes, so we have to assume
  # they will be. If we didn't, we'd have to rely on the filter duping the
  # bytes and returning the dupe.
  if @filter
    if !bytes_duped
      bytes = bytes.dup
      bytes_duped = true
    end
    bytes = @filter.call(self, bytes)
  end

  if bytes && bytes.size > 0
    midi_out(bytes)
  end
end
midi_out(bytes) click to toggle source
# File lib/patchmaster/connection.rb, line 103
def midi_out(bytes)
  @output.midi_out(bytes)
end
note_num_to_name(n) click to toggle source
# File lib/patchmaster/connection.rb, line 111
def note_num_to_name(n)
  oct = (n / 12) - 1
  note = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'][n % 12]
  "#{note}#{oct}"
end
pc?() click to toggle source
# File lib/patchmaster/connection.rb, line 107
def pc?
  @pc_prog != nil
end
start(start_bytes=nil) click to toggle source
# File lib/patchmaster/connection.rb, line 27
def start(start_bytes=nil)
  bytes = []
  bytes += start_bytes if start_bytes
  # Bank select uses MSB if we're only sending one byte
  bytes += [CONTROLLER + @output_chan, CC_BANK_SELECT+32, @bank] if @bank
  bytes += [PROGRAM_CHANGE + @output_chan, @pc_prog] if @pc_prog
  midi_out(bytes) unless bytes.empty?
  @input.add_connection(self)
end
stop(stop_bytes=nil) click to toggle source
# File lib/patchmaster/connection.rb, line 37
def stop(stop_bytes=nil)
  midi_out(stop_bytes) if stop_bytes
  @input.remove_connection(self)
end
to_s() click to toggle source
# File lib/patchmaster/connection.rb, line 117
def to_s
  str = "#{@input.name} ch #{@input_chan ? @input_chan+1 : 'all'} -> #{@output.name} ch #{@output_chan+1}"
  str << "; pc #@pc_prog" if pc?
  str << "; xpose #@xpose" if @xpose
  str << "; zone #{note_num_to_name(@zone.begin)}..#{note_num_to_name(@zone.end)}" if @zone
  str
end