Class: HeadMusic::Content::Score

Inherits:
Layout
  • Object
show all
Defined in:
lib/head_music/content/score.rb

Overview

A score is the layout that shows the players together: everyone's music on one system, in the order and the grouping the ensemble conventionally puts them in.

Constant Summary collapse

KINDS =
%i[score].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(project:, ensemble_type: nil, **layout_kwargs) ⇒ Score

Returns a new instance of Score.



17
18
19
20
21
# File 'lib/head_music/content/score.rb', line 17

def initialize(project:, ensemble_type: nil, **layout_kwargs)
  @ensemble_type = ensemble_type&.to_sym
  ensure_known_ensemble_type!
  super(project: project, **layout_kwargs, kind: :score)
end

Instance Attribute Details

#ensemble_type ⇒ Object (readonly)

Returns the value of attribute ensemble_type.



10
11
12
# File 'lib/head_music/content/score.rb', line 10

def ensemble_type
  @ensemble_type
end

Class Method Details

.attributes_from_h(hash, project:) ⇒ Object



12
13
14
15
# File 'lib/head_music/content/score.rb', line 12

def self.attributes_from_h(hash, project:)
  hash = hash.transform_keys(&:to_s)
  super.merge(ensemble_type: hash["ensemble_type"]&.to_sym)
end

Instance Method Details

#ensure_known_ensemble_type! ⇒ Object (private)

Raises:

  • (ArgumentError)


79
80
81
82
83
84
# File 'lib/head_music/content/score.rb', line 79

def ensure_known_ensemble_type!
  return if ensemble_type.nil? || HeadMusic::Instruments::ScoreOrder.get(ensemble_type)

  known = HeadMusic::Instruments::ScoreOrder::SCORE_ORDERS.keys.join(", ")
  raise ArgumentError, "unknown ensemble type: #{ensemble_type.inspect} (known: #{known})"
end

#kept_part_indexes(flow) ⇒ Object

A score reads top to bottom in score order, so its parts are realized in that order rather than in the order they were authored.



57
58
59
60
# File 'lib/head_music/content/score.rb', line 57

def kept_part_indexes(flow)
  order = ordered_players
  super.sort_by { |index| [rank_of(flow.parts[index], order), index] }
end

#ordered_players ⇒ Object

A permutation of this layout's players, never a selection. Chairs the order does not place come last in authored order, and ties among equals keep authored order too, so two clarinets stay first and second.



32
33
34
35
36
37
# File 'lib/head_music/content/score.rb', line 32

def ordered_players
  return players if score_order.nil?

  placed, unplaced = players.each_with_index.partition { |player, _index| position_of(player) }
  placed.sort_by { |player, index| [position_of(player), index] }.map(&:first) + unplaced.map(&:first)
end

#player_groups ⇒ Object

The sections a score brackets, in score order, each holding its players. Unplaced chairs group under a nil section key.



41
42
43
44
45
46
47
# File 'lib/head_music/content/score.rb', line 41

def player_groups
  # slice_when rather than chunk, which drops the nil-keyed run that the
  # unplaced chairs belong to.
  ordered_players
    .slice_when { |one, other| section_key_of(one) != section_key_of(other) }
    .map { |members| [section_key_of(members.first), members] }
end

#position_of(player) ⇒ Object (private)



64
65
66
# File 'lib/head_music/content/score.rb', line 64

def position_of(player)
  score_order.position_of(player.primary_instrument)
end

#rank_of(part, order) ⇒ Object (private)

A part whose chair this score does not place keeps its authored place at the bottom, which is where an unplaced chair sits in #ordered_players too.



74
75
76
77
# File 'lib/head_music/content/score.rb', line 74

def rank_of(part, order)
  index = part.player && order.index { |player| player.equal?(part.player) }
  index || order.length
end

#score_order ⇒ Object



23
24
25
26
27
# File 'lib/head_music/content/score.rb', line 23

def score_order
  return nil if ensemble_type.nil?

  @score_order ||= HeadMusic::Instruments::ScoreOrder.get(ensemble_type)
end

#section_key_of(player) ⇒ Object (private)



68
69
70
# File 'lib/head_music/content/score.rb', line 68

def section_key_of(player)
  score_order&.section_key_of(player.primary_instrument)
end

#to_h ⇒ Object



49
50
51
# File 'lib/head_music/content/score.rb', line 49

def to_h
  super.merge("ensemble_type" => ensemble_type&.to_s)
end