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,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
+