style: auto-fix linting issues

This commit is contained in:
tryptz 2026-04-01 20:57:22 +00:00 committed by edideaur
parent d4d1fe8494
commit 77f9e10fdc
4 changed files with 164 additions and 59 deletions

View file

@ -2,7 +2,6 @@
// Target Curves & Data Parser - Ported from Seap Engine
// Contains target frequency response curves and raw data parser
// Raw content synchronized with features/autoeq/Targets/ directory
const RAW_HARMAN_OE_2018 = `
@ -2713,7 +2712,6 @@ const RAW_HIFI_ENDGAME_2026_MKII = `
20000.00 72.378
`;
const RAW_PEQDB_ULTRA = `
20.00 83.153
20.36 83.149
@ -3487,7 +3485,6 @@ const RAW_PEQDB_DIAMOND_BETA = `
20000.00 72.028
`;
const RAW_SEAP = `
20.00 82.982
20.36 82.975
@ -4301,7 +4298,6 @@ const RAW_FLAT_LINE = `
20000.00 75.000
`;
// --- PARSER ---
/**
* Parse raw frequency/gain text data into [{freq, gain}] arrays
@ -4331,15 +4327,17 @@ function parseRawData(raw) {
const hasHeader = /[a-zA-Z]/.test(firstLine);
if (hasHeader) {
const headers = firstLine.split(delimiter).map(h => h.trim().toLowerCase().replace(/['"]+/g, ''));
const fIdx = headers.findIndex(h => h.includes('freq') || h === 'f');
const headers = firstLine.split(delimiter).map((h) => h.trim().toLowerCase().replace(/['"]+/g, ''));
const fIdx = headers.findIndex((h) => h.includes('freq') || h === 'f');
if (fIdx > -1) freqIdx = fIdx;
const rIdx = headers.findIndex(h => h === 'raw');
const rIdx = headers.findIndex((h) => h === 'raw');
if (rIdx > -1) {
gainIdx = rIdx;
} else {
const splIdx = headers.findIndex(h => h.includes('spl') || h.includes('gain') || h.includes('db') || h.includes('mag'));
const splIdx = headers.findIndex(
(h) => h.includes('spl') || h.includes('gain') || h.includes('db') || h.includes('mag')
);
if (splIdx > -1 && splIdx !== freqIdx) gainIdx = splIdx;
}
}

View file

@ -21,42 +21,55 @@ const DB_DIVISOR = 40;
function calculateBiquadResponse(f, band, sr = DEFAULT_SR) {
if (!band.enabled) return 0;
if (!band.type || band.type.length === 0) return 0;
const w = 2 * PI * band.freq / sr;
const p = 2 * PI * f / sr;
const w = (2 * PI * band.freq) / sr;
const p = (2 * PI * f) / sr;
const s = Math.sin(w) / (2 * band.q);
const A = Math.pow(DB_BASE, band.gain / DB_DIVISOR);
const c = Math.cos(w);
let b0 = 0, b1 = 0, b2 = 0, a0 = 0, a1 = 0, a2 = 0;
let b0 = 0,
b1 = 0,
b2 = 0,
a0 = 0,
a1 = 0,
a2 = 0;
const t = band.type[0];
if (t === 'p') {
b0 = 1 + s * A; b1 = -2 * c; b2 = 1 - s * A;
a0 = 1 + s / A; a1 = -2 * c; a2 = 1 - s / A;
b0 = 1 + s * A;
b1 = -2 * c;
b2 = 1 - s * A;
a0 = 1 + s / A;
a1 = -2 * c;
a2 = 1 - s / A;
} else if (t === 'l') {
const sq = 2 * Math.sqrt(A) * s;
b0 = A * ((A + 1) - (A - 1) * c + sq);
b1 = 2 * A * ((A - 1) - (A + 1) * c);
b2 = A * ((A + 1) - (A - 1) * c - sq);
a0 = (A + 1) + (A - 1) * c + sq;
a1 = -2 * ((A - 1) + (A + 1) * c);
a2 = (A + 1) + (A - 1) * c - sq;
b0 = A * (A + 1 - (A - 1) * c + sq);
b1 = 2 * A * (A - 1 - (A + 1) * c);
b2 = A * (A + 1 - (A - 1) * c - sq);
a0 = A + 1 + (A - 1) * c + sq;
a1 = -2 * (A - 1 + (A + 1) * c);
a2 = A + 1 + (A - 1) * c - sq;
} else if (t === 'h') {
const sq = 2 * Math.sqrt(A) * s;
b0 = A * ((A + 1) + (A - 1) * c + sq);
b1 = -2 * A * ((A - 1) + (A + 1) * c);
b2 = A * ((A + 1) + (A - 1) * c - sq);
a0 = (A + 1) - (A - 1) * c + sq;
a1 = 2 * ((A - 1) - (A + 1) * c);
a2 = (A + 1) - (A - 1) * c - sq;
b0 = A * (A + 1 + (A - 1) * c + sq);
b1 = -2 * A * (A - 1 + (A + 1) * c);
b2 = A * (A + 1 + (A - 1) * c - sq);
a0 = A + 1 - (A - 1) * c + sq;
a1 = 2 * (A - 1 - (A + 1) * c);
a2 = A + 1 - (A - 1) * c - sq;
} else {
return 0;
}
const _a0 = 1 / a0;
const b0n = b0 * _a0, b1n = b1 * _a0, b2n = b2 * _a0;
const a1n = a1 * _a0, a2n = a2 * _a0;
const cp = Math.cos(p), c2p = Math.cos(2 * p);
const b0n = b0 * _a0,
b1n = b1 * _a0,
b2n = b2 * _a0;
const a1n = a1 * _a0,
a2n = a2 * _a0;
const cp = Math.cos(p),
c2p = Math.cos(2 * p);
const n = b0n * b0n + b1n * b1n + b2n * b2n + 2 * (b0n * b1n + b1n * b2n) * cp + 2 * b0n * b2n * c2p;
const d = 1 + a1n * a1n + a2n * a2n + 2 * (a1n + a1n * a2n) * cp + 2 * a2n * c2p;
return 10 * Math.log10(n / d);
@ -74,7 +87,10 @@ function interpolate(freq, data) {
if (freq >= data[data.length - 1].freq) return data[data.length - 1].gain;
for (let i = 0; i < data.length - 1; i++) {
if (freq >= data[i].freq && freq <= data[i + 1].freq) {
return data[i].gain + (freq - data[i].freq) / (data[i + 1].freq - data[i].freq) * (data[i + 1].gain - data[i].gain);
return (
data[i].gain +
((freq - data[i].freq) / (data[i + 1].freq - data[i].freq)) * (data[i + 1].gain - data[i].gain)
);
}
}
return 0;
@ -86,7 +102,8 @@ function interpolate(freq, data) {
* @returns {number} Average gain in midrange
*/
function getNormalizationOffset(data) {
let sum = 0, count = 0;
let sum = 0,
count = 0;
for (const p of data) {
if (p.freq >= 250 && p.freq <= 2500) {
sum += p.gain;
@ -108,18 +125,29 @@ function getNormalizationOffset(data) {
* @param {number} maxQ - Maximum Q factor
* @returns {Array<{id: number, type: string, freq: number, gain: number, q: number, enabled: boolean}>}
*/
function runAutoEqAlgorithm(measurement, target, bandCount, maxFreq = 16000, minFreq = 20, maxQ = 5.0, sampleRate = DEFAULT_SR) {
function runAutoEqAlgorithm(
measurement,
target,
bandCount,
maxFreq = 16000,
minFreq = 20,
maxQ = 5.0,
sampleRate = DEFAULT_SR
) {
if (minFreq > maxFreq) return [];
const off = getNormalizationOffset(target) - getNormalizationOffset(measurement);
let err = measurement.map(p => ({ freq: p.freq, gain: (p.gain + off) - interpolate(p.freq, target) }));
let err = measurement.map((p) => ({ freq: p.freq, gain: p.gain + off - interpolate(p.freq, target) }));
const hasInRangePoints = err.some(p => p.freq >= minFreq && p.freq <= maxFreq);
const hasInRangePoints = err.some((p) => p.freq >= minFreq && p.freq <= maxFreq);
if (!hasInRangePoints) return [];
const out = [];
for (let i = 0; i < bandCount; i++) {
let maxDev = 0, maxWeightedDev = 0, peakFreq = 1000, peakIdx = 0;
let maxDev = 0,
maxWeightedDev = 0,
peakFreq = 1000,
peakIdx = 0;
// Scan for maximum weighted error
for (let j = 0; j < err.length; j++) {
@ -158,8 +186,10 @@ function runAutoEqAlgorithm(measurement, target, bandCount, maxFreq = 16000, min
if (Math.abs(gain) < 0.2) break;
// Q factor calculation from error bandwidth (half-gain points)
let upperFreq = peakFreq, lowerFreq = peakFreq;
let foundLower = false, foundUpper = false;
let upperFreq = peakFreq,
lowerFreq = peakFreq;
let foundLower = false,
foundUpper = false;
const thresholdError = maxDev / 2;
for (let k = peakIdx; k >= 0; k--) {
if (Math.abs(err[k].gain) < Math.abs(thresholdError)) {
@ -179,9 +209,9 @@ function runAutoEqAlgorithm(measurement, target, bandCount, maxFreq = 16000, min
// If half-gain boundary not found on one side, mirror the other side
// to avoid degenerate bandwidth = 0 producing extremely narrow filters
if (!foundLower && foundUpper) {
lowerFreq = peakFreq * peakFreq / upperFreq;
lowerFreq = (peakFreq * peakFreq) / upperFreq;
} else if (!foundUpper && foundLower) {
upperFreq = peakFreq * peakFreq / lowerFreq;
upperFreq = (peakFreq * peakFreq) / lowerFreq;
} else if (!foundLower && !foundUpper) {
// Neither boundary found — use 1 octave default
lowerFreq = peakFreq / Math.SQRT2;
@ -212,7 +242,7 @@ function runAutoEqAlgorithm(measurement, target, bandCount, maxFreq = 16000, min
out.push(newBand);
// Update error curve by applying the new band's response
err = err.map(p => ({ ...p, gain: p.gain + calculateBiquadResponse(p.freq, newBand, sampleRate) }));
err = err.map((p) => ({ ...p, gain: p.gain + calculateBiquadResponse(p.freq, newBand, sampleRate) }));
}
return out.sort((a, b) => a.freq - b.freq).map((b, i) => ({ ...b, id: i }));

View file

@ -12,23 +12,83 @@ const CACHE_EXPIRY = 24 * 60 * 60 * 1000; // 24 hours
// 5 most popular headphones — pre-loaded as defaults and shown in the headphone select
// All measured on Rtings B&K 5128 rig for consistency
const POPULAR_HEADPHONES = [
{ name: 'Sony WH-1000XM5 (Rtings)', type: 'over-ear', path: 'Rtings/Bruel & Kjaer 5128 over-ear/Sony WH-1000XM5', fileName: 'Sony WH-1000XM5.csv' },
{ name: 'Apple AirPods Pro2 (Rtings)', type: 'in-ear', path: 'Rtings/Bruel & Kjaer 5128 in-ear/Apple AirPods Pro2', fileName: 'Apple AirPods Pro2.csv' },
{ name: 'Sony WF-1000XM5 (Rtings)', type: 'in-ear', path: 'Rtings/Bruel & Kjaer 5128 in-ear/Sony WF-1000XM5', fileName: 'Sony WF-1000XM5.csv' },
{ name: 'Samsung Galaxy Buds3 Pro (Rtings)', type: 'in-ear', path: 'Rtings/Bruel & Kjaer 5128 in-ear/Samsung Galaxy Buds3 Pro', fileName: 'Samsung Galaxy Buds3 Pro.csv' },
{ name: 'Sennheiser HD 600 (Rtings)', type: 'over-ear', path: 'Rtings/Bruel & Kjaer 5128 over-ear/Sennheiser HD 600', fileName: 'Sennheiser HD 600.csv' },
{
name: 'Sony WH-1000XM5 (Rtings)',
type: 'over-ear',
path: 'Rtings/Bruel & Kjaer 5128 over-ear/Sony WH-1000XM5',
fileName: 'Sony WH-1000XM5.csv',
},
{
name: 'Apple AirPods Pro2 (Rtings)',
type: 'in-ear',
path: 'Rtings/Bruel & Kjaer 5128 in-ear/Apple AirPods Pro2',
fileName: 'Apple AirPods Pro2.csv',
},
{
name: 'Sony WF-1000XM5 (Rtings)',
type: 'in-ear',
path: 'Rtings/Bruel & Kjaer 5128 in-ear/Sony WF-1000XM5',
fileName: 'Sony WF-1000XM5.csv',
},
{
name: 'Samsung Galaxy Buds3 Pro (Rtings)',
type: 'in-ear',
path: 'Rtings/Bruel & Kjaer 5128 in-ear/Samsung Galaxy Buds3 Pro',
fileName: 'Samsung Galaxy Buds3 Pro.csv',
},
{
name: 'Sennheiser HD 600 (Rtings)',
type: 'over-ear',
path: 'Rtings/Bruel & Kjaer 5128 over-ear/Sennheiser HD 600',
fileName: 'Sennheiser HD 600.csv',
},
];
// Static fallback list in case GitHub API fails — popular picks + additional well-known models
const FALLBACK_INDEX = [
...POPULAR_HEADPHONES,
{ name: 'Sennheiser HD 600 (Filk)', type: 'over-ear', path: 'Filk/over-ear/Sennheiser HD 600', fileName: 'Sennheiser HD 600.csv' },
{ name: 'Sennheiser HD 600 (Innerfidelity)', type: 'over-ear', path: 'Innerfidelity/over-ear/Sennheiser HD 600', fileName: 'Sennheiser HD 600.csv' },
{ name: 'Samsung Galaxy Buds2 Pro (Rtings)', type: 'in-ear', path: 'Rtings/Bruel & Kjaer 5128 in-ear/Samsung Galaxy Buds2 Pro', fileName: 'Samsung Galaxy Buds2 Pro.csv' },
{ name: 'Sony WF-1000XM5 (Kazi)', type: 'in-ear', path: 'Kazi/in-ear/Sony WF-1000XM5', fileName: 'Sony WF-1000XM5.csv' },
{ name: 'Samsung Galaxy Buds3 Pro (DHRME)', type: 'in-ear', path: 'DHRME/in-ear/Samsung Galaxy Buds3 Pro', fileName: 'Samsung Galaxy Buds3 Pro.csv' },
{ name: 'Apple AirPods Pro (Super Review)', type: 'in-ear', path: 'Super Review/in-ear/Apple AirPods Pro', fileName: 'Apple AirPods Pro.csv' },
{ name: 'Sennheiser HD 600 (2020) (Kuulokenurkka)', type: 'over-ear', path: 'Kuulokenurkka/over-ear/Sennheiser HD 600 (2020)', fileName: 'Sennheiser HD 600 (2020).csv' },
{
name: 'Sennheiser HD 600 (Filk)',
type: 'over-ear',
path: 'Filk/over-ear/Sennheiser HD 600',
fileName: 'Sennheiser HD 600.csv',
},
{
name: 'Sennheiser HD 600 (Innerfidelity)',
type: 'over-ear',
path: 'Innerfidelity/over-ear/Sennheiser HD 600',
fileName: 'Sennheiser HD 600.csv',
},
{
name: 'Samsung Galaxy Buds2 Pro (Rtings)',
type: 'in-ear',
path: 'Rtings/Bruel & Kjaer 5128 in-ear/Samsung Galaxy Buds2 Pro',
fileName: 'Samsung Galaxy Buds2 Pro.csv',
},
{
name: 'Sony WF-1000XM5 (Kazi)',
type: 'in-ear',
path: 'Kazi/in-ear/Sony WF-1000XM5',
fileName: 'Sony WF-1000XM5.csv',
},
{
name: 'Samsung Galaxy Buds3 Pro (DHRME)',
type: 'in-ear',
path: 'DHRME/in-ear/Samsung Galaxy Buds3 Pro',
fileName: 'Samsung Galaxy Buds3 Pro.csv',
},
{
name: 'Apple AirPods Pro (Super Review)',
type: 'in-ear',
path: 'Super Review/in-ear/Apple AirPods Pro',
fileName: 'Apple AirPods Pro.csv',
},
{
name: 'Sennheiser HD 600 (2020) (Kuulokenurkka)',
type: 'over-ear',
path: 'Kuulokenurkka/over-ear/Sennheiser HD 600 (2020)',
fileName: 'Sennheiser HD 600 (2020).csv',
},
];
/**
@ -39,7 +99,11 @@ const FALLBACK_INDEX = [
*/
async function fetchAutoEqIndex() {
// Migrate: remove old localStorage cache to free quota
try { localStorage.removeItem(OLD_LS_CACHE_KEY); } catch { /* ignore */ }
try {
localStorage.removeItem(OLD_LS_CACHE_KEY);
} catch {
/* ignore */
}
// 1. Try loading from IndexedDB cache
try {
@ -61,7 +125,9 @@ async function fetchAutoEqIndex() {
const timeoutId = setTimeout(() => controller.abort(), 8000);
let response;
try {
response = await fetch('https://api.github.com/repos/jaakkopasanen/AutoEq/git/trees/master?recursive=1', { signal: controller.signal });
response = await fetch('https://api.github.com/repos/jaakkopasanen/AutoEq/git/trees/master?recursive=1', {
signal: controller.signal,
});
} finally {
clearTimeout(timeoutId);
}
@ -73,7 +139,9 @@ async function fetchAutoEqIndex() {
console.warn('[AutoEQ] GitHub API limit reached. Using stale cache.');
return cached.data;
}
} catch { /* ignore */ }
} catch {
/* ignore */
}
console.warn('[AutoEQ] GitHub API error. Using fallback.');
return FALLBACK_INDEX;
}
@ -92,13 +160,15 @@ async function fetchAutoEqIndex() {
const fileNameLower = fileName.toLowerCase();
// Skip non-measurement files (EQ presets, not raw frequency response)
if (fileNameLower.includes('parametriceq') ||
if (
fileNameLower.includes('parametriceq') ||
fileNameLower.includes('fixedbandeq') ||
fileNameLower.includes('graphiceq') ||
fileNameLower.includes('convolution') ||
fileNameLower.includes('fixed band eq') ||
fileNameLower.includes('parametric eq') ||
fileNameLower.includes('graphic eq')) {
fileNameLower.includes('graphic eq')
) {
continue;
}
@ -144,7 +214,9 @@ async function fetchAutoEqIndex() {
try {
const cached = await db.getSetting(CACHE_KEY);
if (cached?.data) return cached.data;
} catch { /* ignore */ }
} catch {
/* ignore */
}
} else {
console.error('[AutoEQ] Failed to fetch index:', err);
}
@ -205,12 +277,12 @@ function searchHeadphones(query, entries, typeFilter = 'all', limit = 100) {
let filtered = entries;
if (typeFilter !== 'all') {
filtered = filtered.filter(e => e.type === typeFilter);
filtered = filtered.filter((e) => e.type === typeFilter);
}
if (query && query.trim()) {
const lower = query.toLowerCase().trim();
filtered = filtered.filter(e => e.name.toLowerCase().includes(lower));
filtered = filtered.filter((e) => e.name.toLowerCase().includes(lower));
}
return filtered.slice(0, limit);

View file

@ -7910,6 +7910,7 @@ body:has(#side-panel.active) #close-fullscreen-cover-btn {
background: rgb(255 255 255 / 0.5);
border: 1px dashed rgb(255 255 255 / 0.3);
}
.legend-corrected .legend-dot {
background: #f472b6;
}
@ -8109,6 +8110,7 @@ body:has(#side-panel.active) #close-fullscreen-cover-btn {
.autoeq-status.error {
color: var(--destructive);
}
.autoeq-status.success {
color: var(--primary);
}
@ -8941,12 +8943,15 @@ body:has(#side-panel.active) #close-fullscreen-cover-btn {
.speaker-eq-slider-value.bass {
color: #22d3ee;
}
.speaker-eq-slider-value.room {
color: #f59e0b;
}
.speaker-eq-section .autoeq-control-label.bass {
color: #22d3ee;
}
.speaker-eq-section .autoeq-control-label.room {
color: #f59e0b;
}