feat: add transient suppressor to audio pipeline
Implements a per-sample transient suppressor in the noise gate AudioWorklet that instantly cuts gain when a sudden loud peak (desk hit, mic bump) exceeds the slow background RMS by a configurable threshold, then releases over a short window. Exposes enable, sensitivity, and release controls in the audio settings tab. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,9 @@ import {
|
||||
noiseGateAttack as noiseGateAttackSetting,
|
||||
noiseGateHold as noiseGateHoldSetting,
|
||||
noiseGateRelease as noiseGateReleaseSetting,
|
||||
transientSuppressorEnabled as transientSuppressorEnabledSetting,
|
||||
transientThreshold as transientThresholdSetting,
|
||||
transientRelease as transientReleaseSetting,
|
||||
} from "./settings";
|
||||
import { PreferencesSettingsTab } from "./PreferencesSettingsTab";
|
||||
import { Slider } from "../Slider";
|
||||
@@ -126,6 +129,13 @@ export const SettingsModal: FC<Props> = ({
|
||||
|
||||
const [showAdvancedGate, setShowAdvancedGate] = useState(false);
|
||||
|
||||
// Transient suppressor settings
|
||||
const [transientEnabled, setTransientEnabled] = useSetting(transientSuppressorEnabledSetting);
|
||||
const [transientThreshold, setTransientThreshold] = useSetting(transientThresholdSetting);
|
||||
const [transientThresholdRaw, setTransientThresholdRaw] = useState(transientThreshold);
|
||||
const [transientRelease, setTransientRelease] = useSetting(transientReleaseSetting);
|
||||
const [transientReleaseRaw, setTransientReleaseRaw] = useState(transientRelease);
|
||||
|
||||
const resetGateDefaults = useCallback((): void => {
|
||||
const a = noiseGateAttackSetting.defaultValue;
|
||||
const h = noiseGateHoldSetting.defaultValue;
|
||||
@@ -293,6 +303,62 @@ export const SettingsModal: FC<Props> = ({
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.noiseGateSection}>
|
||||
<Heading
|
||||
type="body"
|
||||
weight="semibold"
|
||||
size="sm"
|
||||
as="h4"
|
||||
className={styles.noiseGateHeading}
|
||||
>
|
||||
Transient Suppressor
|
||||
</Heading>
|
||||
<Separator className={styles.noiseGateSeparator} />
|
||||
<FieldRow>
|
||||
<InputField
|
||||
id="transientEnabled"
|
||||
type="checkbox"
|
||||
label="Enable transient suppressor"
|
||||
description="Cut sudden loud impacts like desk hits or mic bumps."
|
||||
checked={transientEnabled}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>): void =>
|
||||
setTransientEnabled(e.target.checked)
|
||||
}
|
||||
/>
|
||||
</FieldRow>
|
||||
{transientEnabled && (
|
||||
<>
|
||||
<div className={`${styles.volumeSlider} ${styles.thresholdSlider}`}>
|
||||
<span className={styles.sliderLabel}>Sensitivity: {transientThresholdRaw} dB above background</span>
|
||||
<p>Lower values catch more impacts; higher values only catch the loudest ones.</p>
|
||||
<Slider
|
||||
label="Transient threshold"
|
||||
value={transientThresholdRaw}
|
||||
onValueChange={setTransientThresholdRaw}
|
||||
onValueCommit={setTransientThreshold}
|
||||
min={8}
|
||||
max={30}
|
||||
step={1}
|
||||
tooltip={false}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.volumeSlider}>
|
||||
<span className={styles.sliderLabel}>Release: {transientReleaseRaw} ms</span>
|
||||
<p>How quickly audio returns after suppression.</p>
|
||||
<Slider
|
||||
label="Transient release"
|
||||
value={transientReleaseRaw}
|
||||
onValueChange={setTransientReleaseRaw}
|
||||
onValueCommit={setTransientRelease}
|
||||
min={20}
|
||||
max={200}
|
||||
step={10}
|
||||
tooltip={false}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user