diff options
author | Mitya Selivanov <automainint@guattari.tech> | 2025-01-06 23:41:27 +0100 |
---|---|---|
committer | Mitya Selivanov <automainint@guattari.tech> | 2025-01-06 23:41:27 +0100 |
commit | eab2433a2daee814492548b95f16892adde0908d (patch) | |
tree | e08d2b82fc3525d937bb64475da5543b4628158c /index.htm.bak | |
parent | 3359f068dc9e8ac036f0f709aeccf11dfba3cf03 (diff) | |
download | reduced_system_layer-eab2433a2daee814492548b95f16892adde0908d.zip |
Impl rendering in AudioWorklet
Diffstat (limited to 'index.htm.bak')
-rw-r--r-- | index.htm.bak | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/index.htm.bak b/index.htm.bak new file mode 100644 index 0000000..cb1f474 --- /dev/null +++ b/index.htm.bak @@ -0,0 +1,415 @@ +<!DOCTYPE html> +<html style="height: 100%; overflow: hidden; overflow-x: hidden;"> + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width,initial-scale=1"> + <link rel='icon' type='image/gif' href='/favicon.gif'> + <title> Reduced System Layer </title> + </head> + <body style="margin: 0; height: 100%; overflow: hidden; overflow-x: hidden;"> + <canvas style="margin: 0; width: 100%; height: 100%;" id="frame" oncontextmenu="event.preventDefault()"></canvas> + <script type="worklet"> + registerProcessor( + 'Sound_Ring', + class Sound_Ring extends AudioWorkletProcessor { + constructor(options) { + super(); + + this.ring = options.processorOptions.ring; + + this.position = 0; + this.max_num_frames = this.ring.byteLength / 4; + } + + process(input, output, parameters) { + let num_channels = output[0].length; + let num_samples = output[0][0].length; + let num_frames = num_samples * num_channels; + + if (num_frames > this.max_num_frames) { + console.error("Sound buffer overflow"); + return true; + } + + let frames = new Float32Array(this.ring); + + if (num_frames <= this.max_num_frames - this.position) + for (let j = 0; j < num_channels; ++j) + for (let i = 0; i < num_samples; ++i) + output[0][j][i] = frames[this.position + i * num_channels + j]; + else { + let part_one = this.max_num_frames - this.position; + let part_two = num_frames - part_one; + + for (let j = 0; j < num_channels; ++j) { + for (let i = 0; i < part_one; ++i) + output[0][j][i] = frames[this.position + i * num_channels + j]; + for (let i = 0; i < part_two; ++i) + output[0][j][part_one + i] = frames[i * num_channels + j]; + } + } + + this.position = (this.position + num_frames) % this.max_num_frames; + return true; + } + } + ); + </script> + <script type="text/javascript"> + let key_map = { + "Backspace" : 8, + "Tab" : 9, + "Enter" : 10, + "ControlLeft" : 11, + "ControlRight" : 12, + "ShiftLeft" : 13, + "ShiftRight" : 14, + "AltLeft" : 15, + "AltRight" : 16, + "ArrowLeft" : 17, + "ArrowRight" : 18, + "ArrowUp" : 19, + "ArrowDown" : 20, + "Pause" : 21, + "Insert" : 22, + "Home" : 23, + "End" : 24, + "PageUp" : 25, + "PageDown" : 26, + "Escape" : 27, + "PrintScreen" : 28, + "Space" : 32, + "MetaLeft" : 33, + "MetaRight" : 34, + "Quote" : 39, + "Comma" : 44, + "Minus" : 45, + "Period" : 46, + "Slash" : 47, + "Digit0" : 48, + "Digit1" : 49, + "Digit2" : 50, + "Digit3" : 51, + "Digit4" : 52, + "Digit5" : 53, + "Digit6" : 54, + "Digit7" : 55, + "Digit8" : 56, + "Digit9" : 57, + "Semicolon" : 59, + "Equal" : 61, + "BracketLeft" : 91, + "Backslash" : 92, + "BracketRight" : 93, + "Backquote" : 96, + "KeyA" : 97, + "KeyB" : 98, + "KeyC" : 99, + "KeyD" : 100, + "KeyE" : 101, + "KeyF" : 102, + "KeyG" : 103, + "KeyH" : 104, + "KeyI" : 105, + "KeyJ" : 106, + "KeyK" : 107, + "KeyL" : 108, + "KeyM" : 109, + "KeyN" : 110, + "KeyO" : 111, + "KeyP" : 112, + "KeyQ" : 113, + "KeyR" : 114, + "KeyS" : 115, + "KeyT" : 116, + "KeyU" : 117, + "KeyV" : 118, + "KeyW" : 119, + "KeyX" : 120, + "KeyY" : 121, + "KeyZ" : 122, + "Delete" : 127, + "F1" : 145, + "F2" : 146, + "F3" : 147, + "F4" : 148, + "F5" : 149, + "F6" : 150, + "F7" : 151, + "F8" : 152, + "F9" : 153, + "F10" : 154, + "F11" : 155, + "F12" : 156, + "F13" : 157, + "F14" : 158, + "F15" : 159, + "F16" : 160, + "F17" : 161, + "F18" : 162, + "F19" : 163, + "F20" : 164, + "F21" : 165, + "F22" : 166, + "F23" : 167, + "F24" : 168, + }; + + function key_from_code(code) { + if (code in key_map) + return key_map[code]; + return 0; + }; + + function mod_from_event(ev) { + let mod = 0; + if (ev.ctrlKey) mod |= 1; + if (ev.shiftKey) mod |= 2; + if (ev.altKey) mod |= 4; + if (ev.metaKey) mod |= 8; + return mod; + }; + + function string_from_memory(bytes, offset) { + let len = 0; + while (bytes[offset + len] != 0) + ++len; + return new TextDecoder("utf8").decode(bytes.subarray(offset, offset + len)); + } + + async function run(attrs) { + let wait_for_events = false; + let sleep_duration = 0; + + let canvas; + let program; + let memory; + let pixels_address; + let context; + let animation_frame; + + let sound_ready = false; + let sound_context; + let sound_init; + let sound_node; + let sound_num_channels; + let sound_buffer_address; + let sound_max_num_frames; + + let sound_shared_ring; + let sound_position; + + canvas = attrs.canvas; + context = canvas.getContext("2d"); + + sound_init = async () => { + if (sound_ready || sound_context !== undefined) + return; + + sound_context = new AudioContext({ + sampleRate : program.instance.exports.js_sample_rate(), + }); + + let blob = new Blob( + [ document.querySelector("script[type=worklet]").innerText ], + { type : "application/javascript", } + ); + + await sound_context.audioWorklet.addModule(URL.createObjectURL(blob)); + + sound_num_channels = program.instance.exports.js_num_channels(); + sound_max_num_frames = program.instance.exports.js_max_num_audio_frames(); + sound_buffer_address = program.instance.exports.js_sound_buffer(); + + sound_shared_ring = new SharedArrayBuffer(sound_max_num_frames * 4); + sound_position = 0; + + sound_node = new AudioWorkletNode( + sound_context, + "Sound_Ring", + { numberOfInputs : 0, + outputChannelCount : [ sound_num_channels ], + processorOptions : { ring : sound_shared_ring, }, } + ); + + sound_node.connect(sound_context.destination); + + program.instance.exports.js_sound_sync(); + + sound_ready = true; + }; + + program = await WebAssembly.instantiateStreaming( + attrs.wasm, + { + "wasi_snapshot_preview1" : { + clock_time_get : () => { return Date.now(); }, + args_sizes_get : () => { throw new Error("Unexpected args_sizes_get call"); }, + args_get : () => { throw new Error("Unexpected args_get call"); }, + proc_exit : () => { throw new Error("Unexpected proc_exit call"); }, + fd_close : () => { throw new Error("Unexpected fd_close call"); }, + fd_write : () => { throw new Error("Unexpected fd_write call"); }, + fd_seek : () => { throw new Error("Unexpected fd_seek call"); }, + }, + "env" : { + p_init : () => { + program.instance.exports.js_init(); + memory = new Uint8Array(program.instance.exports.memory.buffer); + pixels_address = program.instance.exports.js_pixels(); + document.title = string_from_memory(memory, program.instance.exports.js_title()); + }, + p_render_frame_impl : () => { + let data = new ImageData( + new Uint8ClampedArray( + memory.subarray( + pixels_address, + pixels_address + 4 * canvas.width * canvas.height + ) + ), + canvas.width, + canvas.height + ); + + context.putImageData(data, 0, 0); + }, + p_clipboard_write : (size, text) => { + navigator.clipboard.writeText( + new TextDecoder().decode(memory.subarray(text, text + size)) + ).catch((e) => { console.error(e); }); + }, + p_cleanup : () => {}, + p_wait_events_impl : () => { wait_for_events = true; }, + p_sleep_for_impl : (time) => { sleep_duration += time; }, + p_time_impl : Date.now, + + p_handle_audio_impl : (samples_elapsed) => { + if (!sound_ready) + return; + + let num_frames = samples_elapsed * sound_num_channels; + + if (num_frames > sound_max_num_frames) { + console.error("Sound buffer overflow"); + return; + } + + let dst = new Float32Array(sound_shared_ring); + let src = new Float32Array(memory.buffer, sound_buffer_address, sound_max_num_frames); + + if (num_frames <= sound_max_num_frames - sound_position) + for (let i = 0; i < num_frames; ++i) { + dst[sound_position + i] = src[sound_position + i]; + src[sound_position + i] = 0.0; + } + else { + let part_one = sound_max_num_frames - sound_position; + let part_two = num_frames - part_one; + + for (let i = 0; i < part_one; ++i) { + dst[sound_position + i] = src[sound_position + i]; + src[sound_position + i] = 0.0; + } + for (let i = 0; i < part_two; ++i) { + dst[i] = src[i]; + src[i] = 0.0; + } + } + + sound_position = (sound_position + samples_elapsed * sound_num_channels) % sound_max_num_frames; + }, + + floor : Math.floor, + ceil : Math.ceil, + sqrt : Math.sqrt, + pow : Math.pow, + log : Math.log, + log2 : Math.log2, + log10 : Math.log10, + exp : Math.exp, + sin : Math.sin, + cos : Math.cos, + tan : Math.tan, + asin : Math.asin, + acos : Math.acos, + atan : Math.atan, + atan2 : Math.atan2, + }, + } + ); + + program.instance.exports.js_main(document.location.href); + + animation_frame = (time) => { + if (attrs.fit_window) { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + } + + wait_for_events = false; + program.instance.exports.js_frame(canvas.width, canvas.height); + + if (!wait_for_events) { + if (sleep_duration > 0) { + let timeout = sleep_duration; + sleep_duration = 0; + setTimeout(() => { + window.requestAnimationFrame(animation_frame); + }, timeout); + } else + window.requestAnimationFrame(animation_frame); + } + }; + + window.addEventListener("resize", (ev) => { + window.requestAnimationFrame(animation_frame); + }); + + canvas.addEventListener("mousedown", (ev) => { + sound_init(); + program.instance.exports.js_mousedown(ev.buttons); + window.requestAnimationFrame(animation_frame); + }); + + canvas.addEventListener("mouseup", (ev) => { + program.instance.exports.js_mouseup(ev.buttons); + window.requestAnimationFrame(animation_frame); + }); + + canvas.addEventListener("mousemove", (ev) => { + program.instance.exports.js_mousemove(ev.clientX, ev.clientY); + window.requestAnimationFrame(animation_frame); + }); + + window.addEventListener("keydown", (ev) => { + ev.preventDefault(); + if (!ev.repeat) { + let mod = mod_from_event(ev); + let key = key_from_code(ev.code); + if (key != 0) + program.instance.exports.js_keydown(key, mod); + window.requestAnimationFrame(animation_frame); + } + }); + + window.addEventListener("keyup", (ev) => { + ev.preventDefault(); + if (!ev.repeat) { + let mod = mod_from_event(ev); + let key = key_from_code(ev.code); + if (key != 0) + program.instance.exports.js_keyup(key, mod); + window.requestAnimationFrame(animation_frame); + } + }); + + window.requestAnimationFrame(animation_frame); + } + + run({ + canvas : document.getElementById("frame"), + wasm : fetch("index.wasm"), + fit_window : true, + }).catch((e) => console.error(e)); + </script> + </body> +</html> |