1?g+1:null,I=c(e,L,t.pitches[g],{dir:_,extrax:-b,flag:P,dot:r,dotshiftx:v,scale:this.voiceScale,accidentalSlot:w,shouldExtendStem:!n,printAccidentals:!l.isPercussion,chordPos:D});T=Math.max(d.getSymbolWidth(L),T),e.extraw-=I.extraLeft,(h=I.notehead)&&(this.addSlursAndTies(e,t.pitches[g],h,l,H?_:null,!1),t.gracenotes&&t.gracenotes.length>0&&(h.bottom=h.bottom-1),e.addHead(h)),b+=I.accidentalshiftx,y=Math.max(y,I.dotshiftx)}if(H){var z=Math.round(70*this.voiceScale)/10,F="down"===_?t.minpitch-z:t.minpitch+1/3;F>6&&!n&&(F=6);var j="down"===_?t.maxpitch-1/3:t.maxpitch+z;j<6&&!n&&(j=6);var V="down"===_||0===e.heads.length?0:e.heads[0].w,G="down"===_?1:-1;h&&"noteheads.slash.quarter"===h.c&&("down"===_?j-=1:F+=1),h&&"noteheads.triangle.quarter"===h.c&&("down"===_?j-=.7:F-=1.2),e.addRight(new f(null,V,0,F,{type:"stem",pitch2:j,linewidth:G,bottom:F-1})),u=Math.min(F,j)}return{noteHead:h,roomTaken:b,roomTakenRight:y,min:u,additionalLedgers:x,dir:_,symbolWidth:T}},_.prototype.addLyric=function(e,t,r){var n="";t.lyric.forEach((function(e){var t=" "===e.divider?"":e.divider;n+=e.syllable+t+"\n"}));var a=this.getTextSize.calc(n,"vocalfont","lyric"),i=t.positioning?t.positioning.vocalPosition:"below";e.addCentered(new f(n,0,a.width,void 0,{type:"lyric",position:i,height:a.height/p.STEP,dim:this.getTextSize.attr("vocalfont","lyric"),voiceNumber:r}))},_.prototype.createNote=function(e,t,r,a){var i,s=null,o=0,l=0,h=0,u=[],p=C(e),m=!1;0===p&&(m=!0,p=.25,t=!0);for(var g=Math.floor(Math.log(p)/Math.log(2)),v=0,y=Math.pow(2,g),k=y/2;y1,this.stemdir,r,g,this.voiceScale);s=M.noteHead,o=M.roomTaken,l=M.roomTakenRight}else{var A=this.addNoteToAbcElement(E,e,v,this.stemdir,this.style,m,g,t,a);void 0!==A.min&&(this.minY=Math.min(A.min,this.minY)),s=A.noteHead,o=A.roomTaken,l=A.roomTakenRight,u=A.additionalLedgers,i=A.dir,h=A.symbolWidth}if(void 0!==e.lyric&&this.addLyric(E,e,a.voicenumber),void 0!==e.gracenotes&&(o+=this.addGraceNotes(e,a,E,s,this.stemHeight*this.voiceScale,this.isBagpipes,o)),e.decoration){var B=t&&"up"!==i?Math.min(-3,E.bottom-6):E.bottom;this.decoration.createDecoration(a,e.decoration,E.top,s?s.w:0,E,o,i,B,e.positioning,this.hasVocals,this.accentAbove)}if(e.barNumber&&E.addFixed(new f(e.barNumber,-10,0,0,{type:"barNumber"})),N(E,e.minpitch,e.maxpitch,e.rest,h,u,i,-2,1),void 0!==e.chord){var P=x(this.getTextSize,E,e,o,l,h,this.jazzchords,this.germanAlphabet);o=P.roomTaken,l=P.roomTakenRight}return e.startTriplet&&(this.triplet=new b(e.startTriplet,s,{flatBeams:this.flatBeams})),e.endTriplet&&this.triplet&&this.triplet.setCloseAnchor(s),!this.triplet||e.startTriplet||e.endTriplet||e.rest&&"spacer"===e.rest.type||this.triplet.middleNote(s),E},_.prototype.addSlursAndTies=function(e,t,r,n,a,i){if(t.endTie&&this.ties.length>0){for(var s=!1,o=0;o10&&"treble"===t.abcelem.type?13.5:11;t.addFixed(new f(e,n,r.width,a+r.height/p.STEP,{type:"barNumber",dim:this.getTextSize.attr("measurefont","bar-number")}))},_.prototype.createBarLine=function(e,t,r){var a=new n(t,0,10,"bar",this.tuneNumber),i=null,s=0;t.barNumber&&this.addMeasureNumber(t.barNumber,a);var o="bar_right_repeat"===t.type||"bar_dbl_repeat"===t.type,c="bar_left_repeat"!==t.type&&"bar_thick_thin"!==t.type&&"bar_invisible"!==t.type,l="bar_right_repeat"===t.type||"bar_dbl_repeat"===t.type||"bar_left_repeat"===t.type||"bar_thin_thick"===t.type||"bar_thick_thin"===t.type,h="bar_left_repeat"===t.type||"bar_thick_thin"===t.type||"bar_thin_thin"===t.type||"bar_dbl_repeat"===t.type,d="bar_left_repeat"===t.type||"bar_dbl_repeat"===t.type;if(o||d){for(var p in this.slurs)this.slurs.hasOwnProperty(p)&&this.slurs[p].setEndX(a);this.startlimitelem=a}if(o&&(a.addRight(new f("dots.dot",s,1,7)),a.addRight(new f("dots.dot",s,1,5)),s+=6),c&&(i=new f(null,s,1,2,{type:"bar",pitch2:10,linewidth:.6}),a.addRight(i)),"bar_invisible"===t.type&&(i=new f(null,s,1,2,{type:"none",pitch2:10,linewidth:.6}),a.addRight(i)),t.decoration&&this.decoration.createDecoration(e,t.decoration,12,l?3:1,a,0,"down",2,t.positioning,this.hasVocals,this.accentAbove),l&&(i=new f(null,s+=4,4,2,{type:"bar",pitch2:10,linewidth:4}),a.addRight(i),s+=5),this.partstartelem&&t.endEnding&&(this.partstartelem.anchor2=i,this.partstartelem=null),h&&(i=new f(null,s+=3,1,2,{type:"bar",pitch2:10,linewidth:.6}),a.addRight(i)),d&&(s+=3,a.addRight(new f("dots.dot",s,1,7)),a.addRight(new f("dots.dot",s,1,5))),t.startEnding&&r&&0===e.voicenumber){var m=this.getTextSize.calc(t.startEnding,"repeatfont","").width;a.minspacing+=m+10,this.partstartelem=new u(t.startEnding,i,null),e.addOther(this.partstartelem)}return a.extraw-=5,void 0!==t.chord&&x(this.getTextSize,a,t,0,0,0,!1,this.germanAlphabet),a},e.exports=_},2652:function(e,t,r){var n=r(6658),a=r(4331),i=r(5810);function s(e,t,r,s,o,c,l,h,u,d,f,p,m,g,v){for(var b=e.split("\n"),y=b.length-1;y>=0;y--){var x,k=b[y],w=0;s||(k=i(k,g,v));var C=h.calc(k,o,c),T=C.width,S=C.height/a.STEP;switch(t){case"left":w=-(f+=T+7),x=d.averagepitch,u.addExtra(new n(k,w,T+4,x,{type:"text",height:S,dim:l,position:"left"}));break;case"right":w=p+=4,x=d.averagepitch,u.addRight(new n(k,w,T+4,x,{type:"text",height:S,dim:l,position:"right"}));break;case"below":u.addRight(new n(k,0,0,void 0,{type:"text",position:"below",height:S,dim:l,realWidth:T}));break;case"above":u.addRight(new n(k,0,0,void 0,{type:"text",position:"above",height:S,dim:l,realWidth:T}));break;default:if(r){var _=r.y+3*a.STEP;u.addRight(new n(k,w+r.x,0,d.minpitch+_/a.STEP,{position:"relative",type:"text",height:S,dim:l}))}else{var E="above";d.positioning&&d.positioning.chordPosition&&(E=d.positioning.chordPosition),"hidden"!==E&&u.addCentered(new n(k,m/2,T,void 0,{type:"chord",position:E,height:S,dim:l,realWidth:T}))}}}return{roomTaken:f,roomTakenRight:p}}e.exports=function(e,t,r,n,a,i,o,c){for(var l=0;l0?o.top+3:o.bottom-1,f=s>0?o.top+3:o.bottom-3,p=f-2;"bass-8"===e.type&&(d=3,u=0),o.addRight(new i("8",5+u,a.getSymbolWidth("8")*h,d,{scalex:h,scaley:h,top:f,bottom:p}))}}return o}},3923:function(e,t,r){var n=r(1409),a=r(6020),i=r(6658);e.exports=function(e,t){if(e.el_type="keySignature",!e.accidentals||0===e.accidentals.length)return null;var r=new n(e,0,10,"staff-extra key-signature",t);r.isKeySig=!0;var s=0;return e.accidentals.forEach((function(e){var t,n=0;switch(e.acc){case"sharp":t="accidentals.sharp",n=-3;break;case"natural":t="accidentals.nat";break;case"flat":t="accidentals.flat",n=-1.2;break;case"quartersharp":t="accidentals.halfsharp",n=-2.5;break;case"quarterflat":t="accidentals.halfflat",n=-1.2;break;default:t="accidentals.flat"}r.addRight(new i(t,s,a.getSymbolWidth(t),e.verticalPos,{thickness:a.symbolHeightInPitches(t),top:e.verticalPos+a.symbolHeightInPitches(t)+n,bottom:e.verticalPos+n})),s+=a.getSymbolWidth(t)+2}),this),r}},2143:function(e,t,r){var n=r(6020),a=r(6658);e.exports=function(e,t,r,i){i||(i={});var s,o=void 0!==i.dir?i.dir:null,c=void 0!==i.headx?i.headx:0,l=void 0!==i.extrax?i.extrax:0,h=void 0!==i.flag?i.flag:null,u=void 0!==i.dot?i.dot:0,d=void 0!==i.dotshiftx?i.dotshiftx:0,f=void 0!==i.scale?i.scale:1,p=void 0!==i.accidentalSlot?i.accidentalSlot:[],m=void 0!==i.shouldExtendStem&&i.shouldExtendStem,g=void 0===i.printAccidentals||i.printAccidentals,v=i.chordPos,b=r.verticalPos,y=0,x=0,k=0;if(void 0===t)e.addFixed(new a("pitch is undefined",0,0,0,{type:"debug"}));else if(""===t)s=new a(null,0,0,b,{chordPos:v});else{var w=c;if(r.printer_shift){var C="same"===r.printer_shift?1:0;w="down"===o?-n.getSymbolWidth(t)*f+C:n.getSymbolWidth(t)*f-C}var T={scalex:f,scaley:f,thickness:n.symbolHeightInPitches(t)*f,name:r.name,chordPos:v};if((s=new a(t,w,n.getSymbolWidth(t)*f,b,T)).stemDir=o,h){var S=b+("down"===o?-7:7)*f;m&&("down"===o&&S>6&&(S=6),"up"===o&&S<6&&(S=6));var _="down"===o?c:c+s.w-.6;e.addRight(new a(h,_,n.getSymbolWidth(h)*f,S,{scalex:f,scaley:f,chordPos:v}))}for(x=s.w+d-2+5*u;u>0;u--){var E=1-Math.abs(b)%2;e.addRight(new a("dots.dot",s.w+d-2+5*u,n.getSymbolWidth("dots.dot"),b+E,{chordPos:v}))}}if(s&&(s.highestVert=r.highestVert),g&&r.accidental){var M;switch(r.accidental){case"quartersharp":M="accidentals.halfsharp";break;case"dblsharp":M="accidentals.dblsharp";break;case"sharp":M="accidentals.sharp";break;case"quarterflat":M="accidentals.halfflat";break;case"flat":M="accidentals.flat";break;case"dblflat":M="accidentals.dblflat";break;case"natural":M="accidentals.nat"}for(var A=!1,N=l,B=0;B=6){p[B][0]=b,N=p[B][1],A=!0;break}!1===A&&(N-=n.getSymbolWidth(M)*f+2,p.push([b,N]),y=n.getSymbolWidth(M)*f+2);var P=n.symbolHeightInPitches(M);e.addExtra(new a(M,N,n.getSymbolWidth(M),b,{scalex:f,scaley:f,top:b+P/2,bottom:b-P/2,chordPos:v})),k=n.getSymbolWidth(M)/2}return{notehead:s,accidentalshiftx:y,dotshiftx:x,extraLeft:k}}},2525:function(e,t,r){var n=r(1409),a=r(6020),i=r(6658);e.exports=function(e,t){e.el_type="timeSignature";var r=new n(e,0,10,"staff-extra time-signature",t);if("specified"===e.type)for(var s=0,o=0;o",this.dynamicPositioning)),this.startDiminuendoX=void 0),this.startCrescendoX&&(e.addOther(new a(this.startCrescendoX,d(e.children),"<",this.dynamicPositioning)),this.startCrescendoX=void 0)},l.prototype.dynamicDecoration=function(e,t,r,n){for(var s,o,c,l=0;l",n)),o&&e.addOther(new a(o.start,o.stop,"<",n)),c&&e.addOther(new i(c.start,c.stop))},l.prototype.createDecoration=function(e,t,r,a,i,l,u,d,f,p,m){f||(f={ornamentPosition:"above",volumePosition:p?"above":"below",dynamicPosition:p?"above":"below"}),function(e,t,r,a){for(var i=0;i9&&d++;var m=n/2;"center"!==s.getSymbolAlign(p)&&(m-=s.getSymbolWidth(p)/2),a.addFixedX(new o(p,m,s.getSymbolWidth(p),d))}if("slide"===t[f]&&a.heads[0]){var g=a.heads[0].pitch,v=new o("",-i-15,0,(g-=2)-1),b=new o("",-i-5,0,g+1);a.addFixedX(v),a.addFixedX(b),e.addOther(new c({anchor1:v,anchor2:b,fixedY:!0}))}}return void 0===d&&(d=r),{above:d,below:a.bottom}}(e,t,r,a,i,l,u,d,m);g.above=Math.max(g.above,this.minTop),g.below=Math.min(g.below,d),function(e,t,r,n,a,i,c,l){function h(e,t){"above"===e?n.above+=t:n.below-=t}function u(e){var t;return"above"===e?(t=n.above)c&&(t=c),t}function d(e,n,a){var i=u(n);r.addFixedX(new o(e,t/2,0,i+2,{type:"decoration",klass:"ornament",thickness:3,anchor:a})),h(n,5)}function f(e,n){var a=t/2;"center"!==s.getSymbolAlign(e)&&(a-=s.getSymbolWidth(e)/2);var i=s.symbolHeightInPitches(e)+1,c=u(n);c="above"===n?c+i/2:c-i/2,r.addFixedX(new o(e,a,s.getSymbolWidth(e),c,{klass:"ornament",thickness:s.symbolHeightInPitches(e),position:n})),h(n,i)}for(var p={"+":"scripts.stopped",open:"scripts.open",snap:"scripts.snap",wedge:"scripts.wedge",thumb:"scripts.thumb",shortphrase:"scripts.shortphrase",mediumphrase:"scripts.mediumphrase",longphrase:"scripts.longphrase",trill:"scripts.trill",trillh:"scripts.trill",roll:"scripts.roll",irishroll:"scripts.roll",marcato:"scripts.umarcato",dmarcato:"scripts.dmarcato",umarcato:"scripts.umarcato",turn:"scripts.turn",uppermordent:"scripts.prall",pralltriller:"scripts.prall",mordent:"scripts.mordent",lowermordent:"scripts.mordent",downbow:"scripts.downbow",upbow:"scripts.upbow",fermata:"scripts.ufermata",invertedfermata:"scripts.dfermata",breath:",",coda:"scripts.coda",segno:"scripts.segno"},m=0;mthis.w&&(this.w=e.dx+e.w),this.right[this.right.length]=e,this._addChild(e)},i.prototype.addFixed=function(e){this._addChild(e)},i.prototype.addFixedX=function(e){this._addChild(e)},i.prototype.addCentered=function(e){var t=e.w/2;-tthis.w&&(this.w=e.dx+t),this.right[this.right.length]=e,this._addChild(e)},i.prototype.setLimit=function(e,t){t[e]&&(this.specialY[e]?this.specialY[e]=Math.max(this.specialY[e],t[e]):this.specialY[e]=t[e])},i.prototype._addChild=function(e){var t=!0;"clef"==this.abcelem.el_type&&"barNumber"==e.type&&(t=!1),e.parent=this,this.children[this.children.length]=e,t&&this.pushTop(e.top),this.pushBottom(e.bottom),this.setLimit("tempoHeightAbove",e),this.setLimit("partHeightAbove",e),this.setLimit("volumeHeightAbove",e),this.setLimit("dynamicHeightAbove",e),this.setLimit("endingHeightAbove",e),this.setLimit("chordHeightAbove",e),this.setLimit("lyricHeightAbove",e),this.setLimit("lyricHeightBelow",e),this.setLimit("chordHeightBelow",e),this.setLimit("volumeHeightBelow",e),this.setLimit("dynamicHeightBelow",e)},i.prototype.pushTop=function(e){void 0!==e&&(void 0===this.top?this.top=e:this.top=Math.max(e,this.top))},i.prototype.pushBottom=function(e){void 0!==e&&(void 0===this.bottom?this.bottom=e:this.bottom=Math.min(e,this.bottom))},i.prototype.setX=function(e){this.x=e;for(var t=0;tthis.max)&&(this.max=e.abcelem.maxpitch))},t.prototype.addBeam=function(e){this.beams.push(e)},t.prototype.setStemDirection=function(){this.average=r(this.total,this.count),this.forceup?this.stemsUp=!0:this.forcedown?this.stemsUp=!1:this.stemsUp=this.average<6,delete this.count,this.total=0},t.prototype.calcDir=function(){this.average=r(this.total,this.elems.length),this.forceup?this.stemsUp=!0:this.forcedown?this.stemsUp=!1:this.stemsUp=this.average<6;for(var e=this.stemsUp?"up":"down",t=0;t0&&this.unalignedWords(e.unalignedWords,n,a,i,s),this.extraText(e,n,a,i,s),e.footer&&r&&this.footer(e.footer,t,n,s)}function s(e,t,r,n,i,s,o){r&&(t&&(r="string"==typeof r?t+r:[{text:t}].concat(r)),a(e,r,"historyfont",i=s?"abcjs-extra-text "+i:"","description",n,{absElemType:"extraText",anchor:"start"},o))}function o(e,t,r,i,s,o,c,l,h,u,d,f){if(r){l=d?"abcjs-extra-text "+l:"";var p=f.calc("A",s,l);if("string"==typeof r)t&&(r=t+"\n"+r),n(e,{marginLeft:i,text:r,font:s,absElemType:"extraText",name:h,"dominant-baseline":"middle",klass:l},f);else{e.push({startGroup:c,klass:l,name:h}),e.push({move:u.info}),t&&(n(e,{marginLeft:i,text:t,font:s,absElemType:"extraText",name:h,"dominant-baseline":"middle"},f),e.push({move:3*p.height/4}));for(var m=0;m0&&this.startVoice.staff.voices[0]===e)},e.exports=t},6891:function(e){e.exports=function(e,t,r,n){this.type="CrescendoElem",this.anchor1=e,this.anchor2=t,this.dir=r,"above"===n?this.dynamicHeightAbove=6:this.dynamicHeightBelow=6,this.pitch=void 0}},3185:function(e){e.exports=function(e,t,r){this.type="DynamicDecoration",this.anchor=e,this.dec=t,"below"===r?this.volumeHeightBelow=6:this.volumeHeightAbove=6,this.pitch=void 0}},8256:function(e){e.exports=function(e,t,r){this.type="EndingElem",this.text=e,this.anchor1=t,this.anchor2=r,this.endingHeightAbove=5,this.pitch=void 0}},3736:function(e){e.exports=function(e,t,r,n,a,i){var s,o=e.text;this.rows=[],t&&this.rows.push({move:t});var c=r.calc("textfont","defined-text");if(""===o)this.rows.push({move:2*c.attr["font-size"]});else if("string"==typeof o){this.rows.push({move:c.attr["font-size"]/2}),this.rows.push({left:n,text:o,font:"textfont",klass:"defined-text",anchor:"start",startChar:e.startChar,endChar:e.endChar,absElemType:"freeText",name:"free-text"});var l=o.replace(/^[ \t]*\n/gm,"X\n");s=i.calc(l,"textfont","defined-text"),this.rows.push({move:s.height})}else if(o){for(var h=0,u=n,d="textfont",f=0;fthis.top&&(this.top=this.pitch2),this.bottom=n,void 0!==this.pitch2&&this.pitch20?this.top+=a.stemHeight:this.bottom+=a.stemHeight),a.dim&&(this.dim=a.dim),a.position&&(this.position=a.position),void 0!==a.voiceNumber&&(this.voiceNumber=a.voiceNumber),this.height=a.height?a.height:4,a.top&&(this.top=a.top),a.bottom&&(this.bottom=a.bottom),a.name?this.name=a.name:this.c?this.name=this.c:this.name=this.type,a.realWidth?this.realWidth=a.realWidth:this.realWidth=this.w,this.centerVertically=!1,this.type){case"debug":this.chordHeightAbove=this.height;break;case"lyric":a.position&&"below"===a.position?this.lyricHeightBelow=this.height:this.lyricHeightAbove=this.height;break;case"chord":a.position&&"below"===a.position?this.chordHeightBelow=this.height:this.chordHeightAbove=this.height;break;case"text":void 0===this.pitch?a.position&&"below"===a.position?this.chordHeightBelow=this.height:this.chordHeightAbove=this.height:this.centerVertically=!0;break;case"part":this.partHeightAbove=this.height}};t.prototype.getChordDim=function(){if("debug"===this.type)return null;if(!this.chordHeightAbove&&!this.chordHeightBelow)return null;var e="chord"===this.type?this.realWidth/2:0,t=this.x-e-0;return{left:t,right:t+this.realWidth+0}},t.prototype.invertLane=function(e){void 0===this.lane&&(this.lane=0),this.lane=e-this.lane-1},t.prototype.putChordInLane=function(e){this.lane=e,this.chordHeightAbove?this.chordHeightAbove=1.25*this.height*this.lane:this.chordHeightBelow=1.25*this.height*this.lane},t.prototype.getLane=function(){return void 0===this.lane?0:this.lane},t.prototype.setX=function(e){this.x=e+this.dx},e.exports=t},6994:function(e,t,r){var n=r(1716);e.exports=function(e,t,r,a,i,s,o,c){var l=c.calc("i",r,a);if(""===t)e.push({move:l.height});else{if("string"==typeof t)return void n(e,{marginLeft:s,text:t,font:r,klass:a,marginTop:o.marginTop,anchor:o.anchor,absElemType:o.absElemType,info:o.info,name:i},c);o.marginTop&&e.push({move:o.marginTop});var h=0,u={left:s,anchor:o.anchor,phrases:[]};a&&(u.klass=a),e.push(u);for(var d=0;d0)this.above=!1;else{var e;e=this.anchor1?this.anchor1.pitch:this.anchor2?this.anchor2.pitch:14,this.anchor1&&"down"===this.anchor1.stemDir&&this.anchor2&&"down"===this.anchor2.stemDir?this.above=!0:this.anchor1&&"up"===this.anchor1.stemDir&&this.anchor2&&"up"===this.anchor2.stemDir?this.above=!1:this.anchor1&&this.anchor2?this.above=e>=6:this.anchor1?this.above="down"===this.anchor1.stemDir:this.anchor2?this.above="down"===this.anchor2.stemDir:this.above=e>=6}},t.prototype.calcSlurDirection=function(){if(this.isGrace)this.above=!1;else if(0===this.voiceNumber)this.above=!0;else if(this.voiceNumber>0)this.above=!1;else{var e=!1;this.anchor1&&"down"===this.anchor1.stemDir&&(e=!0),this.anchor2&&"down"===this.anchor2.stemDir&&(e=!0);for(var t=0;te&&(e=this.internalNotes[t].highestVert);e>this.startY&&e>this.endY&&(this.startY=this.endY=e-1)}},t.prototype.getYBounds=function(){var e,t;return this.isTie?(this.calcTieDirection(),this.calcX(10,1e3),this.calcTieY()):(this.calcSlurDirection(),this.calcX(10,1e3),this.calcSlurY()),this.above?e=(t=Math.min(this.startY,this.endY))+3:t=(e=Math.min(this.startY,this.endY))-3,[e,t]},e.exports=t},1134:function(e,t,r){var n=r(1716),a=r(6994);e.exports=function(e,t,r,i,s,o,c,l,h,u){if(this.rows=[],e.header&&o){var d=u.calc("X","headerfont","abcjs-header abcjs-meta-top").height;n(this.rows,{marginLeft:c,text:e.header.left,font:"headerfont",klass:"header meta-top",marginTop:-d,info:t.header,name:"header"},u),n(this.rows,{marginLeft:c+s/2,text:e.header.center,font:"headerfont",klass:"header meta-top",marginTop:-d,anchor:"middle",info:t.header,name:"header"},u),n(this.rows,{marginLeft:c+s,text:e.header.right,font:"headerfont",klass:"header meta-top",marginTop:-d,anchor:"end",info:t.header,name:"header"},u)}o&&this.rows.push({move:l.top});var f=r.titleleft?"start":"middle",p=r.titleleft?c:c+s/2;if(e.title){var m=h?"abcjs-title":"";a(this.rows,e.title,"titlefont",m,"title",p,{marginTop:l.title,anchor:f,absElemType:"title",info:t.title},u)}if(i.length)for(var g=0;g0){var v=!(!e.composer&&!e.origin);m=h?"abcjs-rhythm":"",n(this.rows,{marginLeft:c,text:e.rhythm,font:"infofont",klass:m,absElemType:"rhythm",noMove:v,info:t.rhythm,name:"rhythm"},u)}e.composer&&e.composer,e.origin&&e.origin;var b=e.composer?e.composer:"";e.origin&&("string"==typeof b&&"string"==typeof e.origin?b+=" ("+e.origin+")":"string"==typeof b&&"string"!=typeof e.origin?((b=[{text:b}]).push({text:" ("}),(b=b.concat(e.origin)).push({text:")"})):(b.push({text:" ("}),(b=b.concat(e.origin)).push({text:")"}))),b&&(m=h?"abcjs-composer":"",a(this.rows,b,"composerfont",m,"composer",c+s,{anchor:"end",absElemType:"composer",info:t.composer,ingroup:!0},u))}e.author&&e.author.length>0&&(m=h?"abcjs-author":"",a(this.rows,e.author,"composerfont",m,"author",c+s,{anchor:"end",absElemType:"author",info:t.author},u)),e.partOrder&&e.partOrder.length>0&&(m=h?"abcjs-part-order":"",a(this.rows,e.partOrder,"partsfont",m,"part-order",c,{absElemType:"partOrder",info:t.partOrder,anchor:"start"},u))}},2096:function(e){var t=function(e,t,r){this.type="TripletElem",this.anchor1=t,this.number=e,this.durationClass=("d"+Math.round(1e3*t.parent.durationClass)/1e3).replace(/\./,"-"),this.middleElems=[],this.flatBeams=r.flatBeams};t.prototype.isClosed=function(){return!!this.anchor2},t.prototype.middleNote=function(e){this.middleElems.push(e)},t.prototype.setCloseAnchor=function(e){this.anchor2=e,this.anchor1.parent.beam&&"up"!==this.anchor1.stemDir||(this.endingHeightAbove=4)},e.exports=t},3197:function(e){var t=function(e,t){this.children=[],this.beams=[],this.otherchildren=[],this.w=0,this.duplicate=!1,this.voicenumber=e,this.voicetotal=t,this.bottom=7,this.top=7,this.specialY={tempoHeightAbove:0,partHeightAbove:0,volumeHeightAbove:0,dynamicHeightAbove:0,endingHeightAbove:0,chordHeightAbove:0,lyricHeightAbove:0,lyricHeightBelow:0,chordHeightBelow:0,volumeHeightBelow:0,dynamicHeightBelow:0}};t.prototype.addChild=function(e){if("bar"===e.type){for(var t=!0,r=0;t&&r0&&"TempoElement"===t.children[0].type;t.elemset=[],o.beginGroup(e.paper,e.controller);for(var u=0;u=0&&f.setAttribute("class","abcjs-notehead"),f&&d.chordPos&&0!==d.name.indexOf("flags.")&&(p=(p=f.getAttribute("class"))?p+" abcjs-chord-pos-"+d.chordPos:"abcjs-chord-pos-"+d.chordPos,f.setAttribute("class",p))}}var p=t.type;if(("note"===t.type||"rest"===t.type)&&(t.counters=e.controller.classes.getCurrent(),p=(p+=" d"+Math.round(1e3*t.durationClass)/1e3).replace(/\./g,"-"),t.abcelem.pitches))for(var m=0;m0?g.classList[0]+" ":"";g.setAttribute("class",v+t.overrideClasses)}if(h)t.startChar=t.abcelem.startChar,t.endChar=t.abcelem.endChar,c.add(t,g,!1,l);else{t.elemset.push(g);var b=!1;"note"!==t.type&&"tabNumber"!==t.type||(b=!0),c.add(t,g,b,l)}}else t.elemset.length>0&&c.add(t,t.elemset[0],"note"===t.type,l);if(t.klass&&s(t.elemset,"mark","","#00ff00"),t.hint&&s(t.elemset,"abcjs-hint","",null),t.abcelem.abselem=t,t.heads&&t.heads.length>0){t.notePositions=[];for(var y=0;ys&&l>=14;)l-=2,o.setAttribute("font-size",l),c=o.getBBox()}var c=20,l=-3;function h(e){return"No Chord"===e?"N.C.":e}function u(e,t,r,n,a,i,s,o,c){var l={x:r,y:n,stroke:"none","font-size":a,"font-style":i.style,"font-family":i.face,"font-weight":i.weight,"text-decoration":i.decoration};return s&&(l["data-name"]=s),o&&(l.class=o),l["text-anchor"]=c?"middle":"start",e.paper.text(t,l,null,{"alignment-baseline":"middle"})}e.exports=function(e,t,r,a,d){var f=d.gchordfont,p=(d.partsfont,d.annotationfont),m=d.repeatfont,g=d.textfont,v=d.subtitlefont,b=50;e.paper.openGroup({klass:"abcjs-chord-grid"}),t.forEach((function(t){switch(t.type){case"text":u(e,t.text,r,e.y,16,g,null,null,!1),e.moveY(16);break;case"subtitle":u(e,t.subtitle,r,e.y+10,20,v,null,"abcjs-subtitle",!1),e.moveY(20);break;case"part":if(t.lines.length>0){u(e,t.name,r,e.y+10,20,v,t.name,"abcjs-part",!1),e.moveY(20);var d=t.lines[0].length,y=a/d;t.lines.forEach((function(t,a){var d=!1,g=!1;t.forEach((function(e){e.ending&&(d=!0),e.annotations&&e.annotations.length>0&&(g=!0)}));var v=g?14:d?10:0;t.forEach((function(t,a){if(!t.noBorder){e.paper.rect({x:r+a*y,y:e.y,width:y,height:v+b}),e.paper.rect({x:r+a*y+1,y:e.y+1,width:y-2,height:v+b-2});var d=0,g=0,x=e.y,k=r+y*a;t.hasStartRepeat&&(i(e,k,x,x+b+v,!0,v),d=12),t.hasEndRepeat&&(i(e,k+y,x,x+b+v,!1,v),g=12);var w=0;t.ending&&(w=u(e,t.ending,r+a*y+4,x+10,12,m,null,null,!1).getBBox().width+4),!function(e,t,r,a,i,s,u,d,f,p,m){var g=r+a*s;u[1]||u[2]||u[3]?u[1]||u[3]?function(e,t,r,n,a,i,s,u){var d=3;e.paper.lineToBack({x1:t+d,x2:t+n-d,y1:r+a/2+u,y2:r+a/2+u}),e.paper.lineToBack({x1:t+n/2,x2:t+n/2,y1:r+d+u,y2:r+a-d+u}),i[0]&&o(e,t+n/4,r+a/4+2+u+l,c,h(i[0]),s,n/2),i[1]&&o(e,t+3*n/4,r+a/4+2+u+l,c,h(i[1]),s,n/2),i[2]&&o(e,t+n/4,r+3*a/4+u+l,c,h(i[2]),s,n/2),i[3]&&o(e,t+3*n/4,r+3*a/4+u+l,c,h(i[3]),s,n/2)}(e,g,t,a-f,p,u,d,m):function(e,t,r,n,a,i,s,c,h){e.paper.lineToBack({x1:t,x2:t+n,y1:r+a+h,y2:r+2}),o(e,t+n/4,r+a/4+5+h+l,26,i,c,n/2),o(e,t+3*n/4,r+3*a/4+h+l,26,s,c,n/2)}(e,g,t,a-f,p,u[0],u[2],d,m):function(e,t,r,a,i,s,c,h){"%"===s?function(e,t,r,a){var i=t-10,s=t+10,o=r+10,c=r-10,l=t-10,h=2-e.yToPitch(a),u=t+6.5,d=-e.yToPitch(a)-2.3;e.paper.lineToBack({x1:i,x2:s,y1:o,y2:c,"stroke-width":"3px","stroke-linecap":"round"}),n(e,l,h,"dots.dot",{scalex:1,scaley:1,klass:"",name:"dot"}),n(e,u,d,"dots.dot",{scalex:1,scaley:1,klass:"",name:"dot"})}(e,t+a/2,r+i/2,h+i/2):o(e,t+a/2,r+i/2+l,34,s,c,a)}(e,g,t+m,a-f,p,u[0],d,m)}(e,x,r+d,y,0,a,t.chord,f,d+g,b,v),t.annotations&&t.annotations.length>0&&function(e,t,r,a,i){r+=3;for(var o=0;or.formatting.maxStaves)break;t.shouldAddClasses&&(b.klass="abcjs-staff-wrapper abcjs-l"+t.lineNumber),e.paper.openGroup(b),C.vskip&&e.moveY(C.vskip),x.length>=1&&h(e,e.spacing.staffSeparation,x[x.length-1],C.staffGroup);var T=l(e,C.staffGroup,v,w);T.line=m+w,x.push(T),e.paper.closeGroup()}else C.nonMusic&&(t.shouldAddClasses&&(b.klass="abcjs-non-music"),e.paper.openGroup(b),i(e,C.nonMusic,v),e.paper.closeGroup())}return t.reset(),y||r.bottomText&&r.bottomText.rows&&r.bottomText.rows.length>0&&(t.shouldAddClasses&&(b.klass="abcjs-meta-bottom"),e.paper.openGroup(b),e.moveY(24),i(e,r.bottomText,v),e.paper.closeGroup()),a(e,s,d,u),{staffgroups:x,selectables:v.getElements()}}},9879:function(e,t,r){var n=r(4777);e.exports=function(e,t,r){void 0===t.pitch&&window.console.error("Dynamic Element y-coordinate not set.");var a=n(e,t.anchor.x,t.pitch,t.dec,{scalex:1,scaley:1,klass:e.controller.classes.generate("decoration dynamics"),fill:e.foregroundColor,stroke:"none",name:"dynamics"});return r.wrapSvgEl({el_type:"dynamicDecoration",startChar:-1,endChar:-1,decoration:t.dec},a),[a]}},5947:function(e,t,r){var n=r(6764),a=r(5759),i=r(6454),s=r(7199);e.exports=function(e,t,r,o,c){void 0===t.pitch&&window.console.error("Ending Element y-coordinate not set.");var l=s(e.calcY(t.pitch)),h="";t.anchor1&&(r=s(t.anchor1.x+t.anchor1.w),h+=n("M %f %f L %f %f ",r,l,r,s(l+20))),t.anchor2&&(o=s(t.anchor2.x),h+=n("M %f %f L %f %f ",o,l,o,s(l+20))),h+=n("M %f %f L %f %f ",r,l,o,l),e.paper.openGroup({klass:e.controller.classes.generate("ending"),fill:e.foregroundColor,"data-name":"ending"}),i(e,{path:h,stroke:e.foregroundColor,fill:e.foregroundColor,"data-name":"line"}),t.anchor1&&a(e,{x:s(r+5),y:s(e.calcY(t.pitch-.5)),text:t.text,type:"repeatfont",klass:"ending",anchor:"start",noClass:!0,name:t.text});var u=e.paper.closeGroup();return c.wrapSvgEl({el_type:"ending",startChar:-1,endChar:-1},u),[u]}},9045:function(e,t,r){var n=r(6764),a=r(6454),i=r(7199);function s(e,t,r){return i(e+r*t)}var o=[[3.5,-4.8]],c=[[1.5,-1],[.3,-.3],[-3.5,3.8]],l=[[-1.5,2]],h=[[3,4],[3,-4]],u=[[-3,4],[-3,-4]];function d(e,t){for(var r="",n=0;n1&&s.indexOf(".")<0){var p=i.isInGroup()?"":o.klass;e.paper.openGroup({"data-name":o.name,klass:p});for(var m=0,g=0;g0?t.linewidth+e.lineThickness:t.linewidth-e.lineThickness;t.graphelem=a(e,t.x,u,o,e.calcY(t.pitch2),"abcjs-stem","stem");break;case"ledger":t.graphelem=i(e,t.x,t.x+t.w,t.pitch,"abcjs-ledger","ledger",.35+e.lineThickness)}return 1!==t.scalex&&t.graphelem&&function(e,t,r,n,a,i){e.setAttributeOnElement(t,{style:"transform:scale("+r+","+n+");transform-origin:"+a+"px "+i+"px;"})}(e.paper,t.graphelem,t.scalex,t.scaley,t.x,o),t.graphelem}},7199:function(e){e.exports=function(e){return parseFloat(e.toFixed(2))}},3524:function(e,t,r){var n=r(5741),a=r(5829);function i(e,t,r){this.elements=[],this.paper=e,this.tuneNumber=r,this.selectTypes=t}i.prototype.getElements=function(){return this.elements},i.prototype.add=function(e,t,r,n){if(this.canSelect(e)){var a;a=void 0===this.selectTypes?{selectable:!1,"data-index":this.elements.length}:{selectable:!0,tabindex:0,"data-index":this.elements.length},this.paper.setAttributeOnElement(t,a);var i={absEl:e,svgEl:t,isDraggable:r};void 0!==n&&(i.staffPos=n),this.elements.push(i)}},i.prototype.canSelect=function(e){return!(!1===this.selectTypes||!e||!e.abcelem||!0!==this.selectTypes&&!(void 0===this.selectTypes?"note"===e.abcelem.el_type||"tabNumber"===e.abcelem.el_type:this.selectTypes.indexOf(e.abcelem.el_type)>=0))},i.prototype.wrapSvgEl=function(e,t){var r={tuneNumber:this.tuneNumber,abcelem:e,elemset:[t],highlight:n,unhighlight:a};this.add(r,t,!1)},e.exports=i},4169:function(e){e.exports=function(e,t){var r=Math.round(e.y),n=(e.controller.width-t)/2,a=n+t,i="M "+n+" "+r+" L "+a+" "+r+" L "+a+" "+(r+1)+" L "+n+" "+(r+1)+" L "+n+" "+r+" z";e.paper.pathToBack({path:i,stroke:"rgba(0,0,0,0)",fill:"rgba(0,0,0,255)",class:e.controller.classes.generate("defined-text")})}},7611:function(e){e.exports=function(e,t,r,n){var a=(t+e.padding.left+e.padding.right)*r,i=(e.y+e.padding.bottom)*r;if(e.isPrint&&(i=Math.max(i,1056)),""!==e.ariaLabel){var s="Sheet Music";e.abctune&&e.abctune.metaText&&e.abctune.metaText.title&&(s+=' for "'+e.abctune.metaText.title+'"'),e.paper.setTitle(s);var o=e.ariaLabel?e.ariaLabel:s;e.paper.setAttribute("aria-label",o)}e.paper.insertStyles(".abcjs-dragging-in-progress text, .abcjs-dragging-in-progress tspan {"+["-webkit-touch-callout: none;","-webkit-user-select: none;","-khtml-user-select: none;","-moz-user-select: none;","-ms-user-select: none;","user-select: none;"].join(" ")+"}");var c={overflow:"hidden"};"resize"===n?e.paper.setResponsiveWidth(a,i):(c.width="",c.height=i+"px",r<1?(c.width=a+"px",e.paper.setSize(a/r,i/r)):e.paper.setSize(a,i)),e.paper.setScale(r),e.paper.setParentStyles(c)}},6764:function(e){function t(e){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},t(e)}e.exports=function(){for(var e,r,n,a,i,s=0,o=arguments[s++],c=[];o;){if(r=/^[^\x25]+/.exec(o))c.push(r[0]);else if(r=/^\x25{2}/.exec(o))c.push("%");else{if(!(r=/^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(o)))throw"Huh ?!";if(null==(e=arguments[r[1]||s++])||null==e)throw"Too few arguments.";if(/[^s]/.test(r[7])&&"number"!=typeof e)throw"Expecting number but found "+t(e);switch(r[7]){case"b":e=e.toString(2);break;case"c":e=String.fromCharCode(e);break;case"d":e=parseInt(e);break;case"e":e=r[6]?e.toExponential(r[6]):e.toExponential();break;case"f":e=r[6]?parseFloat(e).toFixed(r[6]):parseFloat(e);break;case"o":e=e.toString(8);break;case"s":e=(e=String(e))&&r[6]?e.substring(0,r[6]):e;break;case"u":e=Math.abs(e);break;case"x":e=e.toString(16);break;case"X":e=e.toString(16).toUpperCase()}e=/[def]/.test(r[7])&&r[2]&&e>0?"+"+e:e,a=r[3]?"0"==r[3]?"0":r[3][1]:" ",i=r[5]-String(e).length,n=r[5]?str_repeat(a,i):"",c.push(r[4]?e+n:n+e)}o=o.substring(r[0].length)}return c.join("")}},5537:function(e,t,r){var n=r(4331),a=r(129),i=r(3145),s=r(7641),o=r(1547),c=r(3624),l=r(5668);function h(e,t,r,i,s){if(r)for(var o=0;o=0&&v.voices&&u(e,t.voices,v.voices),e.showDebug.indexOf("grid")>=0&&(e.paper.dottedLine({x1:e.padding.left,x2:e.padding.left+e.controller.width,y1:m,y2:m,stroke:"#0000ff"}),o(e,{x:e.padding.left,y:e.calcY(v.originalTop),width:e.controller.width,height:e.calcY(v.originalBottom)-e.calcY(v.originalTop),fill:e.foregroundColor,stroke:e.foregroundColor,"fill-opacity":.1,"stroke-opacity":.1}),d=0,E(v,"chordHeightAbove"),E(v,"chordHeightBelow"),E(v,"dynamicHeightAbove"),E(v,"dynamicHeightBelow"),E(v,"endingHeightAbove"),E(v,"lyricHeightAbove"),E(v,"lyricHeightBelow"),E(v,"partHeightAbove"),E(v,"tempoHeightAbove"),E(v,"volumeHeightAbove"),E(v,"volumeHeightBelow"))),e.moveY(n.STEP,-v.bottom),e.showDebug&&e.showDebug.indexOf("grid")>=0&&e.paper.dottedLine({x1:e.padding.left,x2:e.padding.left+e.controller.width,y1:e.y,y2:e.y,stroke:"#0000aa"})}for(var b=2,y=0,x=0;x6&&(d=0)}}_>1&&(f=t.staffs[0].topLine,p=t.staffs[_-1].bottomLine,c(e,t.startx,.6,f,p,null)),e.y=m}},7865:function(e,t,r){var n=r(6637);e.exports=function(e,t,r,a,i,s,o){var c=e.calcY(a);return n(e,t,r,c,i,s,o)}},7641:function(e,t,r){var n=r(7865);e.exports=function(e,t,r,a,i,s){var o="abcjs-top-line",c=2;i&&(c=i),e.paper.openGroup({prepend:!0,klass:e.controller.classes.generate("abcjs-staff")});var l=0,h=0;if(1===a)n(e,t,r,6,o,null,s+e.lineThickness),l=e.calcY(10),h=e.calcY(2);else for(var u=a-1;u>=0;u--){var d=(u+1)*c;h=e.calcY(d),0===l&&(l=h),n(e,t,r,d,o,null,s+e.lineThickness),o=void 0}return e.paper.closeGroup(),[l,h]}},3987:function(e,t,r){var n=r(2435),a=r(5759);e.exports=function(e,t){var r=t.x;void 0===t.pitch&&window.console.error("Tempo Element y-coordinate not set."),t.tempo.el_type="tempo";var i,s=e.calcY(t.pitch)+2;if(t.tempo.preString){i=a(e,{x:r,y:s,text:t.tempo.preString,type:"tempofont",klass:"abcjs-tempo",anchor:"start",noClass:!0,name:"pre"},!0);var o=e.controller.getTextSize.calc(t.tempo.preString,"tempofont","tempo",i).width;r+=o+o/t.tempo.preString.length}if(t.note){t.note.setX(r);for(var c=0;c.1||(this.scale=void 0),t.staffwidth?(this.staffwidthScreen=t.staffwidth,this.staffwidthPrint=t.staffwidth):(this.staffwidthScreen=740,this.staffwidthPrint=680),this.listeners=[],t.clickListener&&this.addSelectListener(t.clickListener),this.renderer=new i(e),this.renderer.setPaddingOverride(t),t.showDebug&&(this.renderer.showDebug=t.showDebug),t.jazzchords&&(this.jazzchords=t.jazzchords),t.accentAbove&&(this.accentAbove=t.accentAbove),t.germanAlphabet&&(this.germanAlphabet=t.germanAlphabet),t.lineThickness&&(this.lineThickness=t.lineThickness),t.chordGrid&&(this.chordGrid=t.chordGrid),this.renderer.controller=this,this.renderer.foregroundColor=t.foregroundColor?t.foregroundColor:"currentColor",void 0!==t.ariaLabel&&(this.renderer.ariaLabel=t.ariaLabel),this.renderer.minPadding=t.minPadding?t.minPadding:0,this.reset()};function x(e){for(var t=document.createElementNS("http://www.w3.org/2000/svg","svg"),r=0;r0)for(var o=s.staffGroup.voices[0],c=!1,l=0,h=0;hthis.width+1&&(e.topText=new l(e.metaText,e.metaTextInfo,e.formatting,e.lines,i,this.renderer.isPrint,this.renderer.padding.left,this.renderer.spacing,this.classes.shouldAddClasses,this.getTextSize),e.lines&&e.lines.length>0))for(var s=e.lines.length,o=0;o0)for(var h=c.nonMusic.rows.length,f=0;f0&&c.text[0].center)&&(p.left=i/2+this.renderer.padding.left)}}e.tablatures&&v.layoutTablatures(this.renderer,e);var m=g(this.renderer,this.classes,e,this.width,i,this.responsive,a,this.selectTypes,t,r,this.chordGrid);if(this.staffgroups=m.staffgroups,this.selectables=m.selectables,this.oneSvgPerLine){var b=this.renderer.paper.svg.parentNode;this.svgs=function(e,t,r,n,a){r||(r="Untitled");var i=t.querySelector("svg");"resize"===n&&(t.style.paddingBottom="");for(var s=i.querySelector("style"),o="resize"===n?i.viewBox.baseVal.width:i.getAttribute("width"),c=t.querySelectorAll("svg > g"),l=0,h=[],u=0;u0&&t.push(e),"abcjs-tab-number"===e)return t.join(" ");if("text instrument-name"===e)return"abcjs-text abcjs-instrument-name";if(null!==this.lineNumber&&t.push("l"+this.lineNumber),null!==this.measureNumber&&t.push("m"+this.measureNumber),null!==this.measureNumber&&t.push("mm"+this.measureTotal()),null!==this.voiceNumber&&t.push("v"+this.voiceNumber),e&&(e.indexOf("note")>=0||e.indexOf("rest")>=0||e.indexOf("lyric")>=0)&&null!==this.noteNumber&&t.push("n"+this.noteNumber),t.length>0){t=(t=t.join(" ")).split(" ");for(var r=0;r0&&(t[r]="abcjs-"+t[r])}return t.join(" ")},e.exports=t},1328:function(e){var t=function(e,t){this.formatting=e,this.classes=t};t.prototype.updateFonts=function(e){e.gchordfont&&(this.formatting.gchordfont=e.gchordfont),e.tripletfont&&(this.formatting.tripletfont=e.tripletfont),e.annotationfont&&(this.formatting.annotationfont=e.annotationfont),e.vocalfont&&(this.formatting.vocalfont=e.vocalfont)},t.prototype.getFamily=function(e){return'"'===e[0]&&'"'===e[e.length-1]?e.substring(1,e.length-1):e},t.prototype.calc=function(e,t){var r;r="string"==typeof e?(r=this.formatting[e])?{face:r.face,size:Math.round(4*r.size/3),decoration:r.decoration,style:r.style,weight:r.weight,box:r.box}:{face:"Arial",size:Math.round(16),decoration:"underline",style:"normal",weight:"normal"}:{face:e.face,size:Math.round(4*e.size/3),decoration:e.decoration,style:e.style,weight:e.weight,box:e.box};var n=this.formatting.fontboxpadding?this.formatting.fontboxpadding:.1;return r.padding=r.size*n,{font:r,attr:{"font-size":r.size,"font-style":r.style,"font-family":this.getFamily(r.face),"font-weight":r.weight,"text-decoration":r.decoration,class:this.classes.generate(t)}}},e.exports=t},9799:function(e){var t=function(e,t){this.getFontAndAttr=e,this.svg=t};t.prototype.updateFonts=function(e){this.getFontAndAttr.updateFonts(e)},t.prototype.attr=function(e,t){return this.getFontAndAttr.calc(e,t)},t.prototype.getFamily=function(e){return'"'===e[0]&&'"'===e[e.length-1]?e.substring(1,e.length-1):e},t.prototype.calc=function(e,t,r,n){var a;a="string"==typeof t?this.attr(t,r):{font:{face:t.face,size:t.size,decoration:t.decoration,style:t.style,weight:t.weight},attr:{"font-size":t.size,"font-style":t.style,"font-family":this.getFamily(t.face),"font-weight":t.weight,"text-decoration":t.decoration,class:this.getFontAndAttr.classes.generate(r)}};var i=this.svg.getTextSize(e,a.attr,n);return a.font.box?{height:i.height+4*a.font.padding,width:i.width+4*a.font.padding}:i},t.prototype.baselineToCenter=function(e,t,r,n,a){return.5*this.calc(e,t,r).height+(a-n-2)*this.attr(t,r).font.size},e.exports=t},2187:function(e){e.exports=function(e,t,r,n){if(e)for(var a=0;a0&&(o.length>0&&" "!==o[o.length-1]&&(o+=" "),o+=t),i.setAttribute("class",o)}}},4331:function(e){var t={FONTEM:360,FONTSIZE:30};t.STEP=93*t.FONTSIZE/720,t.SPACE=10,t.TOPNOTE=15,t.STAVEHEIGHT=100,t.INDENT=50,e.exports=t},8123:function(e){function t(e,t,r,n){if(0===e.indexOf(t)){var a=e.replace(t,""),i=parseInt(a,10);""+i===a&&(r[n]=i)}}e.exports=function(e,r){var n=[];if(e.absEl.elemset){for(var a={},i=0;i=0&&r=0?(a=function(e,t,r){return e.x<=t.offsetX&&e.x+e.width>=t.offsetX&&e.y<=t.offsetY&&e.y+e.height>=t.offsetY||Math.abs(t.layerY/r-t.offsetY)<3?[t.offsetX,t.offsetY]:[t.layerX,t.layerY]}(e.selectables[i].svgEl.getBBox(),t,e.scale),r=a[0],n=a[1]):(a=function(e){var t,r,n=1,a=1,i=e.target.closest("svg"),s=0;return i&&i.viewBox&&i.viewBox.baseVal&&(0!==i.viewBox.baseVal.width&&(n=i.viewBox.baseVal.width/i.clientWidth),0!==i.viewBox.baseVal.height&&(a=i.viewBox.baseVal.height/i.clientHeight),s=i.viewBox.baseVal.y),e.target&&"svg"===e.target.tagName?(t=e.offsetX,r=e.offsetY):(t=e.layerX,r=e.layerY),[t*=n,(r*=a)+s]}(t),i=function(e,t,r){for(var n=9999999,a=-1,i=0;i0;i++){var s=e.selectables[i];if(e.getDim(s),s.dim.leftt&&s.dim.topr)a=i,n=0;else if(s.dim.topr){var o=Math.min(Math.abs(s.dim.left-t),Math.abs(s.dim.right-t));ot){var c=Math.min(Math.abs(s.dim.top-r),Math.abs(s.dim.bottom-r));cMath.abs(t-s.dim.right)?Math.abs(t-s.dim.right):Math.abs(t-s.dim.left),h=Math.abs(r-s.dim.top)>Math.abs(r-s.dim.bottom)?Math.abs(r-s.dim.bottom):Math.abs(r-s.dim.top),u=Math.sqrt(l*l+h*h);u=0&&n<=12?a:-1}(e,r=a[0],n=a[1])),{x:r,y:n,clickedOn:i}}function l(e){if(e&&e.target&&e.touches&&!(e.touches.length<1)){var t=e.target.getBoundingClientRect(),r=e.touches[0].pageX-t.left,n=e.touches[0].pageY-t.top;e.touches[0].offsetX=r,e.touches[0].offsetY=n,e.touches[0].layerX=e.touches[0].pageX,e.touches[0].layerY=e.touches[0].pageY}}function h(e){var t=e;"touchstart"===e.type&&(l(e),e.touches.length>0&&(t=e.touches[0]));var r=c(this,t);r.clickedOn>=0&&("touchstart"===e.type||0===e.button)&&this.selectables[r.clickedOn]&&(this.dragTarget=this.selectables[r.clickedOn],this.dragIndex=r.clickedOn,this.dragMechanism="mouse",this.dragMouseStart={x:r.x,y:r.y},this.dragging&&this.dragTarget.isDraggable&&(function(e,t){if(e){var r=v(e.svg);r[t]=!0,b(e.svg,r)}}(this.renderer.paper,"abcjs-dragging-in-progress"),this.dragTarget.absEl.highlight(void 0,this.dragColor)))}function u(e){var t=e;if("touchmove"===e.type&&(l(e),e.touches.length>0&&(t=e.touches[0])),this.lastTouchMove=e,this.dragTarget&&this.dragging&&this.dragTarget.isDraggable&&"mouse"===this.dragMechanism&&this.dragMouseStart){var r=c(this,t),a=Math.round((r.y-this.dragMouseStart.y)/n.STEP);a!==this.dragYStep&&(this.dragYStep=a,this.dragTarget.svgEl.setAttribute("transform","translate(0,"+a*n.STEP+")"))}}function d(e){var t=e;"touchend"===e.type&&this.lastTouchMove&&(l(this.lastTouchMove),this.lastTouchMove&&this.lastTouchMove.touches&&this.lastTouchMove.touches.length>0&&(t=this.lastTouchMove.touches[0])),this.dragTarget&&(m.bind(this)(),this.dragTarget.absEl&&this.dragTarget.absEl.highlight&&(this.selected=[this.dragTarget.absEl],this.dragTarget.absEl.highlight(void 0,this.selectionColor)),p.bind(this)(this.dragTarget,this.dragYStep,this.selectables.length,this.dragIndex,t),this.dragTarget.svgEl&&this.dragTarget.svgEl.focus&&(this.dragTarget.svgEl.focus(),this.dragTarget=null,this.dragIndex=-1),function(e,t){if(e){var r=v(e.svg);delete r[t],b(e.svg,r)}}(this.renderer.svg,"abcjs-dragging-in-progress"))}function f(e){e>=0&&eo&&e=0;r--)if(!e[r].abcelem.rest)return r;return-1}e.exports=function(e){if(0!==e.elems.length&&!e.allrests){var t=function(e,t){var r=e?a.STEP:-a.STEP;return t&&(r*=.4),r}(e.stemsUp,e.isgrace),r=e.elems[0],u=e.elems[e.elems.length-1],d=0,f=e.stemsUp?r.abcelem.maxpitch:r.abcelem.minpitch;d=o(r,e.stemsUp,f,d),d=o(u,e.stemsUp,f,d),d=Math.max(e.stemHeight,d+3);var p=(b=e.average,y=e.elems.length,x=d,k=e.stemsUp,w=r.abcelem.averagepitch,C=u.abcelem.averagepitch,T=e.isflat,S=e.min,_=e.max,E=e.isgrace,M=x-2,A=x-2,N=Math.round(k?Math.max(b+M,_+A):Math.min(b-M,S-A)),B=function(e,t,r,n){if(n)return 0;var a=e-t,i=r/2;return a>i&&(a=i),a<-i&&(a=-i),a}(w,C,y,T),P=N+Math.floor(B/2),L=N+Math.floor(-B/2),E||(k&&N<6||!k&&N>6)&&(P=6,L=6),[P,L]),m=c(e.stemsUp,r,u);e.addBeam({startX:m[0],endX:m[1],startY:p[0],endY:p[1],dy:t});for(var g=function(e,t,r,n,a){for(var o=[],u=[],d=0;d0&&f.abcelem.beambr&&f.abcelem.beambr<=x+1){u[x].split||(u[x].split=[u[x].x]);var k=c(t,e[d-1],f);u[x].split[u[x].split.length-1]>=k[0]&&(k[0]+=f.w),u[x].split.push(k[0]),u[x].split.push(k[1])}}for(var w=u.length-1;w>=0;w--){var C=l(e,d);if(-1===C||C-w-4){var T=m,S=g+v*(w+1);if(u[w].single){var _=h(e,d);if(-1===_)T=m+5;else if(-1===C)T=m-5;else{var E=e[_].abcelem.duration,M=e[C].abcelem.duration;T=E===M?d%2==0?m+5:m-5:E0?((a=(r-(n-i*a))/i)*s>50&&(a=50/s),a):null}function u(e){for(var t=0;t1){var o=i[0].abcelem.rest&&"rest"===i[0].abcelem.rest.type,c=i[s].abcelem.rest&&"rest"===i[s].abcelem.rest.type;if(o&&!i[s].abcelem.rest){var l=i[0].children.find((function(e){return e.name.includes("rest")})),h=d(i[s]);if(l){var u=l.bottom-h;(u-=2)<0&&i[0].children.length>0&&(i[0].bottom-=u,i[0].top-=u,i[0].children[0].bottom-=u,i[0].children[0].top-=u,i[0].children[0].pitch-=u)}}else if(c&&!i[0].abcelem.rest){var p=i[s].children.find((function(e){return e.name.includes("rest")}));if(p){var m=p.top-f(i[0]);(m+=2)>0&&i[s].children.length>0&&(i[s].bottom-=m,i[s].top-=m,i[s].children[0].bottom-=m,i[s].children[0].top-=m,i[s].children[0].pitch-=m)}}}}}function d(e){if(e.children){for(var t=-90,r=0;r-90)return t}return e.top}function f(e){if(e.children){for(var t=90,r=0;rMath.round(p)&&(p=m,s&&(d=-1)))}for(d=0;d=0&&(c.originalTop=c.top,c.originalBottom=c.bottom),i(c,l,"lyricHeightAbove"),i(c,l,"chordHeightAbove",c.specialY.chordLines.above),c.specialY.endingHeightAbove&&(c.specialY.chordHeightAbove?c.top+=2:c.top+=c.specialY.endingHeightAbove+a,l.endingHeightAbove=c.top),c.specialY.dynamicHeightAbove&&c.specialY.volumeHeightAbove?(c.top+=Math.max(c.specialY.dynamicHeightAbove,c.specialY.volumeHeightAbove)+a,l.dynamicHeightAbove=c.top,l.volumeHeightAbove=c.top):(i(c,l,"dynamicHeightAbove"),i(c,l,"volumeHeightAbove")),i(c,l,"partHeightAbove"),i(c,l,"tempoHeightAbove"),c.specialY.lyricHeightBelow&&(c.specialY.lyricHeightBelow+=e.spacing.vocal/n.STEP,l.lyricHeightBelow=c.bottom,c.bottom-=c.specialY.lyricHeightBelow+a),c.specialY.chordHeightBelow){l.chordHeightBelow=c.bottom;var h=c.specialY.chordHeightBelow;c.specialY.chordLines.below&&(h*=c.specialY.chordLines.below),c.bottom-=h+a}c.specialY.volumeHeightBelow&&c.specialY.dynamicHeightBelow?(l.volumeHeightBelow=c.bottom,l.dynamicHeightBelow=c.bottom,c.bottom-=Math.max(c.specialY.volumeHeightBelow,c.specialY.dynamicHeightBelow)+a):c.specialY.volumeHeightBelow?(l.volumeHeightBelow=c.bottom,c.bottom-=c.specialY.volumeHeightBelow+a):c.specialY.dynamicHeightBelow&&(l.dynamicHeightBelow=c.bottom,c.bottom-=c.specialY.dynamicHeightBelow+a),e.showDebug&&e.showDebug.indexOf("box")>=0&&(c.positionY=l);for(var u=0;u0&&(c.top+=p)}c.top+=e.spacing.staffTopMargin/n.STEP,r=2-c.bottom}}},735:function(e,t,r){var n=r(937);function a(e){for(var t=0;t0?0:5e-7)}e.exports=function(e,t,r,s,o){var c,l=0,h=1e3,u=o;s.startx=u;var d,f,p=0;for(r&&console.log("init layout",e),c=0;c1e-7?v.push(s.voices[c]):g.push(s.voices[c]);m=0;var b=0;for(c=0;cu&&(u=n.getNextX(g[c]),m=n.getSpacingUnits(g[c]),b=g[c].spacingduration);l+=m,h=Math.min(h,m),r&&console.log("currentduration: ",p,l,h);var y=void 0;for(c=0;c0){u=w;for(var T=0;Tu&&(u=n.getNextX(s.voices[c]),m=n.getSpacingUnits(s.voices[c]));return function(e){for(var t=0,r=0;r0){var a=n.children.length-1,i=n.children[a];if("bar"===i.abcelem.el_type){var s=i.children[0].x;s>t?t=s:i.children[0].x=t}}}}(s.voices),l+=m,s.setWidth(u),{spacingUnits:l,minSpace:h}}},1277:function(e){e.exports=function(e){for(var t=[],r=0;r=r}(e);if(e.up=i,e.startNote=i?Math.max(e.anchor1.parent.top,9)+4:Math.min(e.anchor1.parent.bottom,0)-2,e.endNote=i?Math.max(e.anchor2.parent.top,9)+4:Math.min(e.anchor2.parent.bottom,0)-2,"rest"===e.anchor1.parent.type&&"rest"!==e.anchor2.parent.type?e.startNote=e.endNote:"rest"===e.anchor2.parent.type&&"rest"!==e.anchor1.parent.type&&(e.endNote=e.startNote),i){for(var s=0,o=0;oe.startNote||s>e.endNote)&&(e.startNote=s+3,e.endNote=s+3)}else{var c=0;for(o=0;o=e.children.length},t.getNextX=function(e){return Math.max(e.minx,e.nextx)},t.getSpacingUnits=function(e){return Math.sqrt(8*e.spacingduration)},t.layoutOneItem=function(e,t,r,n,a){var i=r.children[r.i];if(!i)return 0;var s=e-r.minx,o=r.durationindex+i.duration>0?n:0;if("note"===i.abcelem.el_type&&!i.abcelem.rest&&0!==r.voicenumber&&a){var c=a.children[a.i],l=c&&(i.abcelem.maxpitch<=c.abcelem.maxpitch+1&&i.abcelem.maxpitch>=c.abcelem.minpitch-1||i.abcelem.minpitch<=c.abcelem.maxpitch+1&&i.abcelem.minpitch>=c.abcelem.minpitch-1);if(l&&i.abcelem.minpitch===c.abcelem.minpitch&&i.abcelem.maxpitch===c.abcelem.maxpitch&&c.heads&&c.heads.length>0&&i.heads&&i.heads.length>0&&c.heads[0].c===i.heads[0].c&&(l=!1),l){var h=c.heads&&c.heads.length>0?c.heads[0].realWidth:c.fixed.w;i.adjustedWidth||(i.adjustedWidth=h+i.w),i.w=i.adjustedWidth;for(var u=0;u0&&t.putChordInLane(n),void(e[n]=r.right);e.push(r.right),t.putChordInLane(e.length-1)}}function c(e){for(var t=0,r=0;r=0;r--)(n=e[t].children[r]).chordHeightBelow&&o(i,n)}return(a.length>1||i.length>1)&&function(e,t,r){for(var n=0;n=0};i.prototype.reset=function(){this.paper.clear(),this.y=0,this.abctune=null,this.path=null,this.isPrint=!1,this.lineThickness=0,this.initVerticalSpace()},i.prototype.newTune=function(e){this.abctune=e,this.setVerticalSpace(e.formatting),this.isPrint="print"===e.media,this.setPadding(e)},i.prototype.setLineThickness=function(e){this.lineThickness=e},i.prototype.setPaddingOverride=function(e){this.paddingOverride={top:e.paddingtop,bottom:e.paddingbottom,right:e.paddingright,left:e.paddingleft}},i.prototype.setPadding=function(e){function t(t,r,n,a,i){void 0!==e.formatting[n]?t.padding[r]=e.formatting[n]:void 0!==t.paddingOverride[r]?t.padding[r]=t.paddingOverride[r]:t.isPrint?t.padding[r]=a:t.padding[r]=i}t(this,"top","topmargin",38,15),t(this,"bottom","botmargin",38,15),t(this,"left","leftmargin",68,15),t(this,"right","rightmargin",68,15)},i.prototype.adjustNonScaledItems=function(e){this.padding.top/=e,this.padding.bottom/=e,this.padding.left/=e,this.padding.right/=e,this.abctune.formatting.headerfont.size/=e,this.abctune.formatting.footerfont.size/=e},i.prototype.initVerticalSpace=function(){this.spacing={composer:7.56,graceBefore:8.67,graceInside:10.67,graceAfter:16,info:0,lineSkipFactor:1.1,music:7.56,paragraphSkipFactor:.4,parts:11.33,slurHeight:1,staffSeparation:61.33,staffTopMargin:0,stemHeight:36.67,subtitle:3.78,systemStaffSeparation:48,text:18.9,title:7.56,top:30.24,vocal:0,words:0}},i.prototype.setVerticalSpace=function(e){void 0!==e.staffsep&&(this.spacing.staffSeparation=4*e.staffsep/3),void 0!==e.composerspace&&(this.spacing.composer=4*e.composerspace/3),void 0!==e.partsspace&&(this.spacing.parts=4*e.partsspace/3),void 0!==e.textspace&&(this.spacing.text=4*e.textspace/3),void 0!==e.musicspace&&(this.spacing.music=4*e.musicspace/3),void 0!==e.titlespace&&(this.spacing.title=4*e.titlespace/3),void 0!==e.sysstaffsep&&(this.spacing.systemStaffSeparation=4*e.sysstaffsep/3),void 0!==e.stafftopmargin&&(this.spacing.staffTopMargin=4*e.stafftopmargin/3),void 0!==e.subtitlespace&&(this.spacing.subtitle=4*e.subtitlespace/3),void 0!==e.topspace&&(this.spacing.top=4*e.topspace/3),void 0!==e.vocalspace&&(this.spacing.vocal=4*e.vocalspace/3),void 0!==e.wordsspace&&(this.spacing.words=4*e.wordsspace/3)},i.prototype.calcY=function(e){return this.y-e*n.STEP},i.prototype.yToPitch=function(e){return e/n.STEP},i.prototype.moveY=function(e,t){void 0===t&&(t=1),this.y+=e*t},i.prototype.absolutemoveY=function(e){this.y=e},e.exports=i},4101:function(e){var t="http://www.w3.org/2000/svg";function r(e){this.svg=s(),this.currentGroup=[],e.appendChild(this.svg)}function n(e,t,r){var n=r-e;return"M "+e+" "+t+" l "+n+" 0 l 0 1 l "+-n+" 0 z "}function a(e,t,r){var n=r-t;return"M "+e+" "+t+" l 0 "+n+" l 1 0 l 0 "+-n+" z "}r.prototype.clear=function(){if(this.svg){var e=this.svg.parentNode;this.svg=s(),this.currentGroup=[],e&&(e.innerHTML="",e.appendChild(this.svg))}},r.prototype.setTitle=function(e){var t=document.createElement("title"),r=document.createTextNode(e);t.appendChild(r),this.svg.insertBefore(t,this.svg.firstChild)},r.prototype.setResponsiveWidth=function(e,t){if(this.svg.setAttribute("viewBox","0 0 "+e+" "+t),this.svg.setAttribute("preserveAspectRatio","xMinYMin meet"),this.svg.removeAttribute("height"),this.svg.removeAttribute("width"),this.svg.style.display="inline-block",this.svg.style.position="absolute",this.svg.style.top="0",this.svg.style.left="0",this.svg.parentNode){var r=this.svg.parentNode.getAttribute("class");r?r.indexOf("abcjs-container")<0&&this.svg.parentNode.setAttribute("class",r+" abcjs-container"):this.svg.parentNode.setAttribute("class","abcjs-container"),this.svg.parentNode.style.display="inline-block",this.svg.parentNode.style.position="relative",this.svg.parentNode.style.width="100%";var n=t/e*100;this.svg.parentNode.style["padding-bottom"]=n+"%",this.svg.parentNode.style["vertical-align"]="middle",this.svg.parentNode.style.overflow="hidden"}},r.prototype.setSize=function(e,t){this.svg.setAttribute("width",e),this.svg.setAttribute("height",t)},r.prototype.setAttribute=function(e,t){this.svg.setAttribute(e,t)},r.prototype.setScale=function(e){1!==e?(this.svg.style.transform="scale("+e+","+e+")",this.svg.style["-ms-transform"]="scale("+e+","+e+")",this.svg.style["-webkit-transform"]="scale("+e+","+e+")",this.svg.style["transform-origin"]="0 0",this.svg.style["-ms-transform-origin-x"]="0",this.svg.style["-ms-transform-origin-y"]="0",this.svg.style["-webkit-transform-origin-x"]="0",this.svg.style["-webkit-transform-origin-y"]="0"):(this.svg.style.transform="",this.svg.style["-ms-transform"]="",this.svg.style["-webkit-transform"]="")},r.prototype.insertStyles=function(e){var r=document.createElementNS(t,"style");r.textContent=e,this.svg.insertBefore(r,this.svg.firstChild)},r.prototype.setParentStyles=function(e){for(var t in e)e.hasOwnProperty(t)&&this.svg.parentNode&&(this.svg.parentNode.style[t]=e[t]);this.dummySvg&&(document.querySelector("body").removeChild(this.dummySvg),this.dummySvg=null)},r.prototype.rect=function(e){var t=[],r=e.x,i=e.y,s=e.x+e.width,o=e.y+e.height;return t.push(n(r,i,s)),t.push(n(r,o,s)),t.push(a(s,i,o)),t.push(a(r,o,i)),this.path({path:t.join(" "),stroke:"none","data-name":e["data-name"]})},r.prototype.dottedLine=function(e){var r=document.createElementNS(t,"line");r.setAttribute("x1",e.x1),r.setAttribute("x2",e.x2),r.setAttribute("y1",e.y1),r.setAttribute("y2",e.y2),r.setAttribute("stroke",e.stroke),r.setAttribute("stroke-dasharray","5,5"),this.svg.insertBefore(r,this.svg.firstChild)},r.prototype.rectBeneath=function(e){var r=document.createElementNS(t,"rect");r.setAttribute("x",e.x),r.setAttribute("width",e.width),r.setAttribute("y",e.y),r.setAttribute("height",e.height),e.stroke&&r.setAttribute("stroke",e.stroke),e["stroke-opacity"]&&r.setAttribute("stroke-opacity",e["stroke-opacity"]),e.fill&&r.setAttribute("fill",e.fill),e["fill-opacity"]&&r.setAttribute("fill-opacity",e["fill-opacity"]),this.svg.insertBefore(r,this.svg.firstChild)},r.prototype.text=function(e,r,n,a){var i=document.createElementNS(t,"text");for(var s in i.setAttribute("stroke","none"),r)r.hasOwnProperty(s)&&i.setAttribute(s,r[s]);for(var o="free-text"==r["data-name"],c=(""+e).split("\n"),l=0;l0?this.currentGroup[0].removeChild(r):this.svg.removeChild(r)),n&&(i[n]=a),a},r.prototype.openGroup=function(e){e=e||{};var r=document.createElementNS(t,"g");return e.klass&&r.setAttribute("class",e.klass),e.fill&&r.setAttribute("fill",e.fill),e.stroke&&r.setAttribute("stroke",e.stroke),e["data-name"]&&r.setAttribute("data-name",e["data-name"]),e.prepend?this.prepend(r):this.append(r),this.currentGroup.unshift(r),r},r.prototype.closeGroup=function(){var e=this.currentGroup.shift();return e&&0===e.children.length?(e.parentElement.removeChild(e),null):e},r.prototype.path=function(e){var r=document.createElementNS(t,"path");for(var n in e)e.hasOwnProperty(n)&&("path"===n?r.setAttributeNS(null,"d",e.path):"klass"===n?r.setAttributeNS(null,"class",e[n]):void 0!==e[n]&&r.setAttributeNS(null,n,e[n]));return this.append(r),r},r.prototype.pathToBack=function(e){var r=document.createElementNS(t,"path");for(var n in e)e.hasOwnProperty(n)&&("path"===n?r.setAttributeNS(null,"d",e.path):"klass"===n?r.setAttributeNS(null,"class",e[n]):r.setAttributeNS(null,n,e[n]));return this.prepend(r),r},r.prototype.lineToBack=function(e){for(var r=document.createElementNS(t,"line"),n=Object.keys(e),a=0;a0?this.currentGroup[0].appendChild(e):this.svg.appendChild(e)},r.prototype.prepend=function(e){this.currentGroup.length>0?this.currentGroup[0].appendChild(e):this.svg.insertBefore(e,this.svg.firstChild)},r.prototype.setAttributeOnElement=function(e,t){for(var r in t)t.hasOwnProperty(r)&&e.setAttributeNS(null,r,t[r])},r.prototype.moveElementToChild=function(e,t){e.appendChild(t)},e.exports=r},1185:function(e){e.exports="6.6.3"}},t={},r=function r(n){var a=t[n];if(void 0!==a)return a.exports;var i=t[n]={exports:{}};return e[n](i,i.exports,r),i.exports}(1045),r;var e,t,r}));
\ No newline at end of file
diff --git a/render_assets/manifest.json b/render_assets/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..ff9402148875916335176c1c3d1bdb3b07a66353
--- /dev/null
+++ b/render_assets/manifest.json
@@ -0,0 +1,106 @@
+{
+ "abcjs_version": "6.6.3",
+ "abcjs_revision": "5dc285c5a4a918d33f2ab4b3c381499847e88426",
+ "piano_revision": "cbd6b6f6d1af89ebfb69402860741288f08ff8b7",
+ "piano_source": "https://github.com/paulrosen/midi-js-soundfonts/tree/cbd6b6f6d1af89ebfb69402860741288f08ff8b7/FluidR3_GM/acoustic_grand_piano-mp3",
+ "files": {
+ "DejaVuSans.ttf": "7da195a74c55bef988d0d48f9508bd5d849425c1770dba5d7bfc6ce9ed848954",
+ "LICENSE.abcjs": "ab1072f859916529e284a8f43643146de9935041bb347a35559a96c87ce5566f",
+ "LICENSE.font": "7a083b136e64d064794c3419751e5c7dd10d2f64c108fe5ba161eae5e5958a93",
+ "abcjs-basic-min.js": "abdab74cf95c39fb9ff4ae0c96735b9c35222851f0844ce471ddd4354739bc75",
+ "renderer.js": "0756398dfe69fe1c54c8779252885fbf573d02eecbe3f2389a0b81e0f43fcd09",
+ "soundfonts/ATTRIBUTION.md": "87237050093c1d4f47f5c459f1e18b57ea6710f92d1a57b4462f8f5aa31e11be",
+ "soundfonts/acoustic_grand_piano-mp3/A0.mp3": "5c47e8142b04505c888b8edd7246a8881b4b30c4340682f0264191599df232fc",
+ "soundfonts/acoustic_grand_piano-mp3/A1.mp3": "abab379d4d0557abb93856f816b7bb395872e656533c3fe7f02048fc993834b7",
+ "soundfonts/acoustic_grand_piano-mp3/A2.mp3": "e21d9ab9b30c73c0aea921c2cd0dd59fece4cc021619927c9ff3dee71f0a9679",
+ "soundfonts/acoustic_grand_piano-mp3/A3.mp3": "02dee2fa184b9b94a5f72886c2370bd076103ebb76fc179982fa503e549dcc10",
+ "soundfonts/acoustic_grand_piano-mp3/A4.mp3": "4f1671ac831bc41c845e67b18e6c9ea200fc9b753bdffe124703b7141aa928c3",
+ "soundfonts/acoustic_grand_piano-mp3/A5.mp3": "2cccfe0aa8077bfd20bcbba470012faa0328ae97d5bd2297147c01f2b255e574",
+ "soundfonts/acoustic_grand_piano-mp3/A6.mp3": "87dab2b5bfdd8bafa575fc24b99591e87e5a0c991a8bbdc8aeb8a1e517b576d2",
+ "soundfonts/acoustic_grand_piano-mp3/A7.mp3": "ace76663dac4610a93259655f069835e6eb397b76aedf85c1f48a5036cf25687",
+ "soundfonts/acoustic_grand_piano-mp3/Ab1.mp3": "a9889722caea82aa67a8b7f600f1def8e51e095f1f423949c3a8e9dbf5c14abd",
+ "soundfonts/acoustic_grand_piano-mp3/Ab2.mp3": "b45bb57e125cdbbc3bb521ae643e67a90c0ff646d37e5b5284e5c70bfeff6c75",
+ "soundfonts/acoustic_grand_piano-mp3/Ab3.mp3": "843d633061631861c943266b4836573fe8ba8b8a10df9bd94adc380376cdb8ce",
+ "soundfonts/acoustic_grand_piano-mp3/Ab4.mp3": "e8612f8602d1c146f9b7f69a441ee518876ccdfa469457366c8064c25d9d3116",
+ "soundfonts/acoustic_grand_piano-mp3/Ab5.mp3": "31c86a311eb68761b8008359b34addfeec249d1c7d6795a52f8f2e172e3e8a05",
+ "soundfonts/acoustic_grand_piano-mp3/Ab6.mp3": "7775c09d08d344af9528edbf7abb6f59ac600e4d15efd6f139845bb107a822ed",
+ "soundfonts/acoustic_grand_piano-mp3/Ab7.mp3": "66932305995ff04e9e0244c5a408ec87c9131ab3915eac15c72c7659132ab0f2",
+ "soundfonts/acoustic_grand_piano-mp3/B0.mp3": "4422d1d2f0498aec427165415f5d665d78699e8623475215ce9b27c18b3d331f",
+ "soundfonts/acoustic_grand_piano-mp3/B1.mp3": "048003df58298d2659bff084885ad69d9531fdc03837c7ee39c8bcf68334b5b9",
+ "soundfonts/acoustic_grand_piano-mp3/B2.mp3": "be2df872b00b68adead4ca24ef68bd4a93bf0c4864d455d742171e5df91e86b7",
+ "soundfonts/acoustic_grand_piano-mp3/B3.mp3": "82c0463af9c9fe51d44aa59b71d2dccefab11c1ea98e2748ee93244f8af9169e",
+ "soundfonts/acoustic_grand_piano-mp3/B4.mp3": "5f09187820410b0f78575f232c3509f367478b7755b6eb5aa46ce6cf4c16a0e8",
+ "soundfonts/acoustic_grand_piano-mp3/B5.mp3": "4e0932827d3e9390080c4a9acd420e9b83871a5f73723feaf61d2d7f6b628b01",
+ "soundfonts/acoustic_grand_piano-mp3/B6.mp3": "d31aa62152acd598d55fb02cc024e59e0176a7dc13d27813882f299cae7baf7a",
+ "soundfonts/acoustic_grand_piano-mp3/B7.mp3": "6e1aad1cc92f17ebf6fe6161766cce279250e67a30569649481c77ab80db1677",
+ "soundfonts/acoustic_grand_piano-mp3/Bb0.mp3": "e4734f8d0285f0e5a39db7f61023510b46fb1570743b20d287479dcaa5536adb",
+ "soundfonts/acoustic_grand_piano-mp3/Bb1.mp3": "6d4face1b6559a08af3f9a49fcbc472b43e974e072156b31850ab9a230713aa3",
+ "soundfonts/acoustic_grand_piano-mp3/Bb2.mp3": "78cc11431a2a2d52294f0c714c59107a886f637806ae2cba63d50ff53585b93e",
+ "soundfonts/acoustic_grand_piano-mp3/Bb3.mp3": "6ac0f67f18e15db35553d4fcd83ceec4473ebe639e31659ce93cf8466cb06644",
+ "soundfonts/acoustic_grand_piano-mp3/Bb4.mp3": "d8f729899595116cb62ca9b7b801b4dfb9322bdf50d1bbfa847010e43a39f8f5",
+ "soundfonts/acoustic_grand_piano-mp3/Bb5.mp3": "51224d4c26f1c89b08e43865ef55d3638367f7f40fbc92e4ab88610b54d4301d",
+ "soundfonts/acoustic_grand_piano-mp3/Bb6.mp3": "394488d6cbd2c58b40f202e5acc238abdb73f261bcebd7105fb81d091113c4aa",
+ "soundfonts/acoustic_grand_piano-mp3/Bb7.mp3": "fe72a195f6036722e587e1597e54d66ec70e0ce11768e74e39bb2319bf256f80",
+ "soundfonts/acoustic_grand_piano-mp3/C1.mp3": "eb3b985e3b692cfe0c48550a26843fc3da44c7630c0b7bcfd820e87c5122cccc",
+ "soundfonts/acoustic_grand_piano-mp3/C2.mp3": "e02c5a80c03871e1889a3bb371d853f92294c1e8538ac06f70df0fd045540207",
+ "soundfonts/acoustic_grand_piano-mp3/C3.mp3": "7d0409ea3c40af25c6d3d606f12890f88f896d93c381b6eb8c58497f308b30d3",
+ "soundfonts/acoustic_grand_piano-mp3/C4.mp3": "b6d32e0d9d0c2e4b88165a2ec27b8787a970f131a03d96d065f28f158640af96",
+ "soundfonts/acoustic_grand_piano-mp3/C5.mp3": "7a4be6dae323ac5564ad1a617b554fd595e5e0a63f19c3581e3ee94c33ae2e4b",
+ "soundfonts/acoustic_grand_piano-mp3/C6.mp3": "6a0030c071ccd6c9a722455851d65b18cb86cbfe515772a6ded4c747eda339a0",
+ "soundfonts/acoustic_grand_piano-mp3/C7.mp3": "5e4cadc06eb25e2302dcc5bf1af76fe85ed258759d02c46e9169376ade83845e",
+ "soundfonts/acoustic_grand_piano-mp3/C8.mp3": "4ec066cab9362f6706b90fedc3249fa3c221769ee95ff52d7e46aaaf0a01fd16",
+ "soundfonts/acoustic_grand_piano-mp3/D1.mp3": "2389562397c7bdf2a856965b8b371eb6311be3cad2875c71d8ff4a607a242cea",
+ "soundfonts/acoustic_grand_piano-mp3/D2.mp3": "6233cc6f3670a287e33c7b8ece3b03a787ee1e169c8355432ce0b5e194bee612",
+ "soundfonts/acoustic_grand_piano-mp3/D3.mp3": "b8a5ed9cf147e10f484f3ad39930130d487b12045a35cef73f48dab401e6583b",
+ "soundfonts/acoustic_grand_piano-mp3/D4.mp3": "90539782bdbbbe714de579bfbbe6f478e07e112c6f78d03e8ee82462fbf2a569",
+ "soundfonts/acoustic_grand_piano-mp3/D5.mp3": "b25b5e541807af700f4e2272ac4c55a2f9b5cc8f176fcd9961d4456803af2b87",
+ "soundfonts/acoustic_grand_piano-mp3/D6.mp3": "46317b9dca22a75986ecfeba93ef34c8f7bc6d4e2e0223dc64b13c9a1984110b",
+ "soundfonts/acoustic_grand_piano-mp3/D7.mp3": "d7db5e63d7caa2fc62e00f55366f9a5236b3a13f7eb0d141a9823e10c7cc4209",
+ "soundfonts/acoustic_grand_piano-mp3/Db1.mp3": "84f2ba7505c515c39797eafe36295b15ee9da8adbafcfbfbfc7685532b4bdce4",
+ "soundfonts/acoustic_grand_piano-mp3/Db2.mp3": "c5d76b97326a189786aba25685c8789f59d4dc41d5fbd4d6a6aa97986b2b7d1d",
+ "soundfonts/acoustic_grand_piano-mp3/Db3.mp3": "c5bd77b92ad8411c47e509643a7d6fc9c74f3aaba2290e2f4aed6a2ba1446e88",
+ "soundfonts/acoustic_grand_piano-mp3/Db4.mp3": "f49892c4e935296db1cb785000818e6a04bf0756c4443bffa6245144fdaf3e47",
+ "soundfonts/acoustic_grand_piano-mp3/Db5.mp3": "924a70321fe728ff7a69c941029e060f529e6ada507f20357ee74039e270e588",
+ "soundfonts/acoustic_grand_piano-mp3/Db6.mp3": "5e4b55cc27db80e899ea817c546f4c0cf15b43267f426f8de53baac770f90be7",
+ "soundfonts/acoustic_grand_piano-mp3/Db7.mp3": "56c93ba30fa46db98590fd4c218fec88e87b02eaa0b177bcc0ad621fd9a506da",
+ "soundfonts/acoustic_grand_piano-mp3/Db8.mp3": "1be0e2193b729fc32a4fbb82f9f46bb4c0b2cbb67027c0f07c8eeff6460b64cf",
+ "soundfonts/acoustic_grand_piano-mp3/E1.mp3": "740bf03f7ac2a9bbd41ae10f7daf876bc8bde6fe6d355f2f2781ae70d95c762a",
+ "soundfonts/acoustic_grand_piano-mp3/E2.mp3": "316b392d51d9c27334976511643112b088a9812a8e37e598957e8acc6725aad0",
+ "soundfonts/acoustic_grand_piano-mp3/E3.mp3": "5e556a4850b93b1adba6655372c9e58913169864c52a8e6f232f4b1e901ca2dc",
+ "soundfonts/acoustic_grand_piano-mp3/E4.mp3": "48bcecf53832988af83f74aa65efd7daa34f1e3ebf10de4679363b89d54f3852",
+ "soundfonts/acoustic_grand_piano-mp3/E5.mp3": "c48cee1a11528da53e1f780a1ed36558530f053b5ed8c7033709b2002b452513",
+ "soundfonts/acoustic_grand_piano-mp3/E6.mp3": "e4de8378854d6b39b40d4cf7b022f60bda097993b492639c8a0a005f02a010e0",
+ "soundfonts/acoustic_grand_piano-mp3/E7.mp3": "e757f51d2fb54dca2c0adb4ee6a2b86459a52e2f7dfa227529558a6a0f28fd07",
+ "soundfonts/acoustic_grand_piano-mp3/Eb1.mp3": "694e89cd30008f47411336ea1b76675503bbc5383c3849df676b2cf6b51eee67",
+ "soundfonts/acoustic_grand_piano-mp3/Eb2.mp3": "708ff787e5ae2bfa45fe386e5fadc90a9339817dd245ed6e6f62532e42fad8d2",
+ "soundfonts/acoustic_grand_piano-mp3/Eb3.mp3": "bec9944789ef9e7ec3dd7f9fcdf63ed1e5e82e29447c81be3578df9b1269b4b6",
+ "soundfonts/acoustic_grand_piano-mp3/Eb4.mp3": "96586e2865651cfa0469e762f3bd40821f6e45eecf4761a86a37bc59cfebfb2c",
+ "soundfonts/acoustic_grand_piano-mp3/Eb5.mp3": "aad99b852c20c2aee66143dd103513523566716cb2f4c6ef6ac478ef84f282b0",
+ "soundfonts/acoustic_grand_piano-mp3/Eb6.mp3": "c0f4c45d7930ed90edebfd93321807cdb7346e364ba1933867c4b49ba18d5368",
+ "soundfonts/acoustic_grand_piano-mp3/Eb7.mp3": "c23b9b43ca8ece152e59ef04832b2290e96db54f9539af3162df4054c23271c7",
+ "soundfonts/acoustic_grand_piano-mp3/F1.mp3": "6b83be26ca990ee91e4d404afd5756e5d63b7a6f9ac46da068c80b2692945dba",
+ "soundfonts/acoustic_grand_piano-mp3/F2.mp3": "7bb9c26ec86e1d32194ab5ced3805e1026e638a4a5481d556a7e3f2860141c46",
+ "soundfonts/acoustic_grand_piano-mp3/F3.mp3": "ff912c3211f80dc0c4f541ebd39c1db11f21cfda684d54999f9bce87aaf0cf20",
+ "soundfonts/acoustic_grand_piano-mp3/F4.mp3": "a7426425ed95ea8652a888d850c34c4b766c7c7a11c168ed6899c38a6836be0c",
+ "soundfonts/acoustic_grand_piano-mp3/F5.mp3": "d7f046daec924ce8607ec92360e0d0e42e29e4b3a108839179e04aba4adadc51",
+ "soundfonts/acoustic_grand_piano-mp3/F6.mp3": "6f7794d298d1036ebbd514ad1b3605ffbd280556925fba05e29b83052cbf3dfd",
+ "soundfonts/acoustic_grand_piano-mp3/F7.mp3": "f49b2b54f0b913f636b5822eae373a85dbb30ec5e9a6ebc18e498fdfe0547011",
+ "soundfonts/acoustic_grand_piano-mp3/G1.mp3": "d187e74ca3a23d08248ea4073abc0893a758c8b15c4b6af16c3ba1d2394830db",
+ "soundfonts/acoustic_grand_piano-mp3/G2.mp3": "034f90b9f81e5aca0012f1ff73d731308653578a7eb4bee30efca37af5afce77",
+ "soundfonts/acoustic_grand_piano-mp3/G3.mp3": "3b75e4dfd657723b3c7a30bca0409dce8013b4083d54e703c8ec513b535cee99",
+ "soundfonts/acoustic_grand_piano-mp3/G4.mp3": "37d21e13d5cecc8c0f1645c83bfe82f6032407a3b7de3dde14021c196526664e",
+ "soundfonts/acoustic_grand_piano-mp3/G5.mp3": "2eb949282bc676f3dc96bf5008459b5fff02696e63b70ff799ff08c36bb97058",
+ "soundfonts/acoustic_grand_piano-mp3/G6.mp3": "c24ccf8d6f0c6c9818fe244d150a42c42585629db413e6b245088fdb589e9f3d",
+ "soundfonts/acoustic_grand_piano-mp3/G7.mp3": "7cc4630a06cbc11438fac85c00b640cc7677be8332105204632b687d7127ebc4",
+ "soundfonts/acoustic_grand_piano-mp3/Gb1.mp3": "6ff97bc16a3020037f46ebe4196ce2602eed8af243b567768d4cb5d47a8ba0bc",
+ "soundfonts/acoustic_grand_piano-mp3/Gb2.mp3": "95ab26a90d088fdc938673005ba2b2a4b166ee188ffbad0c7f75fb746334b043",
+ "soundfonts/acoustic_grand_piano-mp3/Gb3.mp3": "91605f55bf0265c1e526b966a3901f9c2813473ac890f4b7330ee08874c5d30f",
+ "soundfonts/acoustic_grand_piano-mp3/Gb4.mp3": "f0db7ee32b9175907ca60cf53e6c7a129ea8e7c8f6562dc7ebe6e5c81dcbdf99",
+ "soundfonts/acoustic_grand_piano-mp3/Gb5.mp3": "bd1d81203f5338ab62372f88c0b4eb5b4a3133ef58518a8770f3dfdf34f74124",
+ "soundfonts/acoustic_grand_piano-mp3/Gb6.mp3": "c5f87e3bf666919bd1e230f6945ee4af752761faa54dc1814f854e8e9d8a7920",
+ "soundfonts/acoustic_grand_piano-mp3/Gb7.mp3": "8615b92d93fa16b4051bdc8800a6430642bdcff17e26d020ec5fd83bf89db759"
+ },
+ "font_version": "DejaVu Sans 2.37",
+ "font_license_source": "https://raw.githubusercontent.com/dejavu-fonts/dejavu-fonts/version_2_37/LICENSE",
+ "abcjs_git_blob": "a56bbfc0d004c9b14ea17ad4fd6b52a61b74d255"
+}
diff --git a/render_assets/renderer.js b/render_assets/renderer.js
new file mode 100644
index 0000000000000000000000000000000000000000..2e7afb9e86af1717af03a93eb9542356b0aef489
--- /dev/null
+++ b/render_assets/renderer.js
@@ -0,0 +1,165 @@
+"use strict";
+
+// These display adaptations leave the saved ABC unchanged.
+function expandRests(abc) {
+ let header = true, voice = null, meter = [4, 4], unit = [1, 8];
+ const meters = new Map();
+ return abc.split(/\r?\n/).map(line => {
+ const field = line.match(/^([A-Za-z]):\s*(.*)$/);
+ if (field) {
+ const [, key, value] = field;
+ if (key === "V") {
+ voice = value.split(/\s/)[0];
+ if (!meters.has(voice)) meters.set(voice, meter);
+ }
+ if (key === "M") {
+ const match = value.match(/^(\d+)\/(\d+)$/);
+ if (match) {
+ const next = match.slice(1).map(Number);
+ if (header || !voice) meter = next;
+ else meters.set(voice, next);
+ }
+ }
+ if (key === "L") unit = value.split("/").map(Number);
+ if (key === "K") header = false;
+ return line;
+ }
+ if (header || line.startsWith("%")) return line;
+ return line.replace(/"[^"\n]*"|\[[^\]\n]*\]|Z(\d*)\|/g, (token, count) => {
+ if (count === undefined) return token;
+ const [n, d] = meters.get(voice) || meter;
+ return (`z${n * unit[1] / (d * unit[0])}|`).repeat(Number(count || 1));
+ });
+ }).join("\n");
+}
+
+function fixSystemKeys(tune) {
+ for (const line of tune.lines) for (const staff of line.staff || []) {
+ for (const voice of staff.voices) {
+ for (let i = 0; i < voice.length && voice[i].el_type !== "note";) {
+ const event = voice[i];
+ if (event.el_type === "key" || event.el_type === "keySignature") {
+ staff.key = {...event, accidentals: event.accidentals.map(a => ({...a}))};
+ delete staff.key.impliedNaturals;
+ voice.splice(i, 1);
+ } else i++;
+ }
+ }
+ }
+}
+
+window.renderScore = async abc => {
+ document.body.innerHTML = '';
+ const fontStyle = document.createElement("style");
+ fontStyle.textContent = '#source text, #source span, #pages text {font-family:SheetSageSans !important}';
+ document.head.append(fontStyle);
+ const source = document.getElementById("source");
+ source.style.cssText = "position:absolute;left:-2000px;width:714px";
+ const tunes = ABCJS.renderAbc(source, expandRests(abc), {
+ staffwidth: 674, paddingtop: 12, paddingbottom: 12, paddingleft: 20, paddingright: 20,
+ oneSvgPerLine: true, print: true, add_classes: true,
+ wrap: {minSpacing: 1.8, maxSpacing: 2.7, preferredMeasuresPerLine: 4},
+ afterParsing: fixSystemKeys,
+ });
+ if (!tunes.length || !tunes.some(t => t.lines.some(l => l.staff?.length)))
+ throw new Error("ABC contains no music to render.");
+ const warnings = tunes.flatMap(t => t.warnings || []);
+ if (warnings.length) throw new Error("Invalid ABC: " + warnings.join("; "));
+ await document.fonts.ready;
+ const ns = "http://www.w3.org/2000/svg";
+ const pages = [];
+ let page, content, used = 0, systems = 0;
+ function addPage() {
+ page = document.createElementNS(ns, "svg");
+ for (const [k, v] of Object.entries({xmlns:ns, width:794, height:1123, viewBox:"0 0 794 1123"}))
+ page.setAttribute(k,v);
+ page.setAttribute("class", "score-page");
+ const fontStyle = document.createElementNS(ns, "style");
+ fontStyle.textContent = '@font-face{font-family:SheetSageSans;src:url(data:font/ttf;base64,' + window.renderFontData + ')}text{font-family:SheetSageSans !important}';
+ page.append(fontStyle);
+ const license = document.createElementNS(ns, "metadata");
+ license.textContent = window.renderFontLicense;
+ page.append(license);
+ const background = document.createElementNS(ns, "rect");
+ for (const [k,v] of Object.entries({width:794,height:1123,fill:"white"})) background.setAttribute(k,v);
+ page.append(background);
+ content = document.createElementNS(ns, "g");
+ content.setAttribute("transform", "translate(40 40)");
+ page.append(content);
+ document.getElementById("pages").append(page);
+ pages.push(page);
+ used = 0;
+ }
+ for (const svg of source.querySelectorAll("svg")) {
+ const box = svg.getBoundingClientRect();
+ if (!(box.width > 0 && box.height > 0)) continue;
+ const scale = Math.min(1, 714 / box.width);
+ const height = box.height * scale;
+ if (height > 1043) throw new Error("A staff system is taller than one page.");
+ if (!page || used + height > 1043) addPage();
+ const group = document.createElementNS(ns, "g");
+ group.setAttribute("transform", `translate(0 ${used}) scale(${scale})`);
+ const clone = svg.cloneNode(true);
+ clone.setAttribute("width",box.width);
+ clone.setAttribute("height",box.height);
+ clone.style.overflow = "visible";
+ group.append(clone);
+ content.append(group);
+ used += height;
+ systems++;
+ }
+ source.remove();
+ if (!pages.length) throw new Error("No score pages were produced.");
+ return {pages:pages.map(p => new XMLSerializer().serializeToString(p)), systems, warnings};
+};
+
+window.renderAudio = async ({tracks, duration}) => {
+ const context = new AudioContext({sampleRate:44100});
+ await context.resume();
+ const sequence = new ABCJS.synth.SynthSequence();
+ for (const track of tracks) {
+ const id = sequence.addTrack();
+ sequence.setInstrument(id, 0);
+ for (const note of track.notes) sequence.tracks[id].push({
+ cmd:"note", instrument:0, pitch:note.pitch, volume:note.velocity,
+ start:note.start, duration:note.end-note.start, gap:0,
+ });
+ }
+ sequence.totalDuration = duration;
+ const synth = new ABCJS.synth.CreateSynth();
+ const loaded = await synth.init({audioContext:context, sequence, millisecondsPerMeasure:1000,
+ options:{soundFontUrl:"https://render.invalid/soundfonts/", fadeLength:35,
+ programOffsets:{acoustic_grand_piano:0}, soundFontVolumeMultiplier:1}});
+ if (loaded.error?.length) throw new Error("Could not load piano samples: " + loaded.error.join(", "));
+ await synth.prime();
+ const buffer = synth.getAudioBuffer();
+ if (!buffer) throw new Error("The renderer returned no audio.");
+ // Simultaneous voices retain their balance; a peak guard avoids PCM clipping.
+ let peak = 0;
+ for (let c=0; c 0 ? 0.98 / peak : 0.7);
+ window.renderedAudio = {buffer, gain};
+ await context.close();
+ return {frames:buffer.length, sample_rate:buffer.sampleRate, channels:buffer.numberOfChannels, gain};
+};
+
+window.audioBlock = ({start, count}) => {
+ const {buffer, gain} = window.renderedAudio;
+ const frames = Math.min(count, buffer.length-start);
+ const bytes = new Uint8Array(frames*buffer.numberOfChannels*2);
+ const view = new DataView(bytes.buffer);
+ for (let c=0; c {
+ window.renderFontData = font;
+ window.renderFontLicense = license;
+ const face = new FontFace('SheetSageSans', 'url(data:font/ttf;base64,' + font + ')');
+ document.fonts.add(await face.load());
+ }""", {"font": base64.b64encode((assets / "DejaVuSans.ttf").read_bytes()).decode("ascii"),
+ "license": (assets / "LICENSE.font").read_text(encoding="utf-8")})
+ info = page.evaluate("renderScore", abc_text)
+ for kind in formats:
+ output["score"][kind] = []
+ if "pdf" in formats:
+ if destination is None:
+ output["score"]["pdf"] = [page.pdf(format="A4", print_background=True, prefer_css_page_size=True)]
+ else:
+ target = destination / "score.pdf"
+ temporary = target.with_suffix(".pdf.partial")
+ page.pdf(path=str(temporary), format="A4", print_background=True, prefer_css_page_size=True)
+ temporary.replace(target)
+ output["score"]["pdf"] = [str(target)]
+ for index, svg in enumerate(info["pages"], 1):
+ if "svg" in formats:
+ if destination is None:
+ output["score"]["svg"].append(svg)
+ else:
+ target = destination / f"score_{index:03}.svg"
+ target.write_text(svg, encoding="utf-8")
+ output["score"]["svg"].append(str(target))
+ if "png" in formats:
+ element = page.locator(".score-page").nth(index-1)
+ if destination is None:
+ output["score"]["png"].append(element.screenshot(type="png"))
+ else:
+ target = destination / f"score_{index:03}.png"
+ element.screenshot(path=str(target))
+ output["score"]["png"].append(str(target))
+ output["warnings"].extend(info["warnings"])
+ if blocked:
+ raise RuntimeError(f"Rendering attempted to load unavailable assets: {', '.join(blocked)}")
+ except BrowserError as exc:
+ raise RuntimeError(str(exc).split("Call log:", 1)[0].strip()) from exc
+ finally:
+ browser.close()
+ # A failed render leaves existing pages intact. Only remove obsolete pages
+ # in the requested formats, within this renderer's numbered-file namespace.
+ if formats:
+ for number, path in previous_pages:
+ if number > len(info["pages"]):
+ path.unlink(missing_ok=True)
+ return output
diff --git a/requirements-render.txt b/requirements-render.txt
new file mode 100644
index 0000000000000000000000000000000000000000..62924031791cb03af9f3dc115d216f8a64a07dc1
--- /dev/null
+++ b/requirements-render.txt
@@ -0,0 +1,3 @@
+playwright==1.58.0
+pretty_midi==0.2.10
+setuptools==78.1.1
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..641a1f9495a5be42a9afe699c89deec5e0508e47
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,11 @@
+torch==2.8.0
+torchaudio==2.8.0
+transformers==4.45.2
+huggingface-hub==0.36.0
+safetensors==0.5.3
+numpy==1.24.3
+scipy==1.13.1
+mir_eval==0.8.2
+pretty_midi==0.2.10
+mido==1.3.3
+setuptools==78.1.1
diff --git a/schema_sheetsage2.py b/schema_sheetsage2.py
new file mode 100644
index 0000000000000000000000000000000000000000..a8b21fcd28098af1bee0894f6de4f81ab32ec007
--- /dev/null
+++ b/schema_sheetsage2.py
@@ -0,0 +1,122 @@
+from __future__ import annotations
+
+from dataclasses import dataclass
+
+
+@dataclass(frozen=True)
+class PromptTaskSpec:
+ name: str
+ sampling_group: str
+ output_field: str
+
+
+@dataclass(frozen=True)
+class TokenBlockSpec:
+ name: str
+ labels: tuple[str, ...]
+ output_field: str | None = None
+
+
+@dataclass(frozen=True)
+class PromptMultitaskSchema:
+ version: str
+ tasks: tuple[PromptTaskSpec, ...]
+ event_field_order: tuple[str, ...]
+ appended_token_blocks: tuple[TokenBlockSpec, ...] = ()
+
+
+V1_TASKS = (
+ PromptTaskSpec("timestamp", "timestamp", "timestamp"),
+ PromptTaskSpec("downbeat_meter", "rhythm", "rhythm"),
+ PromptTaskSpec("structure", "structure", "structure"),
+ PromptTaskSpec("key", "key", "key"),
+ PromptTaskSpec("chord_majmin", "chord", "chord"),
+ PromptTaskSpec("chord_full", "chord", "chord"),
+ PromptTaskSpec("melody_vocal", "melody", "melody"),
+ PromptTaskSpec("melody_full", "melody", "melody"),
+)
+
+
+PROMPT_MULTITASK_SCHEMAS = {
+ "v1": PromptMultitaskSchema(
+ version="v1",
+ tasks=V1_TASKS,
+ event_field_order=(
+ "timestamp",
+ "rhythm",
+ "structure",
+ "key",
+ "chord",
+ "melody",
+ ),
+ ),
+}
+
+
+def _validate_schema(schema):
+ task_names = tuple(task.name for task in schema.tasks)
+ if len(task_names) != len(set(task_names)):
+ raise ValueError(f"Duplicate task name in schema {schema.version!r}")
+ field_names = tuple(schema.event_field_order)
+ if len(field_names) != len(set(field_names)):
+ raise ValueError(f"Duplicate output field in schema {schema.version!r}")
+ missing_fields = {
+ task.output_field for task in schema.tasks if task.output_field not in field_names
+ }
+ if missing_fields:
+ raise ValueError(
+ f"Tasks in schema {schema.version!r} use unknown output fields: "
+ f"{tuple(sorted(missing_fields))}"
+ )
+ block_names = tuple(block.name for block in schema.appended_token_blocks)
+ if len(block_names) != len(set(block_names)):
+ raise ValueError(f"Duplicate token block in schema {schema.version!r}")
+ for block in schema.appended_token_blocks:
+ if not block.labels or len(block.labels) != len(set(block.labels)):
+ raise ValueError(
+ f"Token block {block.name!r} must contain unique labels"
+ )
+
+
+def register_prompt_multitask_schema(schema):
+ """Register a frozen schema version without permitting redefinition."""
+ if not isinstance(schema, PromptMultitaskSchema):
+ raise TypeError("schema must be a PromptMultitaskSchema")
+ _validate_schema(schema)
+ existing = PROMPT_MULTITASK_SCHEMAS.get(schema.version)
+ if existing is not None and existing != schema:
+ raise ValueError(f"Schema {schema.version!r} is already registered")
+ PROMPT_MULTITASK_SCHEMAS[schema.version] = schema
+ return schema
+
+
+def extend_prompt_multitask_schema(
+ base_version,
+ version,
+ tasks=(),
+ event_fields=(),
+ token_blocks=(),
+):
+ """Append tasks and vocabulary blocks while preserving every base token id."""
+ base = get_prompt_multitask_schema(base_version)
+ schema = PromptMultitaskSchema(
+ version=str(version),
+ tasks=base.tasks + tuple(tasks),
+ event_field_order=base.event_field_order + tuple(event_fields),
+ appended_token_blocks=base.appended_token_blocks + tuple(token_blocks),
+ )
+ return register_prompt_multitask_schema(schema)
+
+
+def get_prompt_multitask_schema(version):
+ version = str(version)
+ try:
+ return PROMPT_MULTITASK_SCHEMAS[version]
+ except KeyError as exc:
+ raise ValueError(
+ f"Unknown prompt multitask schema {version!r}; "
+ f"available={tuple(PROMPT_MULTITASK_SCHEMAS)}"
+ ) from exc
+
+
+_validate_schema(PROMPT_MULTITASK_SCHEMAS["v1"])
diff --git a/setup_render.py b/setup_render.py
new file mode 100644
index 0000000000000000000000000000000000000000..fc0d2cd683a94fd1e434f2ece1277a88f66156bf
--- /dev/null
+++ b/setup_render.py
@@ -0,0 +1,37 @@
+"""Install the optional renderer and its matching headless browser."""
+from pathlib import Path
+import argparse
+import subprocess
+import sys
+
+SCRIPT_DIR = Path(__file__).absolute().parent
+sys.path.insert(0, str(SCRIPT_DIR))
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("--with-deps", action="store_true", help="Also install Chromium OS libraries (may require sudo).")
+ args = parser.parse_args()
+ subprocess.run([sys.executable, "-m", "pip", "install", "-r", str(SCRIPT_DIR / "requirements-render.txt")], check=True)
+ command = [sys.executable, "-m", "playwright", "install", "--only-shell", "chromium"]
+ if args.with_deps:
+ command.append("--with-deps")
+ subprocess.run(command, check=True)
+ check = """from playwright.sync_api import sync_playwright
+with sync_playwright() as p:
+ browser = p.chromium.launch(headless=True, args=['--disable-gpu'])
+ context = browser.new_context(offline=True)
+ page = context.new_page()
+ assert page.evaluate('typeof OfflineAudioContext') == 'function'
+ browser.close()
+"""
+ result = subprocess.run([sys.executable, "-c", check], capture_output=True, text=True)
+ if result.returncode:
+ print("Chromium could not start. On Linux, run python setup_render.py --with-deps to install its system libraries.", file=sys.stderr)
+ return 1
+ print("Rendering is ready. Example: python render.py --input output --audio --score pdf")
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/tensors_sheetsage2.py b/tensors_sheetsage2.py
new file mode 100644
index 0000000000000000000000000000000000000000..aff5b3dff79c21fd26e7674cf7421d65aad764c4
--- /dev/null
+++ b/tensors_sheetsage2.py
@@ -0,0 +1,99 @@
+"""Aligned audio features and token predictions, in memory or on disk."""
+import json
+from pathlib import Path
+
+import torch
+from safetensors.torch import save_file
+
+from .generation_sheetsage2 import inference_autocast
+
+
+class WindowTensorWriter:
+ def __init__(self, output_dir, index, window, logits, scores):
+ self.directory = Path(output_dir) / "tensors" / f"window-{index:04d}" if output_dir is not None else None
+ if self.directory is not None:
+ self.directory.mkdir(parents=True, exist_ok=True)
+ self.window = window
+ self.logits, self.scores = logits, scores
+ self.pending, self.files = [], []
+ self.data = {"window_index": index, "token_batches": [], "decoder_batches": []} if self.directory is None else None
+
+ def _store(self, name, payload, group):
+ if self.directory is not None:
+ save_file(payload, str(self.directory / name))
+ self.files.append(name)
+ elif group == "audio":
+ self.data["audio"] = payload
+ else:
+ self.data[group].append(payload)
+
+ def audio_features(self, model, audio, dtype, hidden_states):
+ with inference_autocast(audio.device, dtype):
+ features = model.get_audio_features(audio, output_hidden_states=hidden_states)
+ payload = {}
+ for name, value in features.items():
+ if torch.is_tensor(value):
+ payload[name] = value.detach().cpu().contiguous().clone()
+ elif isinstance(value, tuple):
+ for index, layer in enumerate(value):
+ if torch.is_tensor(layer):
+ payload[f"{name}.{index}"] = layer.detach().cpu().contiguous().clone()
+ memory = features.encoder_last_hidden_state
+ frames = memory.shape[1]
+ valid_seconds = self.window["end"] - self.window["start"]
+ payload["frame_times"] = torch.arange(frames, dtype=torch.float64) / 25 + self.window["start"]
+ payload["feature_attention_mask"] = (torch.arange(frames) / 25 < valid_seconds)[None]
+ self._store("audio.safetensors", payload, "audio")
+ return memory
+
+ def capture(self, position, ids, logits, masked):
+ # Transcription processes one window at a time; keep at most 32 rows.
+ row = {"token_position": torch.tensor(position, dtype=torch.long)}
+ if self.logits:
+ row["logits"] = logits[0].detach().cpu().contiguous()
+ if self.scores:
+ row["scores"] = masked[0].detach().cpu().contiguous()
+ self.pending.append(row)
+ if len(self.pending) == 32:
+ self.flush()
+
+ def flush(self):
+ if not self.pending:
+ return
+ first = int(self.pending[0]["token_position"])
+ name = f"tokens-{first:04d}.safetensors"
+ self._store(name, {key: torch.stack([row[key] for row in self.pending]) for key in self.pending[0]},
+ "token_batches")
+ self.pending.clear()
+
+ def decoder_features(self, model, audio, tokens, dtype, memory=None):
+ with inference_autocast(audio.device, dtype):
+ if memory is None:
+ memory = model.encode(audio)
+ cache = None
+ for start in range(0, len(tokens) - 1, 64):
+ ids = tokens[start:min(start + 64, len(tokens) - 1)][None].to(audio.device)
+ output = model.decoder(
+ input_ids=ids,
+ attention_mask=ids.ne(model.tokenizer.pad_token) if cache is None else None,
+ encoder_hidden_states=memory, encoder_attention_mask=None,
+ past_key_values=cache, use_cache=True, return_dict=True,
+ )
+ cache = output.past_key_values
+ name = f"decoder-{start:04d}.safetensors"
+ self._store(name, {"decoder_hidden_state": output.last_hidden_state.detach().cpu().contiguous(),
+ "input_token_ids": ids.cpu(),
+ "token_positions": torch.arange(start, start + ids.shape[1])},
+ "decoder_batches")
+
+ def finish(self):
+ self.flush()
+ metadata = dict(self.window, frame_rate=25)
+ if self.directory is not None:
+ metadata["files"] = self.files
+ metadata["token_position_convention"] = "logits predict the token at token_position; decoder embeddings represent input_token_ids"
+ if self.directory is not None:
+ (self.directory / "index.json").write_text(json.dumps(metadata, indent=2) + "\n")
+ else:
+ self.data["metadata"] = metadata
+ return self.data
diff --git a/tests/test_spelling.py b/tests/test_spelling.py
new file mode 100644
index 0000000000000000000000000000000000000000..f685747e36a3843df1f67cf558169fec194741b1
--- /dev/null
+++ b/tests/test_spelling.py
@@ -0,0 +1,147 @@
+import copy
+import json
+from pathlib import Path
+from tempfile import TemporaryDirectory
+import unittest
+
+import mir_eval.chord
+import numpy as np
+
+from ..chord_spelling_sheetsage2 import (QUALITIES, correct_chord_rows, correct_chord_spelling,
+ normalize_key_name, spell_chord_tones)
+from ..tokenization_sheetsage2 import SheetSage2Tokenizer as Tokenizer, FULL_CHORD_VOCABULARY
+from ..exports_sheetsage2 import export_result
+from ..notation_sheetsage2 import get_key_accidentals, note_to_abc
+
+
+def example():
+ events = []
+ for i in range(16):
+ values = {'rhythm': {'meter': [4, 4], 'eighth_position': i % 4 * 2},
+ 'melody': [{'pitch': [63, 68, 70, 67][i % 4], 'track': 1,
+ 'end_time': (i + 1) * .5, 'duration_bin': 2, 'duration_steps': 4}]}
+ if i == 0:
+ values.update(key='D#:major', chord='D#:maj/3', structure='verse')
+ if i == 4:
+ values['chord'] = 'G#:maj'
+ if i == 8:
+ values.update(key='G#:minor', chord='D#:min')
+ events.append({'time': i * .5, 'global_subbeat': i * 4, 'values': values})
+ return {'events': events}
+
+
+class SpellingTests(unittest.TestCase):
+ def test_cm_chords_and_inversions(self):
+ for raw, expected in [('D#:maj', 'Eb:maj'), ('G#:maj', 'Ab:maj'),
+ ('A#:7/3', 'Bb:7/3'), ('D#:min7/b7', 'Eb:min7/b7'),
+ ('C:min', 'C:min'), ('N', 'N'), ('X', 'X')]:
+ self.assertEqual(correct_chord_spelling(raw, 'C:minor'), expected)
+
+ def test_chord_tones_keep_pitch_classes(self):
+ naturals = [0, 2, 4, 5, 7, 9, 11]
+ for quality, chroma in QUALITIES.items():
+ offsets = [i for i, value in enumerate(chroma) if value > 0]
+ for root_pc in range(12):
+ for letter in range(7):
+ acc = (root_pc - naturals[letter] + 6) % 12 - 6
+ tones = spell_chord_tones(np.array([letter, acc]), quality)
+ self.assertEqual([(naturals[l] + a) % 12 for l, a in tones],
+ [(root_pc + o) % 12 for o in offsets])
+
+ def test_chromatic_chords_use_key_side_spelling(self):
+ for raw, key, expected in [('C:maj', 'E:major', 'C:maj'), ('A#:maj', 'G:major', 'Bb:maj'),
+ ('D#:maj', 'G:major', 'Eb:maj'), ('G#:7', 'C:major', 'Ab:7'),
+ ('A#:min', 'C:major', 'Bb:min'), ('C#:hdim7', 'G:major', 'C#:hdim7'),
+ ('F:hdim7', 'G#:minor', 'E#:hdim7')]:
+ self.assertEqual(correct_chord_spelling(raw, key), expected)
+
+ def test_local_midpoint_including_boundary_and_missing_keys(self):
+ rows = [[0, 2, 'G#:maj'], [2, 6, 'G#:maj'], [6, 8, 'G#:maj']]
+ keys = [[0, 4, 'C:minor'], [4, 8, 'E:major']]
+ original = copy.deepcopy((rows, keys))
+ self.assertEqual([r[2] for r in correct_chord_rows(rows, keys)], ['Ab:maj', 'Ab:maj', 'G#:maj'])
+ self.assertEqual((rows, keys), original)
+ self.assertEqual(correct_chord_rows(rows, []), rows)
+ self.assertEqual(correct_chord_rows([], keys), [])
+
+ def test_key_labels_keep_ids_and_checkpoint_fingerprint(self):
+ tokenizer = Tokenizer(300, 100)
+ self.assertEqual(tokenizer.vocab_fingerprint, '5ba3325af0344c7f')
+ major = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'F#', 'G', 'Ab', 'A', 'Bb', 'B']
+ minor = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'Bb', 'B']
+ for offset, roots, mode in [(0, major, 'major'), (12, minor, 'minor')]:
+ for i, root in enumerate(roots):
+ label = f'{root}:{mode}'
+ token = tokenizer.key_token_start + offset + i
+ self.assertEqual(tokenizer._decode_field('key', [token], ()), label)
+ self.assertEqual(tokenizer.describe(token), f'')
+ self.assertEqual(tokenizer.key_to_token(label), token)
+ self.assertEqual(normalize_key_name(label), label)
+ self.assertEqual(tokenizer.key_to_token('D#:major'), tokenizer.key_to_token('Eb:major'))
+
+ def test_entire_chord_vocabulary_preserves_sounding_pitches(self):
+ for key in ('C:minor', 'E:major', 'A#:minor', 'C#:major', 'F#:major'):
+ for chord in FULL_CHORD_VOCABULARY:
+ spelled = correct_chord_spelling(chord, key)
+ before = mir_eval.chord.encode(chord)
+ after = mir_eval.chord.encode(spelled)
+ self.assertEqual(before[0], after[0])
+ np.testing.assert_array_equal(before[1], after[1])
+ self.assertEqual(before[2], after[2])
+ if chord != 'N':
+ self.assertEqual(chord.split(':', 1)[1], spelled.split(':', 1)[1])
+
+ def test_nolm_key_relative_pitch_examples(self):
+ state = {}
+ self.assertEqual([note_to_abc(n, get_key_accidentals('Cm'), state) for n in (63, 68, 70, 75)],
+ ['E', 'A', 'B', 'e'])
+ self.assertEqual(note_to_abc(67, get_key_accidentals('G#m'), {}), '^^F')
+ self.assertEqual(note_to_abc(59, get_key_accidentals('Db'), {}), '_C')
+ self.assertEqual(note_to_abc(60, get_key_accidentals('C#m'), {}), '^B,')
+ state = {}
+ self.assertEqual([note_to_abc(n, get_key_accidentals('Cm'), state) for n in (64, 76, 63)],
+ ['=E', 'e', '_E'])
+ self.assertEqual(note_to_abc(63, get_key_accidentals('Cm'), {}), 'E')
+
+ def test_export_uses_corrected_labels_without_mutating_events(self):
+ decoded = example()
+ original = copy.deepcopy(decoded)
+ with TemporaryDirectory() as td:
+ root = Path(td)
+ result = export_result(decoded, Tokenizer(300, 100), root, 8)
+ self.assertIsNone(result['abc_error'])
+ self.assertEqual(decoded, original)
+ self.assertEqual(json.loads((root/'events.json').read_text()), original)
+ self.assertIn('Eb:major', (root/'key.lab').read_text())
+ self.assertNotIn('D#:major', (root/'notation/song_keys.txt').read_text())
+ for path in ['chord.lab', 'notation/song_chords.txt']:
+ text = (root/path).read_text()
+ self.assertIn('Eb:maj/3', text)
+ self.assertIn('Ab:maj', text)
+ self.assertIn('D#:min', text)
+ abc = (root/'score.abc').read_text()
+ self.assertIn('K:Eb', abc)
+ self.assertIn('"Eb/G"', abc)
+ self.assertIn('"Ab"', abc)
+ self.assertIn('K:G#m', abc)
+
+ def test_in_memory_paper_and_melody_only_exports(self):
+ for paper in (False, True):
+ for melody_only in (False, True):
+ decoded = example()
+ original = copy.deepcopy(decoded)
+ result = export_result(decoded, Tokenizer(300, 100), duration=8,
+ paper=paper, melody_only=melody_only)
+ self.assertIsNone(result['abc_error'])
+ payload = result['payload']
+ self.assertEqual(decoded, original)
+ self.assertEqual(payload['events'], original['events'])
+ self.assertIn('Eb:major', payload['labs']['key'])
+ self.assertIn('Eb:maj/3', payload['labs']['chord'])
+ self.assertIn('K:Eb', payload['abc'])
+ self.assertIn('K:G#m', payload['abc'])
+ self.assertEqual('"Eb/G"' in payload['abc'], not melody_only)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tokenization_sheetsage2.py b/tokenization_sheetsage2.py
new file mode 100644
index 0000000000000000000000000000000000000000..65c3d2cc13163b69794b6a1c46aa6934aeced47f
--- /dev/null
+++ b/tokenization_sheetsage2.py
@@ -0,0 +1,735 @@
+import re
+import hashlib
+import json
+
+import mir_eval.chord
+import numpy as np
+
+from .chord_spelling_sheetsage2 import normalize_key_name
+
+from .durations_sheetsage2 import DURATION_TEMPLATES, duration_boundaries
+from .labels_sheetsage2 import STRUCTURE_LABELS
+from .schema_sheetsage2 import get_prompt_multitask_schema
+
+
+PROMPT_ORDER = tuple(task.name for task in get_prompt_multitask_schema("v1").tasks)
+
+
+CHROMATIC_SHARPS = (
+ "C",
+ "C#",
+ "D",
+ "D#",
+ "E",
+ "F",
+ "F#",
+ "G",
+ "G#",
+ "A",
+ "A#",
+ "B",
+)
+
+FULL_CHORD_QUALITIES = (
+ "maj",
+ "min",
+ "dim",
+ "aug",
+ "maj7",
+ "min7",
+ "7",
+ "hdim7",
+ "dim7",
+ "minmaj7",
+ "sus2",
+ "sus4",
+ "sus4(b7)",
+ "maj6",
+ "min6",
+)
+
+FULL_CHORD_INVERSIONS = {
+ "maj": ("/2", "/3", "/5"),
+ "min": ("/2", "/b3", "/5"),
+ "maj7": ("/3", "/5", "/7"),
+ "min7": ("/b3", "/5", "/b7"),
+ "7": ("/3", "/5", "/b7"),
+}
+
+
+def build_full_chord_vocabulary():
+ labels = ["N"]
+ for quality in FULL_CHORD_QUALITIES:
+ inversions = FULL_CHORD_INVERSIONS.get(quality, ())
+ for root in CHROMATIC_SHARPS:
+ for inversion in (*inversions, ""):
+ labels.append(f"{root}:{quality}{inversion}")
+ return tuple(labels)
+
+
+FULL_CHORD_VOCABULARY = build_full_chord_vocabulary()
+
+
+class SheetSage2Tokenizer:
+ """Typed prompt and event vocabulary for prompt-conditioned transcription."""
+
+ meter_numerators = tuple(range(1, 33))
+ meter_denominators = (1, 2, 4, 8, 16, 32)
+ n_eighth_positions = 256
+ max_subbeat_shift = 256
+ prompt_capacity = 256
+
+ def __init__(
+ self,
+ audio_length_seconds=300.0,
+ time_hz=100,
+ schema_version="v1",
+ expected_fingerprint=None,
+ ):
+ self.audio_length_seconds = float(audio_length_seconds)
+ self.time_hz = int(time_hz)
+ self.schema = get_prompt_multitask_schema(schema_version)
+ self.schema_version = self.schema.version
+ self.task_specs = self.schema.tasks
+ self.event_field_order = self.schema.event_field_order
+ self.n_time_tokens = int(round(self.audio_length_seconds * self.time_hz))
+ if self.n_time_tokens <= 0:
+ raise ValueError("audio_length_seconds must produce at least one time token")
+
+ self.pad_token = 0
+ self.sos_token = 1
+ self.bos_token = self.sos_token
+ self.eos_token = 2
+ self.out_token = 3
+
+ self.prompt_token_start = 4
+ self.prompt_names = tuple(task.name for task in self.task_specs)
+ if len(self.prompt_names) > self.prompt_capacity:
+ raise ValueError(
+ f"Schema {self.schema_version} has {len(self.prompt_names)} prompts, "
+ f"exceeding immutable capacity {self.prompt_capacity}"
+ )
+ self.prompt_to_id = {
+ name: self.prompt_token_start + index
+ for index, name in enumerate(self.prompt_names)
+ }
+ self.prompt_token_end = self.prompt_token_start + self.prompt_capacity
+
+ self.subbeat_shift_token_start = self.prompt_token_end
+ self.n_subbeat_shift_tokens = self.max_subbeat_shift + 1
+ self.subbeat_shift_token_end = (
+ self.subbeat_shift_token_start + self.n_subbeat_shift_tokens
+ )
+
+ self.time_token_start = self.subbeat_shift_token_end
+ self.time_token_end = self.time_token_start + self.n_time_tokens
+
+ self.meter_pairs = tuple(
+ (numerator, denominator)
+ for numerator in self.meter_numerators
+ for denominator in self.meter_denominators
+ )
+ self.meter_to_id = {meter: index for index, meter in enumerate(self.meter_pairs)}
+ self.meter_token_start = self.time_token_end
+ self.meter_token_end = self.meter_token_start + len(self.meter_pairs)
+
+ self.eighth_position_token_start = self.meter_token_end
+ self.eighth_position_token_end = (
+ self.eighth_position_token_start + self.n_eighth_positions
+ )
+
+ self.structure_labels = tuple(STRUCTURE_LABELS)
+ self.structure_token_start = self.eighth_position_token_end
+ self.structure_token_end = self.structure_token_start + len(self.structure_labels)
+
+ self.key_token_start = self.structure_token_end
+ self.n_key_tokens = 24
+ self.key_token_end = self.key_token_start + self.n_key_tokens
+
+ self.majmin_chord_labels = (
+ "N",
+ *(f"{root}:maj" for root in CHROMATIC_SHARPS),
+ *(f"{root}:min" for root in CHROMATIC_SHARPS),
+ )
+ self.majmin_chord_token_start = self.key_token_end
+ self.majmin_chord_token_end = (
+ self.majmin_chord_token_start + len(self.majmin_chord_labels)
+ )
+
+ self.full_chord_labels = FULL_CHORD_VOCABULARY
+ self.full_chord_to_id = {
+ label: index for index, label in enumerate(self.full_chord_labels)
+ }
+ self.full_chord_token_start = self.majmin_chord_token_end
+ self.full_chord_token_end = (
+ self.full_chord_token_start + len(self.full_chord_labels)
+ )
+
+ self.pitch_token_start = self.full_chord_token_end
+ self.n_pitch_tokens = 256
+ self.pitch_token_end = self.pitch_token_start + self.n_pitch_tokens
+
+ self.duration_templates = DURATION_TEMPLATES
+ self.duration_boundaries = duration_boundaries
+ self.duration_token_start = self.pitch_token_end
+ self.n_duration_tokens = len(self.duration_templates)
+ self.duration_token_end = self.duration_token_start + self.n_duration_tokens
+ self.appended_token_blocks = {}
+ next_token = self.duration_token_end
+ for block in self.schema.appended_token_blocks:
+ start = next_token
+ end = start + len(block.labels)
+ self.appended_token_blocks[block.name] = {
+ "start": start,
+ "end": end,
+ "labels": tuple(block.labels),
+ "output_field": block.output_field or block.name,
+ }
+ next_token = end
+ self.n_tokens = next_token
+
+ self._full_chord_templates = self._build_full_chord_templates()
+ self.vocab_fingerprint = self._compute_fingerprint()
+ if (
+ expected_fingerprint is not None
+ and str(expected_fingerprint) != self.vocab_fingerprint
+ ):
+ raise ValueError(
+ f"Tokenizer fingerprint mismatch for schema {self.schema_version}: "
+ f"expected {expected_fingerprint}, got {self.vocab_fingerprint}"
+ )
+
+ def _compute_fingerprint(self):
+ payload = {
+ "schema_version": self.schema_version,
+ "audio_length_seconds": self.audio_length_seconds,
+ "time_hz": self.time_hz,
+ "prompt_capacity": self.prompt_capacity,
+ "prompt_names": self.prompt_names,
+ "event_field_order": self.event_field_order,
+ "meter_pairs": self.meter_pairs,
+ "structure_labels": self.structure_labels,
+ "majmin_chord_labels": self.majmin_chord_labels,
+ "full_chord_labels": self.full_chord_labels,
+ "duration_templates": tuple(int(value) for value in self.duration_templates),
+ "appended_token_blocks": tuple(
+ (name, block["labels"], block["output_field"])
+ for name, block in self.appended_token_blocks.items()
+ ),
+ "n_tokens": self.n_tokens,
+ }
+ encoded = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode(
+ "utf-8"
+ )
+ return hashlib.sha256(encoded).hexdigest()[:16]
+
+ def get_config(self):
+ return {
+ "schema_version": self.schema_version,
+ "audio_length_seconds": self.audio_length_seconds,
+ "time_hz": self.time_hz,
+ "vocab_fingerprint": self.vocab_fingerprint,
+ "n_tokens": self.n_tokens,
+ }
+
+ def normalize_prompts(self, prompts):
+ names = []
+ seen = set()
+ for prompt in prompts:
+ name = str(prompt).strip()
+ if name.startswith("<|") and name.endswith("|>"):
+ name = name[2:-2]
+ if name not in self.prompt_to_id:
+ raise ValueError(f"Unknown prompt: {prompt!r}")
+ if name not in seen:
+ names.append(name)
+ seen.add(name)
+ names.sort(key=self.prompt_names.index)
+ selected_groups = {}
+ task_by_name = {task.name: task for task in self.task_specs}
+ for name in names:
+ group = task_by_name[name].sampling_group
+ previous = selected_groups.get(group)
+ if previous is not None:
+ raise ValueError(
+ f"Prompts {previous!r} and {name!r} are mutually exclusive "
+ f"within sampling group {group!r}"
+ )
+ selected_groups[group] = name
+ if not names:
+ raise ValueError("At least one task prompt is required")
+ return tuple(names)
+
+ def prompt_to_token(self, prompt):
+ return self.prompt_to_id[self.normalize_prompts((prompt,))[0]]
+
+ def token_to_prompt(self, token):
+ token = int(token)
+ for prompt, prompt_token in self.prompt_to_id.items():
+ if token == prompt_token:
+ return prompt
+ raise ValueError(f"token {token} is not a prompt token")
+
+ def prompt_prefix(self, prompts):
+ prompts = self.normalize_prompts(prompts)
+ return [
+ self.sos_token,
+ *(self.prompt_to_id[prompt] for prompt in prompts),
+ self.out_token,
+ ]
+
+ def subbeat_shift_to_tokens(self, shift):
+ shift = int(shift)
+ if shift < 0:
+ raise ValueError("subbeat shift must be non-negative")
+ tokens = []
+ while shift > self.max_subbeat_shift:
+ tokens.append(self.subbeat_shift_token_start + self.max_subbeat_shift)
+ shift -= self.max_subbeat_shift
+ tokens.append(self.subbeat_shift_token_start + shift)
+ return tokens
+
+ def token_to_subbeat_shift(self, token):
+ token = int(token)
+ if not self.subbeat_shift_token_start <= token < self.subbeat_shift_token_end:
+ raise ValueError(f"token {token} is not a subbeat shift token")
+ return token - self.subbeat_shift_token_start
+
+ def time_id_to_token(self, time_id):
+ time_id = int(time_id)
+ if not 0 <= time_id < self.n_time_tokens:
+ raise ValueError(f"time id {time_id} is outside [0, {self.n_time_tokens})")
+ return self.time_token_start + time_id
+
+ def token_to_time_id(self, token):
+ token = int(token)
+ if not self.time_token_start <= token < self.time_token_end:
+ raise ValueError(f"token {token} is not a time token")
+ return token - self.time_token_start
+
+ def meter_to_token(self, numerator, denominator):
+ meter = (int(numerator), int(denominator))
+ if meter not in self.meter_to_id:
+ raise ValueError(f"Unsupported meter {meter[0]}/{meter[1]}")
+ return self.meter_token_start + self.meter_to_id[meter]
+
+ def eighth_position_to_token(self, position):
+ position = int(position)
+ if not 0 <= position < self.n_eighth_positions:
+ raise ValueError(
+ f"eighth-note position {position} is outside [0, {self.n_eighth_positions})"
+ )
+ return self.eighth_position_token_start + position
+
+ def structure_to_token(self, label):
+ label = str(label)
+ if label not in self.structure_labels:
+ raise ValueError(f"Unknown structure label: {label!r}")
+ return self.structure_token_start + self.structure_labels.index(label)
+
+ def key_to_token(self, key_label, pitch_shift=0):
+ tonic, mode = str(key_label).split(":", 1)
+ tonic_id = mir_eval.chord.pitch_class_to_semitone(tonic)
+ if tonic_id < 0:
+ raise ValueError(f"Invalid key tonic: {key_label!r}")
+ tonic_id = (int(tonic_id) + int(pitch_shift)) % 12
+ minor_modes = {"minor", "dorian", "phrygian", "locrian"}
+ mode_id = 1 if mode.lower() in minor_modes else 0
+ return self.key_token_start + mode_id * 12 + tonic_id
+
+ @staticmethod
+ def _canonical_chord_parts(chord_label, pitch_shift=0):
+ chord_label = str(chord_label).strip()
+ if chord_label in {"N", "X", ""}:
+ return "N", None, None
+ root, suffix = chord_label.split(":", 1)
+ root_id = mir_eval.chord.pitch_class_to_semitone(root)
+ if root_id < 0:
+ return "N", None, None
+ root_id = (int(root_id) + int(pitch_shift)) % 12
+ canonical = f"{CHROMATIC_SHARPS[root_id]}:{suffix}"
+ quality = re.split(r"/", suffix, maxsplit=1)[0]
+ return canonical, root_id, quality
+
+ def chord_majmin_to_token(self, chord_label, pitch_shift=0):
+ canonical, root_id, quality = self._canonical_chord_parts(
+ chord_label,
+ pitch_shift,
+ )
+ if canonical == "N":
+ return self.majmin_chord_token_start
+
+ try:
+ original_root, chroma, _ = mir_eval.chord.encode(str(chord_label))
+ relative = mir_eval.chord.rotate_bitmap_to_root(chroma, original_root)
+ has_minor_third = bool(relative[3] > 0)
+ has_major_third = bool(relative[4] > 0)
+ except Exception:
+ has_minor_third = quality.startswith(("min", "dim", "hdim"))
+ has_major_third = not has_minor_third
+
+ if has_major_third and not has_minor_third:
+ quality_id = 0
+ elif has_minor_third and not has_major_third:
+ quality_id = 1
+ elif quality.startswith(("min", "dim", "hdim")):
+ quality_id = 1
+ elif has_major_third:
+ quality_id = 0
+ else:
+ return self.majmin_chord_token_start
+ return self.majmin_chord_token_start + 1 + quality_id * 12 + root_id
+
+ @staticmethod
+ def _chord_template(chord_label):
+ root, chroma, bass = mir_eval.chord.encode(chord_label)
+ root_chroma = np.zeros(12, dtype=np.float32)
+ root_chroma[root] = 1.0
+ relative_chroma = mir_eval.chord.rotate_bitmap_to_root(chroma, root).astype(
+ np.float32
+ )
+ bass_chroma = np.zeros(12, dtype=np.float32)
+ bass_chroma[(bass + root) % 12] = 1.0
+ return np.concatenate([root_chroma, relative_chroma, bass_chroma])
+
+ def _build_full_chord_templates(self):
+ templates = [np.zeros(36, dtype=np.float32)]
+ templates.extend(
+ self._chord_template(label) for label in self.full_chord_labels[1:]
+ )
+ return np.stack(templates)
+
+ def chord_full_to_token(self, chord_label, pitch_shift=0):
+ canonical, _root_id, _quality = self._canonical_chord_parts(
+ chord_label,
+ pitch_shift,
+ )
+ chord_id = self.full_chord_to_id.get(canonical)
+ if chord_id is None:
+ if canonical == "N":
+ chord_id = 0
+ else:
+ try:
+ target = self._chord_template(canonical)
+ distances = np.abs(self._full_chord_templates - target[None]).sum(
+ axis=1
+ )
+ chord_id = int(np.argmin(distances))
+ except Exception:
+ chord_id = 0
+ return self.full_chord_token_start + chord_id
+
+ def pitch_to_token(self, pitch, track=0, full_melody=False):
+ pitch = int(pitch)
+ track = int(track)
+ if not 0 <= pitch < 128:
+ raise ValueError(f"MIDI pitch {pitch} is outside [0, 128)")
+ if track not in (0, 1):
+ raise ValueError(f"melody track {track} is outside [0, 2)")
+ pitch_id = pitch + (128 if full_melody and track == 1 else 0)
+ return self.pitch_token_start + pitch_id
+
+ def duration_bin_to_token(self, duration_bin):
+ duration_bin = int(duration_bin)
+ if not 0 <= duration_bin < self.n_duration_tokens:
+ raise ValueError(
+ f"duration bin {duration_bin} is outside [0, {self.n_duration_tokens})"
+ )
+ return self.duration_token_start + duration_bin
+
+ def token_to_duration_bin(self, token):
+ token = int(token)
+ if not self.duration_token_start <= token < self.duration_token_end:
+ raise ValueError(f"token {token} is not a duration token")
+ return token - self.duration_token_start
+
+ def appended_label_to_token(self, block_name, label):
+ block = self.appended_token_blocks.get(str(block_name))
+ if block is None:
+ raise ValueError(f"unknown appended token block: {block_name!r}")
+ try:
+ index = block["labels"].index(str(label))
+ except ValueError as exc:
+ raise ValueError(
+ f"unknown label {label!r} for appended block {block_name!r}"
+ ) from exc
+ return block["start"] + index
+
+ def token_to_appended_label(self, token):
+ token = int(token)
+ token_type = self.token_type(token)
+ block = self.appended_token_blocks.get(token_type)
+ if block is None:
+ raise ValueError(f"token {token} is not from an appended token block")
+ return token_type, block["labels"][token - block["start"]]
+
+ def _token_output_field(self, token_type):
+ built_in = {
+ "time": "timestamp",
+ "meter": "rhythm",
+ "eighth_position": "rhythm",
+ "structure": "structure",
+ "key": "key",
+ "chord_majmin": "chord",
+ "chord_full": "chord",
+ "pitch": "melody",
+ "duration": "melody",
+ }
+ if token_type in built_in:
+ return built_in[token_type]
+ block = self.appended_token_blocks.get(token_type)
+ return None if block is None else block["output_field"]
+
+ def _decode_field(self, field, field_tokens, prompts):
+ token_types = [self.token_type(token) for token in field_tokens]
+ if field == "timestamp":
+ return self.token_to_time_id(field_tokens[0]) / self.time_hz
+ if field == "rhythm":
+ rhythm = {}
+ for token, token_type in zip(field_tokens, token_types):
+ if token_type == "meter":
+ rhythm["meter"] = self.meter_pairs[token - self.meter_token_start]
+ elif token_type == "eighth_position":
+ rhythm["eighth_position"] = token - self.eighth_position_token_start
+ return rhythm
+ if field == "structure":
+ return self.structure_labels[field_tokens[0] - self.structure_token_start]
+ if field == "key":
+ key_id = field_tokens[0] - self.key_token_start
+ mode = "minor" if key_id >= 12 else "major"
+ return normalize_key_name(f"{CHROMATIC_SHARPS[key_id % 12]}:{mode}")
+ if field == "chord":
+ if token_types[0] == "chord_majmin":
+ return self.majmin_chord_labels[
+ field_tokens[0] - self.majmin_chord_token_start
+ ]
+ return self.full_chord_labels[
+ field_tokens[0] - self.full_chord_token_start
+ ]
+ if field == "melody":
+ notes = []
+ index = 0
+ while index < len(field_tokens):
+ pitch_id = field_tokens[index] - self.pitch_token_start
+ duration_bin = 0
+ if (
+ index + 1 < len(field_tokens)
+ and self.token_type(field_tokens[index + 1]) == "duration"
+ ):
+ duration_bin = self.token_to_duration_bin(field_tokens[index + 1])
+ index += 2
+ else:
+ index += 1
+ notes.append(
+ {
+ "pitch": pitch_id % 128,
+ "track": int(pitch_id >= 128),
+ "duration_bin": duration_bin,
+ "duration_steps": int(self.duration_templates[duration_bin]),
+ }
+ )
+ return notes
+ values = []
+ for token in field_tokens:
+ block_name, label = self.token_to_appended_label(token)
+ values.append({"block": block_name, "label": label})
+ return values[0] if len(values) == 1 else values
+
+ def decode_sequence(self, tokens, strict=True):
+ """Parse one prompt-conditioned sequence into timed, typed events."""
+ if hasattr(tokens, "detach"):
+ tokens = tokens.detach().cpu().tolist()
+ tokens = [int(token) for token in tokens]
+ while tokens and tokens[-1] == self.pad_token:
+ tokens.pop()
+ if not tokens or tokens[0] != self.sos_token:
+ raise ValueError("sequence must begin with <|sos|>")
+
+ try:
+ out_index = tokens.index(self.out_token, 1)
+ except ValueError as exc:
+ raise ValueError("sequence is missing <|out|>") from exc
+ prompts = tuple(self.token_to_prompt(token) for token in tokens[1:out_index])
+ if strict and self.normalize_prompts(prompts) != prompts:
+ raise ValueError("prompt tokens are not in canonical schema order")
+ active_fields = {
+ task.output_field for task in self.task_specs if task.name in prompts
+ }
+
+ events = []
+ position = out_index + 1
+ current_step = 0
+ saw_eos = False
+ while position < len(tokens):
+ token = tokens[position]
+ if token == self.eos_token:
+ saw_eos = True
+ position += 1
+ break
+ if self.token_type(token) != "subbeat_shift":
+ raise ValueError(f"event at token index {position} has no subbeat shift")
+ shift = 0
+ while (
+ position < len(tokens)
+ and self.token_type(tokens[position]) == "subbeat_shift"
+ ):
+ shift += self.token_to_subbeat_shift(tokens[position])
+ position += 1
+ current_step += shift
+
+ tokens_by_field = {field: [] for field in self.event_field_order}
+ while position < len(tokens):
+ token = tokens[position]
+ token_type = self.token_type(token)
+ if token_type == "subbeat_shift" or token == self.eos_token:
+ break
+ field = self._token_output_field(token_type)
+ if field is None or field not in tokens_by_field:
+ raise ValueError(
+ f"token {token} ({token_type}) has no field in schema "
+ f"{self.schema_version}"
+ )
+ if strict and field not in active_fields:
+ raise ValueError(
+ f"token {token} belongs to inactive output field {field!r}"
+ )
+ tokens_by_field[field].append(token)
+ position += 1
+
+ tokens_by_field = {
+ field: values for field, values in tokens_by_field.items() if values
+ }
+ if not tokens_by_field:
+ if strict:
+ raise ValueError(f"empty event at subbeat {current_step}")
+ continue
+ if strict:
+ for field, values in tokens_by_field.items():
+ types = [self.token_type(value) for value in values]
+ if field == "timestamp" and types != ["time"]:
+ raise ValueError("timestamp event must contain exactly one time token")
+ if field == "rhythm":
+ if types not in (["eighth_position"], ["meter", "eighth_position"]):
+ raise ValueError(f"invalid rhythm payload: {types}")
+ if field in {"structure", "key", "chord"} and len(values) != 1:
+ raise ValueError(f"field {field!r} must contain exactly one token")
+ if field == "melody":
+ index = 0
+ while index < len(types):
+ if types[index] != "pitch":
+ raise ValueError(
+ "melody payload must contain pitch tokens with optional duration"
+ )
+ if index + 1 < len(types) and types[index + 1] == "duration":
+ index += 2
+ else:
+ index += 1
+ events.append(
+ {
+ "subbeat": current_step,
+ "tokens_by_field": tokens_by_field,
+ "values": {
+ field: self._decode_field(field, values, prompts)
+ for field, values in tokens_by_field.items()
+ },
+ }
+ )
+
+ if strict and not saw_eos:
+ raise ValueError("sequence is missing <|eos|>")
+ if strict and position != len(tokens):
+ raise ValueError("non-padding tokens follow <|eos|>")
+ return {
+ "schema_version": self.schema_version,
+ "prompts": prompts,
+ "events": events,
+ "has_eos": saw_eos,
+ }
+
+ def encode_decoded_sequence(self, decoded):
+ """Re-encode the lossless representation returned by decode_sequence."""
+ prompts = self.normalize_prompts(decoded["prompts"])
+ output = self.prompt_prefix(prompts)
+ previous_step = 0
+ for event in decoded["events"]:
+ step = int(event["subbeat"])
+ if step < previous_step:
+ raise ValueError("events must be sorted by non-decreasing subbeat")
+ output.extend(self.subbeat_shift_to_tokens(step - previous_step))
+ previous_step = step
+ tokens_by_field = event["tokens_by_field"]
+ for field in self.event_field_order:
+ output.extend(int(token) for token in tokens_by_field.get(field, ()))
+ if decoded.get("has_eos", True):
+ output.append(self.eos_token)
+ return output
+
+ def token_type(self, token):
+ token = int(token)
+ for name, start, end in (
+ ("subbeat_shift", self.subbeat_shift_token_start, self.subbeat_shift_token_end),
+ ("time", self.time_token_start, self.time_token_end),
+ ("meter", self.meter_token_start, self.meter_token_end),
+ ("eighth_position", self.eighth_position_token_start, self.eighth_position_token_end),
+ ("structure", self.structure_token_start, self.structure_token_end),
+ ("key", self.key_token_start, self.key_token_end),
+ ("chord_majmin", self.majmin_chord_token_start, self.majmin_chord_token_end),
+ ("chord_full", self.full_chord_token_start, self.full_chord_token_end),
+ ("pitch", self.pitch_token_start, self.pitch_token_end),
+ ("duration", self.duration_token_start, self.duration_token_end),
+ ):
+ if start <= token < end:
+ return name
+ if token in self.prompt_to_id.values():
+ return "prompt"
+ for name, block in self.appended_token_blocks.items():
+ if block["start"] <= token < block["end"]:
+ return name
+ if token == self.pad_token:
+ return "pad"
+ if token == self.sos_token:
+ return "sos"
+ if token == self.eos_token:
+ return "eos"
+ if token == self.out_token:
+ return "out"
+ raise ValueError(f"token {token} is outside vocabulary size {self.n_tokens}")
+
+ def describe(self, token):
+ token = int(token)
+ token_type = self.token_type(token)
+ if token_type == "prompt":
+ prompt_by_id = {value: key for key, value in self.prompt_to_id.items()}
+ return f"<|{prompt_by_id[token]}|>"
+ if token_type == "subbeat_shift":
+ return f""
+ if token_type == "time":
+ time_id = token - self.time_token_start
+ return f""
+ if token_type == "meter":
+ numerator, denominator = self.meter_pairs[token - self.meter_token_start]
+ return f""
+ if token_type == "eighth_position":
+ return f""
+ if token_type == "structure":
+ return f""
+ if token_type == "key":
+ key_id = token - self.key_token_start
+ mode = "minor" if key_id >= 12 else "major"
+ label = normalize_key_name(f"{CHROMATIC_SHARPS[key_id % 12]}:{mode}")
+ return f""
+ if token_type == "chord_majmin":
+ return f""
+ if token_type == "chord_full":
+ return f""
+ if token_type == "pitch":
+ pitch = token - self.pitch_token_start
+ track = 1 if pitch >= 128 else 0
+ return f""
+ if token_type == "duration":
+ return f""
+ if token_type in self.appended_token_blocks:
+ block = self.appended_token_blocks[token_type]
+ label = block["labels"][token - block["start"]]
+ return f"<{token_type}_{label}>"
+ return f"<|{token_type}|>"