summaryrefslogtreecommitdiff
path: root/tools/mq_editor/index.html
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-18 06:59:32 +0100
committerskal <pascal.massimino@gmail.com>2026-02-18 06:59:32 +0100
commit105c817021a84bfacffa1553d6bcd536808b9f23 (patch)
tree36bc576d4605c5057e1604640f0fa1679866d6b6 /tools/mq_editor/index.html
parent65cd99553cd688c5ad2cfd64d79c6434fe694a33 (diff)
feat(mq_editor): UI improvements and partial detection enhancements
- Right panel with synthesis checkboxes (integrate phase, disable jitter, disable spread) - Style file chooser as button; show filename next to page title - Second backward pass in extractPartials to recover partial onsets - Cursor line drawn on overlay canvas (no full redraw on mousemove) handoff(Claude): UI + algo improvements complete
Diffstat (limited to 'tools/mq_editor/index.html')
-rw-r--r--tools/mq_editor/index.html100
1 files changed, 80 insertions, 20 deletions
diff --git a/tools/mq_editor/index.html b/tools/mq_editor/index.html
index b51a988..345d2b9 100644
--- a/tools/mq_editor/index.html
+++ b/tools/mq_editor/index.html
@@ -10,6 +10,18 @@
background: #1a1a1a;
color: #ddd;
}
+ .page-title {
+ display: flex;
+ align-items: baseline;
+ gap: 16px;
+ margin-bottom: 10px;
+ }
+ .page-title h2 { margin: 0; }
+ #fileLabel {
+ font-size: 13px;
+ color: #8af;
+ opacity: 0.8;
+ }
.toolbar {
margin-bottom: 10px;
padding: 10px;
@@ -27,7 +39,7 @@
}
button:hover { background: #4a4a4a; }
button:disabled { opacity: 0.5; cursor: not-allowed; }
- input[type="file"] { margin-right: 16px; }
+ input[type="file"] { display: none; }
.params {
display: inline-block;
margin-left: 20px;
@@ -43,12 +55,45 @@
padding: 4px;
border-radius: 3px;
}
+ .main-area {
+ display: flex;
+ align-items: flex-start;
+ gap: 10px;
+ margin-top: 10px;
+ }
#canvas {
border: 1px solid #555;
background: #000;
cursor: crosshair;
display: block;
- margin-top: 10px;
+ flex-shrink: 0;
+ }
+ .right-panel {
+ background: #2a2a2a;
+ border: 1px solid #555;
+ border-radius: 4px;
+ padding: 12px;
+ min-width: 160px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ }
+ .right-panel .panel-title {
+ font-size: 11px;
+ color: #888;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ border-bottom: 1px solid #444;
+ padding-bottom: 6px;
+ margin-bottom: 2px;
+ }
+ .right-panel label {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ margin: 0;
+ cursor: pointer;
+ font-size: 13px;
}
#status {
margin-top: 10px;
@@ -57,22 +102,20 @@
border-radius: 4px;
min-height: 20px;
}
- .info {
- color: #4af;
- }
- .warn {
- color: #fa4;
- }
- .error {
- color: #f44;
- }
+ .info { color: #4af; }
+ .warn { color: #fa4; }
+ .error { color: #f44; }
</style>
</head>
<body>
- <h2>MQ Spectral Editor</h2>
+ <div class="page-title">
+ <h2>MQ Spectral Editor</h2>
+ <span id="fileLabel"></span>
+ </div>
<div class="toolbar">
<input type="file" id="wavFile" accept=".wav">
+ <button id="chooseFileBtn">&#x1F4C2; Open WAV</button>
<button id="testWavBtn">⚗ Test WAV</button>
<button id="extractBtn" disabled>Extract Partials</button>
<button id="playBtn" disabled>▶ Play</button>
@@ -85,20 +128,28 @@
<label>Threshold (dB):</label>
<input type="number" id="threshold" value="-60" step="any">
- <label style="margin-left:16px;"><input type="checkbox" id="integratePhase" checked> Integrate phase</label>
-
<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>
- <div style="position: relative;">
- <canvas id="canvas" width="1400" height="600"></canvas>
+ <div class="main-area">
+ <div style="position: relative; flex-shrink: 0;">
+ <canvas id="canvas" width="1400" height="600"></canvas>
+ <canvas id="cursorCanvas" width="1400" height="600" style="position:absolute;top:0;left:0;pointer-events:none;"></canvas>
- <!-- Mini spectrum viewer (bottom-right overlay) -->
- <div id="spectrumViewer" style="position: absolute; bottom: 10px; right: 10px; width: 200px; height: 100px; background: rgba(30, 30, 30, 0.9); border: 1px solid #555; border-radius: 3px; pointer-events: none;">
- <canvas id="spectrumCanvas" width="200" height="100"></canvas>
+ <!-- Mini spectrum viewer (bottom-right overlay) -->
+ <div id="spectrumViewer" style="position: absolute; bottom: 10px; right: 10px; width: 200px; height: 100px; background: rgba(30, 30, 30, 0.9); border: 1px solid #555; border-radius: 3px; pointer-events: none;">
+ <canvas id="spectrumCanvas" width="200" height="100"></canvas>
+ </div>
+ </div>
+
+ <div class="right-panel">
+ <div class="panel-title">Synthesis</div>
+ <label><input type="checkbox" id="integratePhase" checked> Integrate phase</label>
+ <label><input type="checkbox" id="disableJitter"> Disable jitter</label>
+ <label><input type="checkbox" id="disableSpread"> Disable spread</label>
</div>
</div>
@@ -119,11 +170,13 @@
let stftCache = null;
const wavFile = document.getElementById('wavFile');
+ const chooseFileBtn = document.getElementById('chooseFileBtn');
const extractBtn = document.getElementById('extractBtn');
const playBtn = document.getElementById('playBtn');
const stopBtn = document.getElementById('stopBtn');
const canvas = document.getElementById('canvas');
const status = document.getElementById('status');
+ const fileLabel = document.getElementById('fileLabel');
const hopSize = document.getElementById('hopSize');
const threshold = document.getElementById('threshold');
@@ -162,11 +215,15 @@
}, 10);
}
+ // File chooser button
+ chooseFileBtn.addEventListener('click', () => wavFile.click());
+
// Load WAV file
wavFile.addEventListener('change', async (e) => {
const file = e.target.files[0];
if (!file) return;
+ fileLabel.textContent = file.name;
setStatus('Loading WAV...', 'info');
try {
const arrayBuffer = await file.arrayBuffer();
@@ -194,6 +251,7 @@
+ 0.5 * Math.sin(2 * Math.PI * 660 * i / SR);
}
+ fileLabel.textContent = 'test-440+660hz.wav';
loadAudioBuffer(buf, 'Test WAV: 440Hz + 660Hz (2s, 32kHz)');
});
@@ -339,7 +397,9 @@
const partialsToUse = extractedPartials.slice(0, keepCount);
setStatus(`Synthesizing ${keepCount}/${extractedPartials.length} partials (${keepPct.value}%)...`, 'info');
const integratePhase = document.getElementById('integratePhase').checked;
- const pcm = synthesizeMQ(partialsToUse, sampleRate, duration, integratePhase);
+ const disableJitter = document.getElementById('disableJitter').checked;
+ const disableSpread = document.getElementById('disableSpread').checked;
+ const pcm = synthesizeMQ(partialsToUse, sampleRate, duration, integratePhase, {disableJitter, disableSpread});
// Build STFT cache for synth signal (for FFT comparison via key 'a')
if (viewer) {