head_music 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 63d427018297be00f798c809a49236ad36556096
4
- data.tar.gz: 8fcbff20bc04bee42a900b5d9bef1dabb9b9bf1f
3
+ metadata.gz: 27bbe45d50118d4f5f4bcf02e3192da3ddd2ec70
4
+ data.tar.gz: 1900eb67572a4bfb79e0b91c05673bfadd1f7c2c
5
5
  SHA512:
6
- metadata.gz: 5eec89666c474d7cbe5114ca4504f57bdddfc3eb9899b1fe666946cb9b4c8fd8dc4ac144a95512bb9dd0f86a7060fd6e1a201fd78daa94f701095bf8456ecdca
7
- data.tar.gz: 692a3fc51fdbe388e498d873a45d4f50776dcf9b5669b65bf4694986aa94b8be8a7b4208a6243487c21ba00dbab86f4be82aeeb45470c358f1fed40d730e1f62
6
+ metadata.gz: 30f5b988d501ca3286bc41da70c3ca3672d9d30438d81a5740c1c8db4bd1d4e3e9af594302aa928afea446c2d6197f1b3aa0d69280c1e94fc986e03359712872
7
+ data.tar.gz: d5534e5c7dadf6b7339d29b4db89256cf2d853b5415add44032c15eb24f1e10a83bb0d185d4437ef319a29f7c003f0b90228e3cce19bcfe22d5d73646255f561
@@ -2,18 +2,63 @@ class HeadMusic::FunctionalInterval
2
2
  NUMBER_NAMES = %w[unison second third fourth fifth sixth seventh octave ninth tenth eleventh twelfth thirteenth fourteenth fifteenth sixteenth seventeenth]
3
3
  NAME_SUFFIXES = Hash.new('th').merge({ 1 => 'st', 2 => 'nd', 3 => 'rd' })
4
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},
5
+ QUALITY_SEMITONES = {
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
+ octave: { perfect: 12 },
14
+ ninth: { major: 14 },
15
+ tenth: { major: 16 },
16
+ eleventh: { perfect: 17 },
17
+ twelfth: { perfect: 19 },
18
+ thirteenth: { major: 21 },
19
+ fourteenth: { major: 23 },
20
+ fifteenth: { perfect: 24 },
21
+ sixteenth: { major: 26 },
22
+ seventeenth: { major: 28 }
13
23
  }
14
24
 
15
25
  attr_reader :lower_pitch, :higher_pitch
16
26
 
27
+ delegate :to_s, to: :name
28
+ delegate :==, to: :to_s
29
+
30
+ def self.get(name)
31
+ words = name.to_s.split(/[_ ]+/)
32
+ quality_name, degree_name = words[0..-2].join(' '), words.last
33
+ lower_pitch = HeadMusic::Pitch.get('C4')
34
+ steps = NUMBER_NAMES.index(degree_name)
35
+ higher_letter = lower_pitch.letter.steps(steps)
36
+ semitones = degree_quality_semitones.dig(degree_name.to_sym, quality_name.to_sym)
37
+ higher_pitch = HeadMusic::Pitch.from_number_and_letter(lower_pitch + semitones, higher_letter)
38
+ new(lower_pitch, higher_pitch)
39
+ end
40
+
41
+ def self.degree_quality_semitones
42
+ @degree_quality_semitones ||= begin
43
+ degree_quality_semitones = QUALITY_SEMITONES
44
+ QUALITY_SEMITONES.each do |degree_name, qualities|
45
+ default_quality = qualities.keys.first
46
+ if default_quality == :perfect
47
+ modification_hash = HeadMusic::Quality::PERFECT_INTERVAL_MODIFICATION.invert
48
+ else
49
+ modification_hash = HeadMusic::Quality::MAJOR_INTERVAL_MODIFICATION.invert
50
+ end
51
+ default_semitones = qualities[default_quality]
52
+ modification_hash.each do |quality_name, delta|
53
+ if quality_name != default_quality
54
+ degree_quality_semitones[degree_name][quality_name] = default_semitones + delta
55
+ end
56
+ end
57
+ end
58
+ degree_quality_semitones
59
+ end
60
+ end
61
+
17
62
  def initialize(pitch1, pitch2)
18
63
  @lower_pitch, @higher_pitch = [HeadMusic::Pitch.get(pitch1), HeadMusic::Pitch.get(pitch2)].sort
19
64
  end
@@ -69,13 +114,9 @@ class HeadMusic::FunctionalInterval
69
114
  end
70
115
 
71
116
  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
117
+ starting_quality = QUALITY_SEMITONES[simple_number_name.to_sym].keys.first
118
+ delta = simple_semitones - QUALITY_SEMITONES[simple_number_name.to_sym][starting_quality]
119
+ HeadMusic::Quality::from(starting_quality, delta)
79
120
  end
80
121
 
81
122
  def simple_number_name
@@ -87,4 +128,12 @@ class HeadMusic::FunctionalInterval
87
128
  number.to_s + NAME_SUFFIXES[number % 10]
88
129
  end
89
130
  end
131
+
132
+ def inversion
133
+ inverted_low_pitch = lower_pitch
134
+ while inverted_low_pitch < higher_pitch
135
+ inverted_low_pitch += 12
136
+ end
137
+ HeadMusic::FunctionalInterval.new(higher_pitch, inverted_low_pitch)
138
+ end
90
139
  end
@@ -58,6 +58,10 @@ class HeadMusic::Letter
58
58
  NAMES.index(self.to_s) + 1
59
59
  end
60
60
 
61
+ def steps(num)
62
+ cycle[num]
63
+ end
64
+
61
65
  def steps_to(other, direction = :ascending)
62
66
  other = Letter.get(other)
63
67
  other_position = other.position
@@ -7,6 +7,7 @@ class HeadMusic::Pitch
7
7
  delegate :letter, :letter_cycle, to: :spelling
8
8
  delegate :accidental, :sharp?, :flat?, to: :spelling
9
9
  delegate :pitch_class, to: :spelling
10
+ delegate :semitones, to: :accidental, prefix: true, allow_nil: true
10
11
 
11
12
  delegate :smallest_interval_to, to: :pitch_class
12
13
 
@@ -29,7 +30,7 @@ class HeadMusic::Pitch
29
30
  def self.from_number_and_letter(number, letter)
30
31
  letter = HeadMusic::Letter.get(letter)
31
32
  natural_letter_pitch = get(HeadMusic::Letter.get(letter).pitch_class)
32
- natural_letter_pitch += 12 while (number - natural_letter_pitch.to_i) >= 11
33
+ natural_letter_pitch += 12 while (number - natural_letter_pitch.to_i).to_i >= 11
33
34
  natural_letter_pitch = get(natural_letter_pitch)
34
35
  accidental_interval = natural_letter_pitch.smallest_interval_to(HeadMusic::PitchClass.get(number))
35
36
  accidental = HeadMusic::Accidental.for_interval(accidental_interval)
@@ -55,7 +56,7 @@ class HeadMusic::Pitch
55
56
  end
56
57
 
57
58
  def midi_note_number
58
- (octave + 1) * 12 + pitch_class.to_i
59
+ (octave + 1) * 12 + letter.pitch_class.to_i + accidental_semitones.to_i
59
60
  end
60
61
 
61
62
  alias_method :midi, :midi_note_number
@@ -32,6 +32,14 @@ class HeadMusic::Quality
32
32
  @qualities[identifier] ||= new(identifier) if NAMES.include?(identifier)
33
33
  end
34
34
 
35
+ def self.from(starting_quality, delta)
36
+ if starting_quality == :perfect
37
+ PERFECT_INTERVAL_MODIFICATION[delta].to_s.gsub(/_+/, ' ')
38
+ elsif starting_quality == :major
39
+ MAJOR_INTERVAL_MODIFICATION[delta].to_s.gsub(/_+/, ' ')
40
+ end
41
+ end
42
+
35
43
  attr_reader :name
36
44
  delegate :to_s, to: :name
37
45
 
@@ -1,3 +1,3 @@
1
1
  module HeadMusic
2
- VERSION = "0.5.0"
2
+ VERSION = "0.5.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: head_music
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Head
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-02-20 00:00:00.000000000 Z
11
+ date: 2017-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport