Class: HeadMusic::Notation::MusicXML::NoteWriter

Inherits:
Object
  • Object
show all
Includes:
XmlText, PlacementValidation
Defined in:
lib/head_music/notation/music_xml/note_writer.rb

Overview

Serializes the elements a placement occupies.

A placement becomes one per tied component per sounding pitch: the components come from the render plan's duration split, and a chord renders as a lead note followed by its members.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(plan) ⇒ NoteWriter

Returns a new instance of NoteWriter.



14
15
16
17
# File 'lib/head_music/notation/music_xml/note_writer.rb', line 14

def initialize(plan)
  @plan = plan
  @lyric_writer = LyricWriter.new
end

Instance Attribute Details

#lyric_writerObject (readonly, private)

Returns the value of attribute lyric_writer.



34
35
36
# File 'lib/head_music/notation/music_xml/note_writer.rb', line 34

def lyric_writer
  @lyric_writer
end

#planObject (readonly, private)

Returns the value of attribute plan.



34
35
36
# File 'lib/head_music/notation/music_xml/note_writer.rb', line 34

def plan
  @plan
end

Instance Method Details

#beam_lines(beams) ⇒ Object (private)



68
69
70
# File 'lib/head_music/notation/music_xml/note_writer.rb', line 68

def beam_lines(beams)
  beams.map { |beam| %(#{INDENT * 4}<beam number="#{beam.number}">#{beam.type}</beam>) }
end

#element_lines(placement, component, pitch: nil, chord: false, beams: []) ⇒ Object (private)

A chord note carries as its first child, before , marking it as sounding with the preceding note; the lead note (and every single note and rest) omits it, so this path stays byte-identical for those.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/head_music/notation/music_xml/note_writer.rb', line 52

def element_lines(placement, component, pitch: nil, chord: false, beams: [])
  [
    "#{INDENT * 3}<note>",
    *(chord ? ["#{INDENT * 4}<chord/>"] : []),
    *(pitch ? pitch_lines(pitch) : ["#{INDENT * 4}<rest/>"]),
    "#{INDENT * 4}<duration>#{component.duration}</duration>",
    *tie_lines(placement, component),
    "#{INDENT * 4}<type>#{component.type}</type>",
    *Array.new(component.dots) { "#{INDENT * 4}<dot/>" },
    *beam_lines(beams),
    *notation_lines(placement, component),
    *lyric_writer.lines(placement, component, chord: chord),
    "#{INDENT * 3}</note>"
  ]
end

#ensure_pitched_sounds(placement) ⇒ Object (private) Originally defined in module PlacementValidation

Raises:

  • (render_error_class)

#escape(text) ⇒ Object (private) Originally defined in module XmlText

#lines(placement) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/head_music/notation/music_xml/note_writer.rb', line 19

def lines(placement)
  ensure_pitched_sounds(placement)

  components_by_placement[placement].each_with_index.flat_map do |component, component_index|
    beams = beam_annotations[[placement, component_index]] || []
    note_slots(placement).each_with_index.flat_map do |pitch, index|
      element_lines(
        placement, component, pitch: pitch, chord: index.positive?, beams: index.zero? ? beams : []
      )
    end
  end
end

#notation_lines(placement, component) ⇒ Object (private)



94
95
96
97
98
99
100
101
102
103
# File 'lib/head_music/notation/music_xml/note_writer.rb', line 94

def notation_lines(placement, component)
  return [] if placement.rest? || (!component.tie_start && !component.tie_stop)

  [
    "#{INDENT * 4}<notations>",
    component.tie_stop ? %(#{INDENT * 5}<tied type="stop"/>) : nil,
    component.tie_start ? %(#{INDENT * 5}<tied type="start"/>) : nil,
    "#{INDENT * 4}</notations>"
  ].compact
end

#note_slots(placement) ⇒ Object (private)

A rest emits one empty slot; a sounded placement emits its pitches low to high, so the lowest note leads and the rest carry . ensure_pitched_sounds has already rejected any unpitched sound, so pitches covers every sound here.



41
42
43
# File 'lib/head_music/notation/music_xml/note_writer.rb', line 41

def note_slots(placement)
  placement.rest? ? [nil] : placement.pitches.sort
end

#pitch_lines(pitch) ⇒ Object (private)



72
73
74
75
76
77
78
79
80
81
# File 'lib/head_music/notation/music_xml/note_writer.rb', line 72

def pitch_lines(pitch)
  attributes = PitchWriter.attributes(pitch)
  [
    "#{INDENT * 4}<pitch>",
    "#{INDENT * 5}<step>#{attributes[:step]}</step>",
    attributes[:alter] && "#{INDENT * 5}<alter>#{attributes[:alter]}</alter>",
    "#{INDENT * 5}<octave>#{attributes[:octave]}</octave>",
    "#{INDENT * 4}</pitch>"
  ].compact
end

#render_error_classObject (private)



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

def render_error_class
  RenderError
end

#tie_lines(placement, component) ⇒ Object (private)

Rests take no tie elements; the links of a rest’s tied chain render as consecutive independent rests.



85
86
87
88
89
90
91
92
# File 'lib/head_music/notation/music_xml/note_writer.rb', line 85

def tie_lines(placement, component)
  return [] if placement.rest?

  [
    component.tie_stop ? %(#{INDENT * 4}<tie type="stop"/>) : nil,
    component.tie_start ? %(#{INDENT * 4}<tie type="start"/>) : nil
  ].compact
end