dodecaphony 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Readme.md +36 -23
- data/lib/calculator.rb +53 -0
- data/lib/pitch.rb +7 -0
- data/lib/{dodecaphony.rb → row.rb} +18 -44
- data/spec/calculator_spec.rb +84 -0
- data/spec/pitch_spec.rb +21 -0
- data/spec/{dodecaphony_spec.rb → row_spec.rb} +20 -43
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 68d01640c510abfd7e48edade6c57d6dfcb35e07
|
4
|
+
data.tar.gz: b285ae148dff3451117edeb016e5f8ef4af039dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9228481081aa85c962421bfcee1f5e744238f169566b874c42d2b16d0be9e2b8b3f451bfbacef20053a3e4c0f912b624873a80e3d4f97d91a9daa79368e2ee98
|
7
|
+
data.tar.gz: 29489b6ba21afca6133b533d0e4c71944f1b48c09b0e0914d70c0124c1de2a3e1a7cc2799484e3689724e59ae1a54a97473f61a625059fe8eba6d75279ddcd7a
|
data/Readme.md
CHANGED
@@ -5,45 +5,58 @@ Dodecaphony calculates twelve tone rows for
|
|
5
5
|
you. To learn more about what this means, [read
|
6
6
|
this](http://www.tufts.edu/~mdevoto/12TonePrimer.pdf).
|
7
7
|
|
8
|
+
Dodecaphony is a ruby gem. Install with
|
9
|
+
```
|
10
|
+
gem install dodecaphony
|
11
|
+
```
|
12
|
+
|
8
13
|
Examples
|
9
14
|
---
|
10
15
|
|
11
|
-
|
12
|
-
|
16
|
+
First, you'll need a `Dodecaphony::Row` object. `Dodecaphony::Row`
|
17
|
+
requires an array of 12 strings, each representing a unique pitch. Each
|
18
|
+
pitch must begin with a valid letter name (a through g) and can be
|
19
|
+
followed by one or more accidentals. `#`, `+`, `b` and `-` are the four
|
20
|
+
valid accidentals.
|
13
21
|
|
14
22
|
```ruby
|
15
|
-
Dodecaphony::Row.new %w[a a# b c c# d d# e f f# g g#]
|
23
|
+
my_row = Dodecaphony::Row.new %w[a a# b c c# d d# e f f# g g#]
|
16
24
|
```
|
25
|
+
A row can be converted to either an array of pitch names, or a string with
|
26
|
+
pitch names separated by a space.
|
27
|
+
```ruby
|
28
|
+
my_row.to_a # ['a', 'a#', 'b', 'c', 'c#', 'd', 'd#', 'e', 'f', 'f#', 'g', 'g#']
|
17
29
|
|
18
|
-
|
19
|
-
|
20
|
-
|
30
|
+
my_row.to_s # "a a# b c c# d d# e f f# g g#"
|
31
|
+
```
|
32
|
+
You can also ask a row to respell itself, favoring flats or sharps:
|
33
|
+
```ruby
|
34
|
+
tone_row = Dodecaphony::Row.new %w[ a f# g ab e f b bb d c# c eb ]
|
21
35
|
|
22
|
-
|
23
|
-
retrograde inversion like so:
|
36
|
+
tone_row.spell_with_sharps # ["A", "F#", "G", "G#", "E", "F", "B", "A#", "D", "C#", "C", "D#"]
|
24
37
|
|
38
|
+
tone_row.spell_with_flats # ["A", "Gb", "G", "Ab", "E", "F", "B", "Bb", "D", "Db", "C", "Eb"]
|
39
|
+
```
|
40
|
+
The `Dodecaphony::Calculator` class initializes with a `Dodecaphony::Row`
|
41
|
+
object.
|
25
42
|
```ruby
|
26
43
|
tone_row = Dodecaphony::Row.new %w[ a f# g ab e f b bb d c# c eb ]
|
27
|
-
|
44
|
+
calc = Dodecaphony::Calculator.new tone_row
|
45
|
+
```
|
46
|
+
The calculator can then return any prime, inversion, retrograde, or retrograde
|
47
|
+
inversion. Each of these rows is a method of the calculator object, the names
|
48
|
+
being the initial of the type of row you want, followed by the number of the
|
49
|
+
row you want. Observe:
|
50
|
+
```ruby
|
28
51
|
# "p" followed by 0 through 11 returns the corresponding prime row
|
29
|
-
|
52
|
+
calc.p0 # ["a", "f#", "g", "ab", "e", "f", "b", "bb", "d", "c#", "c", "eb"]
|
30
53
|
|
31
54
|
# "r" followed by 0 through 11 returns the corresponding retrograde
|
32
|
-
|
55
|
+
calc.r11 # ["d", "b", "c", "c#", "a", "bb", "e", "eb", "g", "f#", "f", "ab"]
|
33
56
|
|
34
57
|
# "i" followed by 0 through 11 returns the corresponding inversion
|
35
|
-
|
58
|
+
calc.i2 # ["b", "d", "c#", "c", "e", "eb", "a", "bb", "f#", "g", "ab", "f"]
|
36
59
|
|
37
60
|
# "ri" followed by 0 through 11 returns the corresponding retrograde inversion
|
38
|
-
|
39
|
-
```
|
40
|
-
|
41
|
-
You can also ask a row to respell itself, favoring flats or sharps:
|
42
|
-
```ruby
|
43
|
-
tone_row = Dodecaphony::Row.new %w[ a f# g ab e f b bb d c# c eb ]
|
44
|
-
|
45
|
-
tone_row.spell_with_sharps # ["A", "F#", "G", "G#", "E", "F", "B", "A#", "D", "C#", "C", "D#"]
|
46
|
-
|
47
|
-
tone_row.spell_with_flats # ["A", "Gb", "G", "Ab", "E", "F", "B", "Bb", "D", "Db", "C", "Eb"]
|
61
|
+
calc.ri6 # ["a", "c", "b", "bb", "d", "c#", "g", "ab", "e", "f", "f#", "eb"]
|
48
62
|
```
|
49
|
-
|
data/lib/calculator.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative 'row'
|
2
|
+
|
3
|
+
module Dodecaphony
|
4
|
+
class Calculator
|
5
|
+
|
6
|
+
def initialize row
|
7
|
+
self.row = row
|
8
|
+
end
|
9
|
+
|
10
|
+
def i0
|
11
|
+
row.each_with_object([]) { |pitch, new_row|
|
12
|
+
new_row << row.intervals[((row.intervals.key(pitch) - 12).abs)].name
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
(1..11).each do |i|
|
17
|
+
define_method "i#{i}".to_sym do
|
18
|
+
corresponding_p = self.send("p#{i}").to_a
|
19
|
+
new_row = Dodecaphony::Row.new corresponding_p
|
20
|
+
(self.class.new new_row).i0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
(0..11).each do |i|
|
25
|
+
define_method "p#{i}".to_sym do
|
26
|
+
row.pitches.each_with_object([]) { |pitch, new_row|
|
27
|
+
new_row << row.intervals[(transpose i, pitch)].name
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
define_method "r#{i}".to_sym do
|
32
|
+
self.send("p#{i}").reverse
|
33
|
+
end
|
34
|
+
|
35
|
+
define_method "ri#{i}".to_sym do
|
36
|
+
self.send("i#{i}").reverse
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_accessor :row
|
43
|
+
|
44
|
+
def transpose interval, pitch
|
45
|
+
(starting_pitch.distance_from(pitch) + interval) % 12
|
46
|
+
end
|
47
|
+
|
48
|
+
def starting_pitch
|
49
|
+
row.pitches[0]
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/lib/pitch.rb
CHANGED
@@ -3,18 +3,27 @@ require 'set'
|
|
3
3
|
|
4
4
|
module Dodecaphony
|
5
5
|
class Row
|
6
|
+
include Enumerable
|
6
7
|
|
7
|
-
attr_reader :original_row
|
8
|
+
attr_reader :original_row, :intervals
|
9
|
+
|
10
|
+
alias_method :pitches, :original_row
|
8
11
|
|
9
12
|
def initialize tone_row
|
10
13
|
self.original_row = create_row_with_pitches(tone_row)
|
11
14
|
validate_size_of original_row
|
12
|
-
self.
|
15
|
+
self.intervals = create_list_with_intervals(original_row,
|
13
16
|
starting_pitch)
|
14
17
|
end
|
15
18
|
|
16
19
|
def to_s
|
17
|
-
|
20
|
+
to_a.join(" ")
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_a
|
24
|
+
pitches.each_with_object([]) do |pitch, row|
|
25
|
+
row << pitch.name
|
26
|
+
end
|
18
27
|
end
|
19
28
|
|
20
29
|
def spell_with_sharps
|
@@ -25,49 +34,18 @@ module Dodecaphony
|
|
25
34
|
normalize_row(:spell_as_flat)
|
26
35
|
end
|
27
36
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
(0..11).each do |i|
|
35
|
-
define_method "p#{i}".to_sym do
|
36
|
-
original_row.each_with_object([]) do |pitch, row|
|
37
|
-
row << row_with_intervals[(transpose i, pitch)].name
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
(1..11).each do |i|
|
43
|
-
define_method "i#{i}".to_sym do
|
44
|
-
corresponding_p = self.send("p#{i}")
|
45
|
-
new_row = self.class.new corresponding_p
|
46
|
-
new_row.i0
|
37
|
+
def == other
|
38
|
+
(other.pitches).zip(pitches).all? do |pitches|
|
39
|
+
pitches[0] == pitches[1]
|
47
40
|
end
|
48
41
|
end
|
49
42
|
|
50
|
-
|
51
|
-
|
52
|
-
self.send("p#{i}".to_sym).reverse.each_with_object([]) do |pitch, row|
|
53
|
-
row << pitch
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
(0..11).each do |i|
|
59
|
-
define_method "ri#{i}".to_sym do
|
60
|
-
self.send("i#{i}").reverse.each_with_object([]) do |pitch, row|
|
61
|
-
row << pitch
|
62
|
-
end
|
63
|
-
end
|
43
|
+
def each &block
|
44
|
+
pitches.each &block
|
64
45
|
end
|
65
|
-
|
66
46
|
private
|
67
47
|
|
68
|
-
attr_writer :original_row
|
69
|
-
|
70
|
-
attr_accessor :row_with_intervals
|
48
|
+
attr_writer :intervals, :original_row
|
71
49
|
|
72
50
|
def create_list_with_intervals(row, first_pitch)
|
73
51
|
row_list = row.each_with_object({}) do |pitch, hash|
|
@@ -112,9 +90,5 @@ module Dodecaphony
|
|
112
90
|
end
|
113
91
|
end
|
114
92
|
|
115
|
-
def transpose interval, pitch
|
116
|
-
(starting_pitch.distance_from(pitch) + interval) % 12
|
117
|
-
end
|
118
|
-
|
119
93
|
end
|
120
94
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'calculator'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe Dodecaphony::Calculator do
|
5
|
+
|
6
|
+
it "initializes with a row" do
|
7
|
+
row = Dodecaphony::Row.new %w[a b b- c c# d d# e f gb g g#]
|
8
|
+
calc = Dodecaphony::Calculator.new row
|
9
|
+
|
10
|
+
expect(calc).to be_kind_of(Dodecaphony::Calculator)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can provide the original row" do
|
14
|
+
row = Dodecaphony::Row.new %w[a b b- c c# d d# e f gb g g#]
|
15
|
+
calc = Dodecaphony::Calculator.new row
|
16
|
+
|
17
|
+
expect(calc.p0).to eq row.to_a
|
18
|
+
end
|
19
|
+
|
20
|
+
it "can provide p1" do
|
21
|
+
row = Dodecaphony::Row.new %w[ bb b c c# d f e eb g gb a ab ]
|
22
|
+
p1_row = %w[ b c c# d eb gb f e ab g bb a ]
|
23
|
+
calc = Dodecaphony::Calculator.new row
|
24
|
+
|
25
|
+
expect(calc.p1).to eq p1_row.to_a
|
26
|
+
end
|
27
|
+
|
28
|
+
it "can give p7" do
|
29
|
+
row = Dodecaphony::Row.new %w[ d c# a b- f eb e c ab g f# b ]
|
30
|
+
prime7 = %w[ a ab e f c b- b g eb d c# f# ]
|
31
|
+
calc = Dodecaphony::Calculator.new row
|
32
|
+
|
33
|
+
expect(calc.p7).to eq prime7
|
34
|
+
end
|
35
|
+
|
36
|
+
it "can give i0" do
|
37
|
+
row = Dodecaphony::Row.new %w[ A ab a# b c c# d eb e f gb g ]
|
38
|
+
new_row = %w[ A a# ab g gb f e eb d c# c b ]
|
39
|
+
calc = Dodecaphony::Calculator.new row
|
40
|
+
|
41
|
+
expect(calc.i0).to eq new_row
|
42
|
+
end
|
43
|
+
|
44
|
+
it "can give i8" do
|
45
|
+
row = Dodecaphony::Row.new %w[ A ab a# b c c# d eb e f gb g ]
|
46
|
+
new_row = %w[ f gb e eb d c# c b a# A ab g ]
|
47
|
+
calc = Dodecaphony::Calculator.new row
|
48
|
+
|
49
|
+
expect(calc.i8).to eq new_row
|
50
|
+
end
|
51
|
+
|
52
|
+
it "can give r0" do
|
53
|
+
row = Dodecaphony::Row.new %w[ a f# g ab e f b bb d c# c eb ]
|
54
|
+
new_row = %w[ eb c c# d bb b f e ab g f# a ]
|
55
|
+
calc = Dodecaphony::Calculator.new row
|
56
|
+
|
57
|
+
expect(calc.r0).to eq new_row
|
58
|
+
end
|
59
|
+
|
60
|
+
it "can give r9" do
|
61
|
+
row = Dodecaphony::Row.new %w[ a f# g ab e f b bb d c# c eb ]
|
62
|
+
new_row = %w[ c a bb b g ab d c# f e eb f# ]
|
63
|
+
calc = Dodecaphony::Calculator.new row
|
64
|
+
|
65
|
+
expect(calc.r9).to eq new_row
|
66
|
+
end
|
67
|
+
|
68
|
+
it "can give ri0" do
|
69
|
+
row = Dodecaphony::Row.new %w[ a f# g ab e f b bb d c# c eb ]
|
70
|
+
new_row = %w[eb f# f e ab g c# d bb b c a ]
|
71
|
+
calc = Dodecaphony::Calculator.new row
|
72
|
+
|
73
|
+
expect(calc.ri0).to eq new_row
|
74
|
+
end
|
75
|
+
|
76
|
+
it "can give ri11" do
|
77
|
+
row = Dodecaphony::Row.new %w[ a f# g ab e f b bb d c# c eb ]
|
78
|
+
new_row = %w[ c# e eb d f# f b c ab a bb g ]
|
79
|
+
calc = Dodecaphony::Calculator.new row
|
80
|
+
|
81
|
+
expect(calc.ri10).to eq new_row
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
data/spec/pitch_spec.rb
CHANGED
@@ -51,4 +51,25 @@ describe Dodecaphony::Pitch do
|
|
51
51
|
it "raises an error with an invalid pitch name" do
|
52
52
|
expect {Dodecaphony::Pitch.new "H"}.to raise_error(ArgumentError, 'invalid pitch name')
|
53
53
|
end
|
54
|
+
|
55
|
+
it "knows when it is not equal to another pitch" do
|
56
|
+
p1 = Dodecaphony::Pitch.new "a#"
|
57
|
+
p2 = Dodecaphony::Pitch.new "b-"
|
58
|
+
|
59
|
+
expect(p1).to_not eq p2
|
60
|
+
end
|
61
|
+
|
62
|
+
it "knows when it is equal to another pitch" do
|
63
|
+
p1 = Dodecaphony::Pitch.new "a"
|
64
|
+
p2 = Dodecaphony::Pitch.new "a"
|
65
|
+
|
66
|
+
expect(p1).to eq p2
|
67
|
+
end
|
68
|
+
|
69
|
+
it "doesn't factor accidental style into equality" do
|
70
|
+
p1 = Dodecaphony::Pitch.new "c#"
|
71
|
+
p2 = Dodecaphony::Pitch.new "c+"
|
72
|
+
|
73
|
+
expect(p1).to eq p2
|
74
|
+
end
|
54
75
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'row'
|
2
2
|
|
3
3
|
describe Dodecaphony::Row do
|
4
4
|
|
@@ -6,7 +6,7 @@ describe Dodecaphony::Row do
|
|
6
6
|
tone_row = %w[ a a# b c db d eb e f f# g g# ]
|
7
7
|
new_dod = Dodecaphony::Row.new tone_row
|
8
8
|
|
9
|
-
expect(new_dod.
|
9
|
+
expect(new_dod.to_a).to eq tone_row
|
10
10
|
end
|
11
11
|
|
12
12
|
it "raises an error for a row with more than 12 pitches" do
|
@@ -38,59 +38,36 @@ describe Dodecaphony::Row do
|
|
38
38
|
expect(new_dod.spell_with_flats).to eq %w[ C Db D Eb E F Gb G Ab A Bb B ]
|
39
39
|
end
|
40
40
|
|
41
|
-
it "
|
42
|
-
tone_row = %w[ bb b c c# d f e eb g gb a ab ]
|
43
|
-
new_dod = Dodecaphony::Row.new tone_row
|
44
|
-
|
45
|
-
expect(new_dod.p1).to eq %w[ b c c# d eb gb f e ab g bb a ]
|
46
|
-
end
|
47
|
-
|
48
|
-
it "can give p7" do
|
49
|
-
tone_row = Dodecaphony::Row.new %w[ d c# a b- f eb e c ab g f# b ]
|
50
|
-
|
51
|
-
expect(tone_row.p7).to eq %w[ a ab e f c b- b g eb d c# f# ]
|
52
|
-
end
|
53
|
-
|
54
|
-
it "can give i0" do
|
55
|
-
tone_row = Dodecaphony::Row.new %w[ A ab a# b c c# d eb e f gb g ]
|
56
|
-
|
57
|
-
expect(tone_row.i0).to eq %w[ A a# ab g gb f e eb d c# c b ]
|
58
|
-
end
|
59
|
-
|
60
|
-
it "can give i8" do
|
61
|
-
tone_row = Dodecaphony::Row.new %w[ A ab a# b c c# d eb e f gb g ]
|
62
|
-
|
63
|
-
expect(tone_row.i8).to eq %w[ f gb e eb d c# c b a# A ab g ]
|
64
|
-
end
|
65
|
-
|
66
|
-
it "can give r0" do
|
41
|
+
it "returns a reasonable string" do
|
67
42
|
tone_row = Dodecaphony::Row.new %w[ a f# g ab e f b bb d c# c eb ]
|
68
43
|
|
69
|
-
expect(tone_row.
|
44
|
+
expect(tone_row.to_s).to eq "a f# g ab e f b bb d c# c eb"
|
70
45
|
end
|
71
|
-
|
72
|
-
it "can give r9" do
|
73
|
-
tone_row = Dodecaphony::Row.new %w[ a f# g ab e f b bb d c# c eb ]
|
74
46
|
|
75
|
-
|
47
|
+
it "returns an array" do
|
48
|
+
tone_row = Dodecaphony::Row.new %w[a f# g ab e f b b- d c# c eb]
|
49
|
+
|
50
|
+
expect(tone_row.to_a).to eq %w[a f# g ab e f b b- d c# c eb]
|
76
51
|
end
|
77
52
|
|
78
|
-
it "
|
79
|
-
tone_row = Dodecaphony::Row.new %w[
|
53
|
+
it "returns its pitches" do
|
54
|
+
tone_row = Dodecaphony::Row.new %w[a f# g ab e f b b- d c# c eb]
|
55
|
+
first_pitch = Dodecaphony::Pitch.new "a"
|
80
56
|
|
81
|
-
expect(tone_row.
|
57
|
+
expect(tone_row.pitches.to_a[0].name).to eq first_pitch.name
|
82
58
|
end
|
83
59
|
|
84
|
-
it "can
|
85
|
-
|
60
|
+
it "can check for equality with other rows" do
|
61
|
+
row1 = Dodecaphony::Row.new %w[a f# g ab e f b b- d c# c eb]
|
62
|
+
row2 = Dodecaphony::Row.new %w[a f# g ab e f b b- d c# c eb]
|
86
63
|
|
87
|
-
expect(
|
64
|
+
expect(row1).to eq row2
|
88
65
|
end
|
89
66
|
|
90
|
-
it "
|
91
|
-
|
67
|
+
it "knows when rows are unequal" do
|
68
|
+
row1 = Dodecaphony::Row.new %w[a f# g ab e f b b- d c# c eb]
|
69
|
+
row2 = Dodecaphony::Row.new %w[a b b- c d- d e- e f g- g a-]
|
92
70
|
|
93
|
-
expect(
|
71
|
+
expect(row1).to_not eq row2
|
94
72
|
end
|
95
|
-
|
96
73
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dodecaphony
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin McGladdery
|
@@ -39,10 +39,12 @@ extra_rdoc_files: []
|
|
39
39
|
files:
|
40
40
|
- LICENSE
|
41
41
|
- Readme.md
|
42
|
-
- lib/
|
42
|
+
- lib/calculator.rb
|
43
43
|
- lib/pitch.rb
|
44
|
-
-
|
44
|
+
- lib/row.rb
|
45
|
+
- spec/calculator_spec.rb
|
45
46
|
- spec/pitch_spec.rb
|
47
|
+
- spec/row_spec.rb
|
46
48
|
homepage: http://github.com/runkmc/dodecaphony
|
47
49
|
licenses:
|
48
50
|
- MIT
|
@@ -63,10 +65,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
65
|
version: '0'
|
64
66
|
requirements: []
|
65
67
|
rubyforge_project:
|
66
|
-
rubygems_version: 2.
|
68
|
+
rubygems_version: 2.2.2
|
67
69
|
signing_key:
|
68
70
|
specification_version: 4
|
69
71
|
summary: Tone Row Calculator
|
70
72
|
test_files:
|
71
|
-
- spec/
|
73
|
+
- spec/calculator_spec.rb
|
72
74
|
- spec/pitch_spec.rb
|
75
|
+
- spec/row_spec.rb
|