mtk 0.0.2 → 0.0.3

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.
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