diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-18 16:01:13 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-18 16:01:13 +0100 |
| commit | 00ce97d64b8bf7e1dcbdb5151bdf2033132ffbc3 (patch) | |
| tree | 0a5d4a7860ee12a4ba75bea9fa0eb0687e2a91b9 /tools/mq_editor/mq_extract.js | |
| parent | 7a054e8ee8566eea9d06ff1ff9c1ce48c39fe659 (diff) | |
refactor(mq_editor): consolidate duplicates, extract utils.js and app.js
- 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 <noreply@anthropic.com>
Diffstat (limited to 'tools/mq_editor/mq_extract.js')
| -rw-r--r-- | tools/mq_editor/mq_extract.js | 41 |
1 files changed, 17 insertions, 24 deletions
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]; |
