pub const PARAM_BANK: usize = 32;
pub const SHIM_TEMPLATE: &str = r#"
const BANK = __BANK__;
class AwsmWasm extends AudioWorkletProcessor {
static get parameterDescriptors() {
const d = [];
for (let i = 0; i < BANK; i++) {
d.push({ name: 'p' + i, defaultValue: 0,
minValue: -3.4e38, maxValue: 3.4e38, automationRate: 'k-rate' });
}
return d;
}
constructor(opts) {
super();
const po = (opts && opts.processorOptions) || {};
this.ok = false;
try {
this.inst = new WebAssembly.Instance(po.module, {});
const ex = this.inst.exports;
this.ex = ex;
this.mem = ex.memory;
this.maxFrames = 128;
if (ex.init) ex.init(sampleRate, this.maxFrames);
this.paramCount = ex.param_count ? (ex.param_count() | 0) : 0;
this.inPtr = ex.input_ptr ? (ex.input_ptr() | 0) : 0;
this.outPtr = ex.output_ptr ? (ex.output_ptr() | 0) : 0;
this.parPtr = ex.params_ptr ? (ex.params_ptr() | 0) : 0;
this.chans = ex.channels ? Math.max(1, ex.channels() | 0) : 1;
this.run = ex.process || null;
this.ok = !!(this.mem && this.run);
} catch (e) {
// Leave ok=false → passes audio through untouched.
this.err = String(e);
}
}
process(inputs, outputs, parameters) {
const out = outputs[0];
if (!out || !out[0]) return true;
const frames = out[0].length;
const inp = inputs[0];
if (!this.ok) {
// Pass-through: copy input to output if present, else silence.
for (let c = 0; c < out.length; c++) {
if (inp && inp[c]) out[c].set(inp[c]); else out[c].fill(0);
}
return true;
}
const buf = this.mem.buffer;
// Parameters → wasm memory (k-rate: one value per quantum).
if (this.parPtr && this.paramCount) {
const pv = new Float32Array(buf, this.parPtr, this.paramCount);
for (let i = 0; i < this.paramCount; i++) {
const a = parameters['p' + i];
pv[i] = a ? a[0] : 0;
}
}
const CH = this.chans, MF = this.maxFrames;
// Inputs → wasm planar regions (channel c at byte offset ptr + c*MF*4).
// A mono input is duplicated to every channel.
if (this.inPtr) {
for (let c = 0; c < CH; c++) {
const region = new Float32Array(buf, this.inPtr + c * MF * 4, frames);
const src = inp && inp.length ? inp[Math.min(c, inp.length - 1)] : null;
if (src) region.set(src.subarray(0, frames)); else region.fill(0);
}
}
this.run(frames);
// Outputs ← wasm planar regions (clamp channel index to what the module has).
if (this.outPtr) {
for (let c = 0; c < out.length; c++) {
const region = new Float32Array(buf, this.outPtr + Math.min(c, CH - 1) * MF * 4, frames);
out[c].set(region.subarray(0, frames));
}
}
return true;
}
}
registerProcessor('awsm-wasm', AwsmWasm);
"#;
pub const PROCESSOR_NAME: &str = "awsm-wasm";
pub fn shim_source() -> String {
SHIM_TEMPLATE.replace("__BANK__", &PARAM_BANK.to_string())
}