summaryrefslogtreecommitdiff
path: root/tools/mq_editor/viewer.js
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-18 10:17:07 +0100
committerskal <pascal.massimino@gmail.com>2026-02-18 10:17:07 +0100
commit78faa77b208d20c01c242a942a1ddf9278f4ef17 (patch)
treec2f74b31c24dbe7ee79e4a042c854f702a26e106 /tools/mq_editor/viewer.js
parent6d2c3a9fa7ea3e7dc272d5622722f60d889612ce (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.js55
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;