midi-message 0.4.1 → 0.4.2
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 +4 -4
- data/lib/midi-message.rb +1 -1
- data/lib/midi-message/channel_message.rb +2 -2
- data/lib/midi-message/constant.rb +3 -3
- data/lib/midi-message/context.rb +0 -4
- data/lib/midi-message/message.rb +24 -16
- data/lib/midi-message/note_message.rb +1 -1
- data/lib/midi-message/parser.rb +40 -42
- data/lib/midi-message/short_message.rb +1 -1
- data/lib/midi-message/system_exclusive.rb +54 -18
- data/lib/midi-message/system_message.rb +1 -1
- data/lib/midi-message/type_conversion.rb +44 -12
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 539a68753cc6d0c17fed06cae7103d1f7b0f95a8
|
4
|
+
data.tar.gz: 2b3949261e5e5fa7061cb7860c325d6a0c067efb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: edc31730d5ffc918377fb342f63c3ce095b187e8f17ed194a47756a701286f3a78b7d7102f05850a2eeca59108d90ec3ed1a9cb5e0e78294cea699c6ede821b6
|
7
|
+
data.tar.gz: 86c8b7a355baa3a46d93ca63294a512538dc8478fbea9844be00c917916fe1d13fde7bd19eb599a2cf61fefff021df45f853a43622d6bb8380322f03136e6e30
|
data/lib/midi-message.rb
CHANGED
@@ -11,12 +11,12 @@ module MIDIMessage
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def find(name)
|
14
|
-
@constants.find { |const| const.key.to_s.downcase
|
14
|
+
@constants.find { |const| const.key.to_s.downcase == name.to_s.downcase }
|
15
15
|
end
|
16
16
|
alias_method :[], :find
|
17
17
|
|
18
18
|
def find_by_value(value)
|
19
|
-
@constants.find { |const| const.value.to_s.downcase
|
19
|
+
@constants.find { |const| const.value.to_s.downcase == value.to_s.downcase }
|
20
20
|
end
|
21
21
|
|
22
22
|
def self.all
|
@@ -26,7 +26,7 @@ module MIDIMessage
|
|
26
26
|
|
27
27
|
def self.[](key)
|
28
28
|
ensure_initialized
|
29
|
-
@groups.find { |g| g.key.to_s.downcase
|
29
|
+
@groups.find { |g| g.key.to_s.downcase == key.to_s.downcase }
|
30
30
|
end
|
31
31
|
|
32
32
|
private
|
data/lib/midi-message/context.rb
CHANGED
data/lib/midi-message/message.rb
CHANGED
@@ -109,13 +109,13 @@ module MIDIMessage
|
|
109
109
|
|
110
110
|
attr_reader :data
|
111
111
|
|
112
|
-
def initialize(*
|
113
|
-
options =
|
112
|
+
def initialize(*args)
|
113
|
+
options = args.last.kind_of?(Hash) ? args.pop : {}
|
114
114
|
@const = options[:const]
|
115
|
-
id = @const.nil? ?
|
115
|
+
id = @const.nil? ? args.shift : @const.value
|
116
116
|
id = strip_redundant_nibble(id)
|
117
117
|
initialize_short_message(0xF, id)
|
118
|
-
@data =
|
118
|
+
@data = args.slice(0..1)
|
119
119
|
end
|
120
120
|
|
121
121
|
end
|
@@ -129,10 +129,10 @@ module MIDIMessage
|
|
129
129
|
|
130
130
|
DISPLAY_NAME = "System Realtime"
|
131
131
|
|
132
|
-
def initialize(*
|
133
|
-
options =
|
132
|
+
def initialize(*args)
|
133
|
+
options = args.last.kind_of?(Hash) ? args.pop : {}
|
134
134
|
@const = options[:const]
|
135
|
-
id = @const.nil? ?
|
135
|
+
id = @const.nil? ? args.first : @const.value
|
136
136
|
id = strip_redundant_nibble(id)
|
137
137
|
initialize_short_message(0xF, id)
|
138
138
|
end
|
@@ -149,7 +149,7 @@ module MIDIMessage
|
|
149
149
|
# A command message is identified by having a status byte equal to 0x12
|
150
150
|
class Command
|
151
151
|
|
152
|
-
include SystemExclusive
|
152
|
+
include SystemExclusive
|
153
153
|
|
154
154
|
attr_accessor :data
|
155
155
|
alias_method :value, :data
|
@@ -158,7 +158,11 @@ module MIDIMessage
|
|
158
158
|
|
159
159
|
def initialize(address, data, options = {})
|
160
160
|
# store as a byte if it's a single byte
|
161
|
-
@data =
|
161
|
+
@data = if data.kind_of?(Array) && data.length == 1
|
162
|
+
data.first
|
163
|
+
else
|
164
|
+
data
|
165
|
+
end
|
162
166
|
initialize_sysex(address, options)
|
163
167
|
end
|
164
168
|
|
@@ -168,7 +172,7 @@ module MIDIMessage
|
|
168
172
|
# A request message is identified by having a status byte equal to 0x11
|
169
173
|
class Request
|
170
174
|
|
171
|
-
include SystemExclusive
|
175
|
+
include SystemExclusive
|
172
176
|
|
173
177
|
attr_reader :size
|
174
178
|
alias_method :value, :size
|
@@ -176,19 +180,23 @@ module MIDIMessage
|
|
176
180
|
TypeByte = 0x11
|
177
181
|
|
178
182
|
def initialize(address, size, options = {})
|
179
|
-
self.size =
|
183
|
+
self.size = if size.kind_of?(Array) && size.count == 1
|
184
|
+
size.first
|
185
|
+
else
|
186
|
+
size
|
187
|
+
end
|
180
188
|
initialize_sysex(address, options)
|
181
189
|
end
|
182
190
|
|
183
|
-
def size=(
|
191
|
+
def size=(value)
|
184
192
|
# accepts a Numeric or Array but
|
185
193
|
# must always store value as an array of three bytes
|
186
194
|
size = []
|
187
|
-
if
|
188
|
-
size =
|
189
|
-
elsif
|
195
|
+
if value.kind_of?(Array) && value.size <= 3
|
196
|
+
size = value
|
197
|
+
elsif value.kind_of?(Numeric) && (value + 1) / 247 <= 2
|
190
198
|
size = []
|
191
|
-
div, mod = *
|
199
|
+
div, mod = *value.divmod(247)
|
192
200
|
size << mod unless mod.zero?
|
193
201
|
div.times { size << 247 }
|
194
202
|
end
|
data/lib/midi-message/parser.rb
CHANGED
@@ -1,48 +1,46 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
#
|
3
|
-
|
4
1
|
module MIDIMessage
|
5
2
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
else nil
|
3
|
+
# Very simple parsing
|
4
|
+
# for more advanced parsing check out {nibbler}[http://github.com/arirusso/nibbler]
|
5
|
+
class Parser
|
6
|
+
|
7
|
+
# Can take either a hex string eg Parser.new("904040")
|
8
|
+
# or bytes eg Parser.new(0x90, 0x40, 0x40)
|
9
|
+
# or an array of bytes eg Parser.new([0x90, 0x40, 0x40])
|
10
|
+
def initialize(*args)
|
11
|
+
@data = case args.first
|
12
|
+
when Array then args.first
|
13
|
+
when Numeric then args
|
14
|
+
when String then TypeConversion.hex_string_to_numeric_byte_array(args.first)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Parse the data and return a message
|
19
|
+
def parse
|
20
|
+
first_nibble = ((@data.first & 0xF0) >> 4)
|
21
|
+
second_nibble = (@data.first & 0x0F)
|
22
|
+
case first_nibble
|
23
|
+
when 0x8 then NoteOff.new(second_nibble, @data[1], @data[2])
|
24
|
+
when 0x9 then NoteOn.new(second_nibble, @data[1], @data[2])
|
25
|
+
when 0xA then PolyphonicAftertouch.new(second_nibble, @data[1], @data[2])
|
26
|
+
when 0xB then ControlChange.new(second_nibble, @data[1], @data[2])
|
27
|
+
when 0xC then ProgramChange.new(second_nibble, @data[1])
|
28
|
+
when 0xD then ChannelAftertouch.new(second_nibble, @data[1])
|
29
|
+
when 0xE then PitchBend.new(second_nibble, @data[1], @data[2])
|
30
|
+
when 0xF then case second_nibble
|
31
|
+
when 0x0 then SystemExclusive.new(*@data)
|
32
|
+
when 0x1..0x6 then SystemCommon.new(second_nibble, @data[1], @data[2])
|
33
|
+
when 0x8..0xF then SystemRealtime.new(second_nibble)
|
34
|
+
else nil
|
39
35
|
end
|
36
|
+
else nil
|
40
37
|
end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.parse(*args)
|
43
|
+
Parser.new(*args).parse
|
44
|
+
end
|
47
45
|
|
48
46
|
end
|
@@ -3,6 +3,10 @@ module MIDIMessage
|
|
3
3
|
# MIDI System-Exclusive Messages (SysEx)
|
4
4
|
module SystemExclusive
|
5
5
|
|
6
|
+
def self.included(base)
|
7
|
+
base.send(:include, InstanceMethods)
|
8
|
+
end
|
9
|
+
|
6
10
|
# Common SysEx data that a message class will contain
|
7
11
|
module InstanceMethods
|
8
12
|
|
@@ -15,16 +19,17 @@ module MIDIMessage
|
|
15
19
|
# an array of message parts. multiple byte parts will be represented as an array of bytes
|
16
20
|
def to_a(options = {})
|
17
21
|
omit = options[:omit] || []
|
22
|
+
node = @node.to_a(options) unless @node.nil? || omit.include?(:node)
|
18
23
|
# this may need to be cached when properties are updated
|
19
24
|
# might be worth benchmarking
|
20
25
|
[
|
21
|
-
|
22
|
-
|
26
|
+
start_byte,
|
27
|
+
node,
|
23
28
|
(type_byte unless omit.include?(:type)),
|
24
29
|
[address].compact.flatten,
|
25
30
|
[value].compact.flatten,
|
26
31
|
(checksum unless omit.include?(:checksum)),
|
27
|
-
|
32
|
+
end_byte
|
28
33
|
].compact
|
29
34
|
end
|
30
35
|
|
@@ -39,7 +44,12 @@ module MIDIMessage
|
|
39
44
|
|
40
45
|
# string representation of the object's bytes
|
41
46
|
def to_hex_s
|
42
|
-
to_bytes.map
|
47
|
+
strings = to_bytes.map do |byte|
|
48
|
+
string = byte.to_s(16)
|
49
|
+
string = "0#{string}" if string.length == 1
|
50
|
+
string
|
51
|
+
end
|
52
|
+
strings.join.upcase
|
43
53
|
end
|
44
54
|
alias_method :to_bytestr, :to_hex_s
|
45
55
|
|
@@ -48,6 +58,14 @@ module MIDIMessage
|
|
48
58
|
end
|
49
59
|
alias_method :verbose_name, :name
|
50
60
|
|
61
|
+
def start_byte
|
62
|
+
self.class::StartByte
|
63
|
+
end
|
64
|
+
|
65
|
+
def end_byte
|
66
|
+
self.class::EndByte
|
67
|
+
end
|
68
|
+
|
51
69
|
def type_byte
|
52
70
|
self.class::TypeByte
|
53
71
|
end
|
@@ -55,8 +73,9 @@ module MIDIMessage
|
|
55
73
|
# alternate method from
|
56
74
|
# http://www.2writers.com/eddie/TutSysEx.htm
|
57
75
|
def checksum
|
58
|
-
sum = (address + [value].flatten).inject
|
59
|
-
|
76
|
+
sum = (address + [value].flatten).inject(&:+)
|
77
|
+
mod = sum.divmod(128)[1]
|
78
|
+
128 - mod
|
60
79
|
end
|
61
80
|
|
62
81
|
private
|
@@ -78,20 +97,25 @@ module MIDIMessage
|
|
78
97
|
attr_accessor :data
|
79
98
|
|
80
99
|
def initialize(data, options = {})
|
81
|
-
@data =
|
100
|
+
@data = if data.kind_of?(Array) && data.length == 1
|
101
|
+
data.first
|
102
|
+
else
|
103
|
+
data
|
104
|
+
end
|
82
105
|
initialize_sysex(nil, options)
|
83
106
|
end
|
84
107
|
|
85
108
|
# an array of message parts. multiple byte parts will be represented as an array of bytes
|
86
109
|
def to_a(options = {})
|
87
110
|
omit = options[:omit] || []
|
111
|
+
node = @node.to_a(options) unless @node.nil? || omit.include?(:node)
|
88
112
|
# this may need to be cached when properties are updated
|
89
113
|
# might be worth benchmarking
|
90
114
|
[
|
91
|
-
|
92
|
-
|
115
|
+
start_byte,
|
116
|
+
node,
|
93
117
|
@data,
|
94
|
-
|
118
|
+
end_byte
|
95
119
|
].compact
|
96
120
|
end
|
97
121
|
|
@@ -109,16 +133,17 @@ module MIDIMessage
|
|
109
133
|
def initialize(manufacturer, options = {})
|
110
134
|
@device_id = options[:device_id]
|
111
135
|
@model_id = options[:model_id]
|
112
|
-
@manufacturer_id =
|
136
|
+
@manufacturer_id = get_manufacturer_id(manufacturer)
|
113
137
|
end
|
114
138
|
|
115
139
|
def to_a(options = {})
|
116
|
-
omit = options[:omit] || []
|
117
|
-
[
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
140
|
+
omit = options[:omit] || []
|
141
|
+
properties = [:manufacturer, :device, :model].map do |property|
|
142
|
+
unless omit.include?(property) || omit.include?("#{property.to_s}_id")
|
143
|
+
instance_variable_get("@#{property.to_s}_id")
|
144
|
+
end
|
145
|
+
end
|
146
|
+
properties.compact
|
122
147
|
end
|
123
148
|
|
124
149
|
# this message takes a prototype message, copies it, and returns the copy with its node set
|
@@ -143,6 +168,17 @@ module MIDIMessage
|
|
143
168
|
request
|
144
169
|
end
|
145
170
|
|
171
|
+
private
|
172
|
+
|
173
|
+
def get_manufacturer_id(manufacturer)
|
174
|
+
if manufacturer.kind_of?(Numeric)
|
175
|
+
manufacturer
|
176
|
+
else
|
177
|
+
const = Constant.find("Manufacturer", manufacturer)
|
178
|
+
const.value
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
146
182
|
end
|
147
183
|
|
148
184
|
# Convert raw MIDI data to SysEx message objects
|
@@ -151,7 +187,7 @@ module MIDIMessage
|
|
151
187
|
start_status = bytes.shift
|
152
188
|
end_status = bytes.pop
|
153
189
|
|
154
|
-
if start_status
|
190
|
+
if start_status == 0xF0 && end_status == 0xF7
|
155
191
|
|
156
192
|
type_byte = bytes[3]
|
157
193
|
|
@@ -5,8 +5,13 @@ module MIDIMessage
|
|
5
5
|
|
6
6
|
extend self
|
7
7
|
|
8
|
+
# Convert an array of hex nibbles to an array of numeric bytes
|
9
|
+
# eg ["9", "0", "4", "0"] to [0x90, 0x40]
|
10
|
+
#
|
11
|
+
# @param [Array<String>] An array of hex nibbles eg ["9", "0", "4", "0"]
|
12
|
+
# @return [Array<Fixnum] An array of numeric bytes eg [0x90, 0x40]
|
8
13
|
def hex_chars_to_numeric_byte_array(nibbles)
|
9
|
-
nibbles = nibbles.dup
|
14
|
+
nibbles = nibbles.dup # Don't mess with the input
|
10
15
|
# get rid of last nibble if there's an odd number
|
11
16
|
# it will be processed later anyway
|
12
17
|
nibbles.slice!(nibbles.length-2, 1) if nibbles.length.odd?
|
@@ -18,30 +23,57 @@ module MIDIMessage
|
|
18
23
|
bytes
|
19
24
|
end
|
20
25
|
|
21
|
-
# Convert byte
|
22
|
-
|
23
|
-
|
26
|
+
# Convert byte string to an array of numeric bytes
|
27
|
+
# eg. "904040" to [0x90, 0x40, 0x40]
|
28
|
+
# @param [String] string A string representing hex digits eg "904040"
|
29
|
+
# @return [Array<Fixnum>] An array of numeric bytes eg [0x90, 0x40, 0x40]
|
30
|
+
def hex_string_to_numeric_byte_array(string)
|
31
|
+
string = string.dup
|
24
32
|
bytes = []
|
25
|
-
until
|
26
|
-
bytes <<
|
33
|
+
until string.length == 0
|
34
|
+
bytes << string.slice!(0, 2).hex
|
27
35
|
end
|
28
36
|
bytes
|
29
37
|
end
|
30
38
|
|
31
|
-
#
|
32
|
-
|
33
|
-
|
39
|
+
# Convert a string of hex digits to an array of nibbles
|
40
|
+
# eg. "904040" to ["9", "0", "4", "0", "4", "0"]
|
41
|
+
# @param [String] string A string representing hex digits eg "904040"
|
42
|
+
# @return [Array<String>] An array of hex nibble chars eg ["9", "0", "4", "0", "4", "0"]
|
43
|
+
def hex_str_to_hex_chars(string)
|
44
|
+
string.split(//)
|
34
45
|
end
|
35
46
|
|
47
|
+
# Convert an array of numeric bytes to a string of hex digits
|
48
|
+
# eg. [0x90, 0x40, 0x40] to "904040"
|
49
|
+
# @param [Array<Fixnum>] bytes An array of numeric bytes eg [0x90, 0x40, 0x40]
|
50
|
+
# @return [String] A string representing hex digits eg "904040"
|
36
51
|
def numeric_byte_array_to_hex_string(bytes)
|
37
|
-
bytes.map
|
52
|
+
string_bytes = bytes.map do |byte|
|
53
|
+
string = byte.to_s(16)
|
54
|
+
string = "0#{string}" if string.length == 1
|
55
|
+
string
|
56
|
+
end
|
57
|
+
string_bytes.join.upcase
|
38
58
|
end
|
39
59
|
|
60
|
+
# Convert a numeric byte to hex chars
|
61
|
+
# eg 0x90 to ["9", "0"]
|
62
|
+
# @param [Fixnum] num A numeric byte eg 0x90
|
63
|
+
# @return [Array<String>] An array of hex nibble chars eg ["9", "0"]
|
40
64
|
def numeric_byte_to_hex_chars(num)
|
41
|
-
|
65
|
+
nibbles = numeric_byte_to_nibbles(num)
|
66
|
+
nibbles.map { |n| n.to_s(16) }
|
67
|
+
end
|
68
|
+
|
69
|
+
# Convert a numeric byte to nibbles
|
70
|
+
# eg 0x90 to [0x9, 0x0]
|
71
|
+
# @param [Fixnum] num A numeric byte eg 0x90
|
72
|
+
# @return [Array<Fixnum>] An array of nibbles eg [0x9, 0x0]
|
73
|
+
def numeric_byte_to_nibbles(num)
|
74
|
+
[((num & 0xF0) >> 4), (num & 0x0F)]
|
42
75
|
end
|
43
76
|
|
44
|
-
|
45
77
|
end
|
46
78
|
|
47
79
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: midi-message
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ari Russo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Objects and classes for dealing with MIDI messages.
|
14
14
|
email:
|