mtk 0.0.2 → 0.0.3
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 +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
|