mtk 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. data/.yardopts +3 -2
  2. data/DEVELOPMENT_NOTES.md +114 -0
  3. data/INTRO.md +64 -8
  4. data/LICENSE.txt +1 -1
  5. data/README.md +31 -102
  6. data/Rakefile +56 -18
  7. data/bin/mtk +215 -0
  8. data/examples/crescendo.rb +5 -5
  9. data/examples/drum_pattern1.rb +23 -0
  10. data/examples/dynamic_pattern.rb +8 -11
  11. data/examples/gets_and_play.rb +26 -0
  12. data/examples/notation.rb +22 -0
  13. data/examples/play_midi.rb +8 -10
  14. data/examples/random_tone_row.rb +2 -2
  15. data/examples/syntax_to_midi.rb +28 -0
  16. data/examples/test_output.rb +8 -0
  17. data/examples/tone_row_melody.rb +6 -6
  18. data/lib/mtk.rb +52 -40
  19. data/lib/mtk/chord.rb +55 -0
  20. data/lib/mtk/constants/durations.rb +57 -0
  21. data/lib/mtk/constants/intensities.rb +61 -0
  22. data/lib/mtk/constants/intervals.rb +73 -0
  23. data/lib/mtk/constants/pitch_classes.rb +29 -0
  24. data/lib/mtk/constants/pitches.rb +52 -0
  25. data/lib/mtk/duration.rb +211 -0
  26. data/lib/mtk/events/event.rb +119 -0
  27. data/lib/mtk/events/note.rb +112 -0
  28. data/lib/mtk/events/parameter.rb +54 -0
  29. data/lib/mtk/helpers/collection.rb +164 -0
  30. data/lib/mtk/helpers/convert.rb +36 -0
  31. data/lib/mtk/helpers/lilypond.rb +162 -0
  32. data/lib/mtk/helpers/output_selector.rb +67 -0
  33. data/lib/mtk/helpers/pitch_collection.rb +23 -0
  34. data/lib/mtk/helpers/pseudo_constants.rb +26 -0
  35. data/lib/mtk/intensity.rb +156 -0
  36. data/lib/mtk/interval.rb +155 -0
  37. data/lib/mtk/lang/mtk_grammar.citrus +190 -13
  38. data/lib/mtk/lang/parser.rb +29 -0
  39. data/lib/mtk/melody.rb +94 -0
  40. data/lib/mtk/midi/dls_synth_device.rb +144 -0
  41. data/lib/mtk/midi/dls_synth_output.rb +62 -0
  42. data/lib/mtk/midi/file.rb +67 -32
  43. data/lib/mtk/midi/input.rb +97 -0
  44. data/lib/mtk/midi/jsound_input.rb +36 -17
  45. data/lib/mtk/midi/jsound_output.rb +48 -46
  46. data/lib/mtk/midi/output.rb +195 -0
  47. data/lib/mtk/midi/unimidi_input.rb +117 -0
  48. data/lib/mtk/midi/unimidi_output.rb +121 -0
  49. data/lib/mtk/{_numeric_extensions.rb → numeric_extensions.rb} +12 -0
  50. data/lib/mtk/patterns/chain.rb +49 -0
  51. data/lib/mtk/{pattern → patterns}/choice.rb +14 -8
  52. data/lib/mtk/patterns/cycle.rb +18 -0
  53. data/lib/mtk/patterns/for_each.rb +71 -0
  54. data/lib/mtk/patterns/function.rb +39 -0
  55. data/lib/mtk/{pattern → patterns}/lines.rb +11 -17
  56. data/lib/mtk/{pattern → patterns}/palindrome.rb +11 -8
  57. data/lib/mtk/patterns/pattern.rb +171 -0
  58. data/lib/mtk/patterns/sequence.rb +20 -0
  59. data/lib/mtk/pitch.rb +7 -6
  60. data/lib/mtk/pitch_class.rb +124 -46
  61. data/lib/mtk/pitch_class_set.rb +58 -35
  62. data/lib/mtk/sequencers/event_builder.rb +131 -0
  63. data/lib/mtk/sequencers/legato_sequencer.rb +24 -0
  64. data/lib/mtk/sequencers/rhythmic_sequencer.rb +28 -0
  65. data/lib/mtk/{sequencer/abstract_sequencer.rb → sequencers/sequencer.rb} +37 -11
  66. data/lib/mtk/{sequencer → sequencers}/step_sequencer.rb +4 -4
  67. data/lib/mtk/timeline.rb +39 -22
  68. data/lib/mtk/variable.rb +32 -0
  69. data/spec/mtk/chord_spec.rb +83 -0
  70. data/spec/mtk/{_constants → constants}/durations_spec.rb +12 -41
  71. data/spec/mtk/{_constants → constants}/intensities_spec.rb +13 -37
  72. data/spec/mtk/{_constants → constants}/intervals_spec.rb +14 -32
  73. data/spec/mtk/{_constants → constants}/pitch_classes_spec.rb +8 -4
  74. data/spec/mtk/{_constants → constants}/pitches_spec.rb +5 -1
  75. data/spec/mtk/duration_spec.rb +372 -0
  76. data/spec/mtk/events/event_spec.rb +234 -0
  77. data/spec/mtk/events/note_spec.rb +174 -0
  78. data/spec/mtk/events/parameter_spec.rb +220 -0
  79. data/spec/mtk/{helper → helpers}/collection_spec.rb +86 -3
  80. data/spec/mtk/{helper → helpers}/pseudo_constants_spec.rb +2 -2
  81. data/spec/mtk/intensity_spec.rb +289 -0
  82. data/spec/mtk/interval_spec.rb +265 -0
  83. data/spec/mtk/lang/parser_spec.rb +597 -0
  84. data/spec/mtk/melody_spec.rb +223 -0
  85. data/spec/mtk/midi/file_spec.rb +16 -16
  86. data/spec/mtk/midi/jsound_input_spec.rb +11 -0
  87. data/spec/mtk/midi/jsound_output_spec.rb +11 -0
  88. data/spec/mtk/midi/output_spec.rb +102 -0
  89. data/spec/mtk/midi/unimidi_input_spec.rb +11 -0
  90. data/spec/mtk/midi/unimidi_output_spec.rb +11 -0
  91. data/spec/mtk/{_numeric_extensions_spec.rb → numeric_extensions_spec.rb} +1 -0
  92. data/spec/mtk/patterns/chain_spec.rb +110 -0
  93. data/spec/mtk/{pattern → patterns}/choice_spec.rb +20 -30
  94. data/spec/mtk/{pattern → patterns}/cycle_spec.rb +25 -35
  95. data/spec/mtk/patterns/for_each_spec.rb +136 -0
  96. data/spec/mtk/{pattern → patterns}/function_spec.rb +17 -30
  97. data/spec/mtk/{pattern → patterns}/lines_spec.rb +11 -27
  98. data/spec/mtk/{pattern → patterns}/palindrome_spec.rb +13 -29
  99. data/spec/mtk/patterns/pattern_spec.rb +132 -0
  100. data/spec/mtk/patterns/sequence_spec.rb +203 -0
  101. data/spec/mtk/pitch_class_set_spec.rb +23 -21
  102. data/spec/mtk/pitch_class_spec.rb +151 -39
  103. data/spec/mtk/pitch_spec.rb +22 -1
  104. data/spec/mtk/sequencers/event_builder_spec.rb +245 -0
  105. data/spec/mtk/sequencers/legato_sequencer_spec.rb +45 -0
  106. data/spec/mtk/sequencers/rhythmic_sequencer_spec.rb +84 -0
  107. data/spec/mtk/sequencers/sequencer_spec.rb +215 -0
  108. data/spec/mtk/{sequencer → sequencers}/step_sequencer_spec.rb +35 -13
  109. data/spec/mtk/timeline_spec.rb +109 -16
  110. data/spec/mtk/variable_spec.rb +52 -0
  111. data/spec/spec_coverage.rb +2 -0
  112. data/spec/spec_helper.rb +3 -0
  113. metadata +188 -91
  114. data/lib/mtk/_constants/durations.rb +0 -80
  115. data/lib/mtk/_constants/intensities.rb +0 -81
  116. data/lib/mtk/_constants/intervals.rb +0 -85
  117. data/lib/mtk/_constants/pitch_classes.rb +0 -35
  118. data/lib/mtk/_constants/pitches.rb +0 -49
  119. data/lib/mtk/event.rb +0 -70
  120. data/lib/mtk/helper/collection.rb +0 -114
  121. data/lib/mtk/helper/event_builder.rb +0 -85
  122. data/lib/mtk/helper/pseudo_constants.rb +0 -26
  123. data/lib/mtk/lang/grammar.rb +0 -17
  124. data/lib/mtk/note.rb +0 -63
  125. data/lib/mtk/pattern/abstract_pattern.rb +0 -132
  126. data/lib/mtk/pattern/cycle.rb +0 -51
  127. data/lib/mtk/pattern/enumerator.rb +0 -26
  128. data/lib/mtk/pattern/function.rb +0 -46
  129. data/lib/mtk/pattern/sequence.rb +0 -30
  130. data/lib/mtk/pitch_set.rb +0 -84
  131. data/lib/mtk/sequencer/rhythmic_sequencer.rb +0 -29
  132. data/lib/mtk/transform/invertible.rb +0 -15
  133. data/lib/mtk/transform/mappable.rb +0 -18
  134. data/lib/mtk/transform/set_theory_operations.rb +0 -34
  135. data/lib/mtk/transform/transposable.rb +0 -14
  136. data/spec/mtk/event_spec.rb +0 -139
  137. data/spec/mtk/helper/event_builder_spec.rb +0 -92
  138. data/spec/mtk/lang/grammar_spec.rb +0 -100
  139. data/spec/mtk/note_spec.rb +0 -115
  140. data/spec/mtk/pattern/abstract_pattern_spec.rb +0 -45
  141. data/spec/mtk/pattern/note_cycle_spec.rb.bak +0 -116
  142. data/spec/mtk/pattern/pitch_cycle_spec.rb.bak +0 -47
  143. data/spec/mtk/pattern/pitch_sequence_spec.rb.bak +0 -37
  144. data/spec/mtk/pattern/sequence_spec.rb +0 -151
  145. data/spec/mtk/pitch_set_spec.rb +0 -198
  146. data/spec/mtk/sequencer/abstract_sequencer_spec.rb +0 -159
  147. data/spec/mtk/sequencer/rhythmic_sequencer_spec.rb +0 -49
@@ -26,6 +26,12 @@ describe MTK::PitchClassSet do
26
26
  end
27
27
  end
28
28
 
29
+ describe ".new" do
30
+ it "maintains the pitch class collection exactly (preserves order and keeps duplicates)" do
31
+ PitchClassSet.new([C, E, G, E, B, C]).pitch_classes.should == [C, E, G, E, B, C]
32
+ end
33
+ end
34
+
29
35
  describe "#pitch_classes" do
30
36
  it "is the list of pitch_classes contained in this set" do
31
37
  pitch_class_set.pitch_classes.should == pitch_classes
@@ -45,10 +51,6 @@ describe MTK::PitchClassSet do
45
51
  pitch_classes << D
46
52
  pitch_class_set.pitch_classes.length.should == 3
47
53
  end
48
-
49
- it "does not include duplicates" do
50
- PitchClassSet.new([C, E, G, C]).pitch_classes.should == [C, E, G]
51
- end
52
54
  end
53
55
 
54
56
  describe "#to_a" do
@@ -113,7 +115,7 @@ describe MTK::PitchClassSet do
113
115
 
114
116
  describe '#transpose' do
115
117
  it 'transposes by the given semitones' do
116
- (pitch_class_set.transpose 4.semitones).should == PitchClassSet(E, Ab, B)
118
+ (pitch_class_set.transpose 4).should == PitchClassSet(E, Ab, B)
117
119
  end
118
120
  end
119
121
 
@@ -169,12 +171,6 @@ describe MTK::PitchClassSet do
169
171
  end
170
172
  end
171
173
 
172
- describe "#repeat" do
173
- it "does nothing to PitchClassSets, since duplicates are removed from Sets" do
174
- pitch_class_set.repeat.should == pitch_class_set
175
- end
176
- end
177
-
178
174
  describe "#rotate" do
179
175
  it "produces a PitchClassSet that is rotated by the given offset" do
180
176
  pitch_class_set.rotate(2).should == PitchClassSet(G,C,E)
@@ -205,8 +201,8 @@ describe MTK::PitchClassSet do
205
201
  end
206
202
 
207
203
  describe "#concat" do
208
- it "appends the non-duplicate pitch classes from the other set" do
209
- pitch_class_set.concat(PitchClassSet(D,E,F)).should == PitchClassSet(C,E,G,D,F)
204
+ it "appends the pitch classes from the other set" do
205
+ pitch_class_set.concat(PitchClassSet(D,E,F)).should == PitchClassSet(C,E,G,D,E,F)
210
206
  end
211
207
  end
212
208
 
@@ -249,6 +245,10 @@ describe MTK::PitchClassSet do
249
245
  pitch_class_set.should_not == PitchClassSet(C,G,E)
250
246
  end
251
247
 
248
+ it "is false when if otherwise equal pitch class sets don't contain the same number of duplicates" do
249
+ PitchClassSet.new([C, E, G]).should_not == PitchClassSet.new([C, C, E, G])
250
+ end
251
+
252
252
  it "is false if two pitch class sets do not contain the same pitch classes" do
253
253
  pitch_class_set.should_not == PitchClassSet(C,E)
254
254
  end
@@ -263,6 +263,10 @@ describe MTK::PitchClassSet do
263
263
  pitch_class_set.should =~ PitchClassSet(C,E,G)
264
264
  end
265
265
 
266
+ it "is true when all the pitch classes are equal, even with different numbers of duplicates" do
267
+ Melody.new([C, E, G]).should =~ Melody.new([C, C, E, G])
268
+ end
269
+
266
270
  it "is true if two pitch class sets are not in the same order" do
267
271
  pitch_class_set.should =~ PitchClassSet(C,G,E)
268
272
  end
@@ -286,18 +290,16 @@ describe MTK::PitchClassSet do
286
290
  end
287
291
  end
288
292
 
289
- describe ".span_for" do
290
- it "measure the distance between the first and last pitch classes" do
291
- PitchClassSet.span_for([C,D,E,F,G]).should == 7
292
- end
293
- end
294
-
295
293
  end
296
294
 
297
295
  describe MTK do
298
296
 
299
297
  describe '#PitchClassSet' do
300
298
 
299
+ it "constructs a PitchClassSet" do
300
+ PitchClassSet(C,D).should be_a PitchClassSet
301
+ end
302
+
301
303
  it "acts like new for a single Array argument" do
302
304
  PitchClassSet([C,D]).should == PitchClassSet.new([C,D])
303
305
  end
@@ -322,9 +324,9 @@ describe MTK do
322
324
  PitchClassSet('C').should == PitchClassSet.new([C])
323
325
  end
324
326
 
325
- it "returns the argument if it's already a PitchClassSet" do
327
+ it "handles a a PitchClassSet" do
326
328
  pitch_set = PitchClassSet.new([C,D])
327
- PitchClassSet(pitch_set).should be_equal pitch_set
329
+ PitchClassSet(pitch_set).should == PitchClassSet([C,D])
328
330
  end
329
331
 
330
332
  it "raises an error for types it doesn't understand" do
@@ -2,26 +2,29 @@ require 'spec_helper'
2
2
 
3
3
  describe MTK::PitchClass do
4
4
 
5
- let(:names) {
5
+ let(:enharmonic_spellings_grouped_by_value) {
6
+ # not sure how to test this without re-coding these names here
6
7
  [
7
- ['B#', 'C', 'Dbb'],
8
- ['B##', 'C#', 'Db'],
9
- ['C##', 'D', 'Ebb'],
10
- ['D#', 'Eb', 'Fbb'],
11
- ['D##', 'E', 'Fb'],
12
- ['E#', 'F', 'Gbb'],
13
- ['E##', 'F#', 'Gb'],
14
- ['F##', 'G', 'Abb'],
15
- ['G#', 'Ab'],
16
- ['G##', 'A', 'Bbb'],
17
- ['A#', 'Bb', 'Cbb'],
18
- ['A##', 'B', 'Cb']
19
- ].flatten
8
+ %w( B# C Dbb ),
9
+ %w( B## C# Db ),
10
+ %w( C## D Ebb ),
11
+ %w( D# Eb Fbb ),
12
+ %w( D## E Fb ),
13
+ %w( E# F Gbb ),
14
+ %w( E## F# Gb ),
15
+ %w( F## G Abb ),
16
+ %w( G# Ab ),
17
+ %w( G## A Bbb ),
18
+ %w( A# Bb Cbb ),
19
+ %w( A## B Cb )
20
+ ]
20
21
  }
22
+ let(:enharmonic_spellings) { enharmonic_spellings_grouped_by_value.flatten }
21
23
 
24
+
22
25
  describe 'NAMES' do
23
26
  it "is the 12 note names in western chromatic scale" do
24
- PitchClass::NAMES =~ ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B']
27
+ PitchClass::NAMES.should =~ %w( C Db D Eb E F Gb G Ab A Bb B )
25
28
  end
26
29
 
27
30
  it "is immutable" do
@@ -29,60 +32,111 @@ describe MTK::PitchClass do
29
32
  end
30
33
  end
31
34
 
35
+
36
+ describe 'VALID_NAMES_BY_VALUE' do
37
+ it 'contains enharmonic spellings of NAMES' do
38
+ PitchClass::VALID_NAMES_BY_VALUE.should =~ enharmonic_spellings_grouped_by_value
39
+ end
40
+
41
+ it 'organized such that the index of each equivalent spelling matches the index in NAMES' do
42
+ PitchClass::NAMES.each.with_index do |name,index|
43
+ PitchClass::VALID_NAMES_BY_VALUE[index].should include(name)
44
+ end
45
+ end
46
+
47
+ it 'is immutable' do
48
+ lambda{ PitchClass::VALID_NAMES_BY_VALUE << 'H' }.should raise_error
49
+ end
50
+ end
51
+
52
+
32
53
  describe 'VALID_NAMES' do
33
- it "is all enharmonic spellings of NAMES including sharps, flats, double-sharps, and double-flats" do
34
- PitchClass::VALID_NAMES =~ names
54
+ it 'is all enharmonic spellings of NAMES including sharps, flats, double-sharps, and double-flats' do
55
+ PitchClass::VALID_NAMES.should =~ enharmonic_spellings.flatten
35
56
  end
36
57
 
37
- it "is immutable" do
58
+ it 'is immutable' do
38
59
  lambda{ PitchClass::VALID_NAMES << 'H' }.should raise_error
39
60
  end
40
61
  end
41
62
 
63
+
64
+ describe 'PITCH_CLASSES' do
65
+ it 'is the 12 pitch classes in the chromatic scale, indexed by value' do
66
+ for pc in PitchClass::PITCH_CLASSES
67
+ pc.should == PitchClass::PITCH_CLASSES[pc.value]
68
+ end
69
+ end
70
+
71
+ it 'is immutable' do
72
+ lambda{ PitchClass::PITCH_CLASSES << C }.should raise_error
73
+ end
74
+ end
75
+
76
+
42
77
  describe '.new' do
43
78
  it "is private" do
44
79
  lambda{ PitchClass.new('C',0) }.should raise_error
45
80
  end
46
81
  end
47
82
 
48
- describe '.from_s' do
83
+
84
+ describe '.[]' do
49
85
  context "the argument is a valid name" do
50
86
  it "returns a PitchClass" do
51
- names.each { |name| PitchClass[name].should be_a PitchClass }
87
+ enharmonic_spellings.each { |name| PitchClass[name].should be_a PitchClass }
52
88
  end
53
89
  it "returns an object with that name" do
54
- names.each { |name| PitchClass[name].name.should == name }
90
+ enharmonic_spellings.each { |name| PitchClass[name].name.should == name }
55
91
  end
56
92
  it "ignores case" do
57
- for name in names
93
+ for name in enharmonic_spellings
58
94
  PitchClass[name.upcase].name.should == name
59
95
  PitchClass[name.downcase].name.should == name
60
96
  end
61
97
  end
62
98
  end
63
99
  context "the argument is not a valid name" do
64
- it "returns nil, if the name doesn't exist" do
65
- PitchClass['z'].should be_nil
100
+ it "raises a NameError if the name doesn't exist" do
101
+ lambda{ PitchClass['z'] }.should raise_error ArgumentError
66
102
  end
67
103
  end
68
104
  end
69
105
 
106
+
70
107
  describe '.from_s' do
71
- it "acts like .[]" do
72
- for name in ['C', 'bbb', 'z']
73
- PitchClass.from_s(name).should == PitchClass[name]
108
+ it "returns a PitchClass for any valid name" do
109
+ for name in enharmonic_spellings
110
+ pc = PitchClass.from_s(name)
111
+ pc.should be_a PitchClass
112
+ pc.name.should == name
113
+ end
114
+ end
115
+
116
+ it "does case-insensitive matching" do
117
+ for name in enharmonic_spellings
118
+ pc = PitchClass.from_s(name.downcase)
119
+ pc.should be_a PitchClass
120
+ pc.name.should == name
74
121
  end
75
122
  end
123
+
124
+ it "raises a ArgumentError for invalid arguments" do
125
+ lambda{ PitchClass.from_s('H') }.should raise_error ArgumentError
126
+ end
76
127
  end
77
128
 
129
+
78
130
  describe '.from_name' do
79
- it "acts like .[]" do
80
- for name in ['C', 'bbb', 'z']
81
- PitchClass.from_name(name).should == PitchClass[name]
131
+ it "acts like .from_s" do
132
+ for name in enharmonic_spellings
133
+ PitchClass.from_name(name).should == PitchClass.from_s(name)
82
134
  end
135
+ lambda{ PitchClass.from_name('H') }.should raise_error ArgumentError
83
136
  end
84
137
  end
85
138
 
139
+
86
140
  describe '.from_i' do
87
141
  it "returns the PitchClass with that value" do
88
142
  PitchClass.from_i(2).should == D
@@ -94,29 +148,59 @@ describe MTK::PitchClass do
94
148
  end
95
149
  end
96
150
 
151
+
152
+ describe '.from_value' do
153
+ it 'acts like .from_i' do
154
+ (0..11).each{|v| PitchClass.from_value(v).should == PitchClass.from_i(v) }
155
+ end
156
+ end
157
+
97
158
  describe '#name' do
98
159
  it "is the name of the pitch class" do
99
160
  C.name.should == 'C'
100
161
  end
101
162
  end
102
163
 
164
+
165
+ describe '#value' do
166
+ it "is the value of the pitch class" do
167
+ PitchClass::NAMES.each.with_index do |name,value|
168
+ PitchClass[name].value.should == value
169
+ end
170
+ end
171
+ end
172
+
173
+
103
174
  describe '#to_i' do
104
175
  it "is the integer value of the pitch class" do
105
- C.to_i.should == 0
106
- D.to_i.should == 2
107
- E.to_i.should == 4
176
+ PitchClass::NAMES.each.with_index do |name,value|
177
+ PitchClass[name].to_i.should == value
178
+ PitchClass[name].to_i.should be_an Integer
179
+ end
180
+ end
181
+ end
182
+
183
+
184
+ describe '#to_f' do
185
+ it "is the floating point value of the pitch class" do
186
+ PitchClass::NAMES.each.with_index do |name,value|
187
+ PitchClass[name].to_f.should == value
188
+ PitchClass[name].to_f.should be_a Float
189
+ end
108
190
  end
109
191
  end
110
192
 
193
+
111
194
  describe '#to_s' do
112
195
  it "returns the name" do
113
196
  C.to_s.should == C.name
114
- for name in names
197
+ for name in enharmonic_spellings
115
198
  PitchClass.from_s(name).to_s.should == name
116
199
  end
117
200
  end
118
201
  end
119
202
 
203
+
120
204
  describe '#==' do
121
205
  it "checks for equality" do
122
206
  C.should == C
@@ -128,6 +212,7 @@ describe MTK::PitchClass do
128
212
  end
129
213
  end
130
214
 
215
+
131
216
  describe "#<=>" do
132
217
  it "compares the underlying int value" do
133
218
  (C <=> D).should < 0
@@ -135,32 +220,46 @@ describe MTK::PitchClass do
135
220
  end
136
221
  end
137
222
 
223
+
138
224
  describe '#+' do
139
- it "adds the integer value of the argument and #to_i" do
225
+ it "adds an integer argument to the pitch class's value" do
140
226
  (C + 4).should == E
141
227
  end
142
228
 
143
229
  it "'wraps around' the range 0-11" do
144
230
  (D + 10).should == C
145
231
  end
232
+
233
+ it "adds a floating point argument to the pitch class's value, and rounds to the nearest pitch class" do
234
+ (C + 0.49).should == C
235
+ (C + 0.50).should == Db
236
+ end
146
237
  end
147
238
 
239
+
148
240
  describe "#transpose" do
149
241
  it "behaves like #+" do
150
- C.transpose(2.semitones).should == C + 2
242
+ C.transpose(2).should == C + 2
151
243
  end
152
244
  end
153
245
 
246
+
154
247
  describe '#-' do
155
- it "subtracts the integer value of the argument from #to_i" do
248
+ it "subtracts an integer argument from the pitch class's value" do
156
249
  (E - 2).should == D
157
250
  end
158
251
 
159
252
  it "'wraps around' the range 0-11" do
160
253
  (C - 8).should == E
161
254
  end
255
+
256
+ it "subtracts a floating point argument from the pitch class's value, and rounds to the nearest pitch class'" do
257
+ (E - 0.50).should == E
258
+ (E - 0.51).should == Eb
259
+ end
162
260
  end
163
261
 
262
+
164
263
  describe "#invert" do
165
264
  it 'inverts the pitch class around the given center pitch class' do
166
265
  E.invert(D).should == C
@@ -173,8 +272,13 @@ describe MTK::PitchClass do
173
272
  it "returns the pitch class when the argument is the same pitch class" do
174
273
  E.invert(E).should == E
175
274
  end
275
+
276
+ it 'can invert pitch classes around the halfway point between 2 pitch classes' do
277
+ C.invert(0.5).should == Db
278
+ end
176
279
  end
177
280
 
281
+
178
282
  describe "#distance_to" do
179
283
  it "is the distance in semitones between 2 PitchClass objects" do
180
284
  C.distance_to(D).should == 2
@@ -215,7 +319,7 @@ describe MTK do
215
319
  PitchClass(:D).should == PitchClass.from_s('D')
216
320
  end
217
321
 
218
- it "acts like from_i if the argument is a Numberic" do
322
+ it "acts like from_i if the argument is a Numeric" do
219
323
  PitchClass(3).should == PitchClass.from_i(3)
220
324
  end
221
325
 
@@ -223,8 +327,16 @@ describe MTK do
223
327
  PitchClass(C).should be_equal C
224
328
  end
225
329
 
330
+ it "raises an error for Strings it doesn't understand" do
331
+ lambda{ PitchClass('H') }.should raise_error ArgumentError
332
+ end
333
+
334
+ it "raises an error for Symbols it doesn't understand" do
335
+ lambda{ PitchClass(:H) }.should raise_error ArgumentError
336
+ end
337
+
226
338
  it "raises an error for types it doesn't understand" do
227
- lambda{ PitchClass({:not => :compatible}) }.should raise_error
339
+ lambda{ PitchClass({:not => :compatible}) }.should raise_error ArgumentError
228
340
  end
229
341
  end
230
342
 
@@ -69,6 +69,10 @@ describe MTK::Pitch do
69
69
  it("converts 'B#4' to middle c") { Pitch.from_s('B#4').should == middle_c }
70
70
  it("converts 'C-1' to a low c, 5 octaves below middle C") { Pitch.from_s('C-1').should == middle_c - 60 }
71
71
  it("converts 'C4+50.0cents' to middle C and 50 cents") { Pitch.from_s('C4+50.0cents').should == middle_c_and_50_cents }
72
+
73
+ it "raises an ArgumentError for invalid arguments" do
74
+ lambda{ Pitch.from_s('H4') }.should raise_error ArgumentError
75
+ end
72
76
  end
73
77
 
74
78
  describe '.from_name' do
@@ -137,6 +141,15 @@ describe MTK::Pitch do
137
141
  end
138
142
  end
139
143
 
144
+ describe '#inspect' do
145
+ it 'is "#<MTK::Pitch:{object_id} @value={value}>"' do
146
+ for value in [0, 60, 60.5, 127]
147
+ pitch = Pitch.from_f(value)
148
+ pitch.inspect.should == "#<MTK::Pitch:#{pitch.object_id} @value=#{value}>"
149
+ end
150
+ end
151
+ end
152
+
140
153
  describe '#+' do
141
154
  it 'adds the integer value of the argument and #to_i' do
142
155
  (middle_c + 2).should == Pitch.from_i(62)
@@ -156,7 +169,7 @@ describe MTK::Pitch do
156
169
 
157
170
  describe "#transpose" do
158
171
  it "behaves like #+" do
159
- middle_c.transpose(2.semitones).should == middle_c + 2
172
+ middle_c.transpose(2).should == middle_c + 2
160
173
  end
161
174
  end
162
175
 
@@ -268,6 +281,14 @@ describe MTK do
268
281
  Pitch(C, 4, 0.5).should == Pitch.new(C, 4, 0.5)
269
282
  end
270
283
 
284
+ it "raises an error for Strings it doesn't understand" do
285
+ lambda{ Pitch('H4') }.should raise_error
286
+ end
287
+
288
+ it "raises an error for Symbols it doesn't understand" do
289
+ lambda{ Pitch(:H4) }.should raise_error
290
+ end
291
+
271
292
  it "raises an error for types it doesn't understand" do
272
293
  lambda{ Pitch({:not => :compatible}) }.should raise_error
273
294
  end