Class: HeadMusic::Notation::LilyPond::RenderPlan

Inherits:
Object
  • Object
show all
Defined in:
lib/head_music/notation/lily_pond/render_plan.rb

Overview

The computed musical facts a Writer needs to serialize a composition: the token for every placement and the key/time signatures in force at each measure. Construction eagerly computes everything that can raise on unmappable keys, durations, or alterations, so a RenderPlan that builds successfully cannot fail assembly on those grounds.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(composition) ⇒ RenderPlan

Returns a new instance of RenderPlan.



11
12
13
14
# File 'lib/head_music/notation/lily_pond/render_plan.rb', line 11

def initialize(composition)
  @composition = composition
  precompute_eager_data
end

Instance Attribute Details

#compositionObject (readonly)

Returns the value of attribute composition.



9
10
11
# File 'lib/head_music/notation/lily_pond/render_plan.rb', line 9

def composition
  @composition
end

Instance Method Details

#bar_numbersObject



16
17
18
# File 'lib/head_music/notation/lily_pond/render_plan.rb', line 16

def bar_numbers
  composition.earliest_bar_number..composition.latest_bar_number
end

#chord_token(placement) ⇒ Object (private)



89
90
91
92
# File 'lib/head_music/notation/lily_pond/render_plan.rb', line 89

def chord_token(placement)
  body = "<#{placement.pitches.sort.map { |pitch| PitchWriter.token(pitch) }.join(" ")}>"
  placement.rhythmic_value.tied_chain.map { |link| "#{body}#{DurationWriter.token(link)}" }.join("~ ")
end

#effective_meter(bar_number) ⇒ Object



41
42
43
44
# File 'lib/head_music/notation/lily_pond/render_plan.rb', line 41

def effective_meter(bar_number)
  change_bar = measure_time_changes.keys.select { |number| number <= bar_number }.max
  change_bar ? measure_time_changes[change_bar] : composition.meter
end

#first_measure_keyObject



32
33
34
35
# File 'lib/head_music/notation/lily_pond/render_plan.rb', line 32

def first_measure_key
  @first_measure_key ||=
    measure_key_changes[bar_numbers.first] || KeyMapper.token(composition.key_signature)
end

#first_measure_meterObject



37
38
39
# File 'lib/head_music/notation/lily_pond/render_plan.rb', line 37

def first_measure_meter
  @first_measure_meter ||= effective_meter(bar_numbers.first)
end

#measure_key_changesObject



20
21
22
23
24
# File 'lib/head_music/notation/lily_pond/render_plan.rb', line 20

def measure_key_changes
  @measure_key_changes ||= bar_numbers.zip(composition.bars).filter_map { |bar_number, bar|
    [bar_number, KeyMapper.token(bar.key_signature)] if bar.key_signature
  }.to_h
end

#measure_time_changesObject



26
27
28
29
30
# File 'lib/head_music/notation/lily_pond/render_plan.rb', line 26

def measure_time_changes
  @measure_time_changes ||= bar_numbers.zip(composition.bars).filter_map { |bar_number, bar|
    [bar_number, bar.meter] if bar.meter
  }.to_h
end

#note_token(placement) ⇒ Object (private)



84
85
86
87
# File 'lib/head_music/notation/lily_pond/render_plan.rb', line 84

def note_token(placement)
  pitch_token = PitchWriter.token(placement.pitch)
  placement.rhythmic_value.tied_chain.map { |link| "#{pitch_token}#{DurationWriter.token(link)}" }.join("~ ")
end

#placements_by_bar(voice) ⇒ Object



46
47
48
49
# File 'lib/head_music/notation/lily_pond/render_plan.rb', line 46

def placements_by_bar(voice)
  @placements_by_bar ||= {}
  @placements_by_bar[voice] ||= voice.placements.group_by { |placement| placement.position.bar_number }
end

#precompute_eager_dataObject (private)

Everything that can raise on unmappable keys, durations, or alterations is computed here so it raises at construction, before the Writer assembles output.



62
63
64
65
66
67
68
# File 'lib/head_music/notation/lily_pond/render_plan.rb', line 62

def precompute_eager_data
  first_measure_key
  first_measure_meter
  measure_key_changes
  measure_time_changes
  tokens_by_placement
end

#rest_token(placement) ⇒ Object (private)



80
81
82
# File 'lib/head_music/notation/lily_pond/render_plan.rb', line 80

def rest_token(placement)
  placement.rhythmic_value.tied_chain.map { |link| "r#{DurationWriter.token(link)}" }.join(" ")
end

#token(placement) ⇒ Object (private)

A tied chain within a placement joins its links with the tie mark; a chain of rests emits consecutive untied rests, and a tied chord repeats the whole chord.



73
74
75
76
77
78
# File 'lib/head_music/notation/lily_pond/render_plan.rb', line 73

def token(placement)
  return rest_token(placement) if placement.rest?
  return chord_token(placement) if placement.chord?

  note_token(placement)
end

#tokens_by_placementObject



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

def tokens_by_placement
  @tokens_by_placement ||= composition.voices.flat_map(&:placements).to_h do |placement|
    [placement, token(placement)]
  end
end