midi-message 0.4.8 → 0.4.9

Sign up to get free protection for your applications and to get access to all the features.
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