Class: HeadMusic::Rudiment::KeySignature

Inherits:
Base
  • Object
show all
Defined in:
lib/head_music/rudiment/key_signature.rb

Overview

Represents a key signature (traditionally associated with a key) This class maintains backward compatibility while delegating to Key/Mode internally

Defined Under Namespace

Classes: EnharmonicEquivalence

Constant Summary collapse

ORDERED_LETTER_NAMES_OF_SHARPS =
%w[F C G D A E B].freeze
ORDERED_LETTER_NAMES_OF_FLATS =
ORDERED_LETTER_NAMES_OF_SHARPS.reverse.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tonic_spelling, scale_type = nil) ⇒ KeySignature

Returns a new instance of KeySignature.



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/head_music/rudiment/key_signature.rb', line 47

def initialize(tonic_spelling, scale_type = nil)
  @tonic_spelling = HeadMusic::Rudiment::Spelling.get(tonic_spelling)
  @scale_type = HeadMusic::Rudiment::ScaleType.get(scale_type) if scale_type
  @scale_type ||= HeadMusic::Rudiment::ScaleType.default
  @scale_type = @scale_type.parent || @scale_type
  @scale = HeadMusic::Rudiment::Scale.get(@tonic_spelling, @scale_type)

  # Create appropriate tonal context
  scale_type_str = scale_type.to_s.downcase if scale_type

  @tonal_context = if %w[major minor].include?(scale_type_str)
    HeadMusic::Rudiment::Key.get("#{tonic_spelling} #{scale_type}")
  elsif scale_type
    HeadMusic::Rudiment::Mode.get("#{tonic_spelling} #{scale_type}")
  else
    HeadMusic::Rudiment::Key.get("#{tonic_spelling} major")
  end
rescue ArgumentError
  # Fall back to creating a major key if mode is not recognized
  @tonal_context = HeadMusic::Rudiment::Key.get("#{tonic_spelling} major")
end

Instance Attribute Details

#scaleObject (readonly)

Returns the value of attribute scale.



41
42
43
# File 'lib/head_music/rudiment/key_signature.rb', line 41

def scale
  @scale
end

#scale_typeObject (readonly)

Returns the value of attribute scale_type.



41
42
43
# File 'lib/head_music/rudiment/key_signature.rb', line 41

def scale_type
  @scale_type
end

#tonal_contextObject (readonly)

Returns the value of attribute tonal_context.



7
8
9
# File 'lib/head_music/rudiment/key_signature.rb', line 7

def tonal_context
  @tonal_context
end

#tonic_spellingObject (readonly)

Returns the value of attribute tonic_spelling.



41
42
43
# File 'lib/head_music/rudiment/key_signature.rb', line 41

def tonic_spelling
  @tonic_spelling
end

Class Method Details

.defaultObject



12
13
14
# File 'lib/head_music/rudiment/key_signature.rb', line 12

def self.default
  @default ||= new("C", :major)
end

.from_scale(scale) ⇒ Object



34
35
36
37
38
39
# File 'lib/head_music/rudiment/key_signature.rb', line 34

def self.from_scale(scale)
  # Find a key or mode that uses this scale
  tonic = scale.root_pitch.spelling
  scale_type = scale.scale_type
  new(tonic, scale_type)
end

.from_tonal_context(tonal_context) ⇒ Object



30
31
32
# File 'lib/head_music/rudiment/key_signature.rb', line 30

def self.from_tonal_context(tonal_context)
  new_from_context(tonal_context)
end

.get(identifier) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/head_music/rudiment/key_signature.rb', line 16

def self.get(identifier)
  return identifier if identifier.is_a?(HeadMusic::Rudiment::KeySignature)

  @key_signatures ||= {}

  if identifier.is_a?(String)
    tonic_spelling, scale_type_name = identifier.strip.split(/\s/)
    hash_key = HeadMusic::Utilities::HashKey.for(identifier.gsub(/#|♯/, " sharp").gsub(/(\w)[b♭]/, '\\1 flat'))
    @key_signatures[hash_key] ||= new(tonic_spelling, scale_type_name)
  elsif identifier.is_a?(HeadMusic::Rudiment::DiatonicContext)
    identifier.key_signature
  end
end

.new_from_context(context) ⇒ Object



69
70
71
72
73
# File 'lib/head_music/rudiment/key_signature.rb', line 69

def self.new_from_context(context)
  instance = allocate
  instance.instance_variable_set(:@tonal_context, context)
  instance
end

Instance Method Details

#==(other) ⇒ Object



126
127
128
# File 'lib/head_music/rudiment/key_signature.rb', line 126

def ==(other)
  alterations == self.class.get(other).alterations
end

#alterationsObject Also known as: sharps_and_flats, accidentals



115
116
117
# File 'lib/head_music/rudiment/key_signature.rb', line 115

def alterations
  flats.any? ? (double_flats + flats) : (double_sharps + sharps)
end

#double_flatsObject



97
98
99
100
101
# File 'lib/head_music/rudiment/key_signature.rb', line 97

def double_flats
  spellings.select(&:double_flat?).sort_by do |spelling|
    ORDERED_LETTER_NAMES_OF_FLATS.index(spelling.letter_name.to_s)
  end
end

#double_sharpsObject



85
86
87
88
89
# File 'lib/head_music/rudiment/key_signature.rb', line 85

def double_sharps
  spellings.select(&:double_sharp?).sort_by do |spelling|
    ORDERED_LETTER_NAMES_OF_SHARPS.index(spelling.letter_name.to_s)
  end
end

#enharmonic_equivalenceObject (private)



146
147
148
# File 'lib/head_music/rudiment/key_signature.rb', line 146

def enharmonic_equivalence
  @enharmonic_equivalence ||= HeadMusic::Rudiment::KeySignature::EnharmonicEquivalence.get(self)
end

#enharmonic_equivalent?(other) ⇒ Boolean

Returns:

  • (Boolean)


140
141
142
# File 'lib/head_music/rudiment/key_signature.rb', line 140

def enharmonic_equivalent?(other)
  enharmonic_equivalence.enharmonic_equivalent?(other)
end

#flatsObject



91
92
93
94
95
# File 'lib/head_music/rudiment/key_signature.rb', line 91

def flats
  spellings.select(&:flat?).sort_by do |spelling|
    ORDERED_LETTER_NAMES_OF_FLATS.index(spelling.letter_name.to_s)
  end
end

#nameObject



122
123
124
# File 'lib/head_music/rudiment/key_signature.rb', line 122

def name
  [tonic_spelling, scale_type].join(" ")
end

#num_alterationsObject



111
112
113
# File 'lib/head_music/rudiment/key_signature.rb', line 111

def num_alterations
  num_sharps + num_flats
end

#num_flatsObject



107
108
109
# File 'lib/head_music/rudiment/key_signature.rb', line 107

def num_flats
  flats.length + double_flats.length * 2
end

#num_sharpsObject



103
104
105
# File 'lib/head_music/rudiment/key_signature.rb', line 103

def num_sharps
  sharps.length + double_sharps.length * 2
end

#sharpsObject



79
80
81
82
83
# File 'lib/head_music/rudiment/key_signature.rb', line 79

def sharps
  spellings.select(&:sharp?).sort_by do |spelling|
    ORDERED_LETTER_NAMES_OF_SHARPS.index(spelling.letter_name.to_s)
  end
end

#spellingsObject



75
76
77
# File 'lib/head_music/rudiment/key_signature.rb', line 75

def spellings
  pitches.map(&:spelling).uniq
end

#to_sObject



130
131
132
133
134
135
136
137
138
# File 'lib/head_music/rudiment/key_signature.rb', line 130

def to_s
  if sharps.any?
    (sharps.length == 1) ? "1 sharp" : "#{sharps.length} sharps"
  elsif flats.any?
    (flats.length == 1) ? "1 flat" : "#{flats.length} flats"
  else
    "no sharps or flats"
  end
end