summaryrefslogtreecommitdiff
path: root/tools/mq_editor/mq_extract.js
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-18 16:01:13 +0100
committerskal <pascal.massimino@gmail.com>2026-02-18 16:01:13 +0100
commit00ce97d64b8bf7e1dcbdb5151bdf2033132ffbc3 (patch)
tree0a5d4a7860ee12a4ba75bea9fa0eb0687e2a91b9 /tools/mq_editor/mq_extract.js
parent7a054e8ee8566eea9d06ff1ff9c1ce48c39fe659 (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.js41
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];