mtk 0.0.3.3 → 0.4
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.
- 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:
|