summaryrefslogtreecommitdiff
path: root/tools/mq_editor/test_fft.html
diff options
context:
space:
mode:
Diffstat (limited to 'tools/mq_editor/test_fft.html')
-rw-r--r--tools/mq_editor/test_fft.html229
1 files changed, 0 insertions, 229 deletions
diff --git a/tools/mq_editor/test_fft.html b/tools/mq_editor/test_fft.html
deleted file mode 100644
index b4e7f48..0000000
--- a/tools/mq_editor/test_fft.html
+++ /dev/null
@@ -1,229 +0,0 @@
-<!DOCTYPE html>
-<!--
- test_fft.html — Isolated FFT correctness tests for fft.js
- Open directly in a browser (no server needed).
-
- Tests fftForward(), realFFT(), and STFTCache from fft.js.
-
- Test summary:
- 1 DC impulse — all-ones input → bin 0 must equal N
- 2 Single tone — 440 Hz pure sine → peak bin matches expected frequency
- 3 STFT magnitude — STFTCache.getMagnitudeDB() returns loud value at 440 Hz
- 4 Pair equal — 220 + 880 Hz, both peaks found
- 5 Pair equal — 440 + 1320 Hz, both peaks found
- 6 Pair wide — 300 + 3000 Hz (decade separation), both peaks found
- 7 Pair extreme — 100 + 8000 Hz (80× ratio), both peaks found
- 8 Pair unequal — 440 Hz + 2000 Hz at 10:1 amplitude, weak peak still found
- 9 Triplet chord — C4 (261.63) + E4 (329.63) + G4 (392) major chord
- 10 Triplet octaves — 110 + 220 + 440 Hz octave stack
- 11 Triplet harmonic — 500 + 1500 + 4500 Hz (1:3:9 ratio)
- 12 Triplet unequal — 440 + 880 + 1760 Hz at 1.0 / 0.5 / 0.25 (decaying harmonics)
-
- Pass criteria:
- - Each expected frequency bin is found within ±guard bins (guard ≈ 2 × bin_width).
- - Bin width = SR / N = 32000 / 4096 ≈ 7.8 Hz.
-
- All spectra are drawn as linear-magnitude plots (0..Nyquist on x-axis).
- Colored vertical markers show expected frequency positions.
--->
-<html>
-<head>
-<meta charset="utf-8">
-<title>FFT Test</title>
-<style>
- body { font-family: monospace; background: #111; color: #ccc; padding: 20px; }
- h2 { color: #fff; }
- canvas { border: 1px solid #444; display: block; margin: 10px 0; }
- .pass { color: #4f4; }
- .fail { color: #f44; }
- pre { background: #222; padding: 10px; }
-</style>
-</head>
-<body>
-<h2>FFT Isolation Test</h2>
-<pre id="log"></pre>
-<canvas id="spectrum" width="800" height="200"></canvas>
-<script src="fft.js"></script>
-<script>
-const log = document.getElementById('log');
-function print(msg, cls) {
- const span = document.createElement('span');
- if (cls) span.className = cls;
- span.textContent = msg + '\n';
- log.appendChild(span);
-}
-
-// --- Test 1: Single bin impulse (DC) ---
-{
- const N = 8;
- const real = new Float32Array([1,1,1,1,1,1,1,1]);
- const imag = new Float32Array(N);
- fftForward(real, imag, N);
- const ok = Math.abs(real[0] - 8) < 1e-4 && Math.abs(imag[0]) < 1e-4;
- print(`Test 1 DC impulse: real[0]=${real[0].toFixed(4)} ${ok?'PASS':'FAIL'}`, ok?'pass':'fail');
-}
-
-// --- Test 2: 440 Hz peak detection ---
-{
- const SR = 32000;
- const N = 2048;
- const signal = new Float32Array(N);
- for (let i = 0; i < N; ++i)
- signal[i] = Math.sin(2 * Math.PI * 440 * i / SR);
-
- const spectrum = realFFT(signal);
-
- let peakBin = 0, peakVal = 0;
- for (let i = 0; i < N / 2; ++i) {
- const re = spectrum[i * 2], im = spectrum[i * 2 + 1];
- const mag = re * re + im * im;
- if (mag > peakVal) { peakVal = mag; peakBin = i; }
- }
- const peakFreq = peakBin * SR / N;
- const ok = Math.abs(peakFreq - 440) < SR / N;
- print(`Test 2 440Hz peak: bin=${peakBin} freq=${peakFreq.toFixed(1)}Hz ${ok?'PASS':'FAIL'}`, ok?'pass':'fail');
-
- // Draw spectrum
- const canvas = document.getElementById('spectrum');
- const ctx = canvas.getContext('2d');
- ctx.fillStyle = '#111';
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- ctx.strokeStyle = '#0af';
- ctx.beginPath();
- const halfN = N / 2;
- for (let i = 0; i < halfN; ++i) {
- const re = spectrum[i * 2], im = spectrum[i * 2 + 1];
- const mag = Math.sqrt(re * re + im * im) / (N / 2);
- const x = i / halfN * canvas.width;
- const y = canvas.height - mag * canvas.height;
- i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
- }
- ctx.stroke();
-
- // Mark 440Hz
- ctx.strokeStyle = '#f80';
- ctx.beginPath();
- const x440 = peakBin / halfN * canvas.width;
- ctx.moveTo(x440, 0); ctx.lineTo(x440, canvas.height);
- ctx.stroke();
- ctx.fillStyle = '#f80';
- ctx.fillText(`440Hz (bin ${peakBin})`, x440 + 4, 16);
-}
-
-// --- Test 3: STFT getMagnitudeDB at t=0 ---
-{
- const SR = 32000;
- const N = 44032; // ~1.375s
- const signal = new Float32Array(N);
- for (let i = 0; i < N; ++i)
- signal[i] = Math.sin(2 * Math.PI * 440 * i / SR);
-
- const stft = new STFTCache(signal, SR, 2048, 512);
- const db = stft.getMagnitudeDB(0.0, 440);
- const ok = db > -10;
- print(`Test 3 STFT getMagnitudeDB(0, 440Hz): ${db.toFixed(1)} dB ${ok?'PASS':'FAIL'}`, ok?'pass':'fail');
-}
-
-// Helper: find top-K peaks in spectrum (bin indices), ignoring neighbors within 'guard' bins
-function findPeaks(spectrum, halfN, k, guard) {
- const mags = new Float32Array(halfN);
- for (let i = 0; i < halfN; ++i) {
- const re = spectrum[i * 2], im = spectrum[i * 2 + 1];
- mags[i] = re * re + im * im;
- }
- const peaks = [];
- const used = new Uint8Array(halfN);
- for (let p = 0; p < k; ++p) {
- let best = -1, bestVal = 0;
- for (let i = 1; i < halfN; ++i) {
- if (!used[i] && mags[i] > bestVal) { bestVal = mags[i]; best = i; }
- }
- if (best < 0) break;
- peaks.push(best);
- for (let g = Math.max(0, best - guard); g <= Math.min(halfN - 1, best + guard); ++g)
- used[g] = 1;
- }
- return peaks;
-}
-
-// Helper: draw labeled spectrum on a new canvas
-function drawSpectrum(label, spectrum, N, SR, markerFreqs) {
- const halfN = N / 2;
- const colors = ['#f80', '#0f8', '#f0f', '#08f'];
- const canvas = document.createElement('canvas');
- canvas.width = 800; canvas.height = 160;
- const div = document.createElement('div');
- div.style.cssText = 'color:#888;font-family:monospace;font-size:12px;margin-top:8px';
- div.textContent = label;
- document.body.appendChild(div);
- document.body.appendChild(canvas);
-
- const ctx = canvas.getContext('2d');
- ctx.fillStyle = '#111';
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- ctx.strokeStyle = '#0af';
- ctx.beginPath();
- for (let i = 0; i < halfN; ++i) {
- const re = spectrum[i * 2], im = spectrum[i * 2 + 1];
- const mag = Math.sqrt(re * re + im * im) / (N / 2);
- const x = i / halfN * canvas.width;
- const y = canvas.height - mag * canvas.height;
- i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
- }
- ctx.stroke();
-
- markerFreqs.forEach((f, idx) => {
- const bin = Math.round(f * N / SR);
- const x = bin / halfN * canvas.width;
- ctx.strokeStyle = colors[idx % colors.length];
- ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, canvas.height); ctx.stroke();
- ctx.fillStyle = colors[idx % colors.length];
- ctx.fillText(`${f}Hz`, x + 3, 14 + idx * 14);
- });
-}
-
-// Helper: test multi-frequency signal, return pass/fail
-function testMultiFreq(label, freqs, amplitudes, SR, N, testNum) {
- const signal = new Float32Array(N);
- for (let i = 0; i < N; ++i)
- for (let f = 0; f < freqs.length; ++f)
- signal[i] += (amplitudes ? amplitudes[f] : 1.0) * Math.sin(2 * Math.PI * freqs[f] * i / SR);
-
- const spectrum = realFFT(signal);
- const halfN = N / 2;
- const guard = Math.ceil(SR / N * 2); // ~2 bins tolerance
- const peaks = findPeaks(spectrum, halfN, freqs.length, guard);
-
- const detectedFreqs = peaks.map(b => (b * SR / N).toFixed(1));
- const allFound = freqs.every(f => {
- const expectedBin = Math.round(f * N / SR);
- return peaks.some(b => Math.abs(b - expectedBin) <= guard);
- });
-
- const freqStr = freqs.map((f, i) => amplitudes ? `${f}Hz@${amplitudes[i].toFixed(1)}` : `${f}Hz`).join(' + ');
- print(`Test ${testNum} ${label} [${freqStr}]: peaks=[${detectedFreqs.join(', ')}] ${allFound?'PASS':'FAIL'}`,
- allFound ? 'pass' : 'fail');
-
- drawSpectrum(`Test ${testNum}: ${label} — ${freqStr}`, spectrum, N, SR, freqs);
- return allFound;
-}
-
-const SR = 32000, N = 4096;
-
-// --- Pairs ---
-print('\n-- Pairs --');
-testMultiFreq('pair', [220, 880], null, SR, N, 4);
-testMultiFreq('pair', [440, 1320], null, SR, N, 5);
-testMultiFreq('pair', [300, 3000], null, SR, N, 6);
-testMultiFreq('pair', [100, 8000], null, SR, N, 7);
-testMultiFreq('pair unequal amp', [440, 2000], [1.0, 0.1], SR, N, 8);
-
-// --- Triplets ---
-print('\n-- Triplets --');
-testMultiFreq('triplet', [261.63, 329.63, 392.00], null, SR, N, 9); // C E G chord
-testMultiFreq('triplet', [110, 220, 440], null, SR, N, 10); // octave stack
-testMultiFreq('triplet', [500, 1500, 4500], null, SR, N, 11); // harmonic series
-testMultiFreq('triplet unequal', [440, 880, 1760], [1.0, 0.5, 0.25],SR, N, 12); // decaying harmonics
-</script>
-</body>
-</html>