{
   "labels" : [
      "parser abc score notation"
   ],
   "code" : "(\r\n~notedef = Dictionary.newFrom((\"CDEFGABcdefgab\".as(Array).collect { arg ch, idx; [ch, idx-7] }).flat);\r\n~notedef[$_] = \\r;\r\n~notedef[$x] = \\r;\r\n~notedef[$z] = \\r;\r\n~digitdef = Dictionary.newFrom((\"123456789\".as(Array).collect { arg ch, idx; [ch, idx+1] }).flat);\r\n\r\n~is_terminal = { arg char;\r\n\t~notedef[char].notNil or: { [ $[, $] ].includes(char) }\r\n};\r\n\r\n~strip_spaces = { arg str;\r\n\tstr.replace(\" \", \"\")\r\n};\r\n\r\n~split_string = { arg str;\r\n\tvar res = List.new;\r\n\tvar accu = \"\";\r\n\tstr.do { arg char;\r\n\t\tif( ~is_terminal.(char) ) {\r\n\t\t\tif(accu != \"\") {\r\n\t\t\t\tres.add(accu);\r\n\t\t\t};\r\n\t\t\taccu = \"\" ++ char;\r\n\t\t} {\r\n\t\t\taccu = accu ++ char;\r\n\t\t}\r\n\t};\r\n\tres.add(accu);\r\n\tres;\r\n};\r\n\r\n~extract_chords = { arg token_list;\r\n\tvar res = List.new;\r\n\tvar accu = List.new;\r\n\tvar chord = false;\r\n\ttoken_list.do { arg token;\r\n\t\tif( chord ) {\r\n\t\t\tif( token == \"]\") {\r\n\t\t\t\tchord = false;\r\n\t\t\t\tres.add(accu);\r\n\t\t\t\taccu = List.new;\r\n\t\t\t} {\r\n\t\t\t\taccu.add(token)\r\n\t\t\t};\r\n\t\t} {\r\n\t\t\tif( token == \"[\") {\r\n\t\t\t\tchord = true;\r\n\t\t\t} {\r\n\t\t\t\tres.add(token)\r\n\t\t\t};\r\n\t\t};\r\n\t};\r\n\t//res.add(token);\r\n\tres;\r\n};\r\n\r\n~parse_note = { arg token;\r\n\tvar pitch;\r\n\tvar len = 1;\r\n\tvar char;\r\n\tvar divide = false;\r\n\ttoken = token.as(List).reverse;\r\n\tpitch = token.pop;\r\n\tpitch = ~notedef[pitch];\r\n\twhile({ token.isEmpty.not }) {\r\n\t\tchar = token.pop;\t\r\n\t\tcase\r\n\t\t\t{ char == $' } {\r\n\t\t\t\tpitch = pitch + 14;\r\n\t\t\t}\r\n\t\t\t{ char == $, } {\r\n\t\t\t\tpitch = pitch - 14;\r\n\t\t\t}\r\n\t\t\t{ char == $/ } {\r\n\t\t\t\tlen = 1/2;\r\n\t\t\t\tdivide = true;\r\n\t\t\t}\r\n\t\t\t{ ~digitdef[char].notNil } {\r\n\t\t\t\tif(divide) {\r\n\t\t\t\t\tlen = 1 / ~digitdef[char];\r\n\t\t\t\t} {\r\n\t\t\t\t\tlen = ~digitdef[char];\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\r\n\t};\r\n\t[pitch, len]\r\n};\r\n\r\n~parse_sentence = { arg token_list;\r\n\tvar res_pitch = List.new, res_len = List.new;\r\n\tvar pitch, len;\r\n\ttoken_list.do { arg token;\r\n\t\tif(token.isString) {\r\n\t\t\t#pitch, len = ~parse_note.(token);\r\n\t\t\tres_pitch.add(pitch);\r\n\t\t\tres_len.add(len);\r\n\t\t} {\r\n\t\t\t#pitch, len = ~parse_sentence.(token);\r\n\t\t\t//len = len.maxItem; // workaround to the multichannel expand bug\r\n\t\t\tres_pitch.add(pitch);\r\n\t\t\tres_len.add(len);\r\n\t\t}\r\n\t};\r\n\t[res_pitch, res_len]\r\n};\r\n\t\t\r\n~parseabc = { arg str;\r\n\tvar res;\r\n\tres = ~strip_spaces.(str);\r\n\tres = ~split_string.(res);\r\n\tres = ~extract_chords.(res);\r\n\tres = ~parse_sentence.(res);\r\n\tres;\r\n};\r\n\r\n~add_legato = { arg dur;\r\n\tvar legato = List.new;\r\n\tvar maxi;\r\n\tdur = dur.collect { arg du;\r\n\t\tif(du.isSequenceableCollection) {\r\n\t\t\tmaxi = du.maxItem;\r\n\t\t\tlegato.add( du.collect { arg leg;\r\n\t\t\t\tleg / maxi\r\n\t\t\t});\r\n\t\t\tmaxi;\r\n\t\t} {\r\n\t\t\tlegato.add(1);\r\n\t\t\tdu;\r\n\t\t}\r\n\t};\r\n\t[dur, legato]\r\n};\r\n\r\n~abc_to_pbind = { arg str, repeat=inf;\r\n\tvar res, dur, legato;\r\n\tres = ~parseabc.(str);\r\n\t#dur, legato = ~add_legato.(res[1]);\r\n\tPbind(\r\n\t\t\\degree, Pseq(res[0],repeat),\r\n\t\t\\legato, Pkey(\\legato) * Pseq(legato,repeat),\r\n\t\t\\dur, Pseq(dur,repeat)\r\n\t)\r\n};\r\n)\r\n\r\n// usage example\r\n\r\n(Pbind(\\amp, 1, \\stretch, 1/8) <> ~abc_to_pbind.(\"[c4e4g4][d4f4h4]cdef_2c_[c4e4g4][d4fh]cdec\",1)).play;",
   "id" : "1-4Qv",
   "is_private" : null,
   "author" : "grirgz",
   "name" : "parser for (simplified) abc score notation",
   "ancestor_list" : [],
   "description" : "A simple parser for the (simplified) abc score notation. http://abcnotation.com/wiki/abc:standard:v2.0#the_tune_body\r\n\r\nFeatures:\r\n- 4 octaves : C, C c c'\r\n- note dur: c2 c/ c/2\r\n- rest: can use _ in addition to z and x\r\n- chords: [ceg]\r\n- spaces and others symbols are ignored"
}
