head_music 0.1.0 → 0.1.1

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: 180ffc27218ca1ef9eacf64c6d93aff2ab81fc49
4
- data.tar.gz: 76e58d1d8ed78ca58ebe899bedfed0756d7104fc
3
+ metadata.gz: 161df8f1795f4e70adcdc8d7298cdbf1dd2af984
4
+ data.tar.gz: 621aac81ec102840a879186df55eb9aeed040b8b
5
5
  SHA512:
6
- metadata.gz: 730ecdb5ebfadc4cc18c2776b3413a74c31629249e20376ad27928a762347b89ec32392fa4dc441bcd84724e41208915874cfbdacd3b75119c2910638339ba47
7
- data.tar.gz: 1f5ed8080b5584f26702fe7f257459975923ddff48be3239050e721837f65abe3e3f8863dd865e39b2161900b1fd924eec03d2c6337860d7d27bbde69f10537e
6
+ metadata.gz: 947631876b0c2c74a807515783a22783836838d75d2a21eab2f66e8863a5b25d5849e92874885007a99dd0596acacb5c795ad0ffc153d47cac616322ec566fef
7
+ data.tar.gz: 9ff5a2e01209ccd7f515d9d152f49159a0f18518f7bfb17e289609cb519395b96e7ae4399dd321346b3b971f05ae970d2095fb4556ca1bb666b36d268f46fe2c
@@ -8,9 +8,13 @@ class HeadMusic::Accidental
8
8
 
9
9
  attr_reader :string
10
10
 
11
- def self.get(string)
11
+ def self.get(identifier)
12
12
  @accidentals ||= {}
13
- @accidentals[string] ||= new(string)
13
+ @accidentals[identifier] ||= for_symbol(identifier) || for_interval(identifier)
14
+ end
15
+
16
+ def self.for_symbol(identifier)
17
+ new(identifier) if ACCIDENTAL_SEMITONES.keys.include?(identifier)
14
18
  end
15
19
 
16
20
  def self.for_interval(semitones)
@@ -19,6 +19,10 @@ class HeadMusic::Circle
19
19
  @pitch_classes = pitch_classes_by_interval(interval)
20
20
  end
21
21
 
22
+ def index(pitch_class)
23
+ @pitch_classes.index(HeadMusic::Spelling.get(pitch_class).pitch_class)
24
+ end
25
+
22
26
  private_class_method :new
23
27
 
24
28
  private
@@ -21,11 +21,17 @@ class HeadMusic::KeySignature
21
21
  end
22
22
 
23
23
  def num_sharps
24
- (HeadMusic::Circle.of_fifths.pitch_classes.index(tonic_pitch_class) - scale_type_adjustment) % 12
24
+ (HeadMusic::Circle.of_fifths.index(tonic_pitch_class) - scale_type_adjustment) % 12
25
25
  end
26
26
 
27
27
  def num_flats
28
- (HeadMusic::Circle.of_fourths.pitch_classes.index(tonic_pitch_class) + scale_type_adjustment) % 12
28
+ (HeadMusic::Circle.of_fourths.index(tonic_pitch_class) + scale_type_adjustment) % 12
29
+ end
30
+
31
+ def sharps_or_flats
32
+ return sharps if @tonic_spelling.to_s =~ /#/
33
+ return flats if @tonic_spelling.to_s =~ /b/
34
+ num_sharps <= num_flats ? sharps : flats
29
35
  end
30
36
 
31
37
  private
@@ -33,4 +39,17 @@ class HeadMusic::KeySignature
33
39
  def scale_type_adjustment
34
40
  scale_type == :minor ? 3 : 0
35
41
  end
42
+
43
+ def major?
44
+ @scale_type.to_sym == :major
45
+ end
46
+
47
+ def minor?
48
+ @scale_type.to_sym == :minor
49
+ end
50
+
51
+ def relative_major_pitch_class
52
+ return tonic_pitch_class if major?
53
+ return (tonic_pitch_class.to_i + 3) % 12 if minor?
54
+ end
36
55
  end
@@ -17,23 +17,23 @@ class HeadMusic::Letter
17
17
  NAMES.map { |letter_name| get(letter_name)}
18
18
  end
19
19
 
20
- def self.get(name)
20
+ def self.get(identifier)
21
21
  @letters ||= {}
22
+ @letters[identifier] ||= from_name(identifier) || from_pitch_class(identifier)
23
+ end
24
+
25
+ def self.from_name(name)
22
26
  name = name.to_s.first.upcase
23
- if NAMES.include?(name)
24
- @letters[name] ||= new(name)
25
- end
27
+ new(name) if NAMES.include?(name)
26
28
  end
27
29
 
28
30
  def self.from_pitch_class(pitch_class)
29
31
  @letters ||= {}
32
+ return nil if pitch_class.to_s == pitch_class
30
33
  pitch_class = pitch_class.to_i % 12
31
- name =
32
- NAMES.detect { |name| pitch_class == NATURAL_PITCH_CLASS_NUMBERS[name] } ||
33
- NAMES.detect { |name| pitch_class == NATURAL_PITCH_CLASS_NUMBERS[name]+1 }
34
- if NAMES.include?(name)
35
- @letters[name] ||= new(name)
36
- end
34
+ name = NAMES.detect { |name| pitch_class == NATURAL_PITCH_CLASS_NUMBERS[name] }
35
+ name ||= HeadMusic::PitchClass::PREFERRED_SPELLINGS[pitch_class].first
36
+ return new(name) if NAMES.include?(name)
37
37
  end
38
38
 
39
39
  attr_reader :name
@@ -1,6 +1,8 @@
1
1
  class HeadMusic::PitchClass
2
2
  attr_reader :number
3
3
 
4
+ PREFERRED_SPELLINGS = %w[C C# D Eb E F F# G Ab A Bb B]
5
+
4
6
  def self.get(number)
5
7
  @pitch_classes ||= {}
6
8
  number = number.to_i % 12
@@ -5,7 +5,7 @@ class HeadMusic::Spelling
5
5
  attr_reader :accidental
6
6
  attr_reader :pitch_class
7
7
 
8
- SPELLING_MATCHER = /([A-G])([b#]*)(\-?\d+)?/
8
+ SPELLING_MATCHER = /^\s*([A-G])([b#]*)(\-?\d+)?\s*$/
9
9
 
10
10
  def self.get(identifier)
11
11
  @spellings ||= {}
@@ -14,19 +14,17 @@ class HeadMusic::Spelling
14
14
 
15
15
  def self.from_name(name)
16
16
  return nil unless name == name.to_s
17
- match = name.match(SPELLING_MATCHER)
17
+ match = name.to_s.match(SPELLING_MATCHER)
18
18
  if match
19
19
  letter_name, accidental_string, _octave = match.captures
20
20
  letter = HeadMusic::Letter.get(letter_name)
21
- if letter
22
- return new(letter, HeadMusic::Accidental.get(accidental_string))
23
- end
21
+ new(letter, HeadMusic::Accidental.get(accidental_string)) if letter
24
22
  end
25
23
  end
26
24
 
27
25
  def self.from_number(number)
28
26
  return nil unless number == number.to_i
29
- pitch_class_number = number % 12
27
+ pitch_class_number = number.to_i % 12
30
28
  letter = HeadMusic::Letter.from_pitch_class(pitch_class_number)
31
29
  if letter.pitch_class != pitch_class_number
32
30
  accidental = HeadMusic::Accidental.for_interval(pitch_class_number - letter.pitch_class.to_i)
@@ -1,3 +1,3 @@
1
1
  module HeadMusic
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.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.1.0
4
+ version: 0.1.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-13 00:00:00.000000000 Z
11
+ date: 2017-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport