summaryrefslogtreecommitdiff
path: root/tools/mq_editor/viewer.js
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-17 21:28:58 +0100
committerskal <pascal.massimino@gmail.com>2026-02-17 21:28:58 +0100
commit99e94d18ce54d1ad73c7c0349119d4cd8fb4f965 (patch)
treee39536f70a9ff62cc42315ecb1fb5397ccf762b8 /tools/mq_editor/viewer.js
parentfbdfa2a7b54894a565fd57d5e27f19d752fe39fa (diff)
feat(mq_editor): peaks display, STFT cache refactor, threshold reactivity
- STFTCache: cache squaredAmplitude (re²+im²) per frame, add getSquaredAmplitude(t) - getMagnitudeDB uses cached sq amp (10*log10, no sqrt) - detectPeaks uses squaredAmp from cache instead of recomputing FFT - extractPartials: use cache frames, return {partials, frames} - Press 'p' to toggle raw peak overlay in viewer - threshold input: step=any, change event triggers re-extraction - runExtraction() shared by button and threshold change handoff(Claude): mq_partial peaks/cache refactor complete
Diffstat (limited to 'tools/mq_editor/viewer.js')
-rw-r--r--tools/mq_editor/viewer.js37
1 files changed, 37 insertions, 0 deletions
diff --git a/tools/mq_editor/viewer.js b/tools/mq_editor/viewer.js
index c57d693..5065498 100644
--- a/tools/mq_editor/viewer.js
+++ b/tools/mq_editor/viewer.js
@@ -8,6 +8,8 @@ class SpectrogramViewer {
this.audioBuffer = audioBuffer;
this.stftCache = stftCache;
this.partials = [];
+ this.frames = [];
+ this.showPeaks = false;
// Fixed time bounds
this.t_min = 0;
@@ -57,6 +59,15 @@ class SpectrogramViewer {
this.render();
}
+ setFrames(frames) {
+ this.frames = frames;
+ }
+
+ togglePeaks() {
+ this.showPeaks = !this.showPeaks;
+ this.render();
+ }
+
reset() {
this.zoom_factor = 1.0;
this.t_center = this.audioBuffer.duration / 2;
@@ -94,6 +105,7 @@ class SpectrogramViewer {
render() {
this.renderSpectrogram();
+ if (this.showPeaks) this.renderPeaks();
this.renderPartials();
this.drawAxes();
this.drawPlayhead();
@@ -271,6 +283,31 @@ class SpectrogramViewer {
}
}
+ // Render raw peaks from mq_extract (before partial tracking)
+ renderPeaks() {
+ const {ctx, canvas, frames} = this;
+ if (!frames || frames.length === 0) return;
+
+ const timeDuration = this.t_view_max - this.t_view_min;
+ const freqRange = this.freqEnd - this.freqStart;
+
+ ctx.fillStyle = '#fff';
+
+ for (const frame of frames) {
+ const t = frame.time;
+ if (t < this.t_view_min || t > this.t_view_max) continue;
+
+ const x = (t - this.t_view_min) / timeDuration * canvas.width;
+
+ for (const peak of frame.peaks) {
+ if (peak.freq < this.freqStart || peak.freq > this.freqEnd) continue;
+
+ const y = canvas.height - (peak.freq - this.freqStart) / freqRange * canvas.height;
+ ctx.fillRect(x - 1, y - 1, 3, 3);
+ }
+ }
+ }
+
// Draw control point
drawControlPoint(t, v) {
if (t < this.t_view_min || t > this.t_view_max) return;