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