summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tools/mq_editor/README.md2
-rw-r--r--tools/mq_editor/app.js89
-rw-r--r--tools/mq_editor/index.html3
-rw-r--r--tools/mq_editor/style.css1
4 files changed, 6 insertions, 89 deletions
diff --git a/tools/mq_editor/README.md b/tools/mq_editor/README.md
index d79a2f8..9435883 100644
--- a/tools/mq_editor/README.md
+++ b/tools/mq_editor/README.md
@@ -8,7 +8,7 @@ McAulay-Quatieri sinusoidal analysis and synthesis tool.
open tools/mq_editor/index.html
```
-1. Click **Open WAV** (or **⚗ Test WAV** for a built-in 440+660 Hz test signal)
+1. Click **Open WAV**
2. Click **Extract Partials** (optional — Explore/Contour modes work immediately after load)
3. Press **1** to play synthesized, **2** to play original
diff --git a/tools/mq_editor/app.js b/tools/mq_editor/app.js
index 9857c64..bd9a2b3 100644
--- a/tools/mq_editor/app.js
+++ b/tools/mq_editor/app.js
@@ -223,7 +223,6 @@ function loadAudioBuffer(buffer, label) {
viewer.selectPartial(0);
setStatus(`${exploreMode}: added partial (${extractedPartials.length} total)`, 'info');
};
- if (label.startsWith('Test WAV')) validateTestWAVPeaks(stftCache);
}, 10);
}
@@ -248,25 +247,6 @@ wavFile.addEventListener('change', async (e) => {
}
});
-// Test WAV: generate synthetic signal (two sine waves) in-memory
-document.getElementById('testWavBtn').addEventListener('click', () => {
- initAudioContext();
- const SR = 32000;
- const duration = 2.0;
- const numSamples = SR * duration;
-
- // Two sine waves: 440 Hz (A4) + 660 Hz (E5, perfect fifth), equal amplitude
- const buf = audioContext.createBuffer(1, numSamples, SR);
- const data = buf.getChannelData(0);
- for (let i = 0; i < numSamples; ++i) {
- data[i] = 0.5 * Math.sin(2 * Math.PI * 440 * i / SR)
- + 0.5 * Math.sin(2 * Math.PI * 660 * i / SR);
- }
-
- fileLabel.textContent = 'test-440+660hz.wav';
- loadAudioBuffer(buf, 'Test WAV: 440Hz + 660Hz (2s, 32kHz)');
-});
-
// Update cache when hop size changes
hopSize.addEventListener('change', () => {
const val = Math.max(64, parseInt(hopSize.value) || 64);
@@ -571,6 +551,9 @@ document.addEventListener('keydown', (e) => {
} else if (e.code === 'KeyC' && !e.ctrlKey && !e.metaKey) {
e.preventDefault();
if (!document.getElementById('contourBtn').disabled) setExploreMode(exploreMode === 'contour' ? false : 'contour');
+ } else if (e.code === 'Delete' || e.code === 'Backspace') {
+ e.preventDefault();
+ document.getElementById('deletePartialBtn').click();
} else if (e.code === 'Escape') {
if (exploreMode) { setExploreMode(false); return; }
if (viewer) viewer.selectPartial(-1);
@@ -586,69 +569,3 @@ document.querySelectorAll('.tab-btn').forEach(btn => {
document.getElementById('tab' + btn.dataset.tab).style.display = '';
});
});
-
-// --- Test WAV peak validation ---
-function validateTestWAVPeaks(cache) {
- const SR = cache.sampleRate;
- const N = cache.fftSize;
- const binWidth = SR / N; // Hz per bin
- const numBins = N / 2;
- const numBars = 100; // mini-spectrum bar count
-
- // Use a mid-signal frame (avoid edge effects)
- const midFrame = cache.frames[Math.floor(cache.frames.length / 2)];
- if (!midFrame) { console.error('[TestWAV] No frames computed'); return; }
- const sq = midFrame.squaredAmplitude;
- const t = midFrame.time;
-
- console.group('[TestWAV] Peak validation @ t=' + t.toFixed(3) + 's');
-
- // Top 5 bins by magnitude
- const ranked = Array.from(sq)
- .map((v, i) => ({ bin: i, freq: i * binWidth, db: 10 * Math.log10(Math.max(v, 1e-20)) }))
- .sort((a, b) => b.db - a.db);
- console.log('Top 5 FFT bins:');
- ranked.slice(0, 5).forEach(x =>
- console.log(` bin ${x.bin.toString().padStart(3)}: ${x.freq.toFixed(1).padStart(7)}Hz ${x.db.toFixed(1)}dB`));
-
- // Expected bins for 440/660 Hz
- const bin440 = Math.round(440 / binWidth);
- const bin660 = Math.round(660 / binWidth);
- const db440 = 10 * Math.log10(Math.max(sq[bin440], 1e-20));
- const db660 = 10 * Math.log10(Math.max(sq[bin660], 1e-20));
- console.log(`440Hz → bin ${bin440} (${(bin440 * binWidth).toFixed(1)}Hz): ${db440.toFixed(1)}dB`);
- console.log(`660Hz → bin ${bin660} (${(bin660 * binWidth).toFixed(1)}Hz): ${db660.toFixed(1)}dB`);
-
- // Validate: 440/660 Hz must be in top-10
- const top10Freqs = ranked.slice(0, 10).map(x => x.freq);
- const pass440 = top10Freqs.some(f => Math.abs(f - 440) < binWidth * 2);
- const pass660 = top10Freqs.some(f => Math.abs(f - 660) < binWidth * 2);
- console.log('Peak check: 440Hz ' + (pass440 ? 'PASS ✓' : 'FAIL ✗') +
- ', 660Hz ' + (pass660 ? 'PASS ✓' : 'FAIL ✗'));
-
- // Mini-spectrum: which bar do these peaks land in?
- const bar440 = Math.floor(bin440 * numBars / numBins);
- const bar660 = Math.floor(bin660 * numBars / numBins);
- const sampledBin440 = Math.floor(bar440 * numBins / numBars);
- const sampledBin660 = Math.floor(bar660 * numBars / numBars);
- console.log('Mini-spectrum (linear scale, 100 bars):');
- console.log(` 440Hz (bin ${bin440}) → bar ${bar440}/100 [bar samples bin ${sampledBin440} = ${(sampledBin440 * binWidth).toFixed(1)}Hz]`);
- console.log(` 660Hz (bin ${bin660}) → bar ${bar660}/100 [bar samples bin ${Math.floor(bar660 * numBins / numBars)} = ${(Math.floor(bar660 * numBins / numBars) * binWidth).toFixed(1)}Hz]`);
- if (bar440 < 5 || bar660 < 5) {
- console.warn(' ⚠ BUG: peaks fall in bars ' + bar440 + ' and ' + bar660 +
- ' (leftmost ~' + Math.max(bar440, bar660) * 2 + 'px of 200px canvas)' +
- ' — linear scale hides low-frequency peaks. Need log-scale bar mapping.');
- }
-
- // Main spectrogram: confirm bins are in draw range
- const mainFreqStart = 20, mainFreqEnd = 16000;
- const inRange440 = 440 >= mainFreqStart && 440 <= mainFreqEnd;
- const inRange660 = 660 >= mainFreqStart && 660 <= mainFreqEnd;
- const norm440 = (Math.log2(440) - Math.log2(mainFreqStart)) / (Math.log2(mainFreqEnd) - Math.log2(mainFreqStart));
- const norm660 = (Math.log2(660) - Math.log2(mainFreqStart)) / (Math.log2(mainFreqEnd) - Math.log2(mainFreqStart));
- console.log('Main spectrogram (log Y-axis, 600px):');
- console.log(` 440Hz: in range=${inRange440}, y=${Math.round(600 * (1 - norm440))}px, db=${db440.toFixed(1)}dB → intensity=${Math.min(1, Math.pow(Math.max(0, (db440 + 80) / 80), 2)).toFixed(2)}`);
- console.log(` 660Hz: in range=${inRange660}, y=${Math.round(600 * (1 - norm660))}px, db=${db660.toFixed(1)}dB → intensity=${Math.min(1, Math.pow(Math.max(0, (db660 + 80) / 80), 2)).toFixed(2)}`);
-
- console.groupEnd();
-}
diff --git a/tools/mq_editor/index.html b/tools/mq_editor/index.html
index 1dd36a6..77e0f56 100644
--- a/tools/mq_editor/index.html
+++ b/tools/mq_editor/index.html
@@ -14,7 +14,6 @@
<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="autoSpreadAllBtn" disabled>Auto Spread All</button>
<button id="playBtn" disabled>▶ Play</button>
@@ -119,7 +118,7 @@
</div>
<div class="partial-actions">
<button id="mutePartialBtn">Mute</button>
- <button id="deletePartialBtn">Delete</button>
+ <button id="deletePartialBtn">Delete <kbd>Del</kbd></button>
</div>
</div>
diff --git a/tools/mq_editor/style.css b/tools/mq_editor/style.css
index 9563a57..6b266d9 100644
--- a/tools/mq_editor/style.css
+++ b/tools/mq_editor/style.css
@@ -60,6 +60,7 @@ button.contour-active { background: #145; border-color: #0cc; color: #aff; }
#propSwatch { display: inline-block; width: 10px; height: 10px; border-radius: 2px; flex-shrink: 0; }
.partial-actions { display: flex; gap: 4px; margin-top: 8px; }
.partial-actions button { flex: 1; padding: 4px 6px; font-size: 12px; margin: 0; }
+kbd { font-size: 10px; opacity: 0.55; }
/* === Curve tabs & grid === */
.curve-tabs { display: flex; gap: 2px; margin-top: 8px; margin-bottom: 4px; }