Class: HeadMusic::Notation::ABC::Header
- Inherits:
-
Object
- Object
- HeadMusic::Notation::ABC::Header
- Defined in:
- lib/head_music/notation/abc/header.rb
Overview
Splits an ABC tune string into header fields and tune body.
The K: (key) field is required and must be the last header field; everything after that line is the tune body. Requiring K: avoids silently defaulting the composition to C major.
Constant Summary collapse
- FIELD_PATTERN =
/\A([A-Za-z]):(.*)\z/- FRACTION_PATTERN =
%r{\A\d+/\d+\z}
Instance Attribute Summary collapse
-
#annotations ⇒ Object
readonly
Returns the value of attribute annotations.
-
#body ⇒ Object
readonly
Returns the value of attribute body.
-
#body_start_line ⇒ Object
readonly
Returns the value of attribute body_start_line.
-
#composer ⇒ Object
readonly
Returns the value of attribute composer.
-
#key_signature ⇒ Object
readonly
Returns the value of attribute key_signature.
-
#meter ⇒ Object
readonly
Returns the value of attribute meter.
-
#origin ⇒ Object
readonly
Returns the value of attribute origin.
-
#reference_number ⇒ Object
readonly
Returns the value of attribute reference_number.
-
#title ⇒ Object
readonly
Returns the value of attribute title.
-
#voice_ids ⇒ Object
readonly
Returns the value of attribute voice_ids.
Instance Method Summary collapse
-
#apply_defaults ⇒ Object
private
-
#assign_field(stripped_line, line_number) ⇒ Object
private
Returns true when the field is K:, which terminates the header.
-
#capture_body(lines, key_line_index) ⇒ Object
private
-
#default_unit_note_length ⇒ Object
private
ABC 2.1 default: when L: is absent, meters smaller than 3/4 imply sixteenth notes; 3/4 and larger imply eighth notes.
-
#initialize(abc_string, start_line: 1) ⇒ Header
constructor
start_line offsets reported line numbers, so a tune parsed out of a larger book raises errors with book-relative line numbers.
-
#parse(abc_string) ⇒ Object
private
-
#raise_missing_key_error ⇒ Object
private
-
#resolve_meter(value, line_number) ⇒ Object
private
-
#resolve_unit_note_length(value, line_number) ⇒ Object
private
-
#unit_note_length ⇒ Object
Constructor Details
#initialize(abc_string, start_line: 1) ⇒ Header
start_line offsets reported line numbers, so a tune parsed out of a larger book raises errors with book-relative line numbers.
16 17 18 19 20 21 |
# File 'lib/head_music/notation/abc/header.rb', line 16 def initialize(abc_string, start_line: 1) @annotations = [] @voice_ids = [] @start_line = start_line parse(abc_string) end |
Instance Attribute Details
#annotations ⇒ Object (readonly)
Returns the value of attribute annotations.
10 11 12 |
# File 'lib/head_music/notation/abc/header.rb', line 10 def annotations @annotations end |
#body ⇒ Object (readonly)
Returns the value of attribute body.
10 11 12 |
# File 'lib/head_music/notation/abc/header.rb', line 10 def body @body end |
#body_start_line ⇒ Object (readonly)
Returns the value of attribute body_start_line.
10 11 12 |
# File 'lib/head_music/notation/abc/header.rb', line 10 def body_start_line @body_start_line end |
#composer ⇒ Object (readonly)
Returns the value of attribute composer.
10 11 12 |
# File 'lib/head_music/notation/abc/header.rb', line 10 def composer @composer end |
#key_signature ⇒ Object (readonly)
Returns the value of attribute key_signature.
10 11 12 |
# File 'lib/head_music/notation/abc/header.rb', line 10 def key_signature @key_signature end |
#meter ⇒ Object (readonly)
Returns the value of attribute meter.
10 11 12 |
# File 'lib/head_music/notation/abc/header.rb', line 10 def meter @meter end |
#origin ⇒ Object (readonly)
Returns the value of attribute origin.
10 11 12 |
# File 'lib/head_music/notation/abc/header.rb', line 10 def origin @origin end |
#reference_number ⇒ Object (readonly)
Returns the value of attribute reference_number.
10 11 12 |
# File 'lib/head_music/notation/abc/header.rb', line 10 def reference_number @reference_number end |
#title ⇒ Object (readonly)
Returns the value of attribute title.
10 11 12 |
# File 'lib/head_music/notation/abc/header.rb', line 10 def title @title end |
#voice_ids ⇒ Object (readonly)
Returns the value of attribute voice_ids.
10 11 12 |
# File 'lib/head_music/notation/abc/header.rb', line 10 def voice_ids @voice_ids end |
Instance Method Details
#apply_defaults ⇒ Object (private)
104 105 106 |
# File 'lib/head_music/notation/abc/header.rb', line 104 def apply_defaults @meter ||= HeadMusic::Rudiment::Meter.common_time end |
#assign_field(stripped_line, line_number) ⇒ Object (private)
Returns true when the field is K:, which terminates the header.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/head_music/notation/abc/header.rb', line 46 def assign_field(stripped_line, line_number) match = FIELD_PATTERN.match(stripped_line) unless match raise HeadMusic::Notation::ABC::ParseError.new( "Expected a header field; the tune body may not begin before the K: (key) field", line_number: line_number, snippet: stripped_line ) end letter = match[1] value = match[2].strip case letter when "X" then @reference_number = value when "T" then @title = value when "C" then @composer = value when "O" then @origin = value when "N" then @annotations << value when "M" then @meter = resolve_meter(value, line_number) when "L" then @unit_note_length = resolve_unit_note_length(value, line_number) when "V" then @voice_ids << value.split.first when "K" @key_signature = HeadMusic::Notation::ABC::KeyMapper.new(value, line_number: line_number).key_signature return true else raise HeadMusic::Notation::ABC::UnsupportedFeatureError.new( "Unsupported header field #{letter.inspect}", line_number: line_number, snippet: stripped_line ) end false end |
#capture_body(lines, key_line_index) ⇒ Object (private)
76 77 78 79 |
# File 'lib/head_music/notation/abc/header.rb', line 76 def capture_body(lines, key_line_index) @body = lines[(key_line_index + 1)..].join @body_start_line = key_line_index + 1 + @start_line end |
#default_unit_note_length ⇒ Object (private)
ABC 2.1 default: when L: is absent, meters smaller than 3/4 imply sixteenth notes; 3/4 and larger imply eighth notes.
110 111 112 113 |
# File 'lib/head_music/notation/abc/header.rb', line 110 def default_unit_note_length meter_fraction = Rational(meter.top_number, meter.bottom_number) (meter_fraction < Rational(3, 4)) ? Rational(1, 16) : Rational(1, 8) end |
#parse(abc_string) ⇒ Object (private)
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/head_music/notation/abc/header.rb', line 29 def parse(abc_string) lines = abc_string.to_s.lines lines.each_with_index do |raw_line, index| line_number = index + @start_line stripped = raw_line.strip next if stripped.empty? || stripped.start_with?("%") if assign_field(stripped, line_number) capture_body(lines, index) break end end raise_missing_key_error unless @key_signature apply_defaults end |
#raise_missing_key_error ⇒ Object (private)
115 116 117 118 |
# File 'lib/head_music/notation/abc/header.rb', line 115 def raise_missing_key_error raise HeadMusic::Notation::ABC::ParseError, "Missing required K: (key) field" end |
#resolve_meter(value, line_number) ⇒ Object (private)
81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/head_music/notation/abc/header.rb', line 81 def resolve_meter(value, line_number) # "C" and "C|" must be translated before calling Meter.get, which # memoizes by identifier and would cache a meaningless "C" meter. return HeadMusic::Rudiment::Meter.common_time if value == "C" return HeadMusic::Rudiment::Meter.cut_time if value == "C|" unless FRACTION_PATTERN.match?(value) raise HeadMusic::Notation::ABC::ParseError.new( "Invalid meter #{value.inspect}", line_number: line_number, snippet: value ) end HeadMusic::Rudiment::Meter.get(value) end |
#resolve_unit_note_length(value, line_number) ⇒ Object (private)
95 96 97 98 99 100 101 102 |
# File 'lib/head_music/notation/abc/header.rb', line 95 def resolve_unit_note_length(value, line_number) unless FRACTION_PATTERN.match?(value) raise HeadMusic::Notation::ABC::ParseError.new( "Invalid unit note length #{value.inspect}", line_number: line_number, snippet: value ) end Rational(value) end |
#unit_note_length ⇒ Object
23 24 25 |
# File 'lib/head_music/notation/abc/header.rb', line 23 def unit_note_length @unit_note_length || default_unit_note_length end |