feat: noise gate implementation
This commit is contained in:
@@ -5,10 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type FC, type ReactNode, useEffect, useState } from "react";
|
||||
import { type ChangeEvent, type FC, type ReactNode, useEffect, useState, useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { type MatrixClient } from "matrix-js-sdk";
|
||||
import { Button, Root as Form, Separator } from "@vector-im/compound-web";
|
||||
import { Button, Heading, Root as Form, Separator } from "@vector-im/compound-web";
|
||||
import { type Room as LivekitRoom } from "livekit-client";
|
||||
|
||||
import { Modal } from "../Modal";
|
||||
@@ -24,9 +24,15 @@ import {
|
||||
soundEffectVolume as soundEffectVolumeSetting,
|
||||
backgroundBlur as backgroundBlurSetting,
|
||||
developerMode,
|
||||
noiseGateEnabled as noiseGateEnabledSetting,
|
||||
noiseGateThreshold as noiseGateThresholdSetting,
|
||||
noiseGateAttack as noiseGateAttackSetting,
|
||||
noiseGateHold as noiseGateHoldSetting,
|
||||
noiseGateRelease as noiseGateReleaseSetting,
|
||||
} from "./settings";
|
||||
import { PreferencesSettingsTab } from "./PreferencesSettingsTab";
|
||||
import { Slider } from "../Slider";
|
||||
import { NoiseLevelSlider } from "./NoiseLevelSlider";
|
||||
import { DeviceSelection } from "./DeviceSelection";
|
||||
import { useTrackProcessor } from "../livekit/TrackProcessorContext";
|
||||
import { DeveloperSettingsTab } from "./DeveloperSettingsTab";
|
||||
@@ -107,6 +113,28 @@ export const SettingsModal: FC<Props> = ({
|
||||
const [soundVolumeRaw, setSoundVolumeRaw] = useState(soundVolume);
|
||||
const [showDeveloperSettingsTab] = useSetting(developerMode);
|
||||
|
||||
// Noise gate settings
|
||||
const [noiseGateEnabled, setNoiseGateEnabled] = useSetting(noiseGateEnabledSetting);
|
||||
const [noiseGateThreshold, setNoiseGateThreshold] = useSetting(noiseGateThresholdSetting);
|
||||
const [noiseGateThresholdRaw, setNoiseGateThresholdRaw] = useState(noiseGateThreshold);
|
||||
const [noiseGateAttack, setNoiseGateAttack] = useSetting(noiseGateAttackSetting);
|
||||
const [noiseGateAttackRaw, setNoiseGateAttackRaw] = useState(noiseGateAttack);
|
||||
const [noiseGateHold, setNoiseGateHold] = useSetting(noiseGateHoldSetting);
|
||||
const [noiseGateHoldRaw, setNoiseGateHoldRaw] = useState(noiseGateHold);
|
||||
const [noiseGateRelease, setNoiseGateRelease] = useSetting(noiseGateReleaseSetting);
|
||||
const [noiseGateReleaseRaw, setNoiseGateReleaseRaw] = useState(noiseGateRelease);
|
||||
|
||||
const [showAdvancedGate, setShowAdvancedGate] = useState(false);
|
||||
|
||||
const resetGateDefaults = useCallback((): void => {
|
||||
const a = noiseGateAttackSetting.defaultValue;
|
||||
const h = noiseGateHoldSetting.defaultValue;
|
||||
const r = noiseGateReleaseSetting.defaultValue;
|
||||
setNoiseGateAttack(a); setNoiseGateAttackRaw(a);
|
||||
setNoiseGateHold(h); setNoiseGateHoldRaw(h);
|
||||
setNoiseGateRelease(r); setNoiseGateReleaseRaw(r);
|
||||
}, [setNoiseGateAttack, setNoiseGateHold, setNoiseGateRelease]);
|
||||
|
||||
const { available: isRageshakeAvailable } = useSubmitRageshake();
|
||||
|
||||
// For controlled devices, we will not show the input section:
|
||||
@@ -165,6 +193,106 @@ export const SettingsModal: FC<Props> = ({
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
<div className={styles.noiseGateSection}>
|
||||
<Heading
|
||||
type="body"
|
||||
weight="semibold"
|
||||
size="sm"
|
||||
as="h4"
|
||||
className={styles.noiseGateHeading}
|
||||
>
|
||||
Noise Gate
|
||||
</Heading>
|
||||
<Separator className={styles.noiseGateSeparator} />
|
||||
<FieldRow>
|
||||
<InputField
|
||||
id="noiseGateEnabled"
|
||||
type="checkbox"
|
||||
label="Enable noise gate"
|
||||
description="Suppress audio below a configurable threshold."
|
||||
checked={noiseGateEnabled}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>): void =>
|
||||
setNoiseGateEnabled(e.target.checked)
|
||||
}
|
||||
/>
|
||||
</FieldRow>
|
||||
{noiseGateEnabled && (
|
||||
<>
|
||||
<div className={`${styles.volumeSlider} ${styles.thresholdSlider}`}>
|
||||
<span className={styles.sliderLabel}>Threshold</span>
|
||||
<p>Gate opens above this level, closes below it.</p>
|
||||
<NoiseLevelSlider
|
||||
label="Noise gate threshold"
|
||||
value={noiseGateThresholdRaw}
|
||||
onValueChange={setNoiseGateThresholdRaw}
|
||||
onValueCommit={setNoiseGateThreshold}
|
||||
min={-100}
|
||||
max={0}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.advancedGate}>
|
||||
<button
|
||||
className={styles.advancedGateToggle}
|
||||
onClick={(): void => setShowAdvancedGate((v) => !v)}
|
||||
>
|
||||
{showAdvancedGate ? "▾" : "▸"} Advanced settings
|
||||
</button>
|
||||
{showAdvancedGate && (
|
||||
<>
|
||||
<div className={styles.volumeSlider}>
|
||||
<label>Attack: {noiseGateAttackRaw} ms</label>
|
||||
<p>How quickly the gate opens when signal exceeds threshold.</p>
|
||||
<Slider
|
||||
label="Noise gate attack"
|
||||
value={noiseGateAttackRaw}
|
||||
onValueChange={setNoiseGateAttackRaw}
|
||||
onValueCommit={setNoiseGateAttack}
|
||||
min={1}
|
||||
max={100}
|
||||
step={1}
|
||||
tooltip={false}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.volumeSlider}>
|
||||
<label>Hold: {noiseGateHoldRaw} ms</label>
|
||||
<p>How long the gate stays open after signal drops below threshold.</p>
|
||||
<Slider
|
||||
label="Noise gate hold"
|
||||
value={noiseGateHoldRaw}
|
||||
onValueChange={setNoiseGateHoldRaw}
|
||||
onValueCommit={setNoiseGateHold}
|
||||
min={0}
|
||||
max={500}
|
||||
step={10}
|
||||
tooltip={false}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.volumeSlider}>
|
||||
<label>Release: {noiseGateReleaseRaw} ms</label>
|
||||
<p>How quickly the gate closes after hold expires.</p>
|
||||
<Slider
|
||||
label="Noise gate release"
|
||||
value={noiseGateReleaseRaw}
|
||||
onValueChange={setNoiseGateReleaseRaw}
|
||||
onValueCommit={setNoiseGateRelease}
|
||||
min={10}
|
||||
max={500}
|
||||
step={10}
|
||||
tooltip={false}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.restoreDefaults}>
|
||||
<Button kind="secondary" size="sm" onClick={resetGateDefaults}>
|
||||
Restore defaults
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user