post_tonal 0.1.0.pre

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/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
19
+
20
+ .DS_Store
data/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ #CHANGELOG
2
+
3
+ ####October 21, 2012 [0.1.0.pre]
4
+ -Implement transposition in PitchClassSet
5
+ -Change normal_form, inversion, and transpose so that they each return a PitchClassSet
6
+
7
+ ####October 20, 2012 [0.0.3.pre]
8
+ -Implement inversion in PitchClassSet
9
+
10
+ ####October 16, 2012 [0.0.2.pre]
11
+ -Implement normal form in PitchClassSet
12
+
13
+ ####October 15, 2012 [0.0.1.pre]
14
+ - Create PitchClass, PitchClassSet, PitchClassInterval, PitchInterval, and NoteParser
data/README.md ADDED
@@ -0,0 +1,8 @@
1
+ PostTonal
2
+ ==========
3
+
4
+ PostTonal is a Ruby library for analyzing sets of musical pitches. It's based on [pitch class set theory](http://en.wikipedia.org/wiki/Set_theory_\(music\) "Pitch class set theory") pioneered by Allen Forte.
5
+
6
+ Work In Progress: Detailed usage documentation to come soon.
7
+
8
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+ Rake::TestTask.new(:test) do |test|
3
+ test.libs << 'lib' << 'test'
4
+ test.pattern = 'test/{functional,unit}/**/*_test.rb'
5
+ test.verbose = true
6
+ end
7
+
8
+ task :default => :test
@@ -0,0 +1,30 @@
1
+ module PostTonal
2
+ class NoteParser
3
+
4
+ NAME_TO_INT = { :'b#' => 0, :'c' => 0,
5
+ :'c#' => 1, :'db' => 1,
6
+ :'d' => 2,
7
+ :'d#' => 3, :'eb' => 3,
8
+ :'e' => 4, :'fb' => 4,
9
+ :'e#' => 5, :'f' => 5,
10
+ :'f#' => 6, :'gb' => 6,
11
+ :'g' => 7,
12
+ :'g#' => 8, :'ab' => 8,
13
+ :'a' => 9,
14
+ :'a#' => 10, :'bb' => 10,
15
+ :'b' => 11, :'cb' => 11}
16
+
17
+ INT_TO_NAME = ['c', 'c#', 'd', 'd#', 'e', 'f', 'f#', 'g', 'g#', 'a', 'a#', 'b']
18
+ INT_TO_NAME_FLAT = ['c', 'db', 'd', 'eb', 'e', 'f', 'gb', 'g', 'ab', 'a', 'bb', 'b']
19
+
20
+ def self.integer_value(name_or_integer)
21
+ return name_or_integer.abs % 12 if name_or_integer.class == Fixnum
22
+ return NAME_TO_INT[name_or_integer.downcase.to_sym]
23
+ end
24
+
25
+ def self.note_name(name_or_integer)
26
+ return INT_TO_NAME[(name_or_integer.abs % 12)] if name_or_integer.class == Fixnum
27
+ return name_or_integer.downcase if NAME_TO_INT.has_key?(name_or_integer.downcase.to_sym)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,37 @@
1
+ module PostTonal
2
+ class PitchClass
3
+
4
+ require 'post_tonal/note_parser'
5
+
6
+ # value - The integer value of a pitch class, with C as 0.
7
+ # octave - A relative marker denoting the octave of the pitch class. Defaults to 0.
8
+ attr_reader :value, :octave
9
+
10
+ def initialize(note_name_or_integer, octave = 0)
11
+ @value = NoteParser.integer_value(note_name_or_integer)
12
+ @octave = octave
13
+ end
14
+
15
+ def value=(val)
16
+ v = val
17
+ v -= 12 while v > 11
18
+ v += 12 while v < 0
19
+
20
+ @value = v
21
+
22
+ @value
23
+ end
24
+
25
+ def to_s
26
+ "[#{@value}, oct.#{@octave}]"
27
+ end
28
+
29
+ def eql?(pitch_class)
30
+ return pitch_class.value == @value && pitch_class.octave == @octave
31
+ end
32
+
33
+ def ==(pitch_class)
34
+ eql? pitch_class
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,32 @@
1
+ module PostTonal
2
+ class PitchClassInterval
3
+
4
+ attr_reader :pitch1, :pitch2, :ordered, :unordered
5
+
6
+ def initialize(pitch1, pitch2)
7
+ @pitch1 = pitch1
8
+ @pitch2 = pitch2
9
+
10
+ @ordered = calculate_ordered(pitch1, pitch2)
11
+ @unordered = calculate_unordered(pitch1, pitch2)
12
+ end
13
+
14
+ private
15
+
16
+ def calculate_ordered(p1, p2)
17
+ pitches = [p1, p2]
18
+
19
+ if pitches[0].octave != pitches[1].octave
20
+ pitches = pitches.sort { |x, y| y.octave <=> x.octave }
21
+ else
22
+ pitches = pitches.sort { |x, y| y.value <=> x.value }
23
+ end
24
+
25
+ (pitches[0].value + (12 * pitches[0].octave)) - (pitches[1].value + (12 * pitches[1].octave))
26
+ end
27
+
28
+ def calculate_unordered(p1, p2)
29
+ (p1.value - p2.value).abs > 6 ? 12 - (p1.value - p2.value).abs : (p1.value - p2.value).abs
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,145 @@
1
+ module PostTonal
2
+ class PitchClassSet
3
+
4
+ require 'post_tonal/pitch_class'
5
+
6
+ attr_reader :pitch_classes
7
+
8
+ def initialize(pitch_classes = nil)
9
+ @pitch_classes = pitch_classes || []
10
+ @normalized_pitch_classes = pitch_classes ? self.class.to_normal_form(@pitch_classes) : []
11
+ @inverted_pitch_classes = pitch_classes ? invert(@pitch_classes) : []
12
+ end
13
+
14
+ # Add a pitch to the pitch class set
15
+ #
16
+ # The reason you are adding pitches as opposed to pitch classes
17
+ # is that you may want to get the ordered intervals of the set.
18
+ # Therefore, you may pass an optional octave value, which is an
19
+ # arbitrary marker starting at 0 representing the default octave.
20
+ # Non-zero values are calculated relative to octave 0.
21
+ #
22
+ # note_name_or_integer may be the name of the note (e.g. A-G, with #
23
+ # and lowercase b representing sharps and flats, respectively), or the
24
+ # integer value of the pitch class (e.g. 0-11, or T and E for eleven
25
+ # and twelve, respectively)
26
+ def add_pitch(note_name_or_integer, octave = 0)
27
+
28
+ pc = PitchClass.new(note_name_or_integer, octave)
29
+
30
+ if !@pitch_classes.any? { |p| p.value == pc.value && p.octave == pc.octave }
31
+ @pitch_classes << pc
32
+ @normalized_pitch_classes = self.class.to_normal_form(@pitch_classes)
33
+ @inverted_pitch_classes = invert(@pitch_classes)
34
+ end
35
+
36
+ pc
37
+ end
38
+
39
+ def eql?(pitch_class_set)
40
+ return false if @pitch_classes.size != pitch_class_set.pitch_classes.size
41
+
42
+ @normalized_pitch_classes.each_with_index do |pitch_class, i|
43
+ return false if !pitch_class_set.normal_form.pitch_classes[i].eql?(pitch_class)
44
+ end
45
+
46
+ true
47
+ end
48
+
49
+ def ==(pitch_class_set)
50
+ eql? pitch_class_set
51
+ end
52
+
53
+ def to_s
54
+ "PitchClassSet: #{@pitch_classes}"
55
+ end
56
+
57
+ # Returns the normal form of an array of pitch classes
58
+ def self.to_normal_form(pitch_classes)
59
+ normal = pitch_classes.sort { |x, y| x.value <=> y.value }
60
+
61
+ newLen = normal.last.value - normal.first.value
62
+ newLen += 12 if newLen < 0
63
+ newLen += 12 if newLen == 1 && normal.size > 2
64
+
65
+ shortest = {:array => normal.dup, :length => newLen}
66
+
67
+ 0.upto(normal.size - 1) do |q|
68
+ # Rotate
69
+ normal.push normal.shift
70
+
71
+ newLen = normal.last.value - normal.first.value
72
+ newLen += 12 if newLen < 0
73
+ newLen += 12 if newLen == 1 && normal.size > 2
74
+
75
+ if newLen < shortest[:length]
76
+ shortest = {:array => normal.dup, :length => newLen}
77
+ elsif newLen == shortest[:length]
78
+ (normal.size - 1).downto(q) do |r|
79
+
80
+ newLen = normal[r].value - normal.first.value
81
+ newLen += 12 if newLen < 0
82
+ newLen += 12 if newLen == 1 && normal.size > 2
83
+
84
+ sNewLen = shortest[:array][r].value - shortest[:array].first.value
85
+ sNewLen += 12 if sNewLen < 0
86
+ sNewLen += 12 if sNewLen == 1 && normal.size > 2
87
+
88
+ if newLen < sNewLen
89
+ shortest = {:array => normal.dup, :length => newLen}
90
+ break
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ shortest[:array]
97
+ end
98
+
99
+ # Returns a PitchClassSet of the inversino of the current PitchClassSet
100
+ def inversion
101
+ self.class.new(@inverted_pitch_classes)
102
+ end
103
+
104
+ # Returns a PitchClassSet of the current PitchClassSet in normal form
105
+ def normal_form
106
+ self.class.new(@normalized_pitch_classes)
107
+ end
108
+
109
+ # Transposes the set by a degree (integer)
110
+ # Returns PitchClassSet of the transposed set
111
+ def transpose(degree)
112
+ transposed = self.class.new
113
+
114
+ @pitch_classes.each do |pitch_class|
115
+ oct = pitch_class.octave
116
+ val = pitch_class.value
117
+
118
+ val += degree
119
+
120
+ oct += val / 12
121
+ oct -= 1 if val < 0 && val % 12 == 0
122
+
123
+ transposed.add_pitch(val, oct)
124
+ end
125
+
126
+ transposed
127
+ end
128
+
129
+ private
130
+
131
+ # Inverts an array of pitch classes. The PitchClass attribute octave may become invalid after inversion.
132
+ # Returns a PitchClassSet of the inverted pitch classes
133
+ def invert(pitch_classes)
134
+ inverted = []
135
+
136
+ pitch_classes.each do |pitch_class|
137
+ pc = PitchClass.new(12 - pitch_class.value, pitch_class.octave)
138
+ inverted << pc
139
+ end
140
+
141
+ inverted
142
+ end
143
+
144
+ end
145
+ end
@@ -0,0 +1,19 @@
1
+ module PostTonal
2
+ class PitchInterval
3
+
4
+ attr_reader :pitch1, :pitch2
5
+
6
+ def initialize(pitch1, pitch2)
7
+ @pitch1 = pitch1
8
+ @pitch2 = pitch2
9
+ end
10
+
11
+ def ordered
12
+ pitch1.int_value - pitch2.int_value
13
+ end
14
+
15
+ def unordered
16
+ (pitch1.int_value - pitch2.int_value).abs
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module PostTonal
2
+ VERSION = '0.1.0.pre'
3
+ end
data/lib/post_tonal.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'post_tonal/note_parser'
2
+ require 'post_tonal/pitch_class'
3
+ require 'post_tonal/pitch_class_interval'
4
+ require 'post_tonal/pitch_class_set'
@@ -0,0 +1,21 @@
1
+ # coding: utf-8
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "post_tonal/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'post_tonal'
7
+ s.version = PostTonal::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.author = 'Eric Rubio'
10
+ s.email = 'penmanglewood@fastmail.fm'
11
+ s.summary = %q{Pitch-class set analysis library}
12
+ s.description = %q{Ruby library for analyzing pitch-class sets according to post-tonal theory}
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- test/{functional,unit}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.require_paths = ["lib"]
17
+
18
+ s.add_development_dependency "shoulda", ">= 2.1.1"
19
+ s.add_development_dependency "mocha", ">= 0.9.5"
20
+ s.add_development_dependency "rake"
21
+ end
@@ -0,0 +1,3 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
2
+ require 'test/unit'
3
+ require 'post_tonal'
@@ -0,0 +1,56 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
2
+
3
+ class NoteParserTest < Test::Unit::TestCase
4
+
5
+ def test_valid_int_values
6
+ (0..11).each do |i|
7
+ @p = PostTonal::NoteParser.integer_value(i)
8
+ assert_equal(i, @p, "0-11 should be valid")
9
+ end
10
+ end
11
+
12
+ def test_mod12_int_values
13
+ (12..23).each do |i|
14
+ @p = PostTonal::NoteParser.integer_value(i)
15
+ assert_equal(i-12, @p, "12-23 should be valid")
16
+ end
17
+ end
18
+
19
+ def test_negative_int_values
20
+ (-11..0).each do |i|
21
+ @p = PostTonal::NoteParser.integer_value(i)
22
+ assert_equal(i.abs, @p, "(-11)-0 should count as 11-0")
23
+ end
24
+ end
25
+
26
+ def test_valid_str_values
27
+ PostTonal::NoteParser::NAME_TO_INT.each do |k, v|
28
+ @p = PostTonal::NoteParser.integer_value(k)
29
+ @q = PostTonal::NoteParser.integer_value(k.upcase)
30
+ assert_equal(v, @p, "Note names should be valid 1")
31
+ assert_equal(v, @q, "Note names should be valid 2")
32
+ end
33
+ end
34
+
35
+ def test_invalid_str_values
36
+ ('h'..'z').each do |z|
37
+ @p = PostTonal::NoteParser.integer_value(z)
38
+ assert_equal(nil, @p, "h-z should be invalid")
39
+ end
40
+ end
41
+
42
+ def test_note_names
43
+ assert_equal('c', PostTonal::NoteParser.note_name(0), '0 should be c')
44
+ assert_equal('c#', PostTonal::NoteParser.note_name(1), '1 should be c#')
45
+ assert_equal('d', PostTonal::NoteParser.note_name(2), '2 should be d')
46
+ assert_equal('d#', PostTonal::NoteParser.note_name(3), '3 should be d#')
47
+ assert_equal('e', PostTonal::NoteParser.note_name(4), '4 should be e')
48
+ assert_equal('f', PostTonal::NoteParser.note_name(5), '5 should be f')
49
+ assert_equal('f#', PostTonal::NoteParser.note_name(6), '6 should be f#')
50
+ assert_equal('g', PostTonal::NoteParser.note_name(7), '7 should be g')
51
+ assert_equal('g#', PostTonal::NoteParser.note_name(8), '8 should be g#')
52
+ assert_equal('a', PostTonal::NoteParser.note_name(9), '9 should be a')
53
+ assert_equal('a#', PostTonal::NoteParser.note_name(10), '10 should be a#')
54
+ assert_equal('b', PostTonal::NoteParser.note_name(11), '11 should be b')
55
+ end
56
+ end
@@ -0,0 +1,38 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
2
+
3
+ class PitchClassIntervalTest < Test::Unit::TestCase
4
+
5
+ def test_unordered_intervals
6
+ (0..11).each do |i|
7
+ @p1 = PostTonal::PitchClass.new(i)
8
+ (0..11).each do |j|
9
+ @p2 = PostTonal::PitchClass.new(j)
10
+ @interval = PostTonal::PitchClassInterval.new(@p1, @p2)
11
+ expected = (@p1.value - @p2.value).abs > 6 ? 12 - (@p1.value - @p2.value).abs : (@p1.value - @p2.value).abs
12
+ assert_equal(expected, @interval.unordered, "p1: #{i}, p2: #{j}") if i != j
13
+ end
14
+ end
15
+ end
16
+
17
+ def test_ordered_intervals
18
+
19
+ #Same octave ordered intervals
20
+ (0..11).each do |i|
21
+ @p1 = PostTonal::PitchClass.new(i)
22
+ (0..11).each do |j|
23
+ @p2 = PostTonal::PitchClass.new(j)
24
+ @interval = PostTonal::PitchClassInterval.new(@p1, @p2)
25
+ expected = j > i ? @p2.value - @p1.value : @p1.value - @p2.value
26
+ assert_equal(expected, @interval.ordered, "p1: #{i}, p2: #{j}") if i != j
27
+ end
28
+ end
29
+
30
+ #Cross-octave ordered intervals
31
+ @p3 = PostTonal::PitchClass.new(11)
32
+ (0...11).each do |j|
33
+ @p4 = PostTonal::PitchClass.new(j, 1)
34
+ @interval = PostTonal::PitchClassInterval.new(@p3, @p4)
35
+ assert_equal(j+1, @interval.ordered, "Cross-Octave p1: 11, p2: #{j}")
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,159 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
2
+
3
+ class PitchClassSetTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @pitch_class_set = PostTonal::PitchClassSet.new
7
+ end
8
+
9
+ def teardown
10
+ @pitch_class_set = nil
11
+ end
12
+
13
+ def test_duplicate_assignment
14
+ @pitch_class_set.add_pitch(0)
15
+ @pitch_class_set.add_pitch(0)
16
+ @pitch_class_set.add_pitch(0)
17
+
18
+ assert_equal(1, @pitch_class_set.pitch_classes.size)
19
+ end
20
+
21
+ def test_nonduplicate_assignment
22
+ @pitch_class_set.add_pitch(0)
23
+ @pitch_class_set.add_pitch(1)
24
+ @pitch_class_set.add_pitch(0, 1)
25
+
26
+ assert_equal(3, @pitch_class_set.pitch_classes.size)
27
+ end
28
+
29
+ def test_equality
30
+ @pitch_class_set.add_pitch(0)
31
+ @pitch_class_set.add_pitch(1)
32
+ @pitch_class_set.add_pitch(2)
33
+
34
+ @p1 = PostTonal::PitchClassSet.new
35
+ @p1.add_pitch(0)
36
+ @p1.add_pitch(1)
37
+ @p1.add_pitch(2)
38
+
39
+ assert(@pitch_class_set.eql?(@p1), "eql? should be true")
40
+ assert(@pitch_class_set == @p1, "== should be true")
41
+ end
42
+
43
+ def test_normal_form
44
+
45
+ @p1 = PostTonal::PitchClassSet.new
46
+ @p1.add_pitch(0)
47
+ @p1.add_pitch(1)
48
+ @p1.add_pitch(2)
49
+
50
+ [0,1,2].each do |n|
51
+ assert_equal(@p1.pitch_classes[n], @p1.normal_form.pitch_classes[n])
52
+ end
53
+
54
+ @p2 = PostTonal::PitchClassSet.new
55
+ @p2.add_pitch(0)
56
+ @p2.add_pitch(10)
57
+ @p2.add_pitch(11)
58
+
59
+ [10,11,0].each_with_index do |n, i|
60
+ assert_equal(n, @p2.normal_form.pitch_classes[i].value)
61
+ end
62
+
63
+ @p3 = PostTonal::PitchClassSet.new
64
+ @p3.add_pitch(0)
65
+ @p3.add_pitch(3)
66
+ @p3.add_pitch(4)
67
+ @p3.add_pitch(9)
68
+
69
+ [9,0,3,4].each_with_index do |n, i|
70
+ assert_equal(n, @p3.normal_form.pitch_classes[i].value)
71
+ end
72
+
73
+ @p4 = PostTonal::PitchClassSet.new
74
+ @p4.add_pitch(0)
75
+ @p4.add_pitch(3)
76
+ @p4.add_pitch(4)
77
+ @p4.add_pitch(8)
78
+
79
+ [0,3,4,8].each_with_index do |n, i|
80
+ assert_equal(n, @p4.normal_form.pitch_classes[i].value)
81
+ end
82
+
83
+ @p5 = PostTonal::PitchClassSet.new
84
+ @p5.add_pitch(0)
85
+ @p5.add_pitch(1)
86
+ @p5.add_pitch(10)
87
+ @p5.add_pitch(11)
88
+
89
+ [10,11,0,1].each_with_index do |n, i|
90
+ assert_equal(n, @p5.normal_form.pitch_classes[i].value)
91
+ end
92
+
93
+ @p6 = PostTonal::PitchClassSet.new
94
+ @p6.add_pitch(0)
95
+ @p6.add_pitch(1)
96
+ @p6.add_pitch(8)
97
+ @p6.add_pitch(4)
98
+
99
+ [0,1,4,8].each_with_index do |n, i|
100
+ assert_equal(n, @p6.normal_form.pitch_classes[i].value)
101
+ end
102
+
103
+ @p7 = PostTonal::PitchClassSet.new
104
+ @p7.add_pitch(0)
105
+ @p7.add_pitch(3)
106
+ @p7.add_pitch(6)
107
+ @p7.add_pitch(9)
108
+
109
+ [0,3,6,9].each_with_index do |n, i|
110
+ assert_equal(n, @p7.normal_form.pitch_classes[i].value)
111
+ end
112
+
113
+ @p7 = PostTonal::PitchClassSet.new
114
+ @p7.add_pitch(0)
115
+ @p7.add_pitch(6)
116
+ @p7.add_pitch(7)
117
+
118
+ [6,7,0].each_with_index do |n, i|
119
+ assert_equal(n, @p7.normal_form.pitch_classes[i].value)
120
+ end
121
+ end
122
+
123
+ def test_inversion
124
+ @p1 = PostTonal::PitchClassSet.new
125
+ 0.upto(11) do |i|
126
+ @p1.add_pitch(i)
127
+ end
128
+
129
+ @p1i = PostTonal::PitchClassSet.new
130
+ @p1i.add_pitch(0)
131
+ 11.downto(1) do |i|
132
+ @p1i.add_pitch(i)
133
+ end
134
+
135
+ assert_equal(@p1i, @p1.inversion, "Should be inverted")
136
+ end
137
+
138
+ def test_transposition
139
+ @p1 = PostTonal::PitchClassSet.new
140
+ 0.upto(11) do |i|
141
+ @p1.add_pitch(i)
142
+ end
143
+
144
+ #transposition degree
145
+ 0.upto(200) do |t|
146
+ @p1t = PostTonal::PitchClassSet.new
147
+ t.upto(t+11) do |i|
148
+ oct = 0
149
+ oct = i / 12
150
+ oct -= 1 if i < 0 && i % 12 == 0
151
+
152
+ @p1t.add_pitch(i, oct)
153
+ end
154
+
155
+ assert_equal(@p1t, @p1.transpose(t), "Should transpose #{t} degrees")
156
+ end
157
+ end
158
+
159
+ end
@@ -0,0 +1,49 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
2
+
3
+ class PitchClassTest < Test::Unit::TestCase
4
+
5
+ def test_valid_int_values
6
+ (0..11).each do |i|
7
+ @p = PostTonal::PitchClass.new(i)
8
+ assert_equal(i, @p.value, "0-11 should be valid")
9
+ end
10
+ end
11
+
12
+ def test_mod12_int_values
13
+ (12..23).each do |i|
14
+ @p = PostTonal::PitchClass.new(i)
15
+ assert_equal(i-12, @p.value, "12-23 should be valid")
16
+ end
17
+ end
18
+
19
+ def test_negative_int_values
20
+ (-11..0).each do |i|
21
+ @p = PostTonal::PitchClass.new(i)
22
+ assert_equal(i.abs, @p.value, "(-11)-0 should count as 11-0")
23
+ end
24
+ end
25
+
26
+ def test_valid_str_values
27
+ PostTonal::NoteParser::NAME_TO_INT.each do |k, v|
28
+ @p = PostTonal::PitchClass.new(k)
29
+ @q = PostTonal::PitchClass.new(k.upcase)
30
+ assert_equal(v, @p.value, "Note names should be valid 1")
31
+ assert_equal(v, @q.value, "Note names should be valid 2")
32
+ end
33
+ end
34
+
35
+ def test_invalid_str_values
36
+ ('h'..'z').each do |z|
37
+ @p = PostTonal::PitchClass.new(z)
38
+ assert_equal(nil, @p.value, "h-z should be invalid")
39
+ end
40
+ end
41
+
42
+ def test_octave
43
+ (0..11).each do |i|
44
+ @p = PostTonal::PitchClass.new(i, i)
45
+ assert_equal(i, @p.octave, "0-11 should be valid octave values")
46
+ end
47
+ end
48
+
49
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: post_tonal
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.pre
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Eric Rubio
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: shoulda
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 2.1.1
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 2.1.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: mocha
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.9.5
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.9.5
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Ruby library for analyzing pitch-class sets according to post-tonal theory
63
+ email: penmanglewood@fastmail.fm
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files: []
67
+ files:
68
+ - .gitignore
69
+ - CHANGELOG.md
70
+ - README.md
71
+ - Rakefile
72
+ - lib/post_tonal.rb
73
+ - lib/post_tonal/note_parser.rb
74
+ - lib/post_tonal/pitch_class.rb
75
+ - lib/post_tonal/pitch_class_interval.rb
76
+ - lib/post_tonal/pitch_class_set.rb
77
+ - lib/post_tonal/pitch_interval.rb
78
+ - lib/post_tonal/version.rb
79
+ - post_tonal.gemspec
80
+ - test/test_helper.rb
81
+ - test/unit/note_parser_test.rb
82
+ - test/unit/pitch_class_interval_test.rb
83
+ - test/unit/pitch_class_set_test.rb
84
+ - test/unit/pitch_class_test.rb
85
+ homepage:
86
+ licenses: []
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>'
101
+ - !ruby/object:Gem::Version
102
+ version: 1.3.1
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 1.8.24
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: Pitch-class set analysis library
109
+ test_files:
110
+ - test/unit/note_parser_test.rb
111
+ - test/unit/pitch_class_interval_test.rb
112
+ - test/unit/pitch_class_set_test.rb
113
+ - test/unit/pitch_class_test.rb