midi-message 0.4.8 → 0.4.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a74063f4fe5e1a622a69fc4c775763f20046defb
4
- data.tar.gz: 19638072ab7f93b748d5a435263947ece626f9ea
3
+ metadata.gz: 96815929056b3d199312f9d0ea055edcdade2313
4
+ data.tar.gz: bd58cc3e59de12731d9541dcf4cfb00ef81b4ef7
5
5
  SHA512:
6
- metadata.gz: f85ede4091a84b04f9f292298205cdd7e8fa7e12c24f27e078de4076ede187f94aad464cec41640dfb7aeaf45b2d010a753a4248441143b1157ea93ff5be0a37
7
- data.tar.gz: 5f031bf9d385a268d2c3355f787918bf8f1ebdc65321df5a25b7dfc67bc8d5a000f23851e899310f4ee644c9c8f5d161915ffaf74a4845bd87ad0d3630cc4787
6
+ metadata.gz: 61721e744f4c482f7a7318866ef2e61fb23d7d7b7bf21311070b67eb7d37a72a7d4f788b5b1dd87d24877b098bed676506871019fb795382803bfb8e5f872da1
7
+ data.tar.gz: 467155c933d12d8d7de50b65e050013685eb28ab4b663187fa3eb23a88b35b2eb86c0090ef843505b7d57832b3f92bad4143f2bbd6a9516ca5d6f6d02e02ff69
@@ -25,6 +25,6 @@ require "midi-message/parser"
25
25
 
26
26
  module MIDIMessage
27
27
 
28
- VERSION = "0.4.8"
28
+ VERSION = "0.4.9"
29
29
 
30
30
  end
@@ -10,13 +10,13 @@ module MIDIMessage
10
10
  # Shortcut to RawChannelMessage.new
11
11
  # aka build a ChannelMessage from raw nibbles and bytes
12
12
  # eg ChannelMessage.new(0x9, 0x0, 0x40, 0x40)
13
- # @param [*Array<Fixnum>] data The status nibbles and data bytes
13
+ # @param [*Fixnum] data The status nibbles and data bytes
14
14
  # @return [RawChannelMessage] The resulting RawChannelMessage object
15
15
  def self.new(*data, &block)
16
16
  Message.new(*data, &block)
17
17
  end
18
18
 
19
- # @param [*Array<Fixnum>] data The status nibbles and data bytes
19
+ # @param [*Fixnum] data The status nibbles and data bytes
20
20
  def initialize(*data)
21
21
  data = data.dup
22
22
  options = data.last.kind_of?(Hash) ? data.pop : {}
@@ -24,13 +24,6 @@ module MIDIMessage
24
24
  initialize_channel_message(self.class.type_for_status, *data)
25
25
  end
26
26
 
27
- # Decorates the object with the particular properties for its type
28
- # @return [Boolean]
29
- def initialize_properties
30
- properties = self.class.properties
31
- add_properties(properties) unless properties.nil?
32
- end
33
-
34
27
  private
35
28
 
36
29
  def self.included(base)
@@ -38,62 +31,92 @@ module MIDIMessage
38
31
  base.send(:extend, ClassMethods)
39
32
  end
40
33
 
34
+ # Add the given constant to message data
41
35
  def add_constant_value(constant, data)
42
36
  index = Constant::Loader.get_index(self)
43
37
  data.insert(index, constant.value)
44
38
  end
45
39
 
46
- # @param [Array<Symbol>] properties
47
- # @return [Boolean]
48
- def add_properties(properties)
49
- has_properties = false
50
- schema = [
40
+ # Assign the message data
41
+ def assign_data(status_nibble_1, status_nibble_2, data_byte_1, data_byte_2 = 0)
42
+ @status = [status_nibble_1, status_nibble_2]
43
+ @data = [data_byte_1]
44
+ @data[1] = data_byte_2 if self.class.second_data_byte?
45
+ end
46
+
47
+ # Initialize the message: assign data, decorate with accessors
48
+ def initialize_channel_message(status_nibble_1, status_nibble_2, data_byte_1, data_byte_2 = 0)
49
+ assign_data(status_nibble_1, status_nibble_2, data_byte_1, data_byte_2)
50
+ Accessors.initialize(self) unless self.class.properties.nil?
51
+ initialize_message(status_nibble_1, status_nibble_2)
52
+ end
53
+
54
+ class Accessors
55
+
56
+ SCHEMA = [
51
57
  { :name => :status, :index => 1 }, # second status nibble
52
58
  { :name => :data, :index => 0 }, # first data byte
53
59
  { :name => :data, :index => 1 } # second data byte
54
- ]
55
- properties.each_with_index do |property, i|
56
- property_schema = schema[i]
57
- define_getter(property, property_schema)
58
- define_setter(property, property_schema)
59
- has_properties = true
60
+ ].freeze
61
+
62
+ # @param [Class] klass
63
+ # @return [Class]
64
+ def self.decorate(klass)
65
+ decorator = new(klass)
66
+ decorator.decorate
60
67
  end
61
- has_properties
62
- end
63
68
 
64
- # @param [Symbol, String] property
65
- # @param [Hash] container
66
- # @param [Fixnum] index
67
- # @return [Boolean]
68
- def define_getter(property, property_schema)
69
- container = send(property_schema[:name])
70
- index = property_schema[:index]
71
- self.class.send(:attr_reader, property)
72
- instance_variable_set("@#{property.to_s}", container[index])
73
- true
74
- end
69
+ # Initialize a message object with it's properties
70
+ # @param [MIDIMessage] message
71
+ # @return [Boolean]
72
+ def self.initialize(message)
73
+ message.class.properties.each_with_index do |property, i|
74
+ data_mapping = SCHEMA[i]
75
+ container = message.send(data_mapping[:name])
76
+ index = data_mapping[:index]
77
+ message.send(:instance_variable_set, "@#{property.to_s}", container[index])
78
+ end
79
+ true
80
+ end
75
81
 
76
- # @param [Symbol, String] property
77
- # @param [Hash] container
78
- # @param [Fixnum] index
79
- # @return [Boolean]
80
- def define_setter(property, property_schema)
81
- index = property_schema[:index]
82
- self.class.send(:define_method, "#{property.to_s}=") do |value|
83
- send(:instance_variable_set, "@#{property.to_s}", value)
84
- send(property_schema[:name])[index] = value
85
- update
86
- return self
82
+ # @param [Class] klass
83
+ def initialize(klass)
84
+ @klass = klass
85
+ end
86
+
87
+ # @return [Class]
88
+ def decorate
89
+ @klass.properties.each_with_index do |property, i|
90
+ data_mapping = SCHEMA[i]
91
+ define_getter(property)
92
+ define_setter(property, data_mapping)
93
+ end
94
+ @klass
95
+ end
96
+
97
+ private
98
+
99
+ # @param [Symbol, String] property
100
+ # @return [Boolean]
101
+ def define_getter(property)
102
+ @klass.send(:attr_reader, property)
103
+ true
104
+ end
105
+
106
+ # @param [Symbol, String] property
107
+ # @param [Hash] mapping
108
+ # @return [Boolean]
109
+ def define_setter(property, mapping)
110
+ index = mapping[:index]
111
+ @klass.send(:define_method, "#{property.to_s}=") do |value|
112
+ send(:instance_variable_set, "@#{property.to_s}", value)
113
+ send(mapping[:name])[index] = value
114
+ send(:update)
115
+ return self
116
+ end
117
+ true
87
118
  end
88
- true
89
- end
90
119
 
91
- def initialize_channel_message(status_nibble_1, status_nibble_2, data_byte_1, data_byte_2 = 0)
92
- @status = [status_nibble_1, status_nibble_2]
93
- @data = [data_byte_1]
94
- @data[1] = data_byte_2 if self.class.second_data_byte?
95
- initialize_properties
96
- initialize_message(status_nibble_1, status_nibble_2)
97
120
  end
98
121
 
99
122
  # For defining Channel Message class types
@@ -124,7 +147,7 @@ module MIDIMessage
124
147
 
125
148
  # Build a Channel Mssage from raw nibbles and bytes
126
149
  # eg ChannelMessage.new(0x9, 0x0, 0x40, 0x40)
127
- # @param [*Array<Fixnum>] data The status nibbles and data bytes
150
+ # @param [*Fixnum] data The status nibbles and data bytes
128
151
  # @return [RawChannelMessage] The resulting RawChannelMessage object
129
152
  def initialize(*data)
130
153
  initialize_channel_message(*data)
@@ -22,6 +22,27 @@ module MIDIMessage
22
22
  map.value
23
23
  end
24
24
 
25
+ module Name
26
+
27
+ extend self
28
+
29
+ # eg "Control Change" -> "control_change"
30
+ # @param [Symbol, String] string
31
+ # @return [String]
32
+ def underscore(string)
33
+ string.to_s.downcase.gsub(/(\ )+/, "_")
34
+ end
35
+
36
+ # @param [Symbol, String] key
37
+ # @param [Symbol, String] other
38
+ # @return [Boolean]
39
+ def match?(key, other)
40
+ match_key = key.to_s.downcase
41
+ [match_key, Name.underscore(match_key)].include?(other.to_s.downcase)
42
+ end
43
+
44
+ end
45
+
25
46
  # MIDI Constant container
26
47
  class Group
27
48
 
@@ -38,14 +59,14 @@ module MIDIMessage
38
59
  # @param [String, Symbol] name
39
60
  # @return [Constant::Map]
40
61
  def find(name)
41
- @constants.find { |const| const.key.to_s.downcase == name.to_s.downcase }
62
+ @constants.find { |const| Name.match?(const.key, name) }
42
63
  end
43
64
 
44
65
  # Find a constant by its value
45
66
  # @param [Object] value
46
67
  # @return [Constant::Map]
47
68
  def find_by_value(value)
48
- @constants.find { |const| const.value.to_s.downcase == value.to_s.downcase }
69
+ @constants.find { |const| Name.match?(const.value, value) }
49
70
  end
50
71
 
51
72
  class << self
@@ -62,7 +83,7 @@ module MIDIMessage
62
83
  # @return [ConstantGroup]
63
84
  def find(key)
64
85
  ensure_initialized
65
- @groups.find { |g| g.key.to_s.downcase == key.to_s.downcase }
86
+ @groups.find { |group| Name.match?(group.key, key) }
66
87
  end
67
88
  alias_method :[], :find
68
89
 
@@ -78,7 +99,9 @@ module MIDIMessage
78
99
  # @return [Boolean]
79
100
  def populate_dictionary
80
101
  if @dict.nil?
81
- @dict = YAML.load_file(File.expand_path('../../midi.yml', __FILE__))
102
+ file = File.expand_path('../../midi.yml', __FILE__)
103
+ @dict = YAML.load_file(file)
104
+ @dict.freeze
82
105
  true
83
106
  end
84
107
  end
@@ -113,7 +136,7 @@ module MIDIMessage
113
136
  class MessageBuilder
114
137
 
115
138
  # @param [MIDIMessage] klass The message class to build
116
- # @param [String] const The constant to build the message with
139
+ # @param [MIDIMessage::Constant::Map] const The constant to build the message with
117
140
  def initialize(klass, const)
118
141
  @klass = klass
119
142
  @const = const
@@ -132,13 +155,16 @@ module MIDIMessage
132
155
  # Shortcuts for dealing with message status
133
156
  module Status
134
157
 
158
+ extend self
159
+
135
160
  # The value of the Status constant with the name status_name
136
161
  # @param [String] status_name The key to use to look up a constant value
137
162
  # @return [String] The constant value that was looked up
138
- def self.[](status_name)
163
+ def find(status_name)
139
164
  const = Constant.find("Status", status_name)
140
165
  const.value unless const.nil?
141
166
  end
167
+ alias_method :[], :find
142
168
 
143
169
  end
144
170
 
@@ -219,10 +245,11 @@ module MIDIMessage
219
245
  # This returns a MessageBuilder for the class, preloaded with the selected const
220
246
  # @param [String, Symbol] const_name The constant key to use to build the message
221
247
  # @return [MIDIMessage::MessageBuilder] A MessageBuilder object for the passed in constant
222
- def [](const_name)
248
+ def find(const_name)
223
249
  const = get_constant(const_name.to_s)
224
250
  MessageBuilder.new(self, const) unless const.nil?
225
251
  end
252
+ alias_method :[], :find
226
253
 
227
254
  end
228
255
 
@@ -5,6 +5,15 @@ module MIDIMessage
5
5
 
6
6
  attr_accessor :channel, :velocity
7
7
 
8
+ # Open a context with the given options
9
+ # @param [Hash] options
10
+ # @param [Proc] block
11
+ # @option options [Fixnum] :channel
12
+ # @option options [Fixnum] :velocity
13
+ def self.with(options = {}, &block)
14
+ new(options, &block).instance_eval(&block)
15
+ end
16
+
8
17
  # @param [Hash] options
9
18
  # @option options [Fixnum] :channel
10
19
  # @option options [Fixnum] :velocity
@@ -129,12 +138,13 @@ module MIDIMessage
129
138
 
130
139
  end
131
140
 
141
+ # Shortcut to MIDIMessage::Context.with
132
142
  # @param [Hash] options
133
143
  # @param [Proc] block
134
144
  # @option options [Fixnum] :channel
135
145
  # @option options [Fixnum] :velocity
136
146
  def self.with_context(options = {}, &block)
137
- Context.new(options, &block).instance_eval(&block)
147
+ Context.with(options, &block)
138
148
  end
139
149
  class << self
140
150
  alias_method :with, :with_context
@@ -14,8 +14,9 @@ module MIDIMessage
14
14
  # Byte array representation of the message eg [0x90, 0x40, 0x40] for NoteOn(0x40, 0x40)
15
15
  # @return [Array<Fixnum>] The array of bytes in the MIDI message
16
16
  def to_a
17
- data = @data.nil? ? [] : [@data[0], @data[1]]
18
- [(@status[0] << 4) + @status[1], *data].compact
17
+ data = [@data[0], @data[1]] unless @data.nil?
18
+ data ||= []
19
+ [status_as_byte, *data].compact
19
20
  end
20
21
  alias_method :to_byte_a, :to_a
21
22
  alias_method :to_byte_array, :to_a
@@ -32,7 +33,14 @@ module MIDIMessage
32
33
  populate_using_const
33
34
  end
34
35
 
35
- protected
36
+ private
37
+
38
+ # Convert the status nibbles to a single byte
39
+ # Eg [0x9, 0xF] -> 0x9F
40
+ # @return [Fixnum]
41
+ def status_as_byte
42
+ (@status[0] << 4) + @status[1]
43
+ end
36
44
 
37
45
  def populate_using_const
38
46
  unless (info = Constant::Loader.get_info(self)).nil?
@@ -7,9 +7,12 @@ module MIDIMessage
7
7
 
8
8
  include ChannelMessage
9
9
 
10
- DATA = [:channel, :value]
10
+ STATUS = 0xD
11
+ DATA = [:channel, :value].freeze
11
12
  DISPLAY_NAME = "Channel Aftertouch"
12
13
 
14
+ ChannelMessage::Accessors.decorate(self)
15
+
13
16
  end
14
17
  ChannelPressure = ChannelAftertouch
15
18
 
@@ -20,9 +23,12 @@ module MIDIMessage
20
23
 
21
24
  include ChannelMessage
22
25
 
23
- DATA = [:channel, :index, :value]
26
+ STATUS = 0xB
27
+ DATA = [:channel, :index, :value].freeze
24
28
  DISPLAY_NAME = "Control Change"
25
- CONSTANT = { "Control Change" => :index }
29
+ CONSTANT = { "Control Change" => :index }.freeze
30
+
31
+ ChannelMessage::Accessors.decorate(self)
26
32
 
27
33
  end
28
34
  Controller = ControlChange #shortcut
@@ -34,9 +40,12 @@ module MIDIMessage
34
40
 
35
41
  include ChannelMessage
36
42
 
37
- DATA = [:channel, :low, :high]
43
+ STATUS = 0xE
44
+ DATA = [:channel, :low, :high].freeze
38
45
  DISPLAY_NAME = "Pitch Bend"
39
46
 
47
+ ChannelMessage::Accessors.decorate(self)
48
+
40
49
  end
41
50
 
42
51
  #
@@ -46,9 +55,12 @@ module MIDIMessage
46
55
 
47
56
  include ChannelMessage
48
57
 
49
- DATA = [:channel, :note, :value]
58
+ STATUS = 0xA
59
+ DATA = [:channel, :note, :value].freeze
50
60
  DISPLAY_NAME = "Polyphonic Aftertouch"
51
- CONSTANT = { "Note" => :note }
61
+ CONSTANT = { "Note" => :note }.freeze
62
+
63
+ ChannelMessage::Accessors.decorate(self)
52
64
 
53
65
  end
54
66
  PolyAftertouch = PolyphonicAftertouch
@@ -62,9 +74,12 @@ module MIDIMessage
62
74
 
63
75
  include ChannelMessage
64
76
 
65
- DATA = [:channel, :program]
77
+ STATUS = 0xC
78
+ DATA = [:channel, :program].freeze
66
79
  DISPLAY_NAME = "Program Change"
67
80
 
81
+ ChannelMessage::Accessors.decorate(self)
82
+
68
83
  end
69
84
 
70
85
  #
@@ -74,9 +89,12 @@ module MIDIMessage
74
89
 
75
90
  include NoteMessage
76
91
 
77
- DATA = [:channel, :note, :velocity]
92
+ STATUS = 0x8
93
+ DATA = [:channel, :note, :velocity].freeze
78
94
  DISPLAY_NAME = "Note Off"
79
- CONSTANT = { "Note" => :note }
95
+ CONSTANT = { "Note" => :note }.freeze
96
+
97
+ ChannelMessage::Accessors.decorate(self)
80
98
 
81
99
  end
82
100
 
@@ -87,9 +105,12 @@ module MIDIMessage
87
105
 
88
106
  include NoteMessage
89
107
 
90
- DATA = [:channel, :note, :velocity]
108
+ STATUS = 0x9
109
+ DATA = [:channel, :note, :velocity].freeze
91
110
  DISPLAY_NAME = "Note On"
92
- CONSTANT = { "Note" => :note }
111
+ CONSTANT = { "Note" => :note }.freeze
112
+
113
+ ChannelMessage::Accessors.decorate(self)
93
114
 
94
115
  # returns the NoteOff equivalent of this object
95
116
  def to_note_off
@@ -105,6 +126,7 @@ module MIDIMessage
105
126
 
106
127
  include SystemMessage
107
128
 
129
+ ID = 0x1..0x6
108
130
  DISPLAY_NAME = "System Common"
109
131
 
110
132
  attr_reader :data
@@ -114,7 +136,7 @@ module MIDIMessage
114
136
  @const = options[:const]
115
137
  id = @const.nil? ? args.shift : @const.value
116
138
  id = strip_redundant_nibble(id)
117
- initialize_message(0xF, id)
139
+ initialize_message(SystemMessage::STATUS, id)
118
140
  @data = args.slice(0..1)
119
141
  end
120
142
 
@@ -127,6 +149,7 @@ module MIDIMessage
127
149
 
128
150
  include SystemMessage
129
151
 
152
+ ID = 0x8..0xF
130
153
  DISPLAY_NAME = "System Realtime"
131
154
 
132
155
  def initialize(*args)
@@ -134,7 +157,7 @@ module MIDIMessage
134
157
  @const = options[:const]
135
158
  id = @const.nil? ? args.first : @const.value
136
159
  id = strip_redundant_nibble(id)
137
- initialize_message(0xF, id)
160
+ initialize_message(SystemMessage::STATUS, id)
138
161
  end
139
162
 
140
163
  def id
@@ -145,6 +168,13 @@ module MIDIMessage
145
168
 
146
169
  module SystemExclusive
147
170
 
171
+ ID = 0x0
172
+ DELIMITER = {
173
+ :start => 0xF0,
174
+ :finish => 0xF7
175
+ }
176
+ DISPLAY_NAME = "System Exclusive"
177
+
148
178
  # A SysEx command message
149
179
  # A command message is identified by having a status byte equal to 0x12
150
180
  class Command
@@ -154,7 +184,7 @@ module MIDIMessage
154
184
  attr_accessor :data
155
185
  alias_method :value, :data
156
186
 
157
- TypeByte = 0x12
187
+ TYPE = 0x12
158
188
 
159
189
  def initialize(address, data, options = {})
160
190
  # store as a byte if it's a single byte
@@ -177,7 +207,7 @@ module MIDIMessage
177
207
  attr_reader :size
178
208
  alias_method :value, :size
179
209
 
180
- TypeByte = 0x11
210
+ TYPE = 0x11
181
211
 
182
212
  def initialize(address, size, options = {})
183
213
  self.size = if size.kind_of?(Array) && size.count == 1