Class: HeadMusic::Rudiment::RhythmicUnit

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

Overview

A rhythmic unit is a rudiment of duration consisting of doublings and divisions of a whole note.

Defined Under Namespace

Classes: Parser

Constant Summary collapse

RHYTHMIC_UNITS_DATA =
YAML.load_file(File.expand_path("rhythmic_units.yml", __dir__)).freeze
AMERICAN_MULTIPLES_NAMES =
[
  "whole", "double whole", "longa", "maxima"
].freeze
AMERICAN_DIVISIONS_NAMES =
[
  "whole", "half", "quarter", "eighth", "sixteenth", "thirty-second",
  "sixty-fourth", "hundred twenty-eighth", "two hundred fifty-sixth"
].freeze
AMERICAN_DURATIONS =
(AMERICAN_MULTIPLES_NAMES + AMERICAN_DIVISIONS_NAMES).freeze
PATTERN =
/#{Regexp.union(AMERICAN_DURATIONS)}/i
BRITISH_MULTIPLES_NAMES =

British terminology for note values longer than a whole note

%w[semibreve breve longa maxima].freeze
BRITISH_DIVISIONS_NAMES =

British terminology for standard note divisions

%w[
  semibreve minim crotchet quaver semiquaver demisemiquaver
  hemidemisemiquaver semihemidemisemiquaver demisemihemidemisemiquaver
].freeze
NOTEHEADS =

Notehead symbols used for different note values

{
  maxima: 8.0,
  longa: 4.0,
  breve: 2.0,
  open: [0.5, 1.0],
  closed: :default
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(canonical_name) ⇒ RhythmicUnit

Returns a new instance of RhythmicUnit.

Raises:

  • (ArgumentError)


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

def initialize(canonical_name)
  raise ArgumentError, "Name cannot be nil or empty" if canonical_name.to_s.strip.empty?

  self.name = canonical_name
  @numerator = 2**numerator_exponent
  @denominator = 2**denominator_exponent
end

Instance Attribute Details

#alias_name_keysObject (readonly) Originally defined in module Named

Returns the value of attribute alias_name_keys.

#denominatorObject (readonly)

Returns the value of attribute denominator.



52
53
54
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 52

def denominator
  @denominator
end

#name_keyObject (readonly) Originally defined in module Named

Returns the value of attribute name_key.

#numeratorObject (readonly)

Returns the value of attribute numerator.



52
53
54
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 52

def numerator
  @numerator
end

Class Method Details

.allObject



62
63
64
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 62

def self.all
  @all ||= (AMERICAN_MULTIPLES_NAMES.reverse + AMERICAN_DIVISIONS_NAMES).uniq.map { |name| get(name) }.compact
end

.all_normalized_namesObject



72
73
74
75
76
77
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 72

def self.all_normalized_names
  @all_normalized_names ||= [
    AMERICAN_MULTIPLES_NAMES, AMERICAN_DIVISIONS_NAMES,
    BRITISH_MULTIPLES_NAMES, BRITISH_DIVISIONS_NAMES
  ].flat_map { |names| names.map { |name| normalize_name(name) } }.uniq
end

.for_denominator_value(denominator) ⇒ Object



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

def self.for_denominator_value(denominator)
  return nil unless denominator.is_a?(Numeric) && denominator > 0
  return nil unless (denominator & (denominator - 1)) == 0  # Check if power of 2

  index = Math.log2(denominator).to_i
  return nil if index >= AMERICAN_DIVISIONS_NAMES.length

  get(AMERICAN_DIVISIONS_NAMES[index])
end

.get(name) ⇒ Object



54
55
56
57
58
59
60
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 54

def self.get(name)
  # Use the parser to handle tempo shorthand and other formats
  parsed_name = HeadMusic::Rudiment::RhythmicUnit::Parser.parse(name)
  return nil unless parsed_name

  get_by_name(parsed_name)
end

.normalize_name(name) ⇒ Object



132
133
134
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 132

def self.normalize_name(name)
  name.to_s.gsub(/\W+/, "_")
end

.valid_name?(name) ⇒ Boolean

Check if a name represents a valid rhythmic unit

Returns:

  • (Boolean)


67
68
69
70
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 67

def self.valid_name?(name)
  normalized = normalize_name(name)
  all_normalized_names.include?(normalized)
end

Instance Method Details

#<=>(other) ⇒ Object



118
119
120
121
122
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 118

def <=>(other)
  return nil unless other.is_a?(self.class)

  relative_value <=> other.relative_value
end

#british_equivalent(american_names, british_names) ⇒ Object (private)

Translate this unit’s American name to its British equivalent, matched by position in the parallel American/British name arrays.



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

def british_equivalent(american_names, british_names)
  index = american_names.index(name)
  index && british_names[index]
end

#british_nameObject



124
125
126
127
128
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 124

def british_name
  british_equivalent(AMERICAN_MULTIPLES_NAMES, BRITISH_MULTIPLES_NAMES) ||
    british_equivalent(AMERICAN_DIVISIONS_NAMES, BRITISH_DIVISIONS_NAMES) ||
    own_british_name
end

#common?Boolean

Returns true if this note value is commonly used in modern notation

Returns:

  • (Boolean)


114
115
116
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 114

def common?
  AMERICAN_DIVISIONS_NAMES[0..6].include?(name) || BRITISH_DIVISIONS_NAMES[0..6].include?(name)
end

#denominator_exponentObject (private)



153
154
155
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 153

def denominator_exponent
  exponent_from(AMERICAN_DIVISIONS_NAMES, BRITISH_DIVISIONS_NAMES)
end

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

#exponent_from(american_names, british_names) ⇒ Object (private)



157
158
159
160
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 157

def exponent_from(american_names, british_names)
  key = self.class.normalize_name(name)
  normalized_keys(american_names).index(key) || normalized_keys(british_names).index(key) || 0
end

#flagsObject



105
106
107
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 105

def flags
  AMERICAN_DIVISIONS_NAMES.include?(name) ? [AMERICAN_DIVISIONS_NAMES.index(name) - 2, 0].max : 0
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.

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

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

#normalized_keys(names) ⇒ Object (private)



162
163
164
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 162

def normalized_keys(names)
  names.map { |name| self.class.normalize_name(name) }
end

#noteheadObject



95
96
97
98
99
100
101
102
103
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 95

def notehead
  value = relative_value
  return :maxima if value == NOTEHEADS[:maxima]
  return :longa if value == NOTEHEADS[:longa]
  return :breve if value == NOTEHEADS[:breve]
  return :open if NOTEHEADS[:open].include?(value)

  :closed
end

#numerator_exponentObject (private)



149
150
151
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 149

def numerator_exponent
  exponent_from(AMERICAN_MULTIPLES_NAMES, BRITISH_MULTIPLES_NAMES)
end

#own_british_nameObject (private)



145
146
147
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 145

def own_british_name
  name if BRITISH_MULTIPLES_NAMES.include?(name) || BRITISH_DIVISIONS_NAMES.include?(name)
end

#relative_valueObject



87
88
89
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 87

def relative_value
  @numerator.to_f / @denominator
end

#stemmed?Boolean

Returns:

  • (Boolean)


109
110
111
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 109

def stemmed?
  relative_value < 1
end

#ticksObject



91
92
93
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 91

def ticks
  (HeadMusic::Rudiment::Rhythm::PPQN * 4 * relative_value).to_i
end