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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 072224f0fd9e0fff2051143e6a786daf70a975e4
4
- data.tar.gz: a5c8934d86c94c68c26607a88e324f6670ceb97b
3
+ metadata.gz: 63d427018297be00f798c809a49236ad36556096
4
+ data.tar.gz: 8fcbff20bc04bee42a900b5d9bef1dabb9b9bf1f
5
5
  SHA512:
6
- metadata.gz: d68c00dbf07773b7001c6d245dfc11957a111f979d4f919f98b7f9c336108017024ba86ad4a6b9353b054dbef7432230e72d10c9b9beaa49d0040c9572be4ef3
7
- data.tar.gz: d722a4f6569009c22db17dac114f2a61a9e9efdbbca1e8a71a727ac2dfdd238d06e551e78716537a8520c2dbe691b3f14942833be2e68bbc2a7ef0b8c0373f01
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
- def self.between(pitch1, pitch2)
3
- new
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
- 'perfect fifth'
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
@@ -21,7 +21,7 @@ class HeadMusic::Interval
21
21
  @semitones = semitones
22
22
  end
23
23
 
24
- def simplified
24
+ def simple
25
25
  HeadMusic::Interval.get(semitones % 12)
26
26
  end
27
27
 
@@ -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
@@ -78,7 +78,13 @@ class HeadMusic::Pitch
78
78
  end
79
79
 
80
80
  def -(value)
81
- Pitch.get(self.to_i - value.to_i)
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)
@@ -1,10 +1,35 @@
1
1
  class HeadMusic::Quality
2
- QUALITY_NAMES = %w[perfect major minor diminished augmented].map(&:to_sym)
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 QUALITY_NAMES.include?(identifier)
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
@@ -1,3 +1,3 @@
1
1
  module HeadMusic
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/head_music.rb CHANGED
@@ -2,6 +2,7 @@ require "head_music/version"
2
2
 
3
3
  require "active_support/core_ext/module/delegation"
4
4
  require "active_support/core_ext/string/access"
5
+ require "humanize"
5
6
 
6
7
  require "head_music/accidental"
7
8
  require "head_music/circle"
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.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