diff --git a/src/features/dsp408/components/CompressorSection/CompressorSection.tsx b/src/features/dsp408/components/CompressorSection/CompressorSection.tsx index ee24b1d..a4d4668 100644 --- a/src/features/dsp408/components/CompressorSection/CompressorSection.tsx +++ b/src/features/dsp408/components/CompressorSection/CompressorSection.tsx @@ -263,19 +263,44 @@ function CompressorPanel({ ); } -// Soft-knee compressor transfer curve, in dB. +// Compressor transfer curve, in dB. +// +// The knee is NOT a smooth/quadratic blend. It is three straight +// (piecewise-linear) segments: +// 1) input <= kneeStart -> 1:1 line (output = input) +// 2) kneeStart < input < kneeEnd -> a straight line connecting the +// point on segment (1) at kneeStart +// to the point on segment (3) at kneeEnd +// 3) input >= kneeEnd -> ratio line (output = threshold + (input-threshold)/ratio) +// +// kneeStart/kneeEnd are simply the threshold shifted down/up by half the +// knee width, so increasing "knee" widens the middle straight segment +// symmetrically around the threshold. function compressorOutputDb(input: number, threshold: number, ratio: number, knee: number): number { const kneeStart = threshold - knee / 2; const kneeEnd = threshold + knee / 2; - if (knee <= 0 || input <= kneeStart) { + // No knee: simple two-segment (hard-knee) curve. + if (knee <= 0) { return input <= threshold ? input : threshold + (input - threshold) / ratio; } + + // Segment 1: below the knee, straight 1:1 line. + if (input <= kneeStart) { + return input; + } + + // Segment 3: above the knee, straight ratio line. if (input >= kneeEnd) { return threshold + (input - threshold) / ratio; } - const x = input - kneeStart; - return input + ((1 / ratio - 1) * x * x) / (2 * knee); + + // Segment 2: straight line between the two boundary points + // (kneeStart, kneeStart) and (kneeEnd, threshold + (kneeEnd - threshold) / ratio). + const y1 = kneeStart; + const y2 = threshold + (kneeEnd - threshold) / ratio; + const t = (input - kneeStart) / (kneeEnd - kneeStart); + return y1 + t * (y2 - y1); } function CompressorSection({ dsp, set_compressor }: { dsp: DSP; set_compressor: SetCompressorFn }) { @@ -339,7 +364,7 @@ function CompressorSection({ dsp, set_compressor }: { dsp: DSP; set_compressor: const steps = 120; const points: [number, number][] = []; for (let i = 0; i <= steps; i++) { - const x = -60 + (60 * i) / steps; + const x = -100 + (100 * i) / steps; points.push([x, compressorOutputDb(x, threshold, ratioNumeric, knee)]); } return points; @@ -352,9 +377,9 @@ function CompressorSection({ dsp, set_compressor }: { dsp: DSP; set_compressor: