jmtk 0.0.3.3-java
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +10 -0
- data/DEVELOPMENT_NOTES.md +115 -0
- data/INTRO.md +129 -0
- data/LICENSE.txt +27 -0
- data/README.md +50 -0
- data/Rakefile +102 -0
- data/bin/jmtk +250 -0
- data/bin/mtk +250 -0
- data/examples/crescendo.rb +20 -0
- data/examples/drum_pattern.rb +23 -0
- data/examples/dynamic_pattern.rb +36 -0
- data/examples/gets_and_play.rb +27 -0
- data/examples/notation.rb +22 -0
- data/examples/play_midi.rb +17 -0
- data/examples/print_midi.rb +13 -0
- data/examples/random_tone_row.rb +18 -0
- data/examples/syntax_to_midi.rb +28 -0
- data/examples/test_output.rb +7 -0
- data/examples/tone_row_melody.rb +23 -0
- data/lib/mtk.rb +76 -0
- data/lib/mtk/core/duration.rb +213 -0
- data/lib/mtk/core/intensity.rb +158 -0
- data/lib/mtk/core/interval.rb +157 -0
- data/lib/mtk/core/pitch.rb +154 -0
- data/lib/mtk/core/pitch_class.rb +194 -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/events/timeline.rb +232 -0
- data/lib/mtk/groups/chord.rb +56 -0
- data/lib/mtk/groups/collection.rb +196 -0
- data/lib/mtk/groups/melody.rb +96 -0
- data/lib/mtk/groups/pitch_class_set.rb +163 -0
- data/lib/mtk/groups/pitch_collection.rb +23 -0
- data/lib/mtk/io/dls_synth_device.rb +146 -0
- data/lib/mtk/io/dls_synth_output.rb +62 -0
- data/lib/mtk/io/jsound_input.rb +87 -0
- data/lib/mtk/io/jsound_output.rb +82 -0
- data/lib/mtk/io/midi_file.rb +209 -0
- data/lib/mtk/io/midi_input.rb +97 -0
- data/lib/mtk/io/midi_output.rb +195 -0
- data/lib/mtk/io/notation.rb +162 -0
- data/lib/mtk/io/unimidi_input.rb +117 -0
- data/lib/mtk/io/unimidi_output.rb +140 -0
- data/lib/mtk/lang/durations.rb +57 -0
- data/lib/mtk/lang/intensities.rb +61 -0
- data/lib/mtk/lang/intervals.rb +73 -0
- data/lib/mtk/lang/mtk_grammar.citrus +237 -0
- data/lib/mtk/lang/parser.rb +29 -0
- data/lib/mtk/lang/pitch_classes.rb +29 -0
- data/lib/mtk/lang/pitches.rb +52 -0
- data/lib/mtk/lang/pseudo_constants.rb +26 -0
- data/lib/mtk/lang/variable.rb +32 -0
- data/lib/mtk/numeric_extensions.rb +66 -0
- data/lib/mtk/patterns/chain.rb +49 -0
- data/lib/mtk/patterns/choice.rb +43 -0
- 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/patterns/lines.rb +54 -0
- data/lib/mtk/patterns/palindrome.rb +45 -0
- data/lib/mtk/patterns/pattern.rb +171 -0
- data/lib/mtk/patterns/sequence.rb +20 -0
- data/lib/mtk/sequencers/event_builder.rb +132 -0
- data/lib/mtk/sequencers/legato_sequencer.rb +24 -0
- data/lib/mtk/sequencers/rhythmic_sequencer.rb +28 -0
- data/lib/mtk/sequencers/sequencer.rb +111 -0
- data/lib/mtk/sequencers/step_sequencer.rb +26 -0
- data/spec/mtk/core/duration_spec.rb +372 -0
- data/spec/mtk/core/intensity_spec.rb +289 -0
- data/spec/mtk/core/interval_spec.rb +265 -0
- data/spec/mtk/core/pitch_class_spec.rb +343 -0
- data/spec/mtk/core/pitch_spec.rb +297 -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/events/timeline_spec.rb +430 -0
- data/spec/mtk/groups/chord_spec.rb +85 -0
- data/spec/mtk/groups/collection_spec.rb +374 -0
- data/spec/mtk/groups/melody_spec.rb +225 -0
- data/spec/mtk/groups/pitch_class_set_spec.rb +340 -0
- data/spec/mtk/io/midi_file_spec.rb +243 -0
- data/spec/mtk/io/midi_output_spec.rb +102 -0
- data/spec/mtk/lang/durations_spec.rb +89 -0
- data/spec/mtk/lang/intensities_spec.rb +101 -0
- data/spec/mtk/lang/intervals_spec.rb +143 -0
- data/spec/mtk/lang/parser_spec.rb +603 -0
- data/spec/mtk/lang/pitch_classes_spec.rb +62 -0
- data/spec/mtk/lang/pitches_spec.rb +56 -0
- data/spec/mtk/lang/pseudo_constants_spec.rb +20 -0
- data/spec/mtk/lang/variable_spec.rb +52 -0
- data/spec/mtk/numeric_extensions_spec.rb +83 -0
- data/spec/mtk/patterns/chain_spec.rb +110 -0
- data/spec/mtk/patterns/choice_spec.rb +97 -0
- data/spec/mtk/patterns/cycle_spec.rb +123 -0
- data/spec/mtk/patterns/for_each_spec.rb +136 -0
- data/spec/mtk/patterns/function_spec.rb +120 -0
- data/spec/mtk/patterns/lines_spec.rb +77 -0
- data/spec/mtk/patterns/palindrome_spec.rb +108 -0
- data/spec/mtk/patterns/pattern_spec.rb +132 -0
- data/spec/mtk/patterns/sequence_spec.rb +203 -0
- 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/sequencers/step_sequencer_spec.rb +93 -0
- data/spec/spec_coverage.rb +2 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/test.mid +0 -0
- 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
|