music 0.5.1 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -1
- data/Gemfile +6 -0
- data/Gemfile.lock +25 -1
- data/Guardfile +29 -0
- data/README.md +13 -1
- data/lib/music/chord.rb +67 -6
- data/lib/music/note.rb +195 -1
- data/lib/music/version.rb +1 -1
- data/spec/classes/chord_spec.rb +122 -5
- data/spec/classes/note_spec.rb +327 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/support/matchers/have_an_interval.rb +13 -0
- metadata +77 -89
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
music (0.5.
|
4
|
+
music (0.5.1)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: http://rubygems.org/
|
@@ -13,10 +13,29 @@ GEM
|
|
13
13
|
i18n (~> 0.6)
|
14
14
|
multi_json (~> 1.0)
|
15
15
|
builder (3.0.0)
|
16
|
+
coderay (1.0.8)
|
16
17
|
diff-lcs (1.1.3)
|
18
|
+
guard (1.5.3)
|
19
|
+
listen (>= 0.4.2)
|
20
|
+
lumberjack (>= 1.0.2)
|
21
|
+
pry (>= 0.9.10)
|
22
|
+
thor (>= 0.14.6)
|
23
|
+
guard-bundler (1.0.0)
|
24
|
+
bundler (~> 1.0)
|
25
|
+
guard (~> 1.1)
|
26
|
+
guard-rspec (1.2.1)
|
27
|
+
guard (>= 1.1)
|
17
28
|
i18n (0.6.0)
|
29
|
+
listen (0.5.3)
|
30
|
+
lumberjack (1.0.2)
|
31
|
+
method_source (0.8.1)
|
18
32
|
multi_json (1.3.5)
|
33
|
+
pry (0.9.10)
|
34
|
+
coderay (~> 1.0.5)
|
35
|
+
method_source (~> 0.8)
|
36
|
+
slop (~> 3.3.1)
|
19
37
|
rake (0.9.2.2)
|
38
|
+
rb-fsevent (0.9.2)
|
20
39
|
rspec (2.10.0)
|
21
40
|
rspec-core (~> 2.10.0)
|
22
41
|
rspec-expectations (~> 2.10.0)
|
@@ -25,6 +44,8 @@ GEM
|
|
25
44
|
rspec-expectations (2.10.0)
|
26
45
|
diff-lcs (~> 1.1.3)
|
27
46
|
rspec-mocks (2.10.1)
|
47
|
+
slop (3.3.3)
|
48
|
+
thor (0.16.0)
|
28
49
|
|
29
50
|
PLATFORMS
|
30
51
|
ruby
|
@@ -32,6 +53,9 @@ PLATFORMS
|
|
32
53
|
DEPENDENCIES
|
33
54
|
activemodel (>= 3.2.0)
|
34
55
|
bundler (>= 1.1.3)
|
56
|
+
guard-bundler
|
57
|
+
guard-rspec
|
35
58
|
music!
|
36
59
|
rake (>= 0.9)
|
60
|
+
rb-fsevent (~> 0.9.1)
|
37
61
|
rspec
|
data/Guardfile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'bundler' do
|
5
|
+
watch('Gemfile')
|
6
|
+
# Uncomment next line if Gemfile contain `gemspec' command
|
7
|
+
# watch(/^.+\.gemspec/)
|
8
|
+
end
|
9
|
+
|
10
|
+
guard 'rspec', :version => 2, :rvm => ['1.9.3', '1.8.7', 'rbx'] do
|
11
|
+
watch(%r{^spec/.+_spec\.rb$})
|
12
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
13
|
+
watch('spec/spec_helper.rb') { "spec" }
|
14
|
+
|
15
|
+
# Rails example
|
16
|
+
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
17
|
+
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
18
|
+
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
19
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
20
|
+
watch('config/routes.rb') { "spec/routing" }
|
21
|
+
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
22
|
+
|
23
|
+
# Capybara request specs
|
24
|
+
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
|
25
|
+
|
26
|
+
# Turnip features and steps
|
27
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
28
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
29
|
+
end
|
data/README.md
CHANGED
@@ -3,6 +3,8 @@ music
|
|
3
3
|
|
4
4
|
The *music* gem provides a means on calculating notes and chords.
|
5
5
|
|
6
|
+
[![Build Status](https://travis-ci.org/cheerfulstoic/music.png)](https://travis-ci.org/cheerfulstoic/music)
|
7
|
+
|
6
8
|
Examples:
|
7
9
|
---------
|
8
10
|
|
@@ -39,7 +41,17 @@ Describe chords:
|
|
39
41
|
|
40
42
|
Chord.new(['C4', 'Eb4', 'Gb4']) # => ['C', :diminished]
|
41
43
|
|
42
|
-
*For further usage
|
44
|
+
*For further usage:*
|
45
|
+
* Docs: http://rubydoc.info/github/cheerfulstoic/music/frames
|
46
|
+
* See the examples in the spec files
|
47
|
+
|
48
|
+
TODOs:
|
49
|
+
======
|
50
|
+
|
51
|
+
* Use the term musical term 'interval' or 'semitones' instead of 'distance' in code
|
52
|
+
* 'C-' can be used to represent C minor
|
53
|
+
* There can be many versions of chords. See: http://giventowail.com/lessons/evan/the-basics-major-minor-and-power-chords?phpMyAdmin=97715a053dab6cd797cf01c69d7492a4
|
54
|
+
* Implement calculation of 'cents'
|
43
55
|
|
44
56
|
Contributing to music
|
45
57
|
=====================
|
data/lib/music/chord.rb
CHANGED
@@ -1,22 +1,40 @@
|
|
1
1
|
module Music
|
2
2
|
class Chord
|
3
|
+
|
4
|
+
def eql?(other_chord)
|
5
|
+
@notes == other_chord.notes
|
6
|
+
end
|
7
|
+
def hash
|
8
|
+
@notes.hash
|
9
|
+
end
|
10
|
+
def ==(other_chord)
|
11
|
+
self.eql?(other_chord)
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :notes
|
15
|
+
|
3
16
|
def initialize(notes)
|
4
|
-
|
17
|
+
raise ArgumentError, 'Chords must have at least two notes' if notes.size < 2
|
18
|
+
@notes = Set.new(notes) do |note|
|
5
19
|
if note.is_a?(Note)
|
6
20
|
note
|
7
21
|
else
|
8
22
|
Note.new(note)
|
9
23
|
end
|
10
|
-
end
|
24
|
+
end
|
11
25
|
end
|
12
26
|
|
27
|
+
# Spec and implement
|
28
|
+
# def to_i
|
29
|
+
|
13
30
|
def note_strings
|
14
|
-
@notes.collect(&:note_string)
|
31
|
+
Set.new(@notes.collect(&:note_string))
|
15
32
|
end
|
16
33
|
|
17
34
|
def describe
|
18
|
-
|
19
|
-
|
35
|
+
note_array = @notes.to_a.sort
|
36
|
+
distances = (1...note_array.size).collect do |i|
|
37
|
+
note_array[0].distance_to(note_array[i])
|
20
38
|
end
|
21
39
|
|
22
40
|
quality = case distances
|
@@ -40,7 +58,50 @@ module Music
|
|
40
58
|
:augmented_7
|
41
59
|
end
|
42
60
|
|
43
|
-
[
|
61
|
+
[note_array.first.letter, quality]
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
@notes.to_a.sort.collect(&:to_s).join(' / ')
|
66
|
+
end
|
67
|
+
|
68
|
+
# Give the Nth inversion of the chord which simply adjusts the lowest N notes up by one octive
|
69
|
+
#
|
70
|
+
# @returns [Chord] The specified inversion of chord
|
71
|
+
def inversion(amount)
|
72
|
+
raise ArgumentError, "Inversion amount must be greater than or equal to 1" if amount < 1
|
73
|
+
raise ArgumentError, "Not enough notes in chord for inversion" if amount >= @notes.size
|
74
|
+
|
75
|
+
note_array = @notes.to_a.sort
|
76
|
+
notes = (0...amount).collect { note_array.shift.adjust_by_semitones(12) }
|
77
|
+
Chord.new(notes + note_array)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Calls inversion(1)
|
81
|
+
def first_inversion
|
82
|
+
self.inversion(1)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Calls inversion(2)
|
86
|
+
def second_inversion
|
87
|
+
self.inversion(2)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Calls inversion(3)
|
91
|
+
def third_inversion
|
92
|
+
self.inversion(3)
|
93
|
+
end
|
94
|
+
|
95
|
+
class << self
|
96
|
+
def parse_chord_string(chord_string, assumed_octave = nil)
|
97
|
+
if note_string_match = chord_string.match(/^([A-Ga-g])([#b]?)([^\d]*)(\d*)$/)
|
98
|
+
full_string, note, accidental, interval, octave = note_string_match.to_a
|
99
|
+
|
100
|
+
raise ArgumentError, 'No octave found and no octave assumed' if note.blank? && assumed_octave.nil?
|
101
|
+
|
102
|
+
Note.new(note + accidental + octave, assumed_octave).chord(interval)
|
103
|
+
end
|
104
|
+
end
|
44
105
|
end
|
45
106
|
end
|
46
107
|
end
|
data/lib/music/note.rb
CHANGED
@@ -16,7 +16,22 @@ module Music
|
|
16
16
|
def <=>(other_note)
|
17
17
|
self.frequency <=> other_note.frequency
|
18
18
|
end
|
19
|
+
def hash
|
20
|
+
self.frequency.hash
|
21
|
+
end
|
22
|
+
def eql?(other_note)
|
23
|
+
self.frequency == other_note.frequency
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
self.note_string
|
28
|
+
end
|
19
29
|
|
30
|
+
# Creates a new note
|
31
|
+
#
|
32
|
+
# @param [String, Numeric] descriptor Either a string describing the note (e.g. 'C#4') or a number giving the note's frequency (e.g. 440)
|
33
|
+
# @param [Numeric, nil] assumed_octave If no octive is given in the descriptor, use this
|
34
|
+
# @returns [Note] Note specified
|
20
35
|
def initialize(descriptor, assumed_octave = nil)
|
21
36
|
self.frequency = if descriptor.is_a? Numeric
|
22
37
|
Note.nearest_note_frequency(descriptor)
|
@@ -25,35 +40,214 @@ module Music
|
|
25
40
|
end
|
26
41
|
end
|
27
42
|
|
43
|
+
# Returns string representing note with letter, accidental, and octave number
|
44
|
+
# e.g. 'C#5'
|
45
|
+
#
|
46
|
+
# @param [boolean] give_flat Should the result give a flat? (defults to giving a sharp)
|
47
|
+
# @return [String] The resulting note string
|
28
48
|
def note_string(give_flat = false)
|
29
49
|
Note.calculate_note(self.frequency, give_flat).join
|
30
50
|
end
|
31
51
|
|
52
|
+
# Returns the letter portion of the note
|
53
|
+
# e.g. 'C'
|
54
|
+
#
|
55
|
+
# @param [boolean] give_flat Should the result be based on giving a flat? (defaults to giving a sharp)
|
56
|
+
# @return [String] The resulting note letter
|
32
57
|
def letter(give_flat = false)
|
33
58
|
Note.calculate_note(self.frequency, give_flat)[0]
|
34
59
|
end
|
35
60
|
|
61
|
+
# Returns the accidental portion of the note
|
62
|
+
# e.g. '#' or 'b'
|
63
|
+
#
|
64
|
+
# @param [boolean] give_flat Should the result give a flat? (defaults to giving a sharp)
|
65
|
+
# @return [String] The resulting accidental
|
36
66
|
def accidental(give_flat = false)
|
37
67
|
Note.calculate_note(self.frequency, give_flat)[1]
|
38
68
|
end
|
39
69
|
|
70
|
+
# Returns the octive number of the note
|
71
|
+
# e.g. 4
|
72
|
+
#
|
73
|
+
# @return [Fixnum] The resulting octive number
|
40
74
|
def octave
|
41
75
|
Note.calculate_note(self.frequency)[2]
|
42
76
|
end
|
43
77
|
|
44
|
-
|
78
|
+
# Return the previous note (adjusted by one semitone down)
|
79
|
+
#
|
80
|
+
# @return [Note] The previous note
|
81
|
+
def prev
|
45
82
|
Note.new(Note.frequency_adjustment(self.frequency, -1))
|
46
83
|
end
|
47
84
|
|
85
|
+
# Return the next note (adjusted by one semitone up)
|
86
|
+
#
|
87
|
+
# @return [Note] The next note
|
48
88
|
def succ
|
49
89
|
Note.new(Note.frequency_adjustment(self.frequency, 1))
|
50
90
|
end
|
51
91
|
alias :next :succ
|
52
92
|
|
93
|
+
# Return the distance (in semitones) to a note
|
94
|
+
#
|
95
|
+
# @return [Fixnum] Number of semitones
|
53
96
|
def distance_to(note)
|
54
97
|
Note.note_distance(self.note_string, note.note_string)
|
55
98
|
end
|
56
99
|
|
100
|
+
# Return another note adjusted by a given interval
|
101
|
+
#
|
102
|
+
# @param [Fixnum] interval Number of semitones to adjust by
|
103
|
+
# @return [Note] Resulting note after adjustment
|
104
|
+
def adjust_by_semitones(interval)
|
105
|
+
Note.new(Note.frequency_adjustment(self.frequency, interval))
|
106
|
+
end
|
107
|
+
|
108
|
+
{
|
109
|
+
:minor_second => 1,
|
110
|
+
:major_second => 2,
|
111
|
+
|
112
|
+
:minor_third => 3,
|
113
|
+
:major_third => 4,
|
114
|
+
|
115
|
+
:perfect_fourth => 5,
|
116
|
+
|
117
|
+
:tritone => 6, :diminished_fifth => 6, :flat_fifth => 6, :augmented_fourth => 6,
|
118
|
+
:perfect_fifth => 7,
|
119
|
+
:augmented_fifth => 8, :minor_sixth => 8,
|
120
|
+
|
121
|
+
:major_sixth => 9, :diminished_seventh => 9,
|
122
|
+
:minor_seventh => 10,
|
123
|
+
:major_seventh => 11
|
124
|
+
}.each do |interval, semitones_count|
|
125
|
+
define_method interval do
|
126
|
+
adjust_by_semitones(semitones_count)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Uses note as key to give major scale
|
131
|
+
#
|
132
|
+
# @returns [Array<Note>] Notes in major scale
|
133
|
+
def major_scale
|
134
|
+
[self,
|
135
|
+
self.major_second,
|
136
|
+
self.major_third,
|
137
|
+
self.perfect_fourth,
|
138
|
+
self.perfect_fifth,
|
139
|
+
self.major_sixth,
|
140
|
+
self.major_seventh,
|
141
|
+
]
|
142
|
+
end
|
143
|
+
|
144
|
+
# Uses note as key to give minor scale
|
145
|
+
#
|
146
|
+
# @returns [Array<Note>] Notes in minor scale
|
147
|
+
def minor_scale
|
148
|
+
[self,
|
149
|
+
self.major_second,
|
150
|
+
self.minor_third,
|
151
|
+
self.perfect_fourth,
|
152
|
+
self.perfect_fifth,
|
153
|
+
self.minor_sixth,
|
154
|
+
self.minor_seventh,
|
155
|
+
]
|
156
|
+
end
|
157
|
+
|
158
|
+
CHORD_INTERVALS = {
|
159
|
+
:minor => [:minor_third, :perfect_fifth],
|
160
|
+
:major => [:major_third, :perfect_fifth],
|
161
|
+
:fifth => [:perfect_fifth],
|
162
|
+
:diminished => [:minor_third, :diminished_fifth],
|
163
|
+
:augmented => [:major_third, :augmented_fifth],
|
164
|
+
:major_seventh => [:major_third, :perfect_fifth, :major_seventh],
|
165
|
+
:minor_seventh => [:minor_third, :perfect_fifth, :minor_seventh],
|
166
|
+
:diminished_seventh => [:minor_third, :diminished_fifth, :diminished_seventh],
|
167
|
+
:augmented_seventh => [:major_third, :augmented_fifth, :minor_seventh],
|
168
|
+
:half_diminished_seventh => [:minor_third, :diminished_fifth, :minor_seventh]
|
169
|
+
}
|
170
|
+
|
171
|
+
CHORD_ALIASES = {
|
172
|
+
:min => :minor,
|
173
|
+
:m => :minor,
|
174
|
+
:maj => :major,
|
175
|
+
:M => :major,
|
176
|
+
:p => :fifth,
|
177
|
+
:pow => :fifth,
|
178
|
+
:power => :fifth,
|
179
|
+
:'5' => :fifth,
|
180
|
+
:'5th' => :fifth,
|
181
|
+
:dim => :diminished,
|
182
|
+
:aug => :augmented,
|
183
|
+
:'+' => :augmented,
|
184
|
+
|
185
|
+
:maj_seventh => :major_seventh,
|
186
|
+
:major_7 => :major_seventh,
|
187
|
+
:major_7th => :major_seventh,
|
188
|
+
:maj_7 => :major_seventh,
|
189
|
+
:maj_7th => :major_seventh,
|
190
|
+
:maj7 => :major_seventh,
|
191
|
+
:maj7th => :major_seventh,
|
192
|
+
:M7 => :major_seventh,
|
193
|
+
|
194
|
+
:min_seventh => :minor_seventh,
|
195
|
+
:minor_7 => :minor_seventh,
|
196
|
+
:minor_7th => :minor_seventh,
|
197
|
+
:min_7 => :minor_seventh,
|
198
|
+
:min_7th => :minor_seventh,
|
199
|
+
:min7 => :minor_seventh,
|
200
|
+
:min7th => :minor_seventh,
|
201
|
+
:m7 => :minor_seventh,
|
202
|
+
|
203
|
+
:dim_seventh => :diminished_seventh,
|
204
|
+
:diminished_7 => :diminished_seventh,
|
205
|
+
:diminished_7th => :diminished_seventh,
|
206
|
+
:dim_7 => :diminished_seventh,
|
207
|
+
:dim_7th => :diminished_seventh,
|
208
|
+
:dim7 => :diminished_seventh,
|
209
|
+
:dim7th => :diminished_seventh,
|
210
|
+
:d7 => :diminished_seventh,
|
211
|
+
|
212
|
+
:aug_seventh => :augmented_seventh,
|
213
|
+
:augmented_7 => :augmented_seventh,
|
214
|
+
:augmented_7th => :augmented_seventh,
|
215
|
+
:aug_7 => :augmented_seventh,
|
216
|
+
:aug_7th => :augmented_seventh,
|
217
|
+
:aug7 => :augmented_seventh,
|
218
|
+
:aug7th => :augmented_seventh,
|
219
|
+
:'+7' => :augmented_seventh,
|
220
|
+
|
221
|
+
:half_dim_seventh => :half_diminished_seventh,
|
222
|
+
:half_diminished_7 => :half_diminished_seventh,
|
223
|
+
:half_diminished_7th => :half_diminished_seventh,
|
224
|
+
:half_dim_7 => :half_diminished_seventh,
|
225
|
+
:half_dim_7th => :half_diminished_seventh,
|
226
|
+
:half_dim7 => :half_diminished_seventh,
|
227
|
+
:half_dim7th => :half_diminished_seventh,
|
228
|
+
}
|
229
|
+
|
230
|
+
def chord(description)
|
231
|
+
description = :major if description.blank?
|
232
|
+
|
233
|
+
description = description.to_s
|
234
|
+
description.downcase! unless ['M', 'M7'].include?(description)
|
235
|
+
description.gsub!(/[\s\-]+/, '_')
|
236
|
+
description = description.to_sym
|
237
|
+
|
238
|
+
intervals = CHORD_INTERVALS[description] || CHORD_INTERVALS[CHORD_ALIASES[description]]
|
239
|
+
|
240
|
+
if intervals
|
241
|
+
Chord.new([self] + intervals.collect {|interval| self.send(interval) })
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
(CHORD_INTERVALS.keys + CHORD_ALIASES.keys).each do |chord_description|
|
246
|
+
define_method "#{chord_description}_chord" do
|
247
|
+
self.chord(chord_description)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
57
251
|
class << self
|
58
252
|
extend ActiveSupport::Memoizable
|
59
253
|
|
data/lib/music/version.rb
CHANGED
data/spec/classes/chord_spec.rb
CHANGED
@@ -3,6 +3,16 @@ require 'spec_helper'
|
|
3
3
|
describe Music::Chord do
|
4
4
|
before :all do
|
5
5
|
@standard_tuning_notes = [Note.new('E2'), Note.new('A2'), Note.new('D3'), Note.new('G3'), Note.new('B3'), Note.new('E4')]
|
6
|
+
|
7
|
+
@c_minor = Chord.new(['C4', 'Eb4', 'G4'])
|
8
|
+
@c_major = Chord.new(['C4', 'E4', 'G4'])
|
9
|
+
@c_diminished = Chord.new(['C4', 'Eb4', 'Gb4'])
|
10
|
+
@c_augmented = Chord.new(['C4', 'E4', 'G#4'])
|
11
|
+
@c_major_seventh = Chord.new(['C4', 'E4', 'G4', 'B4'])
|
12
|
+
@c_minor_seventh = Chord.new(['C4', 'Eb4', 'G4', 'Bb4'])
|
13
|
+
@c_diminished_seventh = Chord.new(['C4', 'Eb4', 'Gb4', 'A4'])
|
14
|
+
@c_augmented_seventh = Chord.new(['C4', 'E4', 'G#4', 'Bb4'])
|
15
|
+
@c_half_diminished_seventh = Chord.new(['C4', 'Eb4', 'Gb4', 'Bb4'])
|
6
16
|
end
|
7
17
|
|
8
18
|
describe '#new(notes)' do
|
@@ -14,23 +24,36 @@ describe Music::Chord do
|
|
14
24
|
Chord.new(@standard_tuning_notes.collect(&:note_string))
|
15
25
|
end
|
16
26
|
|
17
|
-
it 'should
|
18
|
-
Chord.new(['
|
27
|
+
it 'should validate that chords must have at least two notes' do
|
28
|
+
lambda { Chord.new(['C4']) }.should raise_error(ArgumentError, 'Chords must have at least two notes')
|
29
|
+
lambda { Chord.new([]) }.should raise_error(ArgumentError, 'Chords must have at least two notes')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#==' do
|
34
|
+
it 'should recognize that the order of notes in the chord does not matter' do
|
35
|
+
Chord.new(['C4', 'Eb4', 'G4']).should == Chord.new(['G4', 'Eb4', 'C4'])
|
19
36
|
end
|
20
37
|
end
|
21
38
|
|
22
39
|
describe '#note_strings' do
|
23
|
-
it 'should return
|
40
|
+
it 'should return a set of note strings' do
|
24
41
|
chord = Chord.new(@standard_tuning_notes)
|
25
42
|
|
26
|
-
chord.note_strings.should == @standard_tuning_notes.collect(&:note_string)
|
43
|
+
chord.note_strings.should == Set.new(@standard_tuning_notes.collect(&:note_string))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#to_s' do
|
48
|
+
it 'should output just the sorted note descriptions separated by slashes' do
|
49
|
+
@c_minor.to_s.should == 'C4 / D#4 / G4'
|
27
50
|
end
|
28
51
|
end
|
29
52
|
|
30
53
|
describe '#describe' do
|
31
54
|
describe "triads" do
|
32
55
|
it 'should recognize C major' do
|
33
|
-
|
56
|
+
@c_major.describe.should == ['C', :major]
|
34
57
|
end
|
35
58
|
|
36
59
|
it 'should recognize C minor' do
|
@@ -62,4 +85,98 @@ describe Music::Chord do
|
|
62
85
|
end
|
63
86
|
end
|
64
87
|
end
|
88
|
+
|
89
|
+
describe '.parse_chord_string' do
|
90
|
+
it 'should recognize C major' do
|
91
|
+
Chord.parse_chord_string('Cmajor4').should == @c_major
|
92
|
+
Chord.parse_chord_string('CMajor4').should == @c_major
|
93
|
+
Chord.parse_chord_string('Cmaj4').should == @c_major
|
94
|
+
Chord.parse_chord_string('CMaj4').should == @c_major
|
95
|
+
Chord.parse_chord_string('CM4').should == @c_major
|
96
|
+
Chord.parse_chord_string('C4').should == @c_major
|
97
|
+
|
98
|
+
expect { Chord.parse_chord_string('C') }.to raise_error ArgumentError
|
99
|
+
Chord.parse_chord_string('C', 4).should == @c_major
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should recognize C minor' do
|
103
|
+
Chord.parse_chord_string('Cminor4').should == @c_minor
|
104
|
+
Chord.parse_chord_string('CMinor4').should == @c_minor
|
105
|
+
Chord.parse_chord_string('Cmin4').should == @c_minor
|
106
|
+
Chord.parse_chord_string('CMin4').should == @c_minor
|
107
|
+
Chord.parse_chord_string('Cm4').should == @c_minor
|
108
|
+
|
109
|
+
expect { Chord.parse_chord_string('Cm') }.to raise_error ArgumentError
|
110
|
+
Chord.parse_chord_string('Cm', 4).should == @c_minor
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should recognize C diminished' do
|
115
|
+
Chord.parse_chord_string('Cdiminished4').should == @c_diminished
|
116
|
+
Chord.parse_chord_string('CDiminished4').should == @c_diminished
|
117
|
+
Chord.parse_chord_string('Cdim4').should == @c_diminished
|
118
|
+
Chord.parse_chord_string('CDim4').should == @c_diminished
|
119
|
+
Chord.parse_chord_string('CDIM4').should == @c_diminished
|
120
|
+
|
121
|
+
expect { Chord.parse_chord_string('Cdim').should == @c_diminished }.to raise_error ArgumentError
|
122
|
+
Chord.parse_chord_string('Cdim', 4).should == @c_diminished
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should recognize C augmented' do
|
126
|
+
Chord.parse_chord_string('Caugmented4').should == @c_augmented
|
127
|
+
Chord.parse_chord_string('Caugmented4').should == @c_augmented
|
128
|
+
Chord.parse_chord_string('Caug4').should == @c_augmented
|
129
|
+
Chord.parse_chord_string('CAug4').should == @c_augmented
|
130
|
+
Chord.parse_chord_string('CAUG4').should == @c_augmented
|
131
|
+
Chord.parse_chord_string('C+4').should == @c_augmented
|
132
|
+
|
133
|
+
expect { Chord.parse_chord_string('Caug').should == @c_augmented }.to raise_error ArgumentError
|
134
|
+
Chord.parse_chord_string('Caug', 4).should == @c_augmented
|
135
|
+
end
|
136
|
+
|
137
|
+
# TODO: Fill out other chords
|
138
|
+
end
|
139
|
+
|
140
|
+
describe '#inversion' do
|
141
|
+
it 'should adjust the lowest n notes up by an octive' do
|
142
|
+
@c_major.inversion(1).should == Chord.new(['E4', 'G4', 'C5'])
|
143
|
+
Chord.new(['Eb4', 'C4', 'Gb4']).inversion(1).should == Chord.new(['Eb4', 'C5', 'Gb4'])
|
144
|
+
|
145
|
+
@c_major.inversion(2).should == Chord.new(['E5', 'G4', 'C5'])
|
146
|
+
Chord.new(['Eb4', 'C4', 'Gb4']).inversion(2).should == Chord.new(['Eb5', 'C5', 'Gb4'])
|
147
|
+
|
148
|
+
@c_augmented_seventh.inversion(3).should == Chord.new(['E5', 'G#5', 'C5', 'Bb4'])
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'should raise an error when the inversion amount is too great' do
|
152
|
+
lambda { @c_major.inversion(3) }.should raise_error(ArgumentError, 'Not enough notes in chord for inversion')
|
153
|
+
lambda { @c_major.inversion(4) }.should raise_error(ArgumentError, 'Not enough notes in chord for inversion')
|
154
|
+
|
155
|
+
lambda { @c_augmented_seventh.inversion(4) }.should raise_error(ArgumentError, 'Not enough notes in chord for inversion')
|
156
|
+
lambda { @c_augmented_seventh.inversion(5) }.should raise_error(ArgumentError, 'Not enough notes in chord for inversion')
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'should raise an error when the inversion amount is too small' do
|
160
|
+
lambda { @c_major.inversion(0) }.should raise_error(ArgumentError, 'Inversion amount must be greater than or equal to 1')
|
161
|
+
lambda { @c_major.inversion(-1) }.should raise_error(ArgumentError, 'Inversion amount must be greater than or equal to 1')
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe '#first_inversion' do
|
166
|
+
it 'should adjust the lowest note up by an octive' do
|
167
|
+
@c_major.first_inversion.should == Chord.new(['E4', 'G4', 'C5'])
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe '#second_inversion' do
|
172
|
+
it 'should adjust the lowest two notes up by an octive' do
|
173
|
+
@c_major.second_inversion.should == Chord.new(['E5', 'G4', 'C5'])
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe '#third_inversion' do
|
178
|
+
it 'should adjust the lowest three notes up by an octive' do
|
179
|
+
@c_augmented_seventh.third_inversion.should == Chord.new(['E5', 'G#5', 'C5', 'Bb4'])
|
180
|
+
end
|
181
|
+
end
|
65
182
|
end
|
data/spec/classes/note_spec.rb
CHANGED
@@ -151,6 +151,330 @@ describe Music::Note do
|
|
151
151
|
end
|
152
152
|
end
|
153
153
|
|
154
|
+
describe 'interval calculations' do
|
155
|
+
let(:c4) { Note.new('C4') }
|
156
|
+
let(:b4) { Note.new('B4') }
|
157
|
+
|
158
|
+
it { c4.should have_an_interval :minor_second, 'C#4' }
|
159
|
+
it { b4.should have_an_interval :minor_second, 'C5' }
|
160
|
+
|
161
|
+
it { c4.should have_an_interval :major_second, 'D4' }
|
162
|
+
it { b4.should have_an_interval :major_second, 'C#5' }
|
163
|
+
|
164
|
+
|
165
|
+
it { c4.should have_an_interval :minor_third, 'D#4' }
|
166
|
+
it { b4.should have_an_interval :minor_third, 'D5' }
|
167
|
+
|
168
|
+
it { c4.should have_an_interval :major_third, 'E4' }
|
169
|
+
it { b4.should have_an_interval :major_third, 'D#5' }
|
170
|
+
|
171
|
+
it { c4.should have_an_interval :perfect_fourth, 'F4' }
|
172
|
+
it { b4.should have_an_interval :perfect_fourth, 'E5' }
|
173
|
+
|
174
|
+
# Enharmonic equivalents
|
175
|
+
it { c4.should have_an_interval :tritone, 'F#4' }
|
176
|
+
it { b4.should have_an_interval :tritone, 'F5' }
|
177
|
+
it { c4.should have_an_interval :diminished_fifth, 'F#4' }
|
178
|
+
it { b4.should have_an_interval :diminished_fifth, 'F5' }
|
179
|
+
it { c4.should have_an_interval :flat_fifth, 'F#4' }
|
180
|
+
it { b4.should have_an_interval :flat_fifth, 'F5' }
|
181
|
+
it { c4.should have_an_interval :augmented_fourth, 'F#4' }
|
182
|
+
it { b4.should have_an_interval :augmented_fourth, 'F5' }
|
183
|
+
|
184
|
+
it { c4.should have_an_interval :perfect_fifth, 'G4' }
|
185
|
+
it { b4.should have_an_interval :perfect_fifth, 'F#5' }
|
186
|
+
|
187
|
+
# Enharmonic equivalents
|
188
|
+
it { c4.should have_an_interval :augmented_fifth, 'G#4' }
|
189
|
+
it { b4.should have_an_interval :augmented_fifth, 'G5' }
|
190
|
+
it { c4.should have_an_interval :minor_sixth, 'G#4' }
|
191
|
+
it { b4.should have_an_interval :minor_sixth, 'G5' }
|
192
|
+
|
193
|
+
it { c4.should have_an_interval :major_sixth, 'A4' }
|
194
|
+
it { b4.should have_an_interval :major_sixth, 'G#5' }
|
195
|
+
|
196
|
+
it { c4.should have_an_interval :diminished_seventh, 'A4' }
|
197
|
+
it { b4.should have_an_interval :diminished_seventh, 'G#5' }
|
198
|
+
|
199
|
+
it { c4.should have_an_interval :minor_seventh, 'A#4' }
|
200
|
+
it { b4.should have_an_interval :minor_seventh, 'A5' }
|
201
|
+
|
202
|
+
it { c4.should have_an_interval :major_seventh, 'B4' }
|
203
|
+
it { b4.should have_an_interval :major_seventh, 'A#5' }
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
describe 'scales from notes (as scale key)' do
|
208
|
+
describe '#major_scale' do
|
209
|
+
Note.new('C4').major_scale.should == [
|
210
|
+
Note.new('C4'),
|
211
|
+
Note.new('D4'),
|
212
|
+
Note.new('E4'),
|
213
|
+
Note.new('F4'),
|
214
|
+
Note.new('G4'),
|
215
|
+
Note.new('A4'),
|
216
|
+
Note.new('B4')
|
217
|
+
]
|
218
|
+
|
219
|
+
Note.new('G4').major_scale.should == [
|
220
|
+
Note.new('G4'),
|
221
|
+
Note.new('A4'),
|
222
|
+
Note.new('B4'),
|
223
|
+
Note.new('C5'),
|
224
|
+
Note.new('D5'),
|
225
|
+
Note.new('E5'),
|
226
|
+
Note.new('F#5')
|
227
|
+
]
|
228
|
+
end
|
229
|
+
|
230
|
+
describe '#minor_scale' do
|
231
|
+
Note.new('C4').minor_scale.should == [
|
232
|
+
Note.new('C4'),
|
233
|
+
Note.new('D4'),
|
234
|
+
Note.new('D#4'),
|
235
|
+
Note.new('F4'),
|
236
|
+
Note.new('G4'),
|
237
|
+
Note.new('G#4'),
|
238
|
+
Note.new('A#4')
|
239
|
+
]
|
240
|
+
|
241
|
+
Note.new('G4').minor_scale.should == [
|
242
|
+
Note.new('G4'),
|
243
|
+
Note.new('A4'),
|
244
|
+
Note.new('A#4'),
|
245
|
+
Note.new('C5'),
|
246
|
+
Note.new('D5'),
|
247
|
+
Note.new('D#5'),
|
248
|
+
Note.new('F5')
|
249
|
+
]
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
describe '#to_s' do
|
254
|
+
it 'should output the note_string' do
|
255
|
+
Note.new('E4').to_s.should == 'E4'
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'should use the sharp version' do
|
259
|
+
Note.new('D#5').to_s.should == 'D#5'
|
260
|
+
Note.new('Eb5').to_s.should == 'D#5'
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe 'chords from notes' do
|
265
|
+
c4 = Note.new('C4')
|
266
|
+
|
267
|
+
c_minor = Chord.new(['C4', 'Eb4', 'G4'])
|
268
|
+
c_major = Chord.new(['C4', 'E4', 'G4'])
|
269
|
+
c_fifth = Chord.new(['C4', 'G4'])
|
270
|
+
c_diminished = Chord.new(['C4', 'Eb4', 'Gb4'])
|
271
|
+
c_augmented = Chord.new(['C4', 'E4', 'G#4'])
|
272
|
+
c_major_seventh = Chord.new(['C4', 'E4', 'G4', 'B4'])
|
273
|
+
c_minor_seventh = Chord.new(['C4', 'Eb4', 'G4', 'Bb4'])
|
274
|
+
c_diminished_seventh = Chord.new(['C4', 'Eb4', 'Gb4', 'A4'])
|
275
|
+
c_augmented_seventh = Chord.new(['C4', 'E4', 'G#4', 'Bb4'])
|
276
|
+
c_half_diminished_seventh = Chord.new(['C4', 'Eb4', 'Gb4', 'Bb4'])
|
277
|
+
|
278
|
+
describe 'chords from notes' do
|
279
|
+
describe '#chord' do
|
280
|
+
it 'should recognize minor chords' do
|
281
|
+
c4.chord(:minor).should == c_minor
|
282
|
+
c4.chord('Minor').should == c_minor
|
283
|
+
c4.chord('minor').should == c_minor
|
284
|
+
c4.chord('min').should == c_minor
|
285
|
+
c4.chord('MIN').should == c_minor
|
286
|
+
c4.chord('m').should == c_minor
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'should recognize major chords' do
|
290
|
+
c4.chord(:major).should == c_major
|
291
|
+
c4.chord('Major').should == c_major
|
292
|
+
c4.chord('major').should == c_major
|
293
|
+
c4.chord('maj').should == c_major
|
294
|
+
c4.chord('MAJ').should == c_major
|
295
|
+
c4.chord('M').should == c_major
|
296
|
+
c4.chord('').should == c_major
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'should recognize power chords' do
|
300
|
+
c4.chord(:power).should == c_fifth
|
301
|
+
c4.chord(:fifth).should == c_fifth
|
302
|
+
c4.chord('Power').should == c_fifth
|
303
|
+
c4.chord('power').should == c_fifth
|
304
|
+
c4.chord('Fifth').should == c_fifth
|
305
|
+
c4.chord('fifth').should == c_fifth
|
306
|
+
c4.chord('pow').should == c_fifth
|
307
|
+
c4.chord('POW').should == c_fifth
|
308
|
+
c4.chord('5').should == c_fifth
|
309
|
+
end
|
310
|
+
|
311
|
+
it 'should recognize diminished chords' do
|
312
|
+
c4.chord(:diminished).should == c_diminished
|
313
|
+
c4.chord('Diminished').should == c_diminished
|
314
|
+
c4.chord('diminished').should == c_diminished
|
315
|
+
c4.chord('dim').should == c_diminished
|
316
|
+
c4.chord('DIM').should == c_diminished
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'should recognize augmented chords' do
|
320
|
+
c4.chord(:augmented).should == c_augmented
|
321
|
+
c4.chord('Augmented').should == c_augmented
|
322
|
+
c4.chord('augmented').should == c_augmented
|
323
|
+
c4.chord('aug').should == c_augmented
|
324
|
+
c4.chord('AUG').should == c_augmented
|
325
|
+
c4.chord('+').should == c_augmented
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'should recognize major seventh chords' do
|
329
|
+
c4.chord(:major_seventh).should == c_major_seventh
|
330
|
+
c4.chord('major_seventh').should == c_major_seventh
|
331
|
+
c4.chord('major seventh').should == c_major_seventh
|
332
|
+
c4.chord('Major seventh').should == c_major_seventh
|
333
|
+
c4.chord('maj seventh').should == c_major_seventh
|
334
|
+
c4.chord('maj 7').should == c_major_seventh
|
335
|
+
c4.chord('maj 7th').should == c_major_seventh
|
336
|
+
c4.chord('maj7').should == c_major_seventh
|
337
|
+
c4.chord('maj7th').should == c_major_seventh
|
338
|
+
c4.chord('MAJ7').should == c_major_seventh
|
339
|
+
c4.chord('M7').should == c_major_seventh
|
340
|
+
end
|
341
|
+
|
342
|
+
it 'should recognize minor seventh chords' do
|
343
|
+
c4.chord(:minor_seventh).should == c_minor_seventh
|
344
|
+
c4.chord('minor_seventh').should == c_minor_seventh
|
345
|
+
c4.chord('minor seventh').should == c_minor_seventh
|
346
|
+
c4.chord('minor seventh').should == c_minor_seventh
|
347
|
+
c4.chord('min seventh').should == c_minor_seventh
|
348
|
+
c4.chord('min 7').should == c_minor_seventh
|
349
|
+
c4.chord('min 7th').should == c_minor_seventh
|
350
|
+
c4.chord('min7').should == c_minor_seventh
|
351
|
+
c4.chord('min7th').should == c_minor_seventh
|
352
|
+
c4.chord('min7').should == c_minor_seventh
|
353
|
+
c4.chord('m7').should == c_minor_seventh
|
354
|
+
end
|
355
|
+
|
356
|
+
it 'should recognize diminished seventh chords' do
|
357
|
+
c4.chord(:diminished_seventh).should == c_diminished_seventh
|
358
|
+
c4.chord('diminished_seventh').should == c_diminished_seventh
|
359
|
+
c4.chord('diminished seventh').should == c_diminished_seventh
|
360
|
+
c4.chord('diminished seventh').should == c_diminished_seventh
|
361
|
+
c4.chord('dim seventh').should == c_diminished_seventh
|
362
|
+
c4.chord('dim 7').should == c_diminished_seventh
|
363
|
+
c4.chord('dim 7th').should == c_diminished_seventh
|
364
|
+
c4.chord('dim7').should == c_diminished_seventh
|
365
|
+
c4.chord('dim7th').should == c_diminished_seventh
|
366
|
+
c4.chord('dim7').should == c_diminished_seventh
|
367
|
+
c4.chord('d7').should == c_diminished_seventh
|
368
|
+
end
|
369
|
+
|
370
|
+
it 'should recognize augmented seventh chords' do
|
371
|
+
c4.chord(:augmented_seventh).should == c_augmented_seventh
|
372
|
+
c4.chord('augmented_seventh').should == c_augmented_seventh
|
373
|
+
c4.chord('augmented seventh').should == c_augmented_seventh
|
374
|
+
c4.chord('augmented seventh').should == c_augmented_seventh
|
375
|
+
c4.chord('aug seventh').should == c_augmented_seventh
|
376
|
+
c4.chord('aug 7').should == c_augmented_seventh
|
377
|
+
c4.chord('aug 7th').should == c_augmented_seventh
|
378
|
+
c4.chord('aug7').should == c_augmented_seventh
|
379
|
+
c4.chord('aug7th').should == c_augmented_seventh
|
380
|
+
c4.chord('aug7').should == c_augmented_seventh
|
381
|
+
c4.chord('+7').should == c_augmented_seventh
|
382
|
+
end
|
383
|
+
|
384
|
+
it 'should recognize half diminished seventh chords' do
|
385
|
+
c4.chord(:half_diminished_seventh).should == c_half_diminished_seventh
|
386
|
+
c4.chord('half_diminished_7').should == c_half_diminished_seventh
|
387
|
+
c4.chord('half_diminished_7th').should == c_half_diminished_seventh
|
388
|
+
c4.chord('half-diminished seventh').should == c_half_diminished_seventh
|
389
|
+
c4.chord('half-diminished 7').should == c_half_diminished_seventh
|
390
|
+
c4.chord('half-diminished 7th').should == c_half_diminished_seventh
|
391
|
+
c4.chord('half_dim seventh').should == c_half_diminished_seventh
|
392
|
+
c4.chord('half_dim 7').should == c_half_diminished_seventh
|
393
|
+
c4.chord('half_dim 7th').should == c_half_diminished_seventh
|
394
|
+
c4.chord('half_dim7').should == c_half_diminished_seventh
|
395
|
+
c4.chord('half_dim7th').should == c_half_diminished_seventh
|
396
|
+
c4.chord('half_dim7').should == c_half_diminished_seventh
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
describe 'chord methods' do
|
401
|
+
it 'should have minor methods' do
|
402
|
+
c4.minor_chord.should == c_minor
|
403
|
+
c4.min_chord.should == c_minor
|
404
|
+
c4.m_chord.should == c_minor
|
405
|
+
end
|
406
|
+
|
407
|
+
it 'should have major methods' do
|
408
|
+
c4.major_chord.should == c_major
|
409
|
+
c4.maj_chord.should == c_major
|
410
|
+
c4.M_chord.should == c_major
|
411
|
+
end
|
412
|
+
|
413
|
+
it 'should have diminished methods' do
|
414
|
+
c4.diminished_chord.should == c_diminished
|
415
|
+
c4.dim_chord.should == c_diminished
|
416
|
+
end
|
417
|
+
|
418
|
+
it 'should have augmented methods' do
|
419
|
+
c4.augmented_chord.should == c_augmented
|
420
|
+
c4.aug_chord.should == c_augmented
|
421
|
+
end
|
422
|
+
|
423
|
+
it 'should have major seventh methods' do
|
424
|
+
c4.major_seventh_chord.should == c_major_seventh
|
425
|
+
c4.maj_seventh_chord.should == c_major_seventh
|
426
|
+
c4.maj_7_chord.should == c_major_seventh
|
427
|
+
c4.maj_7th_chord.should == c_major_seventh
|
428
|
+
c4.maj7_chord.should == c_major_seventh
|
429
|
+
c4.maj7th_chord.should == c_major_seventh
|
430
|
+
c4.M7_chord.should == c_major_seventh
|
431
|
+
end
|
432
|
+
|
433
|
+
it 'should have minor seventh methods' do
|
434
|
+
c4.minor_seventh_chord.should == c_minor_seventh
|
435
|
+
c4.min_seventh_chord.should == c_minor_seventh
|
436
|
+
c4.min_7_chord.should == c_minor_seventh
|
437
|
+
c4.min_7th_chord.should == c_minor_seventh
|
438
|
+
c4.min7_chord.should == c_minor_seventh
|
439
|
+
c4.min7th_chord.should == c_minor_seventh
|
440
|
+
c4.m7_chord.should == c_minor_seventh
|
441
|
+
end
|
442
|
+
|
443
|
+
it 'should have diminished seventh methods' do
|
444
|
+
c4.diminished_seventh_chord.should == c_diminished_seventh
|
445
|
+
c4.dim_seventh_chord.should == c_diminished_seventh
|
446
|
+
c4.dim_7_chord.should == c_diminished_seventh
|
447
|
+
c4.dim_7th_chord.should == c_diminished_seventh
|
448
|
+
c4.dim7_chord.should == c_diminished_seventh
|
449
|
+
c4.dim7th_chord.should == c_diminished_seventh
|
450
|
+
c4.d7_chord.should == c_diminished_seventh
|
451
|
+
end
|
452
|
+
|
453
|
+
it 'should have augmented seventh methods' do
|
454
|
+
c4.augmented_seventh_chord.should == c_augmented_seventh
|
455
|
+
c4.aug_seventh_chord.should == c_augmented_seventh
|
456
|
+
c4.aug_7_chord.should == c_augmented_seventh
|
457
|
+
c4.aug_7th_chord.should == c_augmented_seventh
|
458
|
+
c4.aug7_chord.should == c_augmented_seventh
|
459
|
+
c4.aug7th_chord.should == c_augmented_seventh
|
460
|
+
c4.send('+7_chord').should == c_augmented_seventh
|
461
|
+
end
|
462
|
+
|
463
|
+
it 'should have half diminished seventh methods' do
|
464
|
+
c4.half_diminished_seventh_chord.should == c_half_diminished_seventh
|
465
|
+
c4.half_diminished_7_chord.should == c_half_diminished_seventh
|
466
|
+
c4.half_diminished_7th_chord.should == c_half_diminished_seventh
|
467
|
+
c4.half_dim_seventh_chord.should == c_half_diminished_seventh
|
468
|
+
c4.half_dim_7_chord.should == c_half_diminished_seventh
|
469
|
+
c4.half_dim_7th_chord.should == c_half_diminished_seventh
|
470
|
+
c4.half_dim7_chord.should == c_half_diminished_seventh
|
471
|
+
c4.half_dim7th_chord.should == c_half_diminished_seventh
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
end
|
477
|
+
|
154
478
|
describe ".calculate_frequency(letter, accidental, octave)" do
|
155
479
|
{
|
156
480
|
['C', nil, 0] => 16.35,
|
@@ -201,6 +525,8 @@ describe Music::Note do
|
|
201
525
|
end
|
202
526
|
end
|
203
527
|
|
528
|
+
# TODO: Should return accurracy
|
529
|
+
# Thought: ((frequency off) / (distance to next note's frequency)) * 2.0?
|
204
530
|
describe ".calculate_note(frequency)" do
|
205
531
|
test_frequencies = {
|
206
532
|
[16.35] => ['C', nil, 0],
|
@@ -258,7 +584,7 @@ describe Music::Note do
|
|
258
584
|
end
|
259
585
|
|
260
586
|
it "should allow getting of previous note" do
|
261
|
-
Note.new(739.99).
|
587
|
+
Note.new(739.99).prev.should == Note.new(698.46)
|
262
588
|
end
|
263
589
|
end
|
264
590
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
RSpec::Matchers.define :have_an_interval do |interval_description, expected_note_string|
|
2
|
+
match do |note|
|
3
|
+
note.send(interval_description).note_string == expected_note_string
|
4
|
+
end
|
5
|
+
|
6
|
+
failure_message_for_should do |note|
|
7
|
+
"Expected #{note.note_string}'s #{interval_description} interval to be #{expected_note_string}"
|
8
|
+
end
|
9
|
+
|
10
|
+
description do |note|
|
11
|
+
"have an #{interval_description} interval of #{expected_note_string} for #{note.note_string}"
|
12
|
+
end
|
13
|
+
end
|
metadata
CHANGED
@@ -1,98 +1,94 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: music
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.1
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 5
|
9
|
-
- 1
|
10
|
-
version: 0.5.1
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Brian Underwood
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2012-11-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: rake
|
22
|
-
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
24
17
|
none: false
|
25
|
-
requirements:
|
26
|
-
- -
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
segments:
|
30
|
-
- 0
|
31
|
-
- 9
|
32
|
-
version: "0.9"
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.9'
|
33
22
|
type: :development
|
34
|
-
version_requirements: *id001
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
|
-
name: bundler
|
37
23
|
prerelease: false
|
38
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.9'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: bundler
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
39
33
|
none: false
|
40
|
-
requirements:
|
41
|
-
- -
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
hash: 21
|
44
|
-
segments:
|
45
|
-
- 1
|
46
|
-
- 1
|
47
|
-
- 3
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
48
37
|
version: 1.1.3
|
49
38
|
type: :development
|
50
|
-
version_requirements: *id002
|
51
|
-
- !ruby/object:Gem::Dependency
|
52
|
-
name: rspec
|
53
39
|
prerelease: false
|
54
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.1.3
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
55
49
|
none: false
|
56
|
-
requirements:
|
57
|
-
- -
|
58
|
-
- !ruby/object:Gem::Version
|
59
|
-
|
60
|
-
segments:
|
61
|
-
- 0
|
62
|
-
version: "0"
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
63
54
|
type: :development
|
64
|
-
version_requirements: *id003
|
65
|
-
- !ruby/object:Gem::Dependency
|
66
|
-
name: activemodel
|
67
55
|
prerelease: false
|
68
|
-
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: activemodel
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
69
65
|
none: false
|
70
|
-
requirements:
|
71
|
-
- -
|
72
|
-
- !ruby/object:Gem::Version
|
73
|
-
hash: 15
|
74
|
-
segments:
|
75
|
-
- 3
|
76
|
-
- 2
|
77
|
-
- 0
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
78
69
|
version: 3.2.0
|
79
70
|
type: :development
|
80
|
-
|
81
|
-
|
82
|
-
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 3.2.0
|
78
|
+
description: Library for classifying notes and chords and performing calculations
|
79
|
+
on them. See README.md
|
80
|
+
email:
|
83
81
|
- ml+musicgem@semi-sentient.com
|
84
82
|
executables: []
|
85
|
-
|
86
83
|
extensions: []
|
87
|
-
|
88
84
|
extra_rdoc_files: []
|
89
|
-
|
90
|
-
files:
|
85
|
+
files:
|
91
86
|
- .document
|
92
87
|
- .gitignore
|
93
88
|
- .travis.yml
|
94
89
|
- Gemfile
|
95
90
|
- Gemfile.lock
|
91
|
+
- Guardfile
|
96
92
|
- LICENSE.txt
|
97
93
|
- README.md
|
98
94
|
- Rakefile
|
@@ -106,43 +102,35 @@ files:
|
|
106
102
|
- spec/classes/hash_spec.rb
|
107
103
|
- spec/classes/note_spec.rb
|
108
104
|
- spec/spec_helper.rb
|
105
|
+
- spec/support/matchers/have_an_interval.rb
|
109
106
|
homepage: http://github.com/cheerfulstoic/music
|
110
|
-
licenses:
|
107
|
+
licenses:
|
111
108
|
- MIT
|
112
109
|
post_install_message:
|
113
110
|
rdoc_options: []
|
114
|
-
|
115
|
-
require_paths:
|
111
|
+
require_paths:
|
116
112
|
- lib
|
117
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
118
114
|
none: false
|
119
|
-
requirements:
|
120
|
-
- -
|
121
|
-
- !ruby/object:Gem::Version
|
122
|
-
|
123
|
-
|
124
|
-
- 0
|
125
|
-
version: "0"
|
126
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ! '>='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
120
|
none: false
|
128
|
-
requirements:
|
129
|
-
- -
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
|
132
|
-
segments:
|
133
|
-
- 1
|
134
|
-
- 8
|
135
|
-
version: "1.8"
|
121
|
+
requirements:
|
122
|
+
- - ! '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.8'
|
136
125
|
requirements: []
|
137
|
-
|
138
126
|
rubyforge_project:
|
139
|
-
rubygems_version: 1.8.
|
127
|
+
rubygems_version: 1.8.23
|
140
128
|
signing_key:
|
141
129
|
specification_version: 3
|
142
130
|
summary: Library for performing calculations on musical elements
|
143
|
-
test_files:
|
131
|
+
test_files:
|
144
132
|
- spec/classes/chord_spec.rb
|
145
133
|
- spec/classes/hash_spec.rb
|
146
134
|
- spec/classes/note_spec.rb
|
147
135
|
- spec/spec_helper.rb
|
148
|
-
|
136
|
+
- spec/support/matchers/have_an_interval.rb
|