diff options
Diffstat (limited to 'tools')
| -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 + +} + + |
