Class: PM::Connection

Inherits:
Object
  • Object
show all
Defined in:
lib/patchmaster/connection.rb

Overview

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.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input, input_chan, output, output_chan, filter = nil, opts = {}) ⇒ Connection

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.



19
20
21
22
23
24
25
# 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

Instance Attribute Details

#bankObject

Returns the value of attribute bank.



11
12
13
# File 'lib/patchmaster/connection.rb', line 11

def bank
  @bank
end

#filterObject

Returns the value of attribute filter.



11
12
13
# File 'lib/patchmaster/connection.rb', line 11

def filter
  @filter
end

#inputObject

Returns the value of attribute input.



11
12
13
# File 'lib/patchmaster/connection.rb', line 11

def input
  @input
end

#input_chanObject

Returns the value of attribute input_chan.



11
12
13
# File 'lib/patchmaster/connection.rb', line 11

def input_chan
  @input_chan
end

#outputObject

Returns the value of attribute output.



11
12
13
# File 'lib/patchmaster/connection.rb', line 11

def output
  @output
end

#output_chanObject

Returns the value of attribute output_chan.



11
12
13
# File 'lib/patchmaster/connection.rb', line 11

def output_chan
  @output_chan
end

#pc_progObject

Returns the value of attribute pc_prog.



11
12
13
# File 'lib/patchmaster/connection.rb', line 11

def pc_prog
  @pc_prog
end

#xposeObject

Returns the value of attribute xpose.



11
12
13
# File 'lib/patchmaster/connection.rb', line 11

def xpose
  @xpose
end

#zoneObject

Returns the value of attribute zone.



11
12
13
# File 'lib/patchmaster/connection.rb', line 11

def zone
  @zone
end

Instance Method Details

#accept_from_input?(bytes) ⇒ Boolean

Returns:

  • (Boolean)


42
43
44
45
46
# 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) ⇒ Boolean

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

Returns:

  • (Boolean)


50
51
52
# File 'lib/patchmaster/connection.rb', line 50

def inside_zone?(note)
  @zone == nil || @zone.include?(note)
end

#midi_in(bytes) ⇒ Object

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.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# 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) ⇒ Object



103
104
105
# File 'lib/patchmaster/connection.rb', line 103

def midi_out(bytes)
  @output.midi_out(bytes)
end

#note_num_to_name(n) ⇒ Object



111
112
113
114
115
# 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?Boolean

Returns:

  • (Boolean)


107
108
109
# File 'lib/patchmaster/connection.rb', line 107

def pc?
  @pc_prog != nil
end

#start(start_bytes = nil) ⇒ Object



27
28
29
30
31
32
33
34
35
# 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) ⇒ Object



37
38
39
40
# 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_sObject



117
118
119
120
121
122
123
# 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