Class: HeadMusic::Notation::ABC::VoiceState
- Inherits:
-
Object
- Object
- HeadMusic::Notation::ABC::VoiceState
- Defined in:
- lib/head_music/notation/abc/voice_state.rb
Overview
Per-voice interpretation state and the note-assembly it drives. Accidentals, the deferred note, ties, and volta tracking are all independent between voices, so each voice owns one of these. Beam adjacency and tie bookkeeping are exposed as transitions rather than raw setters, and the parser hands notes and chords here to be buffered, tied, and flushed onto the voice — the behavior that reads and mutates this state lives with the state itself.
Defined Under Namespace
Classes: PendingNote
Constant Summary collapse
- ONE =
The identity length scale, used wherever no stretching applies.
Rational(1)
Instance Attribute Summary collapse
-
#active_passes ⇒ Object
Returns the value of attribute active_passes.
-
#awaiting_scale ⇒ Object
Returns the value of attribute awaiting_scale.
-
#broken_line ⇒ Object
Returns the value of attribute broken_line.
-
#pending_note ⇒ Object
Returns the value of attribute pending_note.
-
#pitch_builder ⇒ Object
readonly
Returns the value of attribute pitch_builder.
-
#tie_line ⇒ Object
readonly
Returns the value of attribute tie_line.
-
#voice ⇒ Object
readonly
Returns the value of attribute voice.
-
#volta_start_bar ⇒ Object
Returns the value of attribute volta_start_bar.
Instance Method Summary collapse
-
#append_tied(head, tail) ⇒ Object
private
Attaches
tailat the deep end ofhead’s tied chain, rebuilding each link (RhythmicValue exposes no setter) so a chain like “half tied to eighth” gains a further “tied to quarter”. -
#close_tie ⇒ Object
-
#completed_bar_number ⇒ Object
-
#defer_placement(pitches, length, inner_scale = ONE) ⇒ Object
Buffers a note or chord as the pending note, flushing whatever was pending first.
-
#ensure_tie_pitches_match(pending, pitches) ⇒ Object
private
-
#entered_bar_number ⇒ Object
-
#flush_pending_note ⇒ Object
Places the pending note onto the voice, if any, carrying its authored beam break onto the placement.
-
#initialize(voice, pitch_builder, duration_resolver = nil) ⇒ VoiceState
constructor
A new instance of VoiceState.
-
#mark_beam_break ⇒ Object
Records an explicit beam break; the next note consumes it.
-
#next_beam_break ⇒ Object
The beam-break flag for the note now beginning: true if a break was marked, false if the previous token was also a note (so they beam together), or nil when there is no prior note to beam against.
-
#open_tie(line) ⇒ Object
-
#pending_rhythmic_value(pending) ⇒ Object
private
A pending note’s own value, with any tied prefix appended ahead of it so the whole tie chain renders as one sounding note.
-
#place(length, pitches, scale: ONE) ⇒ Object
Places a note, chord, or rest (nil pitches) directly onto the voice, bypassing the pending-note buffer.
-
#reset_beam_adjacency ⇒ Object
After a rest, bar line, volta, or voice change, the next note must not beam to whatever preceded the boundary.
-
#tie_onto_pending(pitches, length, scale) ⇒ Object
private
Closes an open tie: the pending note becomes the new note’s tied prefix, so the pair (and any longer chain) resolves to a single placement whose rhythmic value carries the author’s chosen split.
-
#tie_open? ⇒ Boolean
Constructor Details
#initialize(voice, pitch_builder, duration_resolver = nil) ⇒ VoiceState
Returns a new instance of VoiceState.
28 29 30 31 32 33 34 35 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 28 def initialize(voice, pitch_builder, duration_resolver = nil) @voice = voice @pitch_builder = pitch_builder @duration_resolver = duration_resolver @beam_break_pending = false @beam_last_was_note = false @tie_open = false end |
Instance Attribute Details
#active_passes ⇒ Object
Returns the value of attribute active_passes.
25 26 27 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 25 def active_passes @active_passes end |
#awaiting_scale ⇒ Object
Returns the value of attribute awaiting_scale.
25 26 27 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 25 def awaiting_scale @awaiting_scale end |
#broken_line ⇒ Object
Returns the value of attribute broken_line.
25 26 27 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 25 def broken_line @broken_line end |
#pending_note ⇒ Object
Returns the value of attribute pending_note.
25 26 27 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 25 def pending_note @pending_note end |
#pitch_builder ⇒ Object (readonly)
Returns the value of attribute pitch_builder.
24 25 26 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 24 def pitch_builder @pitch_builder end |
#tie_line ⇒ Object (readonly)
Returns the value of attribute tie_line.
24 25 26 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 24 def tie_line @tie_line end |
#voice ⇒ Object (readonly)
Returns the value of attribute voice.
24 25 26 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 24 def voice @voice end |
#volta_start_bar ⇒ Object
Returns the value of attribute volta_start_bar.
25 26 27 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 25 def @volta_start_bar end |
Instance Method Details
#append_tied(head, tail) ⇒ Object (private)
Attaches tail at the deep end of head’s tied chain, rebuilding
each link (RhythmicValue exposes no setter) so a chain like
“half tied to eighth” gains a further “tied to quarter”.
155 156 157 158 159 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 155 def append_tied(head, tail) tied = head.tied_value inner = tied ? append_tied(tied, tail) : tail HeadMusic::Rudiment::RhythmicValue.new(head.unit, dots: head.dots, tied_value: inner) end |
#close_tie ⇒ Object
81 82 83 84 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 81 def close_tie @tie_open = false @tie_line = nil end |
#completed_bar_number ⇒ Object
37 38 39 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 37 def voice.last_placement&.position&. end |
#defer_placement(pitches, length, inner_scale = ONE) ⇒ Object
Buffers a note or chord as the pending note, flushing whatever was pending first. A broken-rhythm scale awaiting its right note, and any per-chord inner scale, fold into the buffered length. When a tie is open the arriving note instead extends the pending tie chain.
90 91 92 93 94 95 96 97 98 99 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 90 def defer_placement(pitches, length, inner_scale = ONE) scale = (awaiting_scale || ONE) * inner_scale self.awaiting_scale = nil return tie_onto_pending(pitches, length, scale) if tie_open? flush_pending_note self.pending_note = PendingNote.new( pitches: pitches, length: length, scale: scale, beam_break: next_beam_break ) end |
#ensure_tie_pitches_match(pending, pitches) ⇒ Object (private)
135 136 137 138 139 140 141 142 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 135 def ensure_tie_pitches_match(pending, pitches) return if pending.pitches.sort == pitches.sort raise ParseError.new( "A tie must connect two notes of the same pitch", line_number: tie_line, snippet: "-" ) end |
#entered_bar_number ⇒ Object
41 42 43 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 41 def voice.next_position. end |
#flush_pending_note ⇒ Object
Places the pending note onto the voice, if any, carrying its authored beam break onto the placement.
103 104 105 106 107 108 109 110 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 103 def flush_pending_note pending = pending_note return unless pending self.pending_note = nil placement = voice.place(voice.next_position, pending_rhythmic_value(pending), pending.pitches) placement.beam_break_before = pending.beam_break end |
#mark_beam_break ⇒ Object
Records an explicit beam break; the next note consumes it.
46 47 48 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 46 def mark_beam_break @beam_break_pending = true end |
#next_beam_break ⇒ Object
The beam-break flag for the note now beginning: true if a break was marked, false if the previous token was also a note (so they beam together), or nil when there is no prior note to beam against. Consumes the pending break and records that a note is now current.
54 55 56 57 58 59 60 61 62 63 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 54 def next_beam_break flag = if @beam_break_pending true elsif @beam_last_was_note false end @beam_break_pending = false @beam_last_was_note = true flag end |
#open_tie(line) ⇒ Object
76 77 78 79 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 76 def open_tie(line) @tie_open = true @tie_line = line end |
#pending_rhythmic_value(pending) ⇒ Object (private)
A pending note’s own value, with any tied prefix appended ahead of it so the whole tie chain renders as one sounding note.
146 147 148 149 150 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 146 def pending_rhythmic_value(pending) own = @duration_resolver.rhythmic_value(pending.length, scale: pending.scale) prefix = pending.tied_prefix prefix ? append_tied(prefix, own) : own end |
#place(length, pitches, scale: ONE) ⇒ Object
Places a note, chord, or rest (nil pitches) directly onto the voice, bypassing the pending-note buffer.
114 115 116 117 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 114 def place(length, pitches, scale: ONE) rhythmic_value = @duration_resolver.rhythmic_value(length, scale: scale) voice.place(voice.next_position, rhythmic_value, pitches) end |
#reset_beam_adjacency ⇒ Object
After a rest, bar line, volta, or voice change, the next note must not beam to whatever preceded the boundary.
67 68 69 70 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 67 def reset_beam_adjacency @beam_last_was_note = false @beam_break_pending = false end |
#tie_onto_pending(pitches, length, scale) ⇒ Object (private)
Closes an open tie: the pending note becomes the new note’s tied prefix, so the pair (and any longer chain) resolves to a single placement whose rhythmic value carries the author’s chosen split.
124 125 126 127 128 129 130 131 132 133 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 124 def tie_onto_pending(pitches, length, scale) pending = pending_note ensure_tie_pitches_match(pending, pitches) prefix = pending_rhythmic_value(pending) close_tie self.pending_note = PendingNote.new( pitches: pitches, length: length, scale: scale, tied_prefix: prefix, beam_break: pending.beam_break ) end |
#tie_open? ⇒ Boolean
72 73 74 |
# File 'lib/head_music/notation/abc/voice_state.rb', line 72 def tie_open? @tie_open end |