music 0.6.2 → 0.8.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 +7 -0
- data/.rubocop.yml +72 -0
- data/.rubocop_todo.yml +47 -0
- data/.travis.yml +5 -5
- data/CHANGELOG.md +15 -0
- data/Gemfile +8 -1
- data/Gemfile.lock +283 -44
- data/Guardfile +8 -21
- data/README.md +4 -0
- data/Rakefile +7 -8
- data/lib/music/chord.rb +34 -31
- data/lib/music/note.rb +62 -80
- data/lib/music/version.rb +1 -1
- data/lib/music.rb +0 -1
- data/music.gemspec +14 -17
- data/spec/classes/chord_spec.rb +59 -59
- data/spec/classes/note_spec.rb +295 -286
- data/spec/spec_helper.rb +1 -1
- data/spec/support/matchers/have_an_interval.rb +2 -2
- metadata +20 -75
- data/lib/music/patches/hash.rb +0 -5
- data/spec/classes/hash_spec.rb +0 -17
data/lib/music/chord.rb
CHANGED
@@ -1,21 +1,24 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Music
|
2
4
|
class Chord
|
3
|
-
|
4
|
-
|
5
|
-
@notes == other_chord.notes
|
5
|
+
def eql?(other)
|
6
|
+
@notes == other.notes
|
6
7
|
end
|
8
|
+
|
7
9
|
def hash
|
8
10
|
@notes.hash
|
9
11
|
end
|
10
|
-
|
11
|
-
|
12
|
+
|
13
|
+
def ==(other)
|
14
|
+
self.eql?(other)
|
12
15
|
end
|
13
16
|
|
14
17
|
attr_reader :notes
|
15
18
|
|
16
19
|
def initialize(notes)
|
17
|
-
|
18
|
-
@notes = Set.new(notes) do |note|
|
20
|
+
fail ArgumentError, 'Chords must have at least two notes' if notes.size < 2
|
21
|
+
@notes = ::Set.new(notes) do |note|
|
19
22
|
if note.is_a?(Note)
|
20
23
|
note
|
21
24
|
else
|
@@ -28,7 +31,7 @@ module Music
|
|
28
31
|
# def to_i
|
29
32
|
|
30
33
|
def note_strings
|
31
|
-
Set.new(@notes.collect(&:note_string))
|
34
|
+
::Set.new(@notes.collect(&:note_string))
|
32
35
|
end
|
33
36
|
|
34
37
|
def describe
|
@@ -38,25 +41,25 @@ module Music
|
|
38
41
|
end
|
39
42
|
|
40
43
|
quality = case distances
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
44
|
+
when [4, 7]
|
45
|
+
:major
|
46
|
+
when [3, 7]
|
47
|
+
:minor
|
48
|
+
when [3, 6]
|
49
|
+
:diminished
|
50
|
+
when [4, 8]
|
51
|
+
:augmented
|
52
|
+
when [4, 7, 11]
|
53
|
+
:major_7
|
54
|
+
when [3, 7, 10]
|
55
|
+
:minor_7
|
56
|
+
when [3, 6, 9]
|
57
|
+
:diminished_7
|
58
|
+
when [3, 6, 10]
|
59
|
+
:half_diminished_7
|
60
|
+
when [4, 8, 10]
|
61
|
+
:augmented_7
|
62
|
+
end
|
60
63
|
|
61
64
|
[note_array.first.letter, quality]
|
62
65
|
end
|
@@ -69,8 +72,8 @@ module Music
|
|
69
72
|
#
|
70
73
|
# @returns [Chord] The specified inversion of chord
|
71
74
|
def inversion(amount)
|
72
|
-
|
73
|
-
|
75
|
+
fail ArgumentError, 'Inversion amount must be greater than or equal to 1' if amount < 1
|
76
|
+
fail ArgumentError, 'Not enough notes in chord for inversion' if amount >= @notes.size
|
74
77
|
|
75
78
|
note_array = @notes.to_a.sort
|
76
79
|
notes = (0...amount).collect { note_array.shift.adjust_by_semitones(12) }
|
@@ -95,9 +98,9 @@ module Music
|
|
95
98
|
class << self
|
96
99
|
def parse_chord_string(chord_string, assumed_octave = nil)
|
97
100
|
if note_string_match = chord_string.match(/^([A-Ga-g])([#b]?)([^\d]*)(\d*)$/)
|
98
|
-
|
101
|
+
_, note, accidental, interval, octave = note_string_match.to_a
|
99
102
|
|
100
|
-
|
103
|
+
fail ArgumentError, 'No octave found and no octave assumed' if note.empty? && assumed_octave.nil?
|
101
104
|
|
102
105
|
Note.new(note + accidental + octave, assumed_octave).chord(interval)
|
103
106
|
end
|
data/lib/music/note.rb
CHANGED
@@ -1,26 +1,21 @@
|
|
1
|
-
require 'active_model'
|
2
|
-
require 'music/patches/hash'
|
3
|
-
|
4
1
|
module Music
|
5
2
|
class Note
|
6
|
-
|
7
|
-
|
8
|
-
NOTES = ['C', 'C#/Db', 'D', 'D#/Eb', 'E', 'F', 'F#/Gb', 'G', 'G#/Ab', 'A', 'A#/Bb', 'B']
|
3
|
+
NOTES = [['C', 'B#'], ['C#', 'Db'], ['D'], ['D#', 'Eb'], ['E', 'Fb'], ['F', 'E#'], ['F#', 'Gb'], ['G'], ['G#', 'Ab'], ['A'], ['A#', 'Bb'], ['B', 'Cb']]
|
9
4
|
NOTE_STRINGS = ['Ab', 'A', 'A#', 'Bb', 'B', 'C', 'C#', 'Db', 'D', 'D#', 'Eb', 'E', 'F', 'Gb', 'G', 'G#']
|
10
5
|
|
11
6
|
attr_accessor :frequency
|
12
7
|
|
13
|
-
validates_presence_of :frequency
|
14
|
-
|
15
8
|
include Comparable
|
16
|
-
def <=>(
|
17
|
-
self.frequency <=>
|
9
|
+
def <=>(other)
|
10
|
+
self.frequency <=> other.frequency
|
18
11
|
end
|
12
|
+
|
19
13
|
def hash
|
20
14
|
self.frequency.hash
|
21
15
|
end
|
22
|
-
|
23
|
-
|
16
|
+
|
17
|
+
def eql?(other)
|
18
|
+
self.frequency == other.frequency
|
24
19
|
end
|
25
20
|
|
26
21
|
def to_s
|
@@ -34,10 +29,10 @@ module Music
|
|
34
29
|
# @returns [Note] Note specified
|
35
30
|
def initialize(descriptor, assumed_octave = nil)
|
36
31
|
self.frequency = if descriptor.is_a? Numeric
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
32
|
+
Note.nearest_note_frequency(descriptor)
|
33
|
+
else
|
34
|
+
Note.calculate_frequency(descriptor, assumed_octave)
|
35
|
+
end
|
41
36
|
end
|
42
37
|
|
43
38
|
# Returns string representing note with letter, accidental, and octave number
|
@@ -88,7 +83,7 @@ module Music
|
|
88
83
|
def succ
|
89
84
|
Note.new(Note.frequency_adjustment(self.frequency, 1))
|
90
85
|
end
|
91
|
-
|
86
|
+
alias_method :next, :succ
|
92
87
|
|
93
88
|
# Return the distance (in semitones) to a note
|
94
89
|
#
|
@@ -106,21 +101,21 @@ module Music
|
|
106
101
|
end
|
107
102
|
|
108
103
|
{
|
109
|
-
:
|
110
|
-
:
|
104
|
+
minor_second: 1,
|
105
|
+
major_second: 2,
|
111
106
|
|
112
|
-
:
|
113
|
-
:
|
107
|
+
minor_third: 3,
|
108
|
+
major_third: 4,
|
114
109
|
|
115
|
-
:
|
110
|
+
perfect_fourth: 5,
|
116
111
|
|
117
|
-
:
|
118
|
-
:
|
119
|
-
:
|
112
|
+
tritone: 6, diminished_fifth: 6, flat_fifth: 6, augmented_fourth: 6,
|
113
|
+
perfect_fifth: 7,
|
114
|
+
augmented_fifth: 8, minor_sixth: 8,
|
120
115
|
|
121
|
-
:
|
122
|
-
:
|
123
|
-
:
|
116
|
+
major_sixth: 9, diminished_seventh: 9,
|
117
|
+
minor_seventh: 10,
|
118
|
+
major_seventh: 11
|
124
119
|
}.each do |interval, semitones_count|
|
125
120
|
define_method interval do
|
126
121
|
adjust_by_semitones(semitones_count)
|
@@ -132,12 +127,12 @@ module Music
|
|
132
127
|
# @returns [Array<Note>] Notes in major scale
|
133
128
|
def major_scale
|
134
129
|
[self,
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
130
|
+
self.major_second,
|
131
|
+
self.major_third,
|
132
|
+
self.perfect_fourth,
|
133
|
+
self.perfect_fifth,
|
134
|
+
self.major_sixth,
|
135
|
+
self.major_seventh
|
141
136
|
]
|
142
137
|
end
|
143
138
|
|
@@ -146,26 +141,26 @@ module Music
|
|
146
141
|
# @returns [Array<Note>] Notes in minor scale
|
147
142
|
def minor_scale
|
148
143
|
[self,
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
144
|
+
self.major_second,
|
145
|
+
self.minor_third,
|
146
|
+
self.perfect_fourth,
|
147
|
+
self.perfect_fifth,
|
148
|
+
self.minor_sixth,
|
149
|
+
self.minor_seventh
|
155
150
|
]
|
156
151
|
end
|
157
152
|
|
158
153
|
CHORD_INTERVALS = {
|
159
|
-
:
|
160
|
-
:
|
161
|
-
:
|
162
|
-
:
|
163
|
-
:
|
164
|
-
:
|
165
|
-
:
|
166
|
-
:
|
167
|
-
:
|
168
|
-
:
|
154
|
+
minor: [:minor_third, :perfect_fifth],
|
155
|
+
major: [:major_third, :perfect_fifth],
|
156
|
+
fifth: [:perfect_fifth],
|
157
|
+
diminished: [:minor_third, :diminished_fifth],
|
158
|
+
augmented: [:major_third, :augmented_fifth],
|
159
|
+
major_seventh: [:major_third, :perfect_fifth, :major_seventh],
|
160
|
+
minor_seventh: [:minor_third, :perfect_fifth, :minor_seventh],
|
161
|
+
diminished_seventh: [:minor_third, :diminished_fifth, :diminished_seventh],
|
162
|
+
augmented_seventh: [:major_third, :augmented_fifth, :minor_seventh],
|
163
|
+
half_diminished_seventh: [:minor_third, :diminished_fifth, :minor_seventh]
|
169
164
|
}
|
170
165
|
|
171
166
|
CHORD_ALIASES = {
|
@@ -224,21 +219,21 @@ module Music
|
|
224
219
|
:half_dim_7 => :half_diminished_seventh,
|
225
220
|
:half_dim_7th => :half_diminished_seventh,
|
226
221
|
:half_dim7 => :half_diminished_seventh,
|
227
|
-
:half_dim7th => :half_diminished_seventh
|
222
|
+
:half_dim7th => :half_diminished_seventh
|
228
223
|
}
|
229
224
|
|
230
225
|
def chord(description)
|
231
|
-
description = :major if description.
|
226
|
+
description = :major if description.to_s.empty?
|
232
227
|
|
233
228
|
description = description.to_s
|
234
|
-
description.downcase! unless
|
229
|
+
description.downcase! unless %w(M M7).include?(description)
|
235
230
|
description.gsub!(/[\s\-]+/, '_')
|
236
231
|
description = description.to_sym
|
237
232
|
|
238
233
|
intervals = CHORD_INTERVALS[description] || CHORD_INTERVALS[CHORD_ALIASES[description]]
|
239
234
|
|
240
235
|
if intervals
|
241
|
-
Chord.new([self] + intervals.collect {|interval| self.send(interval) })
|
236
|
+
Chord.new([self] + intervals.collect { |interval| self.send(interval) })
|
242
237
|
end
|
243
238
|
end
|
244
239
|
|
@@ -249,16 +244,14 @@ module Music
|
|
249
244
|
end
|
250
245
|
|
251
246
|
class << self
|
252
|
-
extend ActiveSupport::Memoizable
|
253
|
-
|
254
247
|
def parse_note_string(note_string, assumed_octave = nil)
|
255
248
|
match = note_string.match(/^([A-Ga-g])([#b]?)([0-8]?)$/)
|
256
249
|
|
257
|
-
|
258
|
-
|
259
|
-
|
250
|
+
fail ArgumentError, "Did not recognize note string: #{note_string}" if !match
|
251
|
+
fail ArgumentError, 'No octave found or specified' if match[3].empty? && assumed_octave.nil?
|
252
|
+
fail ArgumentError if match[3].to_i > 8 || (assumed_octave && !(0..8).include?(assumed_octave))
|
260
253
|
|
261
|
-
octave = match[3].
|
254
|
+
octave = match[3].empty? ? assumed_octave : match[3]
|
262
255
|
[match[1].upcase, match[2] == '' ? nil : match[2], octave.to_i]
|
263
256
|
end
|
264
257
|
|
@@ -266,17 +259,9 @@ module Music
|
|
266
259
|
letter1, accidental1, octave1 = parse_note_string(note_string1)
|
267
260
|
letter2, accidental2, octave2 = parse_note_string(note_string2)
|
268
261
|
|
269
|
-
get_index =
|
270
|
-
NOTES.index do |
|
271
|
-
|
272
|
-
when '#' then
|
273
|
-
/^#{letter}#/
|
274
|
-
when 'b' then
|
275
|
-
/#{letter}b$/
|
276
|
-
else
|
277
|
-
/^#{letter}$/
|
278
|
-
end
|
279
|
-
note.match(regex)
|
262
|
+
get_index = proc do |letter, accidental|
|
263
|
+
NOTES.index do |notes|
|
264
|
+
notes.include?("#{letter}#{accidental}")
|
280
265
|
end
|
281
266
|
end
|
282
267
|
|
@@ -287,7 +272,7 @@ module Music
|
|
287
272
|
end
|
288
273
|
|
289
274
|
def frequency_adjustment(start_frequency, distance)
|
290
|
-
result = (start_frequency * (2.0
|
275
|
+
result = (start_frequency * (2.0**(distance.to_f / NOTES.size)))
|
291
276
|
|
292
277
|
# Would like to use #round(2), but want to support Ruby 1.8
|
293
278
|
(result * 100.0).round / 100.0
|
@@ -302,7 +287,7 @@ module Music
|
|
302
287
|
when 3
|
303
288
|
letter, accidental, octave = args
|
304
289
|
else
|
305
|
-
|
290
|
+
fail ArgumentError, 'Invalid octave of arguments'
|
306
291
|
end
|
307
292
|
|
308
293
|
distance = note_distance('A4', "#{letter}#{accidental}#{octave}")
|
@@ -321,14 +306,12 @@ module Music
|
|
321
306
|
octave = 4 + (index / NOTES.size) # 4 is because we're using A4
|
322
307
|
index = (index % NOTES.size)
|
323
308
|
|
324
|
-
parts = "#{NOTES[index]}".split('/')
|
325
309
|
note = if give_flat
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
310
|
+
NOTES[index].last
|
311
|
+
else
|
312
|
+
NOTES[index].first
|
313
|
+
end
|
330
314
|
|
331
|
-
"#{note}#{octave}"
|
332
315
|
note_parts = note.split('')
|
333
316
|
note_parts + (note_parts.size == 1 ? [nil] : []) + [octave.to_i]
|
334
317
|
end
|
@@ -336,7 +319,6 @@ module Music
|
|
336
319
|
def nearest_note_frequency(frequency)
|
337
320
|
Note.calculate_frequency(Note.calculate_note(frequency).join)
|
338
321
|
end
|
339
|
-
|
340
322
|
end
|
341
323
|
end
|
342
324
|
end
|
data/lib/music/version.rb
CHANGED
data/lib/music.rb
CHANGED
data/music.gemspec
CHANGED
@@ -1,29 +1,26 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
require
|
2
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'music/version'
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
# Basic Info
|
7
|
-
s.name =
|
7
|
+
s.name = 'music'
|
8
8
|
s.version = Music::VERSION
|
9
9
|
s.platform = Gem::Platform::RUBY
|
10
|
-
s.authors = [
|
11
|
-
s.email = [
|
12
|
-
s.homepage =
|
13
|
-
s.summary =
|
14
|
-
s.description =
|
15
|
-
s.license =
|
10
|
+
s.authors = ['Brian Underwood']
|
11
|
+
s.email = ['ml+musicgem@semi-sentient.com']
|
12
|
+
s.homepage = 'http://github.com/cheerfulstoic/music'
|
13
|
+
s.summary = 'Library for performing calculations on musical elements'
|
14
|
+
s.description = 'Library for classifying notes and chords and performing calculations on them. See README.md'
|
15
|
+
s.license = 'MIT'
|
16
16
|
|
17
17
|
# Files
|
18
18
|
s.files = `git ls-files`.split("\n")
|
19
19
|
s.test_files = `git ls-files -- {spec,factories}/*`.split("\n")
|
20
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
-
s.require_paths = [
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
21
|
+
s.require_paths = ['lib']
|
22
22
|
|
23
23
|
# Dependencies
|
24
|
-
s.
|
25
|
-
s.add_development_dependency
|
26
|
-
|
27
|
-
s.add_development_dependency "rspec", '~> 2'
|
28
|
-
s.add_development_dependency "activemodel", '~> 3.2.0'
|
29
|
-
end
|
24
|
+
s.add_development_dependency 'rake', '~> 0.9'
|
25
|
+
s.add_development_dependency 'rspec', '~> 2'
|
26
|
+
end
|
data/spec/classes/chord_spec.rb
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
include Music
|
2
3
|
|
3
4
|
describe Music::Chord do
|
4
5
|
before :all do
|
5
6
|
@standard_tuning_notes = [Note.new('E2'), Note.new('A2'), Note.new('D3'), Note.new('G3'), Note.new('B3'), Note.new('E4')]
|
6
7
|
|
7
|
-
@c_minor = Chord.new(
|
8
|
-
@c_major = Chord.new(
|
9
|
-
@c_diminished = Chord.new(
|
8
|
+
@c_minor = Chord.new(%w(C4 Eb4 G4))
|
9
|
+
@c_major = Chord.new(%w(C4 E4 G4))
|
10
|
+
@c_diminished = Chord.new(%w(C4 Eb4 Gb4))
|
10
11
|
@c_augmented = Chord.new(['C4', 'E4', 'G#4'])
|
11
|
-
@c_major_seventh = Chord.new(
|
12
|
-
@c_minor_seventh = Chord.new(
|
13
|
-
@c_diminished_seventh = Chord.new(
|
12
|
+
@c_major_seventh = Chord.new(%w(C4 E4 G4 B4))
|
13
|
+
@c_minor_seventh = Chord.new(%w(C4 Eb4 G4 Bb4))
|
14
|
+
@c_diminished_seventh = Chord.new(%w(C4 Eb4 Gb4 A4))
|
14
15
|
@c_augmented_seventh = Chord.new(['C4', 'E4', 'G#4', 'Bb4'])
|
15
|
-
@c_half_diminished_seventh = Chord.new(
|
16
|
+
@c_half_diminished_seventh = Chord.new(%w(C4 Eb4 Gb4 Bb4))
|
16
17
|
end
|
17
18
|
|
18
19
|
describe '#new(notes)' do
|
@@ -32,7 +33,7 @@ describe Music::Chord do
|
|
32
33
|
|
33
34
|
describe '#==' do
|
34
35
|
it 'should recognize that the order of notes in the chord does not matter' do
|
35
|
-
Chord.new(
|
36
|
+
Chord.new(%w(C4 Eb4 G4)).should eq(Chord.new(%w(G4 Eb4 C4)))
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
@@ -40,98 +41,97 @@ describe Music::Chord do
|
|
40
41
|
it 'should return a set of note strings' do
|
41
42
|
chord = Chord.new(@standard_tuning_notes)
|
42
43
|
|
43
|
-
chord.note_strings.should
|
44
|
+
chord.note_strings.should eq(Set.new(@standard_tuning_notes.collect(&:note_string)))
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
47
48
|
describe '#to_s' do
|
48
49
|
it 'should output just the sorted note descriptions separated by slashes' do
|
49
|
-
@c_minor.to_s.should
|
50
|
+
@c_minor.to_s.should eq('C4 / D#4 / G4')
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
53
54
|
describe '#describe' do
|
54
|
-
describe
|
55
|
+
describe 'triads' do
|
55
56
|
it 'should recognize C major' do
|
56
|
-
@c_major.describe.should
|
57
|
+
@c_major.describe.should eq(['C', :major])
|
57
58
|
end
|
58
59
|
|
59
60
|
it 'should recognize C minor' do
|
60
|
-
Chord.new(
|
61
|
+
Chord.new(%w(C4 Eb4 G4)).describe.should eq(['C', :minor])
|
61
62
|
end
|
62
63
|
|
63
64
|
it 'should recognize C diminished' do
|
64
|
-
Chord.new(
|
65
|
+
Chord.new(%w(C4 Eb4 Gb4)).describe.should eq(['C', :diminished])
|
65
66
|
end
|
66
67
|
it 'should recognize C augmented' do
|
67
|
-
Chord.new(['C4', 'E4', 'G#4']).describe.should
|
68
|
+
Chord.new(['C4', 'E4', 'G#4']).describe.should eq(['C', :augmented])
|
68
69
|
end
|
69
70
|
end
|
70
|
-
describe
|
71
|
+
describe 'seven chords' do
|
71
72
|
it 'should recognize Cmaj7' do
|
72
|
-
Chord.new(
|
73
|
+
Chord.new(%w(C4 E4 G4 B4)).describe.should eq(['C', :major_7])
|
73
74
|
end
|
74
75
|
it 'should recognize Cmin7' do
|
75
|
-
Chord.new(
|
76
|
+
Chord.new(%w(C4 Eb4 G4 Bb4)).describe.should eq(['C', :minor_7])
|
76
77
|
end
|
77
78
|
it 'should recognize Cdim7' do
|
78
|
-
Chord.new(
|
79
|
+
Chord.new(%w(C4 Eb4 Gb4 A4)).describe.should eq(['C', :diminished_7])
|
79
80
|
end
|
80
81
|
it 'should recognize Cmin7b5' do
|
81
|
-
Chord.new(
|
82
|
+
Chord.new(%w(C4 Eb4 Gb4 Bb4)).describe.should eq(['C', :half_diminished_7])
|
82
83
|
end
|
83
84
|
it 'should recognize Caug7' do
|
84
|
-
Chord.new(['C4', 'E4', 'G#4', 'Bb4']).describe.should
|
85
|
+
Chord.new(['C4', 'E4', 'G#4', 'Bb4']).describe.should eq(['C', :augmented_7])
|
85
86
|
end
|
86
87
|
end
|
87
88
|
end
|
88
89
|
|
89
90
|
describe '.parse_chord_string' do
|
90
91
|
it 'should recognize C major' do
|
91
|
-
Chord.parse_chord_string('Cmajor4').should
|
92
|
-
Chord.parse_chord_string('CMajor4').should
|
93
|
-
Chord.parse_chord_string('Cmaj4').should
|
94
|
-
Chord.parse_chord_string('CMaj4').should
|
95
|
-
Chord.parse_chord_string('CM4').should
|
96
|
-
Chord.parse_chord_string('C4').should
|
92
|
+
Chord.parse_chord_string('Cmajor4').should eq(@c_major)
|
93
|
+
Chord.parse_chord_string('CMajor4').should eq(@c_major)
|
94
|
+
Chord.parse_chord_string('Cmaj4').should eq(@c_major)
|
95
|
+
Chord.parse_chord_string('CMaj4').should eq(@c_major)
|
96
|
+
Chord.parse_chord_string('CM4').should eq(@c_major)
|
97
|
+
Chord.parse_chord_string('C4').should eq(@c_major)
|
97
98
|
|
98
99
|
expect { Chord.parse_chord_string('C') }.to raise_error ArgumentError
|
99
|
-
Chord.parse_chord_string('C', 4).should
|
100
|
+
Chord.parse_chord_string('C', 4).should eq(@c_major)
|
100
101
|
end
|
101
102
|
|
102
103
|
it 'should recognize C minor' do
|
103
|
-
Chord.parse_chord_string('Cminor4').should
|
104
|
-
Chord.parse_chord_string('CMinor4').should
|
105
|
-
Chord.parse_chord_string('Cmin4').should
|
106
|
-
Chord.parse_chord_string('CMin4').should
|
107
|
-
Chord.parse_chord_string('Cm4').should
|
104
|
+
Chord.parse_chord_string('Cminor4').should eq(@c_minor)
|
105
|
+
Chord.parse_chord_string('CMinor4').should eq(@c_minor)
|
106
|
+
Chord.parse_chord_string('Cmin4').should eq(@c_minor)
|
107
|
+
Chord.parse_chord_string('CMin4').should eq(@c_minor)
|
108
|
+
Chord.parse_chord_string('Cm4').should eq(@c_minor)
|
108
109
|
|
109
110
|
expect { Chord.parse_chord_string('Cm') }.to raise_error ArgumentError
|
110
|
-
Chord.parse_chord_string('Cm', 4).should
|
111
|
-
|
111
|
+
Chord.parse_chord_string('Cm', 4).should eq(@c_minor)
|
112
112
|
end
|
113
113
|
|
114
114
|
it 'should recognize C diminished' do
|
115
|
-
Chord.parse_chord_string('Cdiminished4').should
|
116
|
-
Chord.parse_chord_string('CDiminished4').should
|
117
|
-
Chord.parse_chord_string('Cdim4').should
|
118
|
-
Chord.parse_chord_string('CDim4').should
|
119
|
-
Chord.parse_chord_string('CDIM4').should
|
115
|
+
Chord.parse_chord_string('Cdiminished4').should eq(@c_diminished)
|
116
|
+
Chord.parse_chord_string('CDiminished4').should eq(@c_diminished)
|
117
|
+
Chord.parse_chord_string('Cdim4').should eq(@c_diminished)
|
118
|
+
Chord.parse_chord_string('CDim4').should eq(@c_diminished)
|
119
|
+
Chord.parse_chord_string('CDIM4').should eq(@c_diminished)
|
120
120
|
|
121
|
-
expect { Chord.parse_chord_string('Cdim')
|
122
|
-
Chord.parse_chord_string('Cdim', 4).should
|
121
|
+
expect { Chord.parse_chord_string('Cdim') }.to raise_error ArgumentError
|
122
|
+
Chord.parse_chord_string('Cdim', 4).should eq(@c_diminished)
|
123
123
|
end
|
124
124
|
|
125
125
|
it 'should recognize C augmented' do
|
126
|
-
Chord.parse_chord_string('Caugmented4').should
|
127
|
-
Chord.parse_chord_string('Caugmented4').should
|
128
|
-
Chord.parse_chord_string('Caug4').should
|
129
|
-
Chord.parse_chord_string('CAug4').should
|
130
|
-
Chord.parse_chord_string('CAUG4').should
|
131
|
-
Chord.parse_chord_string('C+4').should
|
126
|
+
Chord.parse_chord_string('Caugmented4').should eq(@c_augmented)
|
127
|
+
Chord.parse_chord_string('Caugmented4').should eq(@c_augmented)
|
128
|
+
Chord.parse_chord_string('Caug4').should eq(@c_augmented)
|
129
|
+
Chord.parse_chord_string('CAug4').should eq(@c_augmented)
|
130
|
+
Chord.parse_chord_string('CAUG4').should eq(@c_augmented)
|
131
|
+
Chord.parse_chord_string('C+4').should eq(@c_augmented)
|
132
132
|
|
133
|
-
expect { Chord.parse_chord_string('Caug')
|
134
|
-
Chord.parse_chord_string('Caug', 4).should
|
133
|
+
expect { Chord.parse_chord_string('Caug') }.to raise_error ArgumentError
|
134
|
+
Chord.parse_chord_string('Caug', 4).should eq(@c_augmented)
|
135
135
|
end
|
136
136
|
|
137
137
|
# TODO: Fill out other chords
|
@@ -139,13 +139,13 @@ describe Music::Chord do
|
|
139
139
|
|
140
140
|
describe '#inversion' do
|
141
141
|
it 'should adjust the lowest n notes up by an octive' do
|
142
|
-
@c_major.inversion(1).should
|
143
|
-
Chord.new(
|
142
|
+
@c_major.inversion(1).should eq(Chord.new(%w(E4 G4 C5)))
|
143
|
+
Chord.new(%w(Eb4 C4 Gb4)).inversion(1).should eq(Chord.new(%w(Eb4 C5 Gb4)))
|
144
144
|
|
145
|
-
@c_major.inversion(2).should
|
146
|
-
Chord.new(
|
145
|
+
@c_major.inversion(2).should eq(Chord.new(%w(E5 G4 C5)))
|
146
|
+
Chord.new(%w(Eb4 C4 Gb4)).inversion(2).should eq(Chord.new(%w(Eb5 C5 Gb4)))
|
147
147
|
|
148
|
-
@c_augmented_seventh.inversion(3).should
|
148
|
+
@c_augmented_seventh.inversion(3).should eq(Chord.new(['E5', 'G#5', 'C5', 'Bb4']))
|
149
149
|
end
|
150
150
|
|
151
151
|
it 'should raise an error when the inversion amount is too great' do
|
@@ -164,19 +164,19 @@ describe Music::Chord do
|
|
164
164
|
|
165
165
|
describe '#first_inversion' do
|
166
166
|
it 'should adjust the lowest note up by an octive' do
|
167
|
-
@c_major.first_inversion.should
|
167
|
+
@c_major.first_inversion.should eq(Chord.new(%w(E4 G4 C5)))
|
168
168
|
end
|
169
169
|
end
|
170
170
|
|
171
171
|
describe '#second_inversion' do
|
172
172
|
it 'should adjust the lowest two notes up by an octive' do
|
173
|
-
@c_major.second_inversion.should
|
173
|
+
@c_major.second_inversion.should eq(Chord.new(%w(E5 G4 C5)))
|
174
174
|
end
|
175
175
|
end
|
176
176
|
|
177
177
|
describe '#third_inversion' do
|
178
178
|
it 'should adjust the lowest three notes up by an octive' do
|
179
|
-
@c_augmented_seventh.third_inversion.should
|
179
|
+
@c_augmented_seventh.third_inversion.should eq(Chord.new(['E5', 'G#5', 'C5', 'Bb4']))
|
180
180
|
end
|
181
181
|
end
|
182
|
-
end
|
182
|
+
end
|