jmtk 0.0.3.3-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.
Files changed (110) hide show
  1. data/.yardopts +10 -0
  2. data/DEVELOPMENT_NOTES.md +115 -0
  3. data/INTRO.md +129 -0
  4. data/LICENSE.txt +27 -0
  5. data/README.md +50 -0
  6. data/Rakefile +102 -0
  7. data/bin/jmtk +250 -0
  8. data/bin/mtk +250 -0
  9. data/examples/crescendo.rb +20 -0
  10. data/examples/drum_pattern.rb +23 -0
  11. data/examples/dynamic_pattern.rb +36 -0
  12. data/examples/gets_and_play.rb +27 -0
  13. data/examples/notation.rb +22 -0
  14. data/examples/play_midi.rb +17 -0
  15. data/examples/print_midi.rb +13 -0
  16. data/examples/random_tone_row.rb +18 -0
  17. data/examples/syntax_to_midi.rb +28 -0
  18. data/examples/test_output.rb +7 -0
  19. data/examples/tone_row_melody.rb +23 -0
  20. data/lib/mtk.rb +76 -0
  21. data/lib/mtk/core/duration.rb +213 -0
  22. data/lib/mtk/core/intensity.rb +158 -0
  23. data/lib/mtk/core/interval.rb +157 -0
  24. data/lib/mtk/core/pitch.rb +154 -0
  25. data/lib/mtk/core/pitch_class.rb +194 -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/events/timeline.rb +232 -0
  30. data/lib/mtk/groups/chord.rb +56 -0
  31. data/lib/mtk/groups/collection.rb +196 -0
  32. data/lib/mtk/groups/melody.rb +96 -0
  33. data/lib/mtk/groups/pitch_class_set.rb +163 -0
  34. data/lib/mtk/groups/pitch_collection.rb +23 -0
  35. data/lib/mtk/io/dls_synth_device.rb +146 -0
  36. data/lib/mtk/io/dls_synth_output.rb +62 -0
  37. data/lib/mtk/io/jsound_input.rb +87 -0
  38. data/lib/mtk/io/jsound_output.rb +82 -0
  39. data/lib/mtk/io/midi_file.rb +209 -0
  40. data/lib/mtk/io/midi_input.rb +97 -0
  41. data/lib/mtk/io/midi_output.rb +195 -0
  42. data/lib/mtk/io/notation.rb +162 -0
  43. data/lib/mtk/io/unimidi_input.rb +117 -0
  44. data/lib/mtk/io/unimidi_output.rb +140 -0
  45. data/lib/mtk/lang/durations.rb +57 -0
  46. data/lib/mtk/lang/intensities.rb +61 -0
  47. data/lib/mtk/lang/intervals.rb +73 -0
  48. data/lib/mtk/lang/mtk_grammar.citrus +237 -0
  49. data/lib/mtk/lang/parser.rb +29 -0
  50. data/lib/mtk/lang/pitch_classes.rb +29 -0
  51. data/lib/mtk/lang/pitches.rb +52 -0
  52. data/lib/mtk/lang/pseudo_constants.rb +26 -0
  53. data/lib/mtk/lang/variable.rb +32 -0
  54. data/lib/mtk/numeric_extensions.rb +66 -0
  55. data/lib/mtk/patterns/chain.rb +49 -0
  56. data/lib/mtk/patterns/choice.rb +43 -0
  57. data/lib/mtk/patterns/cycle.rb +18 -0
  58. data/lib/mtk/patterns/for_each.rb +71 -0
  59. data/lib/mtk/patterns/function.rb +39 -0
  60. data/lib/mtk/patterns/lines.rb +54 -0
  61. data/lib/mtk/patterns/palindrome.rb +45 -0
  62. data/lib/mtk/patterns/pattern.rb +171 -0
  63. data/lib/mtk/patterns/sequence.rb +20 -0
  64. data/lib/mtk/sequencers/event_builder.rb +132 -0
  65. data/lib/mtk/sequencers/legato_sequencer.rb +24 -0
  66. data/lib/mtk/sequencers/rhythmic_sequencer.rb +28 -0
  67. data/lib/mtk/sequencers/sequencer.rb +111 -0
  68. data/lib/mtk/sequencers/step_sequencer.rb +26 -0
  69. data/spec/mtk/core/duration_spec.rb +372 -0
  70. data/spec/mtk/core/intensity_spec.rb +289 -0
  71. data/spec/mtk/core/interval_spec.rb +265 -0
  72. data/spec/mtk/core/pitch_class_spec.rb +343 -0
  73. data/spec/mtk/core/pitch_spec.rb +297 -0
  74. data/spec/mtk/events/event_spec.rb +234 -0
  75. data/spec/mtk/events/note_spec.rb +174 -0
  76. data/spec/mtk/events/parameter_spec.rb +220 -0
  77. data/spec/mtk/events/timeline_spec.rb +430 -0
  78. data/spec/mtk/groups/chord_spec.rb +85 -0
  79. data/spec/mtk/groups/collection_spec.rb +374 -0
  80. data/spec/mtk/groups/melody_spec.rb +225 -0
  81. data/spec/mtk/groups/pitch_class_set_spec.rb +340 -0
  82. data/spec/mtk/io/midi_file_spec.rb +243 -0
  83. data/spec/mtk/io/midi_output_spec.rb +102 -0
  84. data/spec/mtk/lang/durations_spec.rb +89 -0
  85. data/spec/mtk/lang/intensities_spec.rb +101 -0
  86. data/spec/mtk/lang/intervals_spec.rb +143 -0
  87. data/spec/mtk/lang/parser_spec.rb +603 -0
  88. data/spec/mtk/lang/pitch_classes_spec.rb +62 -0
  89. data/spec/mtk/lang/pitches_spec.rb +56 -0
  90. data/spec/mtk/lang/pseudo_constants_spec.rb +20 -0
  91. data/spec/mtk/lang/variable_spec.rb +52 -0
  92. data/spec/mtk/numeric_extensions_spec.rb +83 -0
  93. data/spec/mtk/patterns/chain_spec.rb +110 -0
  94. data/spec/mtk/patterns/choice_spec.rb +97 -0
  95. data/spec/mtk/patterns/cycle_spec.rb +123 -0
  96. data/spec/mtk/patterns/for_each_spec.rb +136 -0
  97. data/spec/mtk/patterns/function_spec.rb +120 -0
  98. data/spec/mtk/patterns/lines_spec.rb +77 -0
  99. data/spec/mtk/patterns/palindrome_spec.rb +108 -0
  100. data/spec/mtk/patterns/pattern_spec.rb +132 -0
  101. data/spec/mtk/patterns/sequence_spec.rb +203 -0
  102. data/spec/mtk/sequencers/event_builder_spec.rb +245 -0
  103. data/spec/mtk/sequencers/legato_sequencer_spec.rb +45 -0
  104. data/spec/mtk/sequencers/rhythmic_sequencer_spec.rb +84 -0
  105. data/spec/mtk/sequencers/sequencer_spec.rb +215 -0
  106. data/spec/mtk/sequencers/step_sequencer_spec.rb +93 -0
  107. data/spec/spec_coverage.rb +2 -0
  108. data/spec/spec_helper.rb +12 -0
  109. data/spec/test.mid +0 -0
  110. metadata +226 -0
@@ -0,0 +1,108 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Patterns::Palindrome do
4
+
5
+ PALINDROME = MTK::Patterns::Palindrome
6
+
7
+ describe "#next" do
8
+ it "reverses direction when the ends of the list are reached" do
9
+ palindrome = PALINDROME.new [1,2,3,4]
10
+ nexts = []
11
+ 12.times { nexts << palindrome.next }
12
+ nexts.should == [1,2,3, 4,3,2, 1,2,3, 4,3,2]
13
+ end
14
+
15
+ it "reverses direction when the end of the list is reached, and repeats the end (first/last) elements" do
16
+ palindrome = PALINDROME.new [1,2,3,4], :repeat_ends => true
17
+ nexts = []
18
+ 12.times { nexts << palindrome.next }
19
+ nexts.should == [1,2,3,4, 4,3,2,1, 1,2,3,4]
20
+ end
21
+
22
+ it "enumerates nested sequences" do
23
+ palindrome = PALINDROME.new [1, MTK::Patterns.Sequence(2,3), 4]
24
+ nexts = []
25
+ 10.times { nexts << palindrome.next }
26
+ nexts.should == [1,2,3,4, 2,3,1, 2,3,4] # note sequence goes forward in both directions!
27
+ end
28
+
29
+ it "enumerates nested sequences, and repeats the last element" do
30
+ palindrome = PALINDROME.new [1, MTK::Patterns.Sequence(2,3), 4], :repeat_ends => true
31
+ nexts = []
32
+ 9.times { nexts << palindrome.next }
33
+ nexts.should == [1,2,3,4, 4,2,3,1, 1] # note sequence goes forward in both directions!
34
+ end
35
+ end
36
+
37
+ describe "#rewind" do
38
+ it "restarts the Palindrome" do
39
+ palindrome = PALINDROME.new [1,2,3,4]
40
+ 5.times { palindrome.next }
41
+ palindrome.rewind
42
+ palindrome.next.should == 1
43
+ palindrome.next.should == 2
44
+ end
45
+ end
46
+
47
+ describe "#repeat_ends?" do
48
+ it "is true if the :repeat_ends option is true" do
49
+ PALINDROME.new([], :repeat_ends => true).repeat_ends?.should be_true
50
+ end
51
+
52
+ it "is false if the :repeat_ends option is true" do
53
+ PALINDROME.new([], :repeat_ends => false).repeat_ends?.should be_false
54
+ end
55
+ end
56
+
57
+ describe "#==" do
58
+ it "is false if the :repeat_ends options are different" do
59
+ PALINDROME.new([1,2,3], :repeat_ends => true).should_not == PALINDROME.new([1,2,3], :repeat_ends => false)
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+
66
+ describe MTK::Patterns do
67
+
68
+ describe "#Palindrome" do
69
+ it "creates a Palindrome" do
70
+ MTK::Patterns.Palindrome(1,2,3).should be_a MTK::Patterns::Palindrome
71
+ end
72
+
73
+ it "sets #elements from the varargs" do
74
+ MTK::Patterns.Palindrome(1,2,3).elements.should == [1,2,3]
75
+ end
76
+ end
77
+
78
+ describe "#PitchPalindrome" do
79
+ it "creates a Palindrome" do
80
+ MTK::Patterns.PitchPalindrome(1,2,3).should be_a MTK::Patterns::Palindrome
81
+ end
82
+
83
+ it "sets #elements from the varargs" do
84
+ MTK::Patterns.PitchPalindrome(1,2,3).elements.should == [Pitch(1),Pitch(2),Pitch(3)]
85
+ end
86
+ end
87
+
88
+ describe "#IntensityPalindrome" do
89
+ it "creates a Palindrome" do
90
+ MTK::Patterns.IntensityPalindrome(1,2,3).should be_a MTK::Patterns::Palindrome
91
+ end
92
+
93
+ it "sets #elements from the varargs" do
94
+ MTK::Patterns.IntensityPalindrome(1,2,3).elements.should == [Intensity(1),Intensity(2),Intensity(3)]
95
+ end
96
+ end
97
+
98
+ describe "#DurationPalindrome" do
99
+ it "creates a Palindrome" do
100
+ MTK::Patterns.DurationPalindrome(1,2,3).should be_a MTK::Patterns::Palindrome
101
+ end
102
+
103
+ it "sets #elements from the varargs" do
104
+ MTK::Patterns.DurationPalindrome(1,2,3).elements.should == [Duration(1),Duration(2),Duration(3)]
105
+ end
106
+ end
107
+
108
+ end
@@ -0,0 +1,132 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Patterns::Pattern do
4
+
5
+ PATTERN = MTK::Patterns::Pattern
6
+
7
+ let(:elements) { [1,2,3] }
8
+
9
+
10
+ describe "@min_elements" do
11
+ it "is the :min_elements option the pattern was constructed with" do
12
+ PATTERN.new([], min_elements: 1).min_elements.should == 1
13
+ end
14
+
15
+ it "is nil by default" do
16
+ PATTERN.new([]).min_elements.should be_nil
17
+ end
18
+
19
+ it "prevents a StopIteration error until min_elements have been emitted (overriding max_elements if necessary)" do
20
+ pattern = PATTERN.new([1,2,3], min_elements: 5, max_elements: 3)
21
+ 5.times{ pattern.next }
22
+ lambda{ pattern.next }.should raise_error StopIteration
23
+ end
24
+
25
+ it "sets exactly how many elements will be emitted when set to the same values as max_elements" do
26
+ pattern = PATTERN.new([1,2,3], min_elements: 5, max_elements: 5)
27
+ 5.times{ pattern.next }
28
+ lambda{ pattern.next }.should raise_error StopIteration
29
+ end
30
+
31
+ it "is maintained when applying Collection operations" do
32
+ PATTERN.new(elements, min_elements: 2).reverse.min_elements.should == 2
33
+ end
34
+ end
35
+
36
+ describe '#min_elements_unmet?' do
37
+ it "is true until min_elements have been emitted" do
38
+ pattern = PATTERN.new([:anything], min_elements: 5)
39
+ 5.times { pattern.min_elements_unmet?.should be_true and pattern.next }
40
+ pattern.min_elements_unmet?.should be_false
41
+ end
42
+
43
+ it "is always false if min_elements is not set" do
44
+ pattern = PATTERN.new([:anything], min_elements: nil)
45
+ 5.times { pattern.min_elements_unmet?.should be_false and pattern.next }
46
+ end
47
+
48
+ it "is always false if min_elements is 0" do
49
+ pattern = PATTERN.new([:anything], min_elements: 0)
50
+ 5.times { pattern.min_elements_unmet?.should be_false and pattern.next }
51
+ end
52
+ end
53
+
54
+
55
+ describe "@max_elements" do
56
+ it "is the :max_elements option the pattern was constructed with" do
57
+ PATTERN.new([], max_elements: 1).max_elements.should == 1
58
+ end
59
+
60
+ it "is nil by default" do
61
+ PATTERN.new([]).max_elements.should be_nil
62
+ end
63
+
64
+ it "causes a StopIteration exception once max_elements have been emitted" do
65
+ pattern = PATTERN.new([:anything], max_elements: 5)
66
+ 5.times { pattern.next }
67
+ lambda { pattern.next }.should raise_error
68
+ end
69
+
70
+ it "raises a StopIteration error when a nested pattern has emitted more than max_elements" do
71
+ pattern = PATTERN.new([Patterns.Cycle(1,2)], max_elements: 5)
72
+ 5.times{ pattern.next }
73
+ lambda{ pattern.next }.should raise_error StopIteration
74
+ end
75
+
76
+ it "is maintained when applying Collection operations" do
77
+ PATTERN.new(elements, max_elements: 2).reverse.max_elements.should == 2
78
+ end
79
+ end
80
+
81
+
82
+ describe '#max_elements_exceeded?' do
83
+ it "is false until max_elements have been emitted" do
84
+ pattern = PATTERN.new([:anything], max_elements: 5)
85
+ 5.times { pattern.max_elements_exceeded?.should be_false and pattern.next }
86
+ pattern.max_elements_exceeded?.should be_true
87
+ end
88
+
89
+ it "is always false when max_elements is not set" do
90
+ pattern = PATTERN.new([:anything], max_elements: nil)
91
+ 5.times { pattern.max_elements_exceeded?.should be_false and pattern.next }
92
+ end
93
+ end
94
+
95
+
96
+ describe "@max_cycles" do
97
+ it "is the :max_cycles option the pattern was constructed with" do
98
+ PATTERN.new([], max_cycles: 2).max_cycles.should == 2
99
+ end
100
+
101
+ it "is 1 by default" do
102
+ PATTERN.new([]).max_cycles.should == 1
103
+ end
104
+
105
+ it "is maintained when applying Collection operations" do
106
+ PATTERN.new(elements, max_cycles: 2).reverse.max_cycles.should == 2
107
+ end
108
+ end
109
+
110
+
111
+ describe "#next" do
112
+ it "raises StopIteration if elements is empty" do
113
+ lambda{ PATTERN.new([]).next }.should raise_error StopIteration
114
+ end
115
+ end
116
+
117
+
118
+ describe "#==" do
119
+ it "is true if the elements and types are equal" do
120
+ PATTERN.new(elements, :type => :some_type).should == PATTERN.new(elements, :type => :some_type)
121
+ end
122
+
123
+ it "is false if the elements are not equal" do
124
+ PATTERN.new(elements, :type => :some_type).should_not == PATTERN.new(elements + [4], :type => :some_type)
125
+ end
126
+
127
+ it "is false if the types are not equal" do
128
+ PATTERN.new(elements, :type => :some_type).should_not == PATTERN.new(elements, :type => :another_type)
129
+ end
130
+ end
131
+
132
+ end
@@ -0,0 +1,203 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Patterns::Sequence do
4
+
5
+ SEQUENCE = MTK::Patterns::Sequence
6
+
7
+ let(:elements) { [1,2,3] }
8
+ let(:sequence) { SEQUENCE.new(elements) }
9
+
10
+ it "is a MTK::Collection" do
11
+ sequence.should be_a MTK::Groups::Collection
12
+ # and now we won't test any other collection features here... see collection_spec
13
+ end
14
+
15
+ describe ".from_a" do
16
+ it "acts like .new" do
17
+ SEQUENCE.from_a(elements).should == sequence
18
+ end
19
+ end
20
+
21
+ describe "#elements" do
22
+ it "is the array the sequence was constructed with" do
23
+ sequence.elements.should == elements
24
+ end
25
+ end
26
+
27
+ describe "#next" do
28
+ it "enumerates the elements" do
29
+ nexts = []
30
+ elements.length.times do
31
+ nexts << sequence.next
32
+ end
33
+ nexts.should == elements
34
+ end
35
+
36
+ it "raises StopIteration when the end of the Sequence is reached" do
37
+ elements.length.times{ sequence.next }
38
+ lambda{ sequence.next }.should raise_error(StopIteration)
39
+ end
40
+
41
+ it "should automatically break out of Kernel#loop" do
42
+ nexts = []
43
+ loop do # loop rescues StopIteration and exits the loop
44
+ nexts << sequence.next
45
+ end
46
+ nexts.should == elements
47
+ end
48
+
49
+ it "enumerates the elements in sub-sequences" do
50
+ sub_sequence = SEQUENCE.new [2,3]
51
+ sequence = SEQUENCE.new [1,sub_sequence,4]
52
+ nexts = []
53
+ loop { nexts << sequence.next }
54
+ nexts.should == [1,2,3,4]
55
+ end
56
+
57
+ it "skips over empty sub-sequences" do
58
+ sub_sequence = SEQUENCE.new []
59
+ sequence = SEQUENCE.new [1,sub_sequence,4]
60
+ nexts = []
61
+ loop { nexts << sequence.next }
62
+ nexts.should == [1,4]
63
+ end
64
+
65
+ end
66
+
67
+ describe "@min_elements" do
68
+ it "prevents a StopIteration error until min_elements have been emitted" do
69
+ pattern = SEQUENCE.new([1,2,3], cycle_count: 1, min_elements: 5)
70
+ 5.times{ pattern.next }
71
+ lambda{ pattern.next }.should raise_error StopIteration
72
+ end
73
+ end
74
+
75
+
76
+ describe "@max_cycles" do
77
+ it "is the :max_cycles option the pattern was constructed with" do
78
+ SEQUENCE.new( [], max_cycles: 2 ).max_cycles.should == 2
79
+ end
80
+
81
+ it "is 1 by default" do
82
+ SEQUENCE.new( [] ).max_cycles.should == 1
83
+ end
84
+
85
+ it "loops indefinitely when it's nil" do
86
+ sequence = SEQUENCE.new( [1], max_cycles: nil )
87
+ lambda { 100.times { sequence.next } }.should_not raise_error
88
+ end
89
+
90
+ it "causes a StopIteration exception after the number of cycles has completed" do
91
+ sequence = SEQUENCE.new( elements, max_cycles: 2 )
92
+ 2.times do
93
+ elements.length.times { sequence.next } # one full cycle
94
+ end
95
+ lambda { sequence.next }.should raise_error StopIteration
96
+ end
97
+ end
98
+
99
+
100
+ describe "#max_cycles_exceeded?" do
101
+ it "is false until max_elements have been emitted" do
102
+ sequence = SEQUENCE.new( elements, max_cycles: 2 )
103
+ 2.times do
104
+ sequence.max_cycles_exceeded?.should be_false
105
+ elements.length.times { sequence.next } # one full cycle
106
+ end
107
+ # unlike with element_count and max_elements_exceeded?, cycle_count doesn't get bumped until
108
+ # you ask for the next element and a StopIteration is thrown
109
+ lambda { sequence.next }.should raise_error StopIteration
110
+ sequence.max_cycles_exceeded?.should be_true
111
+ end
112
+
113
+ it "is always false when max_cycles is not set" do
114
+ pattern = PATTERN.new([:anything], max_cycles: nil)
115
+ 15.times { pattern.max_cycles_exceeded?.should be_false and pattern.next }
116
+ end
117
+ end
118
+
119
+
120
+ it "has max_elements_exceeded once max_elements have been emitted (edge case, has same number of elements)" do
121
+ sequence = SEQUENCE.new([1,2,3,4,5], :max_elements => 5)
122
+ 5.times { sequence.max_elements_exceeded?.should(be_false) and sequence.next }
123
+ sequence.max_elements_exceeded?.should be_true
124
+ lambda { sequence.next }.should raise_error StopIteration
125
+ end
126
+
127
+
128
+ it "raises a StopIteration error when a nested pattern has emitted more than max_elements" do
129
+ sequence = SEQUENCE.new([Patterns.Cycle(1,2)], :max_elements => 5)
130
+ 5.times { sequence.next }
131
+ lambda{ sequence.next }.should raise_error StopIteration
132
+ end
133
+
134
+
135
+ describe "#rewind" do
136
+ it "restarts at the beginning of the sequence" do
137
+ loop { sequence.next }
138
+ sequence.rewind
139
+ sequence.next.should == elements.first
140
+ end
141
+
142
+ it "returns self, so it can be chained to #next" do
143
+ first = sequence.next
144
+ sequence.rewind.next.should == first
145
+ end
146
+
147
+ it "causes sub-sequences to start from the beginning when encountered again after #rewind" do
148
+ sub_sequence = SEQUENCE.new [2,3]
149
+ sequence = SEQUENCE.new [1,sub_sequence,4]
150
+ loop { sequence.next }
151
+ sequence.rewind
152
+ nexts = []
153
+ loop { nexts << sequence.next }
154
+ nexts.should == [1,2,3,4]
155
+ end
156
+ end
157
+
158
+ end
159
+
160
+
161
+ describe MTK::Patterns do
162
+
163
+ describe "#Sequence" do
164
+ it "creates a Sequence" do
165
+ MTK::Patterns.Sequence(1,2,3).should be_a MTK::Patterns::Sequence
166
+ end
167
+
168
+ it "sets #elements from the varargs" do
169
+ MTK::Patterns.Sequence(1,2,3).elements.should == [1,2,3]
170
+ end
171
+ end
172
+
173
+ describe "#PitchSequence" do
174
+ it "creates a Sequence" do
175
+ MTK::Patterns.PitchSequence(1,2,3).should be_a MTK::Patterns::Sequence
176
+ end
177
+
178
+ it "sets #elements from the varargs" do
179
+ MTK::Patterns.PitchSequence(1,2,3).elements.should == [Pitch(1),Pitch(2),Pitch(3)]
180
+ end
181
+ end
182
+
183
+ describe "#IntensitySequence" do
184
+ it "creates a Sequence" do
185
+ MTK::Patterns.IntensitySequence(1,2,3).should be_a MTK::Patterns::Sequence
186
+ end
187
+
188
+ it "sets #elements from the varargs" do
189
+ MTK::Patterns.IntensitySequence(1,2,3).elements.should == [Intensity(1),Intensity(2),Intensity(3)]
190
+ end
191
+ end
192
+
193
+ describe "#DurationSequence" do
194
+ it "creates a Sequence" do
195
+ MTK::Patterns.DurationSequence(1,2,3).should be_a MTK::Patterns::Sequence
196
+ end
197
+
198
+ it "sets #elements from the varargs" do
199
+ MTK::Patterns.DurationSequence(1,2,3).elements.should == [Duration(1),Duration(2),Duration(3)]
200
+ end
201
+ end
202
+
203
+ end
@@ -0,0 +1,245 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Sequencers::EventBuilder do
4
+
5
+ EVENT_BUILDER = MTK::Sequencers::EventBuilder
6
+
7
+ let(:pitch) { EVENT_BUILDER::DEFAULT_PITCH }
8
+ let(:intensity) { EVENT_BUILDER::DEFAULT_INTENSITY }
9
+ let(:duration) { EVENT_BUILDER::DEFAULT_DURATION }
10
+
11
+ def notes(*pitches)
12
+ pitches.map{|pitch| Note(pitch, intensity, duration) }
13
+ end
14
+
15
+ describe "#new" do
16
+ it "allows default pitch to be specified" do
17
+ event_builder = EVENT_BUILDER.new [Patterns.IntervalCycle(0)], :default_pitch => Gb4
18
+ event_builder.next.should == [Note(Gb4, intensity, duration)]
19
+ end
20
+ it "allows default intensity to be specified" do
21
+ event_builder = EVENT_BUILDER.new [Patterns.IntervalCycle(0)], :default_intensity => ppp
22
+ event_builder.next.should == [Note(pitch, ppp, duration)]
23
+ end
24
+ it "allows default duration to be specified" do
25
+ event_builder = EVENT_BUILDER.new [Patterns.IntervalCycle(0)], :default_duration => 5.25
26
+ event_builder.next.should == [Note(pitch, 5.25, intensity)]
27
+ end
28
+ end
29
+
30
+ describe "#next" do
31
+ it "builds a single-note list from a single-pitch pattern argument" do
32
+ event_builder = EVENT_BUILDER.new [Patterns.Cycle(C4)]
33
+ event_builder.next.should == notes(C4)
34
+ end
35
+
36
+ it "builds a list of notes from any pitches in the argument" do
37
+ event_builder = EVENT_BUILDER.new [Patterns.Cycle(C4), Patterns.Cycle(D4)]
38
+ event_builder.next.should == notes(C4, D4)
39
+ end
40
+
41
+ it "builds a list of notes from pitch sets" do
42
+ event_builder = EVENT_BUILDER.new [ Patterns.Cycle( Chord(C4,D4) ) ]
43
+ event_builder.next.should == notes(C4, D4)
44
+ end
45
+
46
+ it "builds notes from pitch classes and a default_pitch, selecting the nearest pitch class to the previous pitch" do
47
+ event_builder = EVENT_BUILDER.new [Patterns.Sequence(C,G,B,Eb,D,C)], :default_pitch => D3
48
+ notes = []
49
+ loop do
50
+ notes << event_builder.next
51
+ end
52
+ notes.flatten.should == notes(C3,G2,B2,Eb3,D3,C3)
53
+ end
54
+
55
+ it "defaults to a starting point of C4 (middle C)" do
56
+ event_builder = EVENT_BUILDER.new [Patterns.Sequence(C4)]
57
+ event_builder.next.should == notes(C4)
58
+ end
59
+
60
+ it "defaults to intensity 'o' when no intensities are given" do
61
+ event_builder = EVENT_BUILDER.new [Patterns.PitchSequence(C4, D4, E4), Patterns.DurationCycle(2)]
62
+ event_builder.next.should == [Note(C4, o, 2)]
63
+ event_builder.next.should == [Note(D4, o, 2)]
64
+ event_builder.next.should == [Note(E4, o, 2)]
65
+ end
66
+
67
+ it "defaults to duration 1 when no durations are given" do
68
+ event_builder = EVENT_BUILDER.new [Patterns.PitchSequence(C4, D4, E4), Patterns.IntensityCycle(p,o)]
69
+ event_builder.next.should == [Note(C4, p, 1)]
70
+ event_builder.next.should == [Note(D4, o, 1)]
71
+ event_builder.next.should == [Note(E4, p, 1)]
72
+ end
73
+
74
+ it "builds notes from pitch class sets, selecting the nearest pitch classes to the previous/default pitch" do
75
+ pitch_class_sequence = MTK::Patterns::Sequence.new([PitchClassSet(C,G),PitchClassSet(B,Eb),PitchClassSet(D,C)])
76
+ event_builder = EVENT_BUILDER.new [pitch_class_sequence], :default_pitch => D3
77
+ event_builder.next.should == notes(C3,G3)
78
+ event_builder.next.should == notes(B3,Eb3)
79
+ event_builder.next.should == notes(D3,C3)
80
+ end
81
+
82
+ it "builds notes from by adding Numeric intervals in :pitch type Patterns to the previous Pitch" do
83
+ event_builder = EVENT_BUILDER.new [ Patterns.Sequence( C4, M3, m3, -P5 ) ]
84
+ nexts = []
85
+ loop { nexts << event_builder.next }
86
+ nexts.should == [notes(C4), notes(E4), notes(G4), notes(C4)]
87
+ end
88
+
89
+ it "builds notes from by adding Numeric intervals in :pitch type Patterns to all pitches in the previous Chord" do
90
+ event_builder = EVENT_BUILDER.new [ Patterns.Sequence( Chord(C4,Eb4), M3, m3, -P5) ]
91
+ nexts = []
92
+ loop { nexts << event_builder.next }
93
+ nexts.should == [notes(C4,Eb4), notes(E4,G4), notes(G4,Bb4), notes(C4,Eb4)]
94
+ end
95
+
96
+ it "builds notes from intensities" do
97
+ event_builder = EVENT_BUILDER.new [ Patterns.Cycle(C4), Patterns.Sequence(mf, p, fff) ]
98
+ nexts = []
99
+ loop { nexts += event_builder.next }
100
+ nexts.should == [Note(C4, mf, duration), Note(C4, p, duration), Note(C4, fff, duration)]
101
+ end
102
+
103
+ it "builds notes from durations" do
104
+ event_builder = EVENT_BUILDER.new [ Patterns.PitchCycle(C4), Patterns.DurationSequence(1,2,3) ]
105
+ nexts = []
106
+ loop { nexts += event_builder.next }
107
+ nexts.should == [Note(C4, intensity, 1), Note(C4, intensity, 2), Note(C4, intensity, 3)]
108
+ end
109
+
110
+ it "iterates through the pitch, intensity, and duration list in parallel to emit Notes" do
111
+ event_builder = EVENT_BUILDER.new [Patterns.PitchCycle(C4, D4, E4), Patterns.IntensityCycle(p, o), Patterns.DurationCycle(1,2,3,4)]
112
+ event_builder.next.should == [Note(C4, p, 1)]
113
+ event_builder.next.should == [Note(D4, o, 2)]
114
+ event_builder.next.should == [Note(E4, p, 3)]
115
+ event_builder.next.should == [Note(C4, o, 4)]
116
+ event_builder.next.should == [Note(D4, p, 1)]
117
+ event_builder.next.should == [Note(E4, o, 2)]
118
+ end
119
+
120
+ it "returns nil (for a rest) when it encounters a nil value" do
121
+ event_builder = EVENT_BUILDER.new [Patterns.PitchCycle(C4, D4, E4, F4, nil), Patterns.IntensityCycle(mp, mf, o, nil), Patterns.DurationCycle(1, 2, nil)]
122
+ event_builder.next.should == [Note(C4, mp, 1)]
123
+ event_builder.next.should == [Note(D4, mf, 2)]
124
+ event_builder.next.should be_nil
125
+ event_builder.next.should be_nil
126
+ event_builder.next.should be_nil
127
+ end
128
+
129
+ it "goes to the nearest Pitch for any PitchClasses in the pitch list" do
130
+ event_builder = EVENT_BUILDER.new [Patterns::Cycle(C4, F, C, G, C)]
131
+ event_builder.next.should == notes(C4)
132
+ event_builder.next.should == notes(F4)
133
+ event_builder.next.should == notes(C4)
134
+ event_builder.next.should == notes(G3)
135
+ event_builder.next.should == notes(C4)
136
+ end
137
+
138
+ it "does not endlessly ascend or descend when alternating between two pitch classes a tritone apart" do
139
+ event_builder = EVENT_BUILDER.new [Patterns::Cycle(C4, Gb, C, Gb, C)]
140
+ event_builder.next.should == notes(C4)
141
+ event_builder.next.should == notes(Gb4)
142
+ event_builder.next.should == notes(C4)
143
+ event_builder.next.should == notes(Gb4)
144
+ event_builder.next.should == notes(C4)
145
+ end
146
+
147
+ it "handles pitches and chords intermixed" do
148
+ event_builder = EVENT_BUILDER.new [Patterns.Cycle( Chord(C4, E4, G4), C4, Chord(D4, F4, A4) )]
149
+ event_builder.next.should == notes(C4,E4,G4)
150
+ event_builder.next.should == notes(C4)
151
+ event_builder.next.should == notes(D4,F4,A4)
152
+ end
153
+
154
+ it "adds numeric intervals to Chord" do
155
+ event_builder = EVENT_BUILDER.new [Patterns::Cycle( Chord(C4, E4, G4), M2 )]
156
+ event_builder.next.should == notes(C4,E4,G4)
157
+ event_builder.next.should == notes(D4,Gb4,A4)
158
+ end
159
+
160
+ it "goes to the nearest Pitch relative to the lowest note in the Chord for any PitchClasses in the pitch list" do
161
+ event_builder = EVENT_BUILDER.new [Patterns.Cycle( Chord(C4, E4, G4), F, D, Bb )]
162
+ event_builder.next.should == notes(C4,E4,G4)
163
+ event_builder.next.should == notes(F4)
164
+ event_builder.next.should == notes(D4)
165
+ event_builder.next.should == notes(Bb3)
166
+ end
167
+
168
+ it "uses the default_pitch when no pitch pattern is provided" do
169
+ event_builder = EVENT_BUILDER.new [Patterns.Cycle( mp, mf, o )], :default_pitch => G3
170
+ event_builder.next.should == [Note(G3,mp,1)]
171
+ event_builder.next.should == [Note(G3,mf,1)]
172
+ event_builder.next.should == [Note(G3,o,1)]
173
+ end
174
+
175
+ it "handles chains of sequences" do
176
+ event_builder = EVENT_BUILDER.new [ Patterns.Chain( Patterns.Sequence(C4,D4,E4), Patterns.Sequence(mp,mf,ff), Patterns.Sequence(q,h,w) ) ]
177
+ event_builder.next.should == [Note(C4,mp,q)]
178
+ event_builder.next.should == [Note(D4,mf,h)]
179
+ event_builder.next.should == [Note(E4,ff,w)]
180
+ end
181
+
182
+ it "enforces the max_interval option for rising intervals" do
183
+ event_builder = EVENT_BUILDER.new( [ Patterns.Sequence(C4,P5,P5,P5,P5,P5,P5,P5,P5,P5,P5,P5,P5)], max_interval:12 )
184
+ pitches = []
185
+ 13.times{ pitches << event_builder.next[0].pitch }
186
+ pitches.should == [C4,G4,D4,A4,E4,B4,Gb4,Db4,Ab4,Eb4,Bb4,F4,C5]
187
+
188
+ event_builder = EVENT_BUILDER.new( [ Patterns.Sequence(C4,P5,P5,P5,P5,P5,P5,P5,P5,P5,P5,P5,P5)], max_interval:11 )
189
+ pitches = []
190
+ 13.times{ pitches << event_builder.next[0].pitch }
191
+ pitches.should == [C4,G4,D4,A4,E4,B4,Gb4,Db4,Ab4,Eb4,Bb4,F4,C4]
192
+ end
193
+
194
+ it "enforces the max_interval option for falling intervals" do
195
+ event_builder = EVENT_BUILDER.new( [ Patterns.Sequence(C4,-P5,-P5,-P5,-P5,-P5,-P5,-P5,-P5,-P5,-P5,-P5,-P5)], max_interval:12 )
196
+ pitches = []
197
+ 13.times{ pitches << event_builder.next[0].pitch }
198
+ pitches.should == [C4,F3,Bb3,Eb3,Ab3,Db3,Gb3,B3,E3,A3,D3,G3,C3]
199
+
200
+ event_builder = EVENT_BUILDER.new( [ Patterns.Sequence(C4,-P5,-P5,-P5,-P5,-P5,-P5,-P5,-P5,-P5,-P5,-P5,-P5)], max_interval:11 )
201
+ pitches = []
202
+ 13.times{ pitches << event_builder.next[0].pitch }
203
+ pitches.should == [C4,F3,Bb3,Eb3,Ab3,Db3,Gb3,B3,E3,A3,D3,G3,C4]
204
+ end
205
+
206
+ it "adds chained durations together" do
207
+ event_builder = EVENT_BUILDER.new( [Patterns.Chain(h,q,i,s)] )
208
+ event_builder.next[0].duration.should == h+q+i+s
209
+ end
210
+
211
+ it "averages chained intensities together" do
212
+ event_builder = EVENT_BUILDER.new( [Patterns.IntensityChain(0.1, 0.2, 0.3, 0.4)] )
213
+ event_builder.next[0].intensity.should == Intensity(0.25)
214
+ end
215
+
216
+ it "defaults the intensity to the previous intensity" do
217
+ event_builder = EVENT_BUILDER.new(
218
+ [Patterns.Sequence(Patterns.Chain(C4,ppp,q), Patterns.Chain(D4,i), Patterns.Chain(E4,ff,h), Patterns.Chain(F4,i))]
219
+ )
220
+ notes = []
221
+ 4.times{ notes += event_builder.next }
222
+ notes.should == [Note(C4,ppp,q), Note(D4,ppp,i), Note(E4,ff,h), Note(F4,ff,i)]
223
+ end
224
+
225
+ it "defaults the duration to the previous duration" do
226
+ event_builder = EVENT_BUILDER.new(
227
+ [Patterns.Sequence(Patterns.Chain(C4,ppp,h), Patterns.Chain(D4,mp), Patterns.Chain(E4,ff,s), Patterns.Chain(F4,mf))]
228
+ )
229
+ notes = []
230
+ 4.times{ notes += event_builder.next }
231
+ notes.should == [Note(C4,ppp,h), Note(D4,mp,h), Note(E4,ff,s), Note(F4,mf,s)]
232
+ end
233
+ end
234
+
235
+ describe "#rewind" do
236
+ it "resets the state of the Chain" do
237
+ event_builder = EVENT_BUILDER.new [ Patterns.Sequence(C,P8) ]
238
+ event_builder.next.should == [Note(C4,intensity,duration)]
239
+ event_builder.next.should == [Note(C5,intensity,duration)]
240
+ event_builder.rewind
241
+ event_builder.next.should == [Note(C4,intensity,duration)]
242
+ end
243
+ end
244
+
245
+ end