From 14c3bfe09f906e9b80d6100b126e59c9a88ac976 Mon Sep 17 00:00:00 2001 From: skal Date: Wed, 18 Feb 2026 05:38:45 +0100 Subject: feat(mq_editor): add Test WAV button for in-browser UI testing Generates 2s of 440+660Hz (A4+E5) at 32kHz without needing a file. Refactors WAV load path into shared loadAudioBuffer() used by both file input and the test button. handoff(Gemini): test button wired, full UI pipeline testable without assets. --- tools/mq_editor/index.html | 56 +++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 18 deletions(-) (limited to 'tools') diff --git a/tools/mq_editor/index.html b/tools/mq_editor/index.html index d7cec6a..1ebc0d3 100644 --- a/tools/mq_editor/index.html +++ b/tools/mq_editor/index.html @@ -73,6 +73,7 @@
+ @@ -142,6 +143,22 @@ } } + // Shared: initialize editor from an AudioBuffer + function loadAudioBuffer(buffer, label) { + audioBuffer = buffer; + initAudioContext(); + extractBtn.disabled = false; + playBtn.disabled = false; + setStatus('Computing STFT cache...', 'info'); + + setTimeout(() => { + const signal = audioBuffer.getChannelData(0); + stftCache = new STFTCache(signal, audioBuffer.sampleRate, fftSize, parseInt(hopSize.value)); + setStatus(`${label} — ${audioBuffer.duration.toFixed(2)}s, ${audioBuffer.sampleRate}Hz, ${audioBuffer.numberOfChannels}ch (${stftCache.getNumFrames()} frames cached)`, 'info'); + viewer = new SpectrogramViewer(canvas, audioBuffer, stftCache); + }, 10); + } + // Load WAV file wavFile.addEventListener('change', async (e) => { const file = e.target.files[0]; @@ -150,30 +167,33 @@ setStatus('Loading WAV...', 'info'); try { const arrayBuffer = await file.arrayBuffer(); - const audioContext = new AudioContext(); - audioBuffer = await audioContext.decodeAudioData(arrayBuffer); - - initAudioContext(); - extractBtn.disabled = false; - playBtn.disabled = false; - setStatus('Computing STFT cache...', 'info'); - - // Compute STFT cache - setTimeout(() => { - const signal = audioBuffer.getChannelData(0); - stftCache = new STFTCache(signal, audioBuffer.sampleRate, fftSize, parseInt(hopSize.value)); - - setStatus(`Loaded: ${audioBuffer.duration.toFixed(2)}s, ${audioBuffer.sampleRate}Hz, ${audioBuffer.numberOfChannels}ch (${stftCache.getNumFrames()} frames cached)`, 'info'); - - // Create viewer with cache - viewer = new SpectrogramViewer(canvas, audioBuffer, stftCache); - }, 10); + const ctx = new AudioContext(); + const buf = await ctx.decodeAudioData(arrayBuffer); + loadAudioBuffer(buf, `Loaded: ${file.name}`); } catch (err) { setStatus('Error loading WAV: ' + err.message, 'error'); console.error(err); } }); + // 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); + } + + loadAudioBuffer(buf, 'Test WAV: 440Hz + 660Hz (2s, 32kHz)'); + }); + // Update cache when hop size changes hopSize.addEventListener('change', () => { if (stftCache) { -- cgit v1.2.3