midi-nibbler 0.1.1 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![nibbler](http://img17.imageshack.us/img17/1713/dogwithsynth.jpg)
|
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
|