jianpu-1.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // abc2svg - ABC to SVG translator
  2. // @source: https://chiselapp.com/user/moinejf/repository/abc2svg
  3. // Copyright (C) 2014-2023 Jean-Francois Moine - LGPL3+
  4. //jianpu.js-module to output jiănpŭ(简谱)music sheets
  5. if(typeof abc2svg=="undefined")
  6. var abc2svg={}
  7. abc2svg.jianpu={k_tb:["Cb","Gb","Db","Ab","Eb","Bb","F","C","G","D","A","E","B","F#","C#"],cde2fcg:new Int8Array([0,2,4,-1,1,3,5]),cgd2cde:new Int8Array([0,-4,-1,-5,-2,-6,-3,0,-4,-1,-5,-2,-6,-3,0]),acc2:new Int8Array([-2,-1,3,1,2]),acc_tb:["\ue264","\ue260",,"\ue262","\ue263","\ue261"],calc_beam:function(of,bm,s1){if(!s1.p_v.jianpu)
  8. return of(bm,s1)},output_music:function(of){var p_v,v,C=abc2svg.C,abc=this,cur_sy=abc.get_cur_sy(),voice_tb=abc.get_voice_tb()
  9. function ov_def(v){var s1,tim,s=p_v.sym
  10. while(s){s1=s.ts_prev
  11. if(!s.invis&&s.dur&&s1.v!=v&&s1.st==s.st&&s1.time==s.time){while(1){if(!s1.prev||s1.prev.bar_type)
  12. break
  13. s1=s1.prev}
  14. while(!s1.bar_type){s1.dy=14
  15. s1.notes[0].pit=30
  16. if(s1.type==C.REST)
  17. s1.combine=-1
  18. s1=s1.next}
  19. while(1){s.dy=-14
  20. s.notes[0].pit=20
  21. if(!s.next||s.next.bar_type||s.next.time>=s1.time)
  22. break
  23. s=s.next}}
  24. s=s.next}}
  25. function set_head(){var v,p_v,mt,s2,sk,s,tsfirst=abc.get_tsfirst()
  26. for(v=0;v<voice_tb.length;v++){p_v=voice_tb[v]
  27. if(p_v.jianpu)
  28. break}
  29. if(v>=voice_tb.length)
  30. return
  31. mt=p_v.meter.a_meter[0]
  32. sk=p_v.key
  33. s2=p_v.sym
  34. s={type:C.BLOCK,subtype:"text",time:s2.time,dur:0,v:0,p_v:p_v,st:0,fmt:s2.fmt,seqst:true,text:(sk.k_mode+1)+"="+
  35. (abc2svg.jianpu.k_tb[sk.k_sf+7+
  36. abc2svg.jianpu.cde2fcg[sk.k_mode]]),font:abc.get_font("text")}
  37. if(mt)
  38. s.text+=' '+(mt.bot?(mt.top+'/'+mt.bot):mt.top)
  39. s2=tsfirst
  40. s.next=s2.next
  41. if(s.next)
  42. s.next.prev=s
  43. s.prev=s2
  44. s2.next=s
  45. s.ts_next=s2.ts_next
  46. s.ts_next.ts_prev=s
  47. s.ts_prev=s2
  48. s2.ts_next=s}
  49. function slice(s){var n,s2,s3,jn=s.type==C.REST?0:8
  50. if(s.dur>=C.BLEN)
  51. n=3
  52. else if(s.dur==C.BLEN/2)
  53. n=1
  54. else
  55. n=2
  56. s.notes[0].dur=s.dur=s.dur_orig=C.BLEN/4
  57. delete s.fmr
  58. while(--n>=0){s2={type:C.REST,v:s.v,p_v:s.p_v,st:s.st,dur:C.BLEN/4,dur_orig:C.BLEN/4,fmt:s.fmt,stem:0,multi:0,nhd:0,notes:[{dur:s.dur,pit:s.notes[0].pit,jn:jn}],xmx:0,noplay:true,time:s.time+C.BLEN/4,prev:s,next:s.next}
  59. s.next=s2
  60. if(s2.next)
  61. s2.next.prev=s2
  62. if(!s.ts_next){s.ts_next=s2
  63. if(s.soln)
  64. s.soln=false
  65. s2.ts_prev=s
  66. s2.seqst=true}else{for(s3=s.ts_next;s3;s3=s3.ts_next){if(s3.time<s2.time)
  67. continue
  68. if(s3.time>s2.time){s2.seqst=true
  69. s3=s3.ts_prev}
  70. s2.ts_next=s3.ts_next
  71. s2.ts_prev=s3
  72. if(s2.ts_next)
  73. s2.ts_next.ts_prev=s2
  74. s3.ts_next=s2
  75. break}}
  76. s=s2}}
  77. function set_note(s,sf){var i,m,note,p,pit,a,nn,delta=abc2svg.jianpu.cgd2cde[sf+7]-2
  78. s.stem=-1
  79. s.stemless=true
  80. if(s.sls){for(i=0;i<s.sls.length;i++)
  81. s.sls[i].ty=C.SL_ABOVE}
  82. for(m=0;m<=s.nhd;m++){note=s.notes[m]
  83. p=note.pit
  84. pit=p+delta
  85. note.jn=((pit+77)%7)+1
  86. note.pit=25
  87. note.jo=(pit/7)|0
  88. a=note.acc
  89. if(a){nn=abc2svg.jianpu.cde2fcg[(p+5+16*7)%7]-sf
  90. if(a!=3)
  91. nn+=a*7
  92. nn=((((nn+1+21)/7)|0)+2-3+32*5)%5
  93. note.acc=abc2svg.jianpu.acc2[nn]}
  94. if(note.sls){for(i=0;i<note.sls.length;i++)
  95. note.sls[i].ty=C.SL_ABOVE}
  96. if(note.tie_ty)
  97. note.tie_ty=C.SL_ABOVE}
  98. if(s.dur>=C.BLEN/2&&!s.invis)
  99. slice(s)
  100. if(s.a_dd){for(i=0;i<s.a_dd.length;i++){if(s.a_dd[i].glyph=="stc"){abc.deco_put("gstc",s)
  101. s.a_dd[i]=s.a_dd.pop()}}}}
  102. function set_sym(p_v){var s,g,sf=p_v.key.k_sf
  103. delete p_v.key.k_a_acc
  104. s=p_v.clef
  105. s.invis=true
  106. s.clef_type='t'
  107. s.clef_line=2
  108. for(s=p_v.sym;s;s=s.next){s.st=p_v.st
  109. switch(s.type){case C.CLEF:s.invis=true
  110. s.clef_type='t'
  111. s.clef_line=2
  112. default:continue
  113. case C.KEY:sf=s.k_sf
  114. s.a_gch=[{type:'@',font:abc.get_font("annotation"),wh:[10,10],x:-5,y:26,text:(s.k_mode+1)+"="+
  115. (abc2svg.jianpu.k_tb[sf+7+
  116. abc2svg.jianpu.cde2fcg[s.k_mode]])}]
  117. continue
  118. case C.REST:if(s.notes[0].jn)
  119. continue
  120. s.notes[0].jn=0
  121. if(s.dur>=C.BLEN/2&&!s.invis)
  122. slice(s)
  123. continue
  124. case C.NOTE:set_note(s,sf)
  125. break
  126. case C.GRACE:for(g=s.extra;g;g=g.next)
  127. set_note(g,sf)
  128. break}}}
  129. set_head()
  130. for(v=0;v<voice_tb.length;v++){p_v=voice_tb[v]
  131. if(p_v.jianpu){set_sym(p_v)
  132. if(p_v.second)
  133. ov_def(v)}}
  134. of()},draw_symbols:function(of,p_voice){var s,s2,nl,y,C=abc2svg.C,abc=this,dot="\ue1e7",staff_tb=abc.get_staff_tb(),out_svg=abc.out_svg,out_sxsy=abc.out_sxsy,xypath=abc.xypath
  135. if(!p_voice.jianpu){of(p_voice)
  136. return}
  137. function draw_dur(s1,x,y,s2,n,nl){var s,s3,sc=s1.grace?.5:1
  138. xypath(x-3,y+5)
  139. out_svg('h'+((s2.x-s1.x)/sc+8).toFixed(1)+'"/>\n')
  140. y-=2.5
  141. while(++n<=nl){s=s1
  142. while(1){if(s.nflags&&s.nflags>=n){s3=s
  143. while(s!=s2){if(s.next.beam_br1||(s.next.beam_br2&&n>2)||(s.next.nflags&&s.next.nflags<n))
  144. break
  145. s=s.next}
  146. draw_dur(s3,s3.x,y,s,n,nl)}
  147. if(s==s2)
  148. break
  149. s=s.next}}}
  150. function out_mus(x,y,p){out_svg('<text x="')
  151. out_sxsy(x,'" y="',y)
  152. out_svg('">'+p+'</text>\n')}
  153. function out_txt(x,y,p){out_svg('<text class="fj" x="')
  154. out_sxsy(x,'" y="',y)
  155. out_svg('">'+p+'</text>\n')}
  156. function draw_hd(s,x,y){var m,note,ym
  157. for(m=0;m<=s.nhd;m++){note=s.notes[m]
  158. out_txt(x-3.5,y+8,"01234567-"[note.jn])
  159. if(note.acc)
  160. out_mus(x-12,y+12,abc2svg.jianpu.acc_tb[note.acc+2])
  161. if(note.jo>2){out_mus(x-1,y+22,dot)
  162. if(note.jo>3){y+=3
  163. out_mus(x-1,y+22,dot)}}else if(note.jo<2){ym=y+4
  164. if(m==0&&s.nflags>0)
  165. ym-=2.5*s.nflags
  166. out_mus(x-1,ym,dot)
  167. if(note.jo<1){ym-=3
  168. out_mus(x-1,ym,dot)}}
  169. y+=20}}
  170. function draw_note(s){var sc=1,x=s.x,y=staff_tb[s.st].y
  171. if(s.dy)
  172. y+=s.dy
  173. if(s.grace){out_svg('<g transform="translate(')
  174. out_sxsy(x,',',y+15)
  175. out_svg(') scale(.5)">\n')
  176. abc.stv_g().g++
  177. x=0
  178. y=0
  179. sc=.5}
  180. draw_hd(s,x,y)
  181. if(s.nflags>=0&&s.dots)
  182. out_mus(x+8*sc,y+13*sc,dot)
  183. if(s.grace){out_svg('</g>\n')
  184. abc.stv_g().g--}}
  185. for(s=p_voice.sym;s;s=s.next){if(s.invis)
  186. continue
  187. switch(s.type){case C.METER:abc.draw_meter(s)
  188. break
  189. case C.NOTE:case C.REST:draw_note(s)
  190. break
  191. case C.GRACE:for(g=s.extra;g;g=g.next)
  192. draw_note(g)
  193. break}}
  194. for(s=p_voice.sym;s;s=s.next){if(s.invis)
  195. continue
  196. switch(s.type){case C.NOTE:case C.REST:nl=s.nflags
  197. if(nl<=0)
  198. continue
  199. y=staff_tb[s.st].y
  200. s2=s
  201. while(s.next&&s.next.nflags>0){s=s.next
  202. if(s.nflags>nl)
  203. nl=s.nflags
  204. if(s.beam_end)
  205. break}
  206. if(s.dy)
  207. y+=s.dy
  208. draw_dur(s2,s2.x,y,s,1,nl)
  209. break}}},set_fmt:function(of,cmd,param){if(cmd=="jianpu"){this.set_v_param("jianpu",param)
  210. return}
  211. of(cmd,param)},set_pitch:function(of,last_s){of(last_s)
  212. if(!last_s)
  213. return
  214. var C=abc2svg.C
  215. for(var s=this.get_tsfirst();s;s=s.ts_next){if(!s.p_v.jianpu)
  216. continue
  217. switch(s.type){case C.KEY:if(s.prev.type==C.CLEF||s.v!=0)
  218. s.a_gch=null
  219. break
  220. case C.NOTE:s.ymx=20*s.nhd+22
  221. if(s.notes[s.nhd].jo>2){s.ymx+=3
  222. if(s.notes[s.nhd].jo>3)
  223. s.ymx+=2}
  224. s.ymn=0
  225. break}}},set_vp:function(of,a){var i,p_v=this.get_curvoice()
  226. for(i=0;i<a.length;i++){if(a[i]=="jianpu="){p_v.jianpu=this.get_bool(a[++i])
  227. if(p_v.jianpu)
  228. this.set_vp(["staffsep=","20","sysstaffsep=","14","stafflines=","...","tuplets=","0 1 0 1"])
  229. break}}
  230. of(a)},set_width:function(of,s){of(s)
  231. if(!s.p_v||!s.p_v.jianpu)
  232. return
  233. var w,m,note,C=abc2svg.C
  234. switch(s.type){case C.CLEF:case C.KEY:s.wl=s.wr=.1
  235. break
  236. case C.NOTE:for(m=0;m<=s.nhd;m++){note=s.notes[m]
  237. if(note.acc&&s.wl<14)
  238. s.wl=14}
  239. break}},set_hooks:function(abc){abc.calculate_beam=abc2svg.jianpu.calc_beam.bind(abc,abc.calculate_beam)
  240. abc.draw_symbols=abc2svg.jianpu.draw_symbols.bind(abc,abc.draw_symbols)
  241. abc.output_music=abc2svg.jianpu.output_music.bind(abc,abc.output_music)
  242. abc.set_format=abc2svg.jianpu.set_fmt.bind(abc,abc.set_format)
  243. abc.set_pitch=abc2svg.jianpu.set_pitch.bind(abc,abc.set_pitch)
  244. abc.set_vp=abc2svg.jianpu.set_vp.bind(abc,abc.set_vp)
  245. abc.set_width=abc2svg.jianpu.set_width.bind(abc,abc.set_width)
  246. abc.get_glyphs().gstc='<circle id="gstc" cx="0" cy="-3" r="2"/>'
  247. abc.get_decos().gstc="0 gstc 5 1 1"
  248. abc.add_style("\n.fj{font:15px sans-serif}")}}
  249. if(!abc2svg.mhooks)
  250. abc2svg.mhooks={}
  251. abc2svg.mhooks.jianpu=abc2svg.jianpu.set_hooks