midi-message 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|