From 241f9a76a1d75bab2037421898c97efc70e9321e Mon Sep 17 00:00:00 2001 From: DIO85 Date: Thu, 12 Mar 2026 23:30:27 -0300 Subject: [PATCH] feat(audio): add hybrid squelch UI and manual threshold control --- package-lock.json | 1 + src/App.tsx | 2 ++ src/components/audio/types.ts | 2 ++ src/components/audio/useAudioClient.ts | 14 ++++++---- src/components/receiver/panels/AudioPanel.tsx | 27 +++++++++++++++++++ 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index f47ef4c..51ba8f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "frontend", "version": "0.0.0", + "license": "GPL-3.0-only", "dependencies": { "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", diff --git a/src/App.tsx b/src/App.tsx index 0937f4c..ad343f4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -115,6 +115,8 @@ export default function App() { volume: 20, mute: false, squelch: false, + squelchAuto: true, + squelchLevel: -80, nr: false, nb: false, an: false, diff --git a/src/components/audio/types.ts b/src/components/audio/types.ts index 070d77b..7dd6893 100644 --- a/src/components/audio/types.ts +++ b/src/components/audio/types.ts @@ -6,6 +6,8 @@ export type AudioUiSettings = { volume: number; // 0..100 mute: boolean; squelch: boolean; + squelchAuto: boolean; // true = auto (statistical), false = manual threshold + squelchLevel: number; // manual threshold in dB (default: -80) nr: boolean; nb: boolean; an: boolean; diff --git a/src/components/audio/useAudioClient.ts b/src/components/audio/useAudioClient.ts index 086b70e..2fb1502 100644 --- a/src/components/audio/useAudioClient.ts +++ b/src/components/audio/useAudioClient.ts @@ -129,7 +129,7 @@ export function useAudioClient({ receiverId, receiverSessionNonce, mode, centerH const lastWindowRef = useRef(''); const lastDemodRef = useRef(''); const lastSentMuteRef = useRef(null); - const lastSentSquelchRef = useRef(null); + const lastSentSquelchRef = useRef(null); const lastSentAgcRef = useRef(null); const receiverIdRef = useRef(receiverId); const smeterOffsetDbRef = useRef(0); @@ -1003,10 +1003,14 @@ export function useAudioClient({ receiverId, receiverSessionNonce, mode, centerH useEffect(() => { if (!basicInfo) return; const enabled = settings.squelch; - if (lastSentSquelchRef.current === enabled) return; - if (!send({ cmd: 'squelch', enabled })) return; - lastSentSquelchRef.current = enabled; - }, [basicInfo, connectionNonce, send, settings.squelch]); + const level = (enabled && !settings.squelchAuto) + ? settings.squelchLevel - smeterOffsetDbRef.current + : null; + const key = `${enabled}:${settings.squelchAuto}:${settings.squelchLevel}`; + if (lastSentSquelchRef.current === key) return; + if (!send({ cmd: 'squelch', enabled, level })) return; + lastSentSquelchRef.current = key; + }, [basicInfo, connectionNonce, send, settings.squelch, settings.squelchAuto, settings.squelchLevel]); useEffect(() => { if (!basicInfo) return; diff --git a/src/components/receiver/panels/AudioPanel.tsx b/src/components/receiver/panels/AudioPanel.tsx index 52c2ab4..bf1a23c 100644 --- a/src/components/receiver/panels/AudioPanel.tsx +++ b/src/components/receiver/panels/AudioPanel.tsx @@ -90,6 +90,33 @@ export function AudioPanel({ /> + {settings.squelch && ( +
+ + onChange((prev) => ({ ...prev, squelchAuto: checked })) + } + /> + {!settings.squelchAuto && ( +
+ + + onChange((prev) => ({ ...prev, squelchLevel: v })) + } + /> +
+ )} +
+ )} +