import { getFilterShortName } from "@/lib/peqAudio"; import { BAND_FREQ_MAX, BAND_FREQ_MIN, BAND_GAIN_MAX, BAND_GAIN_MIN, BAND_Q_MAX, BAND_Q_MIN, } from "./eqConstants"; import type { BandParamKind } from "./types"; export function eqInterp(template: string | undefined, vars: Record): string { if (!template) return ""; let s = template; for (const [k, v] of Object.entries(vars)) { s = s.split(`{{${k}}}`).join(String(v)); } return s; } /** 将设备/表单中的频率统一为 Hz 数值;无法解析时返回 null */ export function normalizeBandFreqHz(freq: unknown): number | null { if (typeof freq === "number" && Number.isFinite(freq)) return freq; if (typeof freq === "string") { const trimmed = freq.trim().replace(/,/g, ""); if (!trimmed) return null; let s = trimmed.toLowerCase().replace(/hz$/, ""); const kHz = /k(hz)?$/.test(s); if (kHz) s = s.replace(/k(hz)?$/, ""); const num = Number.parseFloat(s); return Number.isFinite(num) ? (kHz ? num * 1000 : num) : null; } return null; } export function formatBandGainDisplay(gain: unknown) { const db = typeof gain === "number" && Number.isFinite(gain) ? gain : Number(gain); if (!Number.isFinite(db)) return "0.0 dB"; const text = db.toFixed(1); return `${db >= 0 ? `+${text}` : text} dB`; } export function formatBandQDisplay(q: unknown) { const n = typeof q === "number" && Number.isFinite(q) ? q : Number(q); return Number.isFinite(n) ? n.toFixed(2) : "—"; } export function formatBandFreqDisplay(freq: unknown) { const hz = normalizeBandFreqHz(freq); if (hz === null) return "—"; if (hz >= 1000) { return `${(hz / 1000).toFixed(2).replace(/\.?0+$/, "")} kHz`; } return `${Math.round(hz)} Hz`; } export function formatBandParamForInput( kind: BandParamKind, band: { freq: number; gain: number; q: number }, ) { switch (kind) { case "freq": return String(band.freq); case "gain": return band.gain.toFixed(1); case "q": return band.q.toFixed(2); } } export function parseBandParamInput( kind: BandParamKind, raw: string, messages: { invalid: string; outOfRange: string }, ): { ok: true; value: number } | { ok: false; message: string } { const trimmed = raw.trim(); if (!trimmed) return { ok: false, message: messages.invalid }; if (kind === "freq") { let s = trimmed.replace(/\s+/g, "").toLowerCase().replace(/hz$/, ""); const kHz = /k(hz)?$/.test(s); if (kHz) s = s.replace(/k(hz)?$/, ""); const num = Number.parseFloat(s); if (!Number.isFinite(num)) return { ok: false, message: messages.invalid }; const hz = Math.round(kHz ? num * 1000 : num); if (hz < BAND_FREQ_MIN || hz > BAND_FREQ_MAX) { return { ok: false, message: messages.outOfRange }; } return { ok: true, value: hz }; } if (kind === "gain") { const s = trimmed.replace(/\s*dB\s*$/i, "").trim(); const num = Number.parseFloat(s); if (!Number.isFinite(num)) return { ok: false, message: messages.invalid }; const gain = Number(num.toFixed(1)); if (gain < BAND_GAIN_MIN || gain > BAND_GAIN_MAX) { return { ok: false, message: messages.outOfRange }; } return { ok: true, value: gain }; } const num = Number.parseFloat(trimmed); if (!Number.isFinite(num)) return { ok: false, message: messages.invalid }; const q = Number(num.toFixed(2)); if (q < BAND_Q_MIN || q > BAND_Q_MAX) { return { ok: false, message: messages.outOfRange }; } return { ok: true, value: q }; } export function normalizeFilterType(type: string | number | undefined): string { if (type === undefined || type === null) return "PEAK"; if (typeof type === "number") { switch (type) { case 0: return "LPF"; case 1: return "HPF"; case 2: return "BPF"; case 3: return "NOTCH"; case 4: return "PEAK"; case 5: return "LSHELF"; case 6: return "HSHELF"; case 7: return "APF"; default: return "PEAK"; } } return getFilterShortName(type); } /** 10 段滤波器按钮上的紧凑频率标签(始终为数字,避免被翻译插件改成英文单词) */ export function freqLabel(freq: unknown) { const hz = normalizeBandFreqHz(freq); if (hz === null) return "—"; if (hz >= 1000) { const k = hz / 1000; const text = k >= 10 ? String(Math.round(k)) : k.toFixed(1).replace(/\.0$/, ""); return `${text}K`; } return String(Math.round(hz)); }