Class: HeadMusic::Rudiment::RhythmicUnit

Inherits:
Object
  • 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.

Constant Summary collapse

AMERICAN_MULTIPLES_NAMES =

Note values longer than a whole note

[
  "whole", "double whole", "longa", "maxima"
].freeze
AMERICAN_DIVISIONS_NAMES =

Note values from whole note down to very short subdivisions

[
  "whole", "half", "quarter", "eighth", "sixteenth", "thirty-second",
  "sixty-fourth", "hundred twenty-eighth", "two hundred fifty-sixth"
].freeze
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)


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

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.



48
49
50
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 48

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.



48
49
50
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 48

def numerator
  @numerator
end

Class Method Details

.allObject



54
55
56
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 54

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

.all_normalized_namesObject



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

def self.all_normalized_names
  @all_normalized_names ||= (
    AMERICAN_MULTIPLES_NAMES.map { |n| normalize_name(n) } +
    AMERICAN_DIVISIONS_NAMES.map { |n| normalize_name(n) } +
    BRITISH_MULTIPLES_NAMES.map { |n| normalize_name(n) } +
    BRITISH_DIVISIONS_NAMES.map { |n| normalize_name(n) }
  ).uniq
end

.for_denominator_value(denominator) ⇒ Object



38
39
40
41
42
43
44
45
46
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 38

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



50
51
52
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 50

def self.get(name)
  get_by_name(name)
end

.normalize_name(name) ⇒ Object



134
135
136
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 134

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)


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

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

Instance Method Details

#<=>(other) ⇒ Object



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

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

  relative_value <=> other.relative_value
end

#british_fractions_keysObject (private)



170
171
172
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 170

def british_fractions_keys
  BRITISH_DIVISIONS_NAMES.map { |fraction| self.class.normalize_name(fraction) }
end

#british_multiples_keysObject (private)



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

def british_multiples_keys
  BRITISH_MULTIPLES_NAMES.map { |multiple| self.class.normalize_name(multiple) }
end

#british_nameObject



118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 118

def british_name
  if has_american_multiple_name?
    index = AMERICAN_MULTIPLES_NAMES.index(name)
    return BRITISH_MULTIPLES_NAMES[index] unless index.nil?
  elsif has_american_division_name?
    index = AMERICAN_DIVISIONS_NAMES.index(name)
    return BRITISH_DIVISIONS_NAMES[index] unless index.nil?
  elsif BRITISH_MULTIPLES_NAMES.include?(name) || BRITISH_DIVISIONS_NAMES.include?(name)
    return name
  end

  nil # Return nil if no British equivalent found
end

#common?Boolean

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

Returns:

  • (Boolean)


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

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

#denominator_exponentObject (private)



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

def denominator_exponent
  normalized_name = self.class.normalize_name(name)
  fractions_keys.index(normalized_name) || british_fractions_keys.index(normalized_name) || 0
end

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

#flagsObject



99
100
101
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 99

def flags
  AMERICAN_DIVISIONS_NAMES.include?(name) ? [AMERICAN_DIVISIONS_NAMES.index(name) - 2, 0].max : 0
end

#fractions_keysObject (private)



166
167
168
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 166

def fractions_keys
  AMERICAN_DIVISIONS_NAMES.map { |fraction| self.class.normalize_name(fraction) }
end

#has_american_division_name?Boolean (private)

Returns:

  • (Boolean)


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

def has_american_division_name?
  AMERICAN_DIVISIONS_NAMES.include?(name)
end

#has_american_multiple_name?Boolean (private)

Returns:

  • (Boolean)


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

def has_american_multiple_name?
  AMERICAN_MULTIPLES_NAMES.include?(name)
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.

#multiples_keysObject (private)



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

def multiples_keys
  AMERICAN_MULTIPLES_NAMES.map { |multiple| self.class.normalize_name(multiple) }
end

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

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

#noteheadObject



89
90
91
92
93
94
95
96
97
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 89

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)



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

def numerator_exponent
  normalized_name = self.class.normalize_name(name)
  multiples_keys.index(normalized_name) || british_multiples_keys.index(normalized_name) || 0
end

#relative_valueObject



81
82
83
# File 'lib/head_music/rudiment/rhythmic_unit.rb', line 81

def relative_value
  @numerator.to_f / @denominator
end

#stemmed?Boolean

Returns:

  • (Boolean)


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

def stemmed?
  relative_value < 1
end

#ticksObject



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

def ticks
  HeadMusic::Rudiment::Rhythm::PPQN * 4 * relative_value
end