lib/patchmaster/connection.rb in patchmaster-1.0.0 vs lib/patchmaster/connection.rb in patchmaster-1.1.2

- old
+ new

@@ -16,20 +16,21 @@ # # All channels (input_chan, output_chan, etc.) are 1-based here but are # turned into 0-based channels for later use. 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 - @pc_prog, @zone, @xpose = opts[:pc_prog], opts[:zone], opts[:xpose] + @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 def start(start_bytes=nil) bytes = [] bytes += start_bytes if start_bytes - bytes += [CC_BANK_SELECT + @output_chan, @bank] if @bank + # 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 @@ -48,24 +49,53 @@ # +@zone+ is a Range and +note+ is inside +@zone+. def inside_zone?(note) @zone == nil || @zone.include?(note) end + # 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. def midi_in(bytes) return unless accept_from_input?(bytes) - # TODO handle running bytes if needed + 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 - bytes[0] = high_nibble + @output_chan + if bytes[0] != high_nibble + @output_chan + bytes = bytes.dup + bytes_duped = true + bytes[0] = high_nibble + @output_chan + end end - bytes = @filter.call(self, bytes) if @filter + # 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