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 {
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:
<div className="compressor-graph__title">{activeOutputState.name} - RMS Compressor</div>
<LinearGraph
xMin={-60}
xMin={-100}
xMax={0}
yMin={-60}
yMin={-100}
yMax={0}
xLabel="in dB"
yLabel="out dB"
@@ -365,7 +390,7 @@ function CompressorSection({ dsp, set_compressor }: { dsp: DSP; set_compressor:
ctx.strokeStyle = '#6b7280';
ctx.lineWidth = 1;
ctx.setLineDash([4, 4]);
const [ux0, uy0] = toPx(-60, -60);
const [ux0, uy0] = toPx(-100, -100);
const [ux1, uy1] = toPx(0, 0);
ctx.beginPath();
ctx.moveTo(ux0, uy0);
@@ -377,7 +402,7 @@ function CompressorSection({ dsp, set_compressor }: { dsp: DSP; set_compressor:
ctx.strokeStyle = '#fbbf24';
ctx.lineWidth = 1;
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);
ctx.beginPath();
ctx.moveTo(tx0, ty0);