diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-18 10:17:07 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-18 10:17:07 +0100 |
| commit | 78faa77b208d20c01c242a942a1ddf9278f4ef17 (patch) | |
| tree | c2f74b31c24dbe7ee79e4a042c854f702a26e106 /tools/mq_editor/viewer.js | |
| parent | 6d2c3a9fa7ea3e7dc272d5622722f60d889612ce (diff) | |
feat(mq_editor): tabbed freq/amp/synth panel, spread band viz, UI polish
- Freq/Amp curve params now in tabs to save vertical space
- Add Synth tab: per-partial decay, jitter, spread_above/below controls
- Visualize spread band on selected partial (filled band + dashed bounds)
- Larger fonts and right panel (14px body, 260px panel width)
- Non-kept partials at 0.12 alpha (was 0.5)
- Default threshold -20dB (was -60dB)
handoff(Claude): mq_editor UI/UX improvements
Diffstat (limited to 'tools/mq_editor/viewer.js')
| -rw-r--r-- | tools/mq_editor/viewer.js | 55 |
1 files changed, 54 insertions, 1 deletions
diff --git a/tools/mq_editor/viewer.js b/tools/mq_editor/viewer.js index c158536..db23c72 100644 --- a/tools/mq_editor/viewer.js +++ b/tools/mq_editor/viewer.js @@ -227,7 +227,7 @@ class SpectrogramViewer { _renderPartial(p, partial, isSelected) { const {ctx} = this; const color = this.partialColor(p); - let alpha = isSelected ? 1.0 : (p < this.keepCount ? 1.0 : 0.5); + let alpha = isSelected ? 1.0 : (p < this.keepCount ? 1.0 : 0.12); if (partial.muted && !isSelected) alpha = 0.15; ctx.globalAlpha = alpha; @@ -247,6 +247,11 @@ class SpectrogramViewer { } if (started) ctx.stroke(); + // Spread band (selected only) + if (isSelected && partial.freqCurve) { + this._renderSpreadBand(partial, color); + } + // Bezier curve if (partial.freqCurve) { const curve = partial.freqCurve; @@ -276,6 +281,54 @@ class SpectrogramViewer { } } + _renderSpreadBand(partial, color) { + const {ctx} = this; + const curve = partial.freqCurve; + const rep = partial.replicas || {}; + const sa = rep.spread_above != null ? rep.spread_above : 0.02; + const sb = rep.spread_below != null ? rep.spread_below : 0.02; + + const STEPS = 60; + const upper = [], lower = []; + for (let i = 0; i <= STEPS; ++i) { + const t = curve.t0 + (curve.t3 - curve.t0) * i / STEPS; + if (t < this.t_view_min - 0.01 || t > this.t_view_max + 0.01) continue; + const f = evalBezier(curve, t); + upper.push([this.timeToX(t), this.freqToY(f * (1 + sa))]); + lower.push([this.timeToX(t), this.freqToY(f * (1 - sb))]); + } + if (upper.length < 2) return; + + const savedAlpha = ctx.globalAlpha; + + // Outer soft fill + ctx.beginPath(); + ctx.moveTo(upper[0][0], upper[0][1]); + for (let i = 1; i < upper.length; ++i) ctx.lineTo(upper[i][0], upper[i][1]); + for (let i = lower.length - 1; i >= 0; --i) ctx.lineTo(lower[i][0], lower[i][1]); + ctx.closePath(); + ctx.fillStyle = color; + ctx.globalAlpha = 0.13; + ctx.fill(); + + // Dashed boundary lines + ctx.globalAlpha = 0.4; + ctx.strokeStyle = color; + ctx.lineWidth = 1; + ctx.setLineDash([3, 4]); + ctx.beginPath(); + ctx.moveTo(upper[0][0], upper[0][1]); + for (let i = 1; i < upper.length; ++i) ctx.lineTo(upper[i][0], upper[i][1]); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(lower[0][0], lower[0][1]); + for (let i = 1; i < lower.length; ++i) ctx.lineTo(lower[i][0], lower[i][1]); + ctx.stroke(); + ctx.setLineDash([]); + + ctx.globalAlpha = savedAlpha; + } + renderPeaks() { const {ctx, frames} = this; if (!frames || frames.length === 0) return; |
