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.
- 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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
+
|