feat: improve compressor graph
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user