diff --git a/client/src/lib/luxsinApi.ts b/client/src/lib/luxsinApi.ts
index c351129..156b48f 100644
--- a/client/src/lib/luxsinApi.ts
+++ b/client/src/lib/luxsinApi.ts
@@ -105,6 +105,10 @@ export interface DeviceState {
scene_value: number;
crossfeed_enable: number;
crossfeed_value: number;
+ /** Custom crossfeed cut-off frequency in Hz (500–1000, step 1). */
+ crossfeed_custom_fc: number;
+ /** Custom crossfeed attenuation, stored as dB×10 (1.0 dB=10 … 15.0 dB=150). */
+ crossfeed_custom_gain: number;
subwoofer_enable: number;
subwoofer_value: number;
subwoofer_rate: number;
@@ -565,6 +569,8 @@ export const MOCK_DEVICE_STATE: DeviceState = {
scene_value: 0,
crossfeed_enable: 0,
crossfeed_value: 0,
+ crossfeed_custom_fc: 700,
+ crossfeed_custom_gain: 45,
subwoofer_enable: 0,
subwoofer_value: 80,
subwoofer_rate: 0,
diff --git a/client/src/locales/data-de.json b/client/src/locales/data-de.json
index 5f081dc..3d43350 100644
--- a/client/src/locales/data-de.json
+++ b/client/src/locales/data-de.json
@@ -109,8 +109,12 @@
"options": [
"BS2B Standard (700 Hz, 4,5 dB)",
"BS2B Populär (700 Hz, 6 dB)",
- "BS2B Entspannt (650 Hz, 9,5 dB)"
- ]
+ "BS2B Entspannt (650 Hz, 9,5 dB)",
+ "Custom"
+ ],
+ "customTitle": "Benutzerdefinierte Einstellungen",
+ "freqLabel": "Frequence",
+ "attenuationLabel": "Attenuation"
},
"hearing": {
"label": "Hörkompensation",
diff --git a/client/src/locales/data-en.json b/client/src/locales/data-en.json
index 83b81aa..1531a2e 100644
--- a/client/src/locales/data-en.json
+++ b/client/src/locales/data-en.json
@@ -109,8 +109,12 @@
"options": [
"BS2B default (700 Hz, 4.5 dB)",
"BS2B popular (700 Hz, 6 dB)",
- "BS2B relax (650 Hz, 9.5 dB)"
- ]
+ "BS2B relax (650 Hz, 9.5 dB)",
+ "Custom"
+ ],
+ "customTitle": "Custom Settings",
+ "freqLabel": "Frequence",
+ "attenuationLabel": "Attenuation"
},
"hearing": {
"label": "Hearing compensation",
diff --git a/client/src/locales/data-es.json b/client/src/locales/data-es.json
index b55379a..f0d6bc7 100644
--- a/client/src/locales/data-es.json
+++ b/client/src/locales/data-es.json
@@ -109,8 +109,12 @@
"options": [
"BS2B predeterminado (700 Hz, 4,5 dB)",
"BS2B popular (700 Hz, 6 dB)",
- "BS2B relajado (650 Hz, 9,5 dB)"
- ]
+ "BS2B relajado (650 Hz, 9,5 dB)",
+ "Custom"
+ ],
+ "customTitle": "Configuración personalizada",
+ "freqLabel": "Frequence",
+ "attenuationLabel": "Attenuation"
},
"hearing": {
"label": "Compensación auditiva",
diff --git a/client/src/locales/data-fr.json b/client/src/locales/data-fr.json
index cbf32d0..6b4f59c 100644
--- a/client/src/locales/data-fr.json
+++ b/client/src/locales/data-fr.json
@@ -109,8 +109,12 @@
"options": [
"BS2B défaut (700 Hz, 4,5 dB)",
"BS2B populaire (700 Hz, 6 dB)",
- "BS2B relax (650 Hz, 9,5 dB)"
- ]
+ "BS2B relax (650 Hz, 9,5 dB)",
+ "Custom"
+ ],
+ "customTitle": "Réglages personnalisés",
+ "freqLabel": "Frequence",
+ "attenuationLabel": "Attenuation"
},
"hearing": {
"label": "Compensation auditive",
diff --git a/client/src/locales/data-zh-HK.json b/client/src/locales/data-zh-HK.json
index 96b1923..be0de66 100644
--- a/client/src/locales/data-zh-HK.json
+++ b/client/src/locales/data-zh-HK.json
@@ -102,8 +102,12 @@
"options": [
"BS2B default(700Hz,4.5dB)",
"BS2B polular(700Hz,6dB)",
- "BS2B relax(650Hz,9.5dB)"
- ]
+ "BS2B relax(650Hz,9.5dB)",
+ "Custom"
+ ],
+ "customTitle": "自訂設置",
+ "freqLabel": "截止頻率",
+ "attenuationLabel": "衰減量"
},
"hearing": {
"label": "聽力補償",
diff --git a/client/src/locales/data-zh.json b/client/src/locales/data-zh.json
index 8fc44ef..acf495c 100644
--- a/client/src/locales/data-zh.json
+++ b/client/src/locales/data-zh.json
@@ -102,8 +102,12 @@
"options": [
"BS2B default(700Hz,4.5dB)",
"BS2B polular(700Hz,6dB)",
- "BS2B relax(650Hz,9.5dB)"
- ]
+ "BS2B relax(650Hz,9.5dB)",
+ "Custom"
+ ],
+ "customTitle": "自定义设置",
+ "freqLabel": "截止频率",
+ "attenuationLabel": "衰减量"
},
"hearing": {
"label": "听力补偿",
diff --git a/client/src/pages/EffectsPage.tsx b/client/src/pages/EffectsPage.tsx
index c25fbcf..c6a4de3 100644
--- a/client/src/pages/EffectsPage.tsx
+++ b/client/src/pages/EffectsPage.tsx
@@ -409,6 +409,20 @@ export default function EffectsPage() {
const sceneDisplay = sceneLabels[sceneIdx] ?? "";
const crossIdx = clampPickIndex(crossfeedVal, crossfeedLabels.length);
const crossfeedDisplay = crossfeedLabels[crossIdx] ?? "";
+ // Custom crossfeed values: fc in Hz, gain stored as dB×10
+ const crossfeedCustomFc = ds?.crossfeed_custom_fc ?? 700;
+ const crossfeedCustomGain = (ds?.crossfeed_custom_gain ?? 45) / 10;
+
+ const [localCrossfeedFc, setLocalCrossfeedFc] = useState(() => crossfeedCustomFc);
+ const [localCrossfeedGain, setLocalCrossfeedGain] = useState(() => crossfeedCustomGain);
+ const isDraggingCrossfeedFc = useRef(false);
+ const isDraggingCrossfeedGain = useRef(false);
+
+ useEffect(() => {
+ if (!isDraggingCrossfeedFc.current) setLocalCrossfeedFc(crossfeedCustomFc);
+ if (!isDraggingCrossfeedGain.current) setLocalCrossfeedGain(crossfeedCustomGain);
+ }, [crossfeedCustomFc, crossfeedCustomGain]);
+
const hearingIdx = clampPickIndex(hearingSelect, hearingLabels.length);
const hearingDisplay =
hearingLabels[hearingIdx] ?? effectText.hearing?.emptyProfile ?? "—";
@@ -528,6 +542,69 @@ export default function EffectsPage() {
{crossfeedDisplay}