summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/editor/script.js106
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