class Nibbler::Parser

Attributes

buffer[R]

Public Class Methods

new(library) click to toggle source
# File lib/nibbler/parser.rb, line 7
def initialize(library)
  @library = library
  @running_status = RunningStatus.new
  @buffer = []
end

Public Instance Methods

nibbles_to_message(fragment) click to toggle source

If possible, convert the given fragment to a MIDI message @param [Array<String>] fragment A fragment of data eg [“9”, “0”, “4”, “0”, “5”, “0”] @return [Hash, nil]

# File lib/nibbler/parser.rb, line 49
def nibbles_to_message(fragment)
  if fragment.length >= 2
    # convert the part of the fragment to start with to a numeric
    slice = fragment.slice(0..1).map(&:hex)
    compute_message(slice, fragment)
  end
end
process(nibbles) click to toggle source

Process the given nibbles and add them to the buffer @param [Array<String, Integer>] nibbles @return [Hash]

# File lib/nibbler/parser.rb, line 16
def process(nibbles)
  report = {
    :messages => [],
    :processed => [],
    :rejected => []
  }
  pointer = 0
  @buffer += nibbles
  # Iterate through nibbles in the buffer until a status message is found
  while pointer <= (@buffer.length - 1)
    # fragment is the piece of the buffer to look at
    fragment = get_fragment(pointer)
    # See if there really is a message there
    unless (processed = nibbles_to_message(fragment)).nil?
      # if fragment contains a real message, reject the nibbles that precede it
      report[:rejected] += @buffer.slice(0, pointer)
      # and record it
      @buffer = fragment.dup # fragment now has the remaining nibbles for next pass
      fragment = nil # Reset fragment
      pointer = 0 # Reset iterator
      report[:messages] << processed[:message]
      report[:processed] += processed[:processed]
    else
      @running_status.cancel
      pointer += 1
    end
  end
  report
end

Private Instance Methods

compute_message(nibbles, fragment) click to toggle source

Attempt to convert the given nibbles into a MIDI message @param [Array<Integer>] nibbles @return [Hash, nil]

# File lib/nibbler/parser.rb, line 62
def compute_message(nibbles, fragment)
  case nibbles[0]
  when 0x8..0xE then lookahead(fragment, MessageBuilder.for_channel_message(@library, nibbles[0]))
  when 0xF then
    case nibbles[1]
    when 0x0 then lookahead_for_sysex(fragment)
    else lookahead(fragment, MessageBuilder.for_system_message(@library, nibbles[1]), :recursive => true)
    end
  else
    lookahead_using_running_status(fragment) if @running_status.possible?
  end
end
get_fragment(pointer) click to toggle source

Get the data in the buffer for the given pointer @param [Integer] pointer @return [Array<String>]

# File lib/nibbler/parser.rb, line 85
def get_fragment(pointer)
  @buffer[pointer, (@buffer.length - pointer)]
end
lookahead(fragment, message_builder, options = {}) click to toggle source

If the given fragment has at least the given number of nibbles, use it to build a hash that can be used to build a MIDI message

@param [Integer] num_nibbles @param [Array<String>] fragment @param [Hash] options @option options [String] :status_nibble_2 @option options [Boolean] :recursive @return [Hash, nil]

# File lib/nibbler/parser.rb, line 98
def lookahead(fragment, message_builder, options = {})
  offset = options.fetch(:offset, 0)
  num_nibbles = message_builder.num_nibbles + offset
  if fragment.size >= num_nibbles
    # if so shift those nibbles off of the array and call block with them
    nibbles = fragment.slice!(0, num_nibbles)
    status_nibble_2 ||= options[:status_nibble_2] || nibbles[1]

    # send the nibbles to the block as bytes
    # return the evaluated block and the remaining nibbles
    bytes = TypeConversion.hex_chars_to_numeric_bytes(nibbles)
    bytes = bytes[1..-1] if options[:status_nibble_2].nil?

    # record the fragment situation in case running status comes up next round
    @running_status.set(offset - 2, message_builder, status_nibble_2)

    message_args = [status_nibble_2.hex]
    message_args += bytes if num_nibbles > 2

    message = message_builder.build(*message_args)
    {
      :message => message,
      :processed => nibbles
    }
  elsif num_nibbles > 0 && !!options[:recursive]
    lookahead(fragment, message_builder, options.merge({ :offset => offset - 2 }))
  end
end
lookahead_for_sysex(fragment) click to toggle source
# File lib/nibbler/parser.rb, line 127
def lookahead_for_sysex(fragment)
  @running_status.cancel
  bytes = TypeConversion.hex_chars_to_numeric_bytes(fragment)
  unless (index = bytes.index(0xF7)).nil?
    message_data = bytes.slice!(0, index + 1)
    message = MessageBuilder.build_system_exclusive(@library, *message_data)
    {
      :message => message,
      :processed => fragment.slice!(0, (index + 1) * 2)
    }
  end
end
lookahead_using_running_status(fragment) click to toggle source

Attempt to convert the fragment to a MIDI message using the given fragment and cached running status @param [Array<String>] fragment A fragment of data eg [“4”, “0”, “5”, “0”] @return [Hash, nil]

# File lib/nibbler/parser.rb, line 78
def lookahead_using_running_status(fragment)
  lookahead(fragment, @running_status[:message_builder], :offset => @running_status[:offset], :status_nibble_2 => @running_status[:status_nibble_2])
end