mtk 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +3 -2
- data/DEVELOPMENT_NOTES.md +114 -0
- data/INTRO.md +64 -8
- data/LICENSE.txt +1 -1
- data/README.md +31 -102
- data/Rakefile +56 -18
- data/bin/mtk +215 -0
- data/examples/crescendo.rb +5 -5
- data/examples/drum_pattern1.rb +23 -0
- data/examples/dynamic_pattern.rb +8 -11
- data/examples/gets_and_play.rb +26 -0
- data/examples/notation.rb +22 -0
- data/examples/play_midi.rb +8 -10
- data/examples/random_tone_row.rb +2 -2
- data/examples/syntax_to_midi.rb +28 -0
- data/examples/test_output.rb +8 -0
- data/examples/tone_row_melody.rb +6 -6
- data/lib/mtk.rb +52 -40
- data/lib/mtk/chord.rb +55 -0
- data/lib/mtk/constants/durations.rb +57 -0
- data/lib/mtk/constants/intensities.rb +61 -0
- data/lib/mtk/constants/intervals.rb +73 -0
- data/lib/mtk/constants/pitch_classes.rb +29 -0
- data/lib/mtk/constants/pitches.rb +52 -0
- data/lib/mtk/duration.rb +211 -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/helpers/collection.rb +164 -0
- data/lib/mtk/helpers/convert.rb +36 -0
- data/lib/mtk/helpers/lilypond.rb +162 -0
- data/lib/mtk/helpers/output_selector.rb +67 -0
- data/lib/mtk/helpers/pitch_collection.rb +23 -0
- data/lib/mtk/helpers/pseudo_constants.rb +26 -0
- data/lib/mtk/intensity.rb +156 -0
- data/lib/mtk/interval.rb +155 -0
- data/lib/mtk/lang/mtk_grammar.citrus +190 -13
- data/lib/mtk/lang/parser.rb +29 -0
- data/lib/mtk/melody.rb +94 -0
- data/lib/mtk/midi/dls_synth_device.rb +144 -0
- data/lib/mtk/midi/dls_synth_output.rb +62 -0
- data/lib/mtk/midi/file.rb +67 -32
- data/lib/mtk/midi/input.rb +97 -0
- data/lib/mtk/midi/jsound_input.rb +36 -17
- data/lib/mtk/midi/jsound_output.rb +48 -46
- data/lib/mtk/midi/output.rb +195 -0
- data/lib/mtk/midi/unimidi_input.rb +117 -0
- data/lib/mtk/midi/unimidi_output.rb +121 -0
- data/lib/mtk/{_numeric_extensions.rb → numeric_extensions.rb} +12 -0
- data/lib/mtk/patterns/chain.rb +49 -0
- data/lib/mtk/{pattern → patterns}/choice.rb +14 -8
- 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/{pattern → patterns}/lines.rb +11 -17
- data/lib/mtk/{pattern → patterns}/palindrome.rb +11 -8
- data/lib/mtk/patterns/pattern.rb +171 -0
- data/lib/mtk/patterns/sequence.rb +20 -0
- data/lib/mtk/pitch.rb +7 -6
- data/lib/mtk/pitch_class.rb +124 -46
- data/lib/mtk/pitch_class_set.rb +58 -35
- data/lib/mtk/sequencers/event_builder.rb +131 -0
- data/lib/mtk/sequencers/legato_sequencer.rb +24 -0
- data/lib/mtk/sequencers/rhythmic_sequencer.rb +28 -0
- data/lib/mtk/{sequencer/abstract_sequencer.rb → sequencers/sequencer.rb} +37 -11
- data/lib/mtk/{sequencer → sequencers}/step_sequencer.rb +4 -4
- data/lib/mtk/timeline.rb +39 -22
- data/lib/mtk/variable.rb +32 -0
- data/spec/mtk/chord_spec.rb +83 -0
- data/spec/mtk/{_constants → constants}/durations_spec.rb +12 -41
- data/spec/mtk/{_constants → constants}/intensities_spec.rb +13 -37
- data/spec/mtk/{_constants → constants}/intervals_spec.rb +14 -32
- data/spec/mtk/{_constants → constants}/pitch_classes_spec.rb +8 -4
- data/spec/mtk/{_constants → constants}/pitches_spec.rb +5 -1
- data/spec/mtk/duration_spec.rb +372 -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/{helper → helpers}/collection_spec.rb +86 -3
- data/spec/mtk/{helper → helpers}/pseudo_constants_spec.rb +2 -2
- data/spec/mtk/intensity_spec.rb +289 -0
- data/spec/mtk/interval_spec.rb +265 -0
- data/spec/mtk/lang/parser_spec.rb +597 -0
- data/spec/mtk/melody_spec.rb +223 -0
- data/spec/mtk/midi/file_spec.rb +16 -16
- data/spec/mtk/midi/jsound_input_spec.rb +11 -0
- data/spec/mtk/midi/jsound_output_spec.rb +11 -0
- data/spec/mtk/midi/output_spec.rb +102 -0
- data/spec/mtk/midi/unimidi_input_spec.rb +11 -0
- data/spec/mtk/midi/unimidi_output_spec.rb +11 -0
- data/spec/mtk/{_numeric_extensions_spec.rb → numeric_extensions_spec.rb} +1 -0
- data/spec/mtk/patterns/chain_spec.rb +110 -0
- data/spec/mtk/{pattern → patterns}/choice_spec.rb +20 -30
- data/spec/mtk/{pattern → patterns}/cycle_spec.rb +25 -35
- data/spec/mtk/patterns/for_each_spec.rb +136 -0
- data/spec/mtk/{pattern → patterns}/function_spec.rb +17 -30
- data/spec/mtk/{pattern → patterns}/lines_spec.rb +11 -27
- data/spec/mtk/{pattern → patterns}/palindrome_spec.rb +13 -29
- data/spec/mtk/patterns/pattern_spec.rb +132 -0
- data/spec/mtk/patterns/sequence_spec.rb +203 -0
- data/spec/mtk/pitch_class_set_spec.rb +23 -21
- data/spec/mtk/pitch_class_spec.rb +151 -39
- data/spec/mtk/pitch_spec.rb +22 -1
- 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/{sequencer → sequencers}/step_sequencer_spec.rb +35 -13
- data/spec/mtk/timeline_spec.rb +109 -16
- data/spec/mtk/variable_spec.rb +52 -0
- data/spec/spec_coverage.rb +2 -0
- data/spec/spec_helper.rb +3 -0
- metadata +188 -91
- data/lib/mtk/_constants/durations.rb +0 -80
- data/lib/mtk/_constants/intensities.rb +0 -81
- data/lib/mtk/_constants/intervals.rb +0 -85
- data/lib/mtk/_constants/pitch_classes.rb +0 -35
- data/lib/mtk/_constants/pitches.rb +0 -49
- data/lib/mtk/event.rb +0 -70
- data/lib/mtk/helper/collection.rb +0 -114
- data/lib/mtk/helper/event_builder.rb +0 -85
- data/lib/mtk/helper/pseudo_constants.rb +0 -26
- data/lib/mtk/lang/grammar.rb +0 -17
- data/lib/mtk/note.rb +0 -63
- data/lib/mtk/pattern/abstract_pattern.rb +0 -132
- data/lib/mtk/pattern/cycle.rb +0 -51
- data/lib/mtk/pattern/enumerator.rb +0 -26
- data/lib/mtk/pattern/function.rb +0 -46
- data/lib/mtk/pattern/sequence.rb +0 -30
- data/lib/mtk/pitch_set.rb +0 -84
- data/lib/mtk/sequencer/rhythmic_sequencer.rb +0 -29
- data/lib/mtk/transform/invertible.rb +0 -15
- data/lib/mtk/transform/mappable.rb +0 -18
- data/lib/mtk/transform/set_theory_operations.rb +0 -34
- data/lib/mtk/transform/transposable.rb +0 -14
- data/spec/mtk/event_spec.rb +0 -139
- data/spec/mtk/helper/event_builder_spec.rb +0 -92
- data/spec/mtk/lang/grammar_spec.rb +0 -100
- data/spec/mtk/note_spec.rb +0 -115
- data/spec/mtk/pattern/abstract_pattern_spec.rb +0 -45
- data/spec/mtk/pattern/note_cycle_spec.rb.bak +0 -116
- data/spec/mtk/pattern/pitch_cycle_spec.rb.bak +0 -47
- data/spec/mtk/pattern/pitch_sequence_spec.rb.bak +0 -37
- data/spec/mtk/pattern/sequence_spec.rb +0 -151
- data/spec/mtk/pitch_set_spec.rb +0 -198
- data/spec/mtk/sequencer/abstract_sequencer_spec.rb +0 -159
- data/spec/mtk/sequencer/rhythmic_sequencer_spec.rb +0 -49
@@ -0,0 +1,223 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MTK::Melody do
|
4
|
+
|
5
|
+
let(:pitches) { [C4, D4, E4, D4] }
|
6
|
+
let(:melody) { Melody.new(pitches) }
|
7
|
+
|
8
|
+
it "is Enumerable" do
|
9
|
+
melody.should be_a Enumerable
|
10
|
+
end
|
11
|
+
|
12
|
+
describe ".new" do
|
13
|
+
it "maintains the pitches collection exactly (preserves order and keeps duplicates)" do
|
14
|
+
Melody.new([C4, E4, G4, E4, B3, C4]).pitches.should == [C4, E4, G4, E4, B3, C4]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe ".from_pitch_classes" do
|
19
|
+
it "creates a pitch sequence from a list of pitch classes and starting point, selecting the nearest pitch to each pitch class" do
|
20
|
+
Melody.from_pitch_classes([C,G,B,Eb,D,C], D3).should == [C3,G2,B2,Eb3,D3,C3]
|
21
|
+
end
|
22
|
+
|
23
|
+
it "defaults to a starting point of C4 (middle C)" do
|
24
|
+
Melody.from_pitch_classes([C]).should == [C4]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "doesn't travel within an octave above or below the starting point by default" do
|
28
|
+
Melody.from_pitch_classes([C,F,Bb,D,A,E,B]).should == [C4,F4,Bb4,D4,A3,E3,B3]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "allows max distance above or below the starting point to be set via the third argument" do
|
32
|
+
Melody.from_pitch_classes([C,F,Bb,D,A,E,B], C4, 6).should == [C4,F4,Bb3,D4,A3,E4,B3]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#pitches' do
|
37
|
+
it 'is the list of pitches used to construct the scale' do
|
38
|
+
melody.pitches.should == pitches
|
39
|
+
end
|
40
|
+
|
41
|
+
it "is immutable" do
|
42
|
+
lambda { melody.pitches << Db4 }.should raise_error
|
43
|
+
end
|
44
|
+
|
45
|
+
it "does not affect the immutabilty of the pitch list used to construct it" do
|
46
|
+
list = melody # force construction with the pitches array
|
47
|
+
expect { pitches << Db4 }.to change(pitches, :length).by(1)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "is not affected by changes to the pitch list used to construct it" do
|
51
|
+
melody # force construction before we modify the pitches array
|
52
|
+
expect { pitches << Db4 }.to_not change(melody.pitches, :length)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#to_a" do
|
58
|
+
it "is equal to #pitches" do
|
59
|
+
melody.to_a.should == melody.pitches
|
60
|
+
end
|
61
|
+
|
62
|
+
it "is mutable" do
|
63
|
+
(melody.to_a << Bb3).should == [C4, D4, E4, D4, Bb3]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#each" do
|
68
|
+
it "yields each pitch" do
|
69
|
+
ps = []
|
70
|
+
melody.each{|p| ps << p }
|
71
|
+
ps.should == pitches
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "#map" do
|
76
|
+
it "returns a Melody with each Pitch replaced with the results of the block" do
|
77
|
+
melody.map{|p| p + 2}.should == [D4, E4, Gb4, E4]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#to_pitch_class_set" do
|
82
|
+
it "is a PitchClassSet" do
|
83
|
+
melody.to_pitch_class_set.should be_a PitchClassSet
|
84
|
+
end
|
85
|
+
|
86
|
+
it "contains all the distinct pitch_classes in this Melody by default" do
|
87
|
+
melody.to_pitch_class_set.pitch_classes.should == melody.pitch_classes.uniq
|
88
|
+
end
|
89
|
+
|
90
|
+
it "contains all the pitch_classes (including duplicates) when the argument is false" do
|
91
|
+
melody.to_pitch_class_set(false).pitch_classes.should == melody.pitch_classes
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
describe '#pitch_classes' do
|
97
|
+
it 'is the list of pitch classes for the pitches in this list' do
|
98
|
+
melody.pitch_classes.should == pitches.map { |p| p.pitch_class }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '#transpose' do
|
103
|
+
it 'transposes upward by the given semitones' do
|
104
|
+
melody.transpose(12).should == Melody.new([C5, D5, E5, D5])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '#invert' do
|
109
|
+
it 'inverts all pitches around the given center pitch' do
|
110
|
+
(melody.invert Gb4).should == Melody.new([C5, Bb4, Ab4, Bb4])
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'inverts all pitches around the first pitch, when no center pitch is given' do
|
114
|
+
melody.invert.should == Melody.new([C4, Bb3, Ab3, Bb3])
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '#include?' do
|
119
|
+
it 'returns true if the Pitch is in the Melody' do
|
120
|
+
(melody.include? C4).should be_true
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'returns false if the Pitch is not in the Melody' do
|
124
|
+
(melody.include? Db4).should be_false
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe '#==' do
|
129
|
+
it "is true when all the pitches are equal" do
|
130
|
+
Melody.new([C4, E4, G4]).should == Melody.new([Pitch.from_i(60), Pitch.from_i(64), Pitch.from_i(67)])
|
131
|
+
end
|
132
|
+
|
133
|
+
it "is false when not all the pitches are equal" do
|
134
|
+
Melody.new([C4, E4, G4]).should_not == Melody.new([Pitch.from_i(60), Pitch.from_i(65), Pitch.from_i(67)])
|
135
|
+
end
|
136
|
+
|
137
|
+
it "is false when if otherwise equal Melodies don't contain the same number of duplicates" do
|
138
|
+
Melody.new([C4, E4, G4]).should_not == Melody.new([C4, C4, E4, G4])
|
139
|
+
end
|
140
|
+
|
141
|
+
it "is false when if otherwise equal Melodies aren't in the same order" do
|
142
|
+
Melody.new([C4, E4, G4]).should_not == Melody.new([C4, G4, E4])
|
143
|
+
end
|
144
|
+
|
145
|
+
it "is false when the argument is not compatible" do
|
146
|
+
Melody.new([C4, E4, G4]).should_not == :invalid
|
147
|
+
end
|
148
|
+
|
149
|
+
it "can be compared directly to Arrays" do
|
150
|
+
Melody.new([C4, E4, G4]).should == [C4, E4, G4]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe "#=~" do
|
155
|
+
it "is true when all the pitches are equal" do
|
156
|
+
Melody.new([C4, E4, G4]).should =~ Melody.new([C4, E4, G4])
|
157
|
+
end
|
158
|
+
|
159
|
+
it "is true when all the pitches are equal, even with different numbers of duplicates" do
|
160
|
+
Melody.new([C4, E4, G4]).should =~ Melody.new([C4, C4, E4, G4])
|
161
|
+
end
|
162
|
+
|
163
|
+
it "is true when all the pitches are equal, even in a different order" do
|
164
|
+
Melody.new([C4, E4, G4]).should =~ Melody.new([C4, G4, E4])
|
165
|
+
end
|
166
|
+
|
167
|
+
it "is false when one Melody contains a Pitch not in the other" do
|
168
|
+
Melody.new([C4, E4, G4]).should_not =~ Melody.new([C4, E4])
|
169
|
+
end
|
170
|
+
|
171
|
+
it "can be compared directly to Arrays" do
|
172
|
+
Melody.new([C4, E4, G4]).should =~ [C4, E4, G4]
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "#to_s" do
|
177
|
+
it "looks like an array of pitches" do
|
178
|
+
melody.to_s.should == "[C4, D4, E4, D4]"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
describe MTK do
|
185
|
+
|
186
|
+
describe '#Melody' do
|
187
|
+
|
188
|
+
it "acts like new for a single Array argument" do
|
189
|
+
Melody([C4,D4]).should == Melody.new([C4,D4])
|
190
|
+
end
|
191
|
+
|
192
|
+
it "acts like new for multiple arguments, by treating them like an Array (splat)" do
|
193
|
+
Melody(C4,D4).should == Melody.new([C4,D4])
|
194
|
+
end
|
195
|
+
|
196
|
+
it "handles an Array with elements that can be converted to Pitches" do
|
197
|
+
Melody(['C4','D4']).should == Melody.new([C4,D4])
|
198
|
+
end
|
199
|
+
|
200
|
+
it "handles multiple arguments that can be converted to a Pitch" do
|
201
|
+
Melody(:C4,:D4).should == Melody.new([C4,D4])
|
202
|
+
end
|
203
|
+
|
204
|
+
it "handles a single Pitch" do
|
205
|
+
Melody(C4).should == Melody.new([C4])
|
206
|
+
end
|
207
|
+
|
208
|
+
it "handles single elements that can be converted to a Pitch" do
|
209
|
+
Melody('C4').should == Melody.new([C4])
|
210
|
+
end
|
211
|
+
|
212
|
+
it "handles a Melody" do
|
213
|
+
melody = Melody.new([C4,D4])
|
214
|
+
Melody(melody).should == [C4,D4]
|
215
|
+
end
|
216
|
+
|
217
|
+
it "raises an error for types it doesn't understand" do
|
218
|
+
lambda{ Melody({:not => :compatible}) }.should raise_error
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
data/spec/mtk/midi/file_spec.rb
CHANGED
@@ -33,10 +33,10 @@ describe MTK::MIDI::File do
|
|
33
33
|
|
34
34
|
it "converts note on/off messages to Note events" do
|
35
35
|
MIDI_File(test_mid).to_timelines.first.should == {
|
36
|
-
0.0 => [Note
|
37
|
-
1.0 => [Note
|
38
|
-
2.0 => [Note
|
39
|
-
3.0 => [Note
|
36
|
+
0.0 => [Note(C4, 0.25, 126/127.0)],
|
37
|
+
1.0 => [Note(Db4, 0.5, 99/127.0)],
|
38
|
+
2.0 => [Note(D4, 0.75, 72/127.0)],
|
39
|
+
3.0 => [Note(Eb4, 1.0, 46/127.0), Note(E4, 1.0, 46/127.0)]
|
40
40
|
}
|
41
41
|
end
|
42
42
|
end
|
@@ -45,9 +45,9 @@ describe MTK::MIDI::File do
|
|
45
45
|
it 'writes monophonic Notes in a Timeline to a MIDI file' do
|
46
46
|
MIDI_File(tempfile).write_timeline(
|
47
47
|
Timeline.from_hash({
|
48
|
-
0 => Note
|
49
|
-
1
|
50
|
-
2 => Note
|
48
|
+
0 => Note(C4, q, 0.7),
|
49
|
+
1 => Note(G4, q, 0.8),
|
50
|
+
2 => Note(C5, q, 0.9)
|
51
51
|
})
|
52
52
|
)
|
53
53
|
|
@@ -85,8 +85,8 @@ describe MTK::MIDI::File do
|
|
85
85
|
it 'writes polyphonic (simultaneous) Notes in a Timeline to a MIDI file' do
|
86
86
|
MIDI_File(tempfile).write_timeline(
|
87
87
|
Timeline.from_hash({
|
88
|
-
0 => [Note(C4,0.5
|
89
|
-
2.0 => [Note(G4,1
|
88
|
+
0 => [Note(C4,q,0.5), Note(E4,q,0.5)],
|
89
|
+
2.0 => [Note(G4,h,1), Note(B4,h,1), Note(D5,h,1)]
|
90
90
|
})
|
91
91
|
)
|
92
92
|
|
@@ -136,9 +136,9 @@ describe MTK::MIDI::File do
|
|
136
136
|
it 'ignores rests (events with negative duration)' do
|
137
137
|
MIDI_File(tempfile).write_timeline(
|
138
138
|
Timeline.from_hash({
|
139
|
-
0 => Note
|
140
|
-
1 => Note
|
141
|
-
2 => Note
|
139
|
+
0 => Note(C4, q, 0.7),
|
140
|
+
1 => Note(G4, -q, 0.8), # this is a rest because it has a negative duration
|
141
|
+
2 => Note(C5, q, 0.9)
|
142
142
|
})
|
143
143
|
)
|
144
144
|
|
@@ -172,12 +172,12 @@ describe MTK::MIDI::File do
|
|
172
172
|
it "writes a multitrack MIDI file" do
|
173
173
|
MIDI_File(tempfile).write_timelines([
|
174
174
|
Timeline.from_hash({
|
175
|
-
0 => Note
|
176
|
-
1.0 => Note
|
175
|
+
0 => Note(C4, q, 0.7),
|
176
|
+
1.0 => Note(G4, q, 0.8),
|
177
177
|
}),
|
178
178
|
Timeline.from_hash({
|
179
|
-
1 => Note
|
180
|
-
2 => Note
|
179
|
+
1 => Note(C5, h, 0.9),
|
180
|
+
2 => Note(D5, h, 1),
|
181
181
|
}),
|
182
182
|
])
|
183
183
|
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mtk/midi/output'
|
3
|
+
|
4
|
+
describe MTK::MIDI::Output do
|
5
|
+
|
6
|
+
class MockOuput < MTK::MIDI::Output
|
7
|
+
public_class_method :new
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:subject) { MockOuput.new(mock_device) }
|
11
|
+
|
12
|
+
let(:mock_device) do
|
13
|
+
mock_device = mock(:device)
|
14
|
+
mock_device.stub(:open)
|
15
|
+
mock_device
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:scheduler) do
|
19
|
+
scheduler = mock(:scheduler)
|
20
|
+
Gamelan::Scheduler.stub(:new).and_return scheduler
|
21
|
+
scheduler
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def timeline_with_param_event(event_type, event_options={})
|
26
|
+
event = MTK::Events::Parameter.new event_type, event_options
|
27
|
+
MTK::Timeline.from_hash 0 => event
|
28
|
+
end
|
29
|
+
|
30
|
+
def should_be_scheduled timed_data
|
31
|
+
timed_data.each do |time,data|
|
32
|
+
scheduler.should_receive(:at) do |scheduled_time,&callback|
|
33
|
+
scheduled_time.should == time
|
34
|
+
callback.yield.should == data
|
35
|
+
end
|
36
|
+
end
|
37
|
+
scheduler.should_receive(:at) # end time, don't care about this here...
|
38
|
+
scheduler.should_receive(:run).and_return mock(:thread,:join=>nil)
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
describe ".new" do
|
43
|
+
it "opens the given device" do
|
44
|
+
mock_device.should_receive(:open)
|
45
|
+
subject
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#play" do
|
50
|
+
|
51
|
+
it "handles note events" do
|
52
|
+
should_be_scheduled 0 => [:note_on, 60, 127, 0],
|
53
|
+
1 => [:note_off, 60, 127, 0]
|
54
|
+
subject.play MTK::Timeline.from_hash( 0 => Note(C4,fff,1) )
|
55
|
+
end
|
56
|
+
|
57
|
+
it "handles control events" do
|
58
|
+
should_be_scheduled 0 => [:control, 5, 32, 3]
|
59
|
+
subject.play timeline_with_param_event(:control, number:5, value:0.25, channel:3)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "handles channel pressure events" do
|
63
|
+
should_be_scheduled 0 => [:channel_pressure, 64, 0]
|
64
|
+
subject.play timeline_with_param_event(:pressure, value:0.5)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "handles poly pressure events" do
|
68
|
+
should_be_scheduled 0 => [:poly_pressure, 60, 127, 0]
|
69
|
+
subject.play timeline_with_param_event(:pressure, number:60, value:1)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "handles bend events" do
|
73
|
+
should_be_scheduled 0 => [:bend, 0, 0]
|
74
|
+
subject.play timeline_with_param_event(:bend, value: -1)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "handles program events" do
|
78
|
+
should_be_scheduled 0 => [:program, 7, 9]
|
79
|
+
subject.play timeline_with_param_event(:program, number:7, channel:9)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "handles simultaneous events" do
|
83
|
+
should_be_scheduled [
|
84
|
+
[0, [:note_on, 60, 127, 0]],
|
85
|
+
[1, [:note_off, 60, 127, 0]],
|
86
|
+
[0, [:note_on, 67, 127, 0]],
|
87
|
+
[1, [:note_off, 67, 127, 0]]
|
88
|
+
]
|
89
|
+
subject.play [Note(C4,fff,1),Note(G4,fff,1)]
|
90
|
+
end
|
91
|
+
|
92
|
+
it "handles a list of timelines" do
|
93
|
+
should_be_scheduled 0 => [:note_on, 60, 127, 0],
|
94
|
+
1 => [:note_off, 60, 127, 0],
|
95
|
+
2 => [:note_on, 67, 127, 0],
|
96
|
+
3 => [:note_off, 67, 127, 0]
|
97
|
+
subject.play [MTK::Timeline.from_hash( 0 => Note(C4,fff,1) ), MTK::Timeline.from_hash( 2 => Note(G4,fff,1) )]
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|