Class: HeadMusic::Notation::MusicXML::AttributesWriter

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

Overview

Renders a measure's element: everything in the first measure, and thereafter only the changes that fall on the measure.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(plan) ⇒ AttributesWriter

Returns a new instance of AttributesWriter.



16
17
18
# File 'lib/head_music/notation/music_xml/attributes_writer.rb', line 16

def initialize(plan)
  @plan = plan
end

Instance Attribute Details

#plan ⇒ Object (readonly, private)

Returns the value of attribute plan.



39
40
41
# File 'lib/head_music/notation/music_xml/attributes_writer.rb', line 39

def plan
  @plan
end

Instance Method Details

#clef_element_lines(clef, number) ⇒ Object (private)



123
124
125
126
127
128
129
130
# File 'lib/head_music/notation/music_xml/attributes_writer.rb', line 123

def clef_element_lines(clef, number)
  [
    "#{INDENT * 4}<clef#{number}>",
    "#{INDENT * 5}<sign>#{clef.pitch.letter_name}</sign>",
    "#{INDENT * 5}<line>#{clef.line}</line>",
    "#{INDENT * 4}</clef>"
  ]
end

#clef_for(part, staff) ⇒ Object (private)



119
120
121
# File 'lib/head_music/notation/music_xml/attributes_writer.rb', line 119

def clef_for(part, staff)
  staff.clef_at(bar_numbers.first) || HeadMusic::Notation::ClefSelector.for(part.voices.first)
end

#clef_lines(part) ⇒ Object (private)

An authored clef wins; the selector is the fallback for a part whose staves were never authored. It reads a voice's pitch range, which is why the fallback lives here, where a voice is in scope, rather than on the staff.



111
112
113
114
115
116
117
# File 'lib/head_music/notation/music_xml/attributes_writer.rb', line 111

def clef_lines(part)
  staves = part.staff_system.staves
  staves.each_with_index.flat_map do |staff, index|
    number = (staves.length > 1) ? %( number="#{index + 1}") : ""
    clef_element_lines(clef_for(part, staff), number)
  end
end

#diatonic_steps(semitones) ⇒ Object (private)



80
81
82
# File 'lib/head_music/notation/music_xml/attributes_writer.rb', line 80

def diatonic_steps(semitones)
  HeadMusic::Content::Layout::Transposition.for_semitones(semitones).diatonic_steps
end

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

#first_measure_lines(part) ⇒ Object (private)



41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/head_music/notation/music_xml/attributes_writer.rb', line 41

def first_measure_lines(part)
  [
    "#{INDENT * 3}<attributes>",
    "#{INDENT * 4}<divisions>#{divisions}</divisions>",
    *key_lines(first_measure_key(part)),
    *time_lines(first_measure_meter),
    *staves_lines(part),
    *clef_lines(part),
    *transpose_lines(part.instrument_at(bar_numbers.first)),
    "#{INDENT * 3}</attributes>"
  ]
end

#instrument_change_transpose_lines(part, bar_number) ⇒ Object (private)



54
55
56
57
58
# File 'lib/head_music/notation/music_xml/attributes_writer.rb', line 54

def instrument_change_transpose_lines(part, bar_number)
  return [] unless part.instrument_changes.key?(bar_number)

  transpose_lines(part.instrument_at(bar_number))
end

#key_lines(key) ⇒ Object (private)



90
91
92
93
94
95
96
97
# File 'lib/head_music/notation/music_xml/attributes_writer.rb', line 90

def key_lines(key)
  [
    "#{INDENT * 4}<key>",
    "#{INDENT * 5}<fifths>#{key[:fifths]}</fifths>",
    key[:mode] && "#{INDENT * 5}<mode>#{key[:mode]}</mode>",
    "#{INDENT * 4}</key>"
  ].compact
end

#lines(part, bar_number) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/head_music/notation/music_xml/attributes_writer.rb', line 20

def lines(part, bar_number)
  return first_measure_lines(part) if bar_number == bar_numbers.first

  key = measure_key_changes(part)[bar_number]
  meter = measure_time_changes[bar_number]
  transpose = instrument_change_transpose_lines(part, bar_number)
  return [] unless key || meter || transpose.any?

  [
    "#{INDENT * 3}<attributes>",
    *(key ? key_lines(key) : []),
    *(meter ? time_lines(meter) : []),
    *transpose,
    "#{INDENT * 3}</attributes>"
  ]
end

#staves_lines(part) ⇒ Object (private)

is omitted for the single-staff part, where it would be noise.



85
86
87
88
# File 'lib/head_music/notation/music_xml/attributes_writer.rb', line 85

def staves_lines(part)
  count = part.staff_system.length
  (count > 1) ? ["#{INDENT * 4}<staves>#{count}</staves>"] : []
end

#time_lines(meter) ⇒ Object (private)



99
100
101
102
103
104
105
106
# File 'lib/head_music/notation/music_xml/attributes_writer.rb', line 99

def time_lines(meter)
  [
    "#{INDENT * 4}<time>",
    "#{INDENT * 5}<beats>#{meter.top_number}</beats>",
    "#{INDENT * 5}<beat-type>#{meter.bottom_number}</beat-type>",
    "#{INDENT * 4}</time>"
  ]
end

#transpose_lines(instrument) ⇒ Object (private)

states sounding = written + transpose, the same convention the instrument catalog states its sounding_transposition in. The octaves go in , which is how publishers write a bass clarinet.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/head_music/notation/music_xml/attributes_writer.rb', line 63

def transpose_lines(instrument)
  return [] unless transposed?

  semitones = instrument&.sounding_transposition.to_i
  return [] if semitones.zero?

  sign = semitones.negative? ? -1 : 1
  octaves, chromatic = semitones.abs.divmod(12)
  [
    "#{INDENT * 4}<transpose>",
    "#{INDENT * 5}<diatonic>#{diatonic_steps(sign * chromatic)}</diatonic>",
    "#{INDENT * 5}<chromatic>#{sign * chromatic}</chromatic>",
    (octaves.zero? ? nil : "#{INDENT * 5}<octave-change>#{sign * octaves}</octave-change>"),
    "#{INDENT * 4}</transpose>"
  ].compact
end