diff options
| author | skal <pascal.massimino@gmail.com> | 2026-01-28 12:44:35 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-01-28 12:44:35 +0100 |
| commit | 488bee7b1424efbf78f60984f59baba889dbb15b (patch) | |
| tree | ea9e4bd929c8b15985c104c3048eaa4e93d515c0 /tools/editor/script.js | |
| parent | ca33980fad60c7427fbdb3276ffdf42197380a57 (diff) | |
fix(editor): Resolve 'canvasToSpectrogramCoords is not defined' error
Moved all spectrogram coordinate and frequency mapping utility functions to the top of to ensure they are defined before any other functions attempt to use them. This resolves the 'canvasToSpectrogramCoords is not defined' error caused by incorrect function scoping.
Diffstat (limited to 'tools/editor/script.js')
| -rw-r--r-- | tools/editor/script.js | 168 |
1 files changed, 165 insertions, 3 deletions
diff --git a/tools/editor/script.js b/tools/editor/script.js index 3afa0ff..0c5200b 100644 --- a/tools/editor/script.js +++ b/tools/editor/script.js @@ -508,159 +508,321 @@ function updateUndoRedoButtons() { } // --- Utility to map canvas coords to spectrogram bins/frames (LOG SCALE) --- + // Maps a linear frequency bin index to its corresponding frequency in Hz + function binIndexToFreq(binIndex) { + return (binIndex / dctSize) * MAX_FREQ; + } + + // Maps a frequency in Hz to its corresponding linear bin index + function freqToBinIndex(freq) { + return Math.floor((freq / MAX_FREQ) * dctSize); + } + + // Maps a frequency (Hz) to its corresponding log-scaled bin index + function freqToBinIndexLog(freq) { + if (freq < MIN_FREQ) freq = MIN_FREQ; // Clamp minimum frequency + const logMin = Math.log(MIN_FREQ); + const logMax = Math.log(MAX_FREQ); + const logFreq = Math.log(freq); + const normalizedLog = (logFreq - logMin) / (logMax - logMin); + return Math.floor(normalizedLog * dctSize); + } + + // Maps a log-scaled bin index to its corresponding frequency in Hz + function binIndexToFreqLog(binIndex) { + const normalizedLog = binIndex / dctSize; + const logMin = Math.log(MIN_FREQ); + const logMax = Math.log(MAX_FREQ); + const logFreq = normalizedLog * (logMax - logMin) + logMin; + return Math.exp(logFreq); + } + + // Converts a frequency (Hz) to a Y-coordinate on the canvas (log scale) + function freqToCanvasYLog(freq, canvasHeight) { + if (freq < MIN_FREQ) freq = MIN_FREQ; // Clamp minimum frequency + const logMin = Math.log(MIN_FREQ); + const logMax = Math.log(MAX_FREQ); + const logFreq = Math.log(freq); + const normalizedLog = (logFreq - logMin) / (logMax - logMin); + return canvasHeight * (1 - normalizedLog); // Y-axis is inverted + } + + // Converts a Y-coordinate on the canvas to a frequency (Hz) (log scale) + function canvasYToFreqLog(canvasY, canvasHeight) { + const normalizedLog = 1 - (canvasY / canvasHeight); + const logMin = Math.log(MIN_FREQ); + const logMax = Math.log(MAX_FREQ); + const logFreq = normalizedLog * (logMax - logMin) + logMin; + return Math.exp(logFreq); + } + + // Converts canvas Y-coordinate to log-scaled bin index + function canvasYToBinIndexLog(canvasY, specData) { + const freq = canvasYToFreqLog(canvasY, canvas.height); + return freqToBinIndex(freq); // Use linear bin index from calculated log freq + } + + // Converts log-scaled bin index to canvas Y-coordinate + function binIndexToCanvasYLog(binIndex, specData) { + const freq = binIndexToFreq(binIndex); + return freqToCanvasYLog(freq, canvas.height); + } + + // Helper to get frequency delta from canvas delta (for ellipse radius in freq) + function canvasDeltaYToFreqDeltaLog(canvasDeltaY, canvasHeight) { + // This is an approximation as delta in log scale is not linear + // For small deltas around a center, it can be approximated + const centerCanvasY = canvasHeight / 2; + const freqAtCenter = canvasYToFreqLog(centerCanvasY, canvasHeight); + const freqAtCenterPlusDelta = canvasYToFreqLog(centerCanvasY - canvasDeltaY, canvasHeight); + return Math.abs(freqAtCenterPlusDelta - freqAtCenter); + } + + // 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 --- + 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)`); + } + + // --- Playback Button Event Listeners --- + listenOriginalButton.addEventListener('click', () => { + if (originalSpecData) { + playSpectrogramData(originalSpecData); - } else { + + } + + else { + alert("No original SPEC data loaded."); + } + }); + + listenGeneratedButton.addEventListener('click', () => { + if (currentSpecData) { + // Ensure currentSpecData reflects all shapes before playing + redrawCanvas(); // This updates currentSpecData based on shapes + playSpectrogramData(currentSpecData); - } else { + + } + + else { + alert("No generated SPEC data to play."); + } + }); + + // --- 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 + +} + + |
