interval 0.0.1

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