1const defs = ` 2 const float PI = 3.14159265359; 3 const vec2 wh = vec2(90.0, 50.0); 4 const float border = 15.0; 5 6 varying vec2 uv; 7 varying float playingNote; 8 varying float element; 9`; 10 11const vs_code = defs+` 12 uniform sampler2D sonicTex; 13 uniform vec2 viewSize; 14 uniform vec2 sonicPos; 15 uniform float progress; 16 17 attribute vec4 vertex; 18 19 float getElement(vec4 v, float i) { 20 if (i<1.0) return v.x; 21 if (i<2.0) return v.y; 22 if (i<3.0) return v.z; 23 return v.w; 24 } 25 26 const float sonicTexW = 16.0*12.0/4.0; 27 28 float getChannel(float i, float ch) { 29 float x = (floor(i*3.0 + ch/4.0)+0.5) / sonicTexW; 30 vec4 v4 = texture2D(sonicTex, vec2(x, 0.0)); 31 return getElement(v4, mod(ch, 4.0)); 32 } 33 34 void main() { 35 vec2 pos = sonicPos; 36 element = vertex.z; 37 38 const float shift = -90.0; 39 40 if (element == -2.0) { 41 uv = vertex.xy; 42 float r = 8.0*(viewSize.x+viewSize.y)/1000.0; 43 pos += uv*r; 44 } else if (element == -1.0) { 45 pos += vec2(0.0, shift); 46 uv = vertex.xy*(wh+border); 47 pos += uv; 48 } else { 49 pos += vec2(0.0, shift); 50 uv = vertex.xy; 51 float i = floor(vertex.z / 9.0); 52 float ch = mod(vertex.z, 9.0); 53 float v = getChannel(i, ch+3.0); // skip RGB 54 pos += vec2((ch+(i-7.5)/40.0)/4.0-1.0, 1.0-v*2.0) * wh; 55 pos += vertex.xy*vec2(5.0, 5.0); 56 playingNote = float(ch==progress); 57 } 58 gl_Position = vec4(pos/viewSize*2.0-1.0 , 0.0, 1.0); 59 gl_Position.y *= -1.0; 60 61 }`; 62 63const fs_code = 'precision highp float;\n'+defs+` 64 uniform sampler2D sonicTex; 65 66 void main() { 67 if (element == -2.0) { 68 float v = smoothstep(1.0, 0.8, length(uv)); 69 gl_FragColor = vec4(1.0, 1.0, 1.0, v*0.5); 70 } else if (element == -1.0) { 71 float v = smoothstep(border, 0.0, length(max(abs(uv)-wh, 0.0))); 72 gl_FragColor = vec4(0.0, 0.0, 0.0, v*0.5); 73 } else { 74 float v = smoothstep(1.0, 0.0, length(uv)); 75 gl_FragColor = vec4(1.0, 1.0, 1.0, v*(0.1+playingNote*0.5)); 76 } 77 } 78`; 79 80 81export class Sonic { 82 constructor(gl, ca) { 83 this.gl = gl; 84 this.ca = ca; 85 this.program = twgl.createProgramInfo(gl, [vs_code, fs_code]); 86 87 const voiceN = 16; 88 89 const pos = []; 90 for (let i=-2; i<9*voiceN; ++i) { 91 pos.push(-1, -1, i, 1, -1, i, -1, 1, i, -1, 1, i, 1, -1, i, 1, 1, i); 92 } 93 this.quads = twgl.createBufferInfoFromArrays(gl, {vertex: pos}); 94 95 this.state = null; 96 this._progress = -1; 97 this.env = new Tone.AmplitudeEnvelope({ 98 attack: 0.05, 99 decay: 0.05, 100 sustain: 0.5, 101 release: 0.2 102 }).toDestination(); 103 this.osc = Array(voiceN).fill().map( 104 ()=>new Tone.Oscillator({type:'triangle'}).connect(this.env).start() 105 ); 106 Tone.Transport.start(); 107 108 this.seq = new Tone.Sequence((time, i) => { 109 if (!this.state || i>=9) 110 return; 111 this._progress = i; 112 for (let j=0; j<voiceN; ++j) { 113 const note = 220*Math.pow(2.0, this.state.buf[j*12+i+3]/128-1.0); 114 this.osc[j].frequency.setValueAtTime(note, time); 115 } 116 this.env.triggerAttackRelease(0.05, time, 0.8/voiceN); 117 }, [...Array(12-3+0).keys()], 1.0/8.0).start(); 118 119 } 120 121 play(state) { 122 this.state = state; 123 } 124 125 stop() { 126 this.state = null; 127 this._progress = -1; 128 } 129 130 progress() { 131 return this._progress; 132 } 133 134 draw(viewSize) { 135 if (!this.state) 136 return; 137 const gl = this.gl; 138 gl.useProgram(this.program.program); 139 twgl.setUniforms(this.program, { 140 viewSize, 141 sonicTex: this.state.tex, 142 sonicPos:this.state.pos, 143 progress: this._progress, 144 }); 145 twgl.setBuffersAndAttributes(gl, this.program, this.quads); 146 gl.enable(gl.BLEND) 147 gl.blendEquation(gl.FUNC_ADD); 148 gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); 149 twgl.drawBufferInfo(gl, this.quads); 150 gl.disable(gl.BLEND) 151 } 152 };
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.