before bugfixing

This commit is contained in:
2025-09-30 21:39:16 +02:00
parent 21c983b41e
commit d1ff3daae1
7 changed files with 502 additions and 41 deletions

View File

@ -0,0 +1,149 @@
import type { Effect } from './Effect.interface'
type ClipMode = 'wrap' | 'clamp' | 'fold'
export class FoldCrushEffect implements Effect {
readonly id = 'foldcrush'
private inputNode: GainNode
private outputNode: GainNode
private processorNode: ScriptProcessorNode
private wetNode: GainNode
private dryNode: GainNode
private clipMode: ClipMode = 'wrap'
private drive: number = 1
private bitDepth: number = 16
private crushAmount: number = 0
constructor(audioContext: AudioContext) {
this.inputNode = audioContext.createGain()
this.outputNode = audioContext.createGain()
this.processorNode = audioContext.createScriptProcessor(4096, 1, 1)
this.wetNode = audioContext.createGain()
this.dryNode = audioContext.createGain()
this.wetNode.gain.value = 1
this.dryNode.gain.value = 0
this.processorNode.onaudioprocess = (e) => {
const input = e.inputBuffer.getChannelData(0)
const output = e.outputBuffer.getChannelData(0)
for (let i = 0; i < input.length; i++) {
const driven = input[i] * this.drive
let processed = this.processWavefolder(driven)
processed = this.processBitcrush(processed, i, output)
output[i] = processed
}
}
this.inputNode.connect(this.dryNode)
this.inputNode.connect(this.processorNode)
this.processorNode.connect(this.wetNode)
this.dryNode.connect(this.outputNode)
this.wetNode.connect(this.outputNode)
}
private processWavefolder(sample: number): number {
switch (this.clipMode) {
case 'wrap':
return this.wrap(sample)
case 'clamp':
return this.clamp(sample)
case 'fold':
return this.fold(sample)
default:
return sample
}
}
private wrap(sample: number): number {
const range = 2.0
let wrapped = sample
while (wrapped > 1.0) wrapped -= range
while (wrapped < -1.0) wrapped += range
return wrapped
}
private clamp(sample: number): number {
return Math.max(-1.0, Math.min(1.0, sample))
}
private fold(sample: number): number {
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
}
private bitcrushPhase: number = 0
private lastCrushedValue: number = 0
private processBitcrush(sample: number, index: number, output: Float32Array): number {
if (this.crushAmount === 0 && this.bitDepth === 16) {
return sample
}
const step = Math.pow(0.5, this.bitDepth)
const phaseIncrement = 1 - (this.crushAmount / 100)
this.bitcrushPhase += phaseIncrement
if (this.bitcrushPhase >= 1.0) {
this.bitcrushPhase -= 1.0
const crushed = Math.floor(sample / step + 0.5) * step
this.lastCrushedValue = Math.max(-1, Math.min(1, crushed))
return this.lastCrushedValue
} else {
return this.lastCrushedValue
}
}
getInputNode(): AudioNode {
return this.inputNode
}
getOutputNode(): AudioNode {
return this.outputNode
}
setBypass(bypass: boolean): void {
if (bypass) {
this.wetNode.gain.value = 0
this.dryNode.gain.value = 1
} else {
this.wetNode.gain.value = 1
this.dryNode.gain.value = 0
}
}
updateParams(values: Record<string, number>): void {
if (values.clipMode !== undefined) {
const modeIndex = values.clipMode
this.clipMode = ['wrap', 'clamp', 'fold'][modeIndex] as ClipMode || 'wrap'
}
if (values.wavefolderDrive !== undefined) {
this.drive = values.wavefolderDrive
}
if (values.bitcrushDepth !== undefined) {
this.bitDepth = values.bitcrushDepth
}
if (values.bitcrushRate !== undefined) {
this.crushAmount = values.bitcrushRate
}
}
dispose(): void {
this.processorNode.disconnect()
this.wetNode.disconnect()
this.dryNode.disconnect()
this.inputNode.disconnect()
this.outputNode.disconnect()
}
}