Safer fold and crush section
This commit is contained in:
@ -2,7 +2,7 @@ class FoldCrushProcessor extends AudioWorkletProcessor {
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
this.clipMode = 'wrap'
|
||||
this.clipMode = 'fold'
|
||||
this.drive = 1
|
||||
this.bitDepth = 16
|
||||
this.crushAmount = 0
|
||||
@ -28,39 +28,63 @@ class FoldCrushProcessor extends AudioWorkletProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
wrap(sample) {
|
||||
const range = 2.0
|
||||
let wrapped = sample
|
||||
while (wrapped > 1.0) wrapped -= range
|
||||
while (wrapped < -1.0) wrapped += range
|
||||
return wrapped
|
||||
clamp(x, min, max) {
|
||||
return Math.max(min, Math.min(max, x))
|
||||
}
|
||||
|
||||
clamp(sample) {
|
||||
return Math.max(-1.0, Math.min(1.0, sample))
|
||||
mod(x, y) {
|
||||
return ((x % y) + y) % y
|
||||
}
|
||||
|
||||
fold(sample) {
|
||||
let folded = sample
|
||||
while (folded > 1.0 || folded < -1.0) {
|
||||
if (folded > 1.0) {
|
||||
folded = 2.0 - folded
|
||||
}
|
||||
if (folded < -1.0) {
|
||||
folded = -2.0 - folded
|
||||
}
|
||||
}
|
||||
return folded
|
||||
squash(x) {
|
||||
return x / (1 + Math.abs(x))
|
||||
}
|
||||
|
||||
soft(x, k) {
|
||||
return Math.tanh(x * (1 + k))
|
||||
}
|
||||
|
||||
hard(x, k) {
|
||||
return this.clamp((1 + k) * x, -1, 1)
|
||||
}
|
||||
|
||||
fold(x, k) {
|
||||
let y = (1 + 0.5 * k) * x
|
||||
const window = this.mod(y + 1, 4)
|
||||
return 1 - Math.abs(window - 2)
|
||||
}
|
||||
|
||||
cubic(x, k) {
|
||||
const t = this.squash(Math.log1p(k))
|
||||
const cubic = (x - (t / 3) * x * x * x) / (1 - t / 3)
|
||||
return this.soft(cubic, k)
|
||||
}
|
||||
|
||||
diode(x, k) {
|
||||
const g = 1 + 2 * k
|
||||
const t = this.squash(Math.log1p(k))
|
||||
const bias = 0.07 * t
|
||||
const pos = this.soft(x + bias, 2 * k)
|
||||
const neg = this.soft(-x + bias, 2 * k)
|
||||
const y = pos - neg
|
||||
const sech = 1 / Math.cosh(g * bias)
|
||||
const sech2 = sech * sech
|
||||
const denom = Math.max(1e-8, 2 * g * sech2)
|
||||
return this.soft(y / denom, k)
|
||||
}
|
||||
|
||||
processWavefolder(sample) {
|
||||
switch (this.clipMode) {
|
||||
case 'wrap':
|
||||
return this.wrap(sample)
|
||||
case 'clamp':
|
||||
return this.clamp(sample)
|
||||
case 'soft':
|
||||
return this.soft(sample, this.drive)
|
||||
case 'hard':
|
||||
return this.hard(sample, this.drive)
|
||||
case 'fold':
|
||||
return this.fold(sample)
|
||||
return this.fold(sample, this.drive)
|
||||
case 'cubic':
|
||||
return this.cubic(sample, this.drive)
|
||||
case 'diode':
|
||||
return this.diode(sample, this.drive)
|
||||
default:
|
||||
return sample
|
||||
}
|
||||
@ -86,6 +110,14 @@ class FoldCrushProcessor extends AudioWorkletProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
safetyLimiter(sample) {
|
||||
const threshold = 0.8
|
||||
if (Math.abs(sample) > threshold) {
|
||||
return Math.tanh(sample * 0.9) / Math.tanh(0.9)
|
||||
}
|
||||
return sample
|
||||
}
|
||||
|
||||
process(inputs, outputs) {
|
||||
const input = inputs[0]
|
||||
const output = outputs[0]
|
||||
@ -95,9 +127,9 @@ class FoldCrushProcessor extends AudioWorkletProcessor {
|
||||
const outputChannel = output[0]
|
||||
|
||||
for (let i = 0; i < inputChannel.length; i++) {
|
||||
const driven = inputChannel[i] * this.drive
|
||||
let processed = this.processWavefolder(driven)
|
||||
let processed = this.processWavefolder(inputChannel[i])
|
||||
processed = this.processBitcrush(processed)
|
||||
processed = this.safetyLimiter(processed)
|
||||
outputChannel[i] = processed
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user