dodecaphony 0.1.0 → 0.2.0
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.
- 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
|