mtk 0.0.1 → 0.0.2

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 (93) hide show
  1. data/.yardopts +9 -0
  2. data/INTRO.md +73 -0
  3. data/LICENSE.txt +27 -0
  4. data/README.md +93 -18
  5. data/Rakefile +13 -1
  6. data/examples/crescendo.rb +20 -0
  7. data/examples/dynamic_pattern.rb +39 -0
  8. data/examples/play_midi.rb +19 -0
  9. data/examples/print_midi.rb +13 -0
  10. data/examples/random_tone_row.rb +18 -0
  11. data/examples/tone_row_melody.rb +21 -0
  12. data/lib/mtk/_constants/durations.rb +80 -0
  13. data/lib/mtk/_constants/intensities.rb +81 -0
  14. data/lib/mtk/{constants → _constants}/intervals.rb +10 -1
  15. data/lib/mtk/_constants/pitch_classes.rb +35 -0
  16. data/lib/mtk/_constants/pitches.rb +49 -0
  17. data/lib/mtk/{numeric_extensions.rb → _numeric_extensions.rb} +0 -0
  18. data/lib/mtk/event.rb +14 -5
  19. data/lib/mtk/helper/collection.rb +114 -0
  20. data/lib/mtk/helper/event_builder.rb +85 -0
  21. data/lib/mtk/{constants → helper}/pseudo_constants.rb +7 -6
  22. data/lib/mtk/lang/grammar.rb +17 -0
  23. data/lib/mtk/lang/mtk_grammar.citrus +60 -0
  24. data/lib/mtk/midi/file.rb +10 -15
  25. data/lib/mtk/midi/jsound_input.rb +68 -0
  26. data/lib/mtk/midi/jsound_output.rb +80 -0
  27. data/lib/mtk/note.rb +22 -3
  28. data/lib/mtk/pattern/abstract_pattern.rb +132 -0
  29. data/lib/mtk/pattern/choice.rb +25 -9
  30. data/lib/mtk/pattern/cycle.rb +51 -0
  31. data/lib/mtk/pattern/enumerator.rb +26 -0
  32. data/lib/mtk/pattern/function.rb +46 -0
  33. data/lib/mtk/pattern/lines.rb +60 -0
  34. data/lib/mtk/pattern/palindrome.rb +42 -0
  35. data/lib/mtk/pattern/sequence.rb +15 -50
  36. data/lib/mtk/pitch.rb +45 -6
  37. data/lib/mtk/pitch_class.rb +36 -35
  38. data/lib/mtk/pitch_class_set.rb +46 -14
  39. data/lib/mtk/pitch_set.rb +20 -31
  40. data/lib/mtk/sequencer/abstract_sequencer.rb +85 -0
  41. data/lib/mtk/sequencer/rhythmic_sequencer.rb +29 -0
  42. data/lib/mtk/sequencer/step_sequencer.rb +26 -0
  43. data/lib/mtk/timeline.rb +75 -22
  44. data/lib/mtk/transform/invertible.rb +15 -0
  45. data/lib/mtk/{util → transform}/mappable.rb +6 -2
  46. data/lib/mtk/transform/set_theory_operations.rb +34 -0
  47. data/lib/mtk/transform/transposable.rb +14 -0
  48. data/lib/mtk.rb +56 -22
  49. data/spec/mtk/_constants/durations_spec.rb +118 -0
  50. data/spec/mtk/{constants/dynamics_spec.rb → _constants/intensities_spec.rb} +48 -17
  51. data/spec/mtk/{constants → _constants}/intervals_spec.rb +21 -0
  52. data/spec/mtk/_constants/pitch_classes_spec.rb +58 -0
  53. data/spec/mtk/_constants/pitches_spec.rb +52 -0
  54. data/spec/mtk/{numeric_extensions_spec.rb → _numeric_extensions_spec.rb} +0 -0
  55. data/spec/mtk/event_spec.rb +19 -0
  56. data/spec/mtk/helper/collection_spec.rb +291 -0
  57. data/spec/mtk/helper/event_builder_spec.rb +92 -0
  58. data/spec/mtk/helper/pseudo_constants_spec.rb +20 -0
  59. data/spec/mtk/lang/grammar_spec.rb +100 -0
  60. data/spec/mtk/midi/file_spec.rb +41 -6
  61. data/spec/mtk/note_spec.rb +53 -3
  62. data/spec/mtk/pattern/abstract_pattern_spec.rb +45 -0
  63. data/spec/mtk/pattern/choice_spec.rb +89 -3
  64. data/spec/mtk/pattern/cycle_spec.rb +133 -0
  65. data/spec/mtk/pattern/function_spec.rb +133 -0
  66. data/spec/mtk/pattern/lines_spec.rb +93 -0
  67. data/spec/mtk/pattern/note_cycle_spec.rb.bak +116 -0
  68. data/spec/mtk/pattern/palindrome_spec.rb +124 -0
  69. data/spec/mtk/pattern/pitch_cycle_spec.rb.bak +47 -0
  70. data/spec/mtk/pattern/pitch_sequence_spec.rb.bak +37 -0
  71. data/spec/mtk/pattern/sequence_spec.rb +128 -31
  72. data/spec/mtk/pitch_class_set_spec.rb +240 -7
  73. data/spec/mtk/pitch_class_spec.rb +84 -18
  74. data/spec/mtk/pitch_set_spec.rb +45 -10
  75. data/spec/mtk/pitch_spec.rb +59 -0
  76. data/spec/mtk/sequencer/abstract_sequencer_spec.rb +159 -0
  77. data/spec/mtk/sequencer/rhythmic_sequencer_spec.rb +49 -0
  78. data/spec/mtk/sequencer/step_sequencer_spec.rb +71 -0
  79. data/spec/mtk/timeline_spec.rb +118 -15
  80. data/spec/spec_helper.rb +4 -3
  81. metadata +59 -22
  82. data/lib/mtk/chord.rb +0 -47
  83. data/lib/mtk/constants/dynamics.rb +0 -56
  84. data/lib/mtk/constants/pitch_classes.rb +0 -18
  85. data/lib/mtk/constants/pitches.rb +0 -24
  86. data/lib/mtk/pattern/note_sequence.rb +0 -60
  87. data/lib/mtk/pattern/pitch_sequence.rb +0 -22
  88. data/lib/mtk/patterns.rb +0 -4
  89. data/spec/mtk/chord_spec.rb +0 -74
  90. data/spec/mtk/constants/pitch_classes_spec.rb +0 -35
  91. data/spec/mtk/constants/pitches_spec.rb +0 -23
  92. data/spec/mtk/pattern/note_sequence_spec.rb +0 -121
  93. data/spec/mtk/pattern/pitch_sequence_spec.rb +0 -47
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Pattern::AbstractPattern do
4
+
5
+ PATTERN = MTK::Pattern::AbstractPattern
6
+
7
+ let(:elements) { [1,2,3] }
8
+
9
+ describe "#type" do
10
+ it "is the :type value from the constuctor's options hash" do
11
+ PATTERN.new([], :type => :my_type).type.should == :my_type
12
+ end
13
+ end
14
+
15
+ describe "#max_elements" do
16
+ it "is the :max_elements option the pattern was constructed with" do
17
+ PATTERN.new([], :max_elements => 1).max_elements.should == 1
18
+ end
19
+
20
+ it "is nil by default" do
21
+ PATTERN.new([]).max_elements.should be_nil
22
+ end
23
+
24
+ it "causes a StopIteration exception after the number of elements have been emitted" do
25
+ cycle = PATTERN.new([:anything], :max_elements => 5)
26
+ 5.times { cycle.next }
27
+ lambda { cycle.next }.should raise_error
28
+ end
29
+ end
30
+
31
+ describe "#==" do
32
+ it "is true if the elements and types are equal" do
33
+ PATTERN.new(elements, :type => :some_type).should == PATTERN.new(elements, :type => :some_type)
34
+ end
35
+
36
+ it "is false if the elements are not equal" do
37
+ PATTERN.new(elements, :type => :some_type).should_not == PATTERN.new(elements + [4], :type => :some_type)
38
+ end
39
+
40
+ it "is false if the types are not equal" do
41
+ PATTERN.new(elements, :type => :some_type).should_not == PATTERN.new(elements, :type => :another_type)
42
+ end
43
+ end
44
+
45
+ end
@@ -3,8 +3,10 @@ require 'set'
3
3
 
4
4
  describe MTK::Pattern::Choice do
5
5
 
6
+ CHOICE = MTK::Pattern::Choice
7
+
6
8
  let(:elements) { [1,2,3] }
7
- let(:choice) { Pattern::Choice.new elements }
9
+ let(:choice) { CHOICE.new elements }
8
10
 
9
11
  describe "#next" do
10
12
  it "randomly chooses one of the elements" do
@@ -12,10 +14,94 @@ describe MTK::Pattern::Choice do
12
14
  100.times do
13
15
  element = choice.next
14
16
  choosen << element
15
- elements.should include(element)
16
17
  end
17
- choosen.to_a.sort.should == elements
18
+ choosen.to_a.should =~ elements
19
+ end
20
+
21
+ it "does a weighted random selection when a weights list is passed in via constructor options[:weights]" do
22
+ choice = CHOICE.new elements, :weights => [1,3,0] # only choose first 2 elements, and choose the second three times as often
23
+ choosen = Set.new
24
+ first_count, second_count = 0,0
25
+ 100.times do
26
+ element = choice.next
27
+ choosen << element
28
+ first_count += 1 if element == elements[0]
29
+ second_count += 1 if element == elements[1]
30
+ end
31
+ choosen.to_a.should =~ elements[0..1]
32
+ (first_count + 10).should < second_count # this may occasional fail, but it seems unlikely
33
+ end
34
+
35
+ it "enumerates a choosen sub-sequence" do
36
+ choice = CHOICE.new [MTK::Pattern.Sequence(4,5,6)]
37
+ choice.next.should == 4
38
+ choice.next.should == 5
39
+ choice.next.should == 6
40
+ choice.next.should == 4 # now it will select a new choice, but there's only one, so it will be the same
41
+ choice.next.should == 5
42
+ choice.next.should == 6
18
43
  end
19
44
  end
20
45
 
21
46
  end
47
+
48
+
49
+ describe MTK::Pattern do
50
+
51
+ describe "#Choice" do
52
+ it "creates a Choice" do
53
+ MTK::Pattern.Choice(1,2,3).should be_a MTK::Pattern::Choice
54
+ end
55
+
56
+ it "sets #elements from the varargs" do
57
+ MTK::Pattern.Choice(1,2,3).elements.should == [1,2,3]
58
+ end
59
+
60
+ it "does not set a type" do
61
+ MTK::Pattern.Choice(1,2,3).type.should be_nil
62
+ end
63
+ end
64
+
65
+ describe "#PitchChoice" do
66
+ it "creates a Choice" do
67
+ MTK::Pattern.PitchChoice(1,2,3).should be_a MTK::Pattern::Choice
68
+ end
69
+
70
+ it "sets #elements from the varargs" do
71
+ MTK::Pattern.PitchChoice(1,2,3).elements.should == [1,2,3]
72
+ end
73
+
74
+ it "sets #type to :pitch" do
75
+ MTK::Pattern.PitchChoice([]).type.should == :pitch
76
+ end
77
+ end
78
+
79
+ describe "#IntensityChoice" do
80
+ it "creates a Choice" do
81
+ MTK::Pattern.IntensityChoice(1,2,3).should be_a MTK::Pattern::Choice
82
+ end
83
+
84
+ it "sets #elements from the varargs" do
85
+ MTK::Pattern.IntensityChoice(1,2,3).elements.should == [1,2,3]
86
+ end
87
+
88
+ it "sets #type to :pitch" do
89
+ MTK::Pattern.IntensityChoice([]).type.should == :intensity
90
+ end
91
+ end
92
+
93
+ describe "#DurationChoice" do
94
+ it "creates a Choice" do
95
+ MTK::Pattern.DurationChoice(1,2,3).should be_a MTK::Pattern::Choice
96
+ end
97
+
98
+ it "sets #elements from the varargs" do
99
+ MTK::Pattern.DurationChoice(1,2,3).elements.should == [1,2,3]
100
+ end
101
+
102
+ it "sets #type to :pitch" do
103
+ MTK::Pattern.DurationChoice([]).type.should == :duration
104
+ end
105
+ end
106
+
107
+ end
@@ -0,0 +1,133 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Pattern::Cycle do
4
+
5
+ CYCLE = MTK::Pattern::Cycle
6
+
7
+ let(:elements) { [1, 2, 3] }
8
+ let(:cycle) { CYCLE.new elements }
9
+
10
+ describe "#next" do
11
+ it "iterates through the list of elements and emits them one at a time" do
12
+ cycle.next.should == elements[0]
13
+ cycle.next.should == elements[1]
14
+ cycle.next.should == elements[2]
15
+ end
16
+
17
+ it "starts at the beginning of the list of elements after the end of the list is reached" do
18
+ elements.length.times do
19
+ cycle.next
20
+ end
21
+ cycle.next.should == elements.first
22
+ end
23
+
24
+ it "enumerates nested sequences" do
25
+ cycle = CYCLE.new [1, MTK::Pattern.Sequence(2,3), 4]
26
+ nexts = []
27
+ 6.times { nexts << cycle.next }
28
+ nexts.should == [1,2,3,4,1,2]
29
+ end
30
+ end
31
+
32
+ describe "#max_cycles" do
33
+ it "is the :max_cycles option the pattern was constructed with" do
34
+ CYCLE.new([], :max_cycles => 1).max_cycles.should == 1
35
+ end
36
+
37
+ it "is nil by default" do
38
+ cycle.max_cycles.should be_nil
39
+ end
40
+
41
+ it "causes a StopIteration exception after the number of cycles has completed" do
42
+ cycle = CYCLE.new(elements, :max_cycles => 2)
43
+ 2.times do
44
+ elements.length.times { cycle.next } # one full cycle
45
+ end
46
+ lambda { cycle.next }.should raise_error
47
+ end
48
+
49
+ it "is maintained when applying Collection operations" do
50
+ CYCLE.new(elements, :max_cycles => 2).reverse.max_cycles.should == 2
51
+ end
52
+ end
53
+
54
+ describe "#max_elements" do
55
+ it "causes a StopIteration exception after the number of elements have been emitted" do
56
+ cycle = CYCLE.new(elements, :max_elements => 5)
57
+ 5.times { cycle.next }
58
+ lambda { cycle.next }.should raise_error
59
+ end
60
+ end
61
+
62
+ describe "#rewind" do
63
+ it "restarts the cycle" do
64
+ (elements.length - 1).times do
65
+ cycle.next
66
+ end
67
+ cycle.rewind
68
+ cycle.next.should == elements.first
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+
75
+ describe MTK::Pattern do
76
+
77
+ describe "#Cycle" do
78
+ it "creates a Cycle" do
79
+ MTK::Pattern.Cycle(1,2,3).should be_a MTK::Pattern::Cycle
80
+ end
81
+
82
+ it "sets #elements from the varargs" do
83
+ MTK::Pattern.Cycle(1,2,3).elements.should == [1,2,3]
84
+ end
85
+
86
+ it "does not set a type" do
87
+ MTK::Pattern.Cycle(1,2,3).type.should be_nil
88
+ end
89
+ end
90
+
91
+ describe "#PitchCycle" do
92
+ it "creates a Cycle" do
93
+ MTK::Pattern.PitchCycle(1,2,3).should be_a MTK::Pattern::Cycle
94
+ end
95
+
96
+ it "sets #elements from the varargs" do
97
+ MTK::Pattern.PitchCycle(1,2,3).elements.should == [1,2,3]
98
+ end
99
+
100
+ it "sets #type to :pitch" do
101
+ MTK::Pattern.PitchCycle([]).type.should == :pitch
102
+ end
103
+ end
104
+
105
+ describe "#IntensityCycle" do
106
+ it "creates a Cycle" do
107
+ MTK::Pattern.IntensityCycle(1,2,3).should be_a MTK::Pattern::Cycle
108
+ end
109
+
110
+ it "sets #elements from the varargs" do
111
+ MTK::Pattern.IntensityCycle(1,2,3).elements.should == [1,2,3]
112
+ end
113
+
114
+ it "sets #type to :pitch" do
115
+ MTK::Pattern.IntensityCycle([]).type.should == :intensity
116
+ end
117
+ end
118
+
119
+ describe "#DurationCycle" do
120
+ it "creates a Cycle" do
121
+ MTK::Pattern.DurationCycle(1,2,3).should be_a MTK::Pattern::Cycle
122
+ end
123
+
124
+ it "sets #elements from the varargs" do
125
+ MTK::Pattern.DurationCycle(1,2,3).elements.should == [1,2,3]
126
+ end
127
+
128
+ it "sets #type to :pitch" do
129
+ MTK::Pattern.DurationCycle([]).type.should == :duration
130
+ end
131
+ end
132
+
133
+ end
@@ -0,0 +1,133 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Pattern::Function do
4
+
5
+ FUNCTION = MTK::Pattern::Function
6
+
7
+ describe "#next" do
8
+ it "calls a lambda to produce elements" do
9
+ function = FUNCTION.new lambda{ 1 + 1 }
10
+ function.next.should == 2
11
+ function.next.should == 2
12
+ end
13
+
14
+ it "calls a 1-arg lambda with the previous value" do
15
+ function = FUNCTION.new lambda{|prev| (prev || 0) + 1 }
16
+ function.next.should == 1
17
+ function.next.should == 2
18
+ function.next.should == 3
19
+ end
20
+
21
+ it "stops looping when the lambda raises StopIteration" do
22
+ function = FUNCTION.new lambda{|prev| raise StopIteration if prev == 2; (prev || 0) + 1 }
23
+ nexts = []
24
+ loop do
25
+ nexts << function.next
26
+ end
27
+ nexts.should == [1,2]
28
+ end
29
+
30
+ it "calls a 2-arg lambda with the previous value and function call count (starting from 0)" do
31
+ function = FUNCTION.new lambda{|prev,index| [prev,index] }
32
+ function.next.should == [nil, 0]
33
+ function.next.should == [[nil,0], 1]
34
+ function.next.should == [[[nil,0],1], 2]
35
+ end
36
+
37
+ it "calls a 3-arg lambda with the previous value, function call count (starting from 0), and element count (starting from 0)" do
38
+ function = FUNCTION.new lambda{|prev,call_index,elem_index| prev.nil? ? MTK::Pattern.Sequence(1,2,3,4) : [call_index,elem_index] }
39
+ function.next.should == 1
40
+ function.next.should == 2
41
+ function.next.should == 3
42
+ function.next.should == 4
43
+ function.next.should == [1,4]
44
+ end
45
+
46
+ it "can generate other Patterns, which will be iterated over before re-calling the function" do
47
+ function = FUNCTION.new lambda{|prev,index| MTK::Pattern.Sequence(index,2,3) }
48
+ function.next.should == 0
49
+ function.next.should == 2
50
+ function.next.should == 3
51
+ function.next.should == 1 # end of sequence, now a new one is generated, this time starting with 1
52
+ function.next.should == 2
53
+ function.next.should == 3
54
+ end
55
+ end
56
+
57
+ describe "#rewind" do
58
+ it "resets previous value and element count" do
59
+ function = FUNCTION.new lambda{|prev,index| [prev,index] }
60
+ function.next.should == [nil, 0]
61
+ function.next.should == [[nil,0], 1]
62
+ function.rewind
63
+ function.next.should == [nil, 0]
64
+ end
65
+ end
66
+
67
+ end
68
+
69
+
70
+ describe MTK::Pattern do
71
+
72
+ describe "#Function" do
73
+ it "creates a Function" do
74
+ MTK::Pattern.Function(nil).should be_a MTK::Pattern::Function
75
+ end
76
+
77
+ it "sets #elements from the varargs" do
78
+ MTK::Pattern.Function(:mock_lambda).function.should == :mock_lambda
79
+ end
80
+
81
+ it "does not set a type" do
82
+ MTK::Pattern.Function(:mock_lambda).type.should be_nil
83
+ end
84
+
85
+ it "doesn't wrap a lambda in the varargs Array" do
86
+ function = MTK::Pattern.Function( lambda{ 1 + 1 } )
87
+ function.next.should == 2
88
+ end
89
+ end
90
+
91
+ describe "#PitchFunction" do
92
+ it "creates a Function" do
93
+ MTK::Pattern.PitchFunction(:mock_lambda).should be_a MTK::Pattern::Function
94
+ end
95
+
96
+ it "sets #elements from the varargs" do
97
+ MTK::Pattern.PitchFunction(:mock_lambda).function.should == :mock_lambda
98
+ end
99
+
100
+ it "sets #type to :pitch" do
101
+ MTK::Pattern.PitchFunction([]).type.should == :pitch
102
+ end
103
+ end
104
+
105
+ describe "#IntensityFunction" do
106
+ it "creates a Function" do
107
+ MTK::Pattern.IntensityFunction(:mock_lambda).should be_a MTK::Pattern::Function
108
+ end
109
+
110
+ it "sets #elements from the varargs" do
111
+ MTK::Pattern.IntensityFunction(:mock_lambda).function.should == :mock_lambda
112
+ end
113
+
114
+ it "sets #type to :pitch" do
115
+ MTK::Pattern.IntensityFunction([]).type.should == :intensity
116
+ end
117
+ end
118
+
119
+ describe "#DurationFunction" do
120
+ it "creates a Function" do
121
+ MTK::Pattern.DurationFunction(:mock_lambda).should be_a MTK::Pattern::Function
122
+ end
123
+
124
+ it "sets #elements from the varargs" do
125
+ MTK::Pattern.DurationFunction(:mock_lambda).function.should == :mock_lambda
126
+ end
127
+
128
+ it "sets #type to :pitch" do
129
+ MTK::Pattern.DurationFunction([]).type.should == :duration
130
+ end
131
+ end
132
+
133
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Pattern::Lines do
4
+
5
+ LINES = MTK::Pattern::Lines
6
+
7
+ let(:elements) { [0, [10,5], [5,10]] }
8
+ let(:lines) { LINES.new elements }
9
+
10
+ describe "#next" do
11
+ it "interpolates between values, treating each element as [value, steps_to_value] pairs" do
12
+ nexts = []
13
+ loop do
14
+ nexts << lines.next
15
+ end
16
+ nexts.should == [0, 2,4,6,8,10, 9.5,9,8.5,8,7.5,7,6.5,6,5.5,5]
17
+ end
18
+ end
19
+
20
+ describe "#rewind" do
21
+ it "starts the pattern from the beginning" do
22
+ 10.times { lines.next }
23
+ lines.rewind
24
+ nexts = []
25
+ loop do
26
+ nexts << lines.next
27
+ end
28
+ nexts.should == [0, 2,4,6,8,10, 9.5,9,8.5,8,7.5,7,6.5,6,5.5,5]
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+
35
+ describe MTK::Pattern do
36
+
37
+ describe "#Lines" do
38
+ it "creates a Lines" do
39
+ MTK::Pattern.Lines(1,2,3).should be_a MTK::Pattern::Lines
40
+ end
41
+
42
+ it "sets #elements from the varargs" do
43
+ MTK::Pattern.Lines(1,2,3).elements.should == [1,2,3]
44
+ end
45
+
46
+ it "does not set a type" do
47
+ MTK::Pattern.Lines(1,2,3).type.should be_nil
48
+ end
49
+ end
50
+
51
+ describe "#PitchLines" do
52
+ it "creates a Lines" do
53
+ MTK::Pattern.PitchLines(1,2,3).should be_a MTK::Pattern::Lines
54
+ end
55
+
56
+ it "sets #elements from the varargs" do
57
+ MTK::Pattern.PitchLines(1,2,3).elements.should == [1,2,3]
58
+ end
59
+
60
+ it "sets #type to :pitch" do
61
+ MTK::Pattern.PitchLines([]).type.should == :pitch
62
+ end
63
+ end
64
+
65
+ describe "#IntensityLines" do
66
+ it "creates a Lines" do
67
+ MTK::Pattern.IntensityLines(1,2,3).should be_a MTK::Pattern::Lines
68
+ end
69
+
70
+ it "sets #elements from the varargs" do
71
+ MTK::Pattern.IntensityLines(1,2,3).elements.should == [1,2,3]
72
+ end
73
+
74
+ it "sets #type to :pitch" do
75
+ MTK::Pattern.IntensityLines([]).type.should == :intensity
76
+ end
77
+ end
78
+
79
+ describe "#DurationLines" do
80
+ it "creates a Lines" do
81
+ MTK::Pattern.DurationLines(1,2,3).should be_a MTK::Pattern::Lines
82
+ end
83
+
84
+ it "sets #elements from the varargs" do
85
+ MTK::Pattern.DurationLines(1,2,3).elements.should == [1,2,3]
86
+ end
87
+
88
+ it "sets #type to :pitch" do
89
+ MTK::Pattern.DurationLines([]).type.should == :duration
90
+ end
91
+ end
92
+
93
+ end
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Pattern::NoteCycle do
4
+
5
+ def note(pitch, intensity=mf, duration=1)
6
+ Note(pitch, intensity, duration)
7
+ end
8
+
9
+ describe "#new" do
10
+ it "allows default pitch to be specified" do
11
+ cycle = Pattern::NoteCycle.new [], [p], [1], :pitch => Gb4
12
+ cycle.next.should == Note(Gb4, p, 1)
13
+ end
14
+ it "allows default intensity to be specified" do
15
+ cycle = Pattern::NoteCycle.new [C4], [], [1], :intensity => ppp
16
+ cycle.next.should == Note(C4, ppp, 1)
17
+ end
18
+ it "allows default duration to be specified" do
19
+ cycle = Pattern::NoteCycle.new [C4], [mf], [], :duration => 12
20
+ cycle.next.should == Note(C4, mf, 12)
21
+ end
22
+ end
23
+
24
+ describe "#next" do
25
+ it "iterates through the pitch, intensity, and duration list in parallel to emit Notes" do
26
+ cycle = Pattern::NoteCycle.new [C4, D4, E4], [p, f], [1,2,3,4]
27
+ cycle.next.should == Note(C4, p, 1)
28
+ cycle.next.should == Note(D4, f, 2)
29
+ cycle.next.should == Note(E4, p, 3)
30
+ cycle.next.should == Note(C4, f, 4)
31
+ cycle.next.should == Note(D4, p, 1)
32
+ cycle.next.should == Note(E4, f, 2)
33
+ end
34
+
35
+ it "defaults to Pitch 'C4' when no pitches are given" do
36
+ cycle = Pattern::NoteCycle.new [], [p,f], [1,2,3]
37
+ cycle.next.should == Note(C4, p, 1)
38
+ cycle.next.should == Note(C4, f, 2)
39
+ cycle.next.should == Note(C4, p, 3)
40
+ end
41
+
42
+ it "defaults to intensity 'mf' when no intensities are given" do
43
+ cycle = Pattern::NoteCycle.new [C4, D4, E4], nil, [2]
44
+ cycle.next.should == Note(C4, mf, 2)
45
+ cycle.next.should == Note(D4, mf, 2)
46
+ cycle.next.should == Note(E4, mf, 2)
47
+ end
48
+
49
+ it "defaults to duration 1 when no durations are given" do
50
+ cycle = Pattern::NoteCycle.new [C4, D4, E4], [p, f]
51
+ cycle.next.should == Note(C4, p, 1)
52
+ cycle.next.should == Note(D4, f, 1)
53
+ cycle.next.should == Note(E4, p, 1)
54
+ end
55
+
56
+ it "uses the previous pitch/intensity/duration when it encounters a nil value" do
57
+ cycle = Pattern::NoteCycle.new [C4, D4, E4, F4, nil], [mp, mf, f, nil], [1, 2, nil]
58
+ cycle.next.should == Note(C4, mp, 1)
59
+ cycle.next.should == Note(D4, mf, 2)
60
+ cycle.next.should == Note(E4, f, 2)
61
+ cycle.next.should == Note(F4, f, 1)
62
+ cycle.next.should == Note(F4, mp, 2)
63
+ cycle.next.should == Note(C4, mf, 2)
64
+ end
65
+
66
+ it "adds Numeric intervals in the pitch list to the previous pitch" do
67
+ cycle = Pattern::NoteCycle.new [C4, 1, 2, 3]
68
+ cycle.next.should == note(C4)
69
+ cycle.next.should == note(C4+1)
70
+ cycle.next.should == note(C4+1+2)
71
+ cycle.next.should == note(C4+1+2+3)
72
+ cycle.next.should == note(C4)
73
+ end
74
+
75
+ it "goes to the nearest Pitch for any PitchClasses in the pitch list" do
76
+ cycle = Pattern::NoteCycle.new [C4, F, C, G, C]
77
+ cycle.next.should == note(C4)
78
+ cycle.next.should == note(F4)
79
+ cycle.next.should == note(C4)
80
+ cycle.next.should == note(G3)
81
+ cycle.next.should == note(C4)
82
+ end
83
+
84
+ it "does not endlessly ascend or descend when alternating between two pitch classes a tritone apart" do
85
+ cycle = Pattern::NoteCycle.new [C4, Gb, C, Gb, C]
86
+ cycle.next.should == note(C4)
87
+ cycle.next.should == note(Gb4)
88
+ cycle.next.should == note(C4)
89
+ cycle.next.should == note(Gb4)
90
+ cycle.next.should == note(C4)
91
+ end
92
+
93
+ it "cycles Note Arrays for pitch list items that are PitchSets" do
94
+ cycle = Pattern::NoteCycle.new [PitchSet.new([C4, E4, G4]), C4, PitchSet.new([D4, F4, A4])]
95
+ cycle.next.should == [note(C4), note(E4), note(G4)]
96
+ cycle.next.should == note(C4)
97
+ cycle.next.should == [note(D4), note(F4), note(A4)]
98
+ end
99
+
100
+ it "adds numeric intervals to PitchSets" do
101
+ cycle = Pattern::NoteCycle.new [PitchSet.new([C4, E4, G4]), 2]
102
+ cycle.next.should == [note(C4), note(E4), note(G4)]
103
+ cycle.next.should == [note(D4), note(Gb4), note(A4)]
104
+ end
105
+
106
+ it "goes to the nearest Pitch relative to the lowest note in the PitchSet for any PitchClasses in the pitch list" do
107
+ cycle = Pattern::NoteCycle.new [PitchSet.new([C4, E4, G4]), F, D, Bb]
108
+ cycle.next.should == [note(C4), note(E4), note(G4)]
109
+ cycle.next.should == [note(F4), note(A4), note(C5)]
110
+ cycle.next.should == [note(D4), note(Gb4), note(A4)]
111
+ cycle.next.should == [note(Bb3), note(D4), note(F4)]
112
+ end
113
+ end
114
+
115
+
116
+ end