# ABC notation 2.0 - simpleparse specification
#   This is a simpleparse EBNF created from the ABC 2.0 ABNF written
#     by Henrik Norbeck.
#   It has a few constructs added to allow parsing of legacy ABC (1.6 and 1.7)
#   This version is written by Tom Satter
#   Copyright 2004, 2005, 2006, 2007 Tom Satter
#   Licensed under the GPL
#
# --------------------------------------------------------------------------------

abc_file   := (abc_tune / file_field / tex_line / EOL / text_line)*, EOF!
abc_tune   := abc_header, !, abc_music_line+, ?EOF
tex_line   := "\\\\", any_text_string, EOL  # deprecated - kept only for backward compatibility with abc2mtex
text_line  := any_text*, EOL # Free text between tunes. This is a catch_all rule - check for fields first!

# --------------------------------------------------------------------------------

abc_header     := field_number?, title_fields, !, header_fields*, field_key
abc_music_line := tune_field / tex_line / abc_line / EOL
abc_line       := element+, EOL!

# --------------------------------------------------------------------------------

# --------------------------------------------------------------------------------

title_fields  := tex_line*, (field_title, tex_line*)+
file_field    := field_area / field_book / field_composer /
                 field_discography / field_file / field_group /
                 field_history / field_instruction / field_length /
                 header_field_meter / field_notes / field_origin / field_parts /
                 field_tempo / field_rhythm / field_source /
                 field_userdef_print / field_userdef_play / field_words /
                 field_transcription / field_key / unused_field
header_fields := field_area / field_book / field_composer /
                 field_discography / field_file / field_group /
                 field_history / field_instruction / field_length /
                 header_field_meter / field_notes / field_origin / field_parts /
                 field_tempo / field_rhythm / field_source /
                 field_userdef_print / field_userdef_play / field_voice /
                 field_words / field_transcription / field_macro /
                 unused_field / tex_line / EOL
tune_field    := field_area / field_book / field_composer /
                 field_discography / field_group / field_history /
                 field_instruction / field_length / field_meter /
                 field_notes / field_origin / field_part / field_tempo /
                 field_rhythm / field_source / field_title /
                 field_voice / field_words / field_lyrics /
                 field_transcription / field_key / unused_field
inline_field  := ifield_area / ifield_book / ifield_composer /
                 ifield_discography / ifield_group / ifield_history /
                 ifield_instruction / ifield_length / ifield_meter /
                 ifield_notes / ifield_origin / ifield_part /
                 ifield_tempo / ifield_rhythm / ifield_source /
                 ifield_title / ifield_voice / ifield_words /
                 ifield_lyrics / ifield_transcription / ifield_key

# --------------------------------------------------------------------------------

field_number         :=  "X:", !, (WSP/LC)?, integer, (WSP/LC)?, EOL
field_title          :=  "T:", !, (WSP/LC)?, any_text_string, EOL
ifield_title         := "[T:", !, (WSP/LC)?, any_text_ifield, "]"
field_area           :=  "A:", !, (WSP/LC)?, any_text_string, EOL
ifield_area          := "[A:", !, (WSP/LC)?, any_text_ifield, "]"
field_book           :=  "B:", !, (WSP/LC)?, any_text_string, EOL
ifield_book          := "[B:", !, (WSP/LC)?, any_text_ifield, "]"
field_composer       :=  "C:", !, (WSP/LC)?, any_text_string, EOL
ifield_composer      := "[C:", !, (WSP/LC)?, any_text_ifield, "]"
field_discography    :=  "D:", !, (WSP/LC)?, any_text_string, EOL
ifield_discography   := "[D:", !, (WSP/LC)?, any_text_ifield, "]"
field_file           :=  "F:", !, (WSP/LC)?, any_text_string, EOL
field_group          :=  "G:", !, (WSP/LC)?, any_text_string, EOL
ifield_group         := "[G:", !, (WSP/LC)?, any_text_ifield, "]"
field_history        :=  "H:", !, (WSP/LC)?, any_text_string, EOL
ifield_history       := "[H:", !, (WSP/LC)?, any_text_ifield, "]"
field_instruction    :=  "I:", !, (WSP/LC)?, instruction, (WSP/LC)?, EOL
ifield_instruction   := "[I:", !, (WSP/LC)?, iinstruction, (WSP/LC)?, "]"
field_key            :=  "K:", !, (WSP/LC)?, key, (WSP/LC)?, EOL
ifield_key           := "[K:", !, (WSP/LC)?, key, (WSP/LC)?, "]"
field_length         :=  "L:", !, (WSP/LC)?, note_length_strict, (WSP/LC)?, EOL
ifield_length        := "[L:", !, (WSP/LC)?, note_length_strict, "]"
header_field_meter   :=  "M:", !, (WSP/LC)?, time_signature, (WSP/LC)?, EOL
field_meter          :=  "M:", !, (WSP/LC)?, time_signature, (WSP/LC)?, EOL
ifield_meter         := "[M:", !, (WSP/LC)?, time_signature, "]"
field_notes          :=  "N:", !, (WSP/LC)?, any_text_string, EOL
ifield_notes         := "[N:", !, (WSP/LC)?, any_text_ifield, "]"
field_origin         :=  "O:", !, (WSP/LC)?, any_text_string, EOL
ifield_origin        := "[O:", !, (WSP/LC)?, any_text_ifield, "]"
field_parts          :=  "P:", !, (WSP/LC)?, parts_play_order, (WSP/LC)?, EOL
field_part           :=  "P:", !, (WSP/LC)?, letter, (WSP/LC)?, EOL
ifield_part          := "[P:", !, (WSP/LC)?, letter, (WSP/LC)?, "]"
field_tempo          :=  "Q:", !, (WSP/LC)?, tempo_def, (WSP/LC)?, EOL
ifield_tempo         := "[Q:", !, (WSP/LC)?, tempo_def, (WSP/LC)?, "]"
field_rhythm         :=  "R:", !, (WSP/LC)?, any_text_string, EOL
ifield_rhythm        := "[R:", !, (WSP/LC)?, any_text_ifield, "]"
field_source         :=  "S:", !, (WSP/LC)?, any_text_string, EOL
ifield_source        := "[S:", !, (WSP/LC)?, any_text_ifield, "]"
field_userdef_print  :=  "U:", !, (WSP/LC)?, userdef, (WSP/LC)?, EOL
field_userdef_play   :=  "u:", !, (WSP/LC)?, userdef, (WSP/LC)?, EOL
field_voice          :=  "V:", !, (WSP/LC)?, voice, (WSP/LC)?, EOL
ifield_voice         := "[V:", !, (WSP/LC)?, voiceref, (WSP/LC)?, "]"
field_words          :=  "W:", !, (WSP/LC)?, any_text_string, EOL
ifield_words         := "[W:", !, (WSP/LC)?, any_text_ifield, "]"
field_transcription  :=  "Z:", !, (WSP/LC)?, any_text_string, EOL
ifield_transcription := "[Z:", !, (WSP/LC)?, any_text_ifield, "]"
field_macro          :=  "m:", !, (WSP/LC)?, any_text_string, EOL
field_lyrics         :=  "w:", !, (WSP/LC)?, lyrics, (WSP/LC)?, EOL
ifield_lyrics        := "[w:", !, (WSP/LC)?, any_text_ifield, "]"
unused_field         :=      [EIJYva-ln-tx-z], ":", any_text_string, EOL
unused_ifield        := "[", [EIJYa-ln-tvx-z], ":", any_text_ifield, "]"

# --------------------------------------------------------------------------------

time_signature     := "C|" / "C" / "none" / meter_num / integer
meter_sums         := integer, ("+", integer)*
meter_numerator    := ("(", meter_sums, ")") / meter_sums
meter_num          := meter_numerator, "/", integer, (WSP/LC, meter_num)?
                      # e.g. 2/4 , 6/8 , 2+2+3/16 , 11/8 , 2/4 3/4 , 2+2+3/8 2+2+3+2+2+2/8
tempo_def          := ((string_double_quote?, (WSP/LC)?, tempo, (WSP/LC)?, string_double_quote?) / string_double_quote)
tempo              := (note_length_strict, (WSP/LC, note_length_strict)*, (WSP/LC)?, "=", (WSP/LC)?, integer) / ("C", (WSP/LC)?, "=", (WSP/LC)?, integer) / integer
                      # e.g. "adagio" 1/4=60, 1/4 3/8 1/4 3/8 = 80, "allegro", 1/4=120 "con brio"
note_length_strict := integer, "/", integer
key                := clef / (key_def, (WSP/LC, clef)?) / "HP" / "Hp"
key_def            := key_pitch, (WSP/LC)?, (mode)?, (WSP/LC, global_accidental)*
key_pitch          := basenote, ("#" / "b")?
mode               := major / lydian / ionian / mixolydian / dorian / aeolian / phrygian / locrian / minor
minor              := [mM], (c"in", ([oO], ([rR])?)?)?
                      # m, min, mino, minor - all modes are case insensitive
major              := c"maj", ([oO], ([rR])?)?
lydian             := c"lyd", ([iI], ([aA], ([nN])?)?)? # major with sharp 4th
ionian             := c"ion", ([iI], ([aA], ([nN])?)?)? # =major
mixolydian         := c"mix", ([oO], ([lL], ([yY], ([dD], ([iI], ([aA], ([nN])?)?)?)?)?)?)? # major with flat 7th
dorian             := c"dor", ([iI], ([aA], ([nN])?)?)? # minor with sharp 6th
aeolian            := c"aeo", ([lL], ([iI], ([aA], ([nN])?)?)?)? # =minor
phrygian           := c"phr", ([yY], ([gG], ([iI], ([aA], ([nN])?)?)?)?)? # minor with flat 2nd
locrian            := c"loc", ([rR], ([iI], ([aA], ([nN])?)?)?)? # minor with flat 2nd and 5th
global_accidental  := accidental, basenote # e.g. ^f =c _b
parts_play_order   := ((letter, integer) / letter / "." / (("(", parts_play_order, ")"), integer) / ("(", parts_play_order, ")"))+
                      # dots are ignored - for legibility only

# --------------------------------------------------------------------------------

voice           := voiceref, (WSP/LC, (clef / voice_name / voice_subname / voice_transpose / voice_stave / voice_merge / stem_dir / voice_prop_str / voice_prop_int ))*
voiceref        := (letter / digit)+
voice_name      := ("name"/"nm"),(WSP/LC)?,"=", (WSP/LC)?, string_double_quote
voice_subname   := ("subname"/"sname"/"snm"),(WSP/LC)?,"=", (WSP/LC)?, string_double_quote
voice_transpose := "transpose",(WSP/LC)?,"=", (WSP/LC)?, signed_integer
voice_stave     := ("stave"/"stv"),(WSP/LC)?,"=",(WSP/LC)?, integer
voice_merge     := "merge"
voice_prop_str  := voice_prop_name, (WSP/LC)?, "=", (WSP/LC)?, string_double_quote
voice_prop_int  := voice_prop_name, (WSP/LC)?, "=", (WSP/LC)?, signed_integer
voice_prop_name := [-A-Za-z0-9_:]+
stem_dir        := ("stem",(WSP/LC)?,"=")?, (WSP/LC)?, stem_dir_value
stem_dir_value  := "up" / "down" / "normal"
clef            := ((c"clef",(WSP/LC)?,"=", (WSP/LC)?, (clef_note / clef_name / clef_unknown)) / clef_name), clef_line?, clef_octave?, (WSP/LC, clef_attributes)*
clef_octave     := "+8" / "-8"
clef_note       := [GCFP]
clef_name       := c"treble" / c"alto" / c"tenor" / c"baritone" / c"bass" / c"mezzo" /
                   c"soprano" / c"perc" / c"none" # Maybe also Doh1-4, Fa1-4
clef_unknown    := letter+
clef_line       := [1-5]
clef_attributes := clef_middle / clef_transpose / clef_stafflines
clef_middle     := [mM], c"iddle"?, "=", clef_pitch
clef_transpose  := [tT], c"ranspose"?, "=", signed_integer
clef_stafflines := [sS], c"tafflines"?, "=", integer
clef_pitch      := basenote, octave?

# --------------------------------------------------------------------------------

userdef         := userdef_symbol, (WSP/LC)?, "=", (WSP/LC)?, (long_decoration / chord_or_text)
userdef_symbol  := [H-Yh-w~]

# --------------------------------------------------------------------------------
# 
# 4.20. Order of ABC constructs
# 
# The order of ABC constructs is: <grace notes>, <chord symbols>,
# <annotations>/<decorations> (e.g. Irish roll, staccato marker or
# up/downbow), <accidentals>, <note>, <octave>, ''<note length>'',
# i.e. ~^c'3 or even "Gm7"v.=G,2.
# 
# Tie symbols, '-', should come immediately after a note group but may
# be followed by a space, i.e. =G,2- . Open and close chord symbols,
# [], should enclose entire note sequences (except for chord symbols),
# i.e.
# 
# "C"[CEGc]| or |"Gm7"[.=G,^c']
# 
# and open and close slur symbols, (), should do likewise, i.e.
# 
# "Gm7"(v.=G,2~^c'2)
# 

element           := WSP / LC / inline_field / barline / broken_rhythm / 
                     stem / chord_or_text / grace_notes /
                     tuplet_start / voice_over_start /
                     voice_over_end / voice_over_rollback /
                     slur_begin / slur_end / hard_line_break /
                     multi_measure_rest / measure_repeat / nth_repeat /
                     end_nth_repeat / unused_char

hard_line_break   := "!"
barline           := decorations?, (invisible_barline / (":"*, "["*, "|"+, "]"*, (":"+ / nth_repeat_num)?) / dashed_barline) # e.g. :| | |:: |2 :||:
invisible_barline := "[|]" / "[]"
dashed_barline    := ":"

# --------------------------------------------------------------------------------

stem            := decorations?, (singlenote / stemchord / rest / spacer)
singlenote      := note
stemchord       := "[", note, note+, "]", note_length?, tie?
note            := WSP?, pitch, note_length?, tie?, WSP?
pitch           := accidental?, basenote, octave?
rest            := (normal_rest / invisible_rest ), note_length?
normal_rest     := "z"
invisible_rest  := "x"
spacer          := "y"
accidental      := "^^" / "^" / "__" / "_" / "=" 
basenote        := [CDEFGABcdefgab]
octave          := [',]+
note_length     := (integer, ("/", integer)?) / ("/", integer) / "/"+
tie             := ".-" / "-"

# --------------------------------------------------------------------------------

broken_rhythm   := stem, b_elem*, broken_type, b_elem*, stem 
broken_type     := "<"+ / ">"+
b_elem          := WSP / LC / chord_or_text / decorations / grace_notes / slur_begin / slur_end
tuplet_start    := decorations?, tuplet_num, (tuplet_into, tuplet_notes?)?
tuplet_num      := "(", integer
tuplet_into     := ":", integer?
tuplet_notes    := ":", integer?

# --------------------------------------------------------------------------------

decorations     := decoration+
decoration      := "." / userdef_symbol / long_decoration
long_decoration := "+", [A-Za-z0-9().<>]+, "+"
grace_notes     := "{", acciaccatura?, grace_note_stem+, "}"
grace_note_stem := grace_note / ("[", grace_note, grace_note+, "]") 
grace_note      := pitch, note_length?
acciaccatura    := "/"

# --------------------------------------------------------------------------------

chord_or_text    := "\"", (chord / text_expression), (chord_newline, (chord / text_expression))*, "\""
chord            := decorations?, basenote, chord_accidental?, chord_type?, ("/", basenote, chord_accidental?)?, non_quote*
chord_accidental := "#" / "b" / "=" 
chord_type       := (letter / digit / "+" / "-")+
text_expression  := ("^" / "<" / ">" / "_" / "@")?, non_quote+
chord_newline    := "\n" / "#" 

# --------------------------------------------------------------------------------

slur_begin           := "(" / ".("
slur_end             := ")" / ".)"
		     
voice_over_start     := "(&"
voice_over_rollback  := "&" 
voice_over_end       := "&)"
		     
measure_repeat       := "/", "/"?
multi_measure_rest   := decorations?, "Z", integer?
		     
nth_repeat           := "[", (nth_repeat_num / nth_repeat_text)
nth_repeat_num       := digits, (("," / "-"), digits)*
nth_repeat_text      := string_double_quote
end_nth_repeat       := "]" 
		     
unused_char          := "#" / "$" / "*" / "+" / "#" / "?" / "@" / "`"
                        # ignore for backward and forward compatibility, but maybe
                        # warn about them
		     
# ------------------ --------------------------------------------------------------
		     
instruction          := inst_abc_copyright / inst_abc_version / inst_abc_creator / 
                        inst_abc_charset / inst_abc_edited_by / inst_staff / 
                        inst_measurenb / inst_text / inst_layout / inst_margins /
                        inst_midi / inst_other
iinstruction         := iinst_abc_copyright / iinst_abc_version / iinst_abc_creator / 
                        iinst_abc_charset / iinst_abc_edited_by / iinst_staff / 
                        iinst_measurenb / iinst_text / iinst_layout / iinst_margins /
                        iinst_midi / iinst_other
		     
inst_abc_copyright   := "abc-copyright", WSP/LC, any_text_string
iinst_abc_copyright  := "abc-copyright", WSP/LC, any_text_ifield
inst_abc_version     := "abc-version", !, WSP/LC, digits, (".", digits)*
iinst_abc_version    := inst_abc_version
inst_abc_creator     := "abc-creator", WSP/LC, any_text_string
iinst_abc_creator    := "abc-creator", WSP/LC, any_text_ifield
inst_abc_charset     := "abc-charset", WSP/LC, any_text_string
iinst_abc_charset    := "abc-charset", WSP/LC, any_text_ifield
inst_abc_edited_by   := "abc-edited-by", WSP/LC, any_text_string
iinst_abc_edited_by  := "abc-edited-by", WSP/LC, any_text_ifield
inst_staff           := inst_staffbreak / inst_multicol / inst_staves / inst_indent 
iinst_staff          := inst_staff
inst_measurenb       := "measurenb" / "measurebox" / "measurefirst" / ("setbarnb", !, WSP/LC, digits) 
iinst_measurenb      := inst_measurenb
inst_text            := inst_textline / inst_textcenter / inst_textblock 
iinst_text           := iinst_textline / iinst_textcenter / iinst_textblock 
inst_textline        := "text", WSP/LC, any_text_string 
iinst_textline       := "text", WSP/LC, any_text_ifield
inst_textcenter      := "center", WSP/LC, any_text_string 
iinst_textcenter     := "center", WSP/LC, any_text_ifield
inst_textblock       := "begintext", (WSP/LC, textblock_param)?, (WSP/LC)?, EOL, !,
                        ("I:", (WSP/LC)?, ?-("endtext",(WSP/LC)?,EOL), any_text_string, EOL)*,
                        "I:", (WSP/LC)?, "endtext"
iinst_textblock      := "begintext", (WSP/LC, textblock_param)?, (WSP/LC)?, "]", !, (WSP/LC)?,
                        ("[I:", (WSP/LC)?, ?-("endtext",(WSP/LC)?,"]"), any_text_ifield, "]", (WSP/LC)?)*,
                        "[I:", (WSP/LC)?, "endtext"
inst_layout          := inst_sep / inst_vskip / inst_newpage
iinst_layout         := inst_layout
inst_margins         := ("botmargin" / "topmargin" / "leftmargin" / "rightmargin"), !, WSP/LC, float, inst_unit 
iinst_margins        := inst_margins
inst_midi            := c"midi", !, WSP/LC, (midi_channel / midi_program / midi_transpose / midi_control / midi_voice / midi_instrument / midi_mute / midi_chordprog )+
iinst_midi           := inst_midi
inst_other           := any_text_string
iinst_other          := any_text_ifield
		     
inst_staffbreak      := "staffbreak", !, WSP/LC, float, inst_unit 
inst_multicol        := "multicol", !, WSP/LC, ("start" / "new" / "end") 
inst_staves          := "staves", !, WSP/LC, stave_voice, ((WSP/LC)?, bar_staves?, stave_voice)*
stave_voice          := single_voice / bracketed_voice / braced_voice / paren_voice
bracketed_voice      := "[", (WSP/LC)?, (single_voice / braced_voice / paren_voice),
                         ((WSP/LC)?, bar_staves?, (single_voice / braced_voice / paren_voice))+, (WSP/LC)?,"]" # staves joined by bracket
braced_voice         := "{", (WSP/LC)?, (single_voice / paren_voice),
                         ((WSP/LC)?, bar_staves?, (single_voice / paren_voice))+, (WSP/LC)?, "}" # staves joined by brace
paren_voice          := "(", single_voice, ((WSP/LC)?, bar_staves?, single_voice)+, ")" # on same staff
single_voice         := "*"?, (letter / digit)+
bar_staves           := "|", (WSP/LC)?
inst_indent          := "indent", !, WSP/LC, float, inst_unit 
textblock_param      := "obeylines" / "fill" / "ragged" / "align" / "justify" / "skip"
inst_sep             := "sep", !, ((WSP/LC, float, inst_unit), (WSP/LC, float, inst_unit), (WSP/LC, float, inst_unit))?
                        # space_above, space_below, width
inst_vskip           := "vskip", !, WSP/LC, float, inst_unit 
inst_newpage         := "newpage", !, (WSP/LC, digits)? # optionally restart page numbering at n
midi_channel         := "channel", !, ((WSP/LC)?, "=", (WSP/LC)?) / WSP/LC, midi_channel_number, (WSP/LC)?
midi_program         := "program", !, ((WSP/LC)?, "=", (WSP/LC)?) / WSP/LC, (midi_channel_number, ((WSP/LC)?, ",", (WSP/LC)?) / WSP/LC, midi_program_number) / midi_program_number, (WSP/LC)?
midi_control         := "control", !, ((WSP/LC)?, "=", (WSP/LC)?) / WSP/LC, (integer, ((WSP/LC)?, [,=], (WSP/LC)?) / WSP/LC, integer) / midi_control_int / midi_control_bool, (WSP/LC)?
midi_channel_number  := ("1", [0-6]) / [1-9]
midi_program_number  := (("1", [01], [0-9]) / ("12", [0-8]) / ([1-9], [0-9]) / [1-9]) / midi_program_name
midi_program_name    := "Acoustic Grand" / "Bright Acoustic" / "Electric Grand" / "Honky-Tonk" / "Electric Piano 1" / "Electric Piano 2" /
                        "Harpsichord" / "Clavinet" / "Celesta" / "Glockenspiel" / "Music Box" / "Vibraphone" / "Marimba" / "Xylophone" /
                        "Tubular Bells" / "Dulcimer" / "Drawbar Organ" / "Percussive Organ" / "Rock Organ" / "Church Organ" / "Reed Organ" /
                        "Accoridan" / "Harmonica" / "Tango Accordian" / "Nylon String Guitar" / "Steel String Guitar" /
                        "Electric Jazz Guitar" / "Electric Clean Guitar" / "Electric Muted Guitar" / "Overdriven Guitar" /
                        "Distortion Guitar" / "Guitar Harmonics" / "Acoustic Bass" / "Electric Bass(finger)" / "Electric Bass(pick)" /
                        "Fretless Bass" / "Slap Bass 1" / "Slap Bass 2" / "Synth Bass 1" / "Synth Bass 2" / "Violin" / "Viola" /
                        "Cello" / "Contrabass" / "Tremolo Strings" / "Pizzicato Strings" / "Orchestral Strings" / "Timpani" /
                        "String Ensemble 1" / "String Ensemble 2" / "SynthStrings 1" / "SynthStrings 2" / "Choir Aahs" / "Voice Oohs" /
                        "Synth Voice" / "Orchestra Hit" / "Trumpet" / "Trombone" / "Tuba" / "Muted Trumpet" / "French Horn" /
                        "Brass Section" / "SynthBrass 1" / "SynthBrass 2" / "Soprano Sax" / "Alto Sax" / "Tenor Sax" / "Baritone Sax" /
                        "Oboe" / "English Horn" / "Bassoon" / "Clarinet" / "Piccolo" / "Flute" / "Recorder" / "Pan Flute" /
                        "Blown Bottle" / "Skakuhachi" / "Whistle" / "Ocarina" / "Lead 1 (square)" / "Lead 2 (sawtooth)" /
                        "Lead 3 (calliope)" / "Lead 4 (chiff)" / "Lead 5 (charang)" / "Lead 6 (voice)" / "Lead 7 (fifths)" /
                        "Lead 8 (bass+lead)" / "Pad 1 (new age)" / "Pad 2 (warm)" / "Pad 3 (polysynth)" / "Pad 4 (choir)" /
                        "Pad 5 (bowed)" / "Pad 6 (metallic)" / "Pad 7 (halo)" / "Pad 8 (sweep)" / "FX 1 (rain)" / "FX 2 (soundtrack)" /
                        "FX 3 (crystal)" / "FX 4 (atmosphere)" / "FX 5 (brightness)" / "FX 6 (goblins)" / "FX 7 (echoes)" /
                        "FX 8 (sci-fi)" / "Sitar" / "Banjo" / "Shamisen" / "Koto" / "Kalimba" / "Bagpipe" / "Fiddle" / "Shanai" /
                        "Tinkle Bell" / "Agogo" / "Steel Drums" / "Woodblock" / "Taiko Drum" / "Melodic Tom" / "Synth Drum" /
                        "Reverse Cymbal" / "Guitar Fret Noise" / "Breath Noise" / "Seashore" / "Bird Tweet" / "Telephone Ring" /
                        "Helicopter" / "Applause" / "Gunshot"
midi_percussion_name := "Acoustic Bass Drum" / "Bass Drum 1" / "Side Stick" / "Acoustic Snare" / "Hand Clap" /
                        "Electric Snare" / "Low Floor Tom" / "Closed Hi-Hat" / "High Floor Tom" / "Pedal Hi-Hat" /
                        "Low Tom" / "Open Hi-Hat" / "Low-Mid Tom" / "Hi-Mid Tom" / "Crash Cymbal 1" / "High Tom" /
                        "Ride Cymbal 1" / "Chinese Cymbal" / "Ride Bell" / "Tambourine" / "Splash Cymbal" /
                        "Cowbell" / "Crash Cymbal 2" / "Vibraslap" / "Ride Cymbal 2" / "Hi Bongo" / "Low Bongo" /
                        "Mute Hi Conga" / "Open Hi Conga" / "Low Conga" / "High Timbale" / "Low Timbale" /
                        "High Agogo" / "Low Agogo" / "Cabasa" / "Maracas" / "Short Whistle" / "Long Whistle" /
                        "Short Guiro" / "Long Guiro" / "Claves" / "Hi Wood Block" / "Low Wood Block" /
                        "Mute Cuica" / "Open Cuica" / "Mute Triangle" / "Open Triangle"      
midi_transpose       := "transpose", !, ((WSP/LC)?, "=", (WSP/LC)?) / WSP/LC, signed_integer, (WSP/LC)?
midi_voice           := "voice", !, ((WSP/LC)?, "=", (WSP/LC)?) / WSP/LC, midi_voiceref, (WSP/LC)?
midi_voiceref        := (letter / digit)+
midi_instrument      := "instrument", !, ((WSP/LC)?, "=", (WSP/LC)?) / WSP/LC, midi_program_number, (WSP/LC)?, (midi_bank)?, (WSP/LC)?
midi_bank            := "bank", !, ((WSP/LC)?, "=", (WSP/LC)?) / WSP/LC, integer, (WSP/LC)?
midi_mute            := "mute", (WSP/LC)?
midi_chordprog       := "chordprog", !, ((WSP/LC)?, "=", (WSP/LC)?) / WSP/LC, midi_program_number, (WSP/LC)?
midi_control_int     := midi_control_volume / midi_control_balance / midi_control_pan
midi_control_volume  := "volume", ((WSP/LC)?, [,=], (WSP/LC)?) / WSP/LC, (integer / midi_volume_label)
midi_volume_label    := "fff", "ff", "f", "mf", "m", "mp", "p", "pp", "ppp"
midi_control_balance := "balance", ((WSP/LC)?, [,=], (WSP/LC)?) / WSP/LC, (integer / midi_pan_label)
midi_control_pan     := "pan", ((WSP/LC)?, [,=], (WSP/LC)?) / WSP/LC, (integer / midi_pan_label)
midi_pan_label       := "far left" / "left" / "mid-left" / "center" / "mid-right" / "right" / "far right"
midi_control_bool    := midi_bool_name, ((WSP/LC)?, [,=], (WSP/LC)?) / WSP/LC, bool
midi_bool_name       := "sustain" / "portamento" / "sostenuto" / "soft pedal" / "legato" / "omni" / "poly" / "mono"
inst_unit            := ("cm" / "pt" / "in")?
integer              := digits
bool                 := [01] / "on" / "off" / "true" / "false"
signed_integer       := [+-]?, digits
float                := (digits, (".", digits)?) / (".", digits)

# --------------------------------------------------------------------------------

lyrics                := (lyrics_char / lyrics_syllable_break / lyrics_next_bar /
                         lyrics_hold / lyrics_skip_note / lyrics_nbsp / lyrics_dash /
                         tex_escape)*
lyrics_ifield         := (lyrics_char_ifield / lyrics_syllable_break /
                         lyrics_next_bar / lyrics_hold / lyrics_skip_note /
                         lyrics_nbsp / lyrics_dash / tex_escape)*
lyrics_syllable_break := "-" # break between syllables in a word
lyrics_next_bar       := "|" # advance to next bar
lyrics_hold           := "_" # hold syllable for one more note
lyrics_skip_note      := "*" # =blank syllable
lyrics_nbsp           := "~" # non_breaking space = words on same note
lyrics_dash           := "\\-" # printed as a -
lyrics_char_ifield    := WSP_internal / digit / letter / special / [!"#$%&'()+,./:;<=>?@[^`{|}]
                         # all characters without special meaning and except ']'
lyrics_char           := lyrics_char_ifield / "]"
                         # all characters without special meaning

# --------------------------------------------------------------------------------

special         := "\\", ([`^'\"=;.vuanso~:/], [a-zA-Z]) / "\\\\" / "\\["
non_quote       := (WSP_internal / [\x21\x23-\x7E])*      # all non_eol characters except double quote
<any_text>      := (WSP_internal / special / [\x21-\x7E]) # any printable character plus all non_eol whitespace
<non_bracket>   := (WSP_internal / special / [\x21-\x5C\x5E-\x7E])  # any character except closing bracket
any_text_string := any_text*                         # string of any_text
any_text_ifield := (non_bracket)* # as above, but except ]
tex_escape      := "\\", (digit / letter / [_])+     # tex line - for backward compatability

# --------------------------------------------------------------------------------

comment := "%", any_text_string
LC      := WSP_internal*, "\\", WSP_internal*, EOL, WSP_internal*
EOL     := comment?, CRLF / LF / CR

# --------------------------------------------------------------------------------

<CRLF>          := "\r\n"
<LF>            := "\n"
<CR>            := "\r"
<WSP>           := WSP_internal+
<WSP_internal>  := [ \t\b\f\v]

# --------------------------------------------------------------------------------