mtk 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. data/.yardopts +3 -2
  2. data/DEVELOPMENT_NOTES.md +114 -0
  3. data/INTRO.md +64 -8
  4. data/LICENSE.txt +1 -1
  5. data/README.md +31 -102
  6. data/Rakefile +56 -18
  7. data/bin/mtk +215 -0
  8. data/examples/crescendo.rb +5 -5
  9. data/examples/drum_pattern1.rb +23 -0
  10. data/examples/dynamic_pattern.rb +8 -11
  11. data/examples/gets_and_play.rb +26 -0
  12. data/examples/notation.rb +22 -0
  13. data/examples/play_midi.rb +8 -10
  14. data/examples/random_tone_row.rb +2 -2
  15. data/examples/syntax_to_midi.rb +28 -0
  16. data/examples/test_output.rb +8 -0
  17. data/examples/tone_row_melody.rb +6 -6
  18. data/lib/mtk.rb +52 -40
  19. data/lib/mtk/chord.rb +55 -0
  20. data/lib/mtk/constants/durations.rb +57 -0
  21. data/lib/mtk/constants/intensities.rb +61 -0
  22. data/lib/mtk/constants/intervals.rb +73 -0
  23. data/lib/mtk/constants/pitch_classes.rb +29 -0
  24. data/lib/mtk/constants/pitches.rb +52 -0
  25. data/lib/mtk/duration.rb +211 -0
  26. data/lib/mtk/events/event.rb +119 -0
  27. data/lib/mtk/events/note.rb +112 -0
  28. data/lib/mtk/events/parameter.rb +54 -0
  29. data/lib/mtk/helpers/collection.rb +164 -0
  30. data/lib/mtk/helpers/convert.rb +36 -0
  31. data/lib/mtk/helpers/lilypond.rb +162 -0
  32. data/lib/mtk/helpers/output_selector.rb +67 -0
  33. data/lib/mtk/helpers/pitch_collection.rb +23 -0
  34. data/lib/mtk/helpers/pseudo_constants.rb +26 -0
  35. data/lib/mtk/intensity.rb +156 -0
  36. data/lib/mtk/interval.rb +155 -0
  37. data/lib/mtk/lang/mtk_grammar.citrus +190 -13
  38. data/lib/mtk/lang/parser.rb +29 -0
  39. data/lib/mtk/melody.rb +94 -0
  40. data/lib/mtk/midi/dls_synth_device.rb +144 -0
  41. data/lib/mtk/midi/dls_synth_output.rb +62 -0
  42. data/lib/mtk/midi/file.rb +67 -32
  43. data/lib/mtk/midi/input.rb +97 -0
  44. data/lib/mtk/midi/jsound_input.rb +36 -17
  45. data/lib/mtk/midi/jsound_output.rb +48 -46
  46. data/lib/mtk/midi/output.rb +195 -0
  47. data/lib/mtk/midi/unimidi_input.rb +117 -0
  48. data/lib/mtk/midi/unimidi_output.rb +121 -0
  49. data/lib/mtk/{_numeric_extensions.rb → numeric_extensions.rb} +12 -0
  50. data/lib/mtk/patterns/chain.rb +49 -0
  51. data/lib/mtk/{pattern → patterns}/choice.rb +14 -8
  52. data/lib/mtk/patterns/cycle.rb +18 -0
  53. data/lib/mtk/patterns/for_each.rb +71 -0
  54. data/lib/mtk/patterns/function.rb +39 -0
  55. data/lib/mtk/{pattern → patterns}/lines.rb +11 -17
  56. data/lib/mtk/{pattern → patterns}/palindrome.rb +11 -8
  57. data/lib/mtk/patterns/pattern.rb +171 -0
  58. data/lib/mtk/patterns/sequence.rb +20 -0
  59. data/lib/mtk/pitch.rb +7 -6
  60. data/lib/mtk/pitch_class.rb +124 -46
  61. data/lib/mtk/pitch_class_set.rb +58 -35
  62. data/lib/mtk/sequencers/event_builder.rb +131 -0
  63. data/lib/mtk/sequencers/legato_sequencer.rb +24 -0
  64. data/lib/mtk/sequencers/rhythmic_sequencer.rb +28 -0
  65. data/lib/mtk/{sequencer/abstract_sequencer.rb → sequencers/sequencer.rb} +37 -11
  66. data/lib/mtk/{sequencer → sequencers}/step_sequencer.rb +4 -4
  67. data/lib/mtk/timeline.rb +39 -22
  68. data/lib/mtk/variable.rb +32 -0
  69. data/spec/mtk/chord_spec.rb +83 -0
  70. data/spec/mtk/{_constants → constants}/durations_spec.rb +12 -41
  71. data/spec/mtk/{_constants → constants}/intensities_spec.rb +13 -37
  72. data/spec/mtk/{_constants → constants}/intervals_spec.rb +14 -32
  73. data/spec/mtk/{_constants → constants}/pitch_classes_spec.rb +8 -4
  74. data/spec/mtk/{_constants → constants}/pitches_spec.rb +5 -1
  75. data/spec/mtk/duration_spec.rb +372 -0
  76. data/spec/mtk/events/event_spec.rb +234 -0
  77. data/spec/mtk/events/note_spec.rb +174 -0
  78. data/spec/mtk/events/parameter_spec.rb +220 -0
  79. data/spec/mtk/{helper → helpers}/collection_spec.rb +86 -3
  80. data/spec/mtk/{helper → helpers}/pseudo_constants_spec.rb +2 -2
  81. data/spec/mtk/intensity_spec.rb +289 -0
  82. data/spec/mtk/interval_spec.rb +265 -0
  83. data/spec/mtk/lang/parser_spec.rb +597 -0
  84. data/spec/mtk/melody_spec.rb +223 -0
  85. data/spec/mtk/midi/file_spec.rb +16 -16
  86. data/spec/mtk/midi/jsound_input_spec.rb +11 -0
  87. data/spec/mtk/midi/jsound_output_spec.rb +11 -0
  88. data/spec/mtk/midi/output_spec.rb +102 -0
  89. data/spec/mtk/midi/unimidi_input_spec.rb +11 -0
  90. data/spec/mtk/midi/unimidi_output_spec.rb +11 -0
  91. data/spec/mtk/{_numeric_extensions_spec.rb → numeric_extensions_spec.rb} +1 -0
  92. data/spec/mtk/patterns/chain_spec.rb +110 -0
  93. data/spec/mtk/{pattern → patterns}/choice_spec.rb +20 -30
  94. data/spec/mtk/{pattern → patterns}/cycle_spec.rb +25 -35
  95. data/spec/mtk/patterns/for_each_spec.rb +136 -0
  96. data/spec/mtk/{pattern → patterns}/function_spec.rb +17 -30
  97. data/spec/mtk/{pattern → patterns}/lines_spec.rb +11 -27
  98. data/spec/mtk/{pattern → patterns}/palindrome_spec.rb +13 -29
  99. data/spec/mtk/patterns/pattern_spec.rb +132 -0
  100. data/spec/mtk/patterns/sequence_spec.rb +203 -0
  101. data/spec/mtk/pitch_class_set_spec.rb +23 -21
  102. data/spec/mtk/pitch_class_spec.rb +151 -39
  103. data/spec/mtk/pitch_spec.rb +22 -1
  104. data/spec/mtk/sequencers/event_builder_spec.rb +245 -0
  105. data/spec/mtk/sequencers/legato_sequencer_spec.rb +45 -0
  106. data/spec/mtk/sequencers/rhythmic_sequencer_spec.rb +84 -0
  107. data/spec/mtk/sequencers/sequencer_spec.rb +215 -0
  108. data/spec/mtk/{sequencer → sequencers}/step_sequencer_spec.rb +35 -13
  109. data/spec/mtk/timeline_spec.rb +109 -16
  110. data/spec/mtk/variable_spec.rb +52 -0
  111. data/spec/spec_coverage.rb +2 -0
  112. data/spec/spec_helper.rb +3 -0
  113. metadata +188 -91
  114. data/lib/mtk/_constants/durations.rb +0 -80
  115. data/lib/mtk/_constants/intensities.rb +0 -81
  116. data/lib/mtk/_constants/intervals.rb +0 -85
  117. data/lib/mtk/_constants/pitch_classes.rb +0 -35
  118. data/lib/mtk/_constants/pitches.rb +0 -49
  119. data/lib/mtk/event.rb +0 -70
  120. data/lib/mtk/helper/collection.rb +0 -114
  121. data/lib/mtk/helper/event_builder.rb +0 -85
  122. data/lib/mtk/helper/pseudo_constants.rb +0 -26
  123. data/lib/mtk/lang/grammar.rb +0 -17
  124. data/lib/mtk/note.rb +0 -63
  125. data/lib/mtk/pattern/abstract_pattern.rb +0 -132
  126. data/lib/mtk/pattern/cycle.rb +0 -51
  127. data/lib/mtk/pattern/enumerator.rb +0 -26
  128. data/lib/mtk/pattern/function.rb +0 -46
  129. data/lib/mtk/pattern/sequence.rb +0 -30
  130. data/lib/mtk/pitch_set.rb +0 -84
  131. data/lib/mtk/sequencer/rhythmic_sequencer.rb +0 -29
  132. data/lib/mtk/transform/invertible.rb +0 -15
  133. data/lib/mtk/transform/mappable.rb +0 -18
  134. data/lib/mtk/transform/set_theory_operations.rb +0 -34
  135. data/lib/mtk/transform/transposable.rb +0 -14
  136. data/spec/mtk/event_spec.rb +0 -139
  137. data/spec/mtk/helper/event_builder_spec.rb +0 -92
  138. data/spec/mtk/lang/grammar_spec.rb +0 -100
  139. data/spec/mtk/note_spec.rb +0 -115
  140. data/spec/mtk/pattern/abstract_pattern_spec.rb +0 -45
  141. data/spec/mtk/pattern/note_cycle_spec.rb.bak +0 -116
  142. data/spec/mtk/pattern/pitch_cycle_spec.rb.bak +0 -47
  143. data/spec/mtk/pattern/pitch_sequence_spec.rb.bak +0 -37
  144. data/spec/mtk/pattern/sequence_spec.rb +0 -151
  145. data/spec/mtk/pitch_set_spec.rb +0 -198
  146. data/spec/mtk/sequencer/abstract_sequencer_spec.rb +0 -159
  147. data/spec/mtk/sequencer/rhythmic_sequencer_spec.rb +0 -49
@@ -0,0 +1,211 @@
1
+ module MTK
2
+
3
+ # A measure of time in musical beats.
4
+ # May be negative to indicate a rest, which uses the absolute value for the effective duration.
5
+ class Duration
6
+
7
+ include Comparable
8
+
9
+ # The names of the base durations. See {MTK::Constants::Durations} for more info.
10
+ NAMES = %w[w h q i s r x].freeze
11
+
12
+ VALUES_BY_NAME = {
13
+ 'w' => 4,
14
+ 'h' => 2,
15
+ 'q' => 1,
16
+ 'i' => Rational(1,2),
17
+ 's' => Rational(1,4),
18
+ 'r' => Rational(1,8),
19
+ 'x' => Rational(1,16)
20
+ }
21
+
22
+ @flyweight = {}
23
+
24
+ # The number of beats, typically represented as a Rational
25
+ attr_reader :value
26
+
27
+ def initialize( length_in_beats )
28
+ @value = length_in_beats
29
+ end
30
+
31
+ # Return a duration, only constructing a new instance when not already in the flyweight cache
32
+ def self.[](length_in_beats)
33
+ if length_in_beats.is_a? Fixnum
34
+ value = length_in_beats
35
+ else
36
+ value = Rational(length_in_beats)
37
+ end
38
+ @flyweight[value] ||= new(value)
39
+ end
40
+
41
+ class << self
42
+ alias :from_f :[]
43
+ alias :from_i :[]
44
+ end
45
+
46
+ # Lookup a duration by name.
47
+ # This method supports appending any combination of '.' and 't' for more fine-grained values.
48
+ # each '.' multiplies by 3/2, and each 't' multiplies by 2/3.
49
+ # You may use the prefix '-' to negate the duration (which turns it into a rest of the same length).
50
+ # You may also prefix (after the '-' if present) the base duration name with an integer, float, or rational number
51
+ # to multiply the base duration value. Rationals are in the form "#{{numerator_integer}}/#{{denominator_integer}}".
52
+ # @example lookup value of 'q.', which is 1.5 times a quarter note (1.5 beats):
53
+ # MTK::Duration.from_s('q.')
54
+ # @example lookup the value of 3/4w, which three-quarters of a whole note (3 beats):
55
+ # MTK::Duration.from_s('3/4w')
56
+ def self.from_s(s)
57
+ if s =~ /^(-)?(\d+([\.\/]\d+)?)?([whqisrx])((\.|t)*)$/i
58
+ name = $4.downcase
59
+ modifier = $5.downcase
60
+ modifier << $1 if $1 # negation
61
+ multiplier = $2
62
+ else
63
+ raise ArgumentError.new("Invalid Duration string '#{s}'")
64
+ end
65
+
66
+ value = VALUES_BY_NAME[name]
67
+ modifier.each_char do |mod|
68
+ case mod
69
+ when '-' then value *= -1
70
+ when '.' then value *= Rational(3,2)
71
+ when 't' then value *= Rational(2,3)
72
+ end
73
+ end
74
+
75
+ if multiplier
76
+ case multiplier
77
+ when /\./
78
+ value *= multiplier.to_f
79
+ when /\//
80
+ numerator, denominator = multiplier.split('/')
81
+ value *= Rational(numerator.to_i, denominator.to_i)
82
+ else
83
+ value *= multiplier.to_i
84
+ end
85
+ end
86
+
87
+ self[value]
88
+ end
89
+
90
+ class << self
91
+ alias :from_name :from_s
92
+ end
93
+
94
+ # The magnitude (absolute value) of the duration.
95
+ # This is the actual duration for rests.
96
+ # @see #rest?
97
+ def length
98
+ @value < 0 ? -@value : @value
99
+ end
100
+
101
+ # Durations with negative values are rests.
102
+ # @see #length
103
+ # @see #-@
104
+ def rest?
105
+ @value < 0
106
+ end
107
+
108
+ # The number of beats as a floating point number
109
+ def to_f
110
+ @value.to_f
111
+ end
112
+
113
+ # The numerical value for the nearest whole number of beats
114
+ def to_i
115
+ @value.round
116
+ end
117
+
118
+ def to_s
119
+ value = @value.to_s
120
+ value = sprintf '%.2f', @value if value.length > 6 # threshold is 6 for no particular reason...
121
+ "#{value} #{@value.abs > 1 || @value==0 ? 'beats' : 'beat'}"
122
+ end
123
+
124
+ def inspect
125
+ "#<#{self.class}:#{object_id} @value=#{@value}>"
126
+ end
127
+
128
+ def ==( other )
129
+ if other.is_a? MTK::Duration
130
+ other.value == @value
131
+ else
132
+ other == @value
133
+ end
134
+ end
135
+
136
+ def <=> other
137
+ if other.respond_to? :value
138
+ @value <=> other.value
139
+ else
140
+ @value <=> other
141
+ end
142
+ end
143
+
144
+ # Add this duration to another.
145
+ # @return a new Duration that has a value of the sum of the arguments.
146
+ def + duration
147
+ if duration.is_a? MTK::Duration
148
+ MTK::Duration[@value + duration.value]
149
+ else
150
+ MTK::Duration[@value + duration]
151
+ end
152
+ end
153
+
154
+ # Subtract another duration from this one.
155
+ # @return a new Duration that has a value of the difference of the arguments.
156
+ def - duration
157
+ if duration.is_a? MTK::Duration
158
+ MTK::Duration[@value - duration.value]
159
+ else
160
+ MTK::Duration[@value - duration]
161
+ end
162
+ end
163
+
164
+ # Multiply this duration with another.
165
+ # @return a new Duration that has a value of the product of the arguments.
166
+ def * duration
167
+ if duration.is_a? MTK::Duration
168
+ MTK::Duration[@value * duration.value]
169
+ else
170
+ MTK::Duration[@value * duration]
171
+ end
172
+ end
173
+
174
+ # Divide this duration with another.
175
+ # @return a new Duration that has a value of the division of the arguments.
176
+ def / duration
177
+ if duration.is_a? MTK::Duration
178
+ MTK::Duration[to_f / duration.value]
179
+ else
180
+ MTK::Duration[to_f / duration]
181
+ end
182
+ end
183
+
184
+ # Negate the duration value.
185
+ # Turns normal durations into rests and vice versa.
186
+ # @return a new Duration that has a negated value.
187
+ # @see #rest?
188
+ def -@
189
+ MTK::Duration[@value * -1]
190
+ end
191
+
192
+ # Allow basic math operations with Numeric objects.
193
+ def coerce(other)
194
+ return MTK::Duration[other], self
195
+ end
196
+
197
+ end
198
+
199
+ # Construct a {Duration} from any supported type
200
+ def Duration(*anything)
201
+ anything = anything.first if anything.length == 1
202
+ case anything
203
+ when Numeric then MTK::Duration[anything]
204
+ when String, Symbol then MTK::Duration.from_s(anything)
205
+ when Duration then anything
206
+ else raise "Duration doesn't understand #{anything.class}"
207
+ end
208
+ end
209
+ module_function :Duration
210
+
211
+ end
@@ -0,0 +1,119 @@
1
+ module MTK
2
+
3
+ module Events
4
+
5
+ # An abstract musical event
6
+ # @abstract
7
+ class Event
8
+
9
+ # The type of event: :note, :control, :pressure, :bend, or :program
10
+ attr_reader :type
11
+
12
+ # The specific element effected by this type of event, when applicable.
13
+ # Depends on the event type. For example, the number of a :note type Event is the pitch,
14
+ # and the number of a :control type Event is the controller (CC) number.
15
+ # This value is nil for inapplicable event types.
16
+ attr_accessor :number
17
+
18
+ # The value of event.
19
+ # Depends on event type. For example, the value of a :note type Event is the intensity,
20
+ # and the value of a :control type Event is the controller (CC) value.
21
+ attr_accessor :value
22
+
23
+ # Duration of the Event in beats (e.g. 1.0 is a quarter note in 4/4 time signatures)
24
+ # @see length
25
+ # @see rest?
26
+ # @see instantaneous?
27
+ # @see duration_in_pulses
28
+ attr_reader :duration
29
+
30
+ def duration= duration
31
+ @duration = duration
32
+ @duration = ::MTK::Duration[@duration || 0] unless @duration.is_a? ::MTK::Duration
33
+ @duration
34
+ end
35
+
36
+ # The channel of the event, for multi-tracked events.
37
+ attr_accessor :channel
38
+
39
+
40
+ def initialize(type, options={})
41
+ @type = type
42
+ @value = options[:value]
43
+ @number = options[:number]
44
+ @duration = options.fetch(:duration, 0)
45
+ @duration = ::MTK::Duration[@duration] unless @duration.is_a? ::MTK::Duration
46
+ @channel = options[:channel]
47
+ end
48
+
49
+ def self.from_hash(hash)
50
+ new(hash[:type], hash)
51
+ end
52
+
53
+ def to_hash
54
+ hash = {:type => @type}
55
+ hash[:value] = @value unless @value.nil?
56
+ hash[:duration] = @duration unless @duration.nil?
57
+ hash[:number] = @number unless @number.nil?
58
+ hash[:channel] = @channel unless @channel.nil?
59
+ hash
60
+ end
61
+
62
+ def midi_value
63
+ if @value and @value.respond_to? :to_midi
64
+ @value.to_midi
65
+ else
66
+ value = @value
67
+ midi_value = (127 * (value || 0)).round
68
+ midi_value = 0 if midi_value < 0
69
+ midi_value = 127 if midi_value > 127
70
+ midi_value
71
+ end
72
+ end
73
+
74
+ def midi_value= value
75
+ @value = value/127.0
76
+ end
77
+
78
+ # The magnitude (absolute value) of the duration.
79
+ # Indicate the "real" duration for rests.
80
+ # @see rest?
81
+ def length
82
+ @duration.length
83
+ end
84
+
85
+ # By convention, any events with negative durations are a rest
86
+ def rest?
87
+ @duration.rest?
88
+ end
89
+
90
+ # By convention, any events with 0 duration are instantaneous
91
+ def instantaneous?
92
+ @duration.nil? or @duration == 0
93
+ end
94
+
95
+ # Convert duration to an integer number of MIDI pulses, given the pulses_per_beat
96
+ def duration_in_pulses(pulses_per_beat)
97
+ (length.to_f * pulses_per_beat).round
98
+ end
99
+
100
+ def == other
101
+ other.respond_to? :type and @type == other.type and
102
+ other.respond_to? :number and @number == other.number and
103
+ other.respond_to? :value and @value == other.value and
104
+ other.respond_to? :duration and @duration == other.duration and
105
+ other.respond_to? :channel and @channel == other.channel
106
+ end
107
+
108
+ def to_s
109
+ "Event(#@type" + (@number ? "[#@number]" : '') + ", #{sprintf '%.2f',@value}, #{sprintf '%.2f',@duration})"
110
+ end
111
+
112
+ def inspect
113
+ "Event(#@type" + (@number ? "[#@number]" : '') + ", #@value, #{@duration.to_f})"
114
+ end
115
+
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,112 @@
1
+ module MTK
2
+
3
+ module Events
4
+
5
+ # A musical {Event} defined by a {Pitch}, intensity, and duration
6
+ class Note < Event
7
+
8
+ DEFAULT_DURATION = MTK::Duration[1]
9
+ DEFAULT_INTENSITY = MTK::Intensity[0.75]
10
+
11
+ # Frequency of the note as a {Pitch}.
12
+ alias :pitch :number
13
+ alias :pitch= :number=
14
+
15
+ # Intensity of the note as a value in the range 0.0 - 1.0.
16
+ alias :intensity :value
17
+ alias :intensity= :value=
18
+
19
+ # intensity scaled to the MIDI range 0-127
20
+ alias :velocity :midi_value
21
+ alias :velocity= :midi_value=
22
+
23
+ def initialize(pitch, duration=DEFAULT_DURATION, intensity=DEFAULT_INTENSITY, channel=nil)
24
+ super :note, number:pitch, duration:duration, value:intensity, channel:channel
25
+ end
26
+
27
+ def self.from_hash(hash)
28
+ new(hash[:pitch]||hash[:number], hash[:duration], hash[:intensity]||hash[:value], hash[:channel])
29
+ end
30
+
31
+ def to_hash
32
+ super.merge({ pitch: @number, intensity: @value })
33
+ end
34
+
35
+ def self.from_midi(pitch, velocity, duration_in_beats, channel=0)
36
+ new( MTK::Constants::Pitches::PITCHES[pitch.to_i], MTK::Duration[duration_in_beats], MTK::Intensity[velocity/127.0], channel )
37
+ end
38
+
39
+ def midi_pitch
40
+ pitch.to_i
41
+ end
42
+
43
+ def transpose(interval)
44
+ self.pitch += interval
45
+ self
46
+ end
47
+
48
+ def invert(around_pitch)
49
+ self.pitch = self.pitch.invert(around_pitch)
50
+ self
51
+ end
52
+
53
+ def ==(other)
54
+ ( other.respond_to? :pitch and pitch == other.pitch and
55
+ other.respond_to? :intensity and intensity == other.intensity and
56
+ other.respond_to? :duration and duration == other.duration
57
+ ) or super
58
+ end
59
+
60
+ def to_s
61
+ "Note(#{@number}, #{@duration}, #{@value.to_percent}%)"
62
+ end
63
+
64
+ def inspect
65
+ "#<#{self.class}:#{object_id} @pitch=#{@number.inspect}, @duration=#{@duration.inspect}, @intensity=#{@value.inspect}>"
66
+ end
67
+
68
+ end
69
+ end
70
+
71
+ # Construct a {Events::Note} from a list of any supported type for the arguments: pitch, intensity, duration, channel
72
+ def Note(*anything)
73
+ anything = anything.first if anything.size == 1
74
+ case anything
75
+ when MTK::Events::Note then anything
76
+
77
+ when MTK::Pitch then MTK::Events::Note.new(anything)
78
+
79
+ when Array
80
+ pitch = nil
81
+ duration = nil
82
+ intensity = nil
83
+ channel = nil
84
+ unknowns = []
85
+ anything.each do |item|
86
+ case item
87
+ when MTK::Pitch then pitch = item
88
+ when MTK::Duration then duration = item
89
+ when MTK::Intensity then intensity = item
90
+ else unknowns << item
91
+ end
92
+ end
93
+
94
+ pitch = MTK::Pitch(unknowns.shift) if pitch.nil? and not unknowns.empty?
95
+ raise "MTK::Note() couldn't find a pitch in arguments: #{anything.inspect}" if pitch.nil?
96
+
97
+ duration = MTK::Duration(unknowns.shift) if duration.nil? and not unknowns.empty?
98
+ intensity = MTK::Intensity(unknowns.shift) if intensity.nil? and not unknowns.empty?
99
+ channel = unknowns.shift.to_i if channel.nil? and not unknowns.empty?
100
+
101
+ duration ||= MTK::Events::Note::DEFAULT_DURATION
102
+ intensity ||= MTK::Events::Note::DEFAULT_INTENSITY
103
+
104
+ MTK::Events::Note.new( pitch, duration, intensity, channel )
105
+
106
+ else
107
+ raise "MTK::Note() doesn't understand #{anything.class}"
108
+ end
109
+ end
110
+ module_function :Note
111
+
112
+ end
@@ -0,0 +1,54 @@
1
+ module MTK
2
+
3
+ module Events
4
+
5
+ class Parameter < Event
6
+
7
+ def self.from_midi(status, data1, data2)
8
+ if status.is_a? Array
9
+ type,channel = *status
10
+ else
11
+ type,channel = status & 0xF0, status & 0x0F
12
+ end
13
+ type, number, value = *(
14
+ case type
15
+ when 0xA0,:poly_pressure then [:pressure, data1, data2]
16
+ when 0xB0,:control_change then [:control, data1, data2]
17
+ when 0xC0,:program_change then [:program, data1]
18
+ when 0xD0,:channel_pressure then [:pressure, nil, data1] # no number means all notes on channel
19
+ when 0xE0,:pitch_bend then [:bend, nil, (data1 + (data2 << 7))]
20
+ else [:unknown, data1, data2]
21
+ end
22
+ )
23
+ if type == :bend
24
+ if value == 16383
25
+ value = 1.0 # special case since the math doesn't quite work out to convert to -1..1 for all values
26
+ else
27
+ value = (value / 8192.0) - 1.0
28
+ end
29
+ elsif value.is_a? Numeric
30
+ value /= 127.0
31
+ end
32
+ new type, :number => number, :value => value, :channel => channel
33
+ end
34
+
35
+ def midi_value
36
+ if @type == :bend
37
+ (16383*(@value+1)/2).round
38
+ else
39
+ super
40
+ end
41
+ end
42
+
43
+ def to_s
44
+ "Parameter(#@type" + (@number ? "[#@number], " : ', ') + "#{sprintf '%.2f', @value || Float::NAN})"
45
+ end
46
+
47
+ def inspect
48
+ "Parameter(#@type" + (@number ? "[#@number], " : ', ') + "#@value)"
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+ end