This commit is contained in:
2025-10-06 16:36:59 +02:00
parent 90f2f4209c
commit 9d26ea5cd7
15 changed files with 1031 additions and 595 deletions

View File

@ -0,0 +1,167 @@
class ChorusProcessor extends AudioWorkletProcessor {
constructor() {
super()
this.mode = 'chorus'
this.rate = 0.5
this.depth = 0.5
this.feedback = 0
this.spread = 0.3
this.bypassed = false
this.delayBufferSize = Math.floor(sampleRate * 0.05)
this.delayBufferL = new Float32Array(this.delayBufferSize)
this.delayBufferR = new Float32Array(this.delayBufferSize)
this.writeIndex = 0
this.lfoPhase = 0
this.lfoPhaseRight = 0
this.port.onmessage = (event) => {
const { type, value } = event.data
switch (type) {
case 'mode':
this.mode = value
break
case 'frequency':
this.rate = value
break
case 'depth':
this.depth = value
break
case 'feedback':
this.feedback = value
break
case 'spread':
this.spread = value
break
case 'bypass':
this.bypassed = value
break
}
}
}
processChorus(sampleL, sampleR) {
const baseDelay = 15
const maxDepth = 8
this.lfoPhase += this.rate / sampleRate
this.lfoPhaseRight += this.rate / sampleRate
if (this.lfoPhase >= 1) this.lfoPhase -= 1
if (this.lfoPhaseRight >= 1) this.lfoPhaseRight -= 1
const spreadPhase = this.spread * 0.5
const lfoL = Math.sin(this.lfoPhase * Math.PI * 2)
const lfoR = Math.sin((this.lfoPhaseRight + spreadPhase) * Math.PI * 2)
const delayTimeL = baseDelay + lfoL * maxDepth * this.depth
const delayTimeR = baseDelay + lfoR * maxDepth * this.depth
const delaySamplesL = (delayTimeL / 1000) * sampleRate
const delaySamplesR = (delayTimeR / 1000) * sampleRate
const readIndexL = (this.writeIndex - delaySamplesL + this.delayBufferSize) % this.delayBufferSize
const readIndexR = (this.writeIndex - delaySamplesR + this.delayBufferSize) % this.delayBufferSize
const readIndexL0 = Math.floor(readIndexL) % this.delayBufferSize
const readIndexL1 = (readIndexL0 + 1) % this.delayBufferSize
const fracL = readIndexL - Math.floor(readIndexL)
const readIndexR0 = Math.floor(readIndexR) % this.delayBufferSize
const readIndexR1 = (readIndexR0 + 1) % this.delayBufferSize
const fracR = readIndexR - Math.floor(readIndexR)
const delayedL = this.delayBufferL[readIndexL0] * (1 - fracL) + this.delayBufferL[readIndexL1] * fracL
const delayedR = this.delayBufferR[readIndexR0] * (1 - fracR) + this.delayBufferR[readIndexR1] * fracR
this.delayBufferL[this.writeIndex] = sampleL + delayedL * this.feedback
this.delayBufferR[this.writeIndex] = sampleR + delayedR * this.feedback
return [delayedL, delayedR]
}
processFlanger(sampleL, sampleR) {
const baseDelay = 1
const maxDepth = 5
this.lfoPhase += this.rate / sampleRate
this.lfoPhaseRight += this.rate / sampleRate
if (this.lfoPhase >= 1) this.lfoPhase -= 1
if (this.lfoPhaseRight >= 1) this.lfoPhaseRight -= 1
const spreadPhase = this.spread * 0.5
const lfoL = Math.sin(this.lfoPhase * Math.PI * 2)
const lfoR = Math.sin((this.lfoPhaseRight + spreadPhase) * Math.PI * 2)
const delayTimeL = baseDelay + lfoL * maxDepth * this.depth
const delayTimeR = baseDelay + lfoR * maxDepth * this.depth
const delaySamplesL = (delayTimeL / 1000) * sampleRate
const delaySamplesR = (delayTimeR / 1000) * sampleRate
const readIndexL = (this.writeIndex - delaySamplesL + this.delayBufferSize) % this.delayBufferSize
const readIndexR = (this.writeIndex - delaySamplesR + this.delayBufferSize) % this.delayBufferSize
const readIndexL0 = Math.floor(readIndexL) % this.delayBufferSize
const readIndexL1 = (readIndexL0 + 1) % this.delayBufferSize
const fracL = readIndexL - Math.floor(readIndexL)
const readIndexR0 = Math.floor(readIndexR) % this.delayBufferSize
const readIndexR1 = (readIndexR0 + 1) % this.delayBufferSize
const fracR = readIndexR - Math.floor(readIndexR)
const delayedL = this.delayBufferL[readIndexL0] * (1 - fracL) + this.delayBufferL[readIndexL1] * fracL
const delayedR = this.delayBufferR[readIndexR0] * (1 - fracR) + this.delayBufferR[readIndexR1] * fracR
this.delayBufferL[this.writeIndex] = sampleL + delayedL * this.feedback * 0.9
this.delayBufferR[this.writeIndex] = sampleR + delayedR * this.feedback * 0.9
return [delayedL, delayedR]
}
process(inputs, outputs) {
const input = inputs[0]
const output = outputs[0]
if (!input || input.length === 0 || !output || output.length === 0) {
return true
}
const inputL = input[0]
const inputR = input[1] || input[0]
const outputL = output[0]
const outputR = output[1] || output[0]
if (!inputL || !outputL) {
return true
}
for (let i = 0; i < inputL.length; i++) {
if (this.bypassed) {
outputL[i] = inputL[i]
if (outputR) outputR[i] = inputR[i]
continue
}
let processedL, processedR
if (this.mode === 'flanger') {
[processedL, processedR] = this.processFlanger(inputL[i], inputR[i])
} else {
[processedL, processedR] = this.processChorus(inputL[i], inputR[i])
}
outputL[i] = processedL
if (outputR) outputR[i] = processedR
this.writeIndex = (this.writeIndex + 1) % this.delayBufferSize
}
return true
}
}
registerProcessor('chorus-processor', ChorusProcessor)