implement custom base address for GES

This commit is contained in:
dranke committed 2022-04-18 16:40:16 +02:00
1 parent 6f20d303c8
commit 7a52ce4e4c
6 files changed
+46 -37

No files matched your search

+1
View File
@@ -55,6 +55,7 @@ data 80 {
include "../../platform/src/ges.cwa" include "../../platform/src/ges.cwa"
import "env.pow" fn pow(f32, f32) -> f32; import "env.pow" fn pow(f32, f32) -> f32;
import "env.exp" fn exp(f32) -> f32;
import "env.sin" fn sin(f32) -> f32; import "env.sin" fn sin(f32) -> f32;
export fn snd(t: i32) -> f32 { export fn snd(t: i32) -> f32 {
Binary file not shown.
+35 -34
View File
@@ -7,21 +7,22 @@ const GesChannelState.Size = 8;
const GesState.Filter = GesChannelState.Size * 4; const GesState.Filter = GesChannelState.Size * 4;
const GesState.Size = GesState.Filter + 8*4; const GesState.Size = GesState.Filter + 8*4;
const GesStateOffset = 112; const GesStateOffset = 32;
const GesBufferOffset = 112 + GesState.Size; const GesBufferOffset = 32 + GesState.Size;
export fn gesSnd(t: i32) -> f32 { export fn gesSnd(t: i32) -> f32 {
let baseAddr = 0!0x12c78;
if !(t & 127) { if !(t & 127) {
let i: i32; let i: i32;
loop clearLoop { loop clearLoop {
i!GesBufferOffset = 0; (baseAddr + i)!GesBufferOffset = 0;
branch_if (i := i + 4) < 128*8: clearLoop; branch_if (i := i + 4) < 128*8: clearLoop;
} }
let ch: i32; let ch: i32;
loop channelLoop { loop channelLoop {
let lazy channelState = GesStateOffset + ch * GesChannelState.Size; let lazy channelState = baseAddr + GesStateOffset + ch * GesChannelState.Size;
let lazy channelReg = 80 + ch * 6; let lazy channelReg = baseAddr + ch * 6;
let envState = channelState?GesChannelState.EnvState; let envState = channelState?GesChannelState.EnvState;
let envVol = i32.load16_u(channelState, GesChannelState.EnvVol); let envVol = i32.load16_u(channelState, GesChannelState.EnvVol);
@@ -77,7 +78,7 @@ export fn gesSnd(t: i32) -> f32 {
let saw2 = saw2 - let saw2 = saw2 -
polyBlep((p - pulsePhase1) >> 16, invPhaseInc, -saw) - polyBlep((p - pulsePhase1) >> 16, invPhaseInc, -saw) -
polyBlep((p - pulsePhase2) >> 16, invPhaseInc, saw); polyBlep((p - pulsePhase2) >> 16, invPhaseInc, saw);
i!(GesBufferOffset + 128*4) = saw2; (baseAddr + i)!(GesBufferOffset + 128*4) = saw2;
phase = phase + phaseInc; phase = phase + phaseInc;
branch_if (i := i + 4) < 64*4: sawLoop; branch_if (i := i + 4) < 64*4: sawLoop;
} }
@@ -86,7 +87,7 @@ export fn gesSnd(t: i32) -> f32 {
{ {
let pulsePhase = 32768 + pulseWidth * 128; let pulsePhase = 32768 + pulseWidth * 128;
loop rectLoop { loop rectLoop {
i!(GesBufferOffset + 128*4) = select((phase & 65535) < pulsePhase, -32768, 32767) - (baseAddr + i)!(GesBufferOffset + 128*4) = select((phase & 65535) < pulsePhase, -32768, 32767) -
polyBlep(phase, invPhaseInc, -32767) - polyBlep(phase, invPhaseInc, -32767) -
polyBlep(phase - pulsePhase, invPhaseInc, 32767); polyBlep(phase - pulsePhase, invPhaseInc, 32767);
phase = phase + phaseInc; phase = phase + phaseInc;
@@ -101,7 +102,7 @@ export fn gesSnd(t: i32) -> f32 {
s = (s ^ (s >> 31)); s = (s ^ (s >> 31));
s = (s >> 8) * scale; s = (s >> 8) * scale;
s = (s ^ (s >> 31)); s = (s ^ (s >> 31));
i!(GesBufferOffset + 128*4) = (s >> 15) - 32768; (baseAddr + i)!(GesBufferOffset + 128*4) = (s >> 15) - 32768;
phase = phase + phaseInc; phase = phase + phaseInc;
branch_if (i := i + 4) < 64*4: triLoop; branch_if (i := i + 4) < 64*4: triLoop;
} }
@@ -111,7 +112,7 @@ export fn gesSnd(t: i32) -> f32 {
let inline pulse = ((phase >> 8) & 255) >= pulseWidth; let inline pulse = ((phase >> 8) & 255) >= pulseWidth;
s = s * 0x6746ba73; s = s * 0x6746ba73;
s = s ^ (s >> 15) * pulse; s = s ^ (s >> 15) * pulse;
i!(GesBufferOffset + 128*4) = (s * 0x835776c7) >> 16; (baseAddr + i)!(GesBufferOffset + 128*4) = (s * 0x835776c7) >> 16;
phase = phase + phaseInc; phase = phase + phaseInc;
branch_if (i := i + 4) < 64*4: noiseLoop; branch_if (i := i + 4) < 64*4: noiseLoop;
} }
@@ -122,8 +123,8 @@ export fn gesSnd(t: i32) -> f32 {
if ctrl & 32 { if ctrl & 32 {
let lazy modSrc = (ch - 1) & 3; let lazy modSrc = (ch - 1) & 3;
let inline channelState = GesStateOffset + modSrc * GesChannelState.Size; let inline channelState = baseAddr + GesStateOffset + modSrc * GesChannelState.Size;
let inline channelReg = 80 + modSrc * 6; let inline channelReg = baseAddr + modSrc * 6;
let inline note = i32.load16_u(channelReg, 2); let inline note = i32.load16_u(channelReg, 2);
let inline freq = 440 as f32 * pow(2.0, (note - 69*256) as f32 / (12*256) as f32); let inline freq = 440 as f32 * pow(2.0, (note - 69*256) as f32 / (12*256) as f32);
@@ -138,13 +139,13 @@ export fn gesSnd(t: i32) -> f32 {
loop ringLoop { loop ringLoop {
let s = phase << 16; let s = phase << 16;
s = (s ^ (s >> 31)); s = (s ^ (s >> 31));
i!(GesBufferOffset + 128*4) = (i!(GesBufferOffset + 128*4) * ((s >> 15) - 32768)) >> 15; (baseAddr + i)!(GesBufferOffset + 128*4) = ((baseAddr + i)!(GesBufferOffset + 128*4) * ((s >> 15) - 32768)) >> 15;
phase = phase + phaseInc; phase = phase + phaseInc;
branch_if (i := i + 4) < 64*4: ringLoop; branch_if (i := i + 4) < 64*4: ringLoop;
} }
} }
let channelVol = ((ch >> 1)?0x68 >> ((ch & 1) * 4)) & 15; let channelVol = ((baseAddr + (ch >> 1))?24 >> ((ch & 1) * 4)) & 15;
envVol = envVol * channelVol / 15; envVol = envVol * channelVol / 15;
let leftVol = (select(ctrl & 16, 0x3d5b, 0x6a79) >> (ch * 4)) & 15; let leftVol = (select(ctrl & 16, 0x3d5b, 0x6a79) >> (ch * 4)) & 15;
@@ -156,30 +157,30 @@ export fn gesSnd(t: i32) -> f32 {
if filter < 2 { if filter < 2 {
if filter { if filter {
let f = (4096 as f32 - min(4096 as f32, 4096 as f32 * exp(freq * (-8.0 * 3.141 / 44100.0)))) as i32; let f = (4096 as f32 - min(4096 as f32, 4096 as f32 * exp(freq * (-8.0 * 3.141 / 44100.0)))) as i32;
let low = (ch * 8)!(GesStateOffset + GesState.Filter); let low = (baseAddr + ch * 8)!(GesStateOffset + GesState.Filter);
loop filterLoop { loop filterLoop {
let in = (i!(GesBufferOffset + 128*4) * envVol) >> 18; let in = ((baseAddr + i)!(GesBufferOffset + 128*4) * envVol) >> 18;
low = low + (((in - low) * f) >> 12); low = low + (((in - low) * f) >> 12);
(i * 2)!GesBufferOffset = (i * 2)!GesBufferOffset + ((low * leftVol) >> 4); (baseAddr + i * 2)!GesBufferOffset = (baseAddr + i * 2)!GesBufferOffset + ((low * leftVol) >> 4);
(i * 2)!(GesBufferOffset + 4) = (i * 2)!(GesBufferOffset + 4) + ((low * rightVol) >> 4); (baseAddr + i * 2)!(GesBufferOffset + 4) = (baseAddr + i * 2)!(GesBufferOffset + 4) + ((low * rightVol) >> 4);
branch_if (i := i + 4) < 64*4: filterLoop; branch_if (i := i + 4) < 64*4: filterLoop;
(ch * 8)!(GesStateOffset + GesState.Filter) = low; (baseAddr + ch * 8)!(GesStateOffset + GesState.Filter) = low;
(ch * 8)!(GesStateOffset + GesState.Filter + 4) = 0; (baseAddr + ch * 8)!(GesStateOffset + GesState.Filter + 4) = 0;
} }
} else { } else {
loop mixLoop { loop mixLoop {
let sample = (i!(GesBufferOffset + 128*4) * envVol) >> 18; let sample = ((baseAddr + i)!(GesBufferOffset + 128*4) * envVol) >> 18;
(i * 2)!GesBufferOffset = (i * 2)!GesBufferOffset + ((sample * leftVol) >> 4); (baseAddr + i * 2)!GesBufferOffset = (baseAddr + i * 2)!GesBufferOffset + ((sample * leftVol) >> 4);
(i * 2)!(GesBufferOffset + 4) = (i * 2)!(GesBufferOffset + 4) + ((sample * rightVol) >> 4); (baseAddr + i * 2)!(GesBufferOffset + 4) = (baseAddr + i * 2)!(GesBufferOffset + 4) + ((sample * rightVol) >> 4);
branch_if (i := i + 4) < 64*4: mixLoop; branch_if (i := i + 4) < 64*4: mixLoop;
(ch * 8)!(GesStateOffset + GesState.Filter) = sample; (baseAddr + ch * 8)!(GesStateOffset + GesState.Filter) = sample;
(ch * 8)!(GesStateOffset + GesState.Filter + 4) = 0; (baseAddr + ch * 8)!(GesStateOffset + GesState.Filter + 4) = 0;
} }
} }
} else { } else {
filter = filter - 2; filter = filter - 2;
let ctrl = filter?0x6a; let ctrl = (baseAddr + filter)?26;
let note = i32.load16_u(filter * 2, 0x6c); let note = i32.load16_u(baseAddr + filter * 2, 28);
let inline freq = 440 as f32 * pow(2.0, (note - 69*256) as f32 / (12*256) as f32); let inline freq = 440 as f32 * pow(2.0, (note - 69*256) as f32 / (12*256) as f32);
let F = (8192 as f32 * sin(min(0.25, freq / 44100 as f32) * 3.1415)) as i32; let F = (8192 as f32 * sin(min(0.25, freq / 44100 as f32) * 3.1415)) as i32;
let Q = 8192 - (ctrl >> 4) * (7000/15); let Q = 8192 - (ctrl >> 4) * (7000/15);
@@ -190,28 +191,28 @@ export fn gesSnd(t: i32) -> f32 {
let low_out = ctrl & 1; let low_out = ctrl & 1;
let high_out = (ctrl >> 1) & 1; let high_out = (ctrl >> 1) & 1;
let band_out = (ctrl >> 2) & 1; let band_out = (ctrl >> 2) & 1;
let low = (ch * 8)!(GesStateOffset + GesState.Filter); let low = (baseAddr + ch * 8)!(GesStateOffset + GesState.Filter);
let band = (ch * 8)!(GesStateOffset + GesState.Filter + 4); let band = (baseAddr + ch * 8)!(GesStateOffset + GesState.Filter + 4);
loop filterLoop { loop filterLoop {
let in = (i!(GesBufferOffset + 128*4) * envVol) >> 18; let in = ((baseAddr + i)!(GesBufferOffset + 128*4) * envVol) >> 18;
let high = in - low - ((band * Q) >> 12); let high = in - low - ((band * Q) >> 12);
band = band + ((F * high) >> 12); band = band + ((F * high) >> 12);
low = low + ((F * band) >> 12); low = low + ((F * band) >> 12);
let sample = low * low_out + high * high_out + band * band_out; let sample = low * low_out + high * high_out + band * band_out;
(i * 2)!GesBufferOffset = (i * 2)!GesBufferOffset + ((sample * leftVol) >> 4); (baseAddr + i * 2)!GesBufferOffset = (baseAddr + i * 2)!GesBufferOffset + ((sample * leftVol) >> 4);
(i * 2)!(GesBufferOffset + 4) = (i * 2)!(GesBufferOffset + 4) + ((sample * rightVol) >> 4); (baseAddr + i * 2)!(GesBufferOffset + 4) = (baseAddr + i * 2)!(GesBufferOffset + 4) + ((sample * rightVol) >> 4);
branch_if (i := i + 4) < 64*4: filterLoop; branch_if (i := i + 4) < 64*4: filterLoop;
(ch * 8)!(GesStateOffset + GesState.Filter) = low; (baseAddr + ch * 8)!(GesStateOffset + GesState.Filter) = low;
(ch * 8)!(GesStateOffset + GesState.Filter + 4) = band; (baseAddr + ch * 8)!(GesStateOffset + GesState.Filter + 4) = band;
} }
} }
branch_if (ch := ch + 1) < 4: channelLoop; branch_if (ch := ch + 1) < 4: channelLoop;
} }
} }
(((t & 127) * 4)!GesBufferOffset) as f32 / 32768 as f32 ((baseAddr + (t & 127) * 4)!GesBufferOffset) as f32 / 32768 as f32
} }
fn polyBlep(transientPhase: i32, invPhaseInc: f32, magnitude: i32) -> i32 { fn polyBlep(transientPhase: i32, invPhaseInc: f32, magnitude: i32) -> i32 {
+4
View File
@@ -576,6 +576,10 @@ data 80 {
) )
} }
data 0x12c78 {
i32(80)
}
data 0x13000+192*4 { data 0x13000+192*4 {
i32( i32(
0x2c1c1a, 0x2c1c1a,
+5 -2
View File
@@ -19,10 +19,13 @@ The memory has to be imported as `env` `memory` and has a maximum size of 256kb
00040-00044: time since module start in ms 00040-00044: time since module start in ms
00044-0004c: gamepad state 00044-0004c: gamepad state
0004c-00050: reserved 0004c-00050: reserved
00050-00070: sound registers 00050-00070: sound data (synced to sound thread)
00070-00078: reserved 00070-00078: reserved
00078-12c78: frame buffer 00078-12c78: frame buffer
12c78-13000: reserved 12c78-12c7c: sound registers/work area base address (for sndGes function)
12c7c-12c80: reserved
12c80-12ca0: sound data (synced from sound thread)
12ca0-13000: reserved
13000-13400: palette 13000-13400: palette
13400-13c00: font 13400-13c00: font
13c00-14000: reserved 13c00-14000: reserved
+1 -1
View File
@@ -1 +1 @@
<!doctype html><html><head><meta charset="utf8"><title>uw8-run</title><style>body,canvas,html{background-color:#202024;margin:0;padding:0}body,html{height:100%}body{display:grid;grid-template-rows:1fr}#screen{align-self:center;border:4px solid #303040;image-rendering:pixelated;justify-self:center}#message{background-color:rgba(0,0,0,.4);color:#c64;font:700 12pt sans-serif;padding:8px;position:absolute;width:calc(100% - 16px);z-index:2}@media (min-width:648px) and (min-height:488px){#screen{height:480px;width:640px}}@media (min-width:968px) and (min-height:728px){#screen{height:720px;width:960px}}</style></head><body> <canvas id="screen" width="320" height="240" tabindex="1"> </canvas> <div id="message"></div> </body><script type="module">function e(e){return e&&e.__esModule?e.default:e}class t extends AudioWorkletNode{constructor(e){super(e,"apu",{outputChannelCount:[2]})}}let A=(...e)=>new Uint8Array(...e),a=(...e)=>new Uint32Array(...e);let n=function(n,B={}){B.setMessage||(B.setMessage=(e,t)=>{t&&console.log("error: "+t)});let o,s,r,i,l=n.getContext("2d"),d=l.createImageData(320,240),F=B.devkitMode,c=0,D=null==B.keyboardElement?n:B.keyboardElement;if(D){let e=e=>{let t,A="keydown"==e.type;switch(e.code){case"ArrowUp":t=1;break;case"ArrowDown":t=2;break;case"ArrowLeft":t=4;break;case"ArrowRight":t=8;break;case"KeyZ":t=16;break;case"KeyX":t=32;break;case"KeyA":t=64;break;case"KeyS":t=128;break;case"KeyR":A&&f(s,!0);break;case"F9":A&&n.toBlob((e=>{p(e,".png")})),e.preventDefault();break;case"F10":A&&function(){if(r)return r.stop(),void(r=null);r=new MediaRecorder(n.captureStream(),{mimeType:"video/webm",videoBitsPerSecond:25e6});let e=[];r.ondataavailable=t=>{e.push(t.data)};let t=B.timerElement;t&&(t.hidden=!1,t.innerText="00:00");function A(){if(i){if(t){let e=Math.floor((Date.now()-i)/1e3);t.innerText=Math.floor(e/60).toString().padStart(2,"0")+":"+(e%60).toString().padStart(2,"0")}setTimeout(A,1e3)}}r.onstop=()=>{t&&(t.hidden=!0),p(new Blob(e,{type:"video/webm"}),".webm")},r.start(),i=Date.now(),setTimeout(A,1e3)}(),e.preventDefault()}A?c|=t:c&=~t};D.onkeydown=e,D.onkeyup=e}async function f(r,i){o&&(o(),o=null);let D=new AudioContext({sampleRate:44100}),p=!0,g=new AbortController;o=()=>{D.close(),p=!1,g.abort()};let Q=r.byteLength;if(B.setMessage(Q),0==Q)return;await D.audioWorklet.addModule(e("data:application/javascript,let%20e%3D%28...e%29%3D%3Enew%20Uint8Array%28...e%29%3Bclass%20s%20extends%20AudioWorkletProcessor%7Bconstructor%28%29%7Bsuper%28%29%2Cthis.sampleIndex%3D0%2Cthis.port.onmessage%3Ds%3D%3E%7Bthis.memory%3Fe%28this.memory.buffer%2C80%2C32%29.set%28e%28s.data%29%29%3Athis.load%28s.data%5B0%5D%2Cs.data%5B1%5D%29%7D%7Dasync%20load%28e%2Cs%29%7Blet%20t%3Dnew%20WebAssembly.Memory%28%7Binitial%3A4%2Cmaximum%3A4%7D%29%2Ca%3D%7Benv%3A%7Bmemory%3At%7D%7D%3Bfor%28let%20e%20of%5B%22acos%22%2C%22asin%22%2C%22atan%22%2C%22atan2%22%2C%22cos%22%2C%22exp%22%2C%22log%22%2C%22sin%22%2C%22tan%22%2C%22pow%22%5D%29a.env%5Be%5D%3DMath%5Be%5D%3Bfor%28let%20e%3D9%3Be%3C64%3B%2B%2Be%29a.env%5B%22reserved%22%2Be%5D%3D%28%29%3D%3E%7B%7D%3Bfor%28let%20e%3D0%3Be%3C16%3B%2B%2Be%29a.env%5B%22g_reserved%22%2Be%5D%3D0%3Blet%20r%3Dasync%20e%3D%3E%28await%20WebAssembly.instantiate%28e%2Ca%29%29.instance%2Cn%3Dawait%20r%28e%29%3Bfor%28let%20e%20in%20n.exports%29a.env%5Be%5D%3Dn.exports%5Be%5D%3Blet%20o%3Dawait%20r%28s%29%3Bthis.memory%3Dt%2Cthis.snd%3Do.exports.snd%7C%7Cn.exports.gesSnd%2Cthis.port.postMessage%282%29%7Dprocess%28e%2Cs%2Ct%29%7Bif%28this.snd%29%7Blet%20e%3Ds%5B0%5D%2Ct%3Dthis.sampleIndex%2Ca%3De%5B0%5D.length%3Bfor%28let%20s%3D0%3Bs%3Ca%3B%2B%2Bs%29e%5B0%5D%5Bs%5D%3Dthis.snd%28t%2B%2B%29%2Ce%5B1%5D%5Bs%5D%3Dthis.snd%28t%2B%2B%29%3Bthis.sampleIndex%3D4294967295%26t%7Dreturn%210%7D%7DregisterProcessor%28%22apu%22%2Cs%29%3B"));let m,C=new t(D),u=0,w=new Promise((e=>m=e)),E=e=>{u|=e,3==u&&m&&(m(!0),m=null)};C.port.onmessage=e=>E(e.data);let I=()=>{"suspended"==D.state?(B.startButton&&(B.startButton.style="",n.style="display:none"),(B.startButton||n).onclick=()=>{D.resume()}):(B.startButton&&(B.startButton.style="display:none",n.style=""),E(1))};D.onstatechange=I,I(),s=r;let b=window.location.pathname;if(Q<=1024&&!i){let e="";for(let t of A(r))e+=String.fromCharCode(t);b+="#"+btoa(e),b!=window.location.pathname+window.location.hash&&history.pushState(null,null,b)}n.width=n.width;try{let t={initial:4};F||(t.maximum=4);let n,o=new WebAssembly.Memory(t),i=A(o.buffer),m={env:{memory:o}},u=e=>{if(n&&(!F||0!=A(e)[0])){i.set(A(e));let t=n.exports.load_uw8(e.byteLength);e=new ArrayBuffer(t),A(e).set(i.slice(0,t))}return e},E=async e=>(await WebAssembly.instantiate(e,m)).instance,I=async e=>u(await(await fetch(e)).arrayBuffer());n=await E(await I(e("data:application/wasm;base64,AGFzbQEAAAABFQRgAX8Bf2ACf38Bf2ADf39%2FAGAAAAIPAQNlbnYGbWVtb3J5AgAEAwkIAAABAgEAAAMGEAN%2FAUEAC38BQQALfwFBAAsHGQIIbG9hZF91dzgAAAp1bmNvbXByZXNzAAQIAQcKngUIxwEBB39BAC0AAEEBayIBQQBIBEAgAA8LQYDAByAAaiECIAFBAXEEQEEBQYHABxAEIQIFQYDAB0EAIAAQAwtBAEGAhA9BCBLine truncated <!doctype html><html><head><meta charset="utf8"><title>uw8-run</title><style>body,canvas,html{background-color:#202024;margin:0;padding:0}body,html{height:100%}body{display:grid;grid-template-rows:1fr}#screen{align-self:center;border:4px solid #303040;image-rendering:pixelated;justify-self:center}#message{background-color:rgba(0,0,0,.4);color:#c64;font:700 12pt sans-serif;padding:8px;position:absolute;width:calc(100% - 16px);z-index:2}@media (min-width:648px) and (min-height:488px){#screen{height:480px;width:640px}}@media (min-width:968px) and (min-height:728px){#screen{height:720px;width:960px}}</style></head><body> <canvas id="screen" width="320" height="240" tabindex="1"> </canvas> <div id="message"></div> </body><script type="module">function e(e){return e&&e.__esModule?e.default:e}class t extends AudioWorkletNode{constructor(e){super(e,"apu",{outputChannelCount:[2]})}}let A=(...e)=>new Uint8Array(...e),a=(...e)=>new Uint32Array(...e);let B=function(B,n={}){n.setMessage||(n.setMessage=(e,t)=>{t&&console.log("error: "+t)});let s,o,r,i,l=B.getContext("2d"),F=l.createImageData(320,240),g=n.devkitMode,c=0,d=null==n.keyboardElement?B:n.keyboardElement;if(d){let e=e=>{let t,A="keydown"==e.type;switch(e.code){case"ArrowUp":t=1;break;case"ArrowDown":t=2;break;case"ArrowLeft":t=4;break;case"ArrowRight":t=8;break;case"KeyZ":t=16;break;case"KeyX":t=32;break;case"KeyA":t=64;break;case"KeyS":t=128;break;case"KeyR":A&&f(o,!0);break;case"F9":A&&B.toBlob((e=>{u(e,".png")})),e.preventDefault();break;case"F10":A&&function(){if(r)return r.stop(),void(r=null);r=new MediaRecorder(B.captureStream(),{mimeType:"video/webm",videoBitsPerSecond:25e6});let e=[];r.ondataavailable=t=>{e.push(t.data)};let t=n.timerElement;t&&(t.hidden=!1,t.innerText="00:00");function A(){if(i){if(t){let e=Math.floor((Date.now()-i)/1e3);t.innerText=Math.floor(e/60).toString().padStart(2,"0")+":"+(e%60).toString().padStart(2,"0")}setTimeout(A,1e3)}}r.onstop=()=>{t&&(t.hidden=!0),u(new Blob(e,{type:"video/webm"}),".webm")},r.start(),i=Date.now(),setTimeout(A,1e3)}(),e.preventDefault()}A?c|=t:c&=~t};d.onkeydown=e,d.onkeyup=e}async function f(r,i){s&&(s(),s=null);let d=new AudioContext({sampleRate:44100}),u=!0,D=new AbortController;s=()=>{d.close(),u=!1,D.abort()};let E=r.byteLength;if(n.setMessage(E),0==E)return;await d.audioWorklet.addModule(e("data:application/javascript,let%20e%3D%28...e%29%3D%3Enew%20Uint8Array%28...e%29%3Bclass%20s%20extends%20AudioWorkletProcessor%7Bconstructor%28%29%7Bsuper%28%29%2Cthis.sampleIndex%3D0%2Cthis.port.onmessage%3Ds%3D%3E%7Bthis.memory%3Fe%28this.memory.buffer%2C80%2C32%29.set%28e%28s.data%29%29%3Athis.load%28s.data%5B0%5D%2Cs.data%5B1%5D%29%7D%7Dasync%20load%28e%2Cs%29%7Blet%20t%3Dnew%20WebAssembly.Memory%28%7Binitial%3A4%2Cmaximum%3A4%7D%29%2Ca%3D%7Benv%3A%7Bmemory%3At%7D%7D%3Bfor%28let%20e%20of%5B%22acos%22%2C%22asin%22%2C%22atan%22%2C%22atan2%22%2C%22cos%22%2C%22exp%22%2C%22log%22%2C%22sin%22%2C%22tan%22%2C%22pow%22%5D%29a.env%5Be%5D%3DMath%5Be%5D%3Bfor%28let%20e%3D9%3Be%3C64%3B%2B%2Be%29a.env%5B%22reserved%22%2Be%5D%3D%28%29%3D%3E%7B%7D%3Bfor%28let%20e%3D0%3Be%3C16%3B%2B%2Be%29a.env%5B%22g_reserved%22%2Be%5D%3D0%3Blet%20r%3Dasync%20e%3D%3E%28await%20WebAssembly.instantiate%28e%2Ca%29%29.instance%2Cn%3Dawait%20r%28e%29%3Bfor%28let%20e%20in%20n.exports%29a.env%5Be%5D%3Dn.exports%5Be%5D%3Blet%20o%3Dawait%20r%28s%29%3Bthis.memory%3Dt%2Cthis.snd%3Do.exports.snd%7C%7Cn.exports.gesSnd%2Cthis.port.postMessage%282%29%7Dprocess%28e%2Cs%2Ct%29%7Bif%28this.snd%29%7Blet%20e%3Ds%5B0%5D%2Ct%3Dthis.sampleIndex%2Ca%3De%5B0%5D.length%3Bfor%28let%20s%3D0%3Bs%3Ca%3B%2B%2Bs%29e%5B0%5D%5Bs%5D%3Dthis.snd%28t%2B%2B%29%2Ce%5B1%5D%5Bs%5D%3Dthis.snd%28t%2B%2B%29%3Bthis.sampleIndex%3D4294967295%26t%7Dreturn%210%7D%7DregisterProcessor%28%22apu%22%2Cs%29%3B"));let I,m=new t(d),C=0,Q=new Promise((e=>I=e)),p=e=>{C|=e,3==C&&I&&(I(!0),I=null)};m.port.onmessage=e=>p(e.data);let w=()=>{"suspended"==d.state?(n.startButton&&(n.startButton.style="",B.style="display:none"),(n.startButton||B).onclick=()=>{d.resume()}):(n.startButton&&(n.startButton.style="display:none",B.style=""),p(1))};d.onstatechange=w,w(),o=r;let b=window.location.pathname;if(E<=1024&&!i){let e="";for(let t of A(r))e+=String.fromCharCode(t);b+="#"+btoa(e),b!=window.location.pathname+window.location.hash&&history.pushState(null,null,b)}B.width=B.width;try{let t={initial:4};g||(t.maximum=4);let B,s=new WebAssembly.Memory(t),i=A(s.buffer),I={env:{memory:s}},C=e=>{if(B&&(!g||0!=A(e)[0])){i.set(A(e));let t=B.exports.load_uw8(e.byteLength);e=new ArrayBuffer(t),A(e).set(i.slice(0,t))}return e},p=async e=>(await WebAssembly.instantiate(e,I)).instance,w=async e=>C(await(await fetch(e)).arrayBuffer());B=await p(await w(e("data:application/wasm;base64,AGFzbQEAAAABFQRgAX8Bf2ACf38Bf2ADf39%2FAGAAAAIPAQNlbnYGbWVtb3J5AgAEAwkIAAABAgEAAAMGEAN%2FAUEAC38BQQALfwFBAAsHGQIIbG9hZF91dzgAAAp1bmNvbXByZXNzAAQIAQcKngUIxwEBB39BAC0AAEEBayIBQQBIBEAgAA8LQYDAByAAaiECIAFBAXEEQEEBQYHABxAEIQIFQYDAB0EAIAAQAwtBAEGAhA9BCBLine truncated