interval 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 YOUR NAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.mkd ADDED
@@ -0,0 +1,17 @@
1
+ # Interval
2
+
3
+ Interval is a tiny library that provides simple musicial note pitch and interval arithmetic.
4
+
5
+ Observe:
6
+
7
+ >> p = Interval::Pitch.from_string("c")
8
+ >> i = Interval::Interval.from_string("M3")
9
+ >> p2 = p + i
10
+ >> p2.to_short_name
11
+ => "e"
12
+
13
+ # Authors
14
+
15
+ Nate Murray 2009
16
+
17
+ based on code from GNU Solfege
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
2
+ require 'rake/gempackagetask'
3
+ require 'rubygems/specification'
4
+ require 'date'
5
+ require 'spec/rake/spectask'
6
+
7
+ GEM = "interval"
8
+ GEM_VERSION = "0.0.1"
9
+
10
+ spec = Gem::Specification.new do |s|
11
+ s.name = GEM
12
+ s.version = GEM_VERSION
13
+ s.author = "Nate Murray"
14
+ s.email = "nate@natemurray.com"
15
+ s.homepage = "http://www.xcombinator.com"
16
+ s.description = s.summary = "interval is a tiny library that provides simple musicial note pitch and interval arithmetic."
17
+
18
+ s.platform = Gem::Platform::RUBY
19
+ s.has_rdoc = true
20
+ s.extra_rdoc_files = ["README.mkd", "LICENSE"]
21
+
22
+ # Uncomment this to add a dependency
23
+ # s.add_dependency "foo"
24
+
25
+ s.require_path = 'lib'
26
+ s.autorequire = GEM
27
+ s.files = %w(LICENSE README.mkd Rakefile) + Dir.glob("{lib,spec}/**/*")
28
+ end
29
+
30
+ task :default => :spec
31
+
32
+ desc "Run specs"
33
+ Spec::Rake::SpecTask.new do |t|
34
+ t.spec_files = FileList['spec/**/*_spec.rb']
35
+ t.spec_opts = %w(-fs --color)
36
+ end
37
+
38
+
39
+ Rake::GemPackageTask.new(spec) do |pkg|
40
+ pkg.gem_spec = spec
41
+ end
42
+
43
+ desc "install the gem locally"
44
+ task :install => [:package] do
45
+ sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
46
+ end
47
+
48
+ desc "create a gemspec file"
49
+ task :make_spec do
50
+ File.open("#{GEM}.gemspec", "w") do |file|
51
+ file.puts spec.to_ruby
52
+ end
53
+ end
data/lib/interval.rb ADDED
@@ -0,0 +1,318 @@
1
+ class Array
2
+ def rand
3
+ self[super(self.length)]
4
+ end
5
+ end
6
+
7
+ module Interval
8
+ class Pitch
9
+ NOTE_NAMES = %w{c d e f g a b}
10
+ NOTE_NAMES_TO_I = {'c' => 0, 'd' => 1, 'e' => 2, 'f' => 3, 'g' => 4, 'a' => 5, 'b' => 6}
11
+
12
+ attr_accessor :octave
13
+ attr_accessor :notename_i
14
+ attr_accessor :accidental
15
+
16
+ class << self
17
+ def from_string(str)
18
+ p = new
19
+ ary = str.split(//)
20
+ possible_notename = ary.shift.downcase
21
+ raise "invalid notename: #{possible_notename}" unless Pitch::NOTE_NAMES_TO_I.has_key?(possible_notename)
22
+ p.notename_i = note_s_to_i(possible_notename)
23
+
24
+ octave = 0
25
+ accidental = 0
26
+ while(char = ary.shift)
27
+ octave = octave + 1 if char == "'"
28
+ octave = octave - 1 if char == ","
29
+ accidental = accidental + 1 if char == "#"
30
+ accidental = accidental - 1 if char == "b"
31
+ end
32
+ p.octave = octave
33
+ p.accidental = accidental
34
+ p
35
+ end
36
+
37
+ def from_int(midiint)
38
+ p = new
39
+ p.octave = (midiint - 48) / 12
40
+ p.notename_i = {0 => 0, 1 => 0,
41
+ 2 => 1, 3 => 1,
42
+ 4 => 2,
43
+ 5 => 3, 6 => 3,
44
+ 7 => 4, 8 => 4,
45
+ 9 => 5, 10 => 5,
46
+ 11 => 6}[midiint % 12]
47
+ p.accidental = midiint - (p.octave + 4) * 12 - ([0, 2, 4, 5, 7, 9, 11][p.notename_i])
48
+ p
49
+ end
50
+
51
+ def notename_s(s)
52
+ NOTE_NAMES[s]
53
+ end
54
+
55
+ # if normal=true then only return notes you would "normally" see.
56
+ # what is the word for that, natural?
57
+ def random(normal=false)
58
+ p = new
59
+ p.notename_i = NOTE_NAMES_TO_I.values.rand
60
+ p.octave = 0
61
+
62
+ accidentals = []
63
+ if normal
64
+ accidentals = case p.notename
65
+ when 'c', 'f'
66
+ [0, 1]
67
+ when 'e', 'b'
68
+ [-1, 0]
69
+ else [-1, 0, 1]
70
+ end
71
+ else
72
+ accidentals = [-1, 0, 1]
73
+ end
74
+ p.accidental = accidentals.rand
75
+ p
76
+ end
77
+
78
+ # def notename_s(s)
79
+ # {0 => 'c', 1 => 'c',
80
+ # 2 => 'd', 3 => 'd',
81
+ # 4 => 'e',
82
+ # 5 => 'f', 6 => 'f',
83
+ # 7 => 'g', 8 => 'g',
84
+ # 9 => 'a', 10 => 'a',
85
+ # 11 => 'c'}[s]
86
+ # end
87
+
88
+ # i dont think this is right. also, just start storing notename as an integer. make it a lot easier
89
+ # this isn't notename_i!!! this is some strange mididint accidental array. todo, redo this
90
+ # def pitchy(i)
91
+ # notes =
92
+ # {'c' => 0,
93
+ # 'd' => 2,
94
+ # 'e' => 4,
95
+ # 'f' => 5,
96
+ # 'g' => 6,
97
+ # 'a' => 9,
98
+ # 'b' => 11}
99
+ # notes[i]
100
+ # end
101
+
102
+ # def note_i_to_s(i)
103
+ # NOTENAMES[i]
104
+ # end
105
+
106
+ def note_s_to_i(s)
107
+ NOTE_NAMES_TO_I[s]
108
+ end
109
+
110
+ def accidental_name(i)
111
+ names =
112
+ { -2 => " double flat",
113
+ -1 => " flat",
114
+ 0 => "",
115
+ 1 => " sharp",
116
+ 2 => " double sharp" }
117
+ names[i]
118
+ end
119
+ end
120
+
121
+ def semitone_pitch
122
+ [0, 2, 4, 5, 7, 9, 11][notename_i] + accidental + (octave * 12) + 48
123
+ end
124
+
125
+ # def notename_i
126
+ # self.class.notename_i(self.notename)
127
+ # end
128
+
129
+ # def notename_i=(i)
130
+ # end
131
+
132
+ def notename
133
+ self.class.notename_s(notename_i)
134
+ end
135
+
136
+ def to_long_name
137
+ "%s%s" % [notename.upcase, self.class.accidental_name(accidental)]
138
+ end
139
+
140
+ def to_short_name
141
+ "%s%s" % [notename, accidental > 0 ? ('#' * accidental.abs) : ('b' * accidental.abs)]
142
+ end
143
+
144
+ def to_s
145
+ to_short_name
146
+ end
147
+
148
+ def +(other)
149
+ if other.kind_of?(Interval)
150
+ plus_interval(other)
151
+ else
152
+ raise "todo"
153
+ end
154
+ end
155
+
156
+ def -(other)
157
+ if other.kind_of?(Interval)
158
+ minus_interval(other)
159
+ else
160
+ raise "todo"
161
+ end
162
+ end
163
+
164
+ private
165
+ def plus_interval(other)
166
+ r = self.dup
167
+ _p = r.semitone_pitch
168
+ r.notename_i = r.notename_i + (other.interval - 1) * other.direction
169
+ r.octave = r.octave + r.notename_i / 7 + other.octave * other.direction
170
+ r.notename_i = r.notename_i % 7
171
+ _diff = r.semitone_pitch - _p
172
+ r.accidental = r.accidental + (other.to_i - _diff)
173
+ r
174
+ end
175
+ def minus_interval(other)
176
+ other2 = other.dup
177
+ other2.direction = other2.direction * -1
178
+ plus_interval(other2)
179
+ end
180
+ end
181
+
182
+ class Interval
183
+ attr_accessor :direction # value to be 1 or -1
184
+ attr_accessor :octave # 0, 1, 2, 3 etc
185
+ attr_accessor :interval # 1:unison, 2:second, ... 7: seventh
186
+ # unison: dim perfect aug
187
+ # second: -2:dim -1:minor 0:major 1:aug
188
+ # third: dim minor major aug
189
+ # fourth: dim perfect aug
190
+ # fifth: dim perfect aug
191
+ # sixth: dim minor major aug
192
+ # seventh: dim minor major aug
193
+ attr_accessor :mod # -2:dim -1:minor 0:major 1:aug
194
+
195
+ QUALITY_LONG_NAMES = {
196
+ 'p' => 'Perfect',
197
+ 'dd' => 'Doubly-diminished',
198
+ 'd' => 'Diminished',
199
+ 'm' => 'Minor',
200
+ 'M' => 'Major',
201
+ 'a' => 'Augmented',
202
+ 'aa' => 'Doubly-augmented',
203
+ }
204
+
205
+ SIZE_LONG_NAMES = %w{x
206
+ Unison
207
+ Second
208
+ Third
209
+ Fourth
210
+ Fifth
211
+ Sixth
212
+ Seventh
213
+ Octave
214
+ }
215
+
216
+ class << self
217
+ # Number of
218
+ # semitones name enharmonic notes
219
+ # 0 Perfect Unison (P1) Diminished second (dim2)
220
+ # 1 Minor second (m2) Augmented unison (aug1)
221
+ # 2 Major second (M2) Diminished third (dim3)
222
+ # 3 Minor third (m3) Augmented second (aug2)
223
+ # 4 Major third (M3) Diminished fourth (dim4)
224
+ # 5 Perfect fourth (P4) Augmented third (aug3)
225
+ # 6 Tritone Augmented fourth (aug4) Diminished fifth (dim5)
226
+ # 7 Perfect fifth (P5) Diminished sixth (dim6)
227
+ # 8 Minor sixth (m6) Augmented fifth (aug5)
228
+ # 9 Major sixth (M6) Diminished seventh (dim7)
229
+ # 10 Minor seventh (m7) Augmented sixth (aug6)
230
+ # 11 Major seventh (M7) Diminished octave (dim8)
231
+ # 12 Perfect octave (P8) Augmented seventh (aug7)
232
+
233
+ # unison p1
234
+ # second m2 M2
235
+ # third m3 M3
236
+ # fourth p4
237
+ # fifth p5
238
+ # sixth m6 M6
239
+ # seventh m7 M7
240
+ # octave p8
241
+ def from_string(str)
242
+ i = new
243
+ i.direction = str[0,1] == "-" ? -1 : 1
244
+ str =~ /([mMdap])(\d+)/
245
+ modifier, size = $1,$2
246
+ raise "#{str} is not a valid interval" unless modifier && size
247
+ size = size.to_i
248
+ if size <= 7
249
+ i.octave = 0
250
+ else
251
+ i.octave = size / 8 # ?
252
+ end
253
+ i.interval = size - (i.octave * 7)
254
+
255
+ i.mod = case i.interval
256
+ when 2, 3, 6, 7
257
+ {"d" => -2, "m" => -1, "M" => 0, "a" => 1}[modifier]
258
+ when 1, 4, 5, 8
259
+ {"d" => -1, "p" => 0, "a" => 1}[modifier]
260
+ else
261
+ raise "unknown interval"
262
+ end
263
+
264
+ i
265
+ end
266
+
267
+ def from_int(number)
268
+ interval = new
269
+ interval.direction = number >= 0 ? 1 : -1
270
+ interval.octave = number.abs / 12
271
+ mod, intervali = *([
272
+ [0, 1], # unison
273
+ [-1, 2], [0, 2], # second
274
+ [-1, 3], [0, 3], # third
275
+ [0, 4], # fourth
276
+ [-1, 5], # dim 5, tritone
277
+ [0, 5], # fifth
278
+ [-1, 6], [0, 6], # sixth
279
+ [-1, 7], [0, 7]])[number.abs % 12] # seventh
280
+ interval.mod = mod
281
+ interval.interval = intervali
282
+ interval
283
+ end
284
+
285
+
286
+ end
287
+
288
+ def to_long_name
289
+ short_modifier = case self.interval
290
+ when 2, 3, 6, 7
291
+ {-2 => "d", -1 => "m", 0 => "M", 1 => "a"}[self.mod]
292
+ when 1, 4, 5, 8
293
+ {-1 => "d", 0 => "p", 1 => "a"}[self.mod]
294
+ else
295
+ raise "unknown interval"
296
+ end
297
+
298
+ long_modifier = QUALITY_LONG_NAMES[short_modifier]
299
+ size = SIZE_LONG_NAMES[self.interval]
300
+ size = "Octave" if interval == 1 && octave > 0
301
+
302
+ # tritone (diminished fifth) has the following: todo
303
+ #<Interval::Interval:0x2efa90 @mod=0, @interval=5, @octave=0, @direction=1>
304
+
305
+ return "%s %s" % [long_modifier, size]
306
+
307
+ end
308
+
309
+ def to_s
310
+ to_long_name
311
+ end
312
+
313
+ def to_i
314
+ return ([0, 0, 2, 4, 5, 7, 9, 11][interval] + octave * 12 + mod) * direction
315
+ end
316
+
317
+ end
318
+ end
@@ -0,0 +1,402 @@
1
+ $:.unshift(File.dirname(__FILE__) + "/../lib")
2
+ require File.dirname(__FILE__) + '/spec_helper'
3
+ require "interval"
4
+ require "pp"
5
+
6
+ include Interval
7
+
8
+ describe "interval" do
9
+ it "should create from int" do
10
+ test_set = {
11
+ 0 => "Perfect Unison",
12
+ 1 => "Minor second",
13
+ 2 => "Major second",
14
+ 3 => "Minor third",
15
+ 4 => "Major third",
16
+ 5 => "Perfect fourth",
17
+ 6 => "Diminished fifth",
18
+ 7 => "Perfect fifth",
19
+ 8 => "Minor sixth",
20
+ 9 => "Major sixth",
21
+ 10 => "Minor seventh",
22
+ 11 => "Major seventh",
23
+ 12 => "Perfect octave"}
24
+
25
+ test_set.keys.sort.each do |k|
26
+ value = test_set[k]
27
+ v = Interval::Interval.from_int(k)
28
+ v.to_long_name.downcase.should == value.downcase
29
+ v.to_i.should == k
30
+ end
31
+ end
32
+
33
+ it "should create from string" do
34
+ test_set = [
35
+ ["p1", "Perfect Unison"],
36
+ ["m2", "Minor second"],
37
+ ["M2", "Major second"],
38
+ ["m3", "Minor third"],
39
+ ["M3", "Major third"],
40
+ ["p4", "Perfect fourth"],
41
+ ["d5", "Diminished fifth"],
42
+ ["p5", "Perfect fifth"],
43
+ ["m6", "Minor sixth"],
44
+ ["M6", "Major sixth"],
45
+ ["m7", "Minor seventh"],
46
+ ["M7", "Major seventh"],
47
+ ["p8", "Perfect octave"]]
48
+ test_set.each do |key,value|
49
+ v = Interval::Interval.from_string(key)
50
+ v.to_long_name.downcase.should == value.downcase
51
+ end
52
+ end
53
+ end
54
+
55
+ describe "pitch" do
56
+ it "should create from string and integer" do
57
+
58
+ p = Interval::Pitch.from_string("c")
59
+ p.notename_i.should == 0
60
+ p.notename.should == "c"
61
+ p.accidental.should == 0
62
+ p.octave.should == 0
63
+
64
+ p2 = Interval::Pitch.from_int(p.semitone_pitch)
65
+ p2.notename.should == "c"
66
+ p2.accidental.should == 0
67
+ p2.octave.should == 0
68
+
69
+ p2.to_long_name.should == "C"
70
+ p2.to_short_name.should == "c"
71
+
72
+ p.semitone_pitch.should == p2.semitone_pitch
73
+
74
+ p = Interval::Pitch.from_string("c#'")
75
+ p.notename.should == "c"
76
+ p.accidental.should == 1
77
+ p.octave.should == 1
78
+
79
+ p2 = Interval::Pitch.from_int(p.semitone_pitch)
80
+ p2.notename.should == "c"
81
+ p2.accidental.should == 1
82
+ p2.octave.should == 1
83
+
84
+ p2.to_long_name.should == "C sharp"
85
+ p2.to_short_name.should == "c#"
86
+
87
+ p.semitone_pitch.should == p2.semitone_pitch
88
+
89
+ p = Interval::Pitch.from_string("eb,,")
90
+ p.notename.should == "e"
91
+ p.accidental.should == -1
92
+ p.octave.should == -2
93
+
94
+ p2 = Interval::Pitch.from_int(p.semitone_pitch)
95
+
96
+ p2.notename.should == "d"
97
+ p2.accidental.should == 1
98
+ p2.octave.should == -2
99
+
100
+ p2.to_long_name.should == "D sharp"
101
+ p2.to_short_name.should == "d#"
102
+
103
+ p.semitone_pitch.should == p2.semitone_pitch
104
+ end
105
+
106
+ describe "adding intervals" do
107
+
108
+ it "should be able to be added to an interval" do
109
+ p = Pitch.from_string("c")
110
+ i = Interval::Interval.from_string("M3")
111
+ p2 = p + i
112
+ p2.to_short_name.should == "e"
113
+ end
114
+
115
+ it "should add minor seconds" do
116
+ i = Interval::Interval.from_string("m2")
117
+ [["c", "db"],
118
+ ["f", "gb"],
119
+ ["Bb", "cb"],
120
+ ["eb", "fb"],
121
+ ["ab", "bbb"],
122
+ ["db", "ebb"],
123
+ ["gb", "abb"],
124
+ ["f#", "g"],
125
+ ["b", "c"],
126
+ ["e", "f"],
127
+ ["a", "bb"],
128
+ ["d", "eb"],
129
+ ["g", "ab"]
130
+ ].each do |pitch, new_pitch|
131
+ (Pitch.from_string(pitch) + i).to_short_name.should == new_pitch
132
+ end
133
+ end
134
+
135
+ it "should add major seconds" do
136
+ i = Interval::Interval.from_string("M2")
137
+ [["c", "d"],
138
+ ["f", "g"],
139
+ ["Bb", "c"],
140
+ ["eb", "f"],
141
+ ["ab", "bb"],
142
+ ["db", "eb"],
143
+ ["gb", "ab"],
144
+ ["f#", "g#"],
145
+ ["b", "c#"],
146
+ ["e", "f#"],
147
+ ["a", "b"],
148
+ ["d", "e"],
149
+ ["g", "a"]
150
+ ].each do |pitch, new_pitch|
151
+ (Pitch.from_string(pitch) + i).to_short_name.should == new_pitch
152
+ end
153
+ end
154
+
155
+ it "should add minor thirds" do
156
+ i = Interval::Interval.from_string("m3")
157
+ [["c", "eb"],
158
+ ["f", "ab"],
159
+ ["Bb", "db"],
160
+ ["eb", "gb"],
161
+ ["ab", "cb"],
162
+ ["db", "fb"],
163
+ ["gb", "bbb"],
164
+ ["f#", "a"],
165
+ ["b", "d"],
166
+ ["e", "g"],
167
+ ["a", "c"],
168
+ ["d", "f"],
169
+ ["g", "bb"]
170
+ ].each do |pitch, new_pitch|
171
+ (Pitch.from_string(pitch) + i).to_short_name.should == new_pitch
172
+ end
173
+ end
174
+
175
+ it "should add major thirds" do
176
+ i = Interval::Interval.from_string("M3")
177
+ [["c", "e"],
178
+ ["f", "a"],
179
+ ["Bb", "d"],
180
+ ["eb", "g"],
181
+ ["ab", "c"],
182
+ ["db", "f"],
183
+ ["gb", "bb"],
184
+ ["f#", "a#"],
185
+ ["b", "d#"],
186
+ ["e", "g#"],
187
+ ["a", "c#"],
188
+ ["d", "f#"],
189
+ ["g", "b"]
190
+ ].each do |pitch, new_pitch|
191
+ (Pitch.from_string(pitch) + i).to_short_name.should == new_pitch
192
+ end
193
+ end
194
+
195
+ it "should add perfect fourths" do
196
+ i = Interval::Interval.from_string("p4")
197
+ [["c", "f"],
198
+ ["f", "bb"],
199
+ ["Bb", "eb"],
200
+ ["eb", "ab"],
201
+ ["ab", "db"],
202
+ ["db", "gb"],
203
+ ["gb", "cb"],
204
+ ["f#", "b"],
205
+ ["b", "e"],
206
+ ["e", "a"],
207
+ ["a", "d"],
208
+ ["d", "g"],
209
+ ["g", "c"]
210
+ ].each do |pitch, new_pitch|
211
+ (Pitch.from_string(pitch) + i).to_short_name.should == new_pitch
212
+ end
213
+ end
214
+
215
+ it "should add diminished fifths (tritone)" do
216
+ i = Interval::Interval.from_string("d5")
217
+ [["c", "gb"],
218
+ ["f", "cb"],
219
+ ["Bb", "fb"],
220
+ ["eb", "bbb"],
221
+ ["ab", "ebb"],
222
+ ["db", "abb"],
223
+ ["gb", "dbb"],
224
+ ["f#", "c"],
225
+ ["b", "f"],
226
+ ["e", "bb"],
227
+ ["a", "eb"],
228
+ ["d", "ab"],
229
+ ["g", "db"]
230
+ ].each do |pitch, new_pitch|
231
+ (Pitch.from_string(pitch) + i).to_short_name.should == new_pitch
232
+ end
233
+ end
234
+
235
+ it "should add perfect fifths" do
236
+ i = Interval::Interval.from_string("p5")
237
+ [["c", "g"],
238
+ ["f", "c"],
239
+ ["Bb", "f"],
240
+ ["eb", "bb"],
241
+ ["ab", "eb"],
242
+ ["db", "ab"],
243
+ ["gb", "db"],
244
+ ["f#", "c#"],
245
+ ["b", "f#"],
246
+ ["e", "b"],
247
+ ["a", "e"],
248
+ ["d", "a"],
249
+ ["g", "d"]
250
+ ].each do |pitch, new_pitch|
251
+ (Pitch.from_string(pitch) + i).to_short_name.should == new_pitch
252
+ end
253
+ end
254
+
255
+ it "should add minor sixths" do
256
+ i = Interval::Interval.from_string("m6")
257
+ [["c", "ab"],
258
+ ["f", "db"],
259
+ ["Bb", "gb"],
260
+ ["eb", "cb"],
261
+ ["ab", "fb"],
262
+ ["db", "bbb"],
263
+ ["gb", "ebb"],
264
+ ["f#", "d"],
265
+ ["b", "g"],
266
+ ["e", "c"],
267
+ ["a", "f"],
268
+ ["d", "bb"],
269
+ ["g", "eb"]
270
+ ].each do |pitch, new_pitch|
271
+ (Pitch.from_string(pitch) + i).to_short_name.should == new_pitch
272
+ end
273
+ end
274
+
275
+ it "should add major sixths" do
276
+ i = Interval::Interval.from_string("M6")
277
+ [["c", "a"],
278
+ ["f", "d"],
279
+ ["Bb", "g"],
280
+ ["eb", "c"],
281
+ ["ab", "f"],
282
+ ["db", "bb"],
283
+ ["gb", "eb"],
284
+ ["f#", "d#"],
285
+ ["b", "g#"],
286
+ ["e", "c#"],
287
+ ["a", "f#"],
288
+ ["d", "b"],
289
+ ["g", "e"]
290
+ ].each do |pitch, new_pitch|
291
+ (Pitch.from_string(pitch) + i).to_short_name.should == new_pitch
292
+ end
293
+ end
294
+
295
+ it "should add minor sevenths" do
296
+ i = Interval::Interval.from_string("m7")
297
+ [["c", "bb"],
298
+ ["f", "eb"],
299
+ ["Bb", "ab"],
300
+ ["eb", "db"],
301
+ ["ab", "gb"],
302
+ ["db", "cb"],
303
+ ["gb", "fb"],
304
+ ["f#", "e"],
305
+ ["b", "a"],
306
+ ["e", "d"],
307
+ ["a", "g"],
308
+ ["d", "c"],
309
+ ["g", "f"]
310
+ ].each do |pitch, new_pitch|
311
+ (Pitch.from_string(pitch) + i).to_short_name.should == new_pitch
312
+ end
313
+ end
314
+
315
+ it "should add major sevenths" do
316
+ i = Interval::Interval.from_string("M7")
317
+ [["c", "b"],
318
+ ["f", "e"],
319
+ ["Bb", "a"],
320
+ ["eb", "d"],
321
+ ["ab", "g"],
322
+ ["db", "c"],
323
+ ["gb", "f"],
324
+ ["f#", "e#"],
325
+ ["b", "a#"],
326
+ ["e", "d#"],
327
+ ["a", "g#"],
328
+ ["d", "c#"],
329
+ ["g", "f#"]
330
+ ].each do |pitch, new_pitch|
331
+ (Pitch.from_string(pitch) + i).to_short_name.should == new_pitch
332
+ end
333
+ end
334
+
335
+ it "should add perfect octaves" do
336
+ i = Interval::Interval.from_string("p8")
337
+ [["c", "c"],
338
+ ["f", "f"],
339
+ ["Bb", "bb"],
340
+ ["eb", "eb"],
341
+ ["ab", "ab"],
342
+ ["db", "db"],
343
+ ["gb", "gb"],
344
+ ["f#", "f#"],
345
+ ["b", "b"],
346
+ ["e", "e"],
347
+ ["a", "a"],
348
+ ["d", "d"],
349
+ ["g", "g"]
350
+ ].each do |pitch, new_pitch|
351
+ (Pitch.from_string(pitch) + i).to_short_name.should == new_pitch
352
+ end
353
+ end
354
+
355
+ describe "adding enharmonic intervals" do
356
+ it "should add diminished seconds" do
357
+ i = Interval::Interval.from_string("d2")
358
+ [["c", "dbb"],
359
+ ["f", "gbb"],
360
+ ["Bb", "cbb"],
361
+ ["eb", "fbb"],
362
+ ["ab", "bbbb"],
363
+ ["db", "ebbb"],
364
+ ["gb", "abbb"],
365
+ ["f#", "gb"],
366
+ ["b", "cb"],
367
+ ["e", "fb"],
368
+ ["a", "bbb"],
369
+ ["d", "ebb"],
370
+ ["g", "abb"]
371
+ ].each do |pitch, new_pitch|
372
+ (Pitch.from_string(pitch) + i).to_short_name.should == new_pitch
373
+ end
374
+ end
375
+ end
376
+
377
+ describe "finding intervals below" do
378
+ it "should subtract major thirds" do
379
+ i = Interval::Interval.from_string("-M3")
380
+ [["c", "ab"],
381
+ ["f", "db"],
382
+ ["Bb", "gb"],
383
+ ["eb", "cb"],
384
+ ["ab", "fb"],
385
+ ["db", "bbb"],
386
+ ["gb", "ebb"],
387
+ ["f#", "d"],
388
+ ["b", "g"],
389
+ ["e", "c"],
390
+ ["a", "f"],
391
+ ["d", "bb"],
392
+ ["g", "eb"]
393
+ ].each do |pitch, new_pitch|
394
+ (Pitch.from_string(pitch) + i).to_short_name.should == new_pitch
395
+ end
396
+ end
397
+ end
398
+
399
+
400
+
401
+ end
402
+ end
@@ -0,0 +1,2 @@
1
+ $TESTING=true
2
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: interval
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Nate Murray
8
+ autorequire: interval
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-17 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: interval is a tiny library that provides simple musicial note pitch and interval arithmetic.
17
+ email: nate@natemurray.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.mkd
24
+ - LICENSE
25
+ files:
26
+ - LICENSE
27
+ - README.mkd
28
+ - Rakefile
29
+ - lib/interval.rb
30
+ - spec/interval_spec.rb
31
+ - spec/spec_helper.rb
32
+ has_rdoc: true
33
+ homepage: http://www.xcombinator.com
34
+ licenses: []
35
+
36
+ post_install_message:
37
+ rdoc_options: []
38
+
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ version:
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ requirements: []
54
+
55
+ rubyforge_project:
56
+ rubygems_version: 1.3.2
57
+ signing_key:
58
+ specification_version: 3
59
+ summary: interval is a tiny library that provides simple musicial note pitch and interval arithmetic.
60
+ test_files: []
61
+