Class: HeadMusic::Notation::MusicXML::DurationWriter

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

Overview

Converts a HeadMusic::Rudiment::RhythmicValue into the ,

, dot count, and tie flags MusicXML needs for each link of a tied chain. A value with no tied chain yields a single component.

Defined Under Namespace

Classes: Component

Constant Summary collapse

TYPES_BY_UNIT_NAME =

MusicXML’s element uses these fixed names rather than the gem's American duration names.

{
  "maxima" => "maxima",
  "longa" => "long",
  "double whole" => "breve",
  "whole" => "whole",
  "half" => "half",
  "quarter" => "quarter",
  "eighth" => "eighth",
  "sixteenth" => "16th",
  "thirty-second" => "32nd",
  "sixty-fourth" => "64th",
  "hundred twenty-eighth" => "128th",
  "two hundred fifty-sixth" => "256th"
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(divisions) ⇒ DurationWriter

Returns a new instance of DurationWriter.



28
29
30
# File 'lib/head_music/notation/music_xml/duration_writer.rb', line 28

def initialize(divisions)
  @divisions = divisions
end

Instance Attribute Details

#divisionsObject (readonly)

Returns the value of attribute divisions.



26
27
28
# File 'lib/head_music/notation/music_xml/duration_writer.rb', line 26

def divisions
  @divisions
end

Class Method Details

.single_quarter_fraction(rhythmic_value) ⇒ Object

A rhythmic value’s own duration in quarter notes, ignoring any tied chain. RhythmicValue’s own methods (relative_value, total_value, ticks) return Floats, so the fraction is rebuilt here from the unit’s integer numerator and denominator to keep the divisions arithmetic exact.



37
38
39
40
41
42
# File 'lib/head_music/notation/music_xml/duration_writer.rb', line 37

def self.single_quarter_fraction(rhythmic_value)
  unit = rhythmic_value.unit
  dots = rhythmic_value.dots
  # A value with d dots spans (2^(d+1) - 1) / 2^d of its own unit.
  Rational(unit.numerator, unit.denominator) * Rational((2**(dots + 1)) - 1, 2**dots) * 4
end

Instance Method Details

#build_component(link, index, length) ⇒ Object (private)



57
58
59
60
61
62
63
64
65
# File 'lib/head_music/notation/music_xml/duration_writer.rb', line 57

def build_component(link, index, length)
  Component.new(
    duration: integer_duration(link),
    type: type_for(link),
    dots: link.dots,
    tie_start: index != length - 1,
    tie_stop: index != 0
  )
end

#chain(rhythmic_value) ⇒ Object (private)



51
52
53
54
55
# File 'lib/head_music/notation/music_xml/duration_writer.rb', line 51

def chain(rhythmic_value)
  links = [rhythmic_value]
  links << links.last.tied_value while links.last.tied_value
  links
end

#components(rhythmic_value) ⇒ Object



44
45
46
47
# File 'lib/head_music/notation/music_xml/duration_writer.rb', line 44

def components(rhythmic_value)
  links = chain(rhythmic_value)
  links.each_with_index.map { |link, index| build_component(link, index, links.length) }
end

#integer_duration(link) ⇒ Object (private)



67
68
69
70
71
72
73
74
# File 'lib/head_music/notation/music_xml/duration_writer.rb', line 67

def integer_duration(link)
  fraction = self.class.single_quarter_fraction(link) * divisions
  unless fraction.denominator == 1
    raise HeadMusic::Notation::MusicXML::RenderError,
      "cannot express #{link} as an integer duration at #{divisions} divisions per quarter note"
  end
  fraction.numerator
end

#type_for(link) ⇒ Object (private)



76
77
78
79
80
# File 'lib/head_music/notation/music_xml/duration_writer.rb', line 76

def type_for(link)
  TYPES_BY_UNIT_NAME.fetch(link.unit_name) do
    raise HeadMusic::Notation::MusicXML::RenderError, "no MusicXML note type is known for #{link.unit_name}"
  end
end