From 00ce97d64b8bf7e1dcbdb5151bdf2033132ffbc3 Mon Sep 17 00:00:00 2001 From: skal Date: Wed, 18 Feb 2026 16:01:13 +0100 Subject: refactor(mq_editor): consolidate duplicates, extract utils.js and app.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - utils.js (new): evalBezier (robust), getCanvasCoords, buildBandPoints - app.js (new): extract ~450-line inline script from index.html - editor.js: generalize _makeJogSlider(inp, options) with onUpdate cb, eliminate 50-line inline resonator jog duplication, use getCanvasCoords - mq_extract.js: extract findBestPeak(), replace two identical loop bodies - viewer.js: remove duplicate evalBezier, use getCanvasCoords/buildBandPoints - mq_synth.js: remove duplicate evalBezier - index.html: inline script removed, load order: utils→fft→extract→synth→viewer→editor→app handoff(Claude): mq_editor refactor complete — no logic changes, browser-ready. Co-Authored-By: Claude Sonnet 4.6 --- tools/mq_editor/mq_extract.js | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) (limited to 'tools/mq_editor/mq_extract.js') diff --git a/tools/mq_editor/mq_extract.js b/tools/mq_editor/mq_extract.js index 3f7490d..107b2ac 100644 --- a/tools/mq_editor/mq_extract.js +++ b/tools/mq_editor/mq_extract.js @@ -102,6 +102,21 @@ function normalizeAngle(angle) { return angle - 2 * Math.PI * Math.floor((angle + Math.PI) / (2 * Math.PI)); } +// Find best matching peak for a predicted freq/phase. Returns {bestIdx, bestCost}. +function findBestPeak(peaks, matched, predictedFreq, predictedPhase, tol, phaseErrorWeight) { + let bestIdx = -1, bestCost = Infinity; + for (let i = 0; i < peaks.length; ++i) { + if (matched.has(i)) continue; + const pk = peaks[i]; + const freqError = Math.abs(pk.freq - predictedFreq); + if (freqError > tol) continue; + const phaseError = Math.abs(normalizeAngle(pk.phase - predictedPhase)); + const cost = freqError + phaseErrorWeight * phaseError * predictedFreq; + if (cost < bestCost) { bestCost = cost; bestIdx = i; } + } + return { bestIdx, bestCost }; +} + // Track partials across frames using phase coherence for robust matching. function trackPartials(frames, params) { const { @@ -134,19 +149,8 @@ function trackPartials(frames, params) { const predictedPhase = lastPhase + phaseAdvance; const tol = Math.max(predictedFreq * trackingRatio, minTrackingHz); - let bestIdx = -1, bestCost = Infinity; - // Find the peak in the new frame with the lowest cost (freq + phase error). - for (let i = 0; i < frame.peaks.length; ++i) { - if (matched.has(i)) continue; - const pk = frame.peaks[i]; - const freqError = Math.abs(pk.freq - predictedFreq); - if (freqError > tol) continue; - - const phaseError = Math.abs(normalizeAngle(pk.phase - predictedPhase)); - const cost = freqError + phaseErrorWeight * phaseError * predictedFreq; - if (cost < bestCost) { bestCost = cost; bestIdx = i; } - } + const { bestIdx } = findBestPeak(frame.peaks, matched, predictedFreq, predictedPhase, tol, phaseErrorWeight); if (bestIdx >= 0) { const pk = frame.peaks[bestIdx]; @@ -175,18 +179,7 @@ function trackPartials(frames, params) { const predictedPhase = lastPhase + phaseAdvance; const tol = Math.max(predictedFreq * trackingRatio, minTrackingHz); - let bestIdx = -1, bestCost = Infinity; - - for (let j = 0; j < frame.peaks.length; ++j) { - if (matched.has(j)) continue; - const pk = frame.peaks[j]; - const freqError = Math.abs(pk.freq - predictedFreq); - if (freqError > tol) continue; - - const phaseError = Math.abs(normalizeAngle(pk.phase - predictedPhase)); - const cost = freqError + phaseErrorWeight * phaseError * predictedFreq; - if (cost < bestCost) { bestCost = cost; bestIdx = j; } - } + const { bestIdx } = findBestPeak(frame.peaks, matched, predictedFreq, predictedPhase, tol, phaseErrorWeight); if (bestIdx >= 0) { const pk = frame.peaks[bestIdx]; -- cgit v1.2.3