post_tonal 0.1.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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