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} + + {/* ── Custom crossfeed sub-module (shown when Custom / index 3 selected) ── */} + {crossfeedOn && crossIdx === 3 && ( +
+
+ {effectText.crossfeed?.customTitle ?? "自定义设置"} +
+ + {/* Frequence — cut-off frequency 500–1000 Hz */} +
+
+ + {effectText.crossfeed?.freqLabel ?? "Frequence"} + + + {Math.round(localCrossfeedFc)} Hz + +
+ { + isDraggingCrossfeedFc.current = true; + setLocalCrossfeedFc(Math.round(v)); + }} + onRelease={(v) => { + isDraggingCrossfeedFc.current = false; + const fc = Math.round(v); + setLocalCrossfeedFc(fc); + updateSetting({ crossfeed_custom_fc: fc }); + }} + /> +
+ + {/* Attenuation — 1.0–15.0 dB, sent as value×10 */} +
+
+ + {effectText.crossfeed?.attenuationLabel ?? "Attenuation"} + + + {localCrossfeedGain.toFixed(1)} dB + +
+ { + isDraggingCrossfeedGain.current = true; + setLocalCrossfeedGain(v); + }} + onRelease={(v) => { + isDraggingCrossfeedGain.current = false; + updateSetting({ crossfeed_custom_gain: Math.round(v * 10) }); + }} + /> +
+
+ )} {/* ── 音调 ── */}