Class: HeadMusic::Rudiment::RhythmicValue

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/head_music/rudiment/rhythmic_value.rb

Overview

A rhythmic value is a duration composed of a rhythmic unit, any number of dots, and a tied value.

Defined Under Namespace

Classes: Parser

Constant Summary collapse

RhythmicUnit =
HeadMusic::Rudiment::RhythmicUnit

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(unit, dots: nil, tied_value: nil) ⇒ RhythmicValue

Returns a new instance of RhythmicValue.



76
77
78
79
80
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 76

def initialize(unit, dots: nil, tied_value: nil)
  @unit = HeadMusic::Rudiment::RhythmicUnit.get(unit)
  @dots = [0, 1, 2, 3].include?(dots) ? dots : 0
  @tied_value = tied_value
end

Instance Attribute Details

#dotsObject (readonly)

Returns the value of attribute dots.



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

def dots
  @dots
end

#tied_valueObject (readonly)

Returns the value of attribute tied_value.



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

def tied_value
  @tied_value
end

#unitObject (readonly)

Returns the value of attribute unit.



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

def unit
  @unit
end

Class Method Details

.dots_from_words(identifier) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 62

def self.dots_from_words(identifier)
  return 0 unless identifier.match?(/dotted/)

  modifier, = identifier.split(/_*dotted_*/)
  case modifier
  when /tripl\w/
    3
  when /doubl\w/
    2
  else
    1
  end
end

.from_tied_words(identifier) ⇒ Object

Splits on the first “ tied to “ and recurses on the remainder, so chained ties (“half tied to eighth tied to sixteenth”) nest naturally.



43
44
45
46
47
48
49
50
51
52
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 43

def self.from_tied_words(identifier)
  head, tail = identifier.split(/\s+tied\s+to\s+/, 2)
  return nil unless tail

  head_value = get(head)
  tied_value = get(tail)
  return nil unless head_value && tied_value

  new(head_value.unit, dots: head_value.dots, tied_value: tied_value)
end

.from_words(identifier) ⇒ Object



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

def self.from_words(identifier)
  new(unit_from_words(identifier), dots: dots_from_words(identifier))
end

.get(identifier) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 15

def self.get(identifier)
  case identifier
  when HeadMusic::Rudiment::RhythmicValue
    identifier
  when HeadMusic::Rudiment::RhythmicUnit
    new(identifier)
  when Symbol, String
    original_identifier = identifier.to_s.strip
    # Handle tied values first, so "half tied to eighth" round-trips through #to_s
    tied = from_tied_words(original_identifier)
    return tied if tied

    # Then try the new parser which handles all formats
    parsed = Parser.parse(original_identifier)
    return parsed if parsed

    # Then try the word-based approach as fallback
    identifier = HeadMusic::Utilities::Case.to_snake_case(original_identifier)
    begin
      from_words(identifier)
    rescue
      nil
    end
  end
end

.unit_from_words(identifier) ⇒ Object



58
59
60
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 58

def self.unit_from_words(identifier)
  identifier.gsub(/^\w*dotted_/, "")
end

Instance Method Details

#<=>(other) ⇒ Object



137
138
139
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 137

def <=>(other)
  total_value <=> other.total_value
end

#==(other) ⇒ Object



129
130
131
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 129

def ==(other)
  to_s == other.to_s
end

#multiplierObject



94
95
96
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 94

def multiplier
  (0..dots).reduce(0) { |sum, i| sum + 0.5**i }
end

#nameObject



121
122
123
124
125
126
127
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 121

def name
  if tied_value
    [single_value_name, tied_value.name].compact.join(" tied to ")
  else
    single_value_name
  end
end

#name_modifier_prefixObject



106
107
108
109
110
111
112
113
114
115
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 106

def name_modifier_prefix
  case dots
  when 1
    "dotted"
  when 2
    "double-dotted"
  when 3
    "triple-dotted"
  end
end

#per_wholeObject



102
103
104
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 102

def per_whole
  1.0 / relative_value
end

#relative_valueObject



86
87
88
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 86

def relative_value
  unit_value * multiplier
end

#single_value_nameObject



117
118
119
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 117

def single_value_name
  [name_modifier_prefix, unit_name].compact.join(" ")
end

#ticksObject



98
99
100
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 98

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

#to_sObject



133
134
135
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 133

def to_s
  name.tr("_", "-")
end

#total_valueObject



90
91
92
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 90

def total_value
  relative_value + (tied_value ? tied_value.total_value : 0)
end

#unit_valueObject



82
83
84
# File 'lib/head_music/rudiment/rhythmic_value.rb', line 82

def unit_value
  unit.relative_value
end