From f8f664964594a341884b2e9947f64feea4b925a6 Mon Sep 17 00:00:00 2001 From: skal Date: Wed, 18 Feb 2026 10:51:25 +0100 Subject: feat(mq_editor): spread autodetection, 50% drop-off line, mini-spectrum peak fix - autodetectSpread(): measures half-power (-3dB) peak width in spectrogram to infer spread_above/below (replaces near-zero bezier residual approach) - 'Auto' button per partial + 'Auto Spread All' toolbar button - Spread panel inputs now trigger viewer refresh on change - 50% drop-off dotted reference lines on spread band (selected partial only) - Mini-spectrum: use max() instead of mean() over bins per pixel column, fixing high-frequency amplitude mismatch between original and synthesis handoff(Gemini): spread autodetection and mini-spectrum fixes done --- tools/mq_editor/editor.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'tools/mq_editor/editor.js') diff --git a/tools/mq_editor/editor.js b/tools/mq_editor/editor.js index 441f787..9cfcdf1 100644 --- a/tools/mq_editor/editor.js +++ b/tools/mq_editor/editor.js @@ -126,6 +126,7 @@ class PartialEditor { { key: 'spread_above', label: 'spread ↑', step: '0.001' }, { key: 'spread_below', label: 'spread ↓', step: '0.001' }, ]; + const inputs = {}; for (const p of params) { const val = rep[p.key] != null ? rep[p.key] : defaults[p.key]; const lbl = document.createElement('span'); @@ -142,10 +143,34 @@ class PartialEditor { if (!this.partials[index].replicas) this.partials[index].replicas = { ...defaults }; this.partials[index].replicas[p.key] = v; + if (this.viewer) this.viewer.render(); }); + inputs[p.key] = inp; grid.appendChild(lbl); grid.appendChild(inp); } + + // Auto-detect spread button + const autoLbl = document.createElement('span'); + autoLbl.textContent = 'spread'; + const autoBtn = document.createElement('button'); + autoBtn.textContent = 'Auto'; + autoBtn.title = 'Infer spread_above/below from frequency variance around the bezier curve'; + autoBtn.addEventListener('click', () => { + if (!this.partials) return; + const p = this.partials[index]; + const sc = this.viewer ? this.viewer.stftCache : null; + const sr = this.viewer ? this.viewer.audioBuffer.sampleRate : 44100; + const fs = sc ? sc.fftSize : 2048; + const {spread_above, spread_below} = autodetectSpread(p, sc, fs, sr); + if (!p.replicas) p.replicas = { ...defaults }; + p.replicas.spread_above = spread_above; + p.replicas.spread_below = spread_below; + inputs['spread_above'].value = spread_above.toFixed(4); + inputs['spread_below'].value = spread_below.toFixed(4); + }); + grid.appendChild(autoLbl); + grid.appendChild(autoBtn); } _makeCurveUpdater(partialIndex, curveKey, field, pointIndex) { -- cgit v1.2.3