x9适配
This commit is contained in:
@@ -103,6 +103,13 @@ export interface DeviceState {
|
||||
subwoofer_value: number;
|
||||
subwoofer_rate: number;
|
||||
subwoofer_gain: number;
|
||||
subwoofer_mix_type: number;
|
||||
subwoofer_delay: number;
|
||||
subwoofer_delay_main: number;
|
||||
subwoofer_delay_r: number;
|
||||
subwoofer_delay_main_r: number;
|
||||
subwoofer_lpf_enable: number;
|
||||
subwoofer_hpf_enable: number;
|
||||
loudness_enable: number;
|
||||
loudness_bass_gain: number;
|
||||
loudness_treble_gain: number;
|
||||
@@ -163,6 +170,9 @@ export interface PeqApplyPayload {
|
||||
// ============================================================
|
||||
export const INPUT_LABELS = ["USB", "USB-C", "Coaxial", "Optical", "Bluetooth", "IIS", "RCA"];
|
||||
export const OUTPUT_LABELS = ["XLR", "RCA", "Headset", "XLR/RCA"];
|
||||
|
||||
/** Device `output` index for headphone / headset. */
|
||||
export const OUTPUT_HEADSET_INDEX = 2;
|
||||
export const LANGUAGE_LABELS = ["English", "繁體中文", "简体中文"];
|
||||
export const SCREEN_LIGHT_LABELS = ["Bright", "Medium", "Dark"];
|
||||
export const KNOB_LIGHT_LABELS = ["Off", "Bright", "Medium", "Dark"];
|
||||
@@ -213,7 +223,9 @@ export class LuxsinAPI {
|
||||
const response = await fetch(`${this.baseUrl}/dev/info.cgi?action=syncData`);
|
||||
const text = await response.text();
|
||||
const decoded = decodeCustomBase64(text.trim());
|
||||
return JSON.parse(decoded) as DeviceState;
|
||||
const state = JSON.parse(decoded) as DeviceState;
|
||||
console.log("[syncData] decoded", state);
|
||||
return state;
|
||||
} catch (error) {
|
||||
this.maybeRedirectForHttpsCert(error);
|
||||
throw error;
|
||||
@@ -333,6 +345,20 @@ export function parseFirmwareVersion(version: unknown): number {
|
||||
return Number(parts[parts.length - 1]);
|
||||
}
|
||||
|
||||
/** Pre-out volume passthrough mode selected (0dB or -12dB). */
|
||||
export function isVolumePassthroughActive(dacVolumeDirect: number | undefined): boolean {
|
||||
return dacVolumeDirect === 1 || dacVolumeDirect === 2;
|
||||
}
|
||||
|
||||
/** Lock home volume controls when passthrough is on, except output is headphone. */
|
||||
export function isHomeVolumeLockedByPassthrough(
|
||||
dacVolumeDirect: number | undefined,
|
||||
output: number | undefined,
|
||||
): boolean {
|
||||
if (!isVolumePassthroughActive(dacVolumeDirect)) return false;
|
||||
return (output ?? -1) !== OUTPUT_HEADSET_INDEX;
|
||||
}
|
||||
|
||||
/** Max `bootSound` index available for the current firmware. */
|
||||
export function getBootSoundMaxIndex(firmwareVersion: number): number {
|
||||
return firmwareVersion >= 26 ? 10 : 6;
|
||||
@@ -401,6 +427,13 @@ export const MOCK_DEVICE_STATE: DeviceState = {
|
||||
subwoofer_value: 80,
|
||||
subwoofer_rate: 0,
|
||||
subwoofer_gain: 0,
|
||||
subwoofer_mix_type: 0,
|
||||
subwoofer_delay: 582,
|
||||
subwoofer_delay_main: 571,
|
||||
subwoofer_delay_r: 582,
|
||||
subwoofer_delay_main_r: 571,
|
||||
subwoofer_lpf_enable: 0,
|
||||
subwoofer_hpf_enable: 0,
|
||||
loudness_enable: 0,
|
||||
loudness_bass_gain: 3,
|
||||
loudness_treble_gain: 2,
|
||||
|
||||
+53
-32
@@ -12,6 +12,8 @@ export const TYPE_HIGHPASS = 1;
|
||||
export const TYPE_BANDPASS = 2;
|
||||
export const TYPE_NOTCH = 3;
|
||||
export const TYPE_ALLPASS = 7;
|
||||
/** First-order low-pass (subwoofer LPF curve). */
|
||||
export const LPF_1ST = 8;
|
||||
|
||||
// Filter type mapping (device uses numeric type; pass-through when already a number)
|
||||
export function getFilterType(typeName: string | number): number {
|
||||
@@ -257,6 +259,16 @@ export function getSectionsMatrix(
|
||||
a1 = b1;
|
||||
a2 = b0;
|
||||
break;
|
||||
case LPF_1ST: {
|
||||
const alpha1st = sin_w / (cos_w + 1);
|
||||
b0 = alpha1st / (1 + alpha1st);
|
||||
b1 = b0;
|
||||
b2 = 0;
|
||||
a0 = 1;
|
||||
a1 = (alpha1st - 1) / (1 + alpha1st);
|
||||
a2 = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
b0 = 1;
|
||||
b1 = 0;
|
||||
@@ -440,7 +452,8 @@ export function getChartOps(
|
||||
dataSet: [number[], number[]],
|
||||
yMax: number,
|
||||
yMin: number,
|
||||
lineColor: string
|
||||
lineColor: string,
|
||||
withPeqLegend = true,
|
||||
) {
|
||||
const labelsToShow = [0, 43, 85, 116, 160, 200, 233, 281, 320, 348];
|
||||
const labelMap: { [key: number]: string } = {
|
||||
@@ -507,44 +520,52 @@ export function getChartOps(
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
show: withPeqLegend,
|
||||
top: 15,
|
||||
data: [
|
||||
{
|
||||
name: 'Equalizer',
|
||||
icon: 'circle',
|
||||
textStyle: { color: lineColor },
|
||||
itemStyle: {
|
||||
color: lineColor,
|
||||
borderColor: lineColor,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Raw',
|
||||
icon: 'circle',
|
||||
textStyle: { color: '#ffffff' },
|
||||
itemStyle: {
|
||||
color: '#ffffff',
|
||||
borderColor: '#ffffff',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Equalized',
|
||||
icon: 'circle',
|
||||
textStyle: { color: '#23d2fe' },
|
||||
itemStyle: {
|
||||
color: '#23d2fe',
|
||||
borderColor: '#23d2fe',
|
||||
},
|
||||
},
|
||||
],
|
||||
data: withPeqLegend
|
||||
? [
|
||||
{
|
||||
name: 'Equalizer',
|
||||
icon: 'circle',
|
||||
textStyle: { color: lineColor },
|
||||
itemStyle: {
|
||||
color: lineColor,
|
||||
borderColor: lineColor,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Raw',
|
||||
icon: 'circle',
|
||||
textStyle: { color: '#ffffff' },
|
||||
itemStyle: {
|
||||
color: '#ffffff',
|
||||
borderColor: '#ffffff',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Equalized',
|
||||
icon: 'circle',
|
||||
textStyle: { color: '#23d2fe' },
|
||||
itemStyle: {
|
||||
color: '#23d2fe',
|
||||
borderColor: '#23d2fe',
|
||||
},
|
||||
},
|
||||
]
|
||||
: [],
|
||||
textStyle: {
|
||||
fontSize: 13,
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: 48,
|
||||
right: 16,
|
||||
top: withPeqLegend ? 40 : 12,
|
||||
bottom: 28,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Equalizer',
|
||||
name: withPeqLegend ? 'Equalizer' : 'LPF',
|
||||
data: dataSet[1],
|
||||
type: 'line' as const,
|
||||
showSymbol: false,
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Subwoofer low-pass frequency response — ported from olds/effect.js (painting, getLps).
|
||||
* Uses peqAudio getSectionsMatrix + visualizeResponse (same as EQ page).
|
||||
*/
|
||||
import {
|
||||
getSectionsMatrix,
|
||||
LPF_1ST,
|
||||
TYPE_LOWPASS,
|
||||
visualizeResponse,
|
||||
} from "@/lib/peqAudio";
|
||||
|
||||
export const SUBWOOFER_LPF_FS = 48000;
|
||||
const SUBWOOFER_PARAM = [1, 1, 1.35, 1.28, 1.5, 1.4, 1.65, 1.55];
|
||||
|
||||
function pushSection(list: ReturnType<typeof getSectionsMatrix>[], fc: number, type: number) {
|
||||
list.push(getSectionsMatrix(0, fc, 0.707, type, false, SUBWOOFER_LPF_FS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Slope layout: [2nd-order count, 1st-order count] — see olds/effect.js getLps.
|
||||
*/
|
||||
export function getSubwooferLps(lpsIndex: number, lpsVal: number): [number, number] {
|
||||
let count12 = 0;
|
||||
let count6 = 1;
|
||||
if (lpsIndex > 1) {
|
||||
const tmp = lpsVal / 12;
|
||||
if (!Number.isInteger(tmp)) {
|
||||
count12 = Math.floor(tmp);
|
||||
} else {
|
||||
count12 = tmp;
|
||||
count6 = 0;
|
||||
}
|
||||
} else if (lpsIndex === 1) {
|
||||
count12 = 1;
|
||||
count6 = 0;
|
||||
}
|
||||
return [count12, count6];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build cascaded LPF coeffs and return log-frequency magnitude plot data.
|
||||
*/
|
||||
export function buildSubwooferLpfResponse(
|
||||
fc: number,
|
||||
lpsIndex: number,
|
||||
): [number[], number[]] {
|
||||
const lpsVal = (lpsIndex + 1) * 6;
|
||||
const lpsArray = getSubwooferLps(lpsIndex, lpsVal);
|
||||
const fcAdj = fc * SUBWOOFER_PARAM[lpsIndex];
|
||||
const list: ReturnType<typeof getSectionsMatrix>[] = [];
|
||||
|
||||
if (lpsArray[0] === 0) {
|
||||
pushSection(list, fcAdj, LPF_1ST);
|
||||
} else {
|
||||
for (let i = 0; i < lpsArray[0]; i += 1) {
|
||||
pushSection(list, fcAdj, TYPE_LOWPASS);
|
||||
}
|
||||
if (lpsArray[1] > 0) {
|
||||
for (let i = 0; i < lpsArray[1]; i += 1) {
|
||||
pushSection(list, fcAdj, LPF_1ST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return visualizeResponse(list, SUBWOOFER_LPF_FS);
|
||||
}
|
||||
Reference in New Issue
Block a user