Safer fold and crush section

This commit is contained in:
2025-10-03 23:34:45 +02:00
parent 697be5cf65
commit 0fc7ffdee0
18 changed files with 189 additions and 105 deletions

View File

@ -45,6 +45,7 @@ export class AudioPlayer {
this.dispose()
this.audioContext = new AudioContext({ sampleRate: this.sampleRate })
this.workletRegistered = false
await this.registerWorklet(this.audioContext)
this.effectsChain = new EffectsChain(this.audioContext)
@ -176,5 +177,6 @@ export class AudioPlayer {
this.audioContext.close()
this.audioContext = null
}
this.workletRegistered = false
}
}

View File

@ -72,12 +72,12 @@ export class BitcrushEffect implements Effect {
}
}
updateParams(values: Record<string, number>): void {
if (values.bitcrushDepth !== undefined) {
updateParams(values: Record<string, number | string>): void {
if (values.bitcrushDepth !== undefined && typeof values.bitcrushDepth === 'number') {
this.bitDepth = values.bitcrushDepth
}
if (values.bitcrushRate !== undefined) {
if (values.bitcrushRate !== undefined && typeof values.bitcrushRate === 'number') {
this.crushAmount = values.bitcrushRate
}
}

View File

@ -92,8 +92,8 @@ export class DelayEffect implements Effect {
}
}
updateParams(values: Record<string, number>): void {
if (values.delayTime !== undefined) {
updateParams(values: Record<string, number | string>): void {
if (values.delayTime !== undefined && typeof values.delayTime === 'number') {
const time = values.delayTime / 1000
this.delayNode.delayTime.setTargetAtTime(
time,
@ -102,7 +102,7 @@ export class DelayEffect implements Effect {
)
}
if (values.delayFeedback !== undefined) {
if (values.delayFeedback !== undefined && typeof values.delayFeedback === 'number') {
const feedback = values.delayFeedback / 100
this.feedbackNode.gain.setTargetAtTime(
feedback * 0.95,
@ -111,7 +111,7 @@ export class DelayEffect implements Effect {
)
}
if (values.delayWetDry !== undefined) {
if (values.delayWetDry !== undefined && typeof values.delayWetDry === 'number') {
const wet = values.delayWetDry / 100
this.currentWetValue = wet
this.currentDryValue = 1 - wet
@ -130,7 +130,7 @@ export class DelayEffect implements Effect {
}
}
if (values.delayTone !== undefined) {
if (values.delayTone !== undefined && typeof values.delayTone === 'number') {
const tone = values.delayTone / 100
const freq = 200 + tone * 7800
this.filterNode.frequency.setTargetAtTime(
@ -140,12 +140,12 @@ export class DelayEffect implements Effect {
)
}
if (values.delaySaturation !== undefined) {
if (values.delaySaturation !== undefined && typeof values.delaySaturation === 'number') {
const saturation = values.delaySaturation / 100
this.createSaturationCurve(saturation)
}
if (values.delayFlutter !== undefined) {
if (values.delayFlutter !== undefined && typeof values.delayFlutter === 'number') {
const flutter = values.delayFlutter / 100
const baseDelay = this.delayNode.delayTime.value
const modDepth = baseDelay * flutter * 0.1

View File

@ -2,7 +2,7 @@ export interface Effect {
readonly id: string
getInputNode(): AudioNode
getOutputNode(): AudioNode
updateParams(values: Record<string, number>): void
updateParams(values: Record<string, number | string>): void
setBypass(bypass: boolean): void
dispose(): void
}

View File

@ -44,7 +44,7 @@ export class EffectsChain {
this.masterGainNode.connect(this.outputNode)
}
updateEffects(values: Record<string, number | boolean>): void {
updateEffects(values: Record<string, number | boolean | string>): void {
for (const effect of this.effects) {
const effectId = effect.id
const bypassKey = `${effectId}Bypass`
@ -53,13 +53,13 @@ export class EffectsChain {
effect.setBypass(Boolean(values[bypassKey]))
}
const numericValues: Record<string, number> = {}
const effectValues: Record<string, number | string> = {}
for (const [key, value] of Object.entries(values)) {
if (typeof value === 'number') {
numericValues[key] = value
if (typeof value === 'number' || typeof value === 'string') {
effectValues[key] = value
}
}
effect.updateParams(numericValues)
effect.updateParams(effectValues)
}
if (values.masterVolume !== undefined) {

View File

@ -72,13 +72,13 @@ export class FilterEffect implements Effect {
}
}
updateParams(values: Record<string, number>): void {
updateParams(values: Record<string, number | string>): void {
if (values.hpEnable !== undefined) {
this.hpEnabled = values.hpEnable === 1
this.updateBypassState()
}
if (values.hpFreq !== undefined) {
if (values.hpFreq !== undefined && typeof values.hpFreq === 'number') {
this.hpFilter.frequency.cancelScheduledValues(this.audioContext.currentTime)
this.hpFilter.frequency.setValueAtTime(
this.hpFilter.frequency.value,
@ -90,7 +90,7 @@ export class FilterEffect implements Effect {
)
}
if (values.hpRes !== undefined) {
if (values.hpRes !== undefined && typeof values.hpRes === 'number') {
this.hpFilter.Q.cancelScheduledValues(this.audioContext.currentTime)
this.hpFilter.Q.setValueAtTime(
this.hpFilter.Q.value,
@ -107,7 +107,7 @@ export class FilterEffect implements Effect {
this.updateBypassState()
}
if (values.lpFreq !== undefined) {
if (values.lpFreq !== undefined && typeof values.lpFreq === 'number') {
this.lpFilter.frequency.cancelScheduledValues(this.audioContext.currentTime)
this.lpFilter.frequency.setValueAtTime(
this.lpFilter.frequency.value,
@ -119,7 +119,7 @@ export class FilterEffect implements Effect {
)
}
if (values.lpRes !== undefined) {
if (values.lpRes !== undefined && typeof values.lpRes === 'number') {
this.lpFilter.Q.cancelScheduledValues(this.audioContext.currentTime)
this.lpFilter.Q.setValueAtTime(
this.lpFilter.Q.value,
@ -136,7 +136,7 @@ export class FilterEffect implements Effect {
this.updateBypassState()
}
if (values.bpFreq !== undefined) {
if (values.bpFreq !== undefined && typeof values.bpFreq === 'number') {
this.bpFilter.frequency.cancelScheduledValues(this.audioContext.currentTime)
this.bpFilter.frequency.setValueAtTime(
this.bpFilter.frequency.value,
@ -148,7 +148,7 @@ export class FilterEffect implements Effect {
)
}
if (values.bpRes !== undefined) {
if (values.bpRes !== undefined && typeof values.bpRes === 'number') {
this.bpFilter.Q.cancelScheduledValues(this.audioContext.currentTime)
this.bpFilter.Q.setValueAtTime(
this.bpFilter.Q.value,

View File

@ -15,8 +15,8 @@ export class FoldCrushEffect implements Effect {
this.wetNode = audioContext.createGain()
this.dryNode = audioContext.createGain()
this.wetNode.gain.value = 1
this.dryNode.gain.value = 0
this.wetNode.gain.value = 0
this.dryNode.gain.value = 1
this.inputNode.connect(this.dryNode)
this.dryNode.connect(this.outputNode)
@ -51,13 +51,11 @@ export class FoldCrushEffect implements Effect {
}
}
updateParams(values: Record<string, number>): void {
updateParams(values: Record<string, number | string>): void {
if (!this.processorNode) return
if (values.clipMode !== undefined) {
const modeIndex = values.clipMode
const clipMode = ['wrap', 'clamp', 'fold'][modeIndex] || 'wrap'
this.processorNode.port.postMessage({ type: 'clipMode', value: clipMode })
this.processorNode.port.postMessage({ type: 'clipMode', value: values.clipMode })
}
if (values.wavefolderDrive !== undefined) {
this.processorNode.port.postMessage({ type: 'drive', value: values.wavefolderDrive })

View File

@ -21,7 +21,7 @@ export class PassThroughEffect implements Effect {
setBypass(_bypass: boolean): void {
}
updateParams(_values: Record<string, number>): void {
updateParams(_values: Record<string, number | string>): void {
}
dispose(): void {

View File

@ -153,20 +153,20 @@ export class ReverbEffect implements Effect {
}
}
updateParams(values: Record<string, number>): void {
updateParams(values: Record<string, number | string>): void {
let needsRegeneration = false
if (values.reverbDecay !== undefined && values.reverbDecay !== this.currentDecay) {
if (values.reverbDecay !== undefined && typeof values.reverbDecay === 'number' && values.reverbDecay !== this.currentDecay) {
this.currentDecay = values.reverbDecay
needsRegeneration = true
}
if (values.reverbDamping !== undefined && values.reverbDamping !== this.currentDamping) {
if (values.reverbDamping !== undefined && typeof values.reverbDamping === 'number' && values.reverbDamping !== this.currentDamping) {
this.currentDamping = values.reverbDamping
needsRegeneration = true
}
if (values.reverbWetDry !== undefined) {
if (values.reverbWetDry !== undefined && typeof values.reverbWetDry === 'number') {
const wet = values.reverbWetDry / 100
this.currentWetValue = wet
this.currentDryValue = 1 - wet
@ -177,7 +177,7 @@ export class ReverbEffect implements Effect {
}
}
if (values.reverbPanRate !== undefined) {
if (values.reverbPanRate !== undefined && typeof values.reverbPanRate === 'number') {
const rate = values.reverbPanRate
this.panLfoNode.frequency.setTargetAtTime(
rate,
@ -186,7 +186,7 @@ export class ReverbEffect implements Effect {
)
}
if (values.reverbPanWidth !== undefined) {
if (values.reverbPanWidth !== undefined && typeof values.reverbPanWidth === 'number') {
const width = values.reverbPanWidth / 100
this.panLfoGainNode.gain.setTargetAtTime(
width,

View File

@ -96,12 +96,11 @@ export class WavefolderEffect implements Effect {
}
}
updateParams(values: Record<string, number>): void {
if (values.clipMode !== undefined) {
const modeIndex = values.clipMode
this.mode = ['wrap', 'clamp', 'fold'][modeIndex] as ClipMode || 'wrap'
updateParams(values: Record<string, number | string>): void {
if (values.clipMode !== undefined && typeof values.clipMode === 'string') {
this.mode = values.clipMode as ClipMode
}
if (values.wavefolderDrive !== undefined) {
if (values.wavefolderDrive !== undefined && typeof values.wavefolderDrive === 'number') {
this.drive = values.wavefolderDrive
}
}