summaryrefslogtreecommitdiff
path: root/index.htm
diff options
context:
space:
mode:
Diffstat (limited to 'index.htm')
-rw-r--r--index.htm672
1 files changed, 367 insertions, 305 deletions
diff --git a/index.htm b/index.htm
index d4729bf..33d6c2d 100644
--- a/index.htm
+++ b/index.htm
@@ -10,151 +10,260 @@
<canvas style="margin: 0; width: 100%; height: 100%;" id="frame" oncontextmenu="event.preventDefault()"></canvas>
<script type="worklet">
registerProcessor(
- 'Sound_Ring',
+ 'Sound_Node',
class Sound_Ring extends AudioWorkletProcessor {
constructor(options) {
super();
- this.ring = options.processorOptions.ring;
+ this.frame_width = 0;
+ this.frame_height = 0;
+
+ let event_handlers = {
+ wasm : (ev) => {
+ WebAssembly.instantiate(
+ ev.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_clipboard_write : (size, text) => {
+ let text_buffer = new ArrayBuffer(size);
+ new Uint8Array(text_buffer).set(new Uint8Array(this.memory_buffer, text, size));
+
+ this.port.postMessage({
+ id : "clipboard",
+ text_buffer : text_buffer,
+ });
+ },
+ p_time_impl : Date.now,
+
+ 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,
+ },
+ }
+ ).then((program) => {
+ program.exports.js_main(options.processorOptions.href);
+
+ this.memory_buffer = program.exports.memory.buffer;
+ this.sound_buffer_address = program.exports.js_sound_buffer();
+ this.pixels_address = program.exports.js_pixels();
+ this.frames = new Float32Array(this.memory_buffer, this.sound_buffer_address, program.exports.js_max_num_audio_frames());
+
+ let bytes = new Uint8Array(this.memory_buffer);
+ let title_address = program.exports.js_title();
+ let len = 0;
+ while (bytes[title_address + len] != 0)
+ ++len;
+ let title_buffer = new ArrayBuffer(len);
+ new Uint8Array(title_buffer).set(bytes.subarray(title_address, title_address + len));
+
+ this.port.postMessage({
+ id : "init",
+ title_buffer : title_buffer,
+ });
+
+ this.program = program;
+ });
+ },
+
+ resize : (ev) => {
+ this.frame_width = ev.width;
+ this.frame_height = ev.height;
+ },
+
+ mousedown : (ev) => {
+ if (this.program === undefined)
+ return;
+
+ this.program.exports.js_mousedown(ev.buttons);
+ },
+
+ mouseup : (ev) => {
+ if (this.program === undefined)
+ return;
+
+ this.program.exports.js_mouseup(ev.buttons);
+ },
+
+ mousemove : (ev) => {
+ if (this.program === undefined)
+ return;
+
+ this.program.exports.js_mousemove(ev.x, ev.y);
+ },
+
+ keydown : (ev) => {
+ if (this.program === undefined)
+ return;
+
+ this.program.exports.js_keydown(ev.key, ev.mod);
+ },
+
+ keyup : (ev) => {
+ if (this.program === undefined)
+ return;
- this.position = 0;
- this.max_num_frames = this.ring.byteLength / 4;
+ this.program.exports.js_keyup(ev.key, ev.mod);
+ },
+ };
+
+ this.port.onmessage = (ev) => {
+ if (ev.data.id in event_handlers)
+ event_handlers[ev.data.id](ev.data);
+ };
}
process(input, output, parameters) {
+ if (this.program === undefined)
+ return true;
+
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;
- }
+ this.program.exports.js_frame(this.frame_width, this.frame_height, num_samples);
- 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];
- }
- }
+ let pixels_size = this.frame_width * this.frame_height * 4;
+ let pixels_buffer = new ArrayBuffer(pixels_size);
+ new Uint8Array(pixels_buffer).set(new Uint8Array(this.memory_buffer, this.pixels_address, pixels_size));
+
+ this.port.postMessage({
+ id : "frame",
+ pixels_buffer : pixels_buffer,
+ });
+
+ for (let j = 0; j < num_channels; ++j)
+ for (let i = 0; i < num_samples; ++i)
+ output[0][j][i] = this.frames[j * num_samples + i];
- 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) {
- switch (code) {
- case "Backspace": return 8;
- case "Tab": return 9;
- case "Enter": return 10;
- case "ControlLeft": return 11;
- case "ControlRight": return 12;
- case "ShiftLeft": return 13;
- case "ShiftRight": return 14;
- case "AltLeft": return 15;
- case "AltRight": return 16;
- case "ArrowLeft": return 17;
- case "ArrowRight": return 18;
- case "ArrowUp": return 19;
- case "ArrowDown": return 20;
- case "Pause": return 21;
- case "Insert": return 22;
- case "Home": return 23;
- case "End": return 24;
- case "PageUp": return 25;
- case "PageDown": return 26;
- case "Escape": return 27;
- case "PrintScreen": return 28;
- case "Space": return 32;
- case "MetaLeft": return 33;
- case "MetaRight": return 34;
- case "Quote": return 39;
- case "Comma": return 44;
- case "Minus": return 45;
- case "Period": return 46;
- case "Slash": return 47;
- case "Digit0": return 48;
- case "Digit1": return 49;
- case "Digit2": return 50;
- case "Digit3": return 51;
- case "Digit4": return 52;
- case "Digit5": return 53;
- case "Digit6": return 54;
- case "Digit7": return 55;
- case "Digit8": return 56;
- case "Digit9": return 57;
- case "Semicolon": return 59;
- case "Equal": return 61;
- case "BracketLeft": return 91;
- case "Backslash": return 92;
- case "BracketRight": return 93;
- case "Backquote": return 96;
- case "KeyA": return 97;
- case "KeyB": return 98;
- case "KeyC": return 99;
- case "KeyD": return 100;
- case "KeyE": return 101;
- case "KeyF": return 102;
- case "KeyG": return 103;
- case "KeyH": return 104;
- case "KeyI": return 105;
- case "KeyJ": return 106;
- case "KeyK": return 107;
- case "KeyL": return 108;
- case "KeyM": return 109;
- case "KeyN": return 110;
- case "KeyO": return 111;
- case "KeyP": return 112;
- case "KeyQ": return 113;
- case "KeyR": return 114;
- case "KeyS": return 115;
- case "KeyT": return 116;
- case "KeyU": return 117;
- case "KeyV": return 118;
- case "KeyW": return 119;
- case "KeyX": return 120;
- case "KeyY": return 121;
- case "KeyZ": return 122;
- case "Delete": return 127;
- case "F1": return 145;
- case "F2": return 146;
- case "F3": return 147;
- case "F4": return 148;
- case "F5": return 149;
- case "F6": return 150;
- case "F7": return 151;
- case "F8": return 152;
- case "F9": return 153;
- case "F10": return 154;
- case "F11": return 155;
- case "F12": return 156;
- case "F13": return 157;
- case "F14": return 158;
- case "F15": return 159;
- case "F16": return 160;
- case "F17": return 161;
- case "F18": return 162;
- case "F19": return 163;
- case "F20": return 164;
- case "F21": return 165;
- case "F22": return 166;
- case "F23": return 167;
- case "F24": return 168;
- }
+ if (code in key_map)
+ return key_map[code];
return 0;
};
@@ -167,44 +276,75 @@
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 sound_sample_rate = 44100; // TODO
+ let sound_num_channels = 2; // TODO
+
+ let frame_width = 0;
+ let frame_height = 0;
let canvas;
- let program;
- let memory;
- let pixels_address;
let context;
+ let pixels_buffer;
let animation_frame;
let sound_ready = false;
- let sound_context;
let sound_init;
+ let sound_context;
let sound_node;
- let sound_num_channels;
- let sound_buffer_address;
- let sound_max_num_frames;
-
- let sound_shared_ring;
- let sound_position = 0;
canvas = attrs.canvas;
context = canvas.getContext("2d");
- sound_init = async () => {
- if (sound_ready || sound_context !== undefined)
- return;
+ context.fillStyle = "#000";
+ context.fillRect(0, 0, canvas.width, canvas.height);
+
+ context.fillStyle = "#fff";
+ context.font = "1.7em serif";
+ context.textAlign = "center";
+ context.textBaseline = "middle";
+ context.fillText("Click to run", canvas.width / 2.0, canvas.height / 2.0);
+
+ animation_frame = () => {
+ if (attrs.fit_window) {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ }
+
+ if (frame_width != canvas.width || frame_height != canvas.height) {
+ frame_width = canvas.width;
+ frame_height = canvas.height;
+
+ sound_node.port.postMessage({
+ id : "resize",
+ width : frame_width,
+ height : frame_height,
+ });
+ }
+
+ let num_bytes = 4 * canvas.width * canvas.height;
+
+ if (pixels_buffer !== undefined && pixels_buffer.byteLength >= num_bytes) {
+ let pixels = new Uint8ClampedArray(pixels_buffer, 0, num_bytes);
+ let data = new ImageData(
+ pixels,
+ canvas.width,
+ canvas.height
+ );
+
+ context.putImageData(data, 0, 0);
+ } else {
+ context.fillStyle = "#000";
+ context.fillRect(0, 0, canvas.width, canvas.height);
+ }
+
+ window.requestAnimationFrame(animation_frame);
+ };
+
+ sound_init = async () => {
sound_context = new AudioContext({
- sampleRate : program.instance.exports.js_sample_rate(),
+ sampleRate : sound_sample_rate,
});
let blob = new Blob(
@@ -214,194 +354,116 @@
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_node = new AudioWorkletNode(
sound_context,
- "Sound_Ring",
- { numberOfInputs : 0,
- outputChannelCount : [ sound_num_channels ],
- processorOptions : { ring : sound_shared_ring, }, }
+ "Sound_Node",
+ { numberOfInputs : 0,
+ outputChannelCount : [ sound_num_channels ],
+ processorOptions : { href : document.location.href }, }
);
sound_node.connect(sound_context.destination);
- program.instance.exports.js_sound_sync();
-
- sound_ready = true;
- };
+ let message_handlers = {
+ init : (ev) => {
+ document.title = new TextDecoder("utf8").decode(new Uint8Array(ev.title_buffer));
+ 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"); },
+ window.requestAnimationFrame(animation_frame);
},
- "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.subarray(sound_buffer_address, sound_buffer_address + sound_max_num_frames * 4));
-
- 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,
-
- memset : (dst, val, num) => { program.instance.exports.js_memset(dst, val, num); },
- memcpy : (dst, src, num) => { program.instance.exports.js_memcpy(dst, src, num); },
+ clipboard : (ev) => {
+ navigator.clipboard.writeText(new TextDecoder("utf8").decode(new Uint8Array(ev.text_buffer)));
},
- }
- );
- program.instance.exports.js_main(document.location.href);
+ frame : (ev) => {
+ pixels_buffer = ev.pixels_buffer;
+ let pixels = new Uint8Array(pixels_buffer);
+ }
+ };
- animation_frame = (time) => {
- if (attrs.fit_window) {
- canvas.width = window.innerWidth;
- canvas.height = window.innerHeight;
- }
+ sound_node.port.onmessage = (ev) => {
+ if (ev.data.id in message_handlers)
+ message_handlers[ev.data.id](ev.data);
+ };
- 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);
- }
+ sound_node.port.postMessage({
+ id : "wasm",
+ wasm : await WebAssembly.compileStreaming(attrs.wasm),
+ });
};
- 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);
+ if (!sound_ready) {
+ sound_init();
+ sound_init = () => {};
+ return;
+ }
+
+ sound_node.port.postMessage({
+ id : "mousedown",
+ buttons : ev.buttons,
+ });
});
canvas.addEventListener("mouseup", (ev) => {
- program.instance.exports.js_mouseup(ev.buttons);
- window.requestAnimationFrame(animation_frame);
+ if (!sound_ready)
+ return;
+
+ sound_node.port.postMessage({
+ id : "mouseup",
+ buttons : ev.buttons,
+ });
});
canvas.addEventListener("mousemove", (ev) => {
- program.instance.exports.js_mousemove(ev.clientX, ev.clientY);
- window.requestAnimationFrame(animation_frame);
+ if (!sound_ready)
+ return;
+
+ sound_node.port.postMessage({
+ id : "mousemove",
+ x : ev.clientX,
+ y : ev.clientY,
+ });
});
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);
- }
+ if (!sound_ready)
+ return;
+ if (ev.repeat)
+ return;
+ let mod = mod_from_event(ev);
+ let key = key_from_code(ev.code);
+ if (key == 0)
+ return;
+
+ console.log(ev);
+
+ sound_node.port.postMessage({
+ id : "keydown",
+ mod : mod,
+ key : key,
+ });
});
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);
- }
- });
+ if (!sound_ready)
+ return;
+ if (ev.repeat)
+ return;
+ let mod = mod_from_event(ev);
+ let key = key_from_code(ev.code);
+ if (key == 0)
+ return;
- window.requestAnimationFrame(animation_frame);
+ sound_node.port.postMessage({
+ id : "keyup",
+ mod : mod,
+ key : key,
+ });
+ });
}
run({
@@ -411,4 +473,4 @@
}).catch((e) => console.error(e));
</script>
</body>
-</html>
+</html>