midi-nibbler 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +2 -2
- data/README.md +139 -0
- data/lib/nibbler.rb +13 -10
- data/lib/nibbler/hex_processor.rb +47 -0
- data/lib/nibbler/midi-message.rb +53 -0
- data/lib/nibbler/midilib.rb +67 -0
- data/lib/nibbler/parser.rb +86 -94
- data/lib/nibbler/session.rb +134 -0
- data/lib/nibbler/type_conversion.rb +17 -10
- data/test/helper.rb +3 -9
- data/test/{test_hex_char_array_filter.rb → hex_processor_test.rb} +18 -23
- data/test/midi_message_test.rb +126 -0
- data/test/midilib_test.rb +131 -0
- data/test/{test_parser_buffer.rb → parser_buffer_test.rb} +3 -8
- data/test/{test_parser_rejected.rb → parser_rejected_test.rb} +9 -14
- data/test/parser_test.rb +188 -0
- data/test/session_test.rb +135 -0
- data/test/{test_type_conversion.rb → type_conversion_test.rb} +6 -11
- metadata +60 -65
- data/README.rdoc +0 -115
- data/lib/nibbler/hex_char_array_filter.rb +0 -37
- data/lib/nibbler/midi-message_factory.rb +0 -54
- data/lib/nibbler/midilib_factory.rb +0 -66
- data/lib/nibbler/nibbler.rb +0 -89
- data/test/test_midi_message_factory.rb +0 -130
- data/test/test_midilib_factory.rb +0 -137
- data/test/test_nibbler.rb +0 -120
- data/test/test_parser.rb +0 -159
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e8413384db29c1e35d128afb0d982cfd866472c8
|
4
|
+
data.tar.gz: 1652fbc29119c0d0cb0f97f4e7c66167cf906253
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 66610505ed6b6111864ca1467d90ab72366bdb874ec3f401b56608e857ba2139b7fe3fe8144fee7bceafe5ef4c7eb6ce16e4f931399fccb998cc3682bfb58f52
|
7
|
+
data.tar.gz: 228e832b552cac66f79b46d2ea9cd6bf1edd67d7535e791e00ec4621a1a74180f6f365ee661268c87936379e06c370abd7adeccc2feefa54438b401eaf6b08a2
|
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright
|
1
|
+
Copyright 2011-2014 Ari Russo
|
2
2
|
|
3
3
|
Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
you may not use this file except in compliance with the License.
|
@@ -10,4 +10,4 @@ Unless required by applicable law or agreed to in writing, software
|
|
10
10
|
distributed under the License is distributed on an "AS IS" BASIS,
|
11
11
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
12
|
See the License for the specific language governing permissions and
|
13
|
-
limitations under the License.
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
# Nibbler
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
Parse MIDI Messages
|
6
|
+
|
7
|
+
## Install
|
8
|
+
|
9
|
+
`gem install midi-nibbler`
|
10
|
+
|
11
|
+
or using Bundler, add this to your Gemfile
|
12
|
+
|
13
|
+
`gem "midi-nibbler"`
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
require 'nibbler'
|
19
|
+
|
20
|
+
nibbler = Nibbler.new
|
21
|
+
```
|
22
|
+
|
23
|
+
Enter a message piece by piece
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
nibbler.parse("90")
|
27
|
+
=> nil
|
28
|
+
|
29
|
+
nibbler.parse("40")
|
30
|
+
=> nil
|
31
|
+
|
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,
|
40
|
+
@verbose_name="Note On: C3">
|
41
|
+
```
|
42
|
+
|
43
|
+
Enter a message all at once
|
44
|
+
|
45
|
+
```ruby
|
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,
|
55
|
+
@verbose_name="Note On: C3">
|
56
|
+
```
|
57
|
+
|
58
|
+
Use bytes
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
nibbler.parse(0x90, 0x40, 0x40)
|
62
|
+
=> #<MIDIMessage::NoteOn:0x98c9818 ...>
|
63
|
+
```
|
64
|
+
|
65
|
+
You can use nibbles in string format
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
nibbler.parse("9", "0", "4", "0", "4", "0")
|
69
|
+
=> #<MIDIMessage::NoteOn:0x98c9818 ...>
|
70
|
+
```
|
71
|
+
|
72
|
+
Interchange the different types
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
nibbler.parse("9", "0", 0x40, 64)
|
76
|
+
=> #<MIDIMessage::NoteOn:0x98c9818 ...>
|
77
|
+
```
|
78
|
+
|
79
|
+
Use running status
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
nibbler.parse(0x40, 64)
|
83
|
+
=> #<MIDIMessage::NoteOn:0x98c9818 ...>
|
84
|
+
```
|
85
|
+
|
86
|
+
Look at the messages we've parsed
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
nibbler.messages
|
90
|
+
=> [#<MIDIMessage::NoteOn:0x98c9804 ...>
|
91
|
+
#<MIDIMessage::NoteOn:0x98c9811 ...>]
|
92
|
+
```
|
93
|
+
|
94
|
+
Add an incomplete message
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
nibbler.parse("9")
|
98
|
+
nibbler.parse("40")
|
99
|
+
```
|
100
|
+
|
101
|
+
See progress
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
nibbler.buffer
|
105
|
+
=> ["9", "4", "0"]
|
106
|
+
|
107
|
+
nibbler.buffer_s
|
108
|
+
=> "940"
|
109
|
+
```
|
110
|
+
|
111
|
+
Pass in a timestamp
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
nibbler.parse("904040", :timestamp => Time.now.to_i)
|
115
|
+
=> { :messages=> #<MIDIMessage::NoteOn:0x92f4564 ..>, :timestamp=>1304488440 }
|
116
|
+
```
|
117
|
+
|
118
|
+
Nibbler defaults to generate [midi-message](http://github.com/arirusso/midi-message) objects, but it is also possible to use [midilib](https://github.com/jimm/midilib)
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
Nibbler.new(:message_lib => :midilib)
|
122
|
+
|
123
|
+
nibbler.parse("9", "0", 0x40, "40")
|
124
|
+
=> "0: ch 00 on 40 40"
|
125
|
+
```
|
126
|
+
|
127
|
+
## Also see
|
128
|
+
|
129
|
+
* [midi-eye](http://github.com/arirusso/midi-eye), a MIDI event listener based on nibbler
|
130
|
+
|
131
|
+
## Author
|
132
|
+
|
133
|
+
* [Ari Russo](http://github.com/arirusso) <ari.russo at gmail.com>
|
134
|
+
|
135
|
+
## License
|
136
|
+
|
137
|
+
Apache 2.0, See the file LICENSE
|
138
|
+
|
139
|
+
Copyright (c) 2011-2014 Ari Russo
|
data/lib/nibbler.rb
CHANGED
@@ -1,26 +1,29 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
1
|
#
|
3
2
|
# Parse MIDI Messages
|
4
|
-
# (c)2011 Ari Russo and licensed under the Apache 2.0 License
|
3
|
+
# (c)2011-2014 Ari Russo and licensed under the Apache 2.0 License
|
5
4
|
#
|
6
5
|
|
7
|
-
|
6
|
+
# libs
|
7
|
+
require "forwardable"
|
8
8
|
|
9
|
-
|
10
|
-
require
|
11
|
-
require
|
12
|
-
|
9
|
+
# modules
|
10
|
+
require "nibbler/hex_processor"
|
11
|
+
require "nibbler/type_conversion"
|
12
|
+
|
13
|
+
# classes
|
14
|
+
require "nibbler/parser"
|
15
|
+
require "nibbler/session"
|
13
16
|
|
14
17
|
#
|
15
18
|
# Parse MIDI Messages
|
16
19
|
#
|
17
20
|
module Nibbler
|
18
21
|
|
19
|
-
VERSION = "0.
|
22
|
+
VERSION = "0.2.1"
|
20
23
|
|
21
|
-
#
|
24
|
+
# Shortcut to a new parser session
|
22
25
|
def self.new(*a, &block)
|
23
|
-
|
26
|
+
Session.new(*a, &block)
|
24
27
|
end
|
25
28
|
|
26
29
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Nibbler
|
2
|
+
|
3
|
+
# Accepts various types of input and returns an array of hex digit chars
|
4
|
+
module HexProcessor
|
5
|
+
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# Accepts various types of input and returns an array of hex digit chars
|
9
|
+
# Invalid input is disregarded
|
10
|
+
#
|
11
|
+
# @param [*String, *Fixnum] args
|
12
|
+
# @return [Array<String>] An array of hex string nibbles eg "6", "a"
|
13
|
+
def process(*args)
|
14
|
+
args.map { |arg| convert(arg) }.flatten.compact.map(&:upcase)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Convert a single value to hex chars
|
20
|
+
# @param [Array<Fixnum>, Array<String>, Fixnum, String] value
|
21
|
+
# @return [Array<String>]
|
22
|
+
def convert(value)
|
23
|
+
case value
|
24
|
+
when Array then value.map { |arr| process(*arr) }.reduce(:+)
|
25
|
+
when String then TypeConversion.hex_str_to_hex_chars(filter_string(value))
|
26
|
+
when Fixnum then TypeConversion.numeric_byte_to_hex_chars(filter_numeric(value))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Limit the given number to bytes usable in MIDI ie values (0..240)
|
31
|
+
# returns nil if the byte is outside of that range
|
32
|
+
# @param [Fixnum] num
|
33
|
+
# @return [Fixnum, nil]
|
34
|
+
def filter_numeric(num)
|
35
|
+
num if (0x00..0xFF).include?(num)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Only return valid hex string characters
|
39
|
+
# @param [String] string
|
40
|
+
# @return [String]
|
41
|
+
def filter_string(string)
|
42
|
+
string.gsub(/[^0-9a-fA-F]/, "").upcase
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "midi-message"
|
2
|
+
|
3
|
+
module Nibbler
|
4
|
+
|
5
|
+
# Construct messages with MIDIMessage in a generic way
|
6
|
+
# http://github.com/arirusso/midi-message
|
7
|
+
module MIDIMessage
|
8
|
+
|
9
|
+
extend self
|
10
|
+
|
11
|
+
def note_off(second_nibble, data_byte_1, data_byte_2)
|
12
|
+
::MIDIMessage::NoteOff.new(second_nibble, data_byte_1, data_byte_2)
|
13
|
+
end
|
14
|
+
|
15
|
+
def note_on(second_nibble, data_byte_1, data_byte_2)
|
16
|
+
::MIDIMessage::NoteOn.new(second_nibble, data_byte_1, data_byte_2)
|
17
|
+
end
|
18
|
+
|
19
|
+
def polyphonic_aftertouch(second_nibble, data_byte_1, data_byte_2)
|
20
|
+
::MIDIMessage::PolyphonicAftertouch.new(second_nibble, data_byte_1, data_byte_2)
|
21
|
+
end
|
22
|
+
|
23
|
+
def control_change(second_nibble, data_byte_1, data_byte_2)
|
24
|
+
::MIDIMessage::ControlChange.new(second_nibble, data_byte_1, data_byte_2)
|
25
|
+
end
|
26
|
+
|
27
|
+
def program_change(second_nibble, data_byte)
|
28
|
+
::MIDIMessage::ProgramChange.new(second_nibble, data_byte)
|
29
|
+
end
|
30
|
+
|
31
|
+
def channel_aftertouch(second_nibble, data_byte)
|
32
|
+
::MIDIMessage::ChannelAftertouch.new(second_nibble, data_byte)
|
33
|
+
end
|
34
|
+
|
35
|
+
def pitch_bend(second_nibble, data_byte_1, data_byte_2)
|
36
|
+
::MIDIMessage::PitchBend.new(second_nibble, data_byte_1, data_byte_2)
|
37
|
+
end
|
38
|
+
|
39
|
+
def system_exclusive(*a)
|
40
|
+
::MIDIMessage::SystemExclusive.new(*a)
|
41
|
+
end
|
42
|
+
|
43
|
+
def system_common(second_nibble, data_byte_1 = nil, data_byte_2 = nil)
|
44
|
+
::MIDIMessage::SystemCommon.new(second_nibble, data_byte_1, data_byte_2)
|
45
|
+
end
|
46
|
+
|
47
|
+
def system_realtime(second_nibble)
|
48
|
+
::MIDIMessage::SystemRealtime.new(second_nibble)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "midilib"
|
2
|
+
|
3
|
+
module Nibbler
|
4
|
+
|
5
|
+
# Construct messages with midilib in a generic way
|
6
|
+
# https://github.com/jimm/midilib
|
7
|
+
# midilib is copyright © 2003-2010 Jim Menard
|
8
|
+
module Midilib
|
9
|
+
|
10
|
+
extend self
|
11
|
+
|
12
|
+
def note_off(second_nibble, data_byte_1, data_byte_2)
|
13
|
+
MIDI::NoteOff.new(second_nibble, data_byte_1, data_byte_2)
|
14
|
+
end
|
15
|
+
|
16
|
+
def note_on(second_nibble, data_byte_1, data_byte_2)
|
17
|
+
MIDI::NoteOn.new(second_nibble, data_byte_1, data_byte_2)
|
18
|
+
end
|
19
|
+
|
20
|
+
def polyphonic_aftertouch(second_nibble, data_byte_1, data_byte_2)
|
21
|
+
MIDI::PolyPressure.new(second_nibble, data_byte_1, data_byte_2)
|
22
|
+
end
|
23
|
+
|
24
|
+
def control_change(second_nibble, data_byte_1, data_byte_2)
|
25
|
+
MIDI::Controller.new(second_nibble, data_byte_1, data_byte_2)
|
26
|
+
end
|
27
|
+
|
28
|
+
def program_change(second_nibble, data_byte)
|
29
|
+
MIDI::ProgramChange.new(second_nibble, data_byte)
|
30
|
+
end
|
31
|
+
|
32
|
+
def channel_aftertouch(second_nibble, data_byte)
|
33
|
+
MIDI::ChannelPressure.new(second_nibble, data_byte)
|
34
|
+
end
|
35
|
+
|
36
|
+
def pitch_bend(second_nibble, data_byte_1, data_byte_2)
|
37
|
+
# to-do handle the midilib lsb/msb
|
38
|
+
# right now the second data byte is being thrown away
|
39
|
+
MIDI:: PitchBend.new(second_nibble, data_byte_1, data_byte_2)
|
40
|
+
end
|
41
|
+
|
42
|
+
def system_exclusive(*a)
|
43
|
+
MIDI::SystemExclusive.new(a)
|
44
|
+
end
|
45
|
+
|
46
|
+
def system_common(second_nibble, data_byte_1 = nil, data_byte_2 = nil)
|
47
|
+
case second_nibble
|
48
|
+
when 0x2 then MIDI::SongPointer.new(data_byte_1) # similar issue to pitch bend here
|
49
|
+
when 0x3 then MIDI::SongSelect.new(data_byte_1)
|
50
|
+
when 0x6 then MIDI::TuneRequest.new
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def system_realtime(second_nibble)
|
55
|
+
case second_nibble
|
56
|
+
when 0x8 then MIDI::Clock.new
|
57
|
+
when 0xA then MIDI::Start.new
|
58
|
+
when 0xB then MIDI::Continue.new
|
59
|
+
when 0xC then MIDI::Stop.new
|
60
|
+
when 0xE then MIDI::ActiveSense.new
|
61
|
+
when 0xF then MIDI::SystemReset.new
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/lib/nibbler/parser.rb
CHANGED
@@ -1,32 +1,23 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
#
|
3
1
|
module Nibbler
|
4
|
-
|
5
2
|
|
6
|
-
# this is where messages go
|
7
3
|
class Parser
|
8
|
-
|
4
|
+
|
9
5
|
attr_reader :buffer
|
10
|
-
|
6
|
+
|
7
|
+
# @param [Hash] options
|
8
|
+
# @option options [Symbol] :message_lib
|
11
9
|
def initialize(options = {})
|
12
10
|
@running_status = nil
|
13
11
|
@buffer = []
|
14
12
|
@iterator = 0
|
15
|
-
|
16
|
-
|
17
|
-
when :midilib then
|
18
|
-
require 'midilib'
|
19
|
-
require 'nibbler/midilib_factory'
|
20
|
-
@message_factory = MidilibFactory.new
|
21
|
-
else
|
22
|
-
require 'midi-message'
|
23
|
-
require 'nibbler/midi-message_factory'
|
24
|
-
@message_factory = MIDIMessageFactory.new
|
25
|
-
end
|
13
|
+
|
14
|
+
initialize_message_library(options[:message_lib])
|
26
15
|
end
|
27
16
|
|
17
|
+
# @param [Array<String, Fixnum>] nibbles
|
18
|
+
# @return [Hash]
|
28
19
|
def process(nibbles)
|
29
|
-
|
20
|
+
report = {
|
30
21
|
:messages => [],
|
31
22
|
:processed => [],
|
32
23
|
:rejected => []
|
@@ -37,118 +28,119 @@ module Nibbler
|
|
37
28
|
# iterate through nibbles until a status message is found
|
38
29
|
# see if there really is a message there
|
39
30
|
populate_current
|
40
|
-
# current is the current piece of the buffer we
|
41
|
-
processed = nibbles_to_message
|
42
|
-
|
43
|
-
|
44
|
-
output[:rejected] += @buffer.slice(0, @iterator)
|
31
|
+
# current is the current piece of the buffer we"re dealing with
|
32
|
+
unless (processed = nibbles_to_message).nil?
|
33
|
+
# if it"s a real message, reject previous nibbles
|
34
|
+
report[:rejected] += @buffer.slice(0, @iterator)
|
45
35
|
# and record it
|
46
36
|
@buffer = @current # current now has the remaining nibbles for next pass
|
47
37
|
@current = nil # reset current
|
48
38
|
@iterator = 0 # reset iterator
|
49
|
-
|
50
|
-
|
39
|
+
report[:messages] << processed[:message]
|
40
|
+
report[:processed] += processed[:processed]
|
51
41
|
else
|
52
42
|
@running_status = nil
|
53
43
|
@iterator += 1
|
54
44
|
end
|
55
45
|
end
|
56
|
-
|
46
|
+
report
|
57
47
|
end
|
58
|
-
|
48
|
+
|
49
|
+
# @return [Hash, nil]
|
59
50
|
def nibbles_to_message
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
:remaining => nil
|
64
|
-
}
|
65
|
-
return output if @current.length < 2
|
66
|
-
first = @current[0].hex
|
67
|
-
second = @current[1].hex
|
68
|
-
|
69
|
-
output[:message], output[:processed] = *case first
|
70
|
-
when 0x8 then lookahead(6) { |status_2, bytes| @message_factory.note_off(status_2, bytes[1], bytes[2]) }
|
71
|
-
when 0x9 then lookahead(6) { |status_2, bytes| @message_factory.note_on(status_2, bytes[1], bytes[2]) }
|
72
|
-
when 0xA then lookahead(6) { |status_2, bytes| @message_factory.polyphonic_aftertouch(status_2, bytes[1], bytes[2]) }
|
73
|
-
when 0xB then lookahead(6) { |status_2, bytes| @message_factory.control_change(status_2, bytes[1], bytes[2]) }
|
74
|
-
when 0xC then lookahead(4) { |status_2, bytes| @message_factory.program_change(status_2, bytes[1]) }
|
75
|
-
when 0xD then lookahead(4) { |status_2, bytes| @message_factory.channel_aftertouch(status_2, bytes[1]) }
|
76
|
-
when 0xE then lookahead(6) { |status_2, bytes| @message_factory.pitch_bend(status_2, bytes[1], bytes[2]) }
|
77
|
-
when 0xF then case second
|
78
|
-
when 0x0 then lookahead_sysex { |bytes| @message_factory.system_exclusive(*bytes) }
|
79
|
-
when 0x1..0x6 then lookahead(6, :recursive => true) { |status_2, bytes| @message_factory.system_common(status_2, bytes[1], bytes[2]) }
|
80
|
-
when 0x8..0xF then lookahead(2) { |status_2, bytes| @message_factory.system_realtime(status_2) }
|
81
|
-
end
|
82
|
-
else
|
83
|
-
use_running_status if running_status_possible?
|
51
|
+
if @current.length >= 2
|
52
|
+
nibbles = @current.slice(0..1).map(&:hex)
|
53
|
+
compute_message(nibbles)
|
84
54
|
end
|
85
|
-
output
|
86
55
|
end
|
87
|
-
|
56
|
+
|
88
57
|
private
|
89
|
-
|
90
|
-
|
58
|
+
|
59
|
+
# @param [Array<Fixnum>] nibbles
|
60
|
+
# @return [Hash, nil]
|
61
|
+
def compute_message(nibbles)
|
62
|
+
case nibbles[0]
|
63
|
+
when 0x8 then lookahead(6) { |status_2, bytes| @message.note_off(status_2, bytes[1], bytes[2]) }
|
64
|
+
when 0x9 then lookahead(6) { |status_2, bytes| @message.note_on(status_2, bytes[1], bytes[2]) }
|
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
|
71
|
+
case nibbles[1]
|
72
|
+
when 0x0 then lookahead_sysex { |bytes| @message.system_exclusive(*bytes) }
|
73
|
+
when 0x1..0x6 then lookahead(6, :recursive => true) { |status_2, bytes| @message.system_common(status_2, bytes[1], bytes[2]) }
|
74
|
+
when 0x8..0xF then lookahead(2) { |status_2, bytes| @message.system_realtime(status_2) }
|
75
|
+
end
|
76
|
+
else
|
77
|
+
use_running_status if possible_running_status?
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Choose a MIDI message object library
|
82
|
+
def initialize_message_library(lib)
|
83
|
+
@message = case lib
|
84
|
+
when :midilib then
|
85
|
+
require "nibbler/midilib"
|
86
|
+
::Nibbler::Midilib
|
87
|
+
else
|
88
|
+
require "nibbler/midi-message"
|
89
|
+
::Nibbler::MIDIMessage
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def possible_running_status?
|
91
94
|
!@running_status.nil?
|
92
95
|
end
|
93
|
-
|
96
|
+
|
94
97
|
def use_running_status
|
95
|
-
lookahead(@running_status[:num], :status_nibble => @running_status[:status_nibble], &@running_status[:
|
98
|
+
lookahead(@running_status[:num], :status_nibble => @running_status[:status_nibble], &@running_status[:callback])
|
96
99
|
end
|
97
|
-
|
100
|
+
|
98
101
|
def populate_current
|
99
102
|
@current = (@buffer[@iterator, (@buffer.length - @iterator)])
|
100
103
|
end
|
101
|
-
|
102
|
-
def lookahead(num, options = {}, &
|
103
|
-
recursive = !options[:recursive].nil? && options[:recursive]
|
104
|
-
status_nibble = options[:status_nibble]
|
105
|
-
processed = []
|
106
|
-
msg = nil
|
104
|
+
|
105
|
+
def lookahead(num, options = {}, &callback)
|
107
106
|
# do we have enough nibbles for num bytes?
|
108
|
-
if @current.
|
107
|
+
if @current.size >= num
|
108
|
+
# if so shift those nibbles off of the array and call block with them
|
109
|
+
nibbles = @current.slice!(0, num)
|
110
|
+
status_nibble ||= options[:status_nibble] || nibbles[1]
|
109
111
|
|
110
|
-
# if so shift those nibbles off of the array and call block with them
|
111
|
-
processed += @current.slice!(0, num)
|
112
|
-
status_nibble ||= processed[1]
|
113
112
|
# send the nibbles to the block as bytes
|
114
113
|
# return the evaluated block and the remaining nibbles
|
115
|
-
bytes = TypeConversion.hex_chars_to_numeric_bytes(
|
114
|
+
bytes = TypeConversion.hex_chars_to_numeric_bytes(nibbles)
|
115
|
+
|
116
116
|
# record the current situation in case running status comes up next round
|
117
117
|
@running_status = {
|
118
|
-
:
|
118
|
+
:callback => callback,
|
119
119
|
:num => num - 2,
|
120
120
|
:status_nibble => status_nibble
|
121
121
|
}
|
122
|
-
|
123
|
-
|
124
|
-
|
122
|
+
|
123
|
+
{
|
124
|
+
:message => yield(status_nibble.hex, bytes),
|
125
|
+
:processed => nibbles
|
126
|
+
}
|
127
|
+
elsif num > 0 && !!options[:recursive]
|
128
|
+
lookahead(num - 2, options, &callback)
|
125
129
|
end
|
126
|
-
[msg, processed]
|
127
130
|
end
|
128
|
-
|
131
|
+
|
129
132
|
def lookahead_sysex(&block)
|
130
|
-
processed = []
|
131
|
-
msg = nil
|
132
133
|
@running_status = nil
|
133
|
-
|
134
|
+
|
134
135
|
bytes = TypeConversion.hex_chars_to_numeric_bytes(@current)
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
136
|
+
unless (index = bytes.index(0xF7)).nil?
|
137
|
+
{
|
138
|
+
:message => yield(bytes.slice!(0, index + 1)),
|
139
|
+
:processed => @current.slice!(0, (index + 1) * 2)
|
140
|
+
}
|
139
141
|
end
|
140
|
-
[msg, processed]
|
141
142
|
end
|
142
|
-
|
143
|
-
# for testing
|
144
|
-
def buffer=(val)
|
145
|
-
@buffer=val
|
146
|
-
end
|
147
|
-
|
148
|
-
def current
|
149
|
-
@current
|
150
|
-
end
|
151
|
-
|
143
|
+
|
152
144
|
end
|
153
145
|
|
154
|
-
end
|
146
|
+
end
|