head_music 0.4.0 → 0.5.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 +4 -4
- data/head_music.gemspec +1 -0
- data/lib/head_music/functional_interval.rb +84 -3
- data/lib/head_music/interval.rb +1 -1
- data/lib/head_music/letter.rb +16 -0
- data/lib/head_music/pitch.rb +7 -1
- data/lib/head_music/quality.rb +35 -2
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music.rb +1 -0
- metadata +15 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 63d427018297be00f798c809a49236ad36556096
|
4
|
+
data.tar.gz: 8fcbff20bc04bee42a900b5d9bef1dabb9b9bf1f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5eec89666c474d7cbe5114ca4504f57bdddfc3eb9899b1fe666946cb9b4c8fd8dc4ac144a95512bb9dd0f86a7060fd6e1a201fd78daa94f701095bf8456ecdca
|
7
|
+
data.tar.gz: 692a3fc51fdbe388e498d873a45d4f50776dcf9b5669b65bf4694986aa94b8be8a7b4208a6243487c21ba00dbab86f4be82aeeb45470c358f1fed40d730e1f62
|
data/head_music.gemspec
CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
24
|
spec.add_runtime_dependency "activesupport", "~> 5.0"
|
25
|
+
spec.add_runtime_dependency "humanize", "~> 1.3"
|
25
26
|
|
26
27
|
spec.add_development_dependency "bundler", "~> 1.13"
|
27
28
|
spec.add_development_dependency "rake", "~> 10.0"
|
@@ -1,9 +1,90 @@
|
|
1
1
|
class HeadMusic::FunctionalInterval
|
2
|
-
|
3
|
-
|
2
|
+
NUMBER_NAMES = %w[unison second third fourth fifth sixth seventh octave ninth tenth eleventh twelfth thirteenth fourteenth fifteenth sixteenth seventeenth]
|
3
|
+
NAME_SUFFIXES = Hash.new('th').merge({ 1 => 'st', 2 => 'nd', 3 => 'rd' })
|
4
|
+
|
5
|
+
QUALITY = {
|
6
|
+
unison: {perfect: 0},
|
7
|
+
second: {major: 2},
|
8
|
+
third: {major: 4},
|
9
|
+
fourth: {perfect: 5},
|
10
|
+
fifth: {perfect: 7},
|
11
|
+
sixth: {major: 9},
|
12
|
+
seventh: {major: 11},
|
13
|
+
}
|
14
|
+
|
15
|
+
attr_reader :lower_pitch, :higher_pitch
|
16
|
+
|
17
|
+
def initialize(pitch1, pitch2)
|
18
|
+
@lower_pitch, @higher_pitch = [HeadMusic::Pitch.get(pitch1), HeadMusic::Pitch.get(pitch2)].sort
|
19
|
+
end
|
20
|
+
|
21
|
+
def number
|
22
|
+
simple_number + octaves * 7
|
23
|
+
end
|
24
|
+
|
25
|
+
def simple_number
|
26
|
+
@simple_number ||= @lower_pitch.letter.steps_to(@higher_pitch.letter) + 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def simple_semitones
|
30
|
+
semitones % 12
|
31
|
+
end
|
32
|
+
|
33
|
+
def semitones
|
34
|
+
(@higher_pitch - @lower_pitch).to_i
|
35
|
+
end
|
36
|
+
|
37
|
+
def octaves
|
38
|
+
(higher_pitch.number - lower_pitch.number) / 12
|
39
|
+
end
|
40
|
+
|
41
|
+
def compound?
|
42
|
+
!simple?
|
43
|
+
end
|
44
|
+
|
45
|
+
def simple?
|
46
|
+
octaves == 0
|
47
|
+
end
|
48
|
+
|
49
|
+
def simple_name
|
50
|
+
[quality_name, simple_number_name].join(' ')
|
4
51
|
end
|
5
52
|
|
6
53
|
def name
|
7
|
-
|
54
|
+
if number < NUMBER_NAMES.length
|
55
|
+
[quality_name, number_name].join(' ')
|
56
|
+
elsif simple_name == 'perfect unison'
|
57
|
+
string = "#{octaves.humanize} octaves"
|
58
|
+
else
|
59
|
+
"#{octaves.humanize} octaves and #{quality.article} #{simple_name}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def shorthand
|
64
|
+
[quality.shorthand, number].join
|
65
|
+
end
|
66
|
+
|
67
|
+
def quality
|
68
|
+
HeadMusic::Quality.get(quality_name)
|
69
|
+
end
|
70
|
+
|
71
|
+
def quality_name
|
72
|
+
starting_quality = QUALITY[simple_number_name.to_sym].keys.first
|
73
|
+
delta = simple_semitones - QUALITY[simple_number_name.to_sym][starting_quality]
|
74
|
+
if starting_quality == :perfect
|
75
|
+
HeadMusic::Quality::PERFECT_INTERVAL_MODIFICATION[delta]
|
76
|
+
elsif starting_quality == :major
|
77
|
+
HeadMusic::Quality::MAJOR_INTERVAL_MODIFICATION[delta]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def simple_number_name
|
82
|
+
NUMBER_NAMES[simple_number - 1]
|
83
|
+
end
|
84
|
+
|
85
|
+
def number_name
|
86
|
+
NUMBER_NAMES[number - 1] || begin
|
87
|
+
number.to_s + NAME_SUFFIXES[number % 10]
|
88
|
+
end
|
8
89
|
end
|
9
90
|
end
|
data/lib/head_music/interval.rb
CHANGED
data/lib/head_music/letter.rb
CHANGED
@@ -54,6 +54,22 @@ class HeadMusic::Letter
|
|
54
54
|
to_s == value.to_s
|
55
55
|
end
|
56
56
|
|
57
|
+
def position
|
58
|
+
NAMES.index(self.to_s) + 1
|
59
|
+
end
|
60
|
+
|
61
|
+
def steps_to(other, direction = :ascending)
|
62
|
+
other = Letter.get(other)
|
63
|
+
other_position = other.position
|
64
|
+
if direction == :descending
|
65
|
+
other_position -= NAMES.length if other_position > position
|
66
|
+
position - other_position
|
67
|
+
else
|
68
|
+
other_position += NAMES.length if other_position < position
|
69
|
+
other_position - position
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
57
73
|
def cycle
|
58
74
|
cycle = NAMES
|
59
75
|
while cycle.first != self.to_s
|
data/lib/head_music/pitch.rb
CHANGED
@@ -78,7 +78,13 @@ class HeadMusic::Pitch
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def -(value)
|
81
|
-
|
81
|
+
if value.is_a?(HeadMusic::Pitch)
|
82
|
+
# return an interval
|
83
|
+
HeadMusic::Interval.get(self.to_i - value.to_i)
|
84
|
+
else
|
85
|
+
# assume value represents an interval in semitones and return another pitch
|
86
|
+
HeadMusic::Pitch.get(self.to_i - value.to_i)
|
87
|
+
end
|
82
88
|
end
|
83
89
|
|
84
90
|
def ==(value)
|
data/lib/head_music/quality.rb
CHANGED
@@ -1,10 +1,35 @@
|
|
1
1
|
class HeadMusic::Quality
|
2
|
-
|
2
|
+
SHORTHAND = {
|
3
|
+
perfect: 'P',
|
4
|
+
major: 'M',
|
5
|
+
minor: 'm',
|
6
|
+
diminished: 'd',
|
7
|
+
augmented: 'A',
|
8
|
+
doubly_diminished: 'dd',
|
9
|
+
doubly_augmented: 'AA',
|
10
|
+
}
|
11
|
+
NAMES = SHORTHAND.keys
|
12
|
+
|
13
|
+
PERFECT_INTERVAL_MODIFICATION = {
|
14
|
+
-2 => :doubly_diminished,
|
15
|
+
-1 => :diminished,
|
16
|
+
0 => :perfect,
|
17
|
+
1 => :augmented,
|
18
|
+
2 => :doubly_augmented
|
19
|
+
}
|
20
|
+
|
21
|
+
MAJOR_INTERVAL_MODIFICATION = {
|
22
|
+
-2 => :diminished,
|
23
|
+
-1 => :minor,
|
24
|
+
0 => :major,
|
25
|
+
1 => :augmented,
|
26
|
+
2 => :doubly_augmented
|
27
|
+
}
|
3
28
|
|
4
29
|
def self.get(identifier)
|
5
30
|
@qualities ||= {}
|
6
31
|
identifier = identifier.to_s.to_sym
|
7
|
-
@qualities[identifier] ||= new(identifier) if
|
32
|
+
@qualities[identifier] ||= new(identifier) if NAMES.include?(identifier)
|
8
33
|
end
|
9
34
|
|
10
35
|
attr_reader :name
|
@@ -18,5 +43,13 @@ class HeadMusic::Quality
|
|
18
43
|
self.to_s == other.to_s
|
19
44
|
end
|
20
45
|
|
46
|
+
def shorthand
|
47
|
+
SHORTHAND[name]
|
48
|
+
end
|
49
|
+
|
50
|
+
def article
|
51
|
+
%w[a e i o u h].include?(name.to_s.first) ? 'an' : 'a'
|
52
|
+
end
|
53
|
+
|
21
54
|
private_class_method :new
|
22
55
|
end
|
data/lib/head_music/version.rb
CHANGED
data/lib/head_music.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: head_music
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob Head
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '5.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: humanize
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|