mtk 0.0.3.3 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/INTRO.md +63 -31
- data/Rakefile +3 -1
- data/bin/mtk +75 -32
- data/examples/drum_pattern.rb +2 -2
- data/examples/dynamic_pattern.rb +1 -1
- data/examples/helpers/output_selector.rb +71 -0
- data/examples/notation.rb +5 -1
- data/examples/tone_row_melody.rb +1 -1
- data/lib/mtk.rb +1 -0
- data/lib/mtk/core/duration.rb +18 -3
- data/lib/mtk/core/intensity.rb +5 -3
- data/lib/mtk/core/interval.rb +21 -14
- data/lib/mtk/core/pitch.rb +2 -0
- data/lib/mtk/core/pitch_class.rb +6 -3
- data/lib/mtk/events/event.rb +2 -1
- data/lib/mtk/events/note.rb +1 -1
- data/lib/mtk/events/parameter.rb +1 -0
- data/lib/mtk/events/rest.rb +85 -0
- data/lib/mtk/events/timeline.rb +6 -2
- data/lib/mtk/io/jsound_input.rb +9 -3
- data/lib/mtk/io/midi_file.rb +38 -2
- data/lib/mtk/io/midi_input.rb +1 -1
- data/lib/mtk/io/midi_output.rb +95 -4
- data/lib/mtk/io/unimidi_input.rb +7 -3
- data/lib/mtk/lang/durations.rb +31 -26
- data/lib/mtk/lang/intensities.rb +29 -30
- data/lib/mtk/lang/intervals.rb +108 -41
- data/lib/mtk/lang/mtk_grammar.citrus +14 -4
- data/lib/mtk/lang/parser.rb +10 -5
- data/lib/mtk/lang/pitch_classes.rb +45 -17
- data/lib/mtk/lang/pitches.rb +169 -32
- data/lib/mtk/lang/tutorial.rb +279 -0
- data/lib/mtk/lang/tutorial_lesson.rb +87 -0
- data/lib/mtk/sequencers/event_builder.rb +29 -8
- data/spec/mtk/core/duration_spec.rb +14 -1
- data/spec/mtk/core/intensity_spec.rb +1 -1
- data/spec/mtk/events/event_spec.rb +10 -16
- data/spec/mtk/events/note_spec.rb +3 -3
- data/spec/mtk/events/rest_spec.rb +184 -0
- data/spec/mtk/events/timeline_spec.rb +5 -1
- data/spec/mtk/io/midi_file_spec.rb +13 -2
- data/spec/mtk/io/midi_output_spec.rb +42 -9
- data/spec/mtk/lang/durations_spec.rb +5 -5
- data/spec/mtk/lang/intensities_spec.rb +5 -5
- data/spec/mtk/lang/intervals_spec.rb +139 -13
- data/spec/mtk/lang/parser_spec.rb +65 -25
- data/spec/mtk/lang/pitch_classes_spec.rb +0 -11
- data/spec/mtk/lang/pitches_spec.rb +0 -15
- data/spec/mtk/patterns/chain_spec.rb +7 -7
- data/spec/mtk/patterns/for_each_spec.rb +2 -2
- data/spec/mtk/sequencers/event_builder_spec.rb +49 -17
- metadata +12 -22
@@ -34,31 +34,31 @@ describe MTK::Lang::Parser do
|
|
34
34
|
|
35
35
|
describe ".parse" do
|
36
36
|
it "can parse a single pitch class and play it" do
|
37
|
-
sequencer = MTK::Lang::Parser.parse('
|
37
|
+
sequencer = MTK::Lang::Parser.parse('C')
|
38
38
|
timeline = sequencer.to_timeline
|
39
39
|
timeline.should == MTK::Events::Timeline.from_h({0 => MTK.Note(C4)})
|
40
40
|
end
|
41
41
|
|
42
42
|
context "default (root rule) behavior" do
|
43
43
|
it "parses a bare_sequencer" do
|
44
|
-
sequencer = parse('C:q:mp D4:ff A
|
44
|
+
sequencer = parse('C:q:mp D4:ff A e:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp')
|
45
45
|
sequencer.should be_a Sequencers::Sequencer
|
46
|
-
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(
|
46
|
+
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(e,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
|
47
47
|
end
|
48
48
|
|
49
49
|
it "parses a sequencer" do
|
50
|
-
sequencer = parse('( C:q:mp D4:ff A
|
50
|
+
sequencer = parse('( C:q:mp D4:ff A e:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp )')
|
51
51
|
sequencer.should be_a Sequencers::Sequencer
|
52
|
-
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(
|
52
|
+
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(e,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
|
53
53
|
end
|
54
54
|
|
55
55
|
it "parses a timeline" do
|
56
56
|
parse("
|
57
57
|
{
|
58
58
|
0 => C4:mp:q
|
59
|
-
1 => D4:
|
59
|
+
1 => D4:f:h
|
60
60
|
}
|
61
|
-
").should == MTK::Events::Timeline.from_h({0 => chain(C4,mp,q), 1 => chain(D4,
|
61
|
+
").should == MTK::Events::Timeline.from_h({0 => chain(C4,mp,q), 1 => chain(D4,f,h)})
|
62
62
|
end
|
63
63
|
|
64
64
|
it "parses a chain of sequences" do
|
@@ -68,17 +68,17 @@ describe MTK::Lang::Parser do
|
|
68
68
|
end
|
69
69
|
|
70
70
|
it "parses a chain of choices" do
|
71
|
-
sequencer = parse("<
|
72
|
-
sequencer.patterns.should == [ chain( choice(
|
71
|
+
sequencer = parse("<e|s>:<C|D|E>")
|
72
|
+
sequencer.patterns.should == [ chain( choice(e,s), choice(C,D,E) ) ]
|
73
73
|
end
|
74
74
|
|
75
75
|
it "parses a chain of choices" do
|
76
|
-
sequencer = parse("(<
|
77
|
-
sequencer.patterns.should == [ seq( chain( choice(
|
76
|
+
sequencer = parse("(<e|s>:<C|D|E>)&8")
|
77
|
+
sequencer.patterns.should == [ seq( chain( choice(e,s), choice(C,D,E) ), min_elements:8, max_elements:8 ) ]
|
78
78
|
end
|
79
79
|
|
80
80
|
it "parses the repetition of a basic note property" do
|
81
|
-
sequencer = parse("
|
81
|
+
sequencer = parse("C*4")
|
82
82
|
sequencer.patterns.should == [ seq(C, max_cycles:4) ]
|
83
83
|
end
|
84
84
|
end
|
@@ -104,15 +104,15 @@ describe MTK::Lang::Parser do
|
|
104
104
|
end
|
105
105
|
|
106
106
|
it "parses pitchclass:duration chains" do
|
107
|
-
sequencer = parse('C:q D:q E:
|
107
|
+
sequencer = parse('C:q D:q E:e F:e G:h', :bare_sequencer)
|
108
108
|
sequencer.should be_a Sequencers::Sequencer
|
109
|
-
sequencer.patterns.should == [seq(chain(C,q), chain(D,q), chain(E,
|
109
|
+
sequencer.patterns.should == [seq(chain(C,q), chain(D,q), chain(E,e), chain(F,e), chain(G,h))]
|
110
110
|
end
|
111
111
|
|
112
112
|
it "parses a mix of chained and unchained pitches, pitch classes, durations, and intensities" do
|
113
|
-
sequencer = parse('C:q:mp D4:ff A
|
113
|
+
sequencer = parse('C:q:mp D4:ff A e:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp', :bare_sequencer)
|
114
114
|
sequencer.should be_a Sequencers::Sequencer
|
115
|
-
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(
|
115
|
+
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(e,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
@@ -137,15 +137,15 @@ describe MTK::Lang::Parser do
|
|
137
137
|
end
|
138
138
|
|
139
139
|
it "parses pitchclass:duration chains" do
|
140
|
-
sequencer = parse('{ C:q D:q E:
|
140
|
+
sequencer = parse('{ C:q D:q E:e F:e G:h }', :sequencer)
|
141
141
|
sequencer.should be_a Sequencers::Sequencer
|
142
|
-
sequencer.patterns.should == [seq(chain(C,q), chain(D,q), chain(E,
|
142
|
+
sequencer.patterns.should == [seq(chain(C,q), chain(D,q), chain(E,e), chain(F,e), chain(G,h))]
|
143
143
|
end
|
144
144
|
|
145
145
|
it "parses a mix of chained and unchained pitches, pitch classes, durations, and intensities" do
|
146
|
-
sequencer = parse('{ C:q:mp D4:ff A
|
146
|
+
sequencer = parse('{ C:q:mp D4:ff A e:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp }', :sequencer)
|
147
147
|
sequencer.should be_a Sequencers::Sequencer
|
148
|
-
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(
|
148
|
+
sequencer.patterns.should == [seq( chain(C,q,mp), chain(D4,ff), A, chain(e,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
@@ -167,9 +167,9 @@ describe MTK::Lang::Parser do
|
|
167
167
|
parse("
|
168
168
|
{
|
169
169
|
0 => C4:mp:q
|
170
|
-
1 => D4:
|
170
|
+
1 => D4:f:h
|
171
171
|
}
|
172
|
-
", :timeline).should == MTK::Events::Timeline.from_h({0 => chain(C4,mp,q), 1 => chain(D4,
|
172
|
+
", :timeline).should == MTK::Events::Timeline.from_h({0 => chain(C4,mp,q), 1 => chain(D4,f,h)})
|
173
173
|
end
|
174
174
|
|
175
175
|
#it "parses a Timeline containing a chord" do
|
@@ -317,7 +317,7 @@ describe MTK::Lang::Parser do
|
|
317
317
|
end
|
318
318
|
|
319
319
|
it "parses duration sequences" do
|
320
|
-
parse("q
|
320
|
+
parse("q e q. ht", :bare_sequence).should == seq(q, e, q*Rational(1.5), h*Rational(2,3))
|
321
321
|
end
|
322
322
|
end
|
323
323
|
|
@@ -344,7 +344,7 @@ describe MTK::Lang::Parser do
|
|
344
344
|
end
|
345
345
|
|
346
346
|
it "parses duration sequences" do
|
347
|
-
parse("(q
|
347
|
+
parse("(q e q. ht)", :sequence).should == seq(q, e, q*Rational(1.5), h*Rational(2,3))
|
348
348
|
end
|
349
349
|
|
350
350
|
it "parses sequences with a max_cycles modifier" do
|
@@ -424,7 +424,7 @@ describe MTK::Lang::Parser do
|
|
424
424
|
|
425
425
|
context 'element rule' do
|
426
426
|
it "parses the repetition of a basic note property as a sequence with a max_cycles option" do
|
427
|
-
sequence = parse("
|
427
|
+
sequence = parse("C*4", :element)
|
428
428
|
sequence.elements.should == [ C ]
|
429
429
|
sequence.max_cycles.should == 4
|
430
430
|
end
|
@@ -456,6 +456,46 @@ describe MTK::Lang::Parser do
|
|
456
456
|
parse(pitch_class_name, :pitch_class).should == PitchClass[pitch_class_name]
|
457
457
|
end
|
458
458
|
end
|
459
|
+
|
460
|
+
it "doesn't allow a sharp and flat to be applied to the same diatonic pitch class" do
|
461
|
+
for pitch_class_name in %w(A B C D E F G)
|
462
|
+
lambda{ parse(pitch_class_name + '#b', :pitch_class) }.should raise_error
|
463
|
+
lambda{ parse(pitch_class_name + 'b#', :pitch_class) }.should raise_error
|
464
|
+
end
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
|
469
|
+
context 'diatonic_pitch_class rule' do
|
470
|
+
it "parses upper case diatonic pitch classes" do
|
471
|
+
for diatonic_pitch_class_name in %w(A B C D E F G)
|
472
|
+
parse(diatonic_pitch_class_name, :diatonic_pitch_class).should == PitchClass[diatonic_pitch_class_name]
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
|
478
|
+
context 'accidental rule' do
|
479
|
+
it "parses a single flat 'b'" do
|
480
|
+
lambda{ parse('b', :accidental) }.should_not raise_error
|
481
|
+
end
|
482
|
+
|
483
|
+
it "parses a double flat 'bb'" do
|
484
|
+
lambda{ parse('bb', :accidental) }.should_not raise_error
|
485
|
+
end
|
486
|
+
|
487
|
+
it "parses a single sharp '#'" do
|
488
|
+
lambda{ parse('#', :accidental) }.should_not raise_error
|
489
|
+
end
|
490
|
+
|
491
|
+
it "parses a double sharp '##'" do
|
492
|
+
lambda{ parse('##', :accidental) }.should_not raise_error
|
493
|
+
end
|
494
|
+
|
495
|
+
it "doesn't parse trip flats or sharps" do
|
496
|
+
lambda{ parse('bbb', :accidental) }.should raise_error
|
497
|
+
lambda{ parse('###', :accidental) }.should raise_error
|
498
|
+
end
|
459
499
|
end
|
460
500
|
|
461
501
|
|
@@ -48,15 +48,4 @@ describe MTK::Lang::PitchClasses do
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
describe ".[]" do
|
52
|
-
it "acts like PitchClass.[]" do
|
53
|
-
for name in PitchClasses::PITCH_CLASS_NAMES
|
54
|
-
PitchClasses[name].should == PitchClass[name]
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
it "returns nil for arguments it doesn't understand" do
|
59
|
-
PitchClasses[:invalid].should be_nil
|
60
|
-
end
|
61
|
-
end
|
62
51
|
end
|
@@ -38,19 +38,4 @@ describe MTK::Lang::Pitches do
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
describe ".[]" do
|
42
|
-
it "acts like Pitch.from_s for the names in PITCH_NAMES, and also treats '_' like '-'" do
|
43
|
-
for name in Pitches::PITCH_NAMES
|
44
|
-
Pitches[name].should == Pitch.from_s(name.sub('_','-')) # the constant names need to use underscores, but Pitch.from_s doesn't understand that
|
45
|
-
if name =~ /_/
|
46
|
-
Pitches[name.sub('_','-')].should == Pitch.from_s(name.sub('_','-')) # make sure '-' works too
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
it "returns nil for arguments it doesn't understand" do
|
52
|
-
Pitches[:invalid].should be_nil
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
41
|
end
|
@@ -43,11 +43,11 @@ describe MTK::Patterns::Chain do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
it 'combines 2 patterns, and returns #next until the last StopIteration of a nested Pattern occurs' do
|
46
|
-
chain = CHAIN.new [Patterns.Sequence(C,D,E), Patterns.Sequence(w,h,q,
|
46
|
+
chain = CHAIN.new [Patterns.Sequence(C,D,E), Patterns.Sequence(w,h,q,e)]
|
47
47
|
chain.next.should == [C,w]
|
48
48
|
chain.next.should == [D,h]
|
49
49
|
chain.next.should == [E,q]
|
50
|
-
chain.next.should == [C,
|
50
|
+
chain.next.should == [C,e]
|
51
51
|
lambda{ chain.next }.should raise_error StopIteration
|
52
52
|
end
|
53
53
|
|
@@ -56,18 +56,18 @@ describe MTK::Patterns::Chain do
|
|
56
56
|
|
57
57
|
|
58
58
|
it 'combines Choice patterns with max_cycles' do
|
59
|
-
chain = CHAIN.new [Patterns.Choice(
|
59
|
+
chain = CHAIN.new [Patterns.Choice(e,s), Patterns.Choice(C,D,E)], max_cycles:100
|
60
60
|
100.times do |time|
|
61
61
|
attrs = chain.next
|
62
62
|
attrs.length.should == 2
|
63
|
-
(attrs[0]==
|
63
|
+
(attrs[0]==e or attrs[0]==s).should be_true
|
64
64
|
(attrs[1]==C or attrs[1]==D or attrs[1]==E).should be_true
|
65
65
|
end
|
66
66
|
lambda{ chain.next }.should raise_error StopIteration
|
67
67
|
end
|
68
68
|
|
69
69
|
it 'combines Choice patterns and emits a only single combination of attributes by default' do
|
70
|
-
chain = CHAIN.new [Patterns.Choice(
|
70
|
+
chain = CHAIN.new [Patterns.Choice(e,s), Patterns.Choice(C,D,E)]
|
71
71
|
chain.next
|
72
72
|
lambda{ chain.next }.should raise_error StopIteration
|
73
73
|
end
|
@@ -78,7 +78,7 @@ describe MTK::Patterns::Chain do
|
|
78
78
|
end
|
79
79
|
|
80
80
|
it 'throws StopIteration when any subpattern throws StopIteration with max_elements_exceeded' do
|
81
|
-
chain = CHAIN.new [Patterns.Sequence(C,D,E,F,G), Patterns.Sequence(w,h,q,
|
81
|
+
chain = CHAIN.new [Patterns.Sequence(C,D,E,F,G), Patterns.Sequence(w,h,q,e, max_elements:3)]
|
82
82
|
chain.next.should == [C,w]
|
83
83
|
chain.next.should == [D,h]
|
84
84
|
chain.next.should == [E,q]
|
@@ -100,7 +100,7 @@ describe MTK::Patterns::Chain do
|
|
100
100
|
chain = CHAIN.new [C,q]
|
101
101
|
chain.next
|
102
102
|
chain.rewind
|
103
|
-
lambda{ chain.next }.should_not raise_error
|
103
|
+
lambda{ chain.next }.should_not raise_error
|
104
104
|
lambda{ chain.next }.should raise_error StopIteration
|
105
105
|
end
|
106
106
|
end
|
@@ -83,11 +83,11 @@ describe MTK::Patterns::ForEach do
|
|
83
83
|
|
84
84
|
it "evaluates nested variables" do
|
85
85
|
# (C4 Bb Ab G)@( (C D C $):(q i i)*4:(mp mf) )
|
86
|
-
foreach = FOREACH.new( [seq(G,A), chain(seq(C,D,var('$')),seq(q,
|
86
|
+
foreach = FOREACH.new( [seq(G,A), chain(seq(C,D,var('$')),seq(q,e,s))] )
|
87
87
|
vals = []
|
88
88
|
6.times{ vals << foreach.next }
|
89
89
|
lambda{ foreach.next }.should raise_error StopIteration
|
90
|
-
vals.should == [[C,q],[D,
|
90
|
+
vals.should == [[C,q],[D,e],[G,s],[C,q],[D,e],[A,s]]
|
91
91
|
end
|
92
92
|
|
93
93
|
end
|
@@ -57,20 +57,26 @@ describe MTK::Sequencers::EventBuilder do
|
|
57
57
|
event_builder.next.should == notes(C4)
|
58
58
|
end
|
59
59
|
|
60
|
-
it "defaults to intensity '
|
60
|
+
it "defaults to intensity 'f' when no intensities are given" do
|
61
61
|
event_builder = EVENT_BUILDER.new [Patterns.PitchSequence(C4, D4, E4), Patterns.DurationCycle(2)]
|
62
|
-
event_builder.next.should == [Note(C4,
|
63
|
-
event_builder.next.should == [Note(D4,
|
64
|
-
event_builder.next.should == [Note(E4,
|
62
|
+
event_builder.next.should == [Note(C4, f, 2)]
|
63
|
+
event_builder.next.should == [Note(D4, f, 2)]
|
64
|
+
event_builder.next.should == [Note(E4, f, 2)]
|
65
65
|
end
|
66
66
|
|
67
67
|
it "defaults to duration 1 when no durations are given" do
|
68
|
-
event_builder = EVENT_BUILDER.new [Patterns.PitchSequence(C4, D4, E4), Patterns.IntensityCycle(p,
|
68
|
+
event_builder = EVENT_BUILDER.new [Patterns.PitchSequence(C4, D4, E4), Patterns.IntensityCycle(p,f)]
|
69
69
|
event_builder.next.should == [Note(C4, p, 1)]
|
70
|
-
event_builder.next.should == [Note(D4,
|
70
|
+
event_builder.next.should == [Note(D4, f, 1)]
|
71
71
|
event_builder.next.should == [Note(E4, p, 1)]
|
72
72
|
end
|
73
73
|
|
74
|
+
it "builds notes from pitch class sets, selecting the first pitches nearest to the default pitch" do
|
75
|
+
pitch_class_sequence = MTK::Patterns::Sequence.new([PitchClassSet(C,G)])
|
76
|
+
event_builder = EVENT_BUILDER.new [pitch_class_sequence], :default_pitch => D3
|
77
|
+
event_builder.next.should == notes(C3,G3)
|
78
|
+
end
|
79
|
+
|
74
80
|
it "builds notes from pitch class sets, selecting the nearest pitch classes to the previous/default pitch" do
|
75
81
|
pitch_class_sequence = MTK::Patterns::Sequence.new([PitchClassSet(C,G),PitchClassSet(B,Eb),PitchClassSet(D,C)])
|
76
82
|
event_builder = EVENT_BUILDER.new [pitch_class_sequence], :default_pitch => D3
|
@@ -108,17 +114,17 @@ describe MTK::Sequencers::EventBuilder do
|
|
108
114
|
end
|
109
115
|
|
110
116
|
it "iterates through the pitch, intensity, and duration list in parallel to emit Notes" do
|
111
|
-
event_builder = EVENT_BUILDER.new [Patterns.PitchCycle(C4, D4, E4), Patterns.IntensityCycle(p,
|
117
|
+
event_builder = EVENT_BUILDER.new [Patterns.PitchCycle(C4, D4, E4), Patterns.IntensityCycle(p, f), Patterns.DurationCycle(1,2,3,4)]
|
112
118
|
event_builder.next.should == [Note(C4, p, 1)]
|
113
|
-
event_builder.next.should == [Note(D4,
|
119
|
+
event_builder.next.should == [Note(D4, f, 2)]
|
114
120
|
event_builder.next.should == [Note(E4, p, 3)]
|
115
|
-
event_builder.next.should == [Note(C4,
|
121
|
+
event_builder.next.should == [Note(C4, f, 4)]
|
116
122
|
event_builder.next.should == [Note(D4, p, 1)]
|
117
|
-
event_builder.next.should == [Note(E4,
|
123
|
+
event_builder.next.should == [Note(E4, f, 2)]
|
118
124
|
end
|
119
125
|
|
120
126
|
it "returns nil (for a rest) when it encounters a nil value" do
|
121
|
-
event_builder = EVENT_BUILDER.new [Patterns.PitchCycle(C4, D4, E4, F4, nil), Patterns.IntensityCycle(mp, mf,
|
127
|
+
event_builder = EVENT_BUILDER.new [Patterns.PitchCycle(C4, D4, E4, F4, nil), Patterns.IntensityCycle(mp, mf, f, nil), Patterns.DurationCycle(1, 2, nil)]
|
122
128
|
event_builder.next.should == [Note(C4, mp, 1)]
|
123
129
|
event_builder.next.should == [Note(D4, mf, 2)]
|
124
130
|
event_builder.next.should be_nil
|
@@ -166,10 +172,10 @@ describe MTK::Sequencers::EventBuilder do
|
|
166
172
|
end
|
167
173
|
|
168
174
|
it "uses the default_pitch when no pitch pattern is provided" do
|
169
|
-
event_builder = EVENT_BUILDER.new [Patterns.Cycle( mp, mf,
|
175
|
+
event_builder = EVENT_BUILDER.new [Patterns.Cycle( mp, mf, f )], :default_pitch => G3
|
170
176
|
event_builder.next.should == [Note(G3,mp,1)]
|
171
177
|
event_builder.next.should == [Note(G3,mf,1)]
|
172
|
-
event_builder.next.should == [Note(G3,
|
178
|
+
event_builder.next.should == [Note(G3,f,1)]
|
173
179
|
end
|
174
180
|
|
175
181
|
it "handles chains of sequences" do
|
@@ -204,8 +210,8 @@ describe MTK::Sequencers::EventBuilder do
|
|
204
210
|
end
|
205
211
|
|
206
212
|
it "adds chained durations together" do
|
207
|
-
event_builder = EVENT_BUILDER.new( [Patterns.Chain(h,q,
|
208
|
-
event_builder.next[0].duration.should == h+q+
|
213
|
+
event_builder = EVENT_BUILDER.new( [Patterns.Chain(h,q,e,s)] )
|
214
|
+
event_builder.next[0].duration.should == h+q+e+s
|
209
215
|
end
|
210
216
|
|
211
217
|
it "averages chained intensities together" do
|
@@ -215,11 +221,11 @@ describe MTK::Sequencers::EventBuilder do
|
|
215
221
|
|
216
222
|
it "defaults the intensity to the previous intensity" do
|
217
223
|
event_builder = EVENT_BUILDER.new(
|
218
|
-
[Patterns.Sequence(Patterns.Chain(C4,ppp,q), Patterns.Chain(D4,
|
224
|
+
[Patterns.Sequence(Patterns.Chain(C4,ppp,q), Patterns.Chain(D4,e), Patterns.Chain(E4,ff,h), Patterns.Chain(F4,e))]
|
219
225
|
)
|
220
226
|
notes = []
|
221
227
|
4.times{ notes += event_builder.next }
|
222
|
-
notes.should == [Note(C4,ppp,q), Note(D4,ppp,
|
228
|
+
notes.should == [Note(C4,ppp,q), Note(D4,ppp,e), Note(E4,ff,h), Note(F4,ff,e)]
|
223
229
|
end
|
224
230
|
|
225
231
|
it "defaults the duration to the previous duration" do
|
@@ -230,6 +236,32 @@ describe MTK::Sequencers::EventBuilder do
|
|
230
236
|
4.times{ notes += event_builder.next }
|
231
237
|
notes.should == [Note(C4,ppp,h), Note(D4,mp,h), Note(E4,ff,s), Note(F4,mf,s)]
|
232
238
|
end
|
239
|
+
|
240
|
+
it "uses the previous pitch class in the chain to determine the octave of the current pitch class" do
|
241
|
+
event_builder = EVENT_BUILDER.new([Patterns.Chain(C4,E,G)])
|
242
|
+
event_builder.next.should == [Note(C4),Note(E4),Note(G4)]
|
243
|
+
end
|
244
|
+
|
245
|
+
it "returns a Rest event when the duration is negative" do
|
246
|
+
event_builder = EVENT_BUILDER.new([Patterns.Chain(C4,-q)])
|
247
|
+
event_builder.next.should == [Rest(q)]
|
248
|
+
end
|
249
|
+
|
250
|
+
it "doesn't uses the absolute value of the previous rest when generating the next event" do
|
251
|
+
event_builder = EVENT_BUILDER.new([Patterns.Sequence(Patterns.Chain(C4,q), -q, D4)])
|
252
|
+
event_builder.next.should == [Note(C4,q)]
|
253
|
+
event_builder.next.should == [Rest(q)]
|
254
|
+
event_builder.next.should == [Note(D4,q)]
|
255
|
+
end
|
256
|
+
|
257
|
+
it "makes all event chained to a rest be a rest" do
|
258
|
+
event_builder = EVENT_BUILDER.new(
|
259
|
+
[Patterns.Sequence(Patterns.Chain(C4,q), Patterns.Chain(-q, Patterns.Sequence(D4,E4)))]
|
260
|
+
)
|
261
|
+
event_builder.next.should == [Note(C4,q)]
|
262
|
+
event_builder.next.should == [Rest(q)]
|
263
|
+
event_builder.next.should == [Rest(q)]
|
264
|
+
end
|
233
265
|
end
|
234
266
|
|
235
267
|
describe "#rewind" do
|
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mtk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: '0.4'
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Adam Murray
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-08-05 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: citrus
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,7 +20,6 @@ dependencies:
|
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ~>
|
28
25
|
- !ruby/object:Gem::Version
|
@@ -30,7 +27,6 @@ dependencies:
|
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: midilib
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
31
|
- - ~>
|
36
32
|
- !ruby/object:Gem::Version
|
@@ -38,7 +34,6 @@ dependencies:
|
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
38
|
- - ~>
|
44
39
|
- !ruby/object:Gem::Version
|
@@ -46,7 +41,6 @@ dependencies:
|
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: gamelan
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
45
|
- - ~>
|
52
46
|
- !ruby/object:Gem::Version
|
@@ -54,7 +48,6 @@ dependencies:
|
|
54
48
|
type: :runtime
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
52
|
- - ~>
|
60
53
|
- !ruby/object:Gem::Version
|
@@ -62,7 +55,6 @@ dependencies:
|
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: unimidi
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
59
|
- - ~>
|
68
60
|
- !ruby/object:Gem::Version
|
@@ -70,7 +62,6 @@ dependencies:
|
|
70
62
|
type: :runtime
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
66
|
- - ~>
|
76
67
|
- !ruby/object:Gem::Version
|
@@ -94,6 +85,7 @@ files:
|
|
94
85
|
- examples/drum_pattern.rb
|
95
86
|
- examples/dynamic_pattern.rb
|
96
87
|
- examples/gets_and_play.rb
|
88
|
+
- examples/helpers/output_selector.rb
|
97
89
|
- examples/notation.rb
|
98
90
|
- examples/play_midi.rb
|
99
91
|
- examples/print_midi.rb
|
@@ -109,6 +101,7 @@ files:
|
|
109
101
|
- lib/mtk/events/event.rb
|
110
102
|
- lib/mtk/events/note.rb
|
111
103
|
- lib/mtk/events/parameter.rb
|
104
|
+
- lib/mtk/events/rest.rb
|
112
105
|
- lib/mtk/events/timeline.rb
|
113
106
|
- lib/mtk/groups/chord.rb
|
114
107
|
- lib/mtk/groups/collection.rb
|
@@ -133,6 +126,8 @@ files:
|
|
133
126
|
- lib/mtk/lang/pitch_classes.rb
|
134
127
|
- lib/mtk/lang/pitches.rb
|
135
128
|
- lib/mtk/lang/pseudo_constants.rb
|
129
|
+
- lib/mtk/lang/tutorial.rb
|
130
|
+
- lib/mtk/lang/tutorial_lesson.rb
|
136
131
|
- lib/mtk/lang/variable.rb
|
137
132
|
- lib/mtk/numeric_extensions.rb
|
138
133
|
- lib/mtk/patterns/chain.rb
|
@@ -158,6 +153,7 @@ files:
|
|
158
153
|
- spec/mtk/events/event_spec.rb
|
159
154
|
- spec/mtk/events/note_spec.rb
|
160
155
|
- spec/mtk/events/parameter_spec.rb
|
156
|
+
- spec/mtk/events/rest_spec.rb
|
161
157
|
- spec/mtk/events/timeline_spec.rb
|
162
158
|
- spec/mtk/groups/chord_spec.rb
|
163
159
|
- spec/mtk/groups/collection_spec.rb
|
@@ -192,34 +188,28 @@ files:
|
|
192
188
|
- spec/spec_helper.rb
|
193
189
|
- spec/test.mid
|
194
190
|
homepage: http://github.com/adamjmurray/mtk
|
195
|
-
licenses:
|
191
|
+
licenses:
|
192
|
+
- BSD
|
193
|
+
metadata: {}
|
196
194
|
post_install_message:
|
197
195
|
rdoc_options: []
|
198
196
|
require_paths:
|
199
197
|
- lib
|
200
198
|
required_ruby_version: !ruby/object:Gem::Requirement
|
201
|
-
none: false
|
202
199
|
requirements:
|
203
200
|
- - ! '>='
|
204
201
|
- !ruby/object:Gem::Version
|
205
202
|
version: '0'
|
206
|
-
segments:
|
207
|
-
- 0
|
208
|
-
hash: 3315185627907297240
|
209
203
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
210
|
-
none: false
|
211
204
|
requirements:
|
212
205
|
- - ! '>='
|
213
206
|
- !ruby/object:Gem::Version
|
214
207
|
version: '0'
|
215
|
-
segments:
|
216
|
-
- 0
|
217
|
-
hash: 3315185627907297240
|
218
208
|
requirements: []
|
219
209
|
rubyforge_project:
|
220
|
-
rubygems_version:
|
210
|
+
rubygems_version: 2.0.6
|
221
211
|
signing_key:
|
222
|
-
specification_version:
|
212
|
+
specification_version: 4
|
223
213
|
summary: Music Tool Kit for Ruby
|
224
214
|
test_files: []
|
225
215
|
has_rdoc:
|