Class: HeadMusic::Notation::ABC::ChordReader

Inherits:
Object
  • Object
show all
Defined in:
lib/head_music/notation/abc/chord_reader.rb

Overview

Resolves a bracket-chord token into the pitches it sounds and the one length its notes share.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(duration_resolver) ⇒ ChordReader

Returns a new instance of ChordReader.



6
7
8
# File 'lib/head_music/notation/abc/chord_reader.rb', line 6

def initialize(duration_resolver)
  @duration_resolver = duration_resolver
end

Instance Attribute Details

#duration_resolverObject (readonly, private)

Returns the value of attribute duration_resolver.



23
24
25
# File 'lib/head_music/notation/abc/chord_reader.rb', line 23

def duration_resolver
  @duration_resolver
end

Instance Method Details

#ensure_unique_pitches(pitches, token) ⇒ Object (private)

Raises:



25
26
27
28
29
30
31
32
# File 'lib/head_music/notation/abc/chord_reader.rb', line 25

def ensure_unique_pitches(pitches, token)
  return if pitches.uniq.length == pitches.length

  raise ParseError.new(
    "Chord pitches must be unique",
    line_number: token.line, snippet: snippet(token)
  )
end

#read(token, pitch_builder) ⇒ Object

Returns the chord’s pitches and its shared inner length. Chord pitches resolve in bracket order, so an explicit accidental inside a chord persists for the rest of the bar like any other.



13
14
15
16
17
18
19
# File 'lib/head_music/notation/abc/chord_reader.rb', line 13

def read(token, pitch_builder)
  pitches = token.notes.map do |note|
    pitch_builder.pitch(note.letter, note.octave_marks, note.accidental)
  end
  ensure_unique_pitches(pitches, token)
  [pitches, uniform_length(token)]
end

#snippet(token) ⇒ Object (private)



49
50
51
52
53
54
# File 'lib/head_music/notation/abc/chord_reader.rb', line 49

def snippet(token)
  inner = token.notes.map do |note|
    "#{note.accidental}#{note.letter}#{note.octave_marks}#{note.length}"
  end.join
  "[#{inner}]"
end

#uniform_length(token) ⇒ Object (private)

ABC 2.1 sec. 4.17 allows per-note lengths only when they agree; the shared inner length then multiplies with any outer length. Unequal lengths (whose ABC meaning is “the duration of the first note”) would need silent reinterpretation to fit one rhythmic value, so we reject.

Raises:



38
39
40
41
42
43
44
45
46
47
# File 'lib/head_music/notation/abc/chord_reader.rb', line 38

def uniform_length(token)
  fractions = token.notes.map { |note| duration_resolver.length_fraction(note.length) }
  return fractions.first if fractions.uniq.length == 1

  raise ParseError.new(
    'Chord notes must share one length; write it after the bracket ("[CEG]2") ' \
    'or repeat it on every note ("[C2E2G2]")',
    line_number: token.line, snippet: snippet(token)
  )
end