Module: Webmidi::Message::Parser

Defined in:
lib/webmidi/message/parser.rb

Constant Summary collapse

CHANNEL_LENGTHS =
{
  0x80 => 3,
  0x90 => 3,
  0xA0 => 3,
  0xB0 => 3,
  0xC0 => 2,
  0xD0 => 2,
  0xE0 => 3
}.freeze
SYSTEM_LENGTHS =
{
  0xF1 => 2,
  0xF2 => 3,
  0xF3 => 2,
  0xF6 => 1,
  0xF8 => 1,
  0xFA => 1,
  0xFB => 1,
  0xFC => 1,
  0xFE => 1,
  0xFF => 1
}.freeze
REAL_TIME_STATUSES =
[0xF8, 0xFA, 0xFB, 0xFC, 0xFE, 0xFF].freeze
INVALID_SYSTEM_STATUSES =
[0xF4, 0xF5, 0xF7, 0xF9, 0xFD].freeze

Class Method Summary collapse

Class Method Details

.parse_many(bytes, normalize_note_on_zero: true) ⇒ Object



68
69
70
# File 'lib/webmidi/message/parser.rb', line 68

def parse_many(bytes, normalize_note_on_zero: true)
  parse_stream(bytes, running_status: false, normalize_note_on_zero: normalize_note_on_zero)
end

.parse_single(bytes, normalize_note_on_zero: true) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/webmidi/message/parser.rb', line 34

def parse_single(bytes, normalize_note_on_zero: true)
  bytes = validate_bytes!(bytes)
  raise InvalidMessageError, "Empty message" if bytes.empty?

  status = bytes[0]
  validate_status!(status)

  if status == 0xF0
    return parse_sysex(bytes)
  end

  validate_exact_length!(bytes, message_length(status))
  validate_data_bytes!(bytes[1..])

  case status & 0xF0
  when 0x80
    parse_note_off(bytes)
  when 0x90
    parse_note_on(bytes, normalize_note_on_zero: normalize_note_on_zero)
  when 0xA0
    parse_polyphonic_pressure(bytes)
  when 0xB0
    parse_control_change(bytes)
  when 0xC0
    parse_program_change(bytes)
  when 0xD0
    parse_channel_pressure(bytes)
  when 0xE0
    parse_pitch_bend(bytes)
  when 0xF0
    parse_system(bytes)
  end
end

.parse_stream(bytes, running_status: true, normalize_note_on_zero: true) ⇒ Object



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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/webmidi/message/parser.rb', line 72

def parse_stream(bytes, running_status: true, normalize_note_on_zero: true)
  bytes = validate_bytes!(bytes)
  messages = []
  pending = []
  needed = nil
  last_channel_status = nil

  bytes.each do |byte|
    if real_time_status?(byte)
      messages << parse_single([byte], normalize_note_on_zero: normalize_note_on_zero)
      next
    end

    if pending.empty?
      if byte < 0x80
        raise InvalidMessageError, "Data byte #{format_byte(byte)} without status" unless running_status && last_channel_status

        pending = [last_channel_status, byte]
        needed = message_length(last_channel_status)
      else
        validate_status!(byte)
        pending = [byte]
        needed = (byte == 0xF0) ? :sysex : message_length(byte)
        last_channel_status = channel_status?(byte) ? byte : nil
      end
    elsif needed == :sysex
      validate_sysex_data_or_end!(byte)
      pending << byte
    else
      raise InvalidMessageError, "Unexpected status byte #{format_byte(byte)} inside message" if byte >= 0x80

      pending << byte
    end

    next unless message_complete?(pending, needed)

    messages << parse_single(pending, normalize_note_on_zero: normalize_note_on_zero)
    pending = []
    needed = nil
  end

  raise_incomplete!(pending, needed) unless pending.empty?

  messages
end