summaryrefslogtreecommitdiff
path: root/tools/mq_editor
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-17 22:30:22 +0100
committerskal <pascal.massimino@gmail.com>2026-02-17 22:30:22 +0100
commite1f12a5a7eafbb5f4cc7b0da5f563850347fc84c (patch)
tree941f323f4f49683fd0fbd24fac59f197e213acf2 /tools/mq_editor
parent99e94d18ce54d1ad73c7c0349119d4cd8fb4f965 (diff)
feat(mq_editor): sort partials by amplitude, keep% slider for reconstruction
- Sort extracted partials by decreasing peak amplitude - Add Keep% range slider (1-100%) to toolbar - Viewer draws omitted partials at 50% opacity (live on slider input) - synthesizeMQ uses only the top-N% partials handoff(Claude): partial amplitude filtering complete
Diffstat (limited to 'tools/mq_editor')
-rw-r--r--tools/mq_editor/index.html30
-rw-r--r--tools/mq_editor/viewer.js11
2 files changed, 38 insertions, 3 deletions
diff --git a/tools/mq_editor/index.html b/tools/mq_editor/index.html
index 27eba2a..e8386c2 100644
--- a/tools/mq_editor/index.html
+++ b/tools/mq_editor/index.html
@@ -83,6 +83,10 @@
<label>Threshold (dB):</label>
<input type="number" id="threshold" value="-60" step="any">
+
+ <label style="margin-left:16px;">Keep:</label>
+ <input type="range" id="keepPct" min="1" max="100" value="100" style="width:100px; vertical-align:middle;">
+ <span id="keepPctLabel" style="margin-left:4px;">100%</span>
</div>
</div>
@@ -120,8 +124,17 @@
const hopSize = document.getElementById('hopSize');
const threshold = document.getElementById('threshold');
+ const keepPct = document.getElementById('keepPct');
+ const keepPctLabel = document.getElementById('keepPctLabel');
const fftSize = 1024; // Fixed
+ keepPct.addEventListener('input', () => {
+ keepPctLabel.textContent = keepPct.value + '%';
+ if (viewer && extractedPartials) {
+ viewer.setKeepCount(Math.max(1, Math.ceil(extractedPartials.length * parseInt(keepPct.value) / 100)));
+ }
+ });
+
// Initialize audio context
function initAudioContext() {
if (!audioContext) {
@@ -190,10 +203,18 @@
const result = extractPartials(params, stftCache);
+ // Sort by decreasing peak amplitude
+ result.partials.sort((a, b) => {
+ const peakA = a.amps.reduce((m, v) => Math.max(m, v), 0);
+ const peakB = b.amps.reduce((m, v) => Math.max(m, v), 0);
+ return peakB - peakA;
+ });
+
extractedPartials = result.partials;
viewer.setFrames(result.frames);
setStatus(`Extracted ${result.partials.length} partials`, 'info');
viewer.setPartials(result.partials);
+ viewer.setKeepCount(Math.max(1, Math.ceil(result.partials.length * parseInt(keepPct.value) / 100)));
} catch (err) {
setStatus('Extraction error: ' + err.message, 'error');
@@ -286,10 +307,13 @@
setStatus('Synthesizing...', 'info');
- // Synthesize PCM from partials
+ // Synthesize PCM from top-N% partials by amplitude
const sampleRate = audioBuffer.sampleRate;
const duration = audioBuffer.duration;
- const pcm = synthesizeMQ(extractedPartials, sampleRate, duration);
+ const keepCount = Math.max(1, Math.ceil(extractedPartials.length * parseInt(keepPct.value) / 100));
+ const partialsToUse = extractedPartials.slice(0, keepCount);
+ setStatus(`Synthesizing ${keepCount}/${extractedPartials.length} partials (${keepPct.value}%)...`, 'info');
+ const pcm = synthesizeMQ(partialsToUse, sampleRate, duration);
// Create audio buffer
const synthBuffer = audioContext.createBuffer(1, pcm.length, sampleRate);
@@ -311,7 +335,7 @@
playBtn.disabled = true;
stopBtn.disabled = false;
- setStatus('Playing synthesized...', 'info');
+ setStatus(`Playing synthesized (${keepCount}/${extractedPartials.length} partials, ${keepPct.value}%)...`, 'info');
// Animate playhead
function updatePlayhead() {
diff --git a/tools/mq_editor/viewer.js b/tools/mq_editor/viewer.js
index 5065498..e8780e7 100644
--- a/tools/mq_editor/viewer.js
+++ b/tools/mq_editor/viewer.js
@@ -30,6 +30,9 @@ class SpectrogramViewer {
// Tooltip
this.tooltip = document.getElementById('tooltip');
+ // Partial keep count (Infinity = all kept)
+ this.keepCount = Infinity;
+
// Playhead
this.playheadTime = -1; // -1 = not playing
@@ -59,6 +62,11 @@ class SpectrogramViewer {
this.render();
}
+ setKeepCount(n) {
+ this.keepCount = n;
+ this.render();
+ }
+
setFrames(frames) {
this.frames = frames;
}
@@ -216,6 +224,7 @@ class SpectrogramViewer {
for (let p = 0; p < partials.length; ++p) {
const partial = partials[p];
const color = colors[p % colors.length];
+ ctx.globalAlpha = p < this.keepCount ? 1.0 : 0.5;
// Draw raw trajectory
ctx.strokeStyle = color + '44';
@@ -281,6 +290,8 @@ class SpectrogramViewer {
this.drawControlPoint(curve.t3, curve.v3);
}
}
+
+ ctx.globalAlpha = 1.0;
}
// Render raw peaks from mq_extract (before partial tracking)