Files
controller-v2/client/src/pages/DspPage.tsx
T

150 lines
6.3 KiB
TypeScript
Raw Normal View History

/* ============================================================
DSP & DAC PAGE
Design: iOS Settings style
Sections: DSP引擎 / DAC配置 / 模拟级 / 音频格式
All selectable options → navigate to /select page
============================================================ */
import { useDevice } from "@/contexts/DeviceContext";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { useLocation } from "wouter";
import BottomNav from "@/components/BottomNav";
import { navigateToSelect } from "./SelectPage";
function IOSToggle({ checked, onChange }: { checked: boolean; onChange: (v: boolean) => void }) {
return (
<label className="ios-toggle" onClick={(e) => e.stopPropagation()}>
<input type="checkbox" checked={checked} onChange={(e) => onChange(e.target.checked)} />
<span className="ios-toggle-track"><span className="ios-toggle-thumb" /></span>
</label>
);
}
function SectionHeader({ title }: { title: string }) {
return (
<div className="px-1 pt-5 pb-2">
<span className="text-[14px] font-semibold" style={{ color: "#00FFF6" }}>{title}</span>
</div>
);
}
function ListRow({ label, description, value, onClick, toggle, checked, onToggle }: {
label: string; description?: string; value?: string;
onClick?: () => void; toggle?: boolean; checked?: boolean; onToggle?: (v: boolean) => void;
}) {
if (toggle) {
return (
<div className="ios-list-row">
<div className="flex-1 min-w-0">
<div className="text-[16px] text-white">{label}</div>
{description && <div className="text-[12px] text-white/35 mt-0.5">{description}</div>}
</div>
<IOSToggle checked={!!checked} onChange={onToggle ?? (() => {})} />
</div>
);
}
return (
<button className="ios-list-row w-full text-left active:bg-white/5 transition-colors" onClick={onClick}>
<div className="flex-1 min-w-0">
<div className="text-[16px] text-white">{label}</div>
{description && <div className="text-[12px] text-white/35 mt-0.5">{description}</div>}
</div>
<div className="flex items-center gap-1">
{value && <span className="ios-row-value">{value}</span>}
<ChevronRight size={16} className="ios-chevron ml-1" />
</div>
</button>
);
}
const DAC_ARC_OPTIONS = ["关闭", "模式1", "模式2", "模式3"];
const DAC_GAIN_OPTIONS = ["0dB", "+3dB", "+6dB", "+9dB"];
const DAC_IMP_OPTIONS = ["32Ω", "150Ω", "300Ω"];
const ANALOG_GAIN_OPTIONS = ["低", "中低", "中高", "高"];
const XLR_OPTIONS = ["正常", "反转"];
export default function DspPage() {
const [, setLocation] = useLocation();
const { deviceState, updateSetting } = useDevice();
const ds = deviceState;
const dspOn = (ds?.dsp_enable ?? 0) === 0;
const peqOn = (ds?.peqEnable ?? 0) === 1;
const dacVolDirect = (ds?.dacVolumeDirect ?? 0) === 1;
const dacArcIdx = ds?.dacArc ?? 0;
const dacGainIdx = ds?.dacGain ?? 0;
const dacImpIdx = ds?.dacImpedance ?? 0;
const analogGainIdx = ds?.analogGain ?? 0;
const xlrIdx = ds?.xlr ?? 0;
const goSelect = (title: string, options: string[], selected: number, key: string) => {
navigateToSelect(title, options, selected, "/dsp", key);
setLocation("/select");
};
return (
<div className="min-h-screen bg-black">
<div className="page-header">
<button onClick={() => setLocation("/")} className="mr-4 text-white/60 active:text-white transition-colors">
<ChevronLeft size={24} />
</button>
<h1 className="flex-1 text-center text-[17px] font-semibold text-white">DSP & DAC</h1>
<div className="w-8" />
</div>
<div className="px-4 pb-32">
{/* ── DSP 引擎 ── */}
<SectionHeader title="DSP 引擎" />
<div className="ios-list-group">
<ListRow label="DSP 处理" description="主数字信号处理器"
toggle checked={dspOn}
onToggle={(v) => updateSetting({ dsp_enable: v ? 0 : 1 })} />
<ListRow label="参数均衡器" description="5 段参数均衡器"
toggle checked={peqOn}
onToggle={(v) => updateSetting({ peqEnable: v ? 1 : 0 })} />
</div>
{/* ── DAC 配置 ── */}
<SectionHeader title="DAC 配置" />
<div className="ios-list-group">
<ListRow label="ARC 模式" description="音频回传通道模式"
value={DAC_ARC_OPTIONS[dacArcIdx]}
onClick={() => goSelect("ARC 模式", DAC_ARC_OPTIONS, dacArcIdx, "dacArc")} />
<ListRow label="DAC 增益" description="数模转换器增益"
value={DAC_GAIN_OPTIONS[dacGainIdx]}
onClick={() => goSelect("DAC 增益", DAC_GAIN_OPTIONS, dacGainIdx, "dacGain")} />
<ListRow label="DAC 阻抗" description="输出阻抗设置"
value={DAC_IMP_OPTIONS[dacImpIdx]}
onClick={() => goSelect("DAC 阻抗", DAC_IMP_OPTIONS, dacImpIdx, "dacImpedance")} />
<ListRow label="直通音量控制" description="DAC 直通音量"
toggle checked={dacVolDirect}
onToggle={(v) => updateSetting({ dacVolumeDirect: v ? 1 : 0 })} />
</div>
{/* ── 模拟级 ── */}
<SectionHeader title="模拟级" />
<div className="ios-list-group">
<ListRow label="模拟增益" description="模拟放大器增益级"
value={ANALOG_GAIN_OPTIONS[analogGainIdx]}
onClick={() => goSelect("模拟增益", ANALOG_GAIN_OPTIONS, analogGainIdx, "analogGain")} />
<ListRow label="XLR 极性" description="XLR 输出相位极性"
value={XLR_OPTIONS[xlrIdx]}
onClick={() => goSelect("XLR 极性", XLR_OPTIONS, xlrIdx, "xlr")} />
</div>
{/* ── 音频格式 ── */}
<SectionHeader title="当前音频格式" />
<div className="ios-list-group p-4">
<div className="flex items-center gap-3">
<div className="w-2 h-2 rounded-full" style={{ background: "#00FFF6", boxShadow: "0 0 6px rgba(0,255,246,0.8)" }} />
<span className="text-[15px] font-semibold" style={{ color: "#00FFF6", fontFamily: "'JetBrains Mono', monospace" }}>
{ds?.audioFormat ?? "PCM 44.1kHz"}
</span>
</div>
</div>
</div>
<BottomNav />
</div>
);
}