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.
- data/.yardopts +3 -2
- data/DEVELOPMENT_NOTES.md +114 -0
- data/INTRO.md +64 -8
- data/LICENSE.txt +1 -1
- data/README.md +31 -102
- data/Rakefile +56 -18
- data/bin/mtk +215 -0
- data/examples/crescendo.rb +5 -5
- data/examples/drum_pattern1.rb +23 -0
- data/examples/dynamic_pattern.rb +8 -11
- data/examples/gets_and_play.rb +26 -0
- data/examples/notation.rb +22 -0
- data/examples/play_midi.rb +8 -10
- data/examples/random_tone_row.rb +2 -2
- data/examples/syntax_to_midi.rb +28 -0
- data/examples/test_output.rb +8 -0
- data/examples/tone_row_melody.rb +6 -6
- data/lib/mtk.rb +52 -40
- data/lib/mtk/chord.rb +55 -0
- data/lib/mtk/constants/durations.rb +57 -0
- data/lib/mtk/constants/intensities.rb +61 -0
- data/lib/mtk/constants/intervals.rb +73 -0
- data/lib/mtk/constants/pitch_classes.rb +29 -0
- data/lib/mtk/constants/pitches.rb +52 -0
- data/lib/mtk/duration.rb +211 -0
- data/lib/mtk/events/event.rb +119 -0
- data/lib/mtk/events/note.rb +112 -0
- data/lib/mtk/events/parameter.rb +54 -0
- data/lib/mtk/helpers/collection.rb +164 -0
- data/lib/mtk/helpers/convert.rb +36 -0
- data/lib/mtk/helpers/lilypond.rb +162 -0
- data/lib/mtk/helpers/output_selector.rb +67 -0
- data/lib/mtk/helpers/pitch_collection.rb +23 -0
- data/lib/mtk/helpers/pseudo_constants.rb +26 -0
- data/lib/mtk/intensity.rb +156 -0
- data/lib/mtk/interval.rb +155 -0
- data/lib/mtk/lang/mtk_grammar.citrus +190 -13
- data/lib/mtk/lang/parser.rb +29 -0
- data/lib/mtk/melody.rb +94 -0
- data/lib/mtk/midi/dls_synth_device.rb +144 -0
- data/lib/mtk/midi/dls_synth_output.rb +62 -0
- data/lib/mtk/midi/file.rb +67 -32
- data/lib/mtk/midi/input.rb +97 -0
- data/lib/mtk/midi/jsound_input.rb +36 -17
- data/lib/mtk/midi/jsound_output.rb +48 -46
- data/lib/mtk/midi/output.rb +195 -0
- data/lib/mtk/midi/unimidi_input.rb +117 -0
- data/lib/mtk/midi/unimidi_output.rb +121 -0
- data/lib/mtk/{_numeric_extensions.rb → numeric_extensions.rb} +12 -0
- data/lib/mtk/patterns/chain.rb +49 -0
- data/lib/mtk/{pattern → patterns}/choice.rb +14 -8
- data/lib/mtk/patterns/cycle.rb +18 -0
- data/lib/mtk/patterns/for_each.rb +71 -0
- data/lib/mtk/patterns/function.rb +39 -0
- data/lib/mtk/{pattern → patterns}/lines.rb +11 -17
- data/lib/mtk/{pattern → patterns}/palindrome.rb +11 -8
- data/lib/mtk/patterns/pattern.rb +171 -0
- data/lib/mtk/patterns/sequence.rb +20 -0
- data/lib/mtk/pitch.rb +7 -6
- data/lib/mtk/pitch_class.rb +124 -46
- data/lib/mtk/pitch_class_set.rb +58 -35
- data/lib/mtk/sequencers/event_builder.rb +131 -0
- data/lib/mtk/sequencers/legato_sequencer.rb +24 -0
- data/lib/mtk/sequencers/rhythmic_sequencer.rb +28 -0
- data/lib/mtk/{sequencer/abstract_sequencer.rb → sequencers/sequencer.rb} +37 -11
- data/lib/mtk/{sequencer → sequencers}/step_sequencer.rb +4 -4
- data/lib/mtk/timeline.rb +39 -22
- data/lib/mtk/variable.rb +32 -0
- data/spec/mtk/chord_spec.rb +83 -0
- data/spec/mtk/{_constants → constants}/durations_spec.rb +12 -41
- data/spec/mtk/{_constants → constants}/intensities_spec.rb +13 -37
- data/spec/mtk/{_constants → constants}/intervals_spec.rb +14 -32
- data/spec/mtk/{_constants → constants}/pitch_classes_spec.rb +8 -4
- data/spec/mtk/{_constants → constants}/pitches_spec.rb +5 -1
- data/spec/mtk/duration_spec.rb +372 -0
- data/spec/mtk/events/event_spec.rb +234 -0
- data/spec/mtk/events/note_spec.rb +174 -0
- data/spec/mtk/events/parameter_spec.rb +220 -0
- data/spec/mtk/{helper → helpers}/collection_spec.rb +86 -3
- data/spec/mtk/{helper → helpers}/pseudo_constants_spec.rb +2 -2
- data/spec/mtk/intensity_spec.rb +289 -0
- data/spec/mtk/interval_spec.rb +265 -0
- data/spec/mtk/lang/parser_spec.rb +597 -0
- data/spec/mtk/melody_spec.rb +223 -0
- data/spec/mtk/midi/file_spec.rb +16 -16
- data/spec/mtk/midi/jsound_input_spec.rb +11 -0
- data/spec/mtk/midi/jsound_output_spec.rb +11 -0
- data/spec/mtk/midi/output_spec.rb +102 -0
- data/spec/mtk/midi/unimidi_input_spec.rb +11 -0
- data/spec/mtk/midi/unimidi_output_spec.rb +11 -0
- data/spec/mtk/{_numeric_extensions_spec.rb → numeric_extensions_spec.rb} +1 -0
- data/spec/mtk/patterns/chain_spec.rb +110 -0
- data/spec/mtk/{pattern → patterns}/choice_spec.rb +20 -30
- data/spec/mtk/{pattern → patterns}/cycle_spec.rb +25 -35
- data/spec/mtk/patterns/for_each_spec.rb +136 -0
- data/spec/mtk/{pattern → patterns}/function_spec.rb +17 -30
- data/spec/mtk/{pattern → patterns}/lines_spec.rb +11 -27
- data/spec/mtk/{pattern → patterns}/palindrome_spec.rb +13 -29
- data/spec/mtk/patterns/pattern_spec.rb +132 -0
- data/spec/mtk/patterns/sequence_spec.rb +203 -0
- data/spec/mtk/pitch_class_set_spec.rb +23 -21
- data/spec/mtk/pitch_class_spec.rb +151 -39
- data/spec/mtk/pitch_spec.rb +22 -1
- data/spec/mtk/sequencers/event_builder_spec.rb +245 -0
- data/spec/mtk/sequencers/legato_sequencer_spec.rb +45 -0
- data/spec/mtk/sequencers/rhythmic_sequencer_spec.rb +84 -0
- data/spec/mtk/sequencers/sequencer_spec.rb +215 -0
- data/spec/mtk/{sequencer → sequencers}/step_sequencer_spec.rb +35 -13
- data/spec/mtk/timeline_spec.rb +109 -16
- data/spec/mtk/variable_spec.rb +52 -0
- data/spec/spec_coverage.rb +2 -0
- data/spec/spec_helper.rb +3 -0
- metadata +188 -91
- data/lib/mtk/_constants/durations.rb +0 -80
- data/lib/mtk/_constants/intensities.rb +0 -81
- data/lib/mtk/_constants/intervals.rb +0 -85
- data/lib/mtk/_constants/pitch_classes.rb +0 -35
- data/lib/mtk/_constants/pitches.rb +0 -49
- data/lib/mtk/event.rb +0 -70
- data/lib/mtk/helper/collection.rb +0 -114
- data/lib/mtk/helper/event_builder.rb +0 -85
- data/lib/mtk/helper/pseudo_constants.rb +0 -26
- data/lib/mtk/lang/grammar.rb +0 -17
- data/lib/mtk/note.rb +0 -63
- data/lib/mtk/pattern/abstract_pattern.rb +0 -132
- data/lib/mtk/pattern/cycle.rb +0 -51
- data/lib/mtk/pattern/enumerator.rb +0 -26
- data/lib/mtk/pattern/function.rb +0 -46
- data/lib/mtk/pattern/sequence.rb +0 -30
- data/lib/mtk/pitch_set.rb +0 -84
- data/lib/mtk/sequencer/rhythmic_sequencer.rb +0 -29
- data/lib/mtk/transform/invertible.rb +0 -15
- data/lib/mtk/transform/mappable.rb +0 -18
- data/lib/mtk/transform/set_theory_operations.rb +0 -34
- data/lib/mtk/transform/transposable.rb +0 -14
- data/spec/mtk/event_spec.rb +0 -139
- data/spec/mtk/helper/event_builder_spec.rb +0 -92
- data/spec/mtk/lang/grammar_spec.rb +0 -100
- data/spec/mtk/note_spec.rb +0 -115
- data/spec/mtk/pattern/abstract_pattern_spec.rb +0 -45
- data/spec/mtk/pattern/note_cycle_spec.rb.bak +0 -116
- data/spec/mtk/pattern/pitch_cycle_spec.rb.bak +0 -47
- data/spec/mtk/pattern/pitch_sequence_spec.rb.bak +0 -37
- data/spec/mtk/pattern/sequence_spec.rb +0 -151
- data/spec/mtk/pitch_set_spec.rb +0 -198
- data/spec/mtk/sequencer/abstract_sequencer_spec.rb +0 -159
- data/spec/mtk/sequencer/rhythmic_sequencer_spec.rb +0 -49
data/lib/mtk/duration.rb
ADDED
@@ -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
|