Class: HeadMusic::Instruments::ScoreOrder

Inherits:
Object
  • Object
show all
Includes:
Named
Defined in:
lib/head_music/instruments/score_order.rb

Constant Summary collapse

SCORE_ORDERS =
YAML.load_file(File.expand_path("score_orders.yml", __dir__)).freeze
DEFAULT_ENSEMBLE_TYPE_KEY =
:orchestral

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ensemble_type_key = DEFAULT_ENSEMBLE_TYPE_KEY) ⇒ ScoreOrder (private)

Returns a new instance of ScoreOrder.



59
60
61
62
63
64
65
# File 'lib/head_music/instruments/score_order.rb', line 59

def initialize(ensemble_type_key = DEFAULT_ENSEMBLE_TYPE_KEY)
  @ensemble_type_key = ensemble_type_key.to_sym
  data = SCORE_ORDERS[ensemble_type_key.to_s]

  @sections = data["sections"] || []
  self.name = data["name"] || ensemble_type_key.to_s.tr("_", " ").capitalize
end

Instance Attribute Details

#alias_name_keysObject (readonly) Originally defined in module Named

Returns the value of attribute alias_name_keys.

#ensemble_type_keyObject (readonly)

Returns the value of attribute ensemble_type_key.



10
11
12
# File 'lib/head_music/instruments/score_order.rb', line 10

def ensemble_type_key
  @ensemble_type_key
end

#name_keyObject (readonly) Originally defined in module Named

Returns the value of attribute name_key.

#sectionsObject (readonly)

Returns the value of attribute sections.



10
11
12
# File 'lib/head_music/instruments/score_order.rb', line 10

def sections
  @sections
end

Class Method Details

.get(ensemble_type) ⇒ Object



12
13
14
15
16
17
18
# File 'lib/head_music/instruments/score_order.rb', line 12

def self.get(ensemble_type)
  @instances ||= {}
  key = HeadMusic::Utilities::HashKey.for(ensemble_type)
  return unless SCORE_ORDERS.key?(key.to_s)

  @instances[key] ||= new(key)
end

.in_band_order(instruments) ⇒ Object



24
25
26
# File 'lib/head_music/instruments/score_order.rb', line 24

def self.in_band_order(instruments)
  get(:band).order(instruments)
end

.in_orchestral_order(instruments) ⇒ Object



20
21
22
# File 'lib/head_music/instruments/score_order.rb', line 20

def self.in_orchestral_order(instruments)
  get(:orchestral).order(instruments)
end

Instance Method Details

#build_section_indexObject (private)

A key listed twice -- two trumpets in a quintet -- keeps the last position, which is where the section still ends.



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/head_music/instruments/score_order.rb', line 112

def build_section_index
  index = {}
  position = 0

  sections.each do |section|
    section_key = section["section_key"]&.to_sym
    instruments = section["instruments"] || []
    instruments.each do |instrument_key|
      index[instrument_key.to_s] = {position: position, section_key: section_key}
      position += 1
    end
  end

  index
end

#ensure_localized_name(name:, locale_code: Locale::DEFAULT_CODE, abbreviation: nil) ⇒ Object Originally defined in module Named

#entry_by_family(instrument) ⇒ Object (private)



141
142
143
144
145
146
147
148
149
# File 'lib/head_music/instruments/score_order.rb', line 141

def entry_by_family(instrument)
  return nil unless instrument.family_key

  family_base = instrument.family_key.to_s
  instrument_key = instrument.name_key.to_s
  return nil unless instrument_key.include?(family_base)

  section_index[instrument_key] || section_index[family_base]
end

#entry_by_name_key(instrument) ⇒ Object (private)



135
136
137
138
139
# File 'lib/head_music/instruments/score_order.rb', line 135

def entry_by_name_key(instrument)
  return nil unless instrument.name_key

  section_index[instrument.name_key.to_s]
end

#entry_by_normalized_name(instrument) ⇒ Object (private)



151
152
153
# File 'lib/head_music/instruments/score_order.rb', line 151

def entry_by_normalized_name(instrument)
  section_index[HeadMusic::Utilities::Case.to_snake_case(instrument.name)]
end

#entry_for(instrument) ⇒ Object (private)



90
91
92
93
94
95
# File 'lib/head_music/instruments/score_order.rb', line 90

def entry_for(instrument)
  return nil if instrument.nil?

  normalized = normalize_to_instrument(instrument)
  normalized && find_entry(normalized)
end

#find_entry(instrument) ⇒ Object (private)

An entry is a Hash, so a nil lookup safely means "absent".



129
130
131
132
133
# File 'lib/head_music/instruments/score_order.rb', line 129

def find_entry(instrument)
  entry_by_name_key(instrument) ||
    entry_by_family(instrument) ||
    entry_by_normalized_name(instrument)
end

#find_position_with_transposition(instrument) ⇒ Object (private)



155
156
157
158
159
160
# File 'lib/head_music/instruments/score_order.rb', line 155

def find_position_with_transposition(instrument)
  entry = find_entry(instrument)
  return nil unless entry

  entry.merge(transposition: instrument.default_sounding_transposition || 0)
end

#group(instruments) ⇒ Object

The same instruments as #order, split into their sections. Instruments this order does not know come last, under a nil section key.



47
48
49
50
51
52
53
# File 'lib/head_music/instruments/score_order.rb', line 47

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

#localized_name(locale_code: Locale::DEFAULT_CODE) ⇒ Object Originally defined in module Named

#localized_name_in_default_localeObject (private) Originally defined in module Named

#localized_name_in_locale_matching_language(locale) ⇒ Object (private) Originally defined in module Named

#localized_name_in_matching_locale(locale) ⇒ Object (private) Originally defined in module Named

#localized_namesObject Originally defined in module Named

Returns an array of LocalizedName instances that are synonymous with the name.

#name(locale_code: Locale::DEFAULT_CODE) ⇒ Object Originally defined in module Named

#name=(name) ⇒ Object Originally defined in module Named

#normalize_inputs(instruments) ⇒ Object (private)



67
68
69
70
# File 'lib/head_music/instruments/score_order.rb', line 67

def normalize_inputs(instruments)
  valid_inputs = instruments.compact.reject { |i| i.respond_to?(:empty?) && i.empty? }
  valid_inputs.map { |i| normalize_to_instrument(i) }.compact
end

#normalize_to_instrument(input) ⇒ Object (private)



97
98
99
100
101
102
# File 'lib/head_music/instruments/score_order.rb', line 97

def normalize_to_instrument(input)
  return input if input.is_a?(HeadMusic::Instruments::Instrument)
  return input if input.respond_to?(:name_key) && input.respond_to?(:family_key)

  HeadMusic::Instruments::Instrument.get(input)
end

#order(instruments) ⇒ Object



28
29
30
31
# File 'lib/head_music/instruments/score_order.rb', line 28

def order(instruments)
  known, unknown = partition_by_known_position(normalize_inputs(instruments))
  sort_known(known) + unknown.sort_by(&:to_s)
end

#partition_by_known_position(instrument_objects) ⇒ Object (private)



72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/head_music/instruments/score_order.rb', line 72

def partition_by_known_position(instrument_objects)
  known = []
  unknown = []
  instrument_objects.each do |instrument|
    position_info = find_position_with_transposition(instrument)
    if position_info
      known << [instrument, position_info]
    else
      unknown << instrument
    end
  end
  [known, unknown]
end

#position_of(instrument) ⇒ Object

Where the instrument sits in the score, counting from the top, or nil where this order does not know it.



35
36
37
# File 'lib/head_music/instruments/score_order.rb', line 35

def position_of(instrument)
  entry_for(instrument)&.fetch(:position)
end

#section_indexObject (private)

One index behind both the ordering and the grouping, so the two cannot disagree about where an instrument belongs.



106
107
108
# File 'lib/head_music/instruments/score_order.rb', line 106

def section_index
  @section_index ||= build_section_index
end

#section_key_of(instrument) ⇒ Object

Which section the instrument belongs to -- what a score brackets together. Nil where this order does not know the instrument.



41
42
43
# File 'lib/head_music/instruments/score_order.rb', line 41

def section_key_of(instrument)
  entry_for(instrument)&.fetch(:section_key)
end

#sort_known(known) ⇒ Object (private)



86
87
88
# File 'lib/head_music/instruments/score_order.rb', line 86

def sort_known(known)
  known.sort_by { |_, pos_info| [pos_info[:position], -pos_info[:transposition]] }.map(&:first)
end