Class: HeadMusic::Rudiment::Alteration

Inherits:
Base
  • Object
show all
Includes:
Comparable, Named
Defined in:
lib/head_music/rudiment/alteration.rb

Overview

An Alteration is a symbol that modifies pitch, such as a sharp, flat, or natural. In French, sharps and flats in the key signature are called “altérations”.

Constant Summary collapse

ALTERATION_RECORDS =
YAML.load_file(File.expand_path("alterations.yml", __dir__), symbolize_names: true)[:alterations].freeze
ALTERATION_IDENTIFIERS =
ALTERATION_RECORDS.keys.freeze
SYMBOLS =

Sorted longest-first because Regexp.union alternates in array order rather than by longest match. Without it, “bb” would match the single-flat branch and leave a stray “b” behind, half-converting a double. Ordering among equal-length symbols is unspecified and does not matter: equal-length strings cannot prefix one another.

ALTERATION_RECORDS
.map { |key, attributes| attributes[:symbols].map { |symbol| [symbol[:unicode], symbol[:ascii]] } }
.flatten.reject { |s| s.nil? || s.empty? }.uniq
.sort_by { |symbol| -symbol.length }
.freeze
PATTERN =
Regexp.union(SYMBOLS)
MATCHER =
PATTERN

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key, attributes) ⇒ Alteration (private)

Returns a new instance of Alteration.



95
96
97
98
99
100
# File 'lib/head_music/rudiment/alteration.rb', line 95

def initialize(key, attributes)
  @identifier = key
  @semitones = attributes[:semitones]
  initialize_musical_symbols(attributes[:symbols])
  initialize_localized_names
end

Instance Attribute Details

#alias_name_keysObject (readonly) Originally defined in module Named

Returns the value of attribute alias_name_keys.

#identifierObject (readonly)

Returns the value of attribute identifier.



10
11
12
# File 'lib/head_music/rudiment/alteration.rb', line 10

def identifier
  @identifier
end

#musical_symbolsObject (readonly)

Returns the value of attribute musical_symbols.



10
11
12
# File 'lib/head_music/rudiment/alteration.rb', line 10

def musical_symbols
  @musical_symbols
end

#name_keyObject (readonly) Originally defined in module Named

Returns the value of attribute name_key.

#semitonesObject (readonly)

Returns the value of attribute semitones.



10
11
12
# File 'lib/head_music/rudiment/alteration.rb', line 10

def semitones
  @semitones
end

Class Method Details

.allObject



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

def self.all
  @all ||= ALTERATION_RECORDS.map { |key, attributes| new(key, attributes) }
end

.by(key, value) ⇒ Object



50
51
52
53
54
# File 'lib/head_music/rudiment/alteration.rb', line 50

def self.by(key, value)
  all.detect do |alteration|
    alteration.send(key) == value if %i[semitones].include?(key.to_sym)
  end
end

.get(identifier) ⇒ Object



42
43
44
45
46
47
48
# File 'lib/head_music/rudiment/alteration.rb', line 42

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

  all.detect do |alteration|
    alteration.representations.include?(identifier)
  end
end

.get_by_name(name) ⇒ Object



56
57
58
# File 'lib/head_music/rudiment/alteration.rb', line 56

def self.get_by_name(name)
  all.detect { |alteration| alteration.name == name.to_s }
end

.symbol?(candidate) ⇒ Boolean

Returns:

  • (Boolean)


38
39
40
# File 'lib/head_music/rudiment/alteration.rb', line 38

def self.symbol?(candidate)
  SYMBOLS.include?(candidate)
end

.symbolsObject



34
35
36
# File 'lib/head_music/rudiment/alteration.rb', line 34

def self.symbols
  SYMBOLS
end

Instance Method Details

#<=>(other) ⇒ Object



84
85
86
87
# File 'lib/head_music/rudiment/alteration.rb', line 84

def <=>(other)
  other = HeadMusic::Rudiment::Alteration.get(other)
  semitones <=> other.semitones
end

#ensure_localized_name(name:, locale_code: Locale::DEFAULT_CODE, abbreviation: nil) ⇒ Object Originally defined in module Named

#initialize_localized_namesObject (private)



102
103
104
105
106
# File 'lib/head_music/rudiment/alteration.rb', line 102

def initialize_localized_names
  # Initialize default English names
  ensure_localized_name(name: identifier.to_s.tr("_", " "), locale_code: :en)
  # Additional localized names will be loaded from locale files
end

#initialize_musical_symbols(list) ⇒ Object (private)



108
109
110
111
112
113
114
115
116
# File 'lib/head_music/rudiment/alteration.rb', line 108

def initialize_musical_symbols(list)
  @musical_symbols = (list || []).map do |record|
    HeadMusic::Notation::MusicalSymbol.new(
      unicode: record[:unicode],
      ascii: record[:ascii],
      html_entity: record[:html_entity]
    )
  end
end

#localized_name(locale_code: Locale::DEFAULT_CODE) ⇒ Object Originally defined in module Named

#localized_name_in_default_localeObject (private) Originally defined in module Named

#localized_name_in_locale_matching_language(locale) ⇒ Object (private) Originally defined in module Named

#localized_name_in_matching_locale(locale) ⇒ Object (private) Originally defined in module Named

#localized_namesObject Originally defined in module Named

Returns an array of LocalizedName instances that are synonymous with the name.

#musical_symbolObject



89
90
91
# File 'lib/head_music/rudiment/alteration.rb', line 89

def musical_symbol
  musical_symbols.first
end

#name(locale_code: I18n.locale) ⇒ Object



60
61
62
# File 'lib/head_music/rudiment/alteration.rb', line 60

def name(locale_code: I18n.locale)
  super || identifier.to_s.tr("_", " ")
end

#name=(name) ⇒ Object Originally defined in module Named

#representations(locale_code: I18n.locale) ⇒ Object

Spans every symbol record, not just the primary one, so that an alternate ASCII spelling like “##” resolves through .get. Memoized per locale because .get runs on every parse and walks this list for each alteration, and #name is locale-dependent.



67
68
69
70
71
72
73
74
# File 'lib/head_music/rudiment/alteration.rb', line 67

def representations(locale_code: I18n.locale)
  @representations ||= {}
  @representations[locale_code] ||=
    ([identifier, identifier.to_s, name(locale_code: locale_code)] +
      musical_symbols.flat_map { |symbol| [symbol.ascii, symbol.unicode, symbol.html_entity] })
      .uniq
      .reject { |representation| representation.to_s.strip == "" }
end

#to_sObject



80
81
82
# File 'lib/head_music/rudiment/alteration.rb', line 80

def to_s
  unicode
end