jmtk 0.0.3.3-java → 0.4-java
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.
- checksums.yaml +15 -0
- data/DEVELOPMENT_NOTES.md +20 -0
- data/INTRO.md +63 -31
- data/README.md +9 -3
- data/Rakefile +42 -42
- data/bin/jmtk +75 -32
- data/bin/mtk +75 -32
- data/examples/drum_pattern.rb +2 -2
- data/examples/dynamic_pattern.rb +1 -1
- data/examples/helpers/output_selector.rb +71 -0
- data/examples/notation.rb +5 -1
- data/examples/tone_row_melody.rb +1 -1
- data/lib/mtk.rb +1 -0
- data/lib/mtk/core/duration.rb +18 -3
- data/lib/mtk/core/intensity.rb +5 -3
- data/lib/mtk/core/interval.rb +21 -14
- data/lib/mtk/core/pitch.rb +2 -0
- data/lib/mtk/core/pitch_class.rb +6 -3
- data/lib/mtk/events/event.rb +2 -1
- data/lib/mtk/events/note.rb +1 -1
- data/lib/mtk/events/parameter.rb +1 -0
- data/lib/mtk/events/rest.rb +85 -0
- data/lib/mtk/events/timeline.rb +6 -2
- data/lib/mtk/io/jsound_input.rb +9 -3
- data/lib/mtk/io/midi_file.rb +38 -2
- data/lib/mtk/io/midi_input.rb +1 -1
- data/lib/mtk/io/midi_output.rb +95 -4
- data/lib/mtk/io/unimidi_input.rb +7 -3
- data/lib/mtk/lang/durations.rb +31 -26
- data/lib/mtk/lang/intensities.rb +29 -30
- data/lib/mtk/lang/intervals.rb +108 -41
- data/lib/mtk/lang/mtk_grammar.citrus +14 -4
- data/lib/mtk/lang/parser.rb +10 -5
- data/lib/mtk/lang/pitch_classes.rb +45 -17
- data/lib/mtk/lang/pitches.rb +169 -32
- data/lib/mtk/lang/tutorial.rb +279 -0
- data/lib/mtk/lang/tutorial_lesson.rb +87 -0
- data/lib/mtk/sequencers/event_builder.rb +29 -8
- data/spec/mtk/core/duration_spec.rb +14 -1
- data/spec/mtk/core/intensity_spec.rb +1 -1
- data/spec/mtk/events/event_spec.rb +10 -16
- data/spec/mtk/events/note_spec.rb +3 -3
- data/spec/mtk/events/rest_spec.rb +184 -0
- data/spec/mtk/events/timeline_spec.rb +5 -1
- data/spec/mtk/io/midi_file_spec.rb +13 -2
- data/spec/mtk/io/midi_output_spec.rb +42 -9
- data/spec/mtk/lang/durations_spec.rb +5 -5
- data/spec/mtk/lang/intensities_spec.rb +5 -5
- data/spec/mtk/lang/intervals_spec.rb +139 -13
- data/spec/mtk/lang/parser_spec.rb +65 -25
- data/spec/mtk/lang/pitch_classes_spec.rb +0 -11
- data/spec/mtk/lang/pitches_spec.rb +0 -15
- data/spec/mtk/patterns/chain_spec.rb +7 -7
- data/spec/mtk/patterns/for_each_spec.rb +2 -2
- data/spec/mtk/sequencers/event_builder_spec.rb +49 -17
- metadata +12 -22
@@ -29,12 +29,12 @@ describe MTK::Lang::Durations do
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
describe '
|
32
|
+
describe 'e' do
|
33
33
|
it 'is 1/2 of a beat' do
|
34
|
-
|
34
|
+
e.value.should == 1.0/2
|
35
35
|
end
|
36
36
|
it 'is available via a module property and via mixin' do
|
37
|
-
Durations::
|
37
|
+
Durations::e.should == e
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -67,7 +67,7 @@ describe MTK::Lang::Durations do
|
|
67
67
|
|
68
68
|
describe "DURATIONS" do
|
69
69
|
it "contains all Durations pseudo-constants" do
|
70
|
-
Durations::DURATIONS.should =~ [w, h, q,
|
70
|
+
Durations::DURATIONS.should =~ [w, h, q, e, s, r, x]
|
71
71
|
end
|
72
72
|
|
73
73
|
it "is immutable" do
|
@@ -77,7 +77,7 @@ describe MTK::Lang::Durations do
|
|
77
77
|
|
78
78
|
describe "DURATION_NAMES" do
|
79
79
|
it "contains all Durations pseudo-constants names as strings" do
|
80
|
-
Durations::DURATION_NAMES.should =~ ['w', 'h', 'q', '
|
80
|
+
Durations::DURATION_NAMES.should =~ ['w', 'h', 'q', 'e', 's', 'r', 'x']
|
81
81
|
end
|
82
82
|
|
83
83
|
it "is immutable" do
|
@@ -47,12 +47,12 @@ describe MTK::Lang::Intensities do
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
-
describe '
|
50
|
+
describe 'f' do # AKA forte
|
51
51
|
it 'is equivalent to MIDI velocity 95' do
|
52
|
-
(
|
52
|
+
(f.value * 127).round.should == 95
|
53
53
|
end
|
54
54
|
it 'is available via a module property and via mixin' do
|
55
|
-
Intensities::
|
55
|
+
Intensities::f.should == f
|
56
56
|
end
|
57
57
|
it "does not overwrite the PitchClass constant 'F'" do
|
58
58
|
F.should be_a PitchClass
|
@@ -79,7 +79,7 @@ describe MTK::Lang::Intensities do
|
|
79
79
|
|
80
80
|
describe "INTENSITIES" do
|
81
81
|
it "contains all Intensities pseudo-constants" do
|
82
|
-
Intensities::INTENSITIES.should =~ [ppp, pp, p, mp, mf,
|
82
|
+
Intensities::INTENSITIES.should =~ [ppp, pp, p, mp, mf, f, ff, fff]
|
83
83
|
end
|
84
84
|
|
85
85
|
it "is immutable" do
|
@@ -89,7 +89,7 @@ describe MTK::Lang::Intensities do
|
|
89
89
|
|
90
90
|
describe "INTENSITY_NAMES" do
|
91
91
|
it "contains all Intensities pseudo-constants names as strings" do
|
92
|
-
Intensities::INTENSITY_NAMES.should =~ ['ppp', 'pp', 'p', 'mp', 'mf', '
|
92
|
+
Intensities::INTENSITY_NAMES.should =~ ['ppp', 'pp', 'p', 'mp', 'mf', 'f', 'ff', 'fff']
|
93
93
|
end
|
94
94
|
|
95
95
|
it "is immutable" do
|
@@ -11,12 +11,30 @@ describe MTK::Lang::Intervals do
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
describe 'd2' do
|
15
|
+
it 'is 0 semitones' do
|
16
|
+
d2.should == Interval[0]
|
17
|
+
end
|
18
|
+
it 'is available via a module property and via mixin' do
|
19
|
+
Intervals::d2.should == d2
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
14
23
|
describe 'm2' do
|
15
24
|
it 'is 1 semitone' do
|
16
25
|
m2.should == Interval[1]
|
17
26
|
end
|
18
27
|
it 'is available via a module property and via mixin' do
|
19
|
-
Intervals::
|
28
|
+
Intervals::m2.should == m2
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'a1' do
|
33
|
+
it 'is 1 semitone' do
|
34
|
+
a1.should == Interval[1]
|
35
|
+
end
|
36
|
+
it 'is available via a module property and via mixin' do
|
37
|
+
Intervals::a1.should == a1
|
20
38
|
end
|
21
39
|
end
|
22
40
|
|
@@ -25,7 +43,16 @@ describe MTK::Lang::Intervals do
|
|
25
43
|
M2.should == Interval[2]
|
26
44
|
end
|
27
45
|
it 'is available via a module property and via mixin' do
|
28
|
-
Intervals::
|
46
|
+
Intervals::M2.should == M2
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'd3' do
|
51
|
+
it 'is 2 semitones' do
|
52
|
+
d3.should == Interval[2]
|
53
|
+
end
|
54
|
+
it 'is available via a module property and via mixin' do
|
55
|
+
Intervals::d3.should == d3
|
29
56
|
end
|
30
57
|
end
|
31
58
|
|
@@ -34,7 +61,16 @@ describe MTK::Lang::Intervals do
|
|
34
61
|
m3.should == Interval[3]
|
35
62
|
end
|
36
63
|
it 'is available via a module property and via mixin' do
|
37
|
-
Intervals::
|
64
|
+
Intervals::m3.should == m3
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'a2' do
|
69
|
+
it 'is 3 semitones' do
|
70
|
+
a2.should == Interval[3]
|
71
|
+
end
|
72
|
+
it 'is available via a module property and via mixin' do
|
73
|
+
Intervals::a2.should == a2
|
38
74
|
end
|
39
75
|
end
|
40
76
|
|
@@ -43,7 +79,16 @@ describe MTK::Lang::Intervals do
|
|
43
79
|
M3.should == Interval[4]
|
44
80
|
end
|
45
81
|
it 'is available via a module property and via mixin' do
|
46
|
-
Intervals::
|
82
|
+
Intervals::M3.should == M3
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'd4' do
|
87
|
+
it 'is 4 semitones' do
|
88
|
+
d4.should == Interval[4]
|
89
|
+
end
|
90
|
+
it 'is available via a module property and via mixin' do
|
91
|
+
Intervals::d4.should == d4
|
47
92
|
end
|
48
93
|
end
|
49
94
|
|
@@ -52,7 +97,16 @@ describe MTK::Lang::Intervals do
|
|
52
97
|
P4.should == Interval[5]
|
53
98
|
end
|
54
99
|
it 'is available via a module property and via mixin' do
|
55
|
-
Intervals::
|
100
|
+
Intervals::P4.should == P4
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe 'a3' do
|
105
|
+
it 'is 5 semitones' do
|
106
|
+
a3.should == Interval[5]
|
107
|
+
end
|
108
|
+
it 'is available via a module property and via mixin' do
|
109
|
+
Intervals::a3.should == a3
|
56
110
|
end
|
57
111
|
end
|
58
112
|
|
@@ -61,7 +115,25 @@ describe MTK::Lang::Intervals do
|
|
61
115
|
TT.should == Interval[6]
|
62
116
|
end
|
63
117
|
it 'is available via a module property and via mixin' do
|
64
|
-
Intervals::
|
118
|
+
Intervals::TT.should == TT
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe 'a4' do
|
123
|
+
it 'is 6 semitones' do
|
124
|
+
a4.should == Interval[6]
|
125
|
+
end
|
126
|
+
it 'is available via a module property and via mixin' do
|
127
|
+
Intervals::a4.should == a4
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe 'd5' do
|
132
|
+
it 'is 6 semitones' do
|
133
|
+
d5.should == Interval[6]
|
134
|
+
end
|
135
|
+
it 'is available via a module property and via mixin' do
|
136
|
+
Intervals::d5.should == d5
|
65
137
|
end
|
66
138
|
end
|
67
139
|
|
@@ -70,7 +142,16 @@ describe MTK::Lang::Intervals do
|
|
70
142
|
P5.should == Interval[7]
|
71
143
|
end
|
72
144
|
it 'is available via a module property and via mixin' do
|
73
|
-
Intervals::
|
145
|
+
Intervals::P5.should == P5
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe 'd6' do
|
150
|
+
it 'is 7 semitones' do
|
151
|
+
d6.should == Interval[7]
|
152
|
+
end
|
153
|
+
it 'is available via a module property and via mixin' do
|
154
|
+
Intervals::d6.should == d6
|
74
155
|
end
|
75
156
|
end
|
76
157
|
|
@@ -83,12 +164,30 @@ describe MTK::Lang::Intervals do
|
|
83
164
|
end
|
84
165
|
end
|
85
166
|
|
167
|
+
describe 'a5' do
|
168
|
+
it 'is 8 semitones' do
|
169
|
+
a5.should == Interval[8]
|
170
|
+
end
|
171
|
+
it 'is available via a module property and via mixin' do
|
172
|
+
Intervals::a5.should == a5
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
86
176
|
describe 'M6' do
|
87
177
|
it 'is 9 semitones' do
|
88
178
|
M6.should == Interval[9]
|
89
179
|
end
|
90
180
|
it 'is available via a module property and via mixin' do
|
91
|
-
Intervals::
|
181
|
+
Intervals::M6.should == M6
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe 'd7' do
|
186
|
+
it 'is 9 semitones' do
|
187
|
+
d7.should == Interval[9]
|
188
|
+
end
|
189
|
+
it 'is available via a module property and via mixin' do
|
190
|
+
Intervals::d7.should == d7
|
92
191
|
end
|
93
192
|
end
|
94
193
|
|
@@ -97,7 +196,16 @@ describe MTK::Lang::Intervals do
|
|
97
196
|
m7.should == Interval[10]
|
98
197
|
end
|
99
198
|
it 'is available via a module property and via mixin' do
|
100
|
-
Intervals::
|
199
|
+
Intervals::m7.should == m7
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe 'a6' do
|
204
|
+
it 'is 10 semitones' do
|
205
|
+
a6.should == Interval[10]
|
206
|
+
end
|
207
|
+
it 'is available via a module property and via mixin' do
|
208
|
+
Intervals::a6.should == a6
|
101
209
|
end
|
102
210
|
end
|
103
211
|
|
@@ -106,7 +214,16 @@ describe MTK::Lang::Intervals do
|
|
106
214
|
M7.should == Interval[11]
|
107
215
|
end
|
108
216
|
it 'is available via a module property and via mixin' do
|
109
|
-
Intervals::
|
217
|
+
Intervals::M7.should == M7
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
describe 'd8' do
|
222
|
+
it 'is 11 semitones' do
|
223
|
+
d8.should == Interval[11]
|
224
|
+
end
|
225
|
+
it 'is available via a module property and via mixin' do
|
226
|
+
Intervals::d8.should == d8
|
110
227
|
end
|
111
228
|
end
|
112
229
|
|
@@ -115,14 +232,23 @@ describe MTK::Lang::Intervals do
|
|
115
232
|
P8.should == Interval[12]
|
116
233
|
end
|
117
234
|
it 'is available via a module property and via mixin' do
|
118
|
-
Intervals::
|
235
|
+
Intervals::P8.should == P8
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
describe 'a7' do
|
240
|
+
it 'is 12 semitones' do
|
241
|
+
a7.should == Interval[12]
|
242
|
+
end
|
243
|
+
it 'is available via a module property and via mixin' do
|
244
|
+
Intervals::a7.should == a7
|
119
245
|
end
|
120
246
|
end
|
121
247
|
|
122
248
|
|
123
249
|
describe "INTERVALS" do
|
124
250
|
it "contains all intervals constants/pseudo-constants" do
|
125
|
-
Intervals::INTERVALS.should =~
|
251
|
+
Intervals::INTERVALS.should =~ MTK::Core::Interval::ALL_NAMES.map{|name| MTK::Core::Interval.from_name(name) }
|
126
252
|
end
|
127
253
|
|
128
254
|
it "is immutable" do
|
@@ -132,7 +258,7 @@ describe MTK::Lang::Intervals do
|
|
132
258
|
|
133
259
|
describe "INTERVAL_NAMES" do
|
134
260
|
it "contains all intervals constants/pseudo-constant names" do
|
135
|
-
Intervals::INTERVAL_NAMES.should =~
|
261
|
+
Intervals::INTERVAL_NAMES.should =~ MTK::Core::Interval::ALL_NAMES
|
136
262
|
end
|
137
263
|
|
138
264
|
it "is immutable" do
|
@@ -34,31 +34,31 @@ describe MTK::Lang::Parser do
|
|
34
34
|
|
35
35
|
describe ".parse" do
|
36
36
|
it "can parse a single pitch class and play it" do
|
37
|
-
sequencer = MTK::Lang::Parser.parse('
|
37
|
+
sequencer = MTK::Lang::Parser.parse('C')
|
38
38
|
timeline = sequencer.to_timeline
|
39
39
|
timeline.should == MTK::Events::Timeline.from_h({0 => MTK.Note(C4)})
|
40
40
|
end
|
41
41
|
|
42
42
|
context "default (root rule) behavior" do
|
43
43
|
it "parses a bare_sequencer" do
|
44
|
-
sequencer = parse('C:q:mp D4:ff A
|
44
|
+
sequencer = parse('C:q:mp D4:ff A e:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp')
|
45
45
|
sequencer.should be_a Sequencers::Sequencer
|
46
|
-
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(
|
46
|
+
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(e,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
|
47
47
|
end
|
48
48
|
|
49
49
|
it "parses a sequencer" do
|
50
|
-
sequencer = parse('( C:q:mp D4:ff A
|
50
|
+
sequencer = parse('( C:q:mp D4:ff A e:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp )')
|
51
51
|
sequencer.should be_a Sequencers::Sequencer
|
52
|
-
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(
|
52
|
+
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(e,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
|
53
53
|
end
|
54
54
|
|
55
55
|
it "parses a timeline" do
|
56
56
|
parse("
|
57
57
|
{
|
58
58
|
0 => C4:mp:q
|
59
|
-
1 => D4:
|
59
|
+
1 => D4:f:h
|
60
60
|
}
|
61
|
-
").should == MTK::Events::Timeline.from_h({0 => chain(C4,mp,q), 1 => chain(D4,
|
61
|
+
").should == MTK::Events::Timeline.from_h({0 => chain(C4,mp,q), 1 => chain(D4,f,h)})
|
62
62
|
end
|
63
63
|
|
64
64
|
it "parses a chain of sequences" do
|
@@ -68,17 +68,17 @@ describe MTK::Lang::Parser do
|
|
68
68
|
end
|
69
69
|
|
70
70
|
it "parses a chain of choices" do
|
71
|
-
sequencer = parse("<
|
72
|
-
sequencer.patterns.should == [ chain( choice(
|
71
|
+
sequencer = parse("<e|s>:<C|D|E>")
|
72
|
+
sequencer.patterns.should == [ chain( choice(e,s), choice(C,D,E) ) ]
|
73
73
|
end
|
74
74
|
|
75
75
|
it "parses a chain of choices" do
|
76
|
-
sequencer = parse("(<
|
77
|
-
sequencer.patterns.should == [ seq( chain( choice(
|
76
|
+
sequencer = parse("(<e|s>:<C|D|E>)&8")
|
77
|
+
sequencer.patterns.should == [ seq( chain( choice(e,s), choice(C,D,E) ), min_elements:8, max_elements:8 ) ]
|
78
78
|
end
|
79
79
|
|
80
80
|
it "parses the repetition of a basic note property" do
|
81
|
-
sequencer = parse("
|
81
|
+
sequencer = parse("C*4")
|
82
82
|
sequencer.patterns.should == [ seq(C, max_cycles:4) ]
|
83
83
|
end
|
84
84
|
end
|
@@ -104,15 +104,15 @@ describe MTK::Lang::Parser do
|
|
104
104
|
end
|
105
105
|
|
106
106
|
it "parses pitchclass:duration chains" do
|
107
|
-
sequencer = parse('C:q D:q E:
|
107
|
+
sequencer = parse('C:q D:q E:e F:e G:h', :bare_sequencer)
|
108
108
|
sequencer.should be_a Sequencers::Sequencer
|
109
|
-
sequencer.patterns.should == [seq(chain(C,q), chain(D,q), chain(E,
|
109
|
+
sequencer.patterns.should == [seq(chain(C,q), chain(D,q), chain(E,e), chain(F,e), chain(G,h))]
|
110
110
|
end
|
111
111
|
|
112
112
|
it "parses a mix of chained and unchained pitches, pitch classes, durations, and intensities" do
|
113
|
-
sequencer = parse('C:q:mp D4:ff A
|
113
|
+
sequencer = parse('C:q:mp D4:ff A e:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp', :bare_sequencer)
|
114
114
|
sequencer.should be_a Sequencers::Sequencer
|
115
|
-
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(
|
115
|
+
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(e,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
@@ -137,15 +137,15 @@ describe MTK::Lang::Parser do
|
|
137
137
|
end
|
138
138
|
|
139
139
|
it "parses pitchclass:duration chains" do
|
140
|
-
sequencer = parse('{ C:q D:q E:
|
140
|
+
sequencer = parse('{ C:q D:q E:e F:e G:h }', :sequencer)
|
141
141
|
sequencer.should be_a Sequencers::Sequencer
|
142
|
-
sequencer.patterns.should == [seq(chain(C,q), chain(D,q), chain(E,
|
142
|
+
sequencer.patterns.should == [seq(chain(C,q), chain(D,q), chain(E,e), chain(F,e), chain(G,h))]
|
143
143
|
end
|
144
144
|
|
145
145
|
it "parses a mix of chained and unchained pitches, pitch classes, durations, and intensities" do
|
146
|
-
sequencer = parse('{ C:q:mp D4:ff A
|
146
|
+
sequencer = parse('{ C:q:mp D4:ff A e:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp }', :sequencer)
|
147
147
|
sequencer.should be_a Sequencers::Sequencer
|
148
|
-
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(
|
148
|
+
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(e,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
@@ -167,9 +167,9 @@ describe MTK::Lang::Parser do
|
|
167
167
|
parse("
|
168
168
|
{
|
169
169
|
0 => C4:mp:q
|
170
|
-
1 => D4:
|
170
|
+
1 => D4:f:h
|
171
171
|
}
|
172
|
-
", :timeline).should == MTK::Events::Timeline.from_h({0 => chain(C4,mp,q), 1 => chain(D4,
|
172
|
+
", :timeline).should == MTK::Events::Timeline.from_h({0 => chain(C4,mp,q), 1 => chain(D4,f,h)})
|
173
173
|
end
|
174
174
|
|
175
175
|
#it "parses a Timeline containing a chord" do
|
@@ -317,7 +317,7 @@ describe MTK::Lang::Parser do
|
|
317
317
|
end
|
318
318
|
|
319
319
|
it "parses duration sequences" do
|
320
|
-
parse("q
|
320
|
+
parse("q e q. ht", :bare_sequence).should == seq(q, e, q*Rational(1.5), h*Rational(2,3))
|
321
321
|
end
|
322
322
|
end
|
323
323
|
|
@@ -344,7 +344,7 @@ describe MTK::Lang::Parser do
|
|
344
344
|
end
|
345
345
|
|
346
346
|
it "parses duration sequences" do
|
347
|
-
parse("(q
|
347
|
+
parse("(q e q. ht)", :sequence).should == seq(q, e, q*Rational(1.5), h*Rational(2,3))
|
348
348
|
end
|
349
349
|
|
350
350
|
it "parses sequences with a max_cycles modifier" do
|
@@ -424,7 +424,7 @@ describe MTK::Lang::Parser do
|
|
424
424
|
|
425
425
|
context 'element rule' do
|
426
426
|
it "parses the repetition of a basic note property as a sequence with a max_cycles option" do
|
427
|
-
sequence = parse("
|
427
|
+
sequence = parse("C*4", :element)
|
428
428
|
sequence.elements.should == [ C ]
|
429
429
|
sequence.max_cycles.should == 4
|
430
430
|
end
|
@@ -456,6 +456,46 @@ describe MTK::Lang::Parser do
|
|
456
456
|
parse(pitch_class_name, :pitch_class).should == PitchClass[pitch_class_name]
|
457
457
|
end
|
458
458
|
end
|
459
|
+
|
460
|
+
it "doesn't allow a sharp and flat to be applied to the same diatonic pitch class" do
|
461
|
+
for pitch_class_name in %w(A B C D E F G)
|
462
|
+
lambda{ parse(pitch_class_name + '#b', :pitch_class) }.should raise_error
|
463
|
+
lambda{ parse(pitch_class_name + 'b#', :pitch_class) }.should raise_error
|
464
|
+
end
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
|
469
|
+
context 'diatonic_pitch_class rule' do
|
470
|
+
it "parses upper case diatonic pitch classes" do
|
471
|
+
for diatonic_pitch_class_name in %w(A B C D E F G)
|
472
|
+
parse(diatonic_pitch_class_name, :diatonic_pitch_class).should == PitchClass[diatonic_pitch_class_name]
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
|
478
|
+
context 'accidental rule' do
|
479
|
+
it "parses a single flat 'b'" do
|
480
|
+
lambda{ parse('b', :accidental) }.should_not raise_error
|
481
|
+
end
|
482
|
+
|
483
|
+
it "parses a double flat 'bb'" do
|
484
|
+
lambda{ parse('bb', :accidental) }.should_not raise_error
|
485
|
+
end
|
486
|
+
|
487
|
+
it "parses a single sharp '#'" do
|
488
|
+
lambda{ parse('#', :accidental) }.should_not raise_error
|
489
|
+
end
|
490
|
+
|
491
|
+
it "parses a double sharp '##'" do
|
492
|
+
lambda{ parse('##', :accidental) }.should_not raise_error
|
493
|
+
end
|
494
|
+
|
495
|
+
it "doesn't parse trip flats or sharps" do
|
496
|
+
lambda{ parse('bbb', :accidental) }.should raise_error
|
497
|
+
lambda{ parse('###', :accidental) }.should raise_error
|
498
|
+
end
|
459
499
|
end
|
460
500
|
|
461
501
|
|