mtk 0.0.1 → 0.0.2

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.
Files changed (93) hide show
  1. data/.yardopts +9 -0
  2. data/INTRO.md +73 -0
  3. data/LICENSE.txt +27 -0
  4. data/README.md +93 -18
  5. data/Rakefile +13 -1
  6. data/examples/crescendo.rb +20 -0
  7. data/examples/dynamic_pattern.rb +39 -0
  8. data/examples/play_midi.rb +19 -0
  9. data/examples/print_midi.rb +13 -0
  10. data/examples/random_tone_row.rb +18 -0
  11. data/examples/tone_row_melody.rb +21 -0
  12. data/lib/mtk/_constants/durations.rb +80 -0
  13. data/lib/mtk/_constants/intensities.rb +81 -0
  14. data/lib/mtk/{constants → _constants}/intervals.rb +10 -1
  15. data/lib/mtk/_constants/pitch_classes.rb +35 -0
  16. data/lib/mtk/_constants/pitches.rb +49 -0
  17. data/lib/mtk/{numeric_extensions.rb → _numeric_extensions.rb} +0 -0
  18. data/lib/mtk/event.rb +14 -5
  19. data/lib/mtk/helper/collection.rb +114 -0
  20. data/lib/mtk/helper/event_builder.rb +85 -0
  21. data/lib/mtk/{constants → helper}/pseudo_constants.rb +7 -6
  22. data/lib/mtk/lang/grammar.rb +17 -0
  23. data/lib/mtk/lang/mtk_grammar.citrus +60 -0
  24. data/lib/mtk/midi/file.rb +10 -15
  25. data/lib/mtk/midi/jsound_input.rb +68 -0
  26. data/lib/mtk/midi/jsound_output.rb +80 -0
  27. data/lib/mtk/note.rb +22 -3
  28. data/lib/mtk/pattern/abstract_pattern.rb +132 -0
  29. data/lib/mtk/pattern/choice.rb +25 -9
  30. data/lib/mtk/pattern/cycle.rb +51 -0
  31. data/lib/mtk/pattern/enumerator.rb +26 -0
  32. data/lib/mtk/pattern/function.rb +46 -0
  33. data/lib/mtk/pattern/lines.rb +60 -0
  34. data/lib/mtk/pattern/palindrome.rb +42 -0
  35. data/lib/mtk/pattern/sequence.rb +15 -50
  36. data/lib/mtk/pitch.rb +45 -6
  37. data/lib/mtk/pitch_class.rb +36 -35
  38. data/lib/mtk/pitch_class_set.rb +46 -14
  39. data/lib/mtk/pitch_set.rb +20 -31
  40. data/lib/mtk/sequencer/abstract_sequencer.rb +85 -0
  41. data/lib/mtk/sequencer/rhythmic_sequencer.rb +29 -0
  42. data/lib/mtk/sequencer/step_sequencer.rb +26 -0
  43. data/lib/mtk/timeline.rb +75 -22
  44. data/lib/mtk/transform/invertible.rb +15 -0
  45. data/lib/mtk/{util → transform}/mappable.rb +6 -2
  46. data/lib/mtk/transform/set_theory_operations.rb +34 -0
  47. data/lib/mtk/transform/transposable.rb +14 -0
  48. data/lib/mtk.rb +56 -22
  49. data/spec/mtk/_constants/durations_spec.rb +118 -0
  50. data/spec/mtk/{constants/dynamics_spec.rb → _constants/intensities_spec.rb} +48 -17
  51. data/spec/mtk/{constants → _constants}/intervals_spec.rb +21 -0
  52. data/spec/mtk/_constants/pitch_classes_spec.rb +58 -0
  53. data/spec/mtk/_constants/pitches_spec.rb +52 -0
  54. data/spec/mtk/{numeric_extensions_spec.rb → _numeric_extensions_spec.rb} +0 -0
  55. data/spec/mtk/event_spec.rb +19 -0
  56. data/spec/mtk/helper/collection_spec.rb +291 -0
  57. data/spec/mtk/helper/event_builder_spec.rb +92 -0
  58. data/spec/mtk/helper/pseudo_constants_spec.rb +20 -0
  59. data/spec/mtk/lang/grammar_spec.rb +100 -0
  60. data/spec/mtk/midi/file_spec.rb +41 -6
  61. data/spec/mtk/note_spec.rb +53 -3
  62. data/spec/mtk/pattern/abstract_pattern_spec.rb +45 -0
  63. data/spec/mtk/pattern/choice_spec.rb +89 -3
  64. data/spec/mtk/pattern/cycle_spec.rb +133 -0
  65. data/spec/mtk/pattern/function_spec.rb +133 -0
  66. data/spec/mtk/pattern/lines_spec.rb +93 -0
  67. data/spec/mtk/pattern/note_cycle_spec.rb.bak +116 -0
  68. data/spec/mtk/pattern/palindrome_spec.rb +124 -0
  69. data/spec/mtk/pattern/pitch_cycle_spec.rb.bak +47 -0
  70. data/spec/mtk/pattern/pitch_sequence_spec.rb.bak +37 -0
  71. data/spec/mtk/pattern/sequence_spec.rb +128 -31
  72. data/spec/mtk/pitch_class_set_spec.rb +240 -7
  73. data/spec/mtk/pitch_class_spec.rb +84 -18
  74. data/spec/mtk/pitch_set_spec.rb +45 -10
  75. data/spec/mtk/pitch_spec.rb +59 -0
  76. data/spec/mtk/sequencer/abstract_sequencer_spec.rb +159 -0
  77. data/spec/mtk/sequencer/rhythmic_sequencer_spec.rb +49 -0
  78. data/spec/mtk/sequencer/step_sequencer_spec.rb +71 -0
  79. data/spec/mtk/timeline_spec.rb +118 -15
  80. data/spec/spec_helper.rb +4 -3
  81. metadata +59 -22
  82. data/lib/mtk/chord.rb +0 -47
  83. data/lib/mtk/constants/dynamics.rb +0 -56
  84. data/lib/mtk/constants/pitch_classes.rb +0 -18
  85. data/lib/mtk/constants/pitches.rb +0 -24
  86. data/lib/mtk/pattern/note_sequence.rb +0 -60
  87. data/lib/mtk/pattern/pitch_sequence.rb +0 -22
  88. data/lib/mtk/patterns.rb +0 -4
  89. data/spec/mtk/chord_spec.rb +0 -74
  90. data/spec/mtk/constants/pitch_classes_spec.rb +0 -35
  91. data/spec/mtk/constants/pitches_spec.rb +0 -23
  92. data/spec/mtk/pattern/note_sequence_spec.rb +0 -121
  93. data/spec/mtk/pattern/pitch_sequence_spec.rb +0 -47
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Durations do
4
+
5
+ describe 'w' do
6
+ it 'is 4 beats' do
7
+ w.should == 4
8
+ end
9
+ it 'is available via a module property and via mixin' do
10
+ Durations::w.should == w
11
+ end
12
+ end
13
+
14
+ describe 'h' do
15
+ it 'is 2 beats' do
16
+ h.should == 2
17
+ end
18
+ it 'is available via a module property and via mixin' do
19
+ Durations::h.should == h
20
+ end
21
+ end
22
+
23
+ describe 'q' do
24
+ it 'is 1 beat' do
25
+ q.round.should == 1
26
+ end
27
+ it 'is available via a module property and via mixin' do
28
+ Durations::q.should == q
29
+ end
30
+ end
31
+
32
+ describe 'e' do
33
+ it 'is 1/2 of a beat' do
34
+ e.should == 1.0/2
35
+ end
36
+ it 'is available via a module property and via mixin' do
37
+ Durations::e.should == e
38
+ end
39
+ end
40
+
41
+ describe 's' do
42
+ it 'is 1/4 of a beat' do
43
+ s.should == 1.0/4
44
+ end
45
+ it 'is available via a module property and via mixin' do
46
+ Durations::s.should == s
47
+ end
48
+ end
49
+
50
+ describe 'r' do
51
+ it 'is 1/8 of a beat' do
52
+ r.should == 1.0/8
53
+ end
54
+ it 'is available via a module property and via mixin' do
55
+ Durations::r.should == r
56
+ end
57
+ end
58
+
59
+ describe 'x' do
60
+ it 'is 1/16 of a beat' do
61
+ x.should == 1.0/16
62
+ end
63
+ it 'is available via a module property and via mixin' do
64
+ Durations::x.should == x
65
+ end
66
+ end
67
+
68
+ describe "DURATIONS" do
69
+ it "contains all Durations pseudo-constants" do
70
+ Durations::DURATIONS.should =~ [w, h, q, e, s, r, x]
71
+ end
72
+
73
+ it "is immutable" do
74
+ lambda{ Durations::DURATIONS << :something }.should raise_error
75
+ end
76
+ end
77
+
78
+ describe "DURATION_NAMES" do
79
+ it "contains all Durations pseudo-constants names as strings" do
80
+ Durations::DURATION_NAMES.should =~ ['w', 'h', 'q', 'e', 's', 'r', 'x']
81
+ end
82
+
83
+ it "is immutable" do
84
+ lambda{ Durations::DURATION_NAMES << :something }.should raise_error
85
+ end
86
+ end
87
+
88
+ describe ".[]" do
89
+ it "looks up the constant by name" do
90
+ for duration in Durations::DURATION_NAMES
91
+ Durations[duration].should == Durations.send(duration)
92
+ end
93
+ end
94
+
95
+ it "supports a '.' suffix, which multiples the value by 1.5" do
96
+ for duration in Durations::DURATION_NAMES
97
+ Durations["#{duration}."].should == Durations.send(duration) * 1.5
98
+ end
99
+ end
100
+
101
+ it "supports a 't' suffix, which multiples the value by 2/3" do
102
+ for duration in Durations::DURATION_NAMES
103
+ Durations["#{duration}t"].should == Durations.send(duration) * 2/3.0
104
+ end
105
+ end
106
+
107
+ it "supports '.' and 't' suffixes in any combination" do
108
+ for duration in Durations::DURATION_NAMES
109
+ Durations["#{duration}.t"].should == Durations.send(duration) * 1.5 * 2/3.0
110
+ Durations["#{duration}t."].should == Durations.send(duration) * 1.5 * 2/3.0
111
+ Durations["#{duration}.."].should == Durations.send(duration) * 1.5 * 1.5
112
+ Durations["#{duration}..t.t."].should == Durations.send(duration) * 1.5**4 * (2/3.0)**2
113
+ end
114
+ end
115
+ end
116
+
117
+ end
118
+
@@ -1,13 +1,13 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe MTK::Dynamics do
3
+ describe MTK::Intensities do
4
4
 
5
5
  describe 'ppp' do
6
6
  it 'is equivalent to MIDI velocity 16' do
7
7
  (ppp * 127).round.should == 16
8
8
  end
9
9
  it 'is available via a module property and via mixin' do
10
- Dynamics::ppp.should == ppp
10
+ Intensities::ppp.should == ppp
11
11
  end
12
12
  end
13
13
 
@@ -16,7 +16,7 @@ describe MTK::Dynamics do
16
16
  (pp * 127).round.should == 32
17
17
  end
18
18
  it 'is available via a module property and via mixin' do
19
- Dynamics::pp.should == pp
19
+ Intensities::pp.should == pp
20
20
  end
21
21
  end
22
22
 
@@ -25,7 +25,7 @@ describe MTK::Dynamics do
25
25
  (p * 127).round.should == 48
26
26
  end
27
27
  it 'is available via a module property and via mixin' do
28
- Dynamics::p.should == p
28
+ Intensities::p.should == p
29
29
  end
30
30
  end
31
31
 
@@ -34,7 +34,7 @@ describe MTK::Dynamics do
34
34
  (mp * 127).round.should == 64
35
35
  end
36
36
  it 'is available via a module property and via mixin' do
37
- Dynamics::mp.should == mp
37
+ Intensities::mp.should == mp
38
38
  end
39
39
  end
40
40
 
@@ -43,7 +43,7 @@ describe MTK::Dynamics do
43
43
  (mf * 127).round.should == 79
44
44
  end
45
45
  it 'is available via a module property and via mixin' do
46
- Dynamics::mf.should == mf
46
+ Intensities::mf.should == mf
47
47
  end
48
48
  end
49
49
 
@@ -52,7 +52,7 @@ describe MTK::Dynamics do
52
52
  (f * 127).round.should == 95
53
53
  end
54
54
  it 'is available via a module property and via mixin' do
55
- Dynamics::f.should == f
55
+ Intensities::f.should == f
56
56
  end
57
57
  it "does not overwrite the PitchClass constant 'F'" do
58
58
  F.should be_a PitchClass
@@ -64,7 +64,7 @@ describe MTK::Dynamics do
64
64
  (ff * 127).round.should == 111
65
65
  end
66
66
  it 'is available via a module property and via mixin' do
67
- Dynamics::ff.should == ff
67
+ Intensities::ff.should == ff
68
68
  end
69
69
  end
70
70
 
@@ -73,20 +73,51 @@ describe MTK::Dynamics do
73
73
  (fff * 127).round.should == 127
74
74
  end
75
75
  it 'is available via a module property and via mixin' do
76
- Dynamics::fff.should == fff
76
+ Intensities::fff.should == fff
77
+ end
78
+ end
79
+
80
+ describe "INTENSITIES" do
81
+ it "contains all Intensities pseudo-constants" do
82
+ Intensities::INTENSITIES.should =~ [ppp, pp, p, mp, mf, f, ff, fff]
83
+ end
84
+
85
+ it "is immutable" do
86
+ lambda{ Intensities::INTENSITIES << :something }.should raise_error
87
+ end
88
+ end
89
+
90
+ describe "INTENSITY_NAMES" do
91
+ it "contains all Intensities pseudo-constants names as strings" do
92
+ Intensities::INTENSITY_NAMES.should =~ ['ppp', 'pp', 'p', 'mp', 'mf', 'f', 'ff', 'fff']
93
+ end
94
+
95
+ it "is immutable" do
96
+ lambda{ Intensities::INTENSITY_NAMES << :something }.should raise_error
77
97
  end
78
98
  end
79
99
 
80
100
  describe ".[]" do
81
101
  it "looks up the constant by name" do
82
- Dynamics['ppp'].should == ppp
83
- Dynamics['pp'].should == pp
84
- Dynamics['p'].should == p
85
- Dynamics['mp'].should == mp
86
- Dynamics['mf'].should == mf
87
- Dynamics['f'].should == f
88
- Dynamics['ff'].should == ff
89
- Dynamics['fff'].should == fff
102
+ for name in INTENSITY_NAMES
103
+ Intensities[name].should == Intensities.send(name)
104
+ end
105
+ end
106
+
107
+ it "adds 1.0/24 when the name ends with '+', except for 'fff+' which is 1.0 like 'fff'" do
108
+ for name in INTENSITY_NAMES
109
+ if name == 'fff'
110
+ Intensities["#{name}+"].should == 1.0
111
+ else
112
+ Intensities["#{name}+"].should == Intensities.send(name)+1.0/24
113
+ end
114
+ end
115
+ end
116
+
117
+ it "subtracts 1.0/24 when the name ends with '-'" do
118
+ for name in INTENSITY_NAMES
119
+ Intensities["#{name}-"].should == Intensities.send(name)-1.0/24
120
+ end
90
121
  end
91
122
  end
92
123
 
@@ -119,6 +119,27 @@ describe MTK::Intervals do
119
119
  end
120
120
  end
121
121
 
122
+
123
+ describe "INTERVALS" do
124
+ it "contains all intervals constants/pseudo-constants" do
125
+ Intervals::INTERVALS.should =~ [P1, m2, M2, m3, M3, P4, TT, P5, m6, M6, m7, M7, P8]
126
+ end
127
+
128
+ it "is immutable" do
129
+ lambda{ Intervals::INTERVALS << :something }.should raise_error
130
+ end
131
+ end
132
+
133
+ describe "INTERVAL_NAMES" do
134
+ it "contains all intervals constants/pseudo-constant names" do
135
+ Intervals::INTERVAL_NAMES.should =~ ['P1', 'm2', 'M2', 'm3', 'M3', 'P4', 'TT', 'P5', 'm6', 'M6', 'm7', 'M7', 'P8']
136
+ end
137
+
138
+ it "is immutable" do
139
+ lambda{ Intervals::INTERVAL_NAMES << :something }.should raise_error
140
+ end
141
+ end
142
+
122
143
  describe ".[]" do
123
144
  it "looks up the constant by name" do
124
145
  Intervals['P1'].should == P1
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::PitchClasses do
4
+ let(:cases) {
5
+ [
6
+ [PitchClass['C'], 'C', 0],
7
+ [PitchClass['Db'], 'Db', 1],
8
+ [PitchClass['D'], 'D', 2],
9
+ [PitchClass['Eb'], 'Eb', 3],
10
+ [PitchClass['E'], 'E', 4],
11
+ [PitchClass['F'], 'F', 5],
12
+ [PitchClass['Gb'], 'Gb', 6],
13
+ [PitchClass['G'], 'G', 7],
14
+ [PitchClass['Ab'], 'Ab', 8],
15
+ [PitchClass['A'], 'A', 9],
16
+ [PitchClass['Bb'], 'Bb', 10],
17
+ [PitchClass['B'], 'B', 11],
18
+ ]
19
+ }
20
+
21
+ it "defines constants for the 12 pitch classes in the twelve-tone octave" do
22
+ cases.length.should == 12
23
+ cases.each do |const, name, int_value|
24
+ const.name.should == name
25
+ const.to_i.should == int_value
26
+ end
27
+ end
28
+
29
+ describe "PITCH_CLASSES" do
30
+ it "contains the 12 pitch class constants" do
31
+ PitchClasses::PITCH_CLASSES.length.should == 12
32
+ PitchClasses::PITCH_CLASSES.should == cases.map{ |const,_,__| const }
33
+ end
34
+
35
+ it "is immutable" do
36
+ lambda{ PitchClasses::PITCH_CLASSES << :something }.should raise_error
37
+ end
38
+ end
39
+
40
+ describe "PITCH_CLASS_NAMES" do
41
+ it "contains the names of the 12 pitch class constants" do
42
+ PitchClasses::PITCH_CLASS_NAMES.length.should == 12
43
+ PitchClasses::PITCH_CLASS_NAMES.should == cases.map{ |_,name,__| name }
44
+ end
45
+
46
+ it "is immutable" do
47
+ lambda{ PitchClasses::PITCH_CLASS_NAMES << :something }.should raise_error
48
+ end
49
+ end
50
+
51
+ describe ".[]" do
52
+ it "acts like PitchClas.[]" do
53
+ for name in PitchClasses::PITCH_CLASS_NAMES
54
+ PitchClasses[name].should == PitchClass[name]
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Pitches do
4
+
5
+ it "defines constants for the 128 notes in MIDI" do
6
+ Pitches.constants.length.should == 130 # there's also the PITCHES and PITCH_NAMES constants
7
+ Pitches::C_1.should == Pitch.from_s('C-1')
8
+ Pitches::D0.should == Pitch.from_s('D0')
9
+ Pitches::Eb1.should == Pitch.from_s('Eb1')
10
+ Pitches::G9.should == Pitch.from_s('g9')
11
+ end
12
+
13
+ describe "PITCHES" do
14
+ it "contains all 128 pitch constants" do
15
+ Pitches::PITCHES.length.should == 128
16
+ Pitches::PITCHES.should include Pitches::C_1
17
+ Pitches::PITCHES.should include Pitches::D0
18
+ Pitches::PITCHES.should include Pitches::Eb1
19
+ Pitches::PITCHES.should include Pitches::G9
20
+ end
21
+
22
+ it "is immutable" do
23
+ lambda{ Pitches::PITCHES << :something }.should raise_error
24
+ end
25
+ end
26
+
27
+ describe "PITCH_NAMES" do
28
+ it "contains all 128 pitch constants" do
29
+ Pitches::PITCH_NAMES.length.should == 128
30
+ Pitches::PITCH_NAMES.should include 'C_1'
31
+ Pitches::PITCH_NAMES.should include 'D0'
32
+ Pitches::PITCH_NAMES.should include 'Eb1'
33
+ Pitches::PITCH_NAMES.should include 'G9'
34
+ end
35
+
36
+ it "is immutable" do
37
+ lambda{ Pitches::PITCH_NAMES << :something }.should raise_error
38
+ end
39
+ end
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
+ end
51
+
52
+ end
@@ -24,6 +24,20 @@ describe MTK::Event do
24
24
  it "is a read-only attribute" do
25
25
  lambda{ event.duration = 0 }.should raise_error
26
26
  end
27
+
28
+ it "is always positive (absolute value of the duration used to construct the Event)" do
29
+ Event.new(intensity, -duration).duration.should == duration
30
+ end
31
+ end
32
+
33
+ describe "#rest?" do
34
+ it "is true when the duration used to create the Event was negative" do
35
+ Event.new(intensity, -duration).rest?.should be_true
36
+ end
37
+
38
+ it "is false when the duration used to create the Event was positive" do
39
+ event.rest?.should be_false
40
+ end
27
41
  end
28
42
 
29
43
  describe "from_hash" do
@@ -88,9 +102,14 @@ describe MTK::Event do
88
102
  it "converts beats to pulses, given pulses_per_beat" do
89
103
  Event.new(0,1).duration_in_pulses(60).should == 60
90
104
  end
105
+
91
106
  it "rounds to the nearest pulse" do
92
107
  Event.new(0,1.5).duration_in_pulses(59).should == 89
93
108
  end
109
+
110
+ it "is always positive (uses absolute value of the duration used to construct the Event)" do
111
+ Event.new(intensity, -1).duration_in_pulses(60).should == 60
112
+ end
94
113
  end
95
114
 
96
115
  describe "#==" do