midi-message 0.3.2 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ad978e231aef5931892a5fb96573c4e709f3ad64
4
+ data.tar.gz: 53cc976f50cb5c4d5ba619f3d3d4b1aaa10a71d9
5
+ SHA512:
6
+ metadata.gz: 6a51573df46bbee8bc764be4d139b52c7366c192c0402846bb3cae67efa4e5cbfa87b4ad1982765fc80f2d6d3e088c857f6ee5a9b0bc6d0f68a2e2ca08f3a931
7
+ data.tar.gz: c7264a018b5b523aeaa7391ab2f387aab0a2a9b66f583692bdd05a12eb7968c9ebc0ef120de61cfe82ab42e6f8c3301522fe957f6fc9ca9b5969d387ff8f1c5c
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2010-2011 Ari Russo
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,120 @@
1
+ # MIDI Message
2
+
3
+ ![midi](http://img208.imageshack.us/img208/5623/mks80small.jpg)
4
+
5
+ Ruby MIDI message objects
6
+
7
+ ## Features
8
+
9
+ * Flexible API to accommodate various sources and destinations of MIDI data
10
+ * Simple approach to System Exclusive data and devices
11
+ * [YAML dictionary of MIDI constants](https://github.com/arirusso/midi-message/blob/master/lib/midi.yml)
12
+
13
+ ## Install
14
+
15
+ `gem install midi-message`
16
+
17
+ Or if you're using Bundler, add this to your Gemfile
18
+
19
+ `gem "midi-message"`
20
+
21
+ ## Usage
22
+
23
+ ```ruby
24
+ require "midi-message"
25
+
26
+ include MIDIMessage
27
+ ```
28
+
29
+ #### Basic Messages
30
+
31
+ There are a few ways to create a new MIDI message. Here are some examples
32
+
33
+ ```ruby
34
+ NoteOn.new(0, 64, 64)
35
+
36
+ NoteOn["E4"].new(0, 100)
37
+
38
+ with(:channel => 0, :velocity => 100) { note_on("E4") }
39
+ ```
40
+
41
+ Those expressions all evaluate to the same object
42
+
43
+ ```ruby
44
+ #<MIDIMessage::NoteOn:0x9c1c240
45
+ @channel=0,
46
+ @data=[64, 64],
47
+ @name="E4",
48
+ @note=64,
49
+ @status=[9, 0],
50
+ @velocity=64,
51
+ @verbose_name="Note On: E4">
52
+ ```
53
+
54
+ #### SysEx Messages
55
+
56
+ As with any kind of message, you can begin with raw data
57
+
58
+ ```ruby
59
+ SystemExclusive.new(0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7)
60
+ ```
61
+
62
+ Or in a more object oriented way
63
+
64
+ ```ruby
65
+ synth = SystemExclusive::Node.new(0x41, :model_id => 0x42, :device_id => 0x10)
66
+
67
+ SystemExclusive::Command.new([0x40, 0x7F, 0x00], 0x00, :node => synth)
68
+ ```
69
+
70
+ A Node represents a device that you're sending a message to (eg. your Yamaha DX7 is a Node). Sysex messages can either be a Command or Request
71
+
72
+ You can use the Node to instantiate a message
73
+
74
+ ```ruby
75
+ synth.command([0x40, 0x7F, 0x00], 0x00)
76
+ ```
77
+
78
+ One way or another, you will wind up with a pair of objects like this
79
+
80
+ ```ruby
81
+ #<MIDIMessage::SystemExclusive::Command:0x9c1e57c
82
+ @address=[64, 0, 127],
83
+ @checksum=[65],
84
+ @data=[0],
85
+ @node=
86
+ #<MIDIMessage::SystemExclusive::Node:0x9c1e5a4
87
+ @device_id=16,
88
+ @manufacturer_id=65,
89
+ @model_id=66>>
90
+ ```
91
+
92
+ #### Parsing
93
+
94
+ The parse method will take any valid message data and return the object representation
95
+
96
+ ```ruby
97
+ MIDIMessage.parse(0x90, 0x40, 0x40)
98
+
99
+ #<MIDIMessage::NoteOn:0x9c1c240 ..>
100
+
101
+ MIDIMessage.parse(0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7)
102
+
103
+ #<MIDIMessage::SystemExclusive::Command:0x9c1e57c ..>
104
+ ```
105
+
106
+ Check out [nibbler](http://github.com/arirusso/nibbler) for more advanced parsing
107
+
108
+ ## Documentation
109
+
110
+ * [rdoc](http://rubydoc.info/github/arirusso/midi-message)
111
+
112
+ ## Author
113
+
114
+ * [Ari Russo](http://github.com/arirusso) <ari.russo at gmail.com>
115
+
116
+ ## License
117
+
118
+ Apache 2.0, See the file LICENSE
119
+
120
+ Copyright (c) 2011-2014 Ari Russo
@@ -1,29 +1,27 @@
1
- #!/usr/bin/env ruby
2
- #
3
1
  module MIDIMessage
4
2
 
5
- # common behavior amongst Channel Message types
3
+ # Common behavior amongst Channel Message types
6
4
  module ChannelMessage
7
5
 
8
- attr_reader :data,
9
- :name
10
-
11
- def self.new(*a, &block)
12
- RawChannelMessage.new(*a, &block)
6
+ attr_reader :data, :name
7
+
8
+ # Shortcut to RawChannelMessage.new
9
+ # aka build a ChannelMessage from raw nibbles and bytes
10
+ # eg ChannelMessage.new(0x9, 0x0, 0x40, 0x40)
11
+ # @param [*Array<Fixnum>] data The status nibbles and data bytes
12
+ # @return [RawChannelMessage] The resulting RawChannelMessage object
13
+ def self.new(*data, &block)
14
+ Message.new(*data, &block)
13
15
  end
14
-
15
- def initialize(*a)
16
- options = a.last.kind_of?(Hash) ? a.pop : {}
17
- @const = options[:const]
18
- unless @const.nil?
19
- key = self.class.map_constants_to
20
- ind = self.class.properties.index(key)
21
- ind ||= 0
22
- a.insert(ind, @const.value)
23
- end
24
- initialize_channel_message(self.class.type_for_status, *a)
16
+
17
+ # @param [*Array<Fixnum>] data The status nibbles and data bytes
18
+ def initialize(*data)
19
+ data = data.dup
20
+ options = data.last.kind_of?(Hash) ? data.pop : {}
21
+ processed_data = options[:const].nil? ? data : data_with_const(data, options[:const])
22
+ initialize_channel_message(self.class.type_for_status, *processed_data)
25
23
  end
26
-
24
+
27
25
  def initialize_properties
28
26
  props = [
29
27
  { :name => :status, :index => 1 },
@@ -44,15 +42,22 @@ module MIDIMessage
44
42
  end
45
43
  end
46
44
  end
47
-
45
+
48
46
  protected
49
-
47
+
50
48
  def self.included(base)
49
+ base.include(ShortMessage)
51
50
  base.extend(ClassMethods)
52
51
  end
53
52
 
54
53
  private
55
-
54
+
55
+ def data_with_const(data, const)
56
+ key = self.class.constant_property
57
+ ind = self.class.properties.index(key) || 0
58
+ data.insert(ind, const.value)
59
+ end
60
+
56
61
  def initialize_channel_message(status_nibble_1, status_nibble_2, data_byte_1, data_byte_2 = 0)
57
62
  @status = [status_nibble_1, status_nibble_2]
58
63
  @data = [data_byte_1]
@@ -60,120 +65,58 @@ module MIDIMessage
60
65
  initialize_properties
61
66
  initialize_short_message(status_nibble_1, status_nibble_2)
62
67
  end
63
-
68
+
69
+ # For defining Channel Message class types
64
70
  module ClassMethods
65
-
66
- attr_reader :properties
67
-
71
+
72
+ # Get the status nibble for this particular message type
73
+ # @return [Fixnum] The status nibble
68
74
  def type_for_status
69
- @display_name.nil? ? nil : Status[@display_name]
75
+ Status[display_name]
70
76
  end
71
77
 
72
- def schema(*args)
73
- @properties = args
78
+ def properties
79
+ const_get("DATA") if const_defined?("DATA")
74
80
  end
75
- alias_method :layout, :schema
76
81
 
82
+ # Does the schema of this Channel Message carry a second data byte?
83
+ # eg. NoteMessage does, and ProgramChange doesn"t
84
+ # @return [Boolean] Is there a second data byte on this message type?
77
85
  def second_data_byte?
78
- @properties.nil? || (@properties.length-1) > 1
86
+ properties.nil? || (properties.length-1) > 1
79
87
  end
80
88
 
81
89
  end
82
-
83
- end
84
-
85
- # use this if you want to instantiate a raw channel message
86
- #
87
- # example = ChannelMessage.new(0x9, 0x0, 0x40, 0x57) # creates a raw note-on message
88
- #
89
- class RawChannelMessage
90
-
91
- include ShortMessage
92
- include ChannelMessage
93
-
94
- use_display_name 'Channel Message'
95
-
96
- def initialize(*a)
97
- initialize_channel_message(*a)
98
- end
99
-
100
- def to_type
101
- status = (@status[0] << 4) + (@status[1])
102
- MIDIMessage.parse(status, *@data)
103
- end
104
-
105
- end
106
-
107
- #
108
- # MIDI Channel Aftertouch message
109
- #
110
- class ChannelAftertouch
111
-
112
- include ShortMessage
113
- include ChannelMessage
114
90
 
115
- schema :channel, :value
116
- use_display_name 'Channel Aftertouch'
117
-
118
- end
119
- ChannelPressure = ChannelAftertouch
120
-
121
- #
122
- # MIDI Control Change message
123
- #
124
- class ControlChange
125
-
126
- include ShortMessage
127
- include ChannelMessage
128
-
129
- schema :channel, :index, :value
130
- use_display_name 'Control Change'
131
- use_constants 'Control Change', :for => :index
132
-
133
- end
134
- Controller = ControlChange #shortcut
135
-
136
- #
137
- # MIDI Pitch Bend message
138
- #
139
- class PitchBend
140
-
141
- include ShortMessage
142
- include ChannelMessage
143
-
144
- schema :channel, :low, :high
145
- use_display_name 'Pitch Bend'
146
-
147
- end
148
-
149
- #
150
- # MIDI Polyphonic (note specific) Aftertouch message
151
- #
152
- class PolyphonicAftertouch
153
-
154
- include ShortMessage
155
- include ChannelMessage
91
+ # Use this if you want to instantiate a raw channel message
92
+ #
93
+ # For example ChannelMessage::Message.new(0x9, 0x0, 0x40, 0x57)
94
+ # creates a raw note-on message
95
+ class Message
96
+
97
+ include ChannelMessage
98
+
99
+ DISPLAY_NAME = "Channel Message"
100
+
101
+ # Build a Channel Mssage from raw nibbles and bytes
102
+ # eg ChannelMessage.new(0x9, 0x0, 0x40, 0x40)
103
+ # @param [*Array<Fixnum>] data The status nibbles and data bytes
104
+ # @return [RawChannelMessage] The resulting RawChannelMessage object
105
+ def initialize(*data)
106
+ initialize_channel_message(*data)
107
+ end
108
+
109
+ # Convert this RawChannelMessage to one of the more specific ChannelMessage types
110
+ # eg. RawChannelMessage.new(0x9, 0x0, 0x40, 0x40).to_type would result in
111
+ # NoteMessage.new(0x0, 0x40, 0x40)
112
+ # @return [ChannelMessage] The resulting specific ChannelMessage object
113
+ def to_type
114
+ status = (@status[0] << 4) + (@status[1])
115
+ MIDIMessage.parse(status, *@data)
116
+ end
156
117
 
157
- schema :channel, :note, :value
158
- use_display_name 'Polyphonic Aftertouch'
159
- use_constants 'Note', :for => :note
118
+ end
160
119
 
161
120
  end
162
- PolyAftertouch = PolyphonicAftertouch
163
- PolyPressure = PolyphonicAftertouch
164
- PolyphonicPressure = PolyphonicAftertouch
165
121
 
166
- #
167
- # MIDI Program Change message
168
- #
169
- class ProgramChange
170
-
171
- include ShortMessage
172
- include ChannelMessage
173
-
174
- schema :channel, :program
175
- use_display_name 'Program Change'
176
-
177
- end
178
-
179
122
  end
@@ -1,6 +1,3 @@
1
- #!/usr/bin/env ruby
2
- #
3
-
4
1
  module MIDIMessage
5
2
 
6
3
  # MIDI Constants
@@ -0,0 +1,204 @@
1
+ module MIDIMessage
2
+
3
+ #
4
+ # MIDI Channel Aftertouch message
5
+ #
6
+ class ChannelAftertouch
7
+
8
+ include ChannelMessage
9
+
10
+ DATA = [:channel, :value]
11
+ DISPLAY_NAME = "Channel Aftertouch"
12
+
13
+ end
14
+ ChannelPressure = ChannelAftertouch
15
+
16
+ #
17
+ # MIDI Control Change message
18
+ #
19
+ class ControlChange
20
+
21
+ include ChannelMessage
22
+
23
+ DATA = [:channel, :index, :value]
24
+ DISPLAY_NAME = "Control Change"
25
+ CONSTANT = { "Control Change" => :index }
26
+
27
+ end
28
+ Controller = ControlChange #shortcut
29
+
30
+ #
31
+ # MIDI Pitch Bend message
32
+ #
33
+ class PitchBend
34
+
35
+ include ChannelMessage
36
+
37
+ DATA = [:channel, :low, :high]
38
+ DISPLAY_NAME = "Pitch Bend"
39
+
40
+ end
41
+
42
+ #
43
+ # MIDI Polyphonic (note specific) Aftertouch message
44
+ #
45
+ class PolyphonicAftertouch
46
+
47
+ include ChannelMessage
48
+
49
+ DATA = [:channel, :note, :value]
50
+ DISPLAY_NAME = "Polyphonic Aftertouch"
51
+ CONSTANT = { "Note" => :note }
52
+
53
+ end
54
+ PolyAftertouch = PolyphonicAftertouch
55
+ PolyPressure = PolyphonicAftertouch
56
+ PolyphonicPressure = PolyphonicAftertouch
57
+
58
+ #
59
+ # MIDI Program Change message
60
+ #
61
+ class ProgramChange
62
+
63
+ include ChannelMessage
64
+
65
+ DATA = [:channel, :program]
66
+ DISPLAY_NAME = "Program Change"
67
+
68
+ end
69
+
70
+ #
71
+ # MIDI Note-Off message
72
+ #
73
+ class NoteOff
74
+
75
+ include NoteMessage
76
+
77
+ DATA = [:channel, :note, :velocity]
78
+ DISPLAY_NAME = "Note Off"
79
+ CONSTANT = { "Note" => :note }
80
+
81
+ end
82
+
83
+ #
84
+ # MIDI Note-On message
85
+ #
86
+ class NoteOn
87
+
88
+ include NoteMessage
89
+
90
+ DATA = [:channel, :note, :velocity]
91
+ DISPLAY_NAME = "Note On"
92
+ CONSTANT = { "Note" => :note }
93
+
94
+ # returns the NoteOff equivalent of this object
95
+ def to_note_off
96
+ NoteOff.new(channel, note, velocity)
97
+ end
98
+
99
+ end
100
+
101
+ #
102
+ # MIDI System-Common message
103
+ #
104
+ class SystemCommon
105
+
106
+ include SystemMessage
107
+
108
+ DISPLAY_NAME = "System Common"
109
+
110
+ attr_reader :data
111
+
112
+ def initialize(*a)
113
+ options = a.last.kind_of?(Hash) ? a.pop : {}
114
+ @const = options[:const]
115
+ id = @const.nil? ? a.shift : @const.value
116
+ id = strip_redundant_nibble(id)
117
+ initialize_short_message(0xF, id)
118
+ @data = [a[0], a[1]]
119
+ end
120
+
121
+ end
122
+
123
+ #
124
+ # MIDI System-Realtime message
125
+ #
126
+ class SystemRealtime
127
+
128
+ include SystemMessage
129
+
130
+ DISPLAY_NAME = "System Realtime"
131
+
132
+ def initialize(*a)
133
+ options = a.last.kind_of?(Hash) ? a.pop : {}
134
+ @const = options[:const]
135
+ id = @const.nil? ? a[0] : @const.value
136
+ id = strip_redundant_nibble(id)
137
+ initialize_short_message(0xF, id)
138
+ end
139
+
140
+ def id
141
+ @status[1]
142
+ end
143
+
144
+ end
145
+
146
+ module SystemExclusive
147
+
148
+ # A SysEx command message
149
+ # A command message is identified by having a status byte equal to 0x12
150
+ class Command
151
+
152
+ include SystemExclusive::InstanceMethods
153
+
154
+ attr_accessor :data
155
+ alias_method :value, :data
156
+
157
+ TypeByte = 0x12
158
+
159
+ def initialize(address, data, options = {})
160
+ # store as a byte if it's a single byte
161
+ @data = (data.kind_of?(Array) && data.length.eql?(1)) ? data[0] : data
162
+ initialize_sysex(address, options)
163
+ end
164
+
165
+ end
166
+
167
+ # A SysEx request message
168
+ # A request message is identified by having a status byte equal to 0x11
169
+ class Request
170
+
171
+ include SystemExclusive::InstanceMethods
172
+
173
+ attr_reader :size
174
+ alias_method :value, :size
175
+
176
+ TypeByte = 0x11
177
+
178
+ def initialize(address, size, options = {})
179
+ self.size = (size.kind_of?(Array) && size.length.eql?(1)) ? size[0] : size
180
+ initialize_sysex(address, options)
181
+ end
182
+
183
+ def size=(val)
184
+ # accepts a Numeric or Array but
185
+ # must always store value as an array of three bytes
186
+ size = []
187
+ if val.kind_of?(Array) && val.size <= 3
188
+ size = val
189
+ elsif val.kind_of?(Numeric) && (((val + 1) / 247) <= 2)
190
+ size = []
191
+ div, mod = *val.divmod(247)
192
+ size << mod unless mod.zero?
193
+ div.times { size << 247 }
194
+ end
195
+ (3 - size.size).times { size.unshift 0 }
196
+ @size = size
197
+ end
198
+
199
+ end
200
+
201
+ end
202
+
203
+ end
204
+
@@ -1,70 +1,40 @@
1
- #!/usr/bin/env ruby
2
- #
3
1
  module MIDIMessage
4
2
 
5
- #
6
3
  # Common Note Message Behavior
7
- #
8
4
  module NoteMessage
9
5
 
10
- # the octave number of the note
6
+ def self.included(base)
7
+ base.include(ChannelMessage)
8
+ end
9
+
10
+ # The octave number of the note
11
+ # @return [Fixnum]
11
12
  def octave
12
- (note / 12) -1
13
+ (note / 12) - 1
13
14
  end
14
15
  alias_method :oct, :octave
15
16
 
16
- # set the octave number of the note
17
- def octave=(val)
18
- self.note = ((val + 1) * 12) + abs_note
17
+ # Set the octave number of the note
18
+ # @param [Fixnum] value
19
+ # @return [NoteMessage] self
20
+ def octave=(value)
21
+ self.note = ((value + 1) * 12) + abs_note
19
22
  self
20
23
  end
21
24
  alias_method :oct=, :octave=
22
25
 
23
- # how many half-steps is this note above the closest C
26
+ # How many half-steps is this note above the closest C
27
+ # @return [Fixnum]
24
28
  def abs_note
25
29
  note - ((note / 12) * 12)
26
30
  end
27
31
 
28
- # the name of the note without its octave e.g. F#
32
+ # The name of the note without its octave e.g. F#
33
+ # @return [String]
29
34
  def note_name
30
35
  name.split(/-?\d\z/).first
31
36
  end
32
37
 
33
38
  end
34
-
35
- #
36
- # MIDI Note-Off message
37
- #
38
- class NoteOff
39
-
40
- include NoteMessage
41
- include ShortMessage
42
- include ChannelMessage
43
-
44
- schema :channel, :note, :velocity
45
- use_display_name 'Note Off'
46
- use_constants 'Note', :for => :note
47
-
48
- end
49
-
50
- #
51
- # MIDI Note-On message
52
- #
53
- class NoteOn
54
-
55
- include NoteMessage
56
- include ShortMessage
57
- include ChannelMessage
58
-
59
- schema :channel, :note, :velocity
60
- use_display_name 'Note On'
61
- use_constants 'Note', :for => :note
62
-
63
- # returns the NoteOff equivalent of this object
64
- def to_note_off
65
- NoteOff.new(channel, note, velocity)
66
- end
67
-
68
- end
69
39
 
70
40
  end