diff options
| -rw-r--r-- | tools/editor/script.js | 106 |
1 files changed, 104 insertions, 2 deletions
diff --git a/tools/editor/script.js b/tools/editor/script.js index dce00bd..aac52ff 100644 --- a/tools/editor/script.js +++ b/tools/editor/script.js @@ -26,6 +26,40 @@ const MIN_FREQ = 20; // Lower bound for log scale visualization const SDF_FALLOFF_FACTOR = 10.0; // Adjust this value to control the softness of SDF edges. +// --- Button Element Declarations --- +const specFileInput = document.getElementById('specFileInput'); +const lineToolButton = document.getElementById('lineTool'); +const ellipseToolButton = document.getElementById('ellipseTool'); +const noiseToolButton = document.getElementById('noiseTool'); +const undoButton = document.getElementById('undoButton'); +const redoButton = document.getElementById('redoButton'); +const listenOriginalButton = document.getElementById('listenOriginalButton'); +const listenGeneratedButton = document.getElementById('listenGeneratedButton'); + +// --- Event Listeners --- +specFileInput.addEventListener('change', handleFileSelect); +lineToolButton.addEventListener('click', () => { activeTool = 'line'; console.log('Line tool selected'); }); +ellipseToolButton.addEventListener('click', () => { activeTool = 'ellipse'; console.log('Ellipse tool selected'); }); +noiseToolButton.addEventListener('click', () => { activeTool = 'noise'; console.log('Noise tool selected'); }); +undoButton.addEventListener('click', handleUndo); +redoButton.addEventListener('click', handleRedo); +listenOriginalButton.addEventListener('click', () => { + if (originalSpecData) { + playSpectrogramData(originalSpecData); + } else { + alert("No original SPEC data loaded."); + } +}); +listenGeneratedButton.addEventListener('click', () => { + if (currentSpecData) { + redrawCanvas(); // Ensure currentSpecData reflects all shapes before playing + playSpectrogramData(currentSpecData); + } else { + alert("No generated SPEC data to play."); + } +}); + + // --- Utility Functions for Audio Processing --- // JavaScript equivalent of C++ idct_512 function javascript_idct_512(input) { @@ -410,8 +444,8 @@ function handleMouseUp(event) { case 'ellipse': { const rx = Math.abs(endPos.x - startX) / 2; const ry = Math.abs(endPos.y - startY) / 2; - const cx = startX + (endPos.x - startX) / 2; - const cy = startY + (endPos.y - startY) / 2; + const cx = startX + (pos.x - startX) / 2; + const cy = startY + (pos.y - startY) / 2; const centerCoords = canvasToSpectrogramCoords(cx, cy, currentSpecData); const halfWidthFrames = Math.floor((rx / canvas.width) * currentSpecData.header.num_frames); @@ -631,3 +665,71 @@ function updateUndoRedoButtons() { undoButton.disabled = undoStack.length === 0; redoButton.disabled = redoStack.length === 0; } + +// Initial setup for canvas size (can be updated on window resize) +window.addEventListener('resize', () => { + if (originalSpecData) { + canvas.width = window.innerWidth * 0.7; + canvas.height = 400; // Fixed height + redrawCanvas(); + } +}); + +// Initial call to set button states +updateUndoRedoButtons(); + +// --- Audio Playback Functions --- +let currentAudioSource = null; // To stop currently playing audio + +async function playSpectrogramData(specData) { + if (!specData || !specData.data || specData.header.num_frames === 0) { + alert("No spectrogram data to play."); + return; + } + + if (currentAudioSource) { + currentAudioSource.stop(); + currentAudioSource.disconnect(); + currentAudioSource = null; + } + + const sampleRate = SAMPLE_RATE; // Fixed sample rate + const numFrames = specData.header.num_frames; + const totalAudioSamples = numFrames * dctSize; // Total samples in time domain + + const audioBuffer = audioContext.createBuffer(1, totalAudioSamples, sampleRate); + const audioData = audioBuffer.getChannelData(0); // Mono channel + + const windowArray = hanningWindow(dctSize); // Generate Hanning window for each frame + + // Convert spectrogram frames (frequency domain) to audio samples (time domain) + for (let frameIndex = 0; frameIndex < numFrames; frameIndex++) { + const spectralFrame = specData.data.slice(frameIndex * dctSize, (frameIndex + 1) * dctSize); + const timeDomainFrame = javascript_idct_512(spectralFrame); + + // Apply Hanning window for smooth transitions + for (let i = 0; i < dctSize; i++) { + const globalIndex = frameIndex * dctSize + i; + if (globalIndex < totalAudioSamples) { + audioData[globalIndex] += timeDomainFrame[i] * windowArray[i]; + } + } + } + + currentAudioSource = audioContext.createBufferSource(); + currentAudioSource.buffer = audioBuffer; + currentAudioSource.connect(audioContext.destination); + currentAudioSource.start(); + + console.log(`Playing audio (Sample Rate: ${sampleRate}, Duration: ${audioBuffer.duration.toFixed(2)}s)`); +} + +// --- Utility for sizeof(float) in JS context --- +// This is a workaround since typeof(float) is not directly available. +// Float32Array.BYTES_PER_ELEMENT is used in handleFileSelect. +function sizeof(type) { + if (type === 'float') { + return Float32Array.BYTES_PER_ELEMENT; + } + return 0; +}
\ No newline at end of file |
