class BytebeatProcessor extends AudioWorkletProcessor { constructor() { super() this.t = 0 this.a = 8 this.b = 16 this.c = 32 this.d = 64 this.formula = null this.compiledFormula = null this.sampleRate = 8000 this.duration = 4 this.loopLength = this.sampleRate * this.duration this.playbackRate = 1.0 this.error = false this.port.onmessage = (event) => { const { type, value } = event.data switch (type) { case 'formula': this.setFormula(value) break case 'variables': this.a = value.a ?? this.a this.b = value.b ?? this.b this.c = value.c ?? this.c this.d = value.d ?? this.d break case 'reset': this.t = 0 break case 'loopLength': this.loopLength = value break case 'playbackRate': this.playbackRate = value break } } } setFormula(formulaString) { try { this.compiledFormula = new Function('t', 'a', 'b', 'c', 'd', `return ${formulaString}`) this.formula = formulaString this.error = false } catch (e) { console.error('Failed to compile bytebeat formula:', e) this.error = true this.compiledFormula = null } } process(inputs, outputs) { const output = outputs[0] if (output.length > 0) { const outputChannel = output[0] for (let i = 0; i < outputChannel.length; i++) { if (!this.compiledFormula || this.error) { outputChannel[i] = 0 } else { try { const value = this.compiledFormula(this.t, this.a, this.b, this.c, this.d) const byteValue = value & 0xFF outputChannel[i] = (byteValue - 128) / 128 } catch (e) { outputChannel[i] = 0 if (!this.error) { console.error('Bytebeat runtime error:', e) this.error = true } } } this.t += this.playbackRate if (this.loopLength > 0 && this.t >= this.loopLength) { this.t = 0 } } } return true } } registerProcessor('bytebeat-processor', BytebeatProcessor)