Class: HeadMusic::Notation::ABC::DurationWriter

Inherits:
Object
  • Object
show all
Defined in:
lib/head_music/notation/abc/duration_writer.rb

Overview

Converts a HeadMusic::Rudiment::RhythmicValue back into the ABC note-length multiplier string relative to the tune’s unit note length — the inverse of DurationResolver.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(unit_note_length) ⇒ DurationWriter

Returns a new instance of DurationWriter.



9
10
11
# File 'lib/head_music/notation/abc/duration_writer.rb', line 9

def initialize(unit_note_length)
  @unit_note_length = Rational(unit_note_length)
end

Instance Attribute Details

#unit_note_length ⇒ Object (readonly)

Returns the value of attribute unit_note_length.



7
8
9
# File 'lib/head_music/notation/abc/duration_writer.rb', line 7

def unit_note_length
  @unit_note_length
end

Instance Method Details

#format_multiplier(multiplier) ⇒ Object (private)



46
47
48
49
50
51
# File 'lib/head_music/notation/abc/duration_writer.rb', line 46

def format_multiplier(multiplier)
  return "" if multiplier == 1
  return multiplier.numerator.to_s if multiplier.denominator == 1

  "#{multiplier.numerator}/#{multiplier.denominator}"
end

#multiplier_string(rhythmic_value) ⇒ Object



13
14
15
# File 'lib/head_music/notation/abc/duration_writer.rb', line 13

def multiplier_string(rhythmic_value)
  multiplier_string_for_fraction(total_fraction(rhythmic_value), rhythmic_value)
end

#multiplier_string_for_fraction(fraction, rhythmic_value) ⇒ Object

For the part of a value that falls inside one bar; the rhythmic value is only named in the error.



19
20
21
22
# File 'lib/head_music/notation/abc/duration_writer.rb', line 19

def multiplier_string_for_fraction(fraction, rhythmic_value)
  validate_fraction!(fraction, rhythmic_value)
  format_multiplier(fraction / unit_note_length)
end

#power_of_two?(integer) ⇒ Boolean (private)

Returns:

  • (Boolean)


53
54
55
# File 'lib/head_music/notation/abc/duration_writer.rb', line 53

def power_of_two?(integer)
  (integer & (integer - 1)).zero?
end

#raise_error(message, rhythmic_value) ⇒ Object (private)



57
58
59
# File 'lib/head_music/notation/abc/duration_writer.rb', line 57

def raise_error(message, rhythmic_value)
  raise HeadMusic::Notation::ABC::RenderError, "#{message}: #{rhythmic_value}"
end

#total_fraction(rhythmic_value) ⇒ Object (private)

RhythmicValue’s own value methods return Floats; rebuild the fraction from integer parts so the multiplier arithmetic stays exact. A tied chain collapses to one multiplier, round-tripping tokens like “A5”.



29
30
31
32
33
34
# File 'lib/head_music/notation/abc/duration_writer.rb', line 29

def total_fraction(rhythmic_value)
  fraction = HeadMusic::Notation::DottedDuration.dotted_unit_fraction(rhythmic_value)
  tied_value = rhythmic_value.tied_value
  fraction += total_fraction(tied_value) if tied_value
  fraction
end

#validate_fraction!(fraction, rhythmic_value) ⇒ Object (private)



36
37
38
39
40
41
42
43
44
# File 'lib/head_music/notation/abc/duration_writer.rb', line 36

def validate_fraction!(fraction, rhythmic_value)
  max_fraction = DurationResolver::MAX_FRACTION
  if fraction > max_fraction
    raise_error("note length exceeds #{max_fraction.to_i} whole notes", rhythmic_value)
  end
  return if power_of_two?(fraction.denominator)

  raise_error("note length #{fraction} is not expressible in binary note values", rhythmic_value)
end