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.
- data/.yardopts +3 -2
- data/DEVELOPMENT_NOTES.md +114 -0
- data/INTRO.md +64 -8
- data/LICENSE.txt +1 -1
- data/README.md +31 -102
- data/Rakefile +56 -18
- data/bin/mtk +215 -0
- data/examples/crescendo.rb +5 -5
- data/examples/drum_pattern1.rb +23 -0
- data/examples/dynamic_pattern.rb +8 -11
- data/examples/gets_and_play.rb +26 -0
- data/examples/notation.rb +22 -0
- data/examples/play_midi.rb +8 -10
- data/examples/random_tone_row.rb +2 -2
- data/examples/syntax_to_midi.rb +28 -0
- data/examples/test_output.rb +8 -0
- data/examples/tone_row_melody.rb +6 -6
- data/lib/mtk.rb +52 -40
- data/lib/mtk/chord.rb +55 -0
- data/lib/mtk/constants/durations.rb +57 -0
- data/lib/mtk/constants/intensities.rb +61 -0
- data/lib/mtk/constants/intervals.rb +73 -0
- data/lib/mtk/constants/pitch_classes.rb +29 -0
- data/lib/mtk/constants/pitches.rb +52 -0
- data/lib/mtk/duration.rb +211 -0
- data/lib/mtk/events/event.rb +119 -0
- data/lib/mtk/events/note.rb +112 -0
- data/lib/mtk/events/parameter.rb +54 -0
- data/lib/mtk/helpers/collection.rb +164 -0
- data/lib/mtk/helpers/convert.rb +36 -0
- data/lib/mtk/helpers/lilypond.rb +162 -0
- data/lib/mtk/helpers/output_selector.rb +67 -0
- data/lib/mtk/helpers/pitch_collection.rb +23 -0
- data/lib/mtk/helpers/pseudo_constants.rb +26 -0
- data/lib/mtk/intensity.rb +156 -0
- data/lib/mtk/interval.rb +155 -0
- data/lib/mtk/lang/mtk_grammar.citrus +190 -13
- data/lib/mtk/lang/parser.rb +29 -0
- data/lib/mtk/melody.rb +94 -0
- data/lib/mtk/midi/dls_synth_device.rb +144 -0
- data/lib/mtk/midi/dls_synth_output.rb +62 -0
- data/lib/mtk/midi/file.rb +67 -32
- data/lib/mtk/midi/input.rb +97 -0
- data/lib/mtk/midi/jsound_input.rb +36 -17
- data/lib/mtk/midi/jsound_output.rb +48 -46
- data/lib/mtk/midi/output.rb +195 -0
- data/lib/mtk/midi/unimidi_input.rb +117 -0
- data/lib/mtk/midi/unimidi_output.rb +121 -0
- data/lib/mtk/{_numeric_extensions.rb → numeric_extensions.rb} +12 -0
- data/lib/mtk/patterns/chain.rb +49 -0
- data/lib/mtk/{pattern → patterns}/choice.rb +14 -8
- data/lib/mtk/patterns/cycle.rb +18 -0
- data/lib/mtk/patterns/for_each.rb +71 -0
- data/lib/mtk/patterns/function.rb +39 -0
- data/lib/mtk/{pattern → patterns}/lines.rb +11 -17
- data/lib/mtk/{pattern → patterns}/palindrome.rb +11 -8
- data/lib/mtk/patterns/pattern.rb +171 -0
- data/lib/mtk/patterns/sequence.rb +20 -0
- data/lib/mtk/pitch.rb +7 -6
- data/lib/mtk/pitch_class.rb +124 -46
- data/lib/mtk/pitch_class_set.rb +58 -35
- data/lib/mtk/sequencers/event_builder.rb +131 -0
- data/lib/mtk/sequencers/legato_sequencer.rb +24 -0
- data/lib/mtk/sequencers/rhythmic_sequencer.rb +28 -0
- data/lib/mtk/{sequencer/abstract_sequencer.rb → sequencers/sequencer.rb} +37 -11
- data/lib/mtk/{sequencer → sequencers}/step_sequencer.rb +4 -4
- data/lib/mtk/timeline.rb +39 -22
- data/lib/mtk/variable.rb +32 -0
- data/spec/mtk/chord_spec.rb +83 -0
- data/spec/mtk/{_constants → constants}/durations_spec.rb +12 -41
- data/spec/mtk/{_constants → constants}/intensities_spec.rb +13 -37
- data/spec/mtk/{_constants → constants}/intervals_spec.rb +14 -32
- data/spec/mtk/{_constants → constants}/pitch_classes_spec.rb +8 -4
- data/spec/mtk/{_constants → constants}/pitches_spec.rb +5 -1
- data/spec/mtk/duration_spec.rb +372 -0
- data/spec/mtk/events/event_spec.rb +234 -0
- data/spec/mtk/events/note_spec.rb +174 -0
- data/spec/mtk/events/parameter_spec.rb +220 -0
- data/spec/mtk/{helper → helpers}/collection_spec.rb +86 -3
- data/spec/mtk/{helper → helpers}/pseudo_constants_spec.rb +2 -2
- data/spec/mtk/intensity_spec.rb +289 -0
- data/spec/mtk/interval_spec.rb +265 -0
- data/spec/mtk/lang/parser_spec.rb +597 -0
- data/spec/mtk/melody_spec.rb +223 -0
- data/spec/mtk/midi/file_spec.rb +16 -16
- data/spec/mtk/midi/jsound_input_spec.rb +11 -0
- data/spec/mtk/midi/jsound_output_spec.rb +11 -0
- data/spec/mtk/midi/output_spec.rb +102 -0
- data/spec/mtk/midi/unimidi_input_spec.rb +11 -0
- data/spec/mtk/midi/unimidi_output_spec.rb +11 -0
- data/spec/mtk/{_numeric_extensions_spec.rb → numeric_extensions_spec.rb} +1 -0
- data/spec/mtk/patterns/chain_spec.rb +110 -0
- data/spec/mtk/{pattern → patterns}/choice_spec.rb +20 -30
- data/spec/mtk/{pattern → patterns}/cycle_spec.rb +25 -35
- data/spec/mtk/patterns/for_each_spec.rb +136 -0
- data/spec/mtk/{pattern → patterns}/function_spec.rb +17 -30
- data/spec/mtk/{pattern → patterns}/lines_spec.rb +11 -27
- data/spec/mtk/{pattern → patterns}/palindrome_spec.rb +13 -29
- data/spec/mtk/patterns/pattern_spec.rb +132 -0
- data/spec/mtk/patterns/sequence_spec.rb +203 -0
- data/spec/mtk/pitch_class_set_spec.rb +23 -21
- data/spec/mtk/pitch_class_spec.rb +151 -39
- data/spec/mtk/pitch_spec.rb +22 -1
- data/spec/mtk/sequencers/event_builder_spec.rb +245 -0
- data/spec/mtk/sequencers/legato_sequencer_spec.rb +45 -0
- data/spec/mtk/sequencers/rhythmic_sequencer_spec.rb +84 -0
- data/spec/mtk/sequencers/sequencer_spec.rb +215 -0
- data/spec/mtk/{sequencer → sequencers}/step_sequencer_spec.rb +35 -13
- data/spec/mtk/timeline_spec.rb +109 -16
- data/spec/mtk/variable_spec.rb +52 -0
- data/spec/spec_coverage.rb +2 -0
- data/spec/spec_helper.rb +3 -0
- metadata +188 -91
- data/lib/mtk/_constants/durations.rb +0 -80
- data/lib/mtk/_constants/intensities.rb +0 -81
- data/lib/mtk/_constants/intervals.rb +0 -85
- data/lib/mtk/_constants/pitch_classes.rb +0 -35
- data/lib/mtk/_constants/pitches.rb +0 -49
- data/lib/mtk/event.rb +0 -70
- data/lib/mtk/helper/collection.rb +0 -114
- data/lib/mtk/helper/event_builder.rb +0 -85
- data/lib/mtk/helper/pseudo_constants.rb +0 -26
- data/lib/mtk/lang/grammar.rb +0 -17
- data/lib/mtk/note.rb +0 -63
- data/lib/mtk/pattern/abstract_pattern.rb +0 -132
- data/lib/mtk/pattern/cycle.rb +0 -51
- data/lib/mtk/pattern/enumerator.rb +0 -26
- data/lib/mtk/pattern/function.rb +0 -46
- data/lib/mtk/pattern/sequence.rb +0 -30
- data/lib/mtk/pitch_set.rb +0 -84
- data/lib/mtk/sequencer/rhythmic_sequencer.rb +0 -29
- data/lib/mtk/transform/invertible.rb +0 -15
- data/lib/mtk/transform/mappable.rb +0 -18
- data/lib/mtk/transform/set_theory_operations.rb +0 -34
- data/lib/mtk/transform/transposable.rb +0 -14
- data/spec/mtk/event_spec.rb +0 -139
- data/spec/mtk/helper/event_builder_spec.rb +0 -92
- data/spec/mtk/lang/grammar_spec.rb +0 -100
- data/spec/mtk/note_spec.rb +0 -115
- data/spec/mtk/pattern/abstract_pattern_spec.rb +0 -45
- data/spec/mtk/pattern/note_cycle_spec.rb.bak +0 -116
- data/spec/mtk/pattern/pitch_cycle_spec.rb.bak +0 -47
- data/spec/mtk/pattern/pitch_sequence_spec.rb.bak +0 -37
- data/spec/mtk/pattern/sequence_spec.rb +0 -151
- data/spec/mtk/pitch_set_spec.rb +0 -198
- data/spec/mtk/sequencer/abstract_sequencer_spec.rb +0 -159
- data/spec/mtk/sequencer/rhythmic_sequencer_spec.rb +0 -49
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe MTK::Intervals do
|
3
|
+
describe MTK::Constants::Intervals do
|
4
4
|
|
5
5
|
describe 'P1' do
|
6
6
|
it 'is 0 semitones' do
|
7
|
-
P1.should == 0
|
7
|
+
P1.should == Interval[0]
|
8
8
|
end
|
9
9
|
it 'is available via a module property and via mixin' do
|
10
10
|
Intervals::P1.should == P1
|
@@ -13,7 +13,7 @@ describe MTK::Intervals do
|
|
13
13
|
|
14
14
|
describe 'm2' do
|
15
15
|
it 'is 1 semitone' do
|
16
|
-
m2.should == 1
|
16
|
+
m2.should == Interval[1]
|
17
17
|
end
|
18
18
|
it 'is available via a module property and via mixin' do
|
19
19
|
Intervals::P1.should == P1
|
@@ -22,7 +22,7 @@ describe MTK::Intervals do
|
|
22
22
|
|
23
23
|
describe 'M2' do
|
24
24
|
it 'is 2 semitones' do
|
25
|
-
M2.should == 2
|
25
|
+
M2.should == Interval[2]
|
26
26
|
end
|
27
27
|
it 'is available via a module property and via mixin' do
|
28
28
|
Intervals::P1.should == P1
|
@@ -31,7 +31,7 @@ describe MTK::Intervals do
|
|
31
31
|
|
32
32
|
describe 'm3' do
|
33
33
|
it 'is 3 semitones' do
|
34
|
-
m3.should == 3
|
34
|
+
m3.should == Interval[3]
|
35
35
|
end
|
36
36
|
it 'is available via a module property and via mixin' do
|
37
37
|
Intervals::P1.should == P1
|
@@ -40,7 +40,7 @@ describe MTK::Intervals do
|
|
40
40
|
|
41
41
|
describe 'M3' do
|
42
42
|
it 'is 4 semitones' do
|
43
|
-
M3.should == 4
|
43
|
+
M3.should == Interval[4]
|
44
44
|
end
|
45
45
|
it 'is available via a module property and via mixin' do
|
46
46
|
Intervals::P1.should == P1
|
@@ -49,7 +49,7 @@ describe MTK::Intervals do
|
|
49
49
|
|
50
50
|
describe 'P4' do
|
51
51
|
it 'is 5 semitones' do
|
52
|
-
P4.should == 5
|
52
|
+
P4.should == Interval[5]
|
53
53
|
end
|
54
54
|
it 'is available via a module property and via mixin' do
|
55
55
|
Intervals::P1.should == P1
|
@@ -58,7 +58,7 @@ describe MTK::Intervals do
|
|
58
58
|
|
59
59
|
describe 'TT' do
|
60
60
|
it 'is 6 semitones' do
|
61
|
-
TT.should == 6
|
61
|
+
TT.should == Interval[6]
|
62
62
|
end
|
63
63
|
it 'is available via a module property and via mixin' do
|
64
64
|
Intervals::P1.should == P1
|
@@ -67,7 +67,7 @@ describe MTK::Intervals do
|
|
67
67
|
|
68
68
|
describe 'P5' do
|
69
69
|
it 'is 7 semitones' do
|
70
|
-
P5.should == 7
|
70
|
+
P5.should == Interval[7]
|
71
71
|
end
|
72
72
|
it 'is available via a module property and via mixin' do
|
73
73
|
Intervals::P1.should == P1
|
@@ -76,7 +76,7 @@ describe MTK::Intervals do
|
|
76
76
|
|
77
77
|
describe 'm6' do
|
78
78
|
it 'is 8 semitones' do
|
79
|
-
m6.should == 8
|
79
|
+
m6.should == Interval[8]
|
80
80
|
end
|
81
81
|
it 'is available via a module property and via mixin' do
|
82
82
|
Intervals::P1.should == P1
|
@@ -85,7 +85,7 @@ describe MTK::Intervals do
|
|
85
85
|
|
86
86
|
describe 'M6' do
|
87
87
|
it 'is 9 semitones' do
|
88
|
-
M6.should == 9
|
88
|
+
M6.should == Interval[9]
|
89
89
|
end
|
90
90
|
it 'is available via a module property and via mixin' do
|
91
91
|
Intervals::P1.should == P1
|
@@ -94,7 +94,7 @@ describe MTK::Intervals do
|
|
94
94
|
|
95
95
|
describe 'm7' do
|
96
96
|
it 'is 10 semitones' do
|
97
|
-
m7.should == 10
|
97
|
+
m7.should == Interval[10]
|
98
98
|
end
|
99
99
|
it 'is available via a module property and via mixin' do
|
100
100
|
Intervals::P1.should == P1
|
@@ -103,7 +103,7 @@ describe MTK::Intervals do
|
|
103
103
|
|
104
104
|
describe 'M7' do
|
105
105
|
it 'is 11 semitones' do
|
106
|
-
M7.should == 11
|
106
|
+
M7.should == Interval[11]
|
107
107
|
end
|
108
108
|
it 'is available via a module property and via mixin' do
|
109
109
|
Intervals::P1.should == P1
|
@@ -112,7 +112,7 @@ describe MTK::Intervals do
|
|
112
112
|
|
113
113
|
describe 'P8' do
|
114
114
|
it 'is 12 semitones' do
|
115
|
-
P8.should == 12
|
115
|
+
P8.should == Interval[12]
|
116
116
|
end
|
117
117
|
it 'is available via a module property and via mixin' do
|
118
118
|
Intervals::P1.should == P1
|
@@ -140,22 +140,4 @@ describe MTK::Intervals do
|
|
140
140
|
end
|
141
141
|
end
|
142
142
|
|
143
|
-
describe ".[]" do
|
144
|
-
it "looks up the constant by name" do
|
145
|
-
Intervals['P1'].should == P1
|
146
|
-
Intervals['m2'].should == m2
|
147
|
-
Intervals['M2'].should == M2
|
148
|
-
Intervals['m3'].should == m3
|
149
|
-
Intervals['M3'].should == M3
|
150
|
-
Intervals['P4'].should == P4
|
151
|
-
Intervals['TT'].should == TT
|
152
|
-
Intervals['P5'].should == P5
|
153
|
-
Intervals['m6'].should == m6
|
154
|
-
Intervals['M6'].should == M6
|
155
|
-
Intervals['m7'].should == m7
|
156
|
-
Intervals['M7'].should == M7
|
157
|
-
Intervals['P8'].should == P8
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
143
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe MTK::PitchClasses do
|
3
|
+
describe MTK::Constants::PitchClasses do
|
4
4
|
let(:cases) {
|
5
5
|
[
|
6
6
|
[PitchClass['C'], 'C', 0],
|
@@ -20,9 +20,9 @@ describe MTK::PitchClasses do
|
|
20
20
|
|
21
21
|
it "defines constants for the 12 pitch classes in the twelve-tone octave" do
|
22
22
|
cases.length.should == 12
|
23
|
-
cases.each do |const, name,
|
23
|
+
cases.each do |const, name, value|
|
24
24
|
const.name.should == name
|
25
|
-
const.
|
25
|
+
const.value.should == value
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
@@ -49,10 +49,14 @@ describe MTK::PitchClasses do
|
|
49
49
|
end
|
50
50
|
|
51
51
|
describe ".[]" do
|
52
|
-
it "acts like
|
52
|
+
it "acts like PitchClass.[]" do
|
53
53
|
for name in PitchClasses::PITCH_CLASS_NAMES
|
54
54
|
PitchClasses[name].should == PitchClass[name]
|
55
55
|
end
|
56
56
|
end
|
57
|
+
|
58
|
+
it "returns nil for arguments it doesn't understand" do
|
59
|
+
PitchClasses[:invalid].should be_nil
|
60
|
+
end
|
57
61
|
end
|
58
62
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe MTK::Pitches do
|
3
|
+
describe MTK::Constants::Pitches do
|
4
4
|
|
5
5
|
it "defines constants for the 128 notes in MIDI" do
|
6
6
|
Pitches.constants.length.should == 130 # there's also the PITCHES and PITCH_NAMES constants
|
@@ -47,6 +47,10 @@ describe MTK::Pitches do
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
50
|
+
|
51
|
+
it "returns nil for arguments it doesn't understand" do
|
52
|
+
Pitches[:invalid].should be_nil
|
53
|
+
end
|
50
54
|
end
|
51
55
|
|
52
56
|
end
|
@@ -0,0 +1,372 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MTK::Duration do
|
4
|
+
|
5
|
+
let(:one_beat) { Duration[1] }
|
6
|
+
let(:two_beats) { Duration[2] }
|
7
|
+
|
8
|
+
|
9
|
+
describe 'NAMES' do
|
10
|
+
it "is the list of base duration names available" do
|
11
|
+
Duration::NAMES.should =~ %w( w h q i s r x )
|
12
|
+
end
|
13
|
+
|
14
|
+
it "is immutable" do
|
15
|
+
lambda{ Duration::NAMES << 'z' }.should raise_error
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
describe 'VALUES_BY_NAME' do
|
21
|
+
it 'maps names to values' do
|
22
|
+
Duration::VALUES_BY_NAME.each do |name,value|
|
23
|
+
Duration.from_s(name).value.should == value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'is immutable' do
|
28
|
+
lambda{ Duration::VALUES_BY_NAME << 'z' }.should raise_error
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
describe '.new' do
|
34
|
+
it "constructs a Duration with whatever value is given" do
|
35
|
+
float = 0.5
|
36
|
+
value = Duration.new(float).value
|
37
|
+
value.should be_equal float
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
describe '.[]' do
|
43
|
+
it "constructs and caches a duration from a Numeric" do
|
44
|
+
Duration[1].should be_equal Duration[1]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "retains the new() method's ability to construct uncached objects" do
|
48
|
+
Duration.new(1).should_not be_equal Duration[1]
|
49
|
+
end
|
50
|
+
|
51
|
+
it "uses the argument as the value, if the argument is a Fixnum" do
|
52
|
+
value = Duration[4].value
|
53
|
+
value.should be_equal 4
|
54
|
+
end
|
55
|
+
|
56
|
+
it "converts other Numerics to Rational values" do
|
57
|
+
value = Duration[0.5].value
|
58
|
+
value.should be_a Rational
|
59
|
+
value.should == Rational(1,2)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
describe '.from_i' do
|
65
|
+
it "uses the argument as the value" do
|
66
|
+
value = Duration.from_i(4).value
|
67
|
+
value.should be_equal 4
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '.from_f' do
|
72
|
+
it "converts Floats to Rational" do
|
73
|
+
value = Duration.from_f(0.5).value
|
74
|
+
value.should be_a Rational
|
75
|
+
value.should == Rational(1,2)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '.from_s' do
|
80
|
+
it "converts any of the duration NAMES into a Duration with the value from the VALUES_BY_NAME mapping" do
|
81
|
+
for name in Duration::NAMES
|
82
|
+
Duration.from_s(name).value.should == Duration::VALUES_BY_NAME[name]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it "converts any of the duration names prefixed with a '-' to the negative value" do
|
87
|
+
for name in Duration::NAMES
|
88
|
+
Duration.from_s("-#{name}").value.should == -1 * Duration::VALUES_BY_NAME[name]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it "converts any of the duration names suffixed a 't' to 2/3 of the value" do
|
93
|
+
for name in Duration::NAMES
|
94
|
+
Duration.from_s("#{name}t").value.should == Rational(2,3) * Duration::VALUES_BY_NAME[name]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
it "converts any of the duration names suffixed a '.' to 3/2 of the value" do
|
99
|
+
for name in Duration::NAMES
|
100
|
+
Duration.from_s("#{name}.").value.should == Rational(3,2) * Duration::VALUES_BY_NAME[name]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
it "converts suffix combinations of 't' and '.' (multiplying by 2/3 and 3/2 for each)" do
|
105
|
+
trip = Rational(2,3)
|
106
|
+
dot = Rational(3,2)
|
107
|
+
for name in Duration::NAMES
|
108
|
+
for suffix,multiplier in {'tt' => trip*trip, 't.' => trip*dot, '..' => dot*dot, 't..t.' => trip*dot*dot*trip*dot}
|
109
|
+
Duration.from_s("#{name}#{suffix}").value.should == multiplier * Duration::VALUES_BY_NAME[name]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
it "parses durations with integer multipliers" do
|
115
|
+
Durations::DURATION_NAMES.each_with_index do |duration, index|
|
116
|
+
multiplier = index+5
|
117
|
+
Duration.from_s("#{multiplier}#{duration}").should == multiplier * Duration(duration)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
it "parses durations with float multipliers" do
|
122
|
+
Durations::DURATION_NAMES.each_with_index do |duration, index|
|
123
|
+
multiplier = (index+1)*1.123
|
124
|
+
Duration.from_s("#{multiplier}#{duration}").should == multiplier * Duration(duration)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it "parses durations with float multipliers" do
|
129
|
+
Durations::DURATION_NAMES.each_with_index do |duration, index|
|
130
|
+
multiplier = Rational(index+1, index+2)
|
131
|
+
Duration.from_s("#{multiplier}#{duration}").should == multiplier * Duration(duration)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
it "parses combinations of all modifiers" do
|
136
|
+
Duration.from_s("-4/5qt.").value.should == -4.0/5 * 1 * 2/3.0 * 3/2.0
|
137
|
+
Duration.from_s("-11.234h.tt").value.should == 2 * 3/2.0 * 2/3.0 * 2/3.0 * -11.234
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '.from_name' do
|
142
|
+
it "acts like .from_s" do
|
143
|
+
for name in Duration::NAMES
|
144
|
+
Duration.from_name(name).value.should == Duration::VALUES_BY_NAME[name]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
describe '#value' do
|
151
|
+
it "is the value used to construct the Duration" do
|
152
|
+
Duration.new(1.111).value.should == 1.111
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
describe '#length' do
|
158
|
+
it 'is the value for positive values' do
|
159
|
+
Duration.new(4).length.should == 4
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'is the absolute value for negative values' do
|
163
|
+
Duration.new(-4).length.should == 4
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
describe '#rest?' do
|
169
|
+
it 'is false for positive values' do
|
170
|
+
Duration.new(4).rest?.should be_false
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'is true for negative values' do
|
174
|
+
Duration.new(-4).rest?.should be_true
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
describe '#to_f' do
|
180
|
+
it "is the value as a floating point number" do
|
181
|
+
f = Duration.new(Rational(1,2)).to_f
|
182
|
+
f.should be_a Float
|
183
|
+
f.should == 0.5
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe '#to_i' do
|
188
|
+
it "is the value rounded to the nearest integer" do
|
189
|
+
i = Duration.new(0.5).to_i
|
190
|
+
i.should be_a Fixnum
|
191
|
+
i.should == 1
|
192
|
+
Duration.new(0.49).to_i.should == 0
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe '#to_s' do
|
197
|
+
it "is value.to_s suffixed with 'beats' for beat values > 1" do
|
198
|
+
for value in [2, Rational(3,2), 3.25]
|
199
|
+
Duration.new(value).to_s.should == value.to_s + ' beats'
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
it "is value.to_s suffixed with 'beats' for positive, non-zero beat values < 1" do
|
204
|
+
for value in [Rational(1,2), 0.25]
|
205
|
+
Duration.new(value).to_s.should == value.to_s + ' beat'
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
it "is value.to_s suffixed with 'beats' for a value of 0" do
|
210
|
+
for value in [0, 0.0, Rational(0,2)]
|
211
|
+
Duration.new(value).to_s.should == value.to_s + ' beats'
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
it "is value.to_s suffixed with 'beats' for beat values < -1" do
|
216
|
+
for value in [-2, -Rational(3,2), -3.25]
|
217
|
+
Duration.new(value).to_s.should == value.to_s + ' beats'
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
it "is value.to_s suffixed with 'beat' for negative, non-zero beat values > -1" do
|
222
|
+
for value in [-Rational(1,2), -0.25]
|
223
|
+
Duration.new(value).to_s.should == value.to_s + ' beat'
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
it "rounds to 2 decimal places when value.to_s is overly long" do
|
228
|
+
Duration.new(Math.sqrt 2).to_s.should == '1.41 beats'
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
describe '#inspect' do
|
233
|
+
it 'is "#<MTK::Duration:{object_id} @value={value}>"' do
|
234
|
+
for value in [0, 60, 60.5, 127]
|
235
|
+
duration = Duration.new(value)
|
236
|
+
duration.inspect.should == "#<MTK::Duration:#{duration.object_id} @value=#{value}>"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
describe '#==' do
|
242
|
+
it "compares two duration values for equality" do
|
243
|
+
Duration.new(Rational(1,2)).should == Duration.new(0.5)
|
244
|
+
Duration.new(4).should == Duration.new(4)
|
245
|
+
Duration.new(1.1).should_not == Duration.new(1)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
describe "#<=>" do
|
250
|
+
it "orders durations based on their underlying value" do
|
251
|
+
( Duration.new(1) <=> Duration.new(1.1) ).should < 0
|
252
|
+
( Duration.new(2) <=> Duration.new(1) ).should > 0
|
253
|
+
( Duration.new(1.0) <=> Duration.new(1) ).should == 0
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
|
259
|
+
describe '#+' do
|
260
|
+
it 'adds #value to the Numeric argument' do
|
261
|
+
(one_beat + 1.5).should == Duration[2.5]
|
262
|
+
end
|
263
|
+
|
264
|
+
it 'works with a Duration argument' do
|
265
|
+
(one_beat + Duration[1.5]).should == Duration[2.5]
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'returns a new duration (Duration is immutable)' do
|
269
|
+
original = one_beat
|
270
|
+
modified = one_beat + 2
|
271
|
+
original.should_not == modified
|
272
|
+
original.should == Duration[1]
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
describe '#-' do
|
277
|
+
it 'subtract the Numeric argument from #value' do
|
278
|
+
(one_beat - 0.5).should == Duration[0.5]
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'works with a Duration argument' do
|
282
|
+
(one_beat - Duration[0.5]).should == Duration[0.5]
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'returns a new duration (Duration is immutable)' do
|
286
|
+
original = one_beat
|
287
|
+
modified = one_beat - 0.5
|
288
|
+
original.should_not == modified
|
289
|
+
original.should == Duration[1]
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
|
294
|
+
describe '#*' do
|
295
|
+
it 'multiplies #value to the Numeric argument' do
|
296
|
+
(two_beats * 3).should == Duration[6]
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'works with a Duration argument' do
|
300
|
+
(two_beats * Duration[3]).should == Duration[6]
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'returns a new duration (Duration is immutable)' do
|
304
|
+
original = one_beat
|
305
|
+
modified = one_beat * 2
|
306
|
+
original.should_not == modified
|
307
|
+
original.should == Duration[1]
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
describe '#/' do
|
312
|
+
it 'divides #value by the Numeric argument' do
|
313
|
+
(two_beats / 4.0).should == Duration[0.5]
|
314
|
+
end
|
315
|
+
|
316
|
+
it 'works with a Duration argument' do
|
317
|
+
(two_beats / Duration[4]).should == Duration[0.5]
|
318
|
+
end
|
319
|
+
|
320
|
+
it 'returns a new duration (Duration is immutable)' do
|
321
|
+
original = one_beat
|
322
|
+
modified = one_beat / 2
|
323
|
+
original.should_not == modified
|
324
|
+
original.should == Duration[1]
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
describe '#coerce' do
|
329
|
+
it 'allows a Duration to be added to a Numeric' do
|
330
|
+
(2 + one_beat).should == Duration[3]
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'allows a Duration to be subtracted from a Numeric' do
|
334
|
+
(7 - two_beats).should == Duration[5]
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'allows a Duration to be multiplied to a Numeric' do
|
338
|
+
(3 * two_beats).should == Duration[6]
|
339
|
+
end
|
340
|
+
|
341
|
+
it 'allows a Numeric to be divided by a Duration' do
|
342
|
+
(8.0 / two_beats).should == Duration[4]
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
end
|
347
|
+
|
348
|
+
describe MTK do
|
349
|
+
|
350
|
+
describe '#Duration' do
|
351
|
+
it "acts like .from_s if the argument is a String" do
|
352
|
+
Duration('w').should == Duration.from_s('w')
|
353
|
+
end
|
354
|
+
|
355
|
+
it "acts like .from_s if the argument is a Symbol" do
|
356
|
+
Duration(:w).should == Duration.from_s('w')
|
357
|
+
end
|
358
|
+
|
359
|
+
it "acts like .[] if the argument is a Numeric" do
|
360
|
+
Duration(3.5).should be_equal Duration[Rational(7,2)]
|
361
|
+
end
|
362
|
+
|
363
|
+
it "returns the argument if it's already a Duration" do
|
364
|
+
Duration(Duration[1]).should be_equal Duration[1]
|
365
|
+
end
|
366
|
+
|
367
|
+
it "raises an error for types it doesn't understand" do
|
368
|
+
lambda{ Duration({:not => :compatible}) }.should raise_error
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
end
|