-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathuncompressed.html
70 lines (70 loc) · 3.25 KB
/
uncompressed.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<meta charset=utf8><canvas tabIndex=1 id=c style=width:900;image-rendering:pixelated><script>
C=c.getContext`2d`;C.fillStyle="tan";Z="charCodeAt";
// draws a box, if w is small, it is a pixel
b=(x,y,w)=>C.strokeRect(x+.5,y+.5,w,w,C.fillRect(x,y,w,w));
// bitmap text
t=(x,y,t)=>{[...t+""].map((k,z)=>{for(k="⭪⒓犣㢣䧭㧏篋┧篯积寪㭫㢎璗扎⒗筯"[Z](k[Z](i=0)-48);i<15;k>>=1,i++)if(k&1)b(4*z+x+i%3,y+i/3|0,.01)})}
// bresenham line algo
l=(a,B,c,d,z=Math.abs)=>{for(let g,x=z(c-a),y=z(d-B),r=(a<c)*2-1,q=(B<d)*2-1,e=(x>y?x:-y)/2;a!=c||B!=d;a+=g>-x?r+(e-=y)*0:0,B+=g<y?q+(e+=x)*0:0)b(a,B,.05,g=e)}
// data for hexagon and synth params
_=[];
// coordinates of synth params, initilizes to 1e-9
[...P="ŀƉŢļĖñĝŃũƏƵǛȁƮɅǷǑƬƭĠņŬƒƸǞȄǏǵȿƦƧƨģʼnůƕƻǡȇĴŚſİçĎĦŌŲƘƾǤȊÄÃÂ+yĩŏŵƛǁǧȍÊÉÈ¡{1ĬŒŸƞDŽǪȐ"].map(y=>_[y[Z](0)]=1e-9);
// populate hexagon coordinates
for(i=0;i<6;i++)_[i]=[[6,3,3],[0,5,5]].map(n=>68+8*n[i%3]*(-1)**~~((i+(n[0]>5))/3))
// draw loop
d=e=>{requestAnimationFrame(d);
//background
b(-1,-1,350);
// operators graph
for(var i=0;i<36;i++){
let {0:x,1:y}=_[I=i/6|0],{0:X,1:Y}=_[i%6];
l(x,y,X,Y);
b(X-12,Y-12,24);
t(X,Y-2,i%6);
// right-side params labels
[...I+":;<=>?@"].map((l,j)=>t(146+24*I,50+8*j,l))}
// draw param boxes
for(i=6;i<722;i++)if(_[i]){
b(A=i%38*8,B=(i/38|0)*8,8);
t(A+1,B+2,_[i]|0)}
// for(i=0;i<38*19;i++) b(8*(i%38),8*~~(i/38),.01) //gridlines
};d(S=6);// S stores the param index when mouse is held down
//mouse behaviors
c.onmousedown=e=>S=~~(e.offsetX/24)+38*~~(e.offsetY/24);
c.onmouseup=e=>S=6;
// when mouse moves, we store the synth patch into URL's hash
c.onmousemove=e=>{_[S]=_[S]?Math.min(99,Math.max(1e-9,~~(_[S]-e.movementY))):0;location.hash=[...P].map(y=>~~_[y[Z](0)]).join(",")}
// loading synth patch
location.hash.substr(1).split(",").map((x,i)=>_[P[Z](i)]=x?x:1e-9);
// K is the mapping of the keyboard
K="q2w3er5t6y7ui9o0p[=]B\\";
// T: Absolute time since sound engine loads
// $: array of voices
// F: last data of the operators. 132 = 6*22
T=0;$=Array(22).fill([0,-9]);F=Array(132);
// keyboard behavior. We store [gate, triggered time] at index corresponding to K
c.onkeyup=c.onkeydown=e=>$[z=K.indexOf(e.key[0])]=[+(e.type[3]=="d"),e.repeat?$[z][1]:T];
// L: mapper. o is the operator index, x is the parameter index
L=(o,x)=>_[P[Z](13*o+x)];
// initializes AudioContext and scriptProcessor
D=(G=new AudioContext).createScriptProcessor(2048,0,1);D.connect(G.destination);
D.onaudioprocess=e=>{
for(i=0;i<2048;i++)
e.outputBuffer.getChannelData(0)[i]=(t=>{
for(k=out=0;k<22;k++) // loops through each voices
for(q=0,a=L(q,6)/33,N=t-$[k][1];$[k][0]&&q<6;q++) //loop through each operators
if(L(q,12)>.1) //if the operator has a really low volume, don't bother computing
out += L(q,12)/900* //output volume
(F[k*6+q]= //after computed the operators, store its value
(N<a?1*N/a:Math.max(L(q,8)/99,1-(N-a)*33/L(q,7)))* // attack, decay, sustain
Math.sin(
(L(q,9)>2?F.slice(k*6,k*6+6).reduce((x,y,i)=>x+L(q,i)/33*y,0)*L(q,9)/99:0) //if operator's modIndex is > 2, we compute it
+
(L(q,11)/99+L(q,10))*t*2765*2**((k-9)/12) // frequency coarse, tune, midi->Hz equal temperament
)
)
return out;
})(T+=1/G.sampleRate);
}
</script>