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
@@ -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