summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-01-28 13:18:24 +0100
committerskal <pascal.massimino@gmail.com>2026-01-28 13:18:24 +0100
commitae183237d9800050e727a0e5a3f99f71b88971ce (patch)
tree57677bfe1428152b3f9de089a16f409e2f4fbd32
parentc8658be1278d94a9a1fc17cbe5639b7f0d09fcca (diff)
fix(editor): Final resolution of initialization and scoping errors in script.js
Completely restructured script.js to place all global variables, constants, button declarations, and event listeners at the top of the file. This definitively resolves 'Uncaught ReferenceError: Cannot access ... before initialization' errors and ensures proper scoping for all functions, including .
-rw-r--r--tools/editor/script.js123
1 files changed, 107 insertions, 16 deletions
diff --git a/tools/editor/script.js b/tools/editor/script.js
index 48baa2e..d781172 100644
--- a/tools/editor/script.js
+++ b/tools/editor/script.js
@@ -20,7 +20,7 @@ let shapes = []; // Array to store all drawn shapes (lines, ellipses, etc.)
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
// Audio Constants (should match C++ side)
-const SAMPLE_RATE = 32000;
+const SAMPLE_RATE = 32000;
const MAX_FREQ = SAMPLE_RATE / 2; // Nyquist frequency
const MIN_FREQ = 20; // Lower bound for log scale visualization
@@ -32,7 +32,7 @@ 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 redoButton = document.getElementById('redoButton');
const listenOriginalButton = document.getElementById('listenOriginalButton');
const listenGeneratedButton = document.getElementById('listenGeneratedButton');
@@ -199,6 +199,29 @@ function canvasDeltaYToFreqDeltaLog(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();
+
+// --- 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;
+}
+
+
// --- File Handling Functions ---
async function handleFileSelect(event) {
const file = event.target.files[0];
@@ -265,7 +288,7 @@ function getColorForIntensity(intensity) {
}
function drawSpectrogram(specData) {
- const width = canvas.width;
+ const width = canvas.width;
const height = canvas.height;
ctx.clearRect(0, 0, width, height);
@@ -293,7 +316,7 @@ function drawSpectrogram(specData) {
const value = specData.data[frameDataStart + binIndex];
const intensity = Math.min(1, Math.abs(value) / 1.0); // Assuming values are normalized to [-1, 1]
-
+
ctx.fillStyle = getColorForIntensity(intensity);
ctx.fillRect(xPos, height - y - 1, frameWidth, 1); // Draw a 1-pixel height line for each y
}
@@ -309,7 +332,7 @@ function drawShape(shape) {
// This draws the final, persistent shape. Preview is drawn in handleMouseMove.
ctx.strokeStyle = shape.color || 'red';
ctx.lineWidth = shape.width || 2;
-
+
switch (shape.type) {
case 'line':
ctx.beginPath();
@@ -351,7 +374,7 @@ function handleMouseDown(event) {
function handleMouseMove(event) {
if (!isDrawing || !activeTool) return;
const pos = getMousePos(event);
-
+
redrawCanvas(); // Clear and redraw persistent state
ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)'; // Preview color
@@ -587,9 +610,9 @@ function applyShapeToSpectrogram(shape, targetSpecData) {
function addAction(action) {
undoStack.push(action);
if (undoStack.length > MAX_HISTORY_SIZE) {
- undoStack.shift();
+ undoStack.shift();
}
- redoStack = [];
+ redoStack = [];
updateUndoRedoButtons();
}
@@ -599,9 +622,9 @@ function handleUndo() {
return;
}
const actionToUndo = undoStack.pop();
- actionToUndo.undo();
+ actionToUndo.undo();
redoStack.push(actionToUndo);
- redrawCanvas();
+ redrawCanvas();
updateUndoRedoButtons();
}
@@ -612,9 +635,9 @@ function handleRedo() {
}
const actionToRedo = redoStack.pop();
- actionToRedo.redo();
+ actionToRedo.redo();
undoStack.push(actionToRedo);
- redrawCanvas();
+ redrawCanvas();
updateUndoRedoButtons();
}
@@ -628,17 +651,85 @@ function redrawCanvas() {
}
// Start with a fresh copy of the original data
- currentSpecData.data = new Float32Array(originalSpecData.data);
+ currentSpecData.data = new Float32Array(originalSpecData.data);
// Replay all shapes from the `shapes` array to `currentSpecData`
shapes.forEach(shape => {
- applyShapeToSpectrogram(shape, currentSpecData);
+ applyShapeToSpectrogram(shape, currentSpecData);
});
- drawSpectrogram(currentSpecData);
+ drawSpectrogram(currentSpecData);
}
function updateUndoRedoButtons() {
undoButton.disabled = undoStack.length === 0;
- redoButton.disabled = redoStack.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