mtk 0.0.3.3 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +15 -0
  2. data/INTRO.md +63 -31
  3. data/Rakefile +3 -1
  4. data/bin/mtk +75 -32
  5. data/examples/drum_pattern.rb +2 -2
  6. data/examples/dynamic_pattern.rb +1 -1
  7. data/examples/helpers/output_selector.rb +71 -0
  8. data/examples/notation.rb +5 -1
  9. data/examples/tone_row_melody.rb +1 -1
  10. data/lib/mtk.rb +1 -0
  11. data/lib/mtk/core/duration.rb +18 -3
  12. data/lib/mtk/core/intensity.rb +5 -3
  13. data/lib/mtk/core/interval.rb +21 -14
  14. data/lib/mtk/core/pitch.rb +2 -0
  15. data/lib/mtk/core/pitch_class.rb +6 -3
  16. data/lib/mtk/events/event.rb +2 -1
  17. data/lib/mtk/events/note.rb +1 -1
  18. data/lib/mtk/events/parameter.rb +1 -0
  19. data/lib/mtk/events/rest.rb +85 -0
  20. data/lib/mtk/events/timeline.rb +6 -2
  21. data/lib/mtk/io/jsound_input.rb +9 -3
  22. data/lib/mtk/io/midi_file.rb +38 -2
  23. data/lib/mtk/io/midi_input.rb +1 -1
  24. data/lib/mtk/io/midi_output.rb +95 -4
  25. data/lib/mtk/io/unimidi_input.rb +7 -3
  26. data/lib/mtk/lang/durations.rb +31 -26
  27. data/lib/mtk/lang/intensities.rb +29 -30
  28. data/lib/mtk/lang/intervals.rb +108 -41
  29. data/lib/mtk/lang/mtk_grammar.citrus +14 -4
  30. data/lib/mtk/lang/parser.rb +10 -5
  31. data/lib/mtk/lang/pitch_classes.rb +45 -17
  32. data/lib/mtk/lang/pitches.rb +169 -32
  33. data/lib/mtk/lang/tutorial.rb +279 -0
  34. data/lib/mtk/lang/tutorial_lesson.rb +87 -0
  35. data/lib/mtk/sequencers/event_builder.rb +29 -8
  36. data/spec/mtk/core/duration_spec.rb +14 -1
  37. data/spec/mtk/core/intensity_spec.rb +1 -1
  38. data/spec/mtk/events/event_spec.rb +10 -16
  39. data/spec/mtk/events/note_spec.rb +3 -3
  40. data/spec/mtk/events/rest_spec.rb +184 -0
  41. data/spec/mtk/events/timeline_spec.rb +5 -1
  42. data/spec/mtk/io/midi_file_spec.rb +13 -2
  43. data/spec/mtk/io/midi_output_spec.rb +42 -9
  44. data/spec/mtk/lang/durations_spec.rb +5 -5
  45. data/spec/mtk/lang/intensities_spec.rb +5 -5
  46. data/spec/mtk/lang/intervals_spec.rb +139 -13
  47. data/spec/mtk/lang/parser_spec.rb +65 -25
  48. data/spec/mtk/lang/pitch_classes_spec.rb +0 -11
  49. data/spec/mtk/lang/pitches_spec.rb +0 -15
  50. data/spec/mtk/patterns/chain_spec.rb +7 -7
  51. data/spec/mtk/patterns/for_each_spec.rb +2 -2
  52. data/spec/mtk/sequencers/event_builder_spec.rb +49 -17
  53. 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('c')
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 i:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp')
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(i,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
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 i:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp )')
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(i,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
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:o:h
59
+ 1 => D4:f:h
60
60
  }
61
- ").should == MTK::Events::Timeline.from_h({0 => chain(C4,mp,q), 1 => chain(D4,o,h)})
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("<i|s>:<c|d|e>")
72
- sequencer.patterns.should == [ chain( choice(i,s), choice(C,D,E) ) ]
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("(<i|s>:<c|d|e>)&8")
77
- sequencer.patterns.should == [ seq( chain( choice(i,s), choice(C,D,E) ), min_elements:8, max_elements:8 ) ]
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("c*4")
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:i F:i G:h', :bare_sequencer)
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,i), chain(F,i), chain(G,h))]
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 i:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp', :bare_sequencer)
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(i,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
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:i F:i G:h }', :sequencer)
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,i), chain(F,i), chain(G,h))]
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 i:p Eb:pp Bb7 F2:h. F#4:mf:s q ppp }', :sequencer)
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(i,p), chain(Eb,pp), Bb7, chain(F2,h+q), chain(Gb4,mf,s), q, ppp )]
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:o:h
170
+ 1 => D4:f:h
171
171
  }
172
- ", :timeline).should == MTK::Events::Timeline.from_h({0 => chain(C4,mp,q), 1 => chain(D4,o,h)})
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 i q. ht", :bare_sequence).should == seq(q, i, q*Rational(1.5), h*Rational(2,3))
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 i q. ht)", :sequence).should == seq(q, i, q*Rational(1.5), h*Rational(2,3))
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("c*4", :element)
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,i)]
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,i]
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(i,s), Patterns.Choice(C,D,E)], max_cycles:100
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]==i or attrs[0]==s).should be_true
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(i,s), Patterns.Choice(C,D,E)]
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,i, max_elements:3)]
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 StopIteration
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,i,s))] )
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,i],[G,s],[C,q],[D,i],[A,s]]
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 'o' when no intensities are given" do
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, o, 2)]
63
- event_builder.next.should == [Note(D4, o, 2)]
64
- event_builder.next.should == [Note(E4, o, 2)]
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,o)]
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, o, 1)]
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, o), Patterns.DurationCycle(1,2,3,4)]
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, o, 2)]
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, o, 4)]
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, o, 2)]
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, o, nil), Patterns.DurationCycle(1, 2, nil)]
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, o )], :default_pitch => G3
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,o,1)]
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,i,s)] )
208
- event_builder.next[0].duration.should == h+q+i+s
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,i), Patterns.Chain(E4,ff,h), Patterns.Chain(F4,i))]
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,i), Note(E4,ff,h), Note(F4,ff,i)]
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.0.3.3
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-06-13 00:00:00.000000000 Z
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: 1.8.25
210
+ rubygems_version: 2.0.6
221
211
  signing_key:
222
- specification_version: 3
212
+ specification_version: 4
223
213
  summary: Music Tool Kit for Ruby
224
214
  test_files: []
225
215
  has_rdoc: