mtk 0.0.3.2 → 0.0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +2 -2
- data/DEVELOPMENT_NOTES.md +20 -0
- data/README.md +9 -3
- data/Rakefile +47 -13
- data/bin/mtk +55 -20
- data/examples/crescendo.rb +4 -4
- data/examples/{drum_pattern1.rb → drum_pattern.rb} +8 -8
- data/examples/dynamic_pattern.rb +5 -5
- data/examples/gets_and_play.rb +3 -2
- data/examples/notation.rb +3 -3
- data/examples/play_midi.rb +4 -4
- data/examples/print_midi.rb +2 -2
- data/examples/random_tone_row.rb +3 -3
- data/examples/syntax_to_midi.rb +2 -2
- data/examples/test_output.rb +4 -5
- data/examples/tone_row_melody.rb +7 -5
- data/lib/mtk/core/duration.rb +213 -0
- data/lib/mtk/core/intensity.rb +158 -0
- data/lib/mtk/core/interval.rb +157 -0
- data/lib/mtk/core/pitch.rb +154 -0
- data/lib/mtk/core/pitch_class.rb +194 -0
- data/lib/mtk/events/event.rb +4 -4
- data/lib/mtk/events/note.rb +12 -12
- data/lib/mtk/events/timeline.rb +232 -0
- data/lib/mtk/groups/chord.rb +56 -0
- data/lib/mtk/{helpers → groups}/collection.rb +33 -1
- data/lib/mtk/groups/melody.rb +96 -0
- data/lib/mtk/groups/pitch_class_set.rb +163 -0
- data/lib/mtk/{helpers → groups}/pitch_collection.rb +1 -1
- data/lib/mtk/{midi → io}/dls_synth_device.rb +3 -1
- data/lib/mtk/{midi → io}/dls_synth_output.rb +10 -10
- data/lib/mtk/{midi → io}/jsound_input.rb +2 -2
- data/lib/mtk/{midi → io}/jsound_output.rb +9 -9
- data/lib/mtk/{midi/file.rb → io/midi_file.rb} +13 -13
- data/lib/mtk/{midi/input.rb → io/midi_input.rb} +4 -4
- data/lib/mtk/{midi/output.rb → io/midi_output.rb} +8 -8
- data/lib/mtk/{helpers/lilypond.rb → io/notation.rb} +5 -5
- data/lib/mtk/{midi → io}/unimidi_input.rb +2 -2
- data/lib/mtk/{midi → io}/unimidi_output.rb +14 -9
- data/lib/mtk/{constants → lang}/durations.rb +11 -11
- data/lib/mtk/{constants → lang}/intensities.rb +11 -11
- data/lib/mtk/{constants → lang}/intervals.rb +17 -17
- data/lib/mtk/lang/mtk_grammar.citrus +9 -9
- data/lib/mtk/{constants → lang}/pitch_classes.rb +5 -5
- data/lib/mtk/{constants → lang}/pitches.rb +7 -7
- data/lib/mtk/{helpers → lang}/pseudo_constants.rb +1 -1
- data/lib/mtk/{variable.rb → lang/variable.rb} +1 -1
- data/lib/mtk/numeric_extensions.rb +40 -47
- data/lib/mtk/patterns/for_each.rb +1 -1
- data/lib/mtk/patterns/pattern.rb +3 -3
- data/lib/mtk/sequencers/event_builder.rb +16 -15
- data/lib/mtk/sequencers/legato_sequencer.rb +1 -1
- data/lib/mtk/sequencers/rhythmic_sequencer.rb +1 -1
- data/lib/mtk/sequencers/sequencer.rb +8 -8
- data/lib/mtk/sequencers/step_sequencer.rb +2 -2
- data/lib/mtk.rb +33 -39
- data/spec/mtk/{duration_spec.rb → core/duration_spec.rb} +3 -3
- data/spec/mtk/{intensity_spec.rb → core/intensity_spec.rb} +3 -3
- data/spec/mtk/{interval_spec.rb → core/interval_spec.rb} +1 -1
- data/spec/mtk/{pitch_class_spec.rb → core/pitch_class_spec.rb} +1 -1
- data/spec/mtk/{pitch_spec.rb → core/pitch_spec.rb} +8 -8
- data/spec/mtk/events/event_spec.rb +4 -4
- data/spec/mtk/events/note_spec.rb +8 -8
- data/spec/mtk/{timeline_spec.rb → events/timeline_spec.rb} +47 -47
- data/spec/mtk/{chord_spec.rb → groups/chord_spec.rb} +18 -16
- data/spec/mtk/{helpers → groups}/collection_spec.rb +3 -3
- data/spec/mtk/{melody_spec.rb → groups/melody_spec.rb} +36 -34
- data/spec/mtk/{pitch_class_set_spec.rb → groups/pitch_class_set_spec.rb} +57 -55
- data/spec/mtk/{midi/file_spec.rb → io/midi_file_spec.rb} +17 -17
- data/spec/mtk/{midi/output_spec.rb → io/midi_output_spec.rb} +6 -6
- data/spec/mtk/{constants → lang}/durations_spec.rb +1 -1
- data/spec/mtk/{constants → lang}/intensities_spec.rb +1 -1
- data/spec/mtk/{constants → lang}/intervals_spec.rb +1 -1
- data/spec/mtk/lang/parser_spec.rb +12 -6
- data/spec/mtk/{constants → lang}/pitch_classes_spec.rb +1 -1
- data/spec/mtk/{constants → lang}/pitches_spec.rb +1 -1
- data/spec/mtk/{helpers → lang}/pseudo_constants_spec.rb +2 -2
- data/spec/mtk/{variable_spec.rb → lang/variable_spec.rb} +4 -4
- data/spec/mtk/numeric_extensions_spec.rb +35 -55
- data/spec/mtk/patterns/for_each_spec.rb +1 -1
- data/spec/mtk/patterns/sequence_spec.rb +1 -1
- data/spec/mtk/sequencers/legato_sequencer_spec.rb +2 -2
- data/spec/mtk/sequencers/rhythmic_sequencer_spec.rb +4 -4
- data/spec/mtk/sequencers/step_sequencer_spec.rb +5 -5
- data/spec/spec_helper.rb +7 -6
- metadata +75 -61
- data/ext/mkrf_conf.rb +0 -25
- data/lib/mtk/chord.rb +0 -55
- data/lib/mtk/duration.rb +0 -211
- data/lib/mtk/helpers/convert.rb +0 -36
- data/lib/mtk/helpers/output_selector.rb +0 -67
- data/lib/mtk/intensity.rb +0 -156
- data/lib/mtk/interval.rb +0 -155
- data/lib/mtk/melody.rb +0 -94
- data/lib/mtk/pitch.rb +0 -152
- data/lib/mtk/pitch_class.rb +0 -192
- data/lib/mtk/pitch_class_set.rb +0 -161
- data/lib/mtk/timeline.rb +0 -230
- data/spec/mtk/midi/jsound_input_spec.rb +0 -11
- data/spec/mtk/midi/jsound_output_spec.rb +0 -11
- data/spec/mtk/midi/unimidi_input_spec.rb +0 -11
- data/spec/mtk/midi/unimidi_output_spec.rb +0 -11
@@ -1,34 +1,36 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe MTK::Chord do
|
3
|
+
describe MTK::Groups::Chord do
|
4
4
|
|
5
|
-
|
5
|
+
CHORD = MTK::Groups::Chord
|
6
|
+
|
7
|
+
let(:c_major) { CHORD.new([C4,E4,G4]) }
|
6
8
|
|
7
9
|
describe ".new" do
|
8
10
|
it "removes duplicates" do
|
9
|
-
|
11
|
+
CHORD.new([C4, E4, G4, C4]).pitches.should == [C4, E4, G4]
|
10
12
|
end
|
11
13
|
|
12
14
|
it "sorts the pitches" do
|
13
|
-
|
15
|
+
CHORD.new([F4, G4, E4, D4, C4]).pitches.should == [C4, D4, E4, F4, G4]
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
17
19
|
describe '#inversion' do
|
18
20
|
it "adds an octave to the chord's pitches starting from the lowest, for each whole number in a postive argument" do
|
19
|
-
c_major.inversion(2).should ==
|
21
|
+
c_major.inversion(2).should == CHORD.new([G4,C5,E5])
|
20
22
|
end
|
21
23
|
|
22
24
|
it "subtracts an octave to the chord's pitches starting fromt he highest, for each whole number in a negative argument" do
|
23
|
-
c_major.inversion(-2).should ==
|
25
|
+
c_major.inversion(-2).should == CHORD.new([E3,G3,C4])
|
24
26
|
end
|
25
27
|
|
26
28
|
it "wraps around to the lowest pitch when the argument is bigger than the number of pitches in the chord (positive argument)" do
|
27
|
-
c_major.inversion(4).should ==
|
29
|
+
c_major.inversion(4).should == CHORD.new([E5,G5,C6])
|
28
30
|
end
|
29
31
|
|
30
32
|
it "wraps around to the highest pitch when the magnitude of the argument is bigger than the number of pitches in the chord (negative argument)" do
|
31
|
-
c_major.inversion(-4).should ==
|
33
|
+
c_major.inversion(-4).should == CHORD.new([G2,C3,E3])
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
@@ -46,32 +48,32 @@ describe MTK do
|
|
46
48
|
describe '#Chord' do
|
47
49
|
|
48
50
|
it "acts like new for a single Array argument" do
|
49
|
-
Chord([C4,D4]).should ==
|
51
|
+
Chord([C4,D4]).should == CHORD.new([C4,D4])
|
50
52
|
end
|
51
53
|
|
52
54
|
it "acts like new for multiple arguments, by treating them like an Array (splat)" do
|
53
|
-
Chord(C4,D4).should ==
|
55
|
+
Chord(C4,D4).should == CHORD.new([C4,D4])
|
54
56
|
end
|
55
57
|
|
56
58
|
it "handles an Array with elements that can be converted to Pitches" do
|
57
|
-
Chord(['C4','D4']).should ==
|
59
|
+
Chord(['C4','D4']).should == CHORD.new([C4,D4])
|
58
60
|
end
|
59
61
|
|
60
62
|
it "handles multiple arguments that can be converted to a Pitch" do
|
61
|
-
Chord(:C4,:D4).should ==
|
63
|
+
Chord(:C4,:D4).should == CHORD.new([C4,D4])
|
62
64
|
end
|
63
65
|
|
64
66
|
it "handles a single Pitch" do
|
65
|
-
Chord(C4).should ==
|
67
|
+
Chord(C4).should == CHORD.new([C4])
|
66
68
|
end
|
67
69
|
|
68
70
|
it "handles single elements that can be converted to a Pitch" do
|
69
|
-
Chord('C4').should ==
|
71
|
+
Chord('C4').should == CHORD.new([C4])
|
70
72
|
end
|
71
73
|
|
72
74
|
it "returns the argument if it's already a Chord" do
|
73
|
-
pitch_set =
|
74
|
-
Chord(pitch_set).should ==
|
75
|
+
pitch_set = CHORD.new([C4,D4,D4])
|
76
|
+
Chord(pitch_set).should == CHORD.new([C4,D4])
|
75
77
|
end
|
76
78
|
|
77
79
|
it "raises an error for types it doesn't understand" do
|
@@ -1,16 +1,16 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe MTK::
|
3
|
+
describe MTK::Groups::Collection do
|
4
4
|
|
5
5
|
class MockCollection
|
6
|
-
include MTK::
|
6
|
+
include MTK::Groups::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::
|
13
|
+
include MTK::Groups::Collection
|
14
14
|
attr_reader :elements, :options
|
15
15
|
def initialize(elements, options={})
|
16
16
|
@elements = elements
|
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe MTK::Melody do
|
3
|
+
describe MTK::Groups::Melody do
|
4
|
+
|
5
|
+
MELODY = MTK::Groups::Melody
|
4
6
|
|
5
7
|
let(:pitches) { [C4, D4, E4, D4] }
|
6
|
-
let(:melody) { Melody.new(pitches) }
|
8
|
+
let(:melody) { MTK::Groups::Melody.new(pitches) }
|
7
9
|
|
8
10
|
it "is Enumerable" do
|
9
11
|
melody.should be_a Enumerable
|
@@ -11,25 +13,25 @@ describe MTK::Melody do
|
|
11
13
|
|
12
14
|
describe ".new" do
|
13
15
|
it "maintains the pitches collection exactly (preserves order and keeps duplicates)" do
|
14
|
-
|
16
|
+
MELODY.new([C4, E4, G4, E4, B3, C4]).pitches.should == [C4, E4, G4, E4, B3, C4]
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
18
20
|
describe ".from_pitch_classes" do
|
19
21
|
it "creates a pitch sequence from a list of pitch classes and starting point, selecting the nearest pitch to each pitch class" do
|
20
|
-
|
22
|
+
MELODY.from_pitch_classes([C,G,B,Eb,D,C], D3).should == [C3,G2,B2,Eb3,D3,C3]
|
21
23
|
end
|
22
24
|
|
23
25
|
it "defaults to a starting point of C4 (middle C)" do
|
24
|
-
|
26
|
+
MELODY.from_pitch_classes([C]).should == [C4]
|
25
27
|
end
|
26
28
|
|
27
29
|
it "doesn't travel within an octave above or below the starting point by default" do
|
28
|
-
|
30
|
+
MELODY.from_pitch_classes([C,F,Bb,D,A,E,B]).should == [C4,F4,Bb4,D4,A3,E3,B3]
|
29
31
|
end
|
30
32
|
|
31
33
|
it "allows max distance above or below the starting point to be set via the third argument" do
|
32
|
-
|
34
|
+
MELODY.from_pitch_classes([C,F,Bb,D,A,E,B], C4, 6).should == [C4,F4,Bb3,D4,A3,E4,B3]
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
@@ -73,17 +75,17 @@ describe MTK::Melody do
|
|
73
75
|
end
|
74
76
|
|
75
77
|
describe "#map" do
|
76
|
-
it "returns a
|
78
|
+
it "returns a MELODY with each Pitch replaced with the results of the block" do
|
77
79
|
melody.map{|p| p + 2}.should == [D4, E4, Gb4, E4]
|
78
80
|
end
|
79
81
|
end
|
80
82
|
|
81
83
|
describe "#to_pitch_class_set" do
|
82
84
|
it "is a PitchClassSet" do
|
83
|
-
melody.to_pitch_class_set.should be_a PitchClassSet
|
85
|
+
melody.to_pitch_class_set.should be_a MTK::Groups::PitchClassSet
|
84
86
|
end
|
85
87
|
|
86
|
-
it "contains all the distinct pitch_classes in this
|
88
|
+
it "contains all the distinct pitch_classes in this MELODY by default" do
|
87
89
|
melody.to_pitch_class_set.pitch_classes.should == melody.pitch_classes.uniq
|
88
90
|
end
|
89
91
|
|
@@ -101,75 +103,75 @@ describe MTK::Melody do
|
|
101
103
|
|
102
104
|
describe '#transpose' do
|
103
105
|
it 'transposes upward by the given semitones' do
|
104
|
-
melody.transpose(12).should ==
|
106
|
+
melody.transpose(12).should == MELODY.new([C5, D5, E5, D5])
|
105
107
|
end
|
106
108
|
end
|
107
109
|
|
108
110
|
describe '#invert' do
|
109
111
|
it 'inverts all pitches around the given center pitch' do
|
110
|
-
(melody.invert Gb4).should ==
|
112
|
+
(melody.invert Gb4).should == MELODY.new([C5, Bb4, Ab4, Bb4])
|
111
113
|
end
|
112
114
|
|
113
115
|
it 'inverts all pitches around the first pitch, when no center pitch is given' do
|
114
|
-
melody.invert.should ==
|
116
|
+
melody.invert.should == MELODY.new([C4, Bb3, Ab3, Bb3])
|
115
117
|
end
|
116
118
|
end
|
117
119
|
|
118
120
|
describe '#include?' do
|
119
|
-
it 'returns true if the Pitch is in the
|
121
|
+
it 'returns true if the Pitch is in the MELODY' do
|
120
122
|
(melody.include? C4).should be_true
|
121
123
|
end
|
122
124
|
|
123
|
-
it 'returns false if the Pitch is not in the
|
125
|
+
it 'returns false if the Pitch is not in the MELODY' do
|
124
126
|
(melody.include? Db4).should be_false
|
125
127
|
end
|
126
128
|
end
|
127
129
|
|
128
130
|
describe '#==' do
|
129
131
|
it "is true when all the pitches are equal" do
|
130
|
-
|
132
|
+
MELODY.new([C4, E4, G4]).should == MELODY.new([Pitch.from_i(60), Pitch.from_i(64), Pitch.from_i(67)])
|
131
133
|
end
|
132
134
|
|
133
135
|
it "is false when not all the pitches are equal" do
|
134
|
-
|
136
|
+
MELODY.new([C4, E4, G4]).should_not == MELODY.new([Pitch.from_i(60), Pitch.from_i(65), Pitch.from_i(67)])
|
135
137
|
end
|
136
138
|
|
137
139
|
it "is false when if otherwise equal Melodies don't contain the same number of duplicates" do
|
138
|
-
|
140
|
+
MELODY.new([C4, E4, G4]).should_not == MELODY.new([C4, C4, E4, G4])
|
139
141
|
end
|
140
142
|
|
141
143
|
it "is false when if otherwise equal Melodies aren't in the same order" do
|
142
|
-
|
144
|
+
MELODY.new([C4, E4, G4]).should_not == MELODY.new([C4, G4, E4])
|
143
145
|
end
|
144
146
|
|
145
147
|
it "is false when the argument is not compatible" do
|
146
|
-
|
148
|
+
MELODY.new([C4, E4, G4]).should_not == :invalid
|
147
149
|
end
|
148
150
|
|
149
151
|
it "can be compared directly to Arrays" do
|
150
|
-
|
152
|
+
MELODY.new([C4, E4, G4]).should == [C4, E4, G4]
|
151
153
|
end
|
152
154
|
end
|
153
155
|
|
154
156
|
describe "#=~" do
|
155
157
|
it "is true when all the pitches are equal" do
|
156
|
-
|
158
|
+
MELODY.new([C4, E4, G4]).should =~ MELODY.new([C4, E4, G4])
|
157
159
|
end
|
158
160
|
|
159
161
|
it "is true when all the pitches are equal, even with different numbers of duplicates" do
|
160
|
-
|
162
|
+
MELODY.new([C4, E4, G4]).should =~ MELODY.new([C4, C4, E4, G4])
|
161
163
|
end
|
162
164
|
|
163
165
|
it "is true when all the pitches are equal, even in a different order" do
|
164
|
-
|
166
|
+
MELODY.new([C4, E4, G4]).should =~ MELODY.new([C4, G4, E4])
|
165
167
|
end
|
166
168
|
|
167
|
-
it "is false when one
|
168
|
-
|
169
|
+
it "is false when one MELODY contains a Pitch not in the other" do
|
170
|
+
MELODY.new([C4, E4, G4]).should_not =~ MELODY.new([C4, E4])
|
169
171
|
end
|
170
172
|
|
171
173
|
it "can be compared directly to Arrays" do
|
172
|
-
|
174
|
+
MELODY.new([C4, E4, G4]).should =~ [C4, E4, G4]
|
173
175
|
end
|
174
176
|
end
|
175
177
|
|
@@ -186,31 +188,31 @@ describe MTK do
|
|
186
188
|
describe '#Melody' do
|
187
189
|
|
188
190
|
it "acts like new for a single Array argument" do
|
189
|
-
Melody([C4,D4]).should == Melody.new([C4,D4])
|
191
|
+
Melody([C4,D4]).should == MTK::Groups::Melody.new([C4,D4])
|
190
192
|
end
|
191
193
|
|
192
194
|
it "acts like new for multiple arguments, by treating them like an Array (splat)" do
|
193
|
-
Melody(C4,D4).should == Melody.new([C4,D4])
|
195
|
+
Melody(C4,D4).should == MTK::Groups::Melody.new([C4,D4])
|
194
196
|
end
|
195
197
|
|
196
198
|
it "handles an Array with elements that can be converted to Pitches" do
|
197
|
-
Melody(['C4','D4']).should == Melody.new([C4,D4])
|
199
|
+
Melody(['C4','D4']).should == MTK::Groups::Melody.new([C4,D4])
|
198
200
|
end
|
199
201
|
|
200
202
|
it "handles multiple arguments that can be converted to a Pitch" do
|
201
|
-
Melody(:C4,:D4).should == Melody.new([C4,D4])
|
203
|
+
Melody(:C4,:D4).should == MTK::Groups::Melody.new([C4,D4])
|
202
204
|
end
|
203
205
|
|
204
206
|
it "handles a single Pitch" do
|
205
|
-
Melody(C4).should == Melody.new([C4])
|
207
|
+
Melody(C4).should == MTK::Groups::Melody.new([C4])
|
206
208
|
end
|
207
209
|
|
208
210
|
it "handles single elements that can be converted to a Pitch" do
|
209
|
-
Melody('C4').should == Melody.new([C4])
|
211
|
+
Melody('C4').should == MTK::Groups::Melody.new([C4])
|
210
212
|
end
|
211
213
|
|
212
214
|
it "handles a Melody" do
|
213
|
-
melody = Melody.new([C4,D4])
|
215
|
+
melody = MTK::Groups::Melody.new([C4,D4])
|
214
216
|
Melody(melody).should == [C4,D4]
|
215
217
|
end
|
216
218
|
|
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe MTK::PitchClassSet do
|
3
|
+
describe MTK::Groups::PitchClassSet do
|
4
|
+
|
5
|
+
PITCH_CLASS_SET = MTK::Groups::PitchClassSet
|
4
6
|
|
5
7
|
let(:pitch_classes) { [C,E,G] }
|
6
|
-
let(:pitch_class_set) { PitchClassSet.new(pitch_classes) }
|
8
|
+
let(:pitch_class_set) { MTK::Groups::PitchClassSet.new(pitch_classes) }
|
7
9
|
|
8
10
|
it "is Enumerable" do
|
9
11
|
pitch_class_set.should be_a Enumerable
|
@@ -11,24 +13,24 @@ describe MTK::PitchClassSet do
|
|
11
13
|
|
12
14
|
describe ".random_row" do
|
13
15
|
it "generates a 12-tone row" do
|
14
|
-
|
16
|
+
PITCH_CLASS_SET.random_row.should =~ PitchClasses::PITCH_CLASSES
|
15
17
|
end
|
16
18
|
|
17
19
|
it "generates a random 12-tone row (NOTE: very slight expected chance of test failure, if this fails run it again!)" do
|
18
20
|
# there's a 1/479_001_600 chance this will fail... whaddyagonnado??
|
19
|
-
|
21
|
+
PITCH_CLASS_SET.random_row.should_not == PITCH_CLASS_SET.random_row
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
23
25
|
describe ".all" do
|
24
26
|
it "is the set of all 12 pitch classes" do
|
25
|
-
|
27
|
+
PITCH_CLASS_SET.all.should == MTK.PitchClassSet(PitchClasses::PITCH_CLASSES)
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
29
31
|
describe ".new" do
|
30
32
|
it "maintains the pitch class collection exactly (preserves order and keeps duplicates)" do
|
31
|
-
|
33
|
+
PITCH_CLASS_SET.new([C, E, G, E, B, C]).pitch_classes.should == [C, E, G, E, B, C]
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
@@ -108,30 +110,30 @@ describe MTK::PitchClassSet do
|
|
108
110
|
end
|
109
111
|
|
110
112
|
describe "#map" do
|
111
|
-
it "returns a
|
113
|
+
it "returns a PITCH_CLASS_SET with each PitchClass replaced with the results of the block" do
|
112
114
|
pitch_class_set.map{|pc| pc + 2}.should == [D, Gb, A]
|
113
115
|
end
|
114
116
|
end
|
115
117
|
|
116
118
|
describe '#transpose' do
|
117
119
|
it 'transposes by the given semitones' do
|
118
|
-
(pitch_class_set.transpose 4).should == PitchClassSet(E, Ab, B)
|
120
|
+
(pitch_class_set.transpose 4).should == MTK.PitchClassSet(E, Ab, B)
|
119
121
|
end
|
120
122
|
end
|
121
123
|
|
122
124
|
describe "#invert" do
|
123
125
|
it 'inverts all pitch_classes around the given center pitch' do
|
124
|
-
pitch_class_set.invert(G).should == PitchClassSet(D,Bb,G)
|
126
|
+
pitch_class_set.invert(G).should == MTK.PitchClassSet(D,Bb,G)
|
125
127
|
end
|
126
128
|
|
127
129
|
it 'inverts all pitches around the first pitch, when no center pitch is given' do
|
128
|
-
pitch_class_set.invert.should == PitchClassSet(C,Ab,F)
|
130
|
+
pitch_class_set.invert.should == MTK.PitchClassSet(C,Ab,F)
|
129
131
|
end
|
130
132
|
end
|
131
133
|
|
132
134
|
describe "#reverse" do
|
133
|
-
it "produces a
|
134
|
-
pitch_class_set.reverse.should == PitchClassSet(G,E,C)
|
135
|
+
it "produces a PITCH_CLASS_SET with pitch classes in reverse order" do
|
136
|
+
pitch_class_set.reverse.should == MTK.PitchClassSet(G,E,C)
|
135
137
|
end
|
136
138
|
end
|
137
139
|
|
@@ -142,39 +144,39 @@ describe MTK::PitchClassSet do
|
|
142
144
|
end
|
143
145
|
|
144
146
|
describe "#intersection" do
|
145
|
-
it "produces a
|
146
|
-
pitch_class_set.intersection(PitchClassSet(E,G,B)).should == PitchClassSet(E,G)
|
147
|
+
it "produces a PITCH_CLASS_SET containing the common pitch classes from self and the argument" do
|
148
|
+
pitch_class_set.intersection(MTK.PitchClassSet(E,G,B)).should == MTK.PitchClassSet(E,G)
|
147
149
|
end
|
148
150
|
end
|
149
151
|
|
150
152
|
describe "#union" do
|
151
|
-
it "produces a
|
152
|
-
pitch_class_set.union(PitchClassSet(E,G,B)).should == PitchClassSet(C,E,G,B)
|
153
|
+
it "produces a PITCH_CLASS_SET containing the all pitch classes from either self or the argument" do
|
154
|
+
pitch_class_set.union(MTK.PitchClassSet(E,G,B)).should == MTK.PitchClassSet(C,E,G,B)
|
153
155
|
end
|
154
156
|
end
|
155
157
|
|
156
158
|
describe "#difference" do
|
157
|
-
it "produces a
|
158
|
-
pitch_class_set.difference(PitchClassSet(E)).should == PitchClassSet(C,G)
|
159
|
+
it "produces a PITCH_CLASS_SET with the pitch classes from the argument removed" do
|
160
|
+
pitch_class_set.difference(MTK.PitchClassSet(E)).should == MTK.PitchClassSet(C,G)
|
159
161
|
end
|
160
162
|
end
|
161
163
|
|
162
164
|
describe "#symmetric_difference" do
|
163
|
-
it "produces a
|
164
|
-
pitch_class_set.symmetric_difference(PitchClassSet(E,G,B)).should == PitchClassSet(C,B)
|
165
|
+
it "produces a PITCH_CLASS_SET containing the pitch classes only in self or only in the argument" do
|
166
|
+
pitch_class_set.symmetric_difference(MTK.PitchClassSet(E,G,B)).should == MTK.PitchClassSet(C,B)
|
165
167
|
end
|
166
168
|
end
|
167
169
|
|
168
170
|
describe "#complement" do
|
169
171
|
it "produces the set of all PitchClasses not in the current set" do
|
170
|
-
pitch_class_set.complement.should =~ PitchClassSet(Db,D,Eb,F,Gb,Ab,A,Bb,B)
|
172
|
+
pitch_class_set.complement.should =~ MTK.PitchClassSet(Db,D,Eb,F,Gb,Ab,A,Bb,B)
|
171
173
|
end
|
172
174
|
end
|
173
175
|
|
174
176
|
describe "#rotate" do
|
175
|
-
it "produces a
|
176
|
-
pitch_class_set.rotate(2).should == PitchClassSet(G,C,E)
|
177
|
-
pitch_class_set.rotate(-2).should == PitchClassSet(E,G,C)
|
177
|
+
it "produces a PITCH_CLASS_SET that is rotated by the given offset" do
|
178
|
+
pitch_class_set.rotate(2).should == MTK.PitchClassSet(G,C,E)
|
179
|
+
pitch_class_set.rotate(-2).should == MTK.PitchClassSet(E,G,C)
|
178
180
|
end
|
179
181
|
|
180
182
|
it "rotates by 1 if no argument is given" do
|
@@ -183,8 +185,8 @@ describe MTK::PitchClassSet do
|
|
183
185
|
end
|
184
186
|
|
185
187
|
describe "#permute" do
|
186
|
-
it "randomly rearranges the
|
187
|
-
all_pcs = PitchClassSet(PitchClasses::PITCH_CLASSES)
|
188
|
+
it "randomly rearranges the PITCH_CLASS_SET order (NOTE: very slight expected chance of test failure, if this fails run it again!)" do
|
189
|
+
all_pcs = MTK.PitchClassSet(PitchClasses::PITCH_CLASSES)
|
188
190
|
permuted = all_pcs.permute
|
189
191
|
permuted.should =~ all_pcs
|
190
192
|
permuted.should_not == all_pcs # there's a 1/479_001_600 chance this will fail...
|
@@ -193,7 +195,7 @@ describe MTK::PitchClassSet do
|
|
193
195
|
|
194
196
|
describe "#shuffle" do
|
195
197
|
it "behaves like permute (NOTE: very slight expected chance of test failure, if this fails run it again!)" do
|
196
|
-
all_pcs = PitchClassSet(PitchClasses::PITCH_CLASSES)
|
198
|
+
all_pcs = MTK.PitchClassSet(PitchClasses::PITCH_CLASSES)
|
197
199
|
shuffled = all_pcs.shuffle
|
198
200
|
shuffled.should =~ all_pcs
|
199
201
|
shuffled.should_not == all_pcs # there's a 1/479_001_600 chance this will fail...
|
@@ -202,55 +204,55 @@ describe MTK::PitchClassSet do
|
|
202
204
|
|
203
205
|
describe "#concat" do
|
204
206
|
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)
|
207
|
+
pitch_class_set.concat(MTK.PitchClassSet(D,E,F)).should == MTK.PitchClassSet(C,E,G,D,E,F)
|
206
208
|
end
|
207
209
|
end
|
208
210
|
|
209
211
|
describe "#normal_order" do
|
210
212
|
it "permutes the set so that the first and last pitch classes are as close together as possible" do
|
211
|
-
|
213
|
+
PITCH_CLASS_SET.new([E,A,C]).normal_order.should == [A,C,E]
|
212
214
|
end
|
213
215
|
|
214
216
|
it "breaks ties by minimizing the distance between the first and second-to-last pitch class" do
|
215
217
|
# 0,4,8,9,11
|
216
|
-
|
218
|
+
PITCH_CLASS_SET.new([C,E,Ab,A,B]).normal_order.should == [Ab,A,B,C,E]
|
217
219
|
end
|
218
220
|
|
219
221
|
end
|
220
222
|
|
221
223
|
describe "#normal_form" do
|
222
224
|
it "is transposes the #normal_order so that the first pitch class set is 0 (C)" do
|
223
|
-
|
225
|
+
PITCH_CLASS_SET.new([E,A,C]).normal_form.should == [0,3,7]
|
224
226
|
end
|
225
227
|
|
226
228
|
it "is invariant across reorderings of the pitch classes" do
|
227
|
-
|
228
|
-
|
229
|
-
|
229
|
+
PITCH_CLASS_SET.new([C,E,G]).normal_form.should == [0,4,7]
|
230
|
+
PITCH_CLASS_SET.new([E,C,G]).normal_form.should == [0,4,7]
|
231
|
+
PITCH_CLASS_SET.new([G,E,C]).normal_form.should == [0,4,7]
|
230
232
|
end
|
231
233
|
|
232
234
|
it "is invariant across transpositions" do
|
233
|
-
|
234
|
-
|
235
|
-
|
235
|
+
PITCH_CLASS_SET.new([C,Eb,G]).normal_form.should == [0,3,7]
|
236
|
+
PITCH_CLASS_SET.new([Db,E,Ab]).normal_form.should == [0,3,7]
|
237
|
+
PITCH_CLASS_SET.new([Bb,F,Db]).normal_form.should == [0,3,7]
|
236
238
|
end
|
237
239
|
end
|
238
240
|
|
239
241
|
describe "#==" do
|
240
242
|
it "is true if two pitch class sets contain the same set in the same order" do
|
241
|
-
pitch_class_set.should == PitchClassSet(C,E,G)
|
243
|
+
pitch_class_set.should == MTK.PitchClassSet(C,E,G)
|
242
244
|
end
|
243
245
|
|
244
246
|
it "is false if two pitch class sets are not in the same order" do
|
245
|
-
pitch_class_set.should_not == PitchClassSet(C,G,E)
|
247
|
+
pitch_class_set.should_not == MTK.PitchClassSet(C,G,E)
|
246
248
|
end
|
247
249
|
|
248
250
|
it "is false when if otherwise equal pitch class sets don't contain the same number of duplicates" do
|
249
|
-
|
251
|
+
PITCH_CLASS_SET.new([C, E, G]).should_not == PITCH_CLASS_SET.new([C, C, E, G])
|
250
252
|
end
|
251
253
|
|
252
254
|
it "is false if two pitch class sets do not contain the same pitch classes" do
|
253
|
-
pitch_class_set.should_not == PitchClassSet(C,E)
|
255
|
+
pitch_class_set.should_not == MTK.PitchClassSet(C,E)
|
254
256
|
end
|
255
257
|
|
256
258
|
it "allows for direct comparison with Arrays" do
|
@@ -260,19 +262,19 @@ describe MTK::PitchClassSet do
|
|
260
262
|
|
261
263
|
describe "#=~" do
|
262
264
|
it "is true if two pitch class sets contain the same set in the same order" do
|
263
|
-
pitch_class_set.should =~ PitchClassSet(C,E,G)
|
265
|
+
pitch_class_set.should =~ MTK.PitchClassSet(C,E,G)
|
264
266
|
end
|
265
267
|
|
266
268
|
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])
|
269
|
+
MTK::Groups::Melody.new([C, E, G]).should =~ MTK::Groups::Melody.new([C, C, E, G])
|
268
270
|
end
|
269
271
|
|
270
272
|
it "is true if two pitch class sets are not in the same order" do
|
271
|
-
pitch_class_set.should =~ PitchClassSet(C,G,E)
|
273
|
+
pitch_class_set.should =~ MTK.PitchClassSet(C,G,E)
|
272
274
|
end
|
273
275
|
|
274
276
|
it "is false if two pitch class sets do not contain the same pitch classes" do
|
275
|
-
pitch_class_set.should_not =~ PitchClassSet(C,E)
|
277
|
+
pitch_class_set.should_not =~ MTK.PitchClassSet(C,E)
|
276
278
|
end
|
277
279
|
|
278
280
|
it "allows for direct comparison with Arrays" do
|
@@ -282,11 +284,11 @@ describe MTK::PitchClassSet do
|
|
282
284
|
|
283
285
|
describe ".span_between" do
|
284
286
|
it "is the distance in semitones between 2 pitch classes" do
|
285
|
-
|
287
|
+
PITCH_CLASS_SET.span_between(F, Bb).should == 5
|
286
288
|
end
|
287
289
|
|
288
290
|
it "assumes an ascending interval between the arguments (order of arguments matters)" do
|
289
|
-
|
291
|
+
PITCH_CLASS_SET.span_between(Bb, F).should == 7
|
290
292
|
end
|
291
293
|
end
|
292
294
|
|
@@ -297,35 +299,35 @@ describe MTK do
|
|
297
299
|
describe '#PitchClassSet' do
|
298
300
|
|
299
301
|
it "constructs a PitchClassSet" do
|
300
|
-
PitchClassSet(C,D).should be_a
|
302
|
+
PitchClassSet(C,D).should be_a PITCH_CLASS_SET
|
301
303
|
end
|
302
304
|
|
303
305
|
it "acts like new for a single Array argument" do
|
304
|
-
PitchClassSet([C,D]).should ==
|
306
|
+
PitchClassSet([C,D]).should == PITCH_CLASS_SET.new([C,D])
|
305
307
|
end
|
306
308
|
|
307
309
|
it "acts like new for multiple arguments, by treating them like an Array (splat)" do
|
308
|
-
PitchClassSet(C,D).should ==
|
310
|
+
PitchClassSet(C,D).should == PITCH_CLASS_SET.new([C,D])
|
309
311
|
end
|
310
312
|
|
311
313
|
it "handles an Array with elements that can be converted to Pitches" do
|
312
|
-
PitchClassSet(['C','D']).should ==
|
314
|
+
PitchClassSet(['C','D']).should == PITCH_CLASS_SET.new([C,D])
|
313
315
|
end
|
314
316
|
|
315
317
|
it "handles multiple arguments that can be converted to a Pitch" do
|
316
|
-
PitchClassSet(:C,:D).should ==
|
318
|
+
PitchClassSet(:C,:D).should == PITCH_CLASS_SET.new([C,D])
|
317
319
|
end
|
318
320
|
|
319
321
|
it "handles a single Pitch" do
|
320
|
-
PitchClassSet(C).should ==
|
322
|
+
PitchClassSet(C).should == PITCH_CLASS_SET.new([C])
|
321
323
|
end
|
322
324
|
|
323
325
|
it "handles single elements that can be converted to a Pitch" do
|
324
|
-
PitchClassSet('C').should ==
|
326
|
+
PitchClassSet('C').should == PITCH_CLASS_SET.new([C])
|
325
327
|
end
|
326
328
|
|
327
329
|
it "handles a a PitchClassSet" do
|
328
|
-
pitch_set =
|
330
|
+
pitch_set = PITCH_CLASS_SET.new([C,D])
|
329
331
|
PitchClassSet(pitch_set).should == PitchClassSet([C,D])
|
330
332
|
end
|
331
333
|
|