Class: HeadMusic::Content::Staff

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

Overview

A staff in a part: how many lines it has, what clef is in force on it at any moment, and -- for a percussion staff -- which catalog staff it realizes.

This is the instance layer. HeadMusic::Instruments::Staff is the catalog layer: what a piano's staves are, read from YAML. A content staff references one rather than re-implementing it, because the catalog class already owns the position-to-instrument mappings a percussion staff needs.

Constant Summary collapse

DEFAULT_LINE_COUNT =
5

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(clef: nil, line_count: DEFAULT_LINE_COUNT, instruments_staff: nil) ⇒ Staff

Returns a new instance of Staff.

Parameters:

  • clef (HeadMusic::Rudiment::Clef, String, Symbol, nil) (defaults to: nil)

    the clef the staff opens with; nil leaves the choice to the writers, which fall back to inferring one from a voice's range

  • instruments_staff (HeadMusic::Instruments::Staff, nil) (defaults to: nil)

    the catalog staff this realizes, for percussion mapping



21
22
23
24
25
# File 'lib/head_music/content/staff.rb', line 21

def initialize(clef: nil, line_count: DEFAULT_LINE_COUNT, instruments_staff: nil)
  @line_count = line_count
  @instruments_staff = instruments_staff
  @clef_map = HeadMusic::Time::EventMap.new(default: clef && HeadMusic::Rudiment::Clef.get(clef))
end

Instance Attribute Details

#instruments_staffObject (readonly)

Returns the value of attribute instruments_staff.



14
15
16
# File 'lib/head_music/content/staff.rb', line 14

def instruments_staff
  @instruments_staff
end

#line_countObject (readonly)

Returns the value of attribute line_count.



14
15
16
# File 'lib/head_music/content/staff.rb', line 14

def line_count
  @line_count
end

Instance Method Details

#change_clef(bar_number, clef) ⇒ Object



40
41
42
# File 'lib/head_music/content/staff.rb', line 40

def change_clef(bar_number, clef)
  @clef_map.add(downbeat_of(bar_number), HeadMusic::Rudiment::Clef.get(clef)).value
end

#clefObject



36
37
38
# File 'lib/head_music/content/staff.rb', line 36

def clef
  clef_at(HeadMusic::Time::MusicalPosition::DEFAULT_FIRST_BAR)
end

#clef_at(bar_number) ⇒ Object

The clef in force at a bar, or nil where none was authored.

Nil rather than a guess: choosing a clef from a voice's pitch range needs a voice, and a staff has no back-reference to one. The writers hold that fallback, which is where the voice is in scope.



32
33
34
# File 'lib/head_music/content/staff.rb', line 32

def clef_at(bar_number)
  @clef_map.at(downbeat_of(bar_number))
end

#clef_changesObject



44
45
46
# File 'lib/head_music/content/staff.rb', line 44

def clef_changes
  @clef_map.events.to_h { |event| [event.position.bar, event.value] }
end

#downbeat_of(bar_number) ⇒ Object (private)



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

def downbeat_of(bar_number)
  HeadMusic::Time::MusicalPosition.new(bar_number, HeadMusic::Time::MusicalPosition::FIRST_COUNT, 0, 0)
end

#instrument_for_position(position_index) ⇒ HeadMusic::Rudiment::Pitch?

Returns what a percussion position sounds.

Returns:



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

def instrument_for_position(position_index)
  instruments_staff&.instrument_for_position(position_index)
end

#to_hObject

A null clef is a staff whose clef was never authored, which the writers infer from a voice's range instead. It is a real state, not a missing one.



59
60
61
62
63
64
# File 'lib/head_music/content/staff.rb', line 59

def to_h
  hash = {"clef" => clef&.name_key&.to_s}
  changes = clef_changes.map { |bar_number, value| {"number" => bar_number, "clef" => value.name_key.to_s} }
  hash["clef_changes"] = changes unless changes.empty?
  hash
end

#to_sObject



53
54
55
# File 'lib/head_music/content/staff.rb', line 53

def to_s
  [clef, "#{line_count}-line"].compact.join(" ")
end