head_music 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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