mtk 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,16 +1,16 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe MTK::Helper::Collection do
3
+ describe MTK::Helpers::Collection do
4
4
 
5
5
  class MockCollection
6
- include MTK::Helper::Collection
6
+ include MTK::Helpers::Collection
7
7
  attr_reader :elements
8
8
  def initialize(elements); @elements = elements end
9
9
  def self.from_a(elements); new(elements) end
10
10
  end
11
11
 
12
12
  class MockCollectionWithOptions
13
- include MTK::Helper::Collection
13
+ include MTK::Helpers::Collection
14
14
  attr_reader :elements, :options
15
15
  def initialize(elements, options={})
16
16
  @elements = elements
@@ -73,6 +73,16 @@ describe MTK::Helper::Collection do
73
73
  end
74
74
  end
75
75
 
76
+ describe "#map" do
77
+ it "returns a Collection with each item replaced with the results of the block" do
78
+ collection.map{|item| item + 10}.should == [11, 12, 13, 14, 15]
79
+ end
80
+
81
+ it "maintains the options from the original collection" do
82
+ collection_with_options.map{|item| item + 10}.options.should == options
83
+ end
84
+ end
85
+
76
86
  describe "#first" do
77
87
  it "is #[0] when no argument is given" do
78
88
  collection.first.should == collection[0]
@@ -252,6 +262,69 @@ describe MTK::Helper::Collection do
252
262
  end
253
263
  end
254
264
 
265
+ describe "#partition" do
266
+ # TODO: better descriptions! (and document the method too!)
267
+
268
+ context "Numeric argument" do
269
+ it "partitions the elements into groups of the size, plus whatever's left over as the last element" do
270
+ collection.partition(2).should == [
271
+ MockCollection.new([1,2]),
272
+ MockCollection.new([3,4]),
273
+ MockCollection.new([5])
274
+ ]
275
+ end
276
+ end
277
+
278
+ context "Array argument" do
279
+ it "partitions the elements into groups of the size of the argument elements" do
280
+ collection.partition([1,2,2]).should == [
281
+ MockCollection.new([1]),
282
+ MockCollection.new([2,3]),
283
+ MockCollection.new([4,5])
284
+ ]
285
+ end
286
+
287
+ it "does not include leftover elements" do
288
+ collection.partition([1,3]).should == [
289
+ MockCollection.new([1]),
290
+ MockCollection.new([2,3,4])
291
+ ]
292
+ end
293
+
294
+ it "does not include extra elements" do
295
+ collection.partition([1,5]).should == [
296
+ MockCollection.new([1]),
297
+ MockCollection.new([2,3,4,5])
298
+ ]
299
+ end
300
+ end
301
+
302
+ context "no argument, block given" do
303
+ it "partitions the elements into groups with the same block return value" do
304
+ collection.partition{|item| item % 3 }.should =~ [
305
+ MockCollection.new([1,4]),
306
+ MockCollection.new([2,5]),
307
+ MockCollection.new([3])
308
+ ]
309
+ end
310
+
311
+ it "optionally passes the item index into the block" do
312
+ collection.partition{|item,index| (item*index) % 3 }.should =~ [
313
+ # 1*0, 2*1, 3*2, 4*3, 5*4 => (0, 2, 6, 12, 20) % 3 => 0, 2, 0, 0, 2
314
+ MockCollection.new([1,3,4]),
315
+ MockCollection.new([2,5]),
316
+ ]
317
+ end
318
+ end
319
+
320
+ context "incompatible / missing argument, no block given" do
321
+ it "returns self" do
322
+ collection.partition.should == collection
323
+ end
324
+ end
325
+
326
+ end
327
+
255
328
  describe "#==" do
256
329
  it "is true when the elements in 2 Collections are equal" do
257
330
  collection.should == MockCollection.new(elements)
@@ -288,4 +361,14 @@ describe MTK::Helper::Collection do
288
361
  end
289
362
  end
290
363
 
364
+ end
365
+
366
+
367
+ describe "Array" do
368
+ describe "#rotate" do
369
+ # test the Ruby 1.8 backport of Array#rotate
370
+ it "should rotate the Array, as in Ruby 1.9's API" do
371
+ [1,2,3].rotate(1).should == [2,3,1]
372
+ end
373
+ end
291
374
  end
@@ -1,9 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe MTK::Helper::PseudoConstants do
3
+ describe MTK::Helpers::PseudoConstants do
4
4
 
5
5
  module MockConstants
6
- extend MTK::Helper::PseudoConstants
6
+ extend MTK::Helpers::PseudoConstants
7
7
  define_constant 'constant', :value
8
8
  end
9
9
 
@@ -0,0 +1,289 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Intensity do
4
+
5
+ let(:half_intensity) { Intensity[0.5] }
6
+
7
+
8
+ describe 'NAMES' do
9
+ it "is the list of base intensity names available" do
10
+ Intensity::NAMES.should =~ %w( ppp pp p mp mf o ff fff )
11
+ end
12
+
13
+ it "is immutable" do
14
+ lambda{ Intensity::NAMES << 'z' }.should raise_error
15
+ end
16
+ end
17
+
18
+
19
+ describe 'VALUES_BY_NAME' do
20
+ it 'maps names to values' do
21
+ Intensity::VALUES_BY_NAME.each do |name,value|
22
+ Intensity.from_s(name).value.should == value
23
+ end
24
+ end
25
+
26
+ it 'is immutable' do
27
+ lambda{ Intensity::VALUES_BY_NAME << 'z' }.should raise_error
28
+ end
29
+ end
30
+
31
+
32
+ describe '.new' do
33
+ it "constructs a Intensity with whatever value is given" do
34
+ float = 0.5
35
+ value = Intensity.new(float).value
36
+ value.should be_equal float
37
+ end
38
+ end
39
+
40
+
41
+ describe '.[]' do
42
+ it "constructs and caches a intensity from a Numeric" do
43
+ Intensity[1].should be_equal Intensity[1]
44
+ end
45
+
46
+ it "retains the new() method's ability to construct uncached objects" do
47
+ Intensity.new(1).should_not be_equal Intensity[1]
48
+ end
49
+
50
+ it "converts the value to floating point" do
51
+ value = Intensity[Rational(1,2)].value
52
+ value.should be_a Float
53
+ value.should == 0.5
54
+ end
55
+
56
+ end
57
+
58
+
59
+ describe '.from_i' do
60
+ it "acts like .[]" do
61
+ Intensity.from_i(0).value.should == 0.0
62
+ Intensity.from_i(4).value.should == 4.0
63
+ end
64
+ end
65
+
66
+ describe '.from_f' do
67
+ it "acts like .[]" do
68
+ value = Intensity.from_f(Rational(1,2)).value
69
+ value.should be_a Float
70
+ value.should == 0.5
71
+ end
72
+ end
73
+
74
+ describe '.from_s' do
75
+ it "converts any of the intensity NAMES into a Intensity with the value from the VALUES_BY_NAME mapping" do
76
+ for name in Intensity::NAMES
77
+ Intensity.from_s(name).value.should == Intensity::VALUES_BY_NAME[name]
78
+ end
79
+ end
80
+
81
+ it "adds 1.0/24 when the name ends with '+', except for 'fff+' which is 1.0 like 'fff'" do
82
+ for name in Intensity::NAMES
83
+ if name == 'fff'
84
+ Intensity.from_s("#{name}+").should == Intensity[1.0]
85
+ else
86
+ Intensity.from_s("#{name}+").should == Intensity[ Intensity::VALUES_BY_NAME[name] +1.0/24 ]
87
+ end
88
+ end
89
+ end
90
+
91
+ it "subtracts 1.0/24 when the name ends with '-'" do
92
+ for name in Intensity::NAMES
93
+ Intensity.from_s("#{name}-").should == Intensity[ Intensity::VALUES_BY_NAME[name] - 1.0/24 ]
94
+ end
95
+ end
96
+ end
97
+
98
+ describe '.from_name' do
99
+ it "acts like .from_s" do
100
+ for name in Intensity::NAMES
101
+ Intensity.from_name(name).value.should == Intensity::VALUES_BY_NAME[name]
102
+ end
103
+ end
104
+ end
105
+
106
+
107
+ describe '#value' do
108
+ it "is the value used to construct the Intensity" do
109
+ Intensity.new(1.111).value.should == 1.111
110
+ end
111
+ end
112
+
113
+
114
+ describe '#to_f' do
115
+ it "is the value as a floating point number" do
116
+ f = Intensity.new(Rational(1,2)).to_f
117
+ f.should be_a Float
118
+ f.should == 0.5
119
+ end
120
+ end
121
+
122
+ describe '#to_i' do
123
+ it "is the value rounded to the nearest integer" do
124
+ i = Intensity.new(0.5).to_i
125
+ i.should be_a Fixnum
126
+ i.should == 1
127
+ Intensity.new(0.49).to_i.should == 0
128
+ end
129
+ end
130
+
131
+ describe '#to_percentage' do
132
+ it 'is the value*100 rounded to the nearest integer' do
133
+ Intensity.new(0).to_percent.should == 0
134
+ Intensity.new(0.5).to_percent.should == 50
135
+ Intensity.new(1).to_percent.should == 100
136
+ Intensity.new(2).to_percent.should == 200
137
+ end
138
+ end
139
+
140
+ describe '#to_s' do
141
+ it "is to_percentage suffixed with '% intensity'" do
142
+ Intensity.new(0).to_s.should == '0% intensity'
143
+ Intensity.new(0.5).to_s.should == '50% intensity'
144
+ Intensity.new(1).to_s.should == '100% intensity'
145
+ Intensity.new(2).to_s.should == '200% intensity'
146
+ end
147
+ end
148
+
149
+ describe '#inspect' do
150
+ it 'is "#<MTK::Intensity:{object_id} @value={value}>"' do
151
+ for value in [0, 60, 60.5, 127]
152
+ intensity = Intensity.new(value)
153
+ intensity.inspect.should == "#<MTK::Intensity:#{intensity.object_id} @value=#{value}>"
154
+ end
155
+ end
156
+ end
157
+
158
+ describe '#==' do
159
+ it "compares two intensity values for equality" do
160
+ Intensity.new(Rational(1,2)).should == Intensity.new(0.5)
161
+ Intensity.new(4).should == Intensity.new(4)
162
+ Intensity.new(1.1).should_not == Intensity.new(1)
163
+ end
164
+ end
165
+
166
+ describe "#<=>" do
167
+ it "orders intensitys based on their underlying value" do
168
+ ( Intensity.new(1) <=> Intensity.new(1.1) ).should < 0
169
+ ( Intensity.new(2) <=> Intensity.new(1) ).should > 0
170
+ ( Intensity.new(1.0) <=> Intensity.new(1) ).should == 0
171
+ end
172
+ end
173
+
174
+
175
+
176
+ describe '#+' do
177
+ it 'adds #value to the Numeric argument' do
178
+ (half_intensity + 0.25).should == Intensity[0.75]
179
+ end
180
+
181
+ it 'works with an Intensity argument' do
182
+ (half_intensity + Intensity[0.25]).should == Intensity[0.75]
183
+ end
184
+
185
+ it 'returns a new intensity (Intensity is immutable)' do
186
+ original = half_intensity
187
+ modified = half_intensity + 0.25
188
+ original.should_not == modified
189
+ original.should == Intensity[0.5]
190
+ end
191
+ end
192
+
193
+ describe '#-' do
194
+ it 'subtract the Numeric argument from #value' do
195
+ (half_intensity - 0.25).should == Intensity[0.25]
196
+ end
197
+
198
+ it 'works with a Intensity argument' do
199
+ (half_intensity - Intensity[0.25]).should == Intensity[0.25]
200
+ end
201
+
202
+ it 'returns a new intensity (Intensity is immutable)' do
203
+ original = half_intensity
204
+ modified = half_intensity - 0.25
205
+ original.should_not == modified
206
+ original.should == Intensity[0.5]
207
+ end
208
+ end
209
+
210
+
211
+ describe '#*' do
212
+ it 'multiplies #value to the Numeric argument' do
213
+ (half_intensity * 0.1).should == Intensity[0.05]
214
+ end
215
+
216
+ it 'works with a Intensity argument' do
217
+ (half_intensity * Intensity[0.1]).should == Intensity[0.05]
218
+ end
219
+
220
+ it 'returns a new intensity (Intensity is immutable)' do
221
+ original = half_intensity
222
+ modified = half_intensity * 2
223
+ original.should_not == modified
224
+ original.should == Intensity[0.5]
225
+ end
226
+ end
227
+
228
+ describe '#/' do
229
+ it 'divides #value by the Numeric argument' do
230
+ (half_intensity / 2).should == Intensity[0.25]
231
+ end
232
+
233
+ it 'works with a Intensity argument' do
234
+ (half_intensity / Intensity[0.5]).should == Intensity[1]
235
+ end
236
+
237
+ it 'returns a new intensity (Intensity is immutable)' do
238
+ original = half_intensity
239
+ modified = half_intensity / 2
240
+ original.should_not == modified
241
+ original.should == Intensity[0.5]
242
+ end
243
+ end
244
+
245
+ describe '#coerce' do
246
+ it 'allows a Intensity to be added to a Numeric' do
247
+ (0.25 + half_intensity).should == Intensity[0.75]
248
+ end
249
+
250
+ it 'allows a Intensity to be subtracted from a Numeric' do
251
+ (0.9 - half_intensity).should == Intensity[0.4]
252
+ end
253
+
254
+ it 'allows a Intensity to be multiplied to a Numeric' do
255
+ (0.5 * half_intensity).should == Intensity[0.25]
256
+ end
257
+
258
+ it 'allows a Numeric to be divided by a Intensity' do
259
+ (0.1 / half_intensity).should == Intensity[0.2]
260
+ end
261
+ end
262
+
263
+ end
264
+
265
+ describe MTK do
266
+
267
+ describe '#Intensity' do
268
+ it "acts like .from_s if the argument is a String" do
269
+ Intensity('ppp').should == Intensity.from_s('ppp')
270
+ end
271
+
272
+ it "acts like .from_s if the argument is a Symbol" do
273
+ Intensity(:ppp).should == Intensity.from_s('ppp')
274
+ end
275
+
276
+ it "acts like .[] if the argument is a Numeric" do
277
+ Intensity(0.5).should be_equal Intensity[0.5]
278
+ end
279
+
280
+ it "returns the argument if it's already a Intensity" do
281
+ Intensity(Intensity[1]).should be_equal Intensity[1]
282
+ end
283
+
284
+ it "raises an error for types it doesn't understand" do
285
+ lambda{ Intensity({:not => :compatible}) }.should raise_error
286
+ end
287
+ end
288
+
289
+ end
@@ -0,0 +1,265 @@
1
+ require 'spec_helper'
2
+
3
+ describe MTK::Interval do
4
+
5
+ let(:minor_second) { Interval[1] }
6
+
7
+
8
+ describe 'NAMES' do
9
+ it "is the list of base interval names available" do
10
+ Interval::NAMES.should =~ %w( P1 m2 M2 m3 M3 P4 TT P5 m6 M6 m7 M7 P8 )
11
+ end
12
+
13
+ it "is immutable" do
14
+ lambda{ Interval::NAMES << 'z' }.should raise_error
15
+ end
16
+ end
17
+
18
+
19
+ describe 'VALUES_BY_NAME' do
20
+ it 'maps names to values' do
21
+ Interval::VALUES_BY_NAME.each do |name,value|
22
+ Interval.from_s(name).value.should == value
23
+ end
24
+ end
25
+
26
+ it 'is immutable' do
27
+ lambda{ Interval::VALUES_BY_NAME << 'z' }.should raise_error
28
+ end
29
+ end
30
+
31
+
32
+ describe 'ALL_NAMES' do
33
+ it 'contains all NAMES' do
34
+ Interval::NAMES.each{|name| Interval::ALL_NAMES.should include name }
35
+ end
36
+ end
37
+
38
+
39
+ describe '.new' do
40
+ it "constructs a Interval with whatever value is given" do
41
+ float = 0.5
42
+ value = Interval.new(float).value
43
+ value.should be_equal float
44
+ end
45
+ end
46
+
47
+
48
+ describe '.[]' do
49
+ it "constructs and caches a interval from a Numeric" do
50
+ Interval[1].should be_equal Interval[1]
51
+ end
52
+
53
+ it "retains the new() method's ability to construct uncached objects" do
54
+ Interval.new(1).should_not be_equal Interval[1]
55
+ end
56
+
57
+ it "converts the value to floating point" do
58
+ value = Interval[Rational(1,2)].value
59
+ value.should be_a Float
60
+ value.should == 0.5
61
+ end
62
+
63
+ end
64
+
65
+
66
+ describe '.from_i' do
67
+ it "acts like .[]" do
68
+ Interval.from_i(0).value.should == 0.0
69
+ Interval.from_i(4).value.should == 4.0
70
+ end
71
+ end
72
+
73
+ describe '.from_f' do
74
+ it "acts like .[]" do
75
+ value = Interval.from_f(Rational(1,2)).value
76
+ value.should be_a Float
77
+ value.should == 0.5
78
+ end
79
+ end
80
+
81
+ describe '.from_s' do
82
+ it "converts any of the interval NAMES into a Interval with the value from the VALUES_BY_NAME mapping" do
83
+ for name in Interval::ALL_NAMES
84
+ Interval.from_s(name).value.should == Interval::VALUES_BY_NAME[name]
85
+ end
86
+ end
87
+ end
88
+
89
+ describe '.from_name' do
90
+ it "acts like .from_s" do
91
+ for name in Interval::NAMES
92
+ Interval.from_name(name).value.should == Interval::VALUES_BY_NAME[name]
93
+ end
94
+ end
95
+ end
96
+
97
+
98
+ describe '#value' do
99
+ it "is the value used to construct the Interval" do
100
+ Interval.new(1.111).value.should == 1.111
101
+ end
102
+ end
103
+
104
+
105
+ describe '#to_f' do
106
+ it "is the value as a floating point number" do
107
+ f = Interval.new(Rational(1,2)).to_f
108
+ f.should be_a Float
109
+ f.should == 0.5
110
+ end
111
+ end
112
+
113
+ describe '#to_i' do
114
+ it "is the value rounded to the nearest integer" do
115
+ i = Interval.new(0.5).to_i
116
+ i.should be_a Fixnum
117
+ i.should == 1
118
+ Interval.new(0.49).to_i.should == 0
119
+ end
120
+ end
121
+
122
+ describe '#to_s' do
123
+ it "should be value.to_s" do
124
+ for value in [1, Rational(1,2), 0.25]
125
+ Interval.new(value).to_s.should == value.to_s
126
+ end
127
+ end
128
+ end
129
+
130
+ describe '#==' do
131
+ it "compares two interval values for equality" do
132
+ Interval.new(Rational(1,2)).should == Interval.new(0.5)
133
+ Interval.new(4).should == Interval.new(4)
134
+ Interval.new(1.1).should_not == Interval.new(1)
135
+ end
136
+ end
137
+
138
+ describe "#<=>" do
139
+ it "orders intervals based on their underlying value" do
140
+ ( Interval.new(1) <=> Interval.new(1.1) ).should < 0
141
+ ( Interval.new(2) <=> Interval.new(1) ).should > 0
142
+ ( Interval.new(1.0) <=> Interval.new(1) ).should == 0
143
+ end
144
+ end
145
+
146
+
147
+
148
+ describe '#+' do
149
+ it 'adds #value to the Numeric argument' do
150
+ (minor_second + 0.25).should == Interval[1.25]
151
+ end
152
+
153
+ it 'works with an Interval argument' do
154
+ (minor_second + Interval[0.25]).should == Interval[1.25]
155
+ end
156
+
157
+ it 'returns a new interval (Interval is immutable)' do
158
+ original = minor_second
159
+ modified = minor_second + 0.25
160
+ original.should_not == modified
161
+ original.should == Interval[1]
162
+ end
163
+ end
164
+
165
+ describe '#-' do
166
+ it 'subtract the Numeric argument from #value' do
167
+ (minor_second - 0.25).should == Interval[0.75]
168
+ end
169
+
170
+ it 'works with a Interval argument' do
171
+ (minor_second - Interval[0.25]).should == Interval[0.75]
172
+ end
173
+
174
+ it 'returns a new interval (Interval is immutable)' do
175
+ original = minor_second
176
+ modified = minor_second - 0.25
177
+ original.should_not == modified
178
+ original.should == Interval[1]
179
+ end
180
+ end
181
+
182
+
183
+ describe '#*' do
184
+ it 'multiplies #value to the Numeric argument' do
185
+ (minor_second * 3).should == Interval[3]
186
+ end
187
+
188
+ it 'works with a Interval argument' do
189
+ (minor_second * Interval[3]).should == Interval[3]
190
+ end
191
+
192
+ it 'returns a new interval (Interval is immutable)' do
193
+ original = minor_second
194
+ modified = minor_second * 2
195
+ original.should_not == modified
196
+ original.should == Interval[1]
197
+ end
198
+ end
199
+
200
+ describe '#/' do
201
+ it 'divides #value by the Numeric argument' do
202
+ (minor_second / 2).should == Interval[0.5]
203
+ end
204
+
205
+ it 'works with a Interval argument' do
206
+ (minor_second / Interval[2]).should == Interval[0.5]
207
+ end
208
+
209
+ it 'returns a new interval (Interval is immutable)' do
210
+ original = minor_second
211
+ modified = minor_second / 2
212
+ original.should_not == modified
213
+ original.should == Interval[1]
214
+ end
215
+ end
216
+
217
+ describe '#coerce' do
218
+ it 'allows a Interval to be added to a Numeric' do
219
+ (0.25 + minor_second).should == Interval[1.25]
220
+ end
221
+
222
+ it 'allows a Interval to be subtracted from a Numeric' do
223
+ (3 - minor_second).should == Interval[2]
224
+ end
225
+
226
+ it 'allows a Interval to be multiplied to a Numeric' do
227
+ (3 * minor_second).should == Interval[3]
228
+ end
229
+
230
+ it 'allows a Numeric to be divided by a Interval' do
231
+ (4 / minor_second).should == Interval[4]
232
+ end
233
+ end
234
+
235
+ end
236
+
237
+ describe MTK do
238
+
239
+ describe '#Interval' do
240
+ it "acts like .from_s if the argument is a String" do
241
+ for name in Interval::ALL_NAMES
242
+ Interval(name).should == Interval.from_s(name)
243
+ end
244
+ end
245
+
246
+ it "acts like .from_s if the argument is a Symbol" do
247
+ for name in Interval::ALL_NAMES
248
+ Interval(name.to_sym).should == Interval.from_s(name)
249
+ end
250
+ end
251
+
252
+ it "acts like .[] if the argument is a Numeric" do
253
+ Interval(0.5).should be_equal Interval[0.5]
254
+ end
255
+
256
+ it "returns the argument if it's already a Interval" do
257
+ Interval(Interval[1]).should be_equal Interval[1]
258
+ end
259
+
260
+ it "raises an error for types it doesn't understand" do
261
+ lambda{ Interval({:not => :compatible}) }.should raise_error
262
+ end
263
+ end
264
+
265
+ end