diff --git a/.prettierignore b/.prettierignore index 010ad28a6..315c77dd0 100644 --- a/.prettierignore +++ b/.prettierignore @@ -10,4 +10,5 @@ paper pnpm-lock.yaml pnpm-workspace.yaml **/dev-dist +superdough-wasm website/.astro diff --git a/packages/superdough-wasm/.gitignore b/packages/superdough-wasm/.gitignore new file mode 100644 index 000000000..4f0e5318e --- /dev/null +++ b/packages/superdough-wasm/.gitignore @@ -0,0 +1 @@ +rustsaw/target \ No newline at end of file diff --git a/packages/superdough-wasm/README.md b/packages/superdough-wasm/README.md new file mode 100644 index 000000000..75a2e9c0c --- /dev/null +++ b/packages/superdough-wasm/README.md @@ -0,0 +1,57 @@ +# superdough-wasm + +This is just a very early experiment to find out how to run wasm in an AudioWorklet. +WASM can be compiled from several languages, which are tested here.. + +## zig + + + +```sh +# (re)compile dsp.zig +brew install zig # prequisite +cd zigsaw +zig build-lib zigsaw.zig -target wasm32-freestanding -dynamic -rdynamic -O ReleaseSmall # build +npx http-server .. -o # run +``` + +wasm file size: 690B + +## rust + + + +```sh +# https://www.rust-lang.org/tools/install +cargo install wasm-pack # prequisite +cd rustsaw +wasm-pack build --target bundler # build +npx http-server .. -o # run +``` + +wasm file size: 653B + +## c + + + +```sh +# brew install emscripten +cd csaw +emcc -O2 csaw.c -o csaw # build +npx http-server .. -o +``` + +wasm file size: 680B + +## assemblyscript + + + +```sh +cd ascsaw +# npm i +npm run asbuild # build +``` + +wasm file size: 122B ! diff --git a/packages/superdough-wasm/ascsaw/asconfig.json b/packages/superdough-wasm/ascsaw/asconfig.json new file mode 100644 index 000000000..fab108684 --- /dev/null +++ b/packages/superdough-wasm/ascsaw/asconfig.json @@ -0,0 +1,22 @@ +{ + "targets": { + "debug": { + "outFile": "build/debug.wasm", + "textFile": "build/debug.wat", + "sourceMap": true, + "debug": true + }, + "release": { + "outFile": "build/release.wasm", + "textFile": "build/release.wat", + "sourceMap": true, + "optimizeLevel": 3, + "shrinkLevel": 2, + "converge": false, + "noAssert": false + } + }, + "options": { + "bindings": "esm" + } +} \ No newline at end of file diff --git a/packages/superdough-wasm/ascsaw/assembly/index.ts b/packages/superdough-wasm/ascsaw/assembly/index.ts new file mode 100644 index 000000000..2e8ca7b02 --- /dev/null +++ b/packages/superdough-wasm/ascsaw/assembly/index.ts @@ -0,0 +1,3 @@ +export function saw(t: f64, f: f64): f64 { + return (((f * t * 1.0) % 1.0) - 0.5) * 2.0; +} diff --git a/packages/superdough-wasm/ascsaw/assembly/tsconfig.json b/packages/superdough-wasm/ascsaw/assembly/tsconfig.json new file mode 100644 index 000000000..e28fcf257 --- /dev/null +++ b/packages/superdough-wasm/ascsaw/assembly/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": [ + "./**/*.ts" + ] +} \ No newline at end of file diff --git a/packages/superdough-wasm/ascsaw/build/.gitignore b/packages/superdough-wasm/ascsaw/build/.gitignore new file mode 100644 index 000000000..d6b7ef32c --- /dev/null +++ b/packages/superdough-wasm/ascsaw/build/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/packages/superdough-wasm/ascsaw/package-lock.json b/packages/superdough-wasm/ascsaw/package-lock.json new file mode 100644 index 000000000..d808c9688 --- /dev/null +++ b/packages/superdough-wasm/ascsaw/package-lock.json @@ -0,0 +1,54 @@ +{ + "name": "ascsaw", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ascsaw", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "assemblyscript": "^0.27.22" + } + }, + "node_modules/assemblyscript": { + "version": "0.27.22", + "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.27.22.tgz", + "integrity": "sha512-6ClobsR4Hxn6K0daYp/+n9qWTqVbpdVeSGSVDqRvUEz66vvFb8atS6nLm+fnQ54JXuXmzLQy0uWYYgB8G59btQ==", + "dev": true, + "dependencies": { + "binaryen": "116.0.0-nightly.20231102", + "long": "^5.2.1" + }, + "bin": { + "asc": "bin/asc.js", + "asinit": "bin/asinit.js" + }, + "engines": { + "node": ">=16", + "npm": ">=7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/assemblyscript" + } + }, + "node_modules/binaryen": { + "version": "116.0.0-nightly.20231102", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-116.0.0-nightly.20231102.tgz", + "integrity": "sha512-aPU9tlKdw/gcXx6u4PxtDgOtGjg/ZKnYdk23ctYb70GxZgPhWnGWmnBt01aV5dt5yFFo2V4rbB7SzpSFhViFQA==", + "dev": true, + "bin": { + "wasm-opt": "bin/wasm-opt", + "wasm2js": "bin/wasm2js" + } + }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "dev": true + } + } +} diff --git a/packages/superdough-wasm/ascsaw/package.json b/packages/superdough-wasm/ascsaw/package.json new file mode 100644 index 000000000..27371a3c1 --- /dev/null +++ b/packages/superdough-wasm/ascsaw/package.json @@ -0,0 +1,25 @@ +{ + "name": "ascsaw", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "node tests", + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release", + "start": "npx serve ." + }, + "author": "", + "license": "ISC", + "devDependencies": { + "assemblyscript": "^0.27.22" + }, + "type": "module", + "exports": { + ".": { + "import": "./build/release.js", + "types": "./build/release.d.ts" + } + } +} \ No newline at end of file diff --git a/packages/superdough-wasm/csaw/.gitignore b/packages/superdough-wasm/csaw/.gitignore new file mode 100644 index 000000000..69d4d83ec --- /dev/null +++ b/packages/superdough-wasm/csaw/.gitignore @@ -0,0 +1 @@ +csaw \ No newline at end of file diff --git a/packages/superdough-wasm/csaw/csaw.c b/packages/superdough-wasm/csaw/csaw.c new file mode 100644 index 000000000..6cc7594fb --- /dev/null +++ b/packages/superdough-wasm/csaw/csaw.c @@ -0,0 +1,7 @@ +#include +#include + +EMSCRIPTEN_KEEPALIVE +double saw(double t, double f) { + return fmod((f * t * 1.0), 1.0) - 0.5 * 2.0; +} diff --git a/packages/superdough-wasm/csaw/csaw.wasm b/packages/superdough-wasm/csaw/csaw.wasm new file mode 100755 index 000000000..5e77520bf Binary files /dev/null and b/packages/superdough-wasm/csaw/csaw.wasm differ diff --git a/packages/superdough-wasm/index.html b/packages/superdough-wasm/index.html new file mode 100644 index 000000000..9b4002c4a --- /dev/null +++ b/packages/superdough-wasm/index.html @@ -0,0 +1,11 @@ + + + + WASM AudioWorklet Demo + + + + + + + diff --git a/packages/superdough-wasm/main.js b/packages/superdough-wasm/main.js new file mode 100644 index 000000000..0206df915 --- /dev/null +++ b/packages/superdough-wasm/main.js @@ -0,0 +1,24 @@ +let ac; +document.getElementById('play').addEventListener('click', async () => { + ac = ac || new AudioContext(); + await ac.resume(); + await ac.audioWorklet.addModule('./worklet.js'); + const node = new AudioWorkletNode(ac, 'saw-processor'); + + //let res = await fetch('./zigsaw/zigsaw.wasm'); + // let res = await fetch('./csaw/csaw.wasm'); + let res = await fetch('./ascsaw/build/release.wasm'); + //let res = await fetch('./rustsaw/pkg/rustsaw_bg.wasm'); + const buffer = await res.arrayBuffer(); + node.port.onmessage = (e) => { + if (e.data === 'OK') { + console.log('worklet ready'); + } + }; + node.port.postMessage({ webassembly: buffer }); + node.connect(ac.destination); + + document.getElementById('freq').addEventListener('input', async (e) => { + node.port.postMessage({ frequency: e.target.value }); + }); +}); diff --git a/packages/superdough-wasm/rustsaw/Cargo.lock b/packages/superdough-wasm/rustsaw/Cargo.lock new file mode 100644 index 000000000..b5a16f217 --- /dev/null +++ b/packages/superdough-wasm/rustsaw/Cargo.lock @@ -0,0 +1,123 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "proc-macro2" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustsaw" +version = "0.1.0" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" diff --git a/packages/superdough-wasm/rustsaw/Cargo.toml b/packages/superdough-wasm/rustsaw/Cargo.toml new file mode 100644 index 000000000..a59f89234 --- /dev/null +++ b/packages/superdough-wasm/rustsaw/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rustsaw" +version = "0.1.0" +authors = ["Your Name "] +description = "A sample project with wasm-pack" +license = "MIT/Apache-2.0" +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wasm-bindgen = "0.2" diff --git a/packages/superdough-wasm/rustsaw/src/lib.rs b/packages/superdough-wasm/rustsaw/src/lib.rs new file mode 100644 index 000000000..6f119c68e --- /dev/null +++ b/packages/superdough-wasm/rustsaw/src/lib.rs @@ -0,0 +1,6 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn saw(t: f64, f: f64) -> f64 { + return (((f * t * 1.0) % 1.0) - 0.5) * 2.0; +} diff --git a/packages/superdough-wasm/worklet.js b/packages/superdough-wasm/worklet.js new file mode 100644 index 000000000..b926bdb4b --- /dev/null +++ b/packages/superdough-wasm/worklet.js @@ -0,0 +1,38 @@ +class SawProcessor extends AudioWorkletProcessor { + constructor() { + super(); + this.t = 0; // samples passed + this.f = 110; + this.port.onmessage = (e) => { + const key = Object.keys(e.data)[0]; + const value = e.data[key]; + switch (key) { + case 'webassembly': + WebAssembly.instantiate(value, this.importObject).then((result) => { + this.api = result.instance.exports; + this.port.postMessage('OK'); + }); + break; + case 'frequency': + this.f = value; + } + }; + } + + process(inputs, outputs, parameters) { + if (this.api) { + const output = outputs[0]; + for (let i = 0; i < output[0].length; i++) { + let t = this.t; + let out = 0; + out = this.api.saw(t / 44100, this.f); + output.forEach((channel) => { + channel[i] = out; + }); + this.t++; + } + } + return true; + } +} +registerProcessor('saw-processor', SawProcessor); diff --git a/packages/superdough-wasm/zigsaw/zigsaw.wasm b/packages/superdough-wasm/zigsaw/zigsaw.wasm new file mode 100755 index 000000000..c2928a3e4 Binary files /dev/null and b/packages/superdough-wasm/zigsaw/zigsaw.wasm differ diff --git a/packages/superdough-wasm/zigsaw/zigsaw.zig b/packages/superdough-wasm/zigsaw/zigsaw.zig new file mode 100644 index 000000000..c9b4fb8c0 --- /dev/null +++ b/packages/superdough-wasm/zigsaw/zigsaw.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +export fn saw(t: f64, f: f64) f64 { + return ((@mod(f * t, 1.0)) - 0.5) * 2.0; +}