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
@@ -0,0 +1,26 @@
|
|
1
|
+
module MTK
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
# Extension for modules that want to define pseudo-constants (constant-like values with lower-case names)
|
5
|
+
module PseudoConstants
|
6
|
+
|
7
|
+
# Define a module method and module function (available both through the module namespace and as a mixin method),
|
8
|
+
# to provide a constant with a lower-case name.
|
9
|
+
#
|
10
|
+
# @param name [Symbol] the name of the pseudo-constant
|
11
|
+
# @param value [Object] the value of the pseudo-constant
|
12
|
+
# @return [nil]
|
13
|
+
def define_constant name, value
|
14
|
+
if name[0..0] =~ /[A-Z]/
|
15
|
+
const_set name, value # it's just a normal constant
|
16
|
+
else
|
17
|
+
# the pseudo-constant definition is the combination of a method and module_function:
|
18
|
+
define_method(name) { value }
|
19
|
+
module_function name
|
20
|
+
end
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
module MTK
|
2
|
+
|
3
|
+
# A measure of intensity, using an underlying value in the range 0.0-1.0
|
4
|
+
class Intensity
|
5
|
+
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
# The names of the base intensities. See {}MTK::Constants::Intensities} for more info.
|
9
|
+
NAMES = %w[ppp pp p mp mf o ff fff].freeze
|
10
|
+
|
11
|
+
VALUES_BY_NAME = {
|
12
|
+
'ppp' => 0.125,
|
13
|
+
'pp' => 0.25,
|
14
|
+
'p' => 0.375,
|
15
|
+
'mp' => 0.5,
|
16
|
+
'mf' => 0.625,
|
17
|
+
'o' => 0.75,
|
18
|
+
'ff' => 0.875,
|
19
|
+
'fff' => 1.0
|
20
|
+
}
|
21
|
+
|
22
|
+
@flyweight = {}
|
23
|
+
|
24
|
+
# The number of beats, typically representation as a Rational
|
25
|
+
attr_reader :value
|
26
|
+
|
27
|
+
def initialize(value)
|
28
|
+
@value = value
|
29
|
+
end
|
30
|
+
|
31
|
+
# Return an Intensity, only constructing a new instance when not already in the flyweight cache
|
32
|
+
def self.[](value)
|
33
|
+
value = value.to_f
|
34
|
+
@flyweight[value] ||= new(value)
|
35
|
+
end
|
36
|
+
|
37
|
+
class << self
|
38
|
+
alias :from_f :[]
|
39
|
+
alias :from_i :[]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Lookup an intensity by name.
|
43
|
+
# This method supports appending '-' or '+' for more fine-grained values.
|
44
|
+
def self.from_s(s)
|
45
|
+
return self[1.0] if s == 'fff+' # special case because "fff" is already the maximum
|
46
|
+
|
47
|
+
name = nil
|
48
|
+
modifier = nil
|
49
|
+
if s =~ /^(\w+)([+-])?$/
|
50
|
+
name = $1
|
51
|
+
modifier = $2
|
52
|
+
end
|
53
|
+
|
54
|
+
value = VALUES_BY_NAME[name]
|
55
|
+
raise ArgumentError.new("Invalid Intensity string '#{s}'") unless value
|
56
|
+
|
57
|
+
value += 1.0/24 if modifier == '+'
|
58
|
+
value -= 1.0/24 if modifier == '-'
|
59
|
+
|
60
|
+
self[value]
|
61
|
+
end
|
62
|
+
|
63
|
+
class << self
|
64
|
+
alias :from_name :from_s
|
65
|
+
end
|
66
|
+
|
67
|
+
# The number of beats as a floating point number
|
68
|
+
def to_f
|
69
|
+
@value.to_f
|
70
|
+
end
|
71
|
+
|
72
|
+
# The numerical value for the nearest whole number of beats
|
73
|
+
def to_i
|
74
|
+
@value.round
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_midi
|
78
|
+
(to_f * 127).round
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_percent
|
82
|
+
(@value * 100).round
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_s
|
86
|
+
"#{to_percent}% intensity"
|
87
|
+
end
|
88
|
+
|
89
|
+
def inspect
|
90
|
+
"#<#{self.class}:#{object_id} @value=#{@value}>"
|
91
|
+
end
|
92
|
+
|
93
|
+
def ==( other )
|
94
|
+
other.is_a? MTK::Intensity and other.value == @value
|
95
|
+
end
|
96
|
+
|
97
|
+
def <=> other
|
98
|
+
if other.respond_to? :value
|
99
|
+
@value <=> other.value
|
100
|
+
else
|
101
|
+
@value <=> other
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
def + intensity
|
107
|
+
if intensity.is_a? MTK::Intensity
|
108
|
+
MTK::Intensity[@value + intensity.value]
|
109
|
+
else
|
110
|
+
MTK::Intensity[@value + intensity]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def -intensity
|
115
|
+
if intensity.is_a? MTK::Intensity
|
116
|
+
MTK::Intensity[@value - intensity.value]
|
117
|
+
else
|
118
|
+
MTK::Intensity[@value - intensity]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def * intensity
|
123
|
+
if intensity.is_a? MTK::Intensity
|
124
|
+
MTK::Intensity[@value * intensity.value]
|
125
|
+
else
|
126
|
+
MTK::Intensity[@value * intensity]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def / intensity
|
131
|
+
if intensity.is_a? MTK::Intensity
|
132
|
+
MTK::Intensity[to_f / intensity.value]
|
133
|
+
else
|
134
|
+
MTK::Intensity[to_f / intensity]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def coerce(other)
|
139
|
+
return MTK::Intensity[other], self
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
# Construct a {Duration} from any supported type
|
145
|
+
def Intensity(*anything)
|
146
|
+
anything = anything.first if anything.length == 1
|
147
|
+
case anything
|
148
|
+
when Numeric then MTK::Intensity[anything]
|
149
|
+
when String, Symbol then MTK::Intensity.from_s(anything)
|
150
|
+
when Intensity then anything
|
151
|
+
else raise "Intensity doesn't understand #{anything.class}"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
module_function :Intensity
|
155
|
+
|
156
|
+
end
|
data/lib/mtk/interval.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
module MTK
|
2
|
+
|
3
|
+
# A measure of intensity, using an underlying value in the range 0.0-1.0
|
4
|
+
class Interval
|
5
|
+
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
# The preferred names of all pre-defined intervals
|
9
|
+
NAMES = %w[P1 m2 M2 m3 M3 P4 TT P5 m6 M6 m7 M7 P8].freeze
|
10
|
+
|
11
|
+
# All valid names of pre-defined intervals, indexed by their value.
|
12
|
+
NAMES_BY_VALUE =
|
13
|
+
[ # names # value # description
|
14
|
+
%w( P1 p1 ), # 0 # unison
|
15
|
+
%w( m2 min2 ), # 1 # minor second
|
16
|
+
%w( M2 maj2 ), # 2 # major second
|
17
|
+
%w( m3 min3 ), # 3 # minor third
|
18
|
+
%w( M3 maj3 ), # 4 # major third
|
19
|
+
%w( P4 p4 ), # 5 # perfect fourth
|
20
|
+
%w( TT tt ), # 6 # tritone (AKA augmented fourth, diminished fifth)
|
21
|
+
%w( P5 p5 ), # 7 # perfect fifth
|
22
|
+
%w( m6 min6 ), # 8 # minor sixth
|
23
|
+
%w( M6 maj6 ), # 9 # major sixth
|
24
|
+
%w( m7 min7 ), # 10 # minor seventh
|
25
|
+
%w( M7 maj7 ), # 11 # major seventh
|
26
|
+
%w( P8 p8 ) # 12 # octave
|
27
|
+
].freeze
|
28
|
+
|
29
|
+
# A mapping from intervals names to their value
|
30
|
+
VALUES_BY_NAME = Hash[ # a map from a list of name,value pairs
|
31
|
+
NAMES_BY_VALUE.map.with_index do |names,value|
|
32
|
+
names.map{|name| [name,value] }
|
33
|
+
end.flatten(1)
|
34
|
+
].freeze
|
35
|
+
|
36
|
+
# All valid interval names
|
37
|
+
ALL_NAMES = NAMES_BY_VALUE.flatten.freeze
|
38
|
+
|
39
|
+
|
40
|
+
@flyweight = {}
|
41
|
+
|
42
|
+
# The number of semitones represented by this interval
|
43
|
+
attr_reader :value
|
44
|
+
|
45
|
+
def initialize(value)
|
46
|
+
@value = value
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return an {Interval}, only constructing a new instance when not already in the flyweight cache
|
50
|
+
def self.[](value)
|
51
|
+
value = value.to_f unless value.is_a? Fixnum
|
52
|
+
@flyweight[value] ||= new(value)
|
53
|
+
end
|
54
|
+
|
55
|
+
class << self
|
56
|
+
alias :from_f :[]
|
57
|
+
alias :from_i :[]
|
58
|
+
end
|
59
|
+
|
60
|
+
# Lookup an interval duration by name.
|
61
|
+
def self.from_s(s)
|
62
|
+
value = VALUES_BY_NAME[s.to_s]
|
63
|
+
raise ArgumentError.new("Invalid Interval string '#{s}'") unless value
|
64
|
+
self[value]
|
65
|
+
end
|
66
|
+
|
67
|
+
class << self
|
68
|
+
alias :from_name :from_s
|
69
|
+
end
|
70
|
+
|
71
|
+
# The number of semitones as a floating point number
|
72
|
+
def to_f
|
73
|
+
@value.to_f
|
74
|
+
end
|
75
|
+
|
76
|
+
# The numerical value for the nearest whole number of semitones in this interval
|
77
|
+
def to_i
|
78
|
+
@value.round
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_s
|
82
|
+
@value.to_s
|
83
|
+
end
|
84
|
+
|
85
|
+
def inspect
|
86
|
+
"#{self.class}<#{to_s} semitones>"
|
87
|
+
end
|
88
|
+
|
89
|
+
def ==( other )
|
90
|
+
other.is_a? MTK::Interval and other.value == @value
|
91
|
+
end
|
92
|
+
|
93
|
+
def <=> other
|
94
|
+
if other.respond_to? :value
|
95
|
+
@value <=> other.value
|
96
|
+
else
|
97
|
+
@value <=> other
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def + interval
|
102
|
+
if interval.is_a? MTK::Interval
|
103
|
+
MTK::Interval[@value + interval.value]
|
104
|
+
else
|
105
|
+
MTK::Interval[@value + interval]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def -interval
|
110
|
+
if interval.is_a? MTK::Interval
|
111
|
+
MTK::Interval[@value - interval.value]
|
112
|
+
else
|
113
|
+
MTK::Interval[@value - interval]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def * interval
|
118
|
+
if interval.is_a? MTK::Interval
|
119
|
+
MTK::Interval[@value * interval.value]
|
120
|
+
else
|
121
|
+
MTK::Interval[@value * interval]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def / interval
|
126
|
+
if interval.is_a? MTK::Interval
|
127
|
+
MTK::Interval[to_f / interval.value]
|
128
|
+
else
|
129
|
+
MTK::Interval[to_f / interval]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def -@
|
134
|
+
MTK::Interval[@value * -1]
|
135
|
+
end
|
136
|
+
|
137
|
+
def coerce(other)
|
138
|
+
return MTK::Interval[other], self
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
# Construct a {Duration} from any supported type
|
144
|
+
def Interval(*anything)
|
145
|
+
anything = anything.first if anything.length == 1
|
146
|
+
case anything
|
147
|
+
when Numeric then MTK::Interval[anything]
|
148
|
+
when String, Symbol then MTK::Interval.from_s(anything)
|
149
|
+
when Interval then anything
|
150
|
+
else raise "Interval doesn't understand #{anything.class}"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
module_function :Interval
|
154
|
+
|
155
|
+
end
|
@@ -1,44 +1,175 @@
|
|
1
1
|
grammar MTK_Grammar
|
2
|
-
include MTK
|
3
2
|
|
4
|
-
rule
|
5
|
-
(
|
6
|
-
|
3
|
+
rule root
|
4
|
+
( space? root:(bare_sequencer | sequencer | timeline) space? ) {
|
5
|
+
root.value
|
7
6
|
}
|
8
7
|
end
|
9
8
|
|
9
|
+
rule bare_sequencer
|
10
|
+
( pattern '' ) {
|
11
|
+
# it seems at least 2 elements are needed to access the pattern submatch,
|
12
|
+
# so using an empty string as a workaround
|
13
|
+
MTK::Sequencers.LegatoSequencer pattern.value
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
rule sequencer
|
18
|
+
( left_curly bare_sequencer right_curly ) {
|
19
|
+
bare_sequencer.value
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
rule timeline
|
24
|
+
( left_curly timepoint pattern (space timepoint pattern)* right_curly ) {
|
25
|
+
MTK::Timeline.from_a values_of(:timepoint).zip(values_of :pattern)
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
rule pattern
|
30
|
+
( pattern:(bare_choice | choice) '' ) {
|
31
|
+
val = first.value
|
32
|
+
if val.is_a? MTK::Patterns::Pattern then val else MTK::Patterns::Pattern.new [val] end
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
rule bare_choice
|
38
|
+
(
|
39
|
+
seq:(bare_sequence | sequence) (pipe seq:(bare_sequence | sequence))*
|
40
|
+
)
|
41
|
+
{
|
42
|
+
vals = values_of :seq
|
43
|
+
if vals.length==1 then vals[0] else MTK::Patterns.Choice(*vals) end
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
rule choice
|
49
|
+
(
|
50
|
+
left_angle bare_choice right_angle
|
51
|
+
)
|
52
|
+
{
|
53
|
+
bare_choice.value
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
rule bare_sequence
|
59
|
+
(
|
60
|
+
chain (space chain)*
|
61
|
+
)
|
62
|
+
{
|
63
|
+
vals = values_of :chain
|
64
|
+
if vals.length==1 then vals[0] else MTK::Patterns.Sequence(*vals) end
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
rule sequence
|
70
|
+
(
|
71
|
+
left_paren chain (space chain)* right_paren ('*' max_cycles:int)? ('&' element_count:int)?
|
72
|
+
)
|
73
|
+
{
|
74
|
+
chains = values_of(:chain)
|
75
|
+
options = {}
|
76
|
+
if element_count
|
77
|
+
options[:min_elements] = options[:max_elements] = element_count.value
|
78
|
+
end
|
79
|
+
options[:max_cycles] = max_cycles.value if max_cycles
|
80
|
+
if chains.length == 1 and options.empty?
|
81
|
+
chains[0] # Don't form a chain unnecessarily
|
82
|
+
else
|
83
|
+
MTK::Patterns::Sequence.new chains, options
|
84
|
+
end
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
rule foreach
|
90
|
+
(
|
91
|
+
sequence ('@' sequence)+
|
92
|
+
)
|
93
|
+
{
|
94
|
+
MTK::Patterns::ForEach.new(values_of :sequence)
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
rule chain
|
100
|
+
( chainable (':' chainable)* ) {
|
101
|
+
vals = values_of(:chainable)
|
102
|
+
vals.length == 1 ? vals[0] : MTK::Patterns.Chain(*vals)
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
rule chainable
|
107
|
+
( foreach | choice | sequence | element )
|
108
|
+
end
|
109
|
+
|
110
|
+
rule element
|
111
|
+
(
|
112
|
+
elem:( intensity | duration | interval | pitch | pitch_class | variable ) ('*' max_cycles:int)?
|
113
|
+
)
|
114
|
+
{
|
115
|
+
if max_cycles
|
116
|
+
MTK::Patterns::Sequence.new( [elem.value], max_cycles: max_cycles.value )
|
117
|
+
else
|
118
|
+
elem.value
|
119
|
+
end
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
# rule chord
|
124
|
+
# ( left_bracket pitch (space pitch)* right_bracket ) {
|
125
|
+
# MTK::Chord *values_of(:pitch)
|
126
|
+
# }
|
127
|
+
# end
|
128
|
+
|
10
129
|
rule pitch
|
11
130
|
( pitch_class int ) {
|
12
|
-
Pitch[pitch_class.value, int.value]
|
131
|
+
MTK::Pitch[pitch_class.value, int.value]
|
13
132
|
}
|
14
133
|
end
|
15
134
|
|
16
135
|
rule pitch_class
|
17
136
|
( [A-Ga-g] [#b]*2 ) {
|
18
|
-
PitchClass[to_s]
|
137
|
+
MTK::PitchClass[to_s]
|
19
138
|
}
|
20
139
|
end
|
21
140
|
|
22
141
|
rule interval
|
23
|
-
(
|
24
|
-
|
142
|
+
( [Pp] [1458] | ('maj'|'min'|[Mm]) [2367] | 'TT' | 'tt' ) {
|
143
|
+
MTK::Interval.from_s(to_s)
|
25
144
|
}
|
26
145
|
end
|
27
146
|
|
28
147
|
rule intensity
|
29
|
-
( ('p'1*3 | 'mp' | 'mf' | 'f'
|
30
|
-
|
148
|
+
( ('p'1*3 | 'mp' | 'mf' | 'o' | 'f'2*3) ('+'|'-')? ) {
|
149
|
+
MTK::Intensity.from_s(to_s)
|
31
150
|
}
|
32
151
|
end
|
33
152
|
|
34
153
|
rule duration
|
35
|
-
( [
|
36
|
-
|
154
|
+
( rest:'-'? multiplier:number? [whqisrx] ('.'|'t')* ) {
|
155
|
+
MTK::Duration.from_s(to_s)
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
rule variable
|
160
|
+
( '$'+ ) {
|
161
|
+
MTK::Variable.new(to_s)
|
162
|
+
}
|
163
|
+
end
|
164
|
+
|
165
|
+
rule timepoint
|
166
|
+
( number right_arrow ) {
|
167
|
+
number.value
|
37
168
|
}
|
38
169
|
end
|
39
170
|
|
40
171
|
rule number
|
41
|
-
float | int
|
172
|
+
float | rational | int
|
42
173
|
end
|
43
174
|
|
44
175
|
rule float
|
@@ -47,12 +178,58 @@ grammar MTK_Grammar
|
|
47
178
|
}
|
48
179
|
end
|
49
180
|
|
181
|
+
rule rational
|
182
|
+
( numerator:int '/' denominator:[0-9]+ ) {
|
183
|
+
Rational(numerator.value, denominator.to_i)
|
184
|
+
}
|
185
|
+
end
|
186
|
+
|
50
187
|
rule int
|
51
188
|
( '-'? [0-9]+ ) {
|
52
189
|
to_i
|
53
190
|
}
|
54
191
|
end
|
55
192
|
|
193
|
+
rule left_paren
|
194
|
+
( '(' space? ) { nil }
|
195
|
+
end
|
196
|
+
|
197
|
+
rule right_paren
|
198
|
+
( space? ')' ) { nil }
|
199
|
+
end
|
200
|
+
|
201
|
+
rule left_bracket
|
202
|
+
( '[' space? ) { nil }
|
203
|
+
end
|
204
|
+
|
205
|
+
rule right_bracket
|
206
|
+
( space? ']' ) { nil }
|
207
|
+
end
|
208
|
+
|
209
|
+
rule left_curly
|
210
|
+
( '{' space? ) { nil }
|
211
|
+
end
|
212
|
+
|
213
|
+
rule right_curly
|
214
|
+
( space? '}' ) { nil }
|
215
|
+
end
|
216
|
+
|
217
|
+
rule left_angle
|
218
|
+
( '<' space? ) { nil }
|
219
|
+
end
|
220
|
+
|
221
|
+
rule right_angle
|
222
|
+
( space? '>' ) { nil }
|
223
|
+
end
|
224
|
+
|
225
|
+
rule pipe
|
226
|
+
( space? '|' space? ) { nil }
|
227
|
+
end
|
228
|
+
|
229
|
+
rule right_arrow
|
230
|
+
( space? '=>' space? ) { nil }
|
231
|
+
end
|
232
|
+
|
56
233
|
rule space
|
57
234
|
[\s]+ { nil }
|
58
235
|
end
|