jmtk 0.0.3.3-java

Sign up to get free protection for your applications and to get access to all the features.
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,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Sequencers::LegatoSequencer do
4
+
5
+ LEGATO_SEQUENCER = Sequencers::LegatoSequencer
6
+
7
+ let(:pitches) { Patterns.PitchSequence(C4, D4, E4, C4) }
8
+ let(:durations) { Patterns.DurationSequence(1, 0.5, 1.5, 4) }
9
+ let(:intensities) { Patterns.IntensitySequence(0.3, 0.6, 0.9, 1.0) }
10
+ let(:legato_sequencer) { LEGATO_SEQUENCER.new [pitches, durations, intensities] }
11
+
12
+
13
+ describe "#to_timeline" do
14
+ it "contains notes assembled from the given patterns, with Timeline time deltas from the max event duration at the previous step" do
15
+ legato_sequencer.to_timeline.should == MTK::Events::Timeline.from_h({
16
+ 0 => Note(C4,1,0.3),
17
+ 1.0 => Note(D4,0.5,0.6),
18
+ 1.5 => Note(E4,1.5,0.9),
19
+ 3.0 => Note(C4,4,1.0)
20
+ })
21
+ end
22
+
23
+ it "treats negative durations as rests" do
24
+ legato_sequencer = LEGATO_SEQUENCER.new( [pitches, Patterns.DurationSequence(-1,-0.5,-1.5,4), intensities] )
25
+ legato_sequencer.to_timeline.should == MTK::Events::Timeline.from_h({
26
+ 3.0 => Note(C4,4,1.0)
27
+ })
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+
34
+ describe MTK::Sequencers do
35
+
36
+ describe "#LegatoSequencer" do
37
+ it "creates a LegatoSequencer" do
38
+ MTK::Sequencers.LegatoSequencer(1,2,3, rhythm:1).should be_a MTK::Sequencers::LegatoSequencer
39
+ end
40
+
41
+ it "sets #patterns from the varargs" do
42
+ MTK::Sequencers.LegatoSequencer(1,2,3, rhythm:1).patterns.should == [1,2,3]
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Sequencers::RhythmicSequencer do
4
+
5
+ RHYTHMIC_SEQUENCER = Sequencers::RhythmicSequencer
6
+
7
+ let(:pitches) { Patterns.PitchSequence(C4, D4, E4, C4) }
8
+ let(:intensities) { Patterns.IntensitySequence(0.3, 0.6, 0.9, 1.0) }
9
+ let(:durations) { Patterns.DurationSequence(1, 1, 2, 1) }
10
+ let(:rhythm) { Patterns.RhythmSequence(0.5, 1.5, 4) }
11
+ let(:rhythmic_sequencer) { RHYTHMIC_SEQUENCER.new [pitches, intensities, durations], rhythm: rhythm }
12
+
13
+ describe "#new" do
14
+ it "defaults @max_steps to nil" do
15
+ rhythmic_sequencer.max_steps.should be_nil
16
+ end
17
+
18
+ it "sets @max_steps from the options hash" do
19
+ rhythmic_sequencer = RHYTHMIC_SEQUENCER.new [], rhythm: :mock_pattern, max_steps: 4
20
+ rhythmic_sequencer.max_steps.should == 4
21
+ end
22
+ end
23
+
24
+ describe "#to_timeline" do
25
+ it "returns a Timeline" do
26
+ rhythmic_sequencer.to_timeline.should be_a MTK::Events::Timeline
27
+ end
28
+
29
+ it "contains notes assembled from the given patterns, with Timeline time deltas from the :rhythm type pattern" do
30
+ rhythmic_sequencer.to_timeline.should == MTK::Events::Timeline.from_h({
31
+ 0 => Note(C4,1,0.3),
32
+ 0.5 => Note(D4,1,0.6),
33
+ 2.0 => Note(E4,2,0.9),
34
+ 6.0 => Note(C4,1,1.0)
35
+ })
36
+ end
37
+
38
+
39
+ it "uses the absolute value of any negative durations in the rhythm pattern" do
40
+ timeline = RHYTHMIC_SEQUENCER.new( [pitches, intensities, durations], rhythm: Patterns.RhythmSequence(-0.5, 1.5, -4) ).to_timeline
41
+ timeline.should == MTK::Events::Timeline.from_h({
42
+ 0 => Note(C4,1,0.3),
43
+ 0.5 => Note(D4,1,0.6),
44
+ 2.0 => Note(E4,2,0.9),
45
+ 6.0 => Note(C4,1,1.0)
46
+ })
47
+ end
48
+ end
49
+
50
+ describe "#max_steps" do
51
+ it "controls the maximum number of times in the generated timeline" do
52
+ rhythmic_sequencer.max_steps = 2
53
+ rhythmic_sequencer.to_timeline.should == MTK::Events::Timeline.from_h({
54
+ 0 => Note(C4,1,0.3),
55
+ 0.5 => Note(D4,1,0.6)
56
+ })
57
+ end
58
+ end
59
+
60
+ describe "#rewind" do
61
+ it "rewinds the rhythm pattern (in addition to normal #rewind behavior)" do
62
+ rhythm.length.times{ rhythmic_sequencer.send :advance }
63
+ # now the next call would normally throw a StopIteration exception
64
+ rhythmic_sequencer.rewind
65
+ rhythm.length.times{ rhythmic_sequencer.send :advance }
66
+ # if we didn't get an exception, then #rewind did it's job
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+
73
+ describe MTK::Sequencers do
74
+
75
+ describe "#RhythmicSequencer" do
76
+ it "creates a RhythmicSequencer" do
77
+ MTK::Sequencers.RhythmicSequencer(1,2,3, rhythm:1).should be_a MTK::Sequencers::RhythmicSequencer
78
+ end
79
+
80
+ it "sets #patterns from the varargs" do
81
+ MTK::Sequencers.RhythmicSequencer(1,2,3, rhythm:1).patterns.should == [1,2,3]
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,215 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Sequencers::Sequencer do
4
+
5
+ ABSTRACT_SEQUENCER = Sequencers::Sequencer
6
+
7
+ class MockEventBuilder < Patterns::Chain
8
+ attr_accessor :mock_attribute
9
+ end
10
+
11
+ let(:patterns) { [Patterns.PitchCycle(C4,D4)] }
12
+ let(:sequencer) { ABSTRACT_SEQUENCER.new patterns }
13
+ let(:pitch) { ::MTK::Sequencers::EventBuilder::DEFAULT_PITCH }
14
+ let(:intensity) { ::MTK::Sequencers::EventBuilder::DEFAULT_INTENSITY }
15
+ let(:duration) { ::MTK::Sequencers::EventBuilder::DEFAULT_DURATION }
16
+
17
+ describe "#new" do
18
+ it "defaults @max_steps to nil" do
19
+ sequencer.max_steps.should be_nil
20
+ end
21
+
22
+ it "sets @max_steps from the options hash" do
23
+ sequencer = ABSTRACT_SEQUENCER.new patterns, :max_steps => 4
24
+ sequencer.max_steps.should == 4
25
+ end
26
+
27
+ it "defaults @max_time to nil" do
28
+ sequencer.max_time.should be_nil
29
+ end
30
+
31
+ it "sets @max_time from the options hash" do
32
+ sequencer = ABSTRACT_SEQUENCER.new patterns, :max_time => 4
33
+ sequencer.max_time.should == 4
34
+ end
35
+
36
+ it "defaults @event_builder to ::MTK::Sequencers::EventBuilder" do
37
+ sequencer.event_builder.should be_a ::MTK::Sequencers::EventBuilder
38
+ end
39
+
40
+ it "sets @event_buidler from the options hash" do
41
+ sequencer = ABSTRACT_SEQUENCER.new patterns, :event_builder => MockEventBuilder
42
+ sequencer.event_builder.should be_a MockEventBuilder
43
+ end
44
+
45
+ it "allows default pitch to be specified" do
46
+ sequencer = ABSTRACT_SEQUENCER.new [Patterns.IntervalCycle(0)], :default_pitch => Gb4
47
+ sequencer.next.should == [Note(Gb4, intensity, duration)]
48
+ end
49
+ it "allows default intensity to be specified" do
50
+ sequencer = ABSTRACT_SEQUENCER.new [Patterns.IntervalCycle(0)], :default_intensity => ppp
51
+ sequencer.next.should == [Note(pitch, ppp, duration)]
52
+ end
53
+ it "allows default duration to be specified" do
54
+ sequencer = ABSTRACT_SEQUENCER.new [Patterns.IntervalCycle(0)], :default_duration => 5.25
55
+ sequencer.next.should == [Note(pitch, intensity, 5.25)]
56
+ end
57
+ end
58
+
59
+ describe "#event_builder" do
60
+ it "provides access to the internal EventBuilder" do
61
+ sequencer = ABSTRACT_SEQUENCER.new patterns, :event_builder => MockEventBuilder
62
+ sequencer.event_builder.mock_attribute = :value
63
+ sequencer.event_builder.mock_attribute.should == :value
64
+ end
65
+ end
66
+
67
+ describe "#to_timeline" do
68
+ it "combines pitch, intensity, and duration patterns into notes" do
69
+ pitches = Patterns.PitchSequence(C4, D4, E4)
70
+ intensities = Patterns.IntensitySequence(0.3, 0.7, 1.0)
71
+ durations = Patterns.DurationSequence(1, 2, 3)
72
+ sequencer = ABSTRACT_SEQUENCER.new [pitches, intensities, durations]
73
+ # default implementation just increments the time by 1 for each event (more interesting behavior is provided by subclasses)
74
+ sequencer.to_timeline.should == {
75
+ 0.0 => [Note(C4,1,0.3)],
76
+ 1.0 => [Note(D4,2,0.7)],
77
+ 2.0 => [Note(E4,3,1.0)]
78
+ }
79
+ end
80
+
81
+ it "combines patterns of different types and lengths" do
82
+ pitches = Patterns.PitchSequence(C4, D4, E4, F4, G4, A4, B4, C5)
83
+ intensities = Patterns.IntensityCycle(0.5, 1.0)
84
+ durations = Patterns.DurationPalindrome(1, 2, 3)
85
+ sequencer = ABSTRACT_SEQUENCER.new [pitches, intensities, durations]
86
+ # default implementation just increments the time by 1 for each event (more interesting behavior is provided by subclasses)
87
+ sequencer.to_timeline.should == {
88
+ 0.0 => [Note(C4,1,0.5)],
89
+ 1.0 => [Note(D4,2,1.0)],
90
+ 2.0 => [Note(E4,3,0.5)],
91
+ 3.0 => [Note(F4,2,1.0)],
92
+ 4.0 => [Note(G4,1,0.5)],
93
+ 5.0 => [Note(A4,2,1.0)],
94
+ 6.0 => [Note(B4,3,0.5)],
95
+ 7.0 => [Note(C5,2,1.0)]
96
+ }
97
+ end
98
+
99
+ it "produces consistent results by reseting the patterns each time" do
100
+ pitches = Patterns.PitchSequence(C4, D4, E4)
101
+ intensities = Patterns.IntensityCycle(1)
102
+ durations = Patterns.DurationCycle(1, 2)
103
+ sequencer = ABSTRACT_SEQUENCER.new [pitches, intensities, durations]
104
+ sequencer.to_timeline.should == sequencer.to_timeline
105
+ end
106
+ end
107
+
108
+ describe "#time" do
109
+ it "is the current timeline time that the sequencer is generating events for" do
110
+ # AbstractSequencer just advances by 1 each step
111
+ sequencer.next # time doesn't advance until the second #next call
112
+ sequencer.time.should == 0
113
+ sequencer.next
114
+ sequencer.time.should == 1
115
+ sequencer.next
116
+ sequencer.time.should == 2
117
+ end
118
+ end
119
+
120
+ describe "#step" do
121
+ it "is the index for how many of times #next has been called (i.e. count starting from 0)" do
122
+ sequencer.step.should == -1 # -1 indicates #next has not yet been called
123
+ sequencer.next
124
+ sequencer.step.should == 0
125
+ sequencer.next
126
+ sequencer.step.should == 1
127
+ sequencer.next
128
+ sequencer.step.should == 2
129
+ end
130
+ end
131
+
132
+ describe "#next" do
133
+ it "returns a list of notes formed from the patterns in the sequencer" do
134
+ sequencer.next.should == [Note(C4,intensity,duration)]
135
+ sequencer.next.should == [Note(D4,intensity,duration)]
136
+ sequencer.next.should == [Note(C4,intensity,duration)]
137
+ end
138
+
139
+ it "returns a filtered list of notes if the sequencer was constructed with a options[:filter] lambda" do
140
+ sequencer = ABSTRACT_SEQUENCER.new patterns, :filter => lambda{|events| events.map{|event| event.transpose(P8) } }
141
+ sequencer.next.should == [Note(C5,intensity,duration)]
142
+ sequencer.next.should == [Note(D5,intensity,duration)]
143
+ end
144
+
145
+ context "pitch patterns" do
146
+ it "adds Numeric elements (intervals) to the previous pitch" do
147
+ sequencer = ABSTRACT_SEQUENCER.new [Patterns.Cycle(C4, m2, M2, m3)]
148
+ sequencer.next.should == [Note(C4,intensity,duration)]
149
+ sequencer.next.should == [Note(C4+1,intensity,duration)]
150
+ sequencer.next.should == [Note(C4+1+2,intensity,duration)]
151
+ sequencer.next.should == [Note(C4+1+2+3,intensity,duration)]
152
+ end
153
+
154
+ it "returns a note with the given pitch when encountering a Pitch after another type" do
155
+ sequencer = ABSTRACT_SEQUENCER.new [Patterns.Cycle(C4, m2, C4)]
156
+ sequencer.next
157
+ sequencer.next
158
+ sequencer.next.should == [Note(C4,intensity,duration)]
159
+ end
160
+
161
+ it "goes to the nearest Pitch for any PitchClasses in the pitch list" do
162
+ sequencer = ABSTRACT_SEQUENCER.new [Patterns.Cycle(C4, F, C, G, C)]
163
+ sequencer.next.should == [Note(C4,intensity,duration)]
164
+ sequencer.next.should == [Note(F4,intensity,duration)]
165
+ sequencer.next.should == [Note(C4,intensity,duration)]
166
+ sequencer.next.should == [Note(G3,intensity,duration)]
167
+ sequencer.next.should == [Note(C4,intensity,duration)]
168
+ end
169
+
170
+ it "does not endlessly ascend or descend when alternating between two pitch classes a tritone apart" do
171
+ sequencer = ABSTRACT_SEQUENCER.new [Patterns.Cycle(C4, Gb, C, Gb, C)]
172
+ sequencer.next.should == [Note(C4,intensity,duration)]
173
+ sequencer.next.should == [Note(Gb4,intensity,duration)]
174
+ sequencer.next.should == [Note(C4,intensity,duration)]
175
+ sequencer.next.should == [Note(Gb4,intensity,duration)]
176
+ sequencer.next.should == [Note(C4,intensity,duration)]
177
+ end
178
+ end
179
+
180
+ end
181
+
182
+ describe "#rewind" do
183
+ it "resets the sequencer and its patterns" do
184
+ sequencer.next
185
+ sequencer.rewind
186
+ sequencer.step.should == -1
187
+ sequencer.time.should == 0
188
+ sequencer.next.should == [Note(C4,intensity,duration)]
189
+ end
190
+
191
+ it "resets pitches properly for patterns that rely on previous pitches" do
192
+ relative_pitch_pattern = Patterns.Sequence(C,P8)
193
+ sequencer = ABSTRACT_SEQUENCER.new [relative_pitch_pattern]
194
+ sequencer.next.should == [Note(C4,intensity,duration)]
195
+ sequencer.next.should == [Note(C5,intensity,duration)]
196
+ sequencer.rewind
197
+ sequencer.next.should == [Note(C4,intensity,duration)] # if the internal EventChain is not properly reset, the Note would be C5
198
+ end
199
+ end
200
+
201
+ describe "#max_steps" do
202
+ it "controls the maximum number of entries in the generated timeline" do
203
+ sequencer.max_steps = 2
204
+ sequencer.to_timeline.times.length.should == 2
205
+ end
206
+ end
207
+
208
+ describe "#max_time" do
209
+ it "controls the maximum time in the generated timeline" do
210
+ sequencer.max_time = 4
211
+ sequencer.to_timeline.times.last.should == 4
212
+ end
213
+ end
214
+
215
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Sequencers::StepSequencer do
4
+
5
+ STEP_SEQUENCER = Sequencers::StepSequencer
6
+
7
+ let(:pitches) { Patterns.PitchSequence(C4, D4, E4) }
8
+ let(:intensities) { Patterns.IntensitySequence(0.3, 0.7, 1.0) }
9
+ let(:durations) { Patterns.DurationSequence(1, 1, 2) }
10
+ let(:step_sequencer) { STEP_SEQUENCER.new [pitches, intensities, durations] }
11
+
12
+ describe "#new" do
13
+ it "defaults @step_size to 1" do
14
+ step_sequencer.step_size.should == 1
15
+ end
16
+
17
+ it "sets @step_size from the options hash" do
18
+ step_sequencer = STEP_SEQUENCER.new [], :step_size => 0.25
19
+ step_sequencer.step_size.should == 0.25
20
+ end
21
+
22
+ it "defaults @max_steps to nil" do
23
+ step_sequencer.max_steps.should be_nil
24
+ end
25
+
26
+ it "sets @max_steps from the options hash" do
27
+ step_sequencer = STEP_SEQUENCER.new [], :max_steps => 4
28
+ step_sequencer.max_steps.should == 4
29
+ end
30
+ end
31
+
32
+ describe "#to_timeline" do
33
+ it "returns a Timeline" do
34
+ timeline = step_sequencer.to_timeline
35
+ timeline.should be_a MTK::Events::Timeline
36
+ end
37
+
38
+ it "contains notes assembled from the given patterns" do
39
+ timeline = step_sequencer.to_timeline
40
+ timeline.should == MTK::Events::Timeline.from_h({
41
+ 0 => Note(C4,1,0.3),
42
+ 1 => Note(D4,1,0.7),
43
+ 2 => Note(E4,2,1.0)
44
+ })
45
+ end
46
+
47
+ it "treats negative durations as rests" do
48
+ timeline = STEP_SEQUENCER.new( [pitches, intensities, Patterns.DurationSequence(1, -1, 2)] ).to_timeline
49
+ timeline.should == MTK::Events::Timeline.from_h({
50
+ 0 => Note(C4,1,0.3),
51
+ 2 => Note(E4,2,1.0)
52
+ })
53
+ end
54
+ end
55
+
56
+ describe "#step_size" do
57
+ it "controls the delta between each time in the generated timeline" do
58
+ step_sequencer.step_size = 2
59
+ timeline = step_sequencer.to_timeline
60
+ timeline.should == MTK::Events::Timeline.from_h({
61
+ 0 => Note(C4,1,0.3),
62
+ 2 => Note(D4,1,0.7),
63
+ 4 => Note(E4,2,1.0)
64
+ })
65
+ end
66
+ end
67
+
68
+ describe "#max_steps" do
69
+ it "controls the maximum number of times in the generated timeline" do
70
+ step_sequencer.max_steps = 2
71
+ timeline = step_sequencer.to_timeline
72
+ timeline.should == MTK::Events::Timeline.from_h({
73
+ 0 => Note(C4,1,0.3),
74
+ 1 => Note(D4,1,0.7)
75
+ })
76
+ end
77
+ end
78
+
79
+ end
80
+
81
+
82
+ describe MTK::Sequencers do
83
+
84
+ describe "#StepSequencer" do
85
+ it "creates a StepSequencer" do
86
+ MTK::Sequencers.StepSequencer(1,2,3).should be_a MTK::Sequencers::StepSequencer
87
+ end
88
+
89
+ it "sets #patterns from the varargs" do
90
+ MTK::Sequencers.StepSequencer(1,2,3).patterns.should == [1,2,3]
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,2 @@
1
+ at_exit { CoverMe.complete! }
2
+ require 'cover_me'
@@ -0,0 +1,12 @@
1
+ $__RUNNING_RSPEC_TESTS__ = true
2
+
3
+ require 'mtk'
4
+ include MTK
5
+ include MTK::Core
6
+ include MTK::Lang
7
+ include MTK::Lang::PitchClasses
8
+ include MTK::Lang::Pitches
9
+ include MTK::Lang::Intensities
10
+ include MTK::Lang::Durations
11
+ include MTK::Lang::Intervals
12
+