mtk 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. data/.yardopts +3 -2
  2. data/DEVELOPMENT_NOTES.md +114 -0
  3. data/INTRO.md +64 -8
  4. data/LICENSE.txt +1 -1
  5. data/README.md +31 -102
  6. data/Rakefile +56 -18
  7. data/bin/mtk +215 -0
  8. data/examples/crescendo.rb +5 -5
  9. data/examples/drum_pattern1.rb +23 -0
  10. data/examples/dynamic_pattern.rb +8 -11
  11. data/examples/gets_and_play.rb +26 -0
  12. data/examples/notation.rb +22 -0
  13. data/examples/play_midi.rb +8 -10
  14. data/examples/random_tone_row.rb +2 -2
  15. data/examples/syntax_to_midi.rb +28 -0
  16. data/examples/test_output.rb +8 -0
  17. data/examples/tone_row_melody.rb +6 -6
  18. data/lib/mtk.rb +52 -40
  19. data/lib/mtk/chord.rb +55 -0
  20. data/lib/mtk/constants/durations.rb +57 -0
  21. data/lib/mtk/constants/intensities.rb +61 -0
  22. data/lib/mtk/constants/intervals.rb +73 -0
  23. data/lib/mtk/constants/pitch_classes.rb +29 -0
  24. data/lib/mtk/constants/pitches.rb +52 -0
  25. data/lib/mtk/duration.rb +211 -0
  26. data/lib/mtk/events/event.rb +119 -0
  27. data/lib/mtk/events/note.rb +112 -0
  28. data/lib/mtk/events/parameter.rb +54 -0
  29. data/lib/mtk/helpers/collection.rb +164 -0
  30. data/lib/mtk/helpers/convert.rb +36 -0
  31. data/lib/mtk/helpers/lilypond.rb +162 -0
  32. data/lib/mtk/helpers/output_selector.rb +67 -0
  33. data/lib/mtk/helpers/pitch_collection.rb +23 -0
  34. data/lib/mtk/helpers/pseudo_constants.rb +26 -0
  35. data/lib/mtk/intensity.rb +156 -0
  36. data/lib/mtk/interval.rb +155 -0
  37. data/lib/mtk/lang/mtk_grammar.citrus +190 -13
  38. data/lib/mtk/lang/parser.rb +29 -0
  39. data/lib/mtk/melody.rb +94 -0
  40. data/lib/mtk/midi/dls_synth_device.rb +144 -0
  41. data/lib/mtk/midi/dls_synth_output.rb +62 -0
  42. data/lib/mtk/midi/file.rb +67 -32
  43. data/lib/mtk/midi/input.rb +97 -0
  44. data/lib/mtk/midi/jsound_input.rb +36 -17
  45. data/lib/mtk/midi/jsound_output.rb +48 -46
  46. data/lib/mtk/midi/output.rb +195 -0
  47. data/lib/mtk/midi/unimidi_input.rb +117 -0
  48. data/lib/mtk/midi/unimidi_output.rb +121 -0
  49. data/lib/mtk/{_numeric_extensions.rb → numeric_extensions.rb} +12 -0
  50. data/lib/mtk/patterns/chain.rb +49 -0
  51. data/lib/mtk/{pattern → patterns}/choice.rb +14 -8
  52. data/lib/mtk/patterns/cycle.rb +18 -0
  53. data/lib/mtk/patterns/for_each.rb +71 -0
  54. data/lib/mtk/patterns/function.rb +39 -0
  55. data/lib/mtk/{pattern → patterns}/lines.rb +11 -17
  56. data/lib/mtk/{pattern → patterns}/palindrome.rb +11 -8
  57. data/lib/mtk/patterns/pattern.rb +171 -0
  58. data/lib/mtk/patterns/sequence.rb +20 -0
  59. data/lib/mtk/pitch.rb +7 -6
  60. data/lib/mtk/pitch_class.rb +124 -46
  61. data/lib/mtk/pitch_class_set.rb +58 -35
  62. data/lib/mtk/sequencers/event_builder.rb +131 -0
  63. data/lib/mtk/sequencers/legato_sequencer.rb +24 -0
  64. data/lib/mtk/sequencers/rhythmic_sequencer.rb +28 -0
  65. data/lib/mtk/{sequencer/abstract_sequencer.rb → sequencers/sequencer.rb} +37 -11
  66. data/lib/mtk/{sequencer → sequencers}/step_sequencer.rb +4 -4
  67. data/lib/mtk/timeline.rb +39 -22
  68. data/lib/mtk/variable.rb +32 -0
  69. data/spec/mtk/chord_spec.rb +83 -0
  70. data/spec/mtk/{_constants → constants}/durations_spec.rb +12 -41
  71. data/spec/mtk/{_constants → constants}/intensities_spec.rb +13 -37
  72. data/spec/mtk/{_constants → constants}/intervals_spec.rb +14 -32
  73. data/spec/mtk/{_constants → constants}/pitch_classes_spec.rb +8 -4
  74. data/spec/mtk/{_constants → constants}/pitches_spec.rb +5 -1
  75. data/spec/mtk/duration_spec.rb +372 -0
  76. data/spec/mtk/events/event_spec.rb +234 -0
  77. data/spec/mtk/events/note_spec.rb +174 -0
  78. data/spec/mtk/events/parameter_spec.rb +220 -0
  79. data/spec/mtk/{helper → helpers}/collection_spec.rb +86 -3
  80. data/spec/mtk/{helper → helpers}/pseudo_constants_spec.rb +2 -2
  81. data/spec/mtk/intensity_spec.rb +289 -0
  82. data/spec/mtk/interval_spec.rb +265 -0
  83. data/spec/mtk/lang/parser_spec.rb +597 -0
  84. data/spec/mtk/melody_spec.rb +223 -0
  85. data/spec/mtk/midi/file_spec.rb +16 -16
  86. data/spec/mtk/midi/jsound_input_spec.rb +11 -0
  87. data/spec/mtk/midi/jsound_output_spec.rb +11 -0
  88. data/spec/mtk/midi/output_spec.rb +102 -0
  89. data/spec/mtk/midi/unimidi_input_spec.rb +11 -0
  90. data/spec/mtk/midi/unimidi_output_spec.rb +11 -0
  91. data/spec/mtk/{_numeric_extensions_spec.rb → numeric_extensions_spec.rb} +1 -0
  92. data/spec/mtk/patterns/chain_spec.rb +110 -0
  93. data/spec/mtk/{pattern → patterns}/choice_spec.rb +20 -30
  94. data/spec/mtk/{pattern → patterns}/cycle_spec.rb +25 -35
  95. data/spec/mtk/patterns/for_each_spec.rb +136 -0
  96. data/spec/mtk/{pattern → patterns}/function_spec.rb +17 -30
  97. data/spec/mtk/{pattern → patterns}/lines_spec.rb +11 -27
  98. data/spec/mtk/{pattern → patterns}/palindrome_spec.rb +13 -29
  99. data/spec/mtk/patterns/pattern_spec.rb +132 -0
  100. data/spec/mtk/patterns/sequence_spec.rb +203 -0
  101. data/spec/mtk/pitch_class_set_spec.rb +23 -21
  102. data/spec/mtk/pitch_class_spec.rb +151 -39
  103. data/spec/mtk/pitch_spec.rb +22 -1
  104. data/spec/mtk/sequencers/event_builder_spec.rb +245 -0
  105. data/spec/mtk/sequencers/legato_sequencer_spec.rb +45 -0
  106. data/spec/mtk/sequencers/rhythmic_sequencer_spec.rb +84 -0
  107. data/spec/mtk/sequencers/sequencer_spec.rb +215 -0
  108. data/spec/mtk/{sequencer → sequencers}/step_sequencer_spec.rb +35 -13
  109. data/spec/mtk/timeline_spec.rb +109 -16
  110. data/spec/mtk/variable_spec.rb +52 -0
  111. data/spec/spec_coverage.rb +2 -0
  112. data/spec/spec_helper.rb +3 -0
  113. metadata +188 -91
  114. data/lib/mtk/_constants/durations.rb +0 -80
  115. data/lib/mtk/_constants/intensities.rb +0 -81
  116. data/lib/mtk/_constants/intervals.rb +0 -85
  117. data/lib/mtk/_constants/pitch_classes.rb +0 -35
  118. data/lib/mtk/_constants/pitches.rb +0 -49
  119. data/lib/mtk/event.rb +0 -70
  120. data/lib/mtk/helper/collection.rb +0 -114
  121. data/lib/mtk/helper/event_builder.rb +0 -85
  122. data/lib/mtk/helper/pseudo_constants.rb +0 -26
  123. data/lib/mtk/lang/grammar.rb +0 -17
  124. data/lib/mtk/note.rb +0 -63
  125. data/lib/mtk/pattern/abstract_pattern.rb +0 -132
  126. data/lib/mtk/pattern/cycle.rb +0 -51
  127. data/lib/mtk/pattern/enumerator.rb +0 -26
  128. data/lib/mtk/pattern/function.rb +0 -46
  129. data/lib/mtk/pattern/sequence.rb +0 -30
  130. data/lib/mtk/pitch_set.rb +0 -84
  131. data/lib/mtk/sequencer/rhythmic_sequencer.rb +0 -29
  132. data/lib/mtk/transform/invertible.rb +0 -15
  133. data/lib/mtk/transform/mappable.rb +0 -18
  134. data/lib/mtk/transform/set_theory_operations.rb +0 -34
  135. data/lib/mtk/transform/transposable.rb +0 -14
  136. data/spec/mtk/event_spec.rb +0 -139
  137. data/spec/mtk/helper/event_builder_spec.rb +0 -92
  138. data/spec/mtk/lang/grammar_spec.rb +0 -100
  139. data/spec/mtk/note_spec.rb +0 -115
  140. data/spec/mtk/pattern/abstract_pattern_spec.rb +0 -45
  141. data/spec/mtk/pattern/note_cycle_spec.rb.bak +0 -116
  142. data/spec/mtk/pattern/pitch_cycle_spec.rb.bak +0 -47
  143. data/spec/mtk/pattern/pitch_sequence_spec.rb.bak +0 -37
  144. data/spec/mtk/pattern/sequence_spec.rb +0 -151
  145. data/spec/mtk/pitch_set_spec.rb +0 -198
  146. data/spec/mtk/sequencer/abstract_sequencer_spec.rb +0 -159
  147. data/spec/mtk/sequencer/rhythmic_sequencer_spec.rb +0 -49
@@ -0,0 +1,597 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Lang::Parser do
4
+
5
+ def chain *args
6
+ Patterns.Chain *args
7
+ end
8
+
9
+ def seq *args
10
+ Patterns.Sequence *args
11
+ end
12
+
13
+ def cycle *args
14
+ Patterns.Cycle *args
15
+ end
16
+
17
+ def choice *args
18
+ Patterns.Choice *args
19
+ end
20
+
21
+ def foreach *args
22
+ Patterns.ForEach *args
23
+ end
24
+
25
+ def var(name)
26
+ ::MTK::Variable.new(name)
27
+ end
28
+
29
+
30
+ def parse(*args)
31
+ MTK::Lang::Parser.parse(*args)
32
+ end
33
+
34
+
35
+ describe ".parse" do
36
+ context "default (root rule) behavior" do
37
+ it "parses a bare_sequencer" do
38
+ sequencer = parse('C:q:mp D4:ff A i:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp')
39
+ sequencer.should be_a Sequencers::Sequencer
40
+ sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(i,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
41
+ end
42
+
43
+ it "parses a sequencer" do
44
+ sequencer = parse('( C:q:mp D4:ff A i:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp )')
45
+ sequencer.should be_a Sequencers::Sequencer
46
+ sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(i,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
47
+ end
48
+
49
+ it "parses a timeline" do
50
+ parse("
51
+ {
52
+ 0 => C4:mp:q
53
+ 1 => D4:o:h
54
+ }
55
+ ").should == Timeline.from_hash({0 => chain(C4,mp,q), 1 => chain(D4,o,h)})
56
+ end
57
+
58
+ it "parses a chain of sequences" do
59
+ sequencer = parse("(C D E F G):(mp mf ff):(q h w)")
60
+ sequencer.should be_a Sequencers::Sequencer
61
+ sequencer.patterns.should == [ chain( seq(C,D,E,F,G), seq(mp,mf,ff), seq(q,h,w) ) ]
62
+ end
63
+
64
+ it "parses a chain of choices" do
65
+ sequencer = parse("<i|s>:<c|d|e>")
66
+ sequencer.patterns.should == [ chain( choice(i,s), choice(C,D,E) ) ]
67
+ end
68
+
69
+ it "parses a chain of choices" do
70
+ sequencer = parse("(<i|s>:<c|d|e>)&8")
71
+ sequencer.patterns.should == [ seq( chain( choice(i,s), choice(C,D,E) ), min_elements:8, max_elements:8 ) ]
72
+ end
73
+
74
+ it "parses the repetition of a basic note property" do
75
+ sequencer = parse("c*4")
76
+ sequencer.patterns.should == [ seq(C, max_cycles:4) ]
77
+ end
78
+ end
79
+
80
+
81
+ context 'bare_sequencer rule (no curly braces)' do
82
+ it "parses a sequence of pitch classes" do
83
+ sequencer = parse('C D E F G', :bare_sequencer)
84
+ sequencer.should be_a Sequencers::Sequencer
85
+ sequencer.patterns.should == [seq(C, D, E, F, G)]
86
+ end
87
+
88
+ it "parses a sequence of pitches" do
89
+ sequencer = parse('C4 D4 E4 F4 G3', :bare_sequencer)
90
+ sequencer.should be_a Sequencers::Sequencer
91
+ sequencer.patterns.should == [seq(C4, D4, E4, F4, G3)]
92
+ end
93
+
94
+ it "parses a sequence of pitch classes + pitches" do
95
+ sequencer = parse('C4 D E3 F G5', :bare_sequencer)
96
+ sequencer.should be_a Sequencers::Sequencer
97
+ sequencer.patterns.should == [seq(C4, D, E3, F, G5)]
98
+ end
99
+
100
+ it "parses pitchclass:duration chains" do
101
+ sequencer = parse('C:q D:q E:i F:i G:h', :bare_sequencer)
102
+ sequencer.should be_a Sequencers::Sequencer
103
+ sequencer.patterns.should == [seq(chain(C,q), chain(D,q), chain(E,i), chain(F,i), chain(G,h))]
104
+ end
105
+
106
+ it "parses a mix of chained and unchained pitches, pitch classes, durations, and intensities" do
107
+ sequencer = parse('C:q:mp D4:ff A i:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp', :bare_sequencer)
108
+ sequencer.should be_a Sequencers::Sequencer
109
+ sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(i,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
110
+ end
111
+ end
112
+
113
+
114
+ context 'sequencer rule' do
115
+ it "parses a sequence of pitch classes" do
116
+ sequencer = parse('{C D E F G}', :sequencer)
117
+ sequencer.should be_a Sequencers::Sequencer
118
+ sequencer.patterns.should == [seq(C, D, E, F, G)]
119
+ end
120
+
121
+ it "parses a sequence of pitches" do
122
+ sequencer = parse('{ C4 D4 E4 F4 G3}', :sequencer)
123
+ sequencer.should be_a Sequencers::Sequencer
124
+ sequencer.patterns.should == [seq(C4, D4, E4, F4, G3)]
125
+ end
126
+
127
+ it "parses a sequence of pitch classes + pitches" do
128
+ sequencer = parse('{C4 D E3 F G5 }', :sequencer)
129
+ sequencer.should be_a Sequencers::Sequencer
130
+ sequencer.patterns.should == [seq(C4, D, E3, F, G5)]
131
+ end
132
+
133
+ it "parses pitchclass:duration chains" do
134
+ sequencer = parse('{ C:q D:q E:i F:i G:h }', :sequencer)
135
+ sequencer.should be_a Sequencers::Sequencer
136
+ sequencer.patterns.should == [seq(chain(C,q), chain(D,q), chain(E,i), chain(F,i), chain(G,h))]
137
+ end
138
+
139
+ it "parses a mix of chained and unchained pitches, pitch classes, durations, and intensities" do
140
+ sequencer = parse('{ C:q:mp D4:ff A i:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp }', :sequencer)
141
+ sequencer.should be_a Sequencers::Sequencer
142
+ sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(i,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
143
+ end
144
+ end
145
+
146
+
147
+ context "timeline rule" do
148
+ it "parses a very simple Timeline" do
149
+ parse("{0 => C}", :timeline).should == Timeline.from_hash({0 => seq(C)})
150
+ end
151
+
152
+ it "parses a Timeline with one entry" do
153
+ parse("
154
+ {
155
+ 0 => C4:mp:q
156
+ }
157
+ ", :timeline).should == Timeline.from_hash({0 => chain(C4,mp,q)})
158
+ end
159
+
160
+ it "parses a Timeline with multiple entries" do
161
+ parse("
162
+ {
163
+ 0 => C4:mp:q
164
+ 1 => D4:o:h
165
+ }
166
+ ", :timeline).should == Timeline.from_hash({0 => chain(C4,mp,q), 1 => chain(D4,o,h)})
167
+ end
168
+
169
+ #it "parses a Timeline containing a chord" do
170
+ # parse("
171
+ # {
172
+ # 0 => [C4 E4 G4]:fff:w
173
+ # }
174
+ # ", :timeline).should == Timeline.from_hash({0 => chain(Chord(C4,E4,G4),fff,w)})
175
+ #end
176
+ end
177
+
178
+
179
+ context 'timepoint rule' do
180
+ it 'should evaluate a number followed by "=>" as the numerical value' do
181
+ parse('42 => ', :timepoint).should == 42
182
+ end
183
+ end
184
+
185
+
186
+ context "pattern rule" do
187
+ it 'parses bare sequences' do
188
+ parse("C4 D4 E4", :pattern).should == seq(C4, D4, E4)
189
+ end
190
+
191
+ it 'parses sequences' do
192
+ parse("(C4 D4 E4)", :pattern).should == seq(C4, D4, E4)
193
+ end
194
+
195
+ it 'parses chains and wraps them in a Sequence' do
196
+ # The wrapping behavior isn't completely desirable,
197
+ # but I'm having trouble making the grammar do exactly what I want in all scenarios, so this is a compromise
198
+ parse("C4:q:ff", :pattern).should == chain(C4, q, ff)
199
+ end
200
+
201
+ it "parses chains of sequencers" do
202
+ parse("(C D E F G):(mp mf ff):(q h w)", :pattern).should == chain( seq(C,D,E,F,G), seq(mp,mf,ff), seq(q,h,w) )
203
+ end
204
+
205
+ it "parses chains of sequencers with modifiers" do
206
+ pattern = parse('(C D E F)*3:(q h w)&2', :pattern)
207
+ pattern.should == chain( cycle(C,D,E,F, max_cycles:3), seq(q,h,w, min_elements:2, max_elements:2))
208
+ pattern.next
209
+ pattern.next
210
+ lambda{ pattern.next }.should raise_error StopIteration
211
+ end
212
+
213
+ it "parses chains of sequencers with modifiers" do
214
+ pattern = parse('(C D E F G):(q h w)&3', :pattern)
215
+ pattern.should == chain( seq(C,D,E,F,G), seq(q,h,w, min_elements:3, max_elements:3))
216
+ pattern.next
217
+ pattern.next
218
+ pattern.next
219
+ lambda{ pattern.next }.should raise_error StopIteration
220
+ end
221
+
222
+ it "ensures a single element is wrapped in a Pattern" do
223
+ parse("C", :pattern).should be_a ::MTK::Patterns::Pattern
224
+ end
225
+
226
+ it "parse 'recursively' nested constructs" do
227
+ parse('C <D|E <F|G F>> | A B:(q h <q|h:mp h:<mf|ff fff>>)', :pattern).should ==
228
+ choice(
229
+ seq(C, choice(D, seq(E, choice(F,seq(G,F))))),
230
+ seq(A, chain(B,seq(q, h, choice(q,seq(chain(h,mp),chain(h,choice(mf,seq(ff,fff))))))))
231
+ )
232
+ end
233
+ end
234
+
235
+
236
+ context 'bare_choice rule (no parentheses)' do
237
+ it "parses a choice of simple elements" do
238
+ parse('C | D | E', :bare_choice).should == choice(C,D,E)
239
+ end
240
+
241
+ it 'parses a choice of sequences (parentheses implied)' do
242
+ parse('C D E | D E F | E F', :bare_choice).should == choice(seq(C,D,E), seq(D,E,F), seq(E,F))
243
+ end
244
+
245
+ it 'parses a choice of sequences and simple elements' do
246
+ parse('C D E | G | E F | A', :bare_choice).should == choice(seq(C,D,E), G, seq(E,F), A)
247
+ end
248
+
249
+ it "doesn't require spaces around the pipe characters"do
250
+ parse('C D E|G|E F', :bare_choice).should == choice(seq(C,D,E), G, seq(E,F))
251
+ end
252
+
253
+ it 'parses choices containing chains' do
254
+ parse('C D:q | G | A:mp|E:fff:h F:s', :bare_choice).should ==
255
+ choice( seq(C,chain(D,q)), G, chain(A,mp), seq(chain(E,fff,h),chain(F,s)) )
256
+ end
257
+
258
+ it 'parses choices containing nested choices' do
259
+ parse('C <D|E> | F | G', :bare_choice).should == choice(seq(C,choice(D,E)), F, G)
260
+ end
261
+ end
262
+
263
+
264
+ context 'choice rule' do
265
+ it "parses a choice of simple elements" do
266
+ parse('<C | D | E>', :bare_choice).should == choice(C,D,E)
267
+ end
268
+
269
+ it 'parses a choice of sequences (parentheses implied)' do
270
+ parse('< C D E | D E F | E F>', :bare_choice).should == choice(seq(C,D,E), seq(D,E,F), seq(E,F))
271
+ end
272
+
273
+ it 'parses a choice of sequences and simple elements' do
274
+ parse('<C D E | G | E F | A >', :bare_choice).should == choice(seq(C,D,E), G, seq(E,F), A)
275
+ end
276
+
277
+ it "doesn't require spaces around the pipe characters"do
278
+ parse('< C D E|G|E F >', :bare_choice).should == choice(seq(C,D,E), G, seq(E,F))
279
+ end
280
+
281
+ it 'parses choices containing chains' do
282
+ parse('<C D:q | G | A:mp|E:fff:h F:s>', :bare_choice).should ==
283
+ choice( seq(C,chain(D,q)), G, chain(A,mp), seq(chain(E,fff,h),chain(F,s)) )
284
+ end
285
+
286
+ it 'parses choices containing nested choices' do
287
+ parse('< C <D|E> | F | G >', :bare_choice).should == choice(seq(C,choice(D,E)), F, G)
288
+ end
289
+ end
290
+
291
+
292
+ context 'bare_sequence rule (no parentheses)' do
293
+ it "parses pitch sequences" do
294
+ parse("C4 D4 E4", :bare_sequence).should == seq(C4, D4, E4)
295
+ end
296
+
297
+ #it "parses pitch sequences with chords" do
298
+ # parse("C4 [D4 E4]", :bare_sequence).should == seq( C4, Chord(D4,E4) )
299
+ #end
300
+
301
+ it "parses pitch sequences with pitch classes" do
302
+ parse("C4 D E4", :bare_sequence).should == seq( C4, D, E4 )
303
+ end
304
+
305
+ it "parses pitch sequences with intervals" do
306
+ parse("C4 m2", :bare_sequence).should == seq( C4, m2 )
307
+ end
308
+
309
+ it "parses intensity sequences" do
310
+ parse("ppp mf mp ff", :bare_sequence).should == seq(ppp, mf, mp, ff)
311
+ end
312
+
313
+ it "parses duration sequences" do
314
+ parse("q i q. ht", :bare_sequence).should == seq(q, i, q*Rational(1.5), h*Rational(2,3))
315
+ end
316
+ end
317
+
318
+
319
+ context "sequence rule" do
320
+ it "parses pitch sequences" do
321
+ parse("(C4 D4 E4)", :sequence).should == seq(C4, D4, E4)
322
+ end
323
+
324
+ #it "parses pitch sequences with chords" do
325
+ # parse("( C4 [D4 E4])", :sequence).should == seq( C4, Chord(D4,E4) )
326
+ #end
327
+
328
+ it "parses pitch sequences with pitch classes" do
329
+ parse("(C4 D E4 )", :sequence).should == seq( C4, D, E4 )
330
+ end
331
+
332
+ it "parses pitch sequences with intervals" do
333
+ parse("( C4 m2 )", :sequence).should == seq( C4, m2 )
334
+ end
335
+
336
+ it "parses intensity sequences" do
337
+ parse("( ppp mf mp ff )", :sequence).should == seq(ppp, mf, mp, ff)
338
+ end
339
+
340
+ it "parses duration sequences" do
341
+ parse("(q i q. ht)", :sequence).should == seq(q, i, q*Rational(1.5), h*Rational(2,3))
342
+ end
343
+
344
+ it "parses sequences with a max_cycles modifier" do
345
+ sequence = parse("(C D)*2", :sequence)
346
+ sequence.should == Patterns.Cycle(C,D, max_cycles: 2)
347
+ end
348
+
349
+ it "parses sequences with a min+max_elements modifier" do
350
+ sequence = parse("(C D E)&2", :sequence)
351
+ sequence.should == Patterns.Cycle(C,D,E, min_elements: 2, max_elements: 2)
352
+ end
353
+ end
354
+
355
+
356
+ context "foreach rule" do
357
+ it "parses a for each pattern with 2 subpatterns" do
358
+ foreach = parse('(C D)@(E F)', :foreach)
359
+ foreach.should == Patterns.ForEach(seq(C,D),seq(E,F))
360
+ end
361
+
362
+ it "parses a for each pattern with 3 subpatterns" do
363
+ foreach = parse('(C D)@(E F)@(G A B)', :foreach)
364
+ foreach.should == Patterns.ForEach(seq(C,D),seq(E,F),seq(G,A,B))
365
+ end
366
+
367
+ it "parses a for each pattern with '$' variables" do
368
+ foreach = parse('(C D)@(E F)@($ $$)', :foreach)
369
+ foreach.should == Patterns.ForEach(seq(C,D),seq(E,F),seq(var('$'),var('$$')))
370
+ end
371
+ end
372
+
373
+
374
+ context "chain rule" do
375
+ it "parses a basic chain of note properties" do
376
+ parse("G4:h.:ff", :chain).should == Patterns.Chain(G4,h+q,ff)
377
+ end
378
+
379
+ it "parses a chain of for each patterns" do
380
+ parse('(C D)@(E F):(G A)@(B C)', :chain).should == chain( foreach(seq(C,D),seq(E,F)), foreach(seq(G,A),seq(B,C)) )
381
+ end
382
+
383
+ it "parses chains of elements with max_cycles" do
384
+ parse('C*3:mp*4:q*5', :chain).should == chain( seq(C,max_cycles:3), seq(mp,max_cycles:4), seq(q,max_cycles:5))
385
+ end
386
+ end
387
+
388
+
389
+ context "chainable rule" do
390
+ it "parses a pitch" do
391
+ parse("C4", :chainable).should == C4
392
+ end
393
+
394
+ #it "parses a chord" do
395
+ # parse("[C4 D4]", :chainable).should == Chord(C4,D4)
396
+ #end
397
+
398
+ it "parses a pitch class" do
399
+ parse("C", :chainable).should == C
400
+ end
401
+
402
+ it "parses intervals" do
403
+ parse("m2", :chainable).should == m2
404
+ end
405
+
406
+ it "parses durations" do
407
+ parse("h", :chainable).should == h
408
+ end
409
+
410
+ it "parses rests" do
411
+ parse("-h", :chainable).should == -h
412
+ end
413
+
414
+ it "parses intensities" do
415
+ parse("ff", :chainable).should == ff
416
+ end
417
+ end
418
+
419
+ context 'element rule' do
420
+ it "parses the repetition of a basic note property as a sequence with a max_cycles option" do
421
+ sequence = parse("c*4", :element)
422
+ sequence.elements.should == [ C ]
423
+ sequence.max_cycles.should == 4
424
+ end
425
+ end
426
+
427
+
428
+ #context 'chord rule' do
429
+ # it "parses chords" do
430
+ # parse("[C4 E4 G4]", :chord).should == Chord(C4,E4,G4)
431
+ # end
432
+ #end
433
+
434
+
435
+ context 'pitch rule' do
436
+ it "parses pitches" do
437
+ for pitch_class_name in PitchClass::VALID_NAMES
438
+ pc = PitchClass[pitch_class_name]
439
+ for octave in -1..9
440
+ parse("#{pitch_class_name}#{octave}", :pitch).should == Pitch[pc,octave]
441
+ end
442
+ end
443
+ end
444
+ end
445
+
446
+
447
+ context 'pitch_class rule' do
448
+ it "parses pitch classes" do
449
+ for pitch_class_name in PitchClass::VALID_NAMES
450
+ parse(pitch_class_name, :pitch_class).should == PitchClass[pitch_class_name]
451
+ end
452
+ end
453
+ end
454
+
455
+
456
+ context 'interval rule' do
457
+ it "parses intervals" do
458
+ for interval_name in Interval::ALL_NAMES
459
+ parse(interval_name, :interval).should == Interval(interval_name)
460
+ end
461
+ end
462
+ end
463
+
464
+
465
+ context 'intensity rule' do
466
+ it "parses intensities" do
467
+ for intensity_name in Intensity::NAMES
468
+ parse(intensity_name, :intensity).should == Intensity(intensity_name)
469
+ end
470
+ end
471
+
472
+ it "parses intensities with + and - modifiers" do
473
+ for intensity_name in Intensity::NAMES
474
+ name = "#{intensity_name}+"
475
+ parse(name, :intensity).should == Intensity(name)
476
+ name = "#{intensity_name}-"
477
+ parse(name, :intensity).should == Intensity(name)
478
+ end
479
+ end
480
+ end
481
+
482
+
483
+ context 'duration rule' do
484
+ it "parses durations" do
485
+ for duration in Durations::DURATION_NAMES
486
+ parse(duration, :duration).should == Duration(duration)
487
+ end
488
+ end
489
+
490
+ it "parses durations with . and t modifiers" do
491
+ for duration in Durations::DURATION_NAMES
492
+ name = "#{duration}."
493
+ parse(name, :duration).should == Duration(name)
494
+ name = "#{duration}t"
495
+ parse(name, :duration).should == Duration(name)
496
+ name = "#{duration}..t.t"
497
+ parse(name, :duration).should == Duration(name)
498
+ end
499
+ end
500
+
501
+ it "parses durations with - modifier (it parses rests)" do
502
+ for duration in Durations::DURATION_NAMES
503
+ parse("-#{duration}", :duration).should == -1 * Duration(duration)
504
+ end
505
+ end
506
+
507
+ it "parses durations with integer multipliers" do
508
+ Durations::DURATION_NAMES.each_with_index do |duration, index|
509
+ multiplier = index+5
510
+ parse("#{multiplier}#{duration}", :duration).should == multiplier * Duration(duration)
511
+ end
512
+ end
513
+
514
+ it "parses durations with float multipliers" do
515
+ Durations::DURATION_NAMES.each_with_index do |duration, index|
516
+ multiplier = (index+1)*1.123
517
+ parse("#{multiplier}#{duration}", :duration).should == multiplier * Duration(duration)
518
+ end
519
+ end
520
+
521
+ it "parses durations with float multipliers" do
522
+ Durations::DURATION_NAMES.each_with_index do |duration, index|
523
+ multiplier = Rational(index+1, index+2)
524
+ parse("#{multiplier}#{duration}", :duration).should == multiplier * Duration(duration)
525
+ end
526
+ end
527
+
528
+ end
529
+
530
+
531
+ context 'variable rule' do
532
+ it "parses the '$' variable" do
533
+ parse('$', :variable).should == var('$')
534
+ end
535
+
536
+ it "parses the '$$', '$$$', etc variables" do
537
+ parse('$$', :variable).should == var('$$')
538
+ parse('$$$', :variable).should == var('$$$')
539
+ parse('$$$$', :variable).should == var('$$$$')
540
+ end
541
+ end
542
+
543
+
544
+ context 'number rule' do
545
+ it "parses floats as numbers" do
546
+ parse("1.23", :number).should == 1.23
547
+ end
548
+
549
+ it "parses rationals as numbers" do
550
+ parse("12/34", :number).should == Rational(12,34)
551
+ end
552
+
553
+ it "parses ints as numbers" do
554
+ parse("123", :number).should == 123
555
+ end
556
+
557
+ end
558
+
559
+ context 'float rule' do
560
+ it "parses floats" do
561
+ parse("1.23", :float).should == 1.23
562
+ end
563
+
564
+ it "parses negative floats" do
565
+ parse("-1.23", :float).should == -1.23
566
+ end
567
+ end
568
+
569
+ context 'rational rule' do
570
+ it 'parses rationals' do
571
+ parse('12/13', :rational).should == Rational(12,13)
572
+ end
573
+
574
+ it 'parses negative rationals' do
575
+ parse('-12/13', :rational).should == -Rational(12,13)
576
+ end
577
+ end
578
+
579
+
580
+ context 'int rule' do
581
+ it "parses ints" do
582
+ parse("123", :int).should == 123
583
+ end
584
+
585
+ it "parses negative ints" do
586
+ parse("-123", :int).should == -123
587
+ end
588
+ end
589
+
590
+
591
+ context 'space rule' do
592
+ it "returns nil as the value for whitespace" do
593
+ parse(" \t\n", :space).should == nil
594
+ end
595
+ end
596
+ end
597
+ end