midi-nibbler 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +29 -29
- data/lib/nibbler.rb +6 -3
- data/lib/nibbler/{hex_processor.rb → data_processor.rb} +20 -14
- data/lib/nibbler/message_builder.rb +84 -0
- data/lib/nibbler/message_library.rb +21 -0
- data/lib/nibbler/parser.rb +113 -91
- data/lib/nibbler/session.rb +27 -25
- data/lib/nibbler/type_conversion.rb +47 -11
- data/test/data_processor_test.rb +146 -0
- data/test/functional_buffer_test.rb +64 -0
- data/test/functional_rejected_test.rb +154 -0
- data/test/helper.rb +2 -0
- data/test/message_library_test.rb +37 -0
- data/test/midi_message_test.rb +201 -105
- data/test/parser_test.rb +221 -149
- data/test/type_conversion_test.rb +116 -17
- metadata +11 -8
- data/test/hex_processor_test.rb +0 -56
- data/test/parser_buffer_test.rb +0 -66
- data/test/parser_rejected_test.rb +0 -60
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 022b8e486f76d1621f5d3a73ad6eb3dff9764b6a
|
4
|
+
data.tar.gz: a030a1248bf74224ae98c85138ca146bbf07d1d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0fee2f393f46e64144d0f0c12a9421e6bcf86781bcf870b4d9af586cd3e284266f27678ffa985d6d80730e0e865ccfbddfe838729ff40c3810f49562497bcdb0
|
7
|
+
data.tar.gz: 418ece8a8a607b38c350342aa197ba94e636b3e138b0e40e66a2e87499fc4356a9257722d0d0307a8ecb36fb025e691e9cfeaef72b2d56e12ca106c2182a8b8b
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# Nibbler
|
2
2
|
|
3
|
-
![nibbler](http://
|
3
|
+
![nibbler](http://i.imgur.com/4BFZPJY.png)
|
4
4
|
|
5
5
|
Parse MIDI Messages
|
6
|
-
|
6
|
+
|
7
7
|
## Install
|
8
8
|
|
9
9
|
`gem install midi-nibbler`
|
@@ -16,12 +16,12 @@ or using Bundler, add this to your Gemfile
|
|
16
16
|
|
17
17
|
```ruby
|
18
18
|
require 'nibbler'
|
19
|
-
|
19
|
+
|
20
20
|
nibbler = Nibbler.new
|
21
21
|
```
|
22
22
|
|
23
23
|
Enter a message piece by piece
|
24
|
-
|
24
|
+
|
25
25
|
```ruby
|
26
26
|
nibbler.parse("90")
|
27
27
|
=> nil
|
@@ -30,31 +30,31 @@ nibbler.parse("40")
|
|
30
30
|
=> nil
|
31
31
|
|
32
32
|
nibbler.parse("40")
|
33
|
-
=> #<MIDIMessage::NoteOn:0x98c9818
|
34
|
-
@channel=0,
|
35
|
-
@data=[64, 100],
|
36
|
-
@name="C3",
|
37
|
-
@note=64,
|
38
|
-
@status=[9, 0],
|
39
|
-
@velocity=100,
|
33
|
+
=> #<MIDIMessage::NoteOn:0x98c9818
|
34
|
+
@channel=0,
|
35
|
+
@data=[64, 100],
|
36
|
+
@name="C3",
|
37
|
+
@note=64,
|
38
|
+
@status=[9, 0],
|
39
|
+
@velocity=100,
|
40
40
|
@verbose_name="Note On: C3">
|
41
41
|
```
|
42
|
-
|
42
|
+
|
43
43
|
Enter a message all at once
|
44
|
-
|
44
|
+
|
45
45
|
```ruby
|
46
46
|
nibbler.parse("904040")
|
47
|
-
|
48
|
-
=> #<MIDIMessage::NoteOn:0x98c9818
|
49
|
-
@channel=0,
|
50
|
-
@data=[64, 100],
|
51
|
-
@name="C3",
|
52
|
-
@note=64,
|
53
|
-
@status=[9, 0],
|
54
|
-
@velocity=100,
|
47
|
+
|
48
|
+
=> #<MIDIMessage::NoteOn:0x98c9818
|
49
|
+
@channel=0,
|
50
|
+
@data=[64, 100],
|
51
|
+
@name="C3",
|
52
|
+
@note=64,
|
53
|
+
@status=[9, 0],
|
54
|
+
@velocity=100,
|
55
55
|
@verbose_name="Note On: C3">
|
56
56
|
```
|
57
|
-
|
57
|
+
|
58
58
|
Use bytes
|
59
59
|
|
60
60
|
```ruby
|
@@ -63,14 +63,14 @@ nibbler.parse(0x90, 0x40, 0x40)
|
|
63
63
|
```
|
64
64
|
|
65
65
|
You can use nibbles in string format
|
66
|
-
|
66
|
+
|
67
67
|
```ruby
|
68
68
|
nibbler.parse("9", "0", "4", "0", "4", "0")
|
69
69
|
=> #<MIDIMessage::NoteOn:0x98c9818 ...>
|
70
70
|
```
|
71
71
|
|
72
72
|
Interchange the different types
|
73
|
-
|
73
|
+
|
74
74
|
```ruby
|
75
75
|
nibbler.parse("9", "0", 0x40, 64)
|
76
76
|
=> #<MIDIMessage::NoteOn:0x98c9818 ...>
|
@@ -84,7 +84,7 @@ nibbler.parse(0x40, 64)
|
|
84
84
|
```
|
85
85
|
|
86
86
|
Look at the messages we've parsed
|
87
|
-
|
87
|
+
|
88
88
|
```ruby
|
89
89
|
nibbler.messages
|
90
90
|
=> [#<MIDIMessage::NoteOn:0x98c9804 ...>
|
@@ -92,7 +92,7 @@ nibbler.messages
|
|
92
92
|
```
|
93
93
|
|
94
94
|
Add an incomplete message
|
95
|
-
|
95
|
+
|
96
96
|
```ruby
|
97
97
|
nibbler.parse("9")
|
98
98
|
nibbler.parse("40")
|
@@ -107,7 +107,7 @@ nibbler.buffer
|
|
107
107
|
nibbler.buffer_s
|
108
108
|
=> "940"
|
109
109
|
```
|
110
|
-
|
110
|
+
|
111
111
|
Pass in a timestamp
|
112
112
|
|
113
113
|
```ruby
|
@@ -119,7 +119,7 @@ Nibbler defaults to generate [midi-message](http://github.com/arirusso/midi-mess
|
|
119
119
|
|
120
120
|
```ruby
|
121
121
|
Nibbler.new(:message_lib => :midilib)
|
122
|
-
|
122
|
+
|
123
123
|
nibbler.parse("9", "0", 0x40, "40")
|
124
124
|
=> "0: ch 00 on 40 40"
|
125
125
|
```
|
@@ -136,4 +136,4 @@ nibbler.parse("9", "0", 0x40, "40")
|
|
136
136
|
|
137
137
|
Apache 2.0, See the file LICENSE
|
138
138
|
|
139
|
-
Copyright (c) 2011-
|
139
|
+
Copyright (c) 2011-2015 Ari Russo
|
data/lib/nibbler.rb
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
#
|
2
|
+
# Nibbler
|
2
3
|
# Parse MIDI Messages
|
3
|
-
# (c)2011-
|
4
|
+
# (c)2011-2015 Ari Russo and licensed under the Apache 2.0 License
|
4
5
|
#
|
5
6
|
|
6
7
|
# libs
|
7
8
|
require "forwardable"
|
8
9
|
|
9
10
|
# modules
|
10
|
-
require "nibbler/
|
11
|
+
require "nibbler/data_processor"
|
11
12
|
require "nibbler/type_conversion"
|
12
13
|
|
13
14
|
# classes
|
15
|
+
require "nibbler/message_builder"
|
16
|
+
require "nibbler/message_library"
|
14
17
|
require "nibbler/parser"
|
15
18
|
require "nibbler/session"
|
16
19
|
|
@@ -19,7 +22,7 @@ require "nibbler/session"
|
|
19
22
|
#
|
20
23
|
module Nibbler
|
21
24
|
|
22
|
-
VERSION = "0.2.
|
25
|
+
VERSION = "0.2.4"
|
23
26
|
|
24
27
|
# Shortcut to a new parser session
|
25
28
|
def self.new(*a, &block)
|
@@ -1,47 +1,53 @@
|
|
1
|
-
module Nibbler
|
2
|
-
|
1
|
+
module Nibbler
|
2
|
+
|
3
3
|
# Accepts various types of input and returns an array of hex digit chars
|
4
|
-
|
4
|
+
#
|
5
|
+
# Ideally this would output Integer objects. However, given that Ruby numerics 0x0 and 0x00 result in the same
|
6
|
+
# object (0 Integer), this would limit the parser to only working with bytes instead of both nibbles and bytes.
|
7
|
+
#
|
8
|
+
# For example, if the input were "5" then the processor would return an ambiguous 0x5
|
9
|
+
#
|
10
|
+
module DataProcessor
|
5
11
|
|
6
12
|
extend self
|
7
|
-
|
13
|
+
|
8
14
|
# Accepts various types of input and returns an array of hex digit chars
|
9
15
|
# Invalid input is disregarded
|
10
16
|
#
|
11
|
-
# @param [*String, *
|
17
|
+
# @param [*String, *Integer] args
|
12
18
|
# @return [Array<String>] An array of hex string nibbles eg "6", "a"
|
13
19
|
def process(*args)
|
14
|
-
args.map { |arg| convert(arg) }.flatten.compact.map(&:upcase)
|
20
|
+
args.map { |arg| convert(arg) }.flatten.compact.map(&:upcase)
|
15
21
|
end
|
16
|
-
|
22
|
+
|
17
23
|
private
|
18
24
|
|
19
25
|
# Convert a single value to hex chars
|
20
|
-
# @param [Array<
|
26
|
+
# @param [Array<Integer>, Array<String>, Integer, String] value
|
21
27
|
# @return [Array<String>]
|
22
28
|
def convert(value)
|
23
29
|
case value
|
24
30
|
when Array then value.map { |arr| process(*arr) }.reduce(:+)
|
25
31
|
when String then TypeConversion.hex_str_to_hex_chars(filter_string(value))
|
26
|
-
when
|
32
|
+
when Integer then TypeConversion.numeric_byte_to_hex_chars(filter_numeric(value))
|
27
33
|
end
|
28
34
|
end
|
29
|
-
|
35
|
+
|
30
36
|
# Limit the given number to bytes usable in MIDI ie values (0..240)
|
31
37
|
# returns nil if the byte is outside of that range
|
32
|
-
# @param [
|
33
|
-
# @return [
|
38
|
+
# @param [Integer] num
|
39
|
+
# @return [Integer, nil]
|
34
40
|
def filter_numeric(num)
|
35
41
|
num if (0x00..0xFF).include?(num)
|
36
42
|
end
|
37
|
-
|
43
|
+
|
38
44
|
# Only return valid hex string characters
|
39
45
|
# @param [String] string
|
40
46
|
# @return [String]
|
41
47
|
def filter_string(string)
|
42
48
|
string.gsub(/[^0-9a-fA-F]/, "").upcase
|
43
49
|
end
|
44
|
-
|
50
|
+
|
45
51
|
end
|
46
52
|
|
47
53
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Nibbler
|
2
|
+
|
3
|
+
class MessageBuilder
|
4
|
+
|
5
|
+
CHANNEL_MESSAGE = [
|
6
|
+
{
|
7
|
+
:status => 0x8,
|
8
|
+
:name => :note_off,
|
9
|
+
:nibbles => 6
|
10
|
+
},
|
11
|
+
{
|
12
|
+
:status => 0x9,
|
13
|
+
:name => :note_on,
|
14
|
+
:nibbles => 6
|
15
|
+
},
|
16
|
+
{
|
17
|
+
:status => 0xA,
|
18
|
+
:name => :polyphonic_aftertouch,
|
19
|
+
:nibbles => 6
|
20
|
+
},
|
21
|
+
{
|
22
|
+
:status => 0xB,
|
23
|
+
:name => :control_change,
|
24
|
+
:nibbles => 6
|
25
|
+
},
|
26
|
+
{
|
27
|
+
:status => 0xC,
|
28
|
+
:name => :program_change,
|
29
|
+
:nibbles => 4
|
30
|
+
},
|
31
|
+
{
|
32
|
+
:status => 0xD,
|
33
|
+
:name => :channel_aftertouch,
|
34
|
+
:nibbles => 4
|
35
|
+
},
|
36
|
+
{
|
37
|
+
:status => 0xE,
|
38
|
+
:name => :pitch_bend,
|
39
|
+
:nibbles => 6
|
40
|
+
}
|
41
|
+
].freeze
|
42
|
+
|
43
|
+
SYSTEM_MESSAGE = [
|
44
|
+
{
|
45
|
+
:status => 0x1..0x6,
|
46
|
+
:name => :system_common,
|
47
|
+
:nibbles => 6
|
48
|
+
},
|
49
|
+
{
|
50
|
+
:status => 0x8..0xF,
|
51
|
+
:name => :system_realtime,
|
52
|
+
:nibbles => 2
|
53
|
+
}
|
54
|
+
].freeze
|
55
|
+
|
56
|
+
attr_reader :num_nibbles, :name
|
57
|
+
|
58
|
+
def self.build_system_exclusive(library, *message_data)
|
59
|
+
library.system_exclusive(*message_data)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.for_system_message(library, status)
|
63
|
+
type = SYSTEM_MESSAGE.find { |type| type[:status].cover?(status) }
|
64
|
+
new(library, type[:name], type[:nibbles])
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.for_channel_message(library, status)
|
68
|
+
type = CHANNEL_MESSAGE.find { |type| type[:status] == status }
|
69
|
+
new(library, type[:name], type[:nibbles])
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize(library, name, num_nibbles)
|
73
|
+
@library = library
|
74
|
+
@name = name
|
75
|
+
@num_nibbles = num_nibbles
|
76
|
+
end
|
77
|
+
|
78
|
+
def build(*message_data)
|
79
|
+
@library.send(@name, *message_data)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Nibbler
|
2
|
+
|
3
|
+
class MessageLibrary
|
4
|
+
|
5
|
+
# MIDI message object library adapter
|
6
|
+
# @param [Symbol] lib The MIDI message library module eg MIDIMessage or Midilib
|
7
|
+
# @return [Module]
|
8
|
+
def self.adapter(lib = nil)
|
9
|
+
case lib
|
10
|
+
when :midilib then
|
11
|
+
require "nibbler/midilib"
|
12
|
+
::Nibbler::Midilib
|
13
|
+
else
|
14
|
+
require "nibbler/midi-message"
|
15
|
+
::Nibbler::MIDIMessage
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/lib/nibbler/parser.rb
CHANGED
@@ -4,143 +4,165 @@ module Nibbler
|
|
4
4
|
|
5
5
|
attr_reader :buffer
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@running_status = nil
|
7
|
+
def initialize(library)
|
8
|
+
@library = library
|
9
|
+
@running_status = RunningStatus.new
|
11
10
|
@buffer = []
|
12
|
-
@iterator = 0
|
13
|
-
|
14
|
-
initialize_message_library(options[:message_lib])
|
15
11
|
end
|
16
12
|
|
17
|
-
#
|
13
|
+
# Process the given nibbles and add them to the buffer
|
14
|
+
# @param [Array<String, Integer>] nibbles
|
18
15
|
# @return [Hash]
|
19
16
|
def process(nibbles)
|
20
|
-
report = {
|
21
|
-
:messages => [],
|
22
|
-
:processed => [],
|
23
|
-
:rejected => []
|
24
|
-
}
|
25
|
-
|
26
|
-
@buffer += nibbles
|
27
|
-
|
28
|
-
|
29
|
-
#
|
30
|
-
|
31
|
-
#
|
32
|
-
unless (processed = nibbles_to_message).nil?
|
33
|
-
# if
|
34
|
-
report[:rejected] += @buffer.slice(0,
|
35
|
-
# and record it
|
36
|
-
@buffer =
|
37
|
-
|
38
|
-
|
17
|
+
report = {
|
18
|
+
:messages => [],
|
19
|
+
:processed => [],
|
20
|
+
:rejected => []
|
21
|
+
}
|
22
|
+
pointer = 0
|
23
|
+
@buffer += nibbles
|
24
|
+
# Iterate through nibbles in the buffer until a status message is found
|
25
|
+
while pointer <= (@buffer.length - 1)
|
26
|
+
# fragment is the piece of the buffer to look at
|
27
|
+
fragment = get_fragment(pointer)
|
28
|
+
# See if there really is a message there
|
29
|
+
unless (processed = nibbles_to_message(fragment)).nil?
|
30
|
+
# if fragment contains a real message, reject the nibbles that precede it
|
31
|
+
report[:rejected] += @buffer.slice(0, pointer)
|
32
|
+
# and record it
|
33
|
+
@buffer = fragment.dup # fragment now has the remaining nibbles for next pass
|
34
|
+
fragment = nil # Reset fragment
|
35
|
+
pointer = 0 # Reset iterator
|
39
36
|
report[:messages] << processed[:message]
|
40
|
-
report[:processed] += processed[:processed]
|
37
|
+
report[:processed] += processed[:processed]
|
41
38
|
else
|
42
|
-
@running_status
|
43
|
-
|
44
|
-
end
|
39
|
+
@running_status.cancel
|
40
|
+
pointer += 1
|
41
|
+
end
|
45
42
|
end
|
46
43
|
report
|
47
44
|
end
|
48
45
|
|
46
|
+
# If possible, convert the given fragment to a MIDI message
|
47
|
+
# @param [Array<String>] fragment A fragment of data eg ["9", "0", "4", "0", "5", "0"]
|
49
48
|
# @return [Hash, nil]
|
50
|
-
def nibbles_to_message
|
51
|
-
if
|
52
|
-
|
53
|
-
|
49
|
+
def nibbles_to_message(fragment)
|
50
|
+
if fragment.length >= 2
|
51
|
+
# convert the part of the fragment to start with to a numeric
|
52
|
+
slice = fragment.slice(0..1).map(&:hex)
|
53
|
+
compute_message(slice, fragment)
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
57
|
private
|
58
58
|
|
59
|
-
#
|
59
|
+
# Attempt to convert the given nibbles into a MIDI message
|
60
|
+
# @param [Array<Integer>] nibbles
|
60
61
|
# @return [Hash, nil]
|
61
|
-
def compute_message(nibbles)
|
62
|
+
def compute_message(nibbles, fragment)
|
62
63
|
case nibbles[0]
|
63
|
-
when 0x8 then lookahead(
|
64
|
-
when
|
65
|
-
when 0xA then lookahead(6) { |status_2, bytes| @message.polyphonic_aftertouch(status_2, bytes[1], bytes[2]) }
|
66
|
-
when 0xB then lookahead(6) { |status_2, bytes| @message.control_change(status_2, bytes[1], bytes[2]) }
|
67
|
-
when 0xC then lookahead(4) { |status_2, bytes| @message.program_change(status_2, bytes[1]) }
|
68
|
-
when 0xD then lookahead(4) { |status_2, bytes| @message.channel_aftertouch(status_2, bytes[1]) }
|
69
|
-
when 0xE then lookahead(6) { |status_2, bytes| @message.pitch_bend(status_2, bytes[1], bytes[2]) }
|
70
|
-
when 0xF then
|
64
|
+
when 0x8..0xE then lookahead(fragment, MessageBuilder.for_channel_message(@library, nibbles[0]))
|
65
|
+
when 0xF then
|
71
66
|
case nibbles[1]
|
72
|
-
when 0x0 then
|
73
|
-
|
74
|
-
when 0x8..0xF then lookahead(2) { |status_2, bytes| @message.system_realtime(status_2) }
|
67
|
+
when 0x0 then lookahead_for_sysex(fragment)
|
68
|
+
else lookahead(fragment, MessageBuilder.for_system_message(@library, nibbles[1]), :recursive => true)
|
75
69
|
end
|
76
70
|
else
|
77
|
-
|
71
|
+
lookahead_using_running_status(fragment) if @running_status.possible?
|
78
72
|
end
|
79
73
|
end
|
80
74
|
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
::Nibbler::Midilib
|
87
|
-
else
|
88
|
-
require "nibbler/midi-message"
|
89
|
-
::Nibbler::MIDIMessage
|
90
|
-
end
|
75
|
+
# Attempt to convert the fragment to a MIDI message using the given fragment and cached running status
|
76
|
+
# @param [Array<String>] fragment A fragment of data eg ["4", "0", "5", "0"]
|
77
|
+
# @return [Hash, nil]
|
78
|
+
def lookahead_using_running_status(fragment)
|
79
|
+
lookahead(fragment, @running_status[:message_builder], :offset => @running_status[:offset], :status_nibble_2 => @running_status[:status_nibble_2])
|
91
80
|
end
|
92
81
|
|
93
|
-
|
94
|
-
|
82
|
+
# Get the data in the buffer for the given pointer
|
83
|
+
# @param [Integer] pointer
|
84
|
+
# @return [Array<String>]
|
85
|
+
def get_fragment(pointer)
|
86
|
+
@buffer[pointer, (@buffer.length - pointer)]
|
95
87
|
end
|
96
88
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
89
|
+
# If the given fragment has at least the given number of nibbles, use it to build a hash that can be used
|
90
|
+
# to build a MIDI message
|
91
|
+
#
|
92
|
+
# @param [Integer] num_nibbles
|
93
|
+
# @param [Array<String>] fragment
|
94
|
+
# @param [Hash] options
|
95
|
+
# @option options [String] :status_nibble_2
|
96
|
+
# @option options [Boolean] :recursive
|
97
|
+
# @return [Hash, nil]
|
98
|
+
def lookahead(fragment, message_builder, options = {})
|
99
|
+
offset = options.fetch(:offset, 0)
|
100
|
+
num_nibbles = message_builder.num_nibbles + offset
|
101
|
+
if fragment.size >= num_nibbles
|
108
102
|
# if so shift those nibbles off of the array and call block with them
|
109
|
-
nibbles =
|
110
|
-
|
103
|
+
nibbles = fragment.slice!(0, num_nibbles)
|
104
|
+
status_nibble_2 ||= options[:status_nibble_2] || nibbles[1]
|
111
105
|
|
112
|
-
# send the nibbles to the block as bytes
|
113
|
-
# return the evaluated block and the remaining nibbles
|
106
|
+
# send the nibbles to the block as bytes
|
107
|
+
# return the evaluated block and the remaining nibbles
|
114
108
|
bytes = TypeConversion.hex_chars_to_numeric_bytes(nibbles)
|
109
|
+
bytes = bytes[1..-1] if options[:status_nibble_2].nil?
|
115
110
|
|
116
|
-
# record the
|
117
|
-
@running_status
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
}
|
111
|
+
# record the fragment situation in case running status comes up next round
|
112
|
+
@running_status.set(offset - 2, message_builder, status_nibble_2)
|
113
|
+
|
114
|
+
message_args = [status_nibble_2.hex]
|
115
|
+
message_args += bytes if num_nibbles > 2
|
122
116
|
|
117
|
+
message = message_builder.build(*message_args)
|
123
118
|
{
|
124
|
-
:message =>
|
119
|
+
:message => message,
|
125
120
|
:processed => nibbles
|
126
121
|
}
|
127
|
-
elsif
|
128
|
-
lookahead(
|
122
|
+
elsif num_nibbles > 0 && !!options[:recursive]
|
123
|
+
lookahead(fragment, message_builder, options.merge({ :offset => offset - 2 }))
|
129
124
|
end
|
130
125
|
end
|
131
126
|
|
132
|
-
def
|
133
|
-
@running_status
|
134
|
-
|
135
|
-
bytes = TypeConversion.hex_chars_to_numeric_bytes(@current)
|
127
|
+
def lookahead_for_sysex(fragment)
|
128
|
+
@running_status.cancel
|
129
|
+
bytes = TypeConversion.hex_chars_to_numeric_bytes(fragment)
|
136
130
|
unless (index = bytes.index(0xF7)).nil?
|
131
|
+
message_data = bytes.slice!(0, index + 1)
|
132
|
+
message = MessageBuilder.build_system_exclusive(@library, *message_data)
|
137
133
|
{
|
138
|
-
:message =>
|
139
|
-
:processed =>
|
134
|
+
:message => message,
|
135
|
+
:processed => fragment.slice!(0, (index + 1) * 2)
|
140
136
|
}
|
141
137
|
end
|
142
138
|
end
|
143
139
|
|
140
|
+
class RunningStatus
|
141
|
+
|
142
|
+
extend Forwardable
|
143
|
+
|
144
|
+
def_delegators :@state, :[]
|
145
|
+
|
146
|
+
def cancel
|
147
|
+
@state = nil
|
148
|
+
end
|
149
|
+
|
150
|
+
# Is there an active cached running status?
|
151
|
+
# @return [Boolean]
|
152
|
+
def possible?
|
153
|
+
!@state.nil?
|
154
|
+
end
|
155
|
+
|
156
|
+
def set(offset, message_builder, status_nibble_2)
|
157
|
+
@state = {
|
158
|
+
:message_builder => message_builder,
|
159
|
+
:offset => offset,
|
160
|
+
:status_nibble_2 => status_nibble_2
|
161
|
+
}
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
144
166
|
end
|
145
167
|
|
146
168
|
end
|