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