feat: improve compressor graph

This commit is contained in:
2026-08-12 12:19:17 +02:00
parent ab9de39556
commit 5945b48b98
@@ -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 { function compressorOutputDb(input: number, threshold: number, ratio: number, knee: number): number {
const kneeStart = threshold - knee / 2; const kneeStart = threshold - knee / 2;
const kneeEnd = 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; 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) { if (input >= kneeEnd) {
return threshold + (input - threshold) / ratio; 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 }) { 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 steps = 120;
const points: [number, number][] = []; const points: [number, number][] = [];
for (let i = 0; i <= steps; i++) { 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)]); points.push([x, compressorOutputDb(x, threshold, ratioNumeric, knee)]);
} }
return points; return points;
@@ -352,9 +377,9 @@ function CompressorSection({ dsp, set_compressor }: { dsp: DSP; set_compressor:
<div className="compressor-graph__title">{activeOutputState.name} - RMS Compressor</div> <div className="compressor-graph__title">{activeOutputState.name} - RMS Compressor</div>
<LinearGraph <LinearGraph
xMin={-60} xMin={-100}
xMax={0} xMax={0}
yMin={-60} yMin={-100}
yMax={0} yMax={0}
xLabel="in dB" xLabel="in dB"
yLabel="out dB" yLabel="out dB"
@@ -365,7 +390,7 @@ function CompressorSection({ dsp, set_compressor }: { dsp: DSP; set_compressor:
ctx.strokeStyle = '#6b7280'; ctx.strokeStyle = '#6b7280';
ctx.lineWidth = 1; ctx.lineWidth = 1;
ctx.setLineDash([4, 4]); ctx.setLineDash([4, 4]);
const [ux0, uy0] = toPx(-60, -60); const [ux0, uy0] = toPx(-100, -100);
const [ux1, uy1] = toPx(0, 0); const [ux1, uy1] = toPx(0, 0);
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(ux0, uy0); ctx.moveTo(ux0, uy0);
@@ -377,7 +402,7 @@ function CompressorSection({ dsp, set_compressor }: { dsp: DSP; set_compressor:
ctx.strokeStyle = '#fbbf24'; ctx.strokeStyle = '#fbbf24';
ctx.lineWidth = 1; ctx.lineWidth = 1;
ctx.setLineDash([2, 3]); ctx.setLineDash([2, 3]);
const [tx0, ty0] = toPx(activeComp.threshold, -60); const [tx0, ty0] = toPx(activeComp.threshold, -100);
const [tx1, ty1] = toPx(activeComp.threshold, 0); const [tx1, ty1] = toPx(activeComp.threshold, 0);
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(tx0, ty0); ctx.moveTo(tx0, ty0);