summaryrefslogtreecommitdiff
path: root/tools/spectral_editor
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-07 15:07:01 +0100
committerskal <pascal.massimino@gmail.com>2026-02-07 15:07:01 +0100
commita0dd0a27c4d6831fb2fb5ad81283f36512ef16ef (patch)
treebc961189b10cf9f983d39854b9a5770d87574427 /tools/spectral_editor
parenta6a7bf0440dbabdc6c994c0fb21a8ac31c27be07 (diff)
update doc, optimize spectral_editor
Diffstat (limited to 'tools/spectral_editor')
-rw-r--r--tools/spectral_editor/script.js140
1 files changed, 49 insertions, 91 deletions
diff --git a/tools/spectral_editor/script.js b/tools/spectral_editor/script.js
index 392d3a5..cafd7e9 100644
--- a/tools/spectral_editor/script.js
+++ b/tools/spectral_editor/script.js
@@ -1083,18 +1083,7 @@ function drawReferenceSpectrogram(ctx) {
// Sample spectrogram
const specValue = state.referenceSpectrogram[frameIdx * state.referenceDctSize + bin];
-
- // Logarithmic intensity mapping (dB scale)
- // Maps wide dynamic range to visible range
- const amplitude = Math.abs(specValue);
- let intensity = 0;
- if (amplitude > 0.0001) { // Noise floor
- const dB = 20.0 * Math.log10(amplitude);
- const dB_min = -60.0; // Noise floor (-60 dB)
- const dB_max = 40.0; // Peak (40 dB headroom)
- const normalized = (dB - dB_min) / (dB_max - dB_min);
- intensity = Math.floor(Math.max(0, Math.min(255, normalized * 255)));
- }
+ const intensity = amp_to_dB(specValue, 255);
// Write pixel
const pixelIdx = (screenY * state.canvasWidth + screenX) * 4;
@@ -1153,24 +1142,13 @@ function drawProceduralSpectrogram(ctx) {
const specValue = curveSpec[frameIdx * state.referenceDctSize + bin];
// Logarithmic intensity mapping with steeper falloff for procedural curves
- const amplitude = Math.abs(specValue);
- let intensity = 0.0;
- if (amplitude > 0.001) { // Higher noise floor for cleaner visualization
- const dB = 20.0 * Math.log10(amplitude);
- const dB_min = -40.0; // Higher floor = steeper falloff (was -60)
- const dB_max = 40.0; // Peak
- const normalized = (dB - dB_min) / (dB_max - dB_min);
- intensity = Math.max(0, Math.min(1.0, normalized)); // 0.0 to 1.0
- }
-
- if (intensity > 0.01) { // Only draw visible pixels
- const pixelIdx = (screenY * state.canvasWidth + screenX) * 4;
- // Use constant color with alpha for intensity (pure colors)
- imgData.data[pixelIdx + 0] = color.r;
- imgData.data[pixelIdx + 1] = color.g;
- imgData.data[pixelIdx + 2] = color.b;
- imgData.data[pixelIdx + 3] = Math.floor(intensity * 255); // Alpha = intensity
- }
+ const intensity = amp_to_dB(specValue, 255.);
+ const pixelIdx = (screenY * state.canvasWidth + screenX) * 4;
+ // Use constant color with alpha for intensity (pure colors)
+ imgData.data[pixelIdx + 0] = color.r;
+ imgData.data[pixelIdx + 1] = color.g;
+ imgData.data[pixelIdx + 2] = color.b;
+ imgData.data[pixelIdx + 3] = intensity;
}
}
@@ -1468,6 +1446,19 @@ function updatePlayhead() {
requestAnimationFrame(updatePlayhead);
}
+// Logarithmic intensity mapping (dB scale)
+// Maps wide dynamic range to visible range
+function amp_to_dB(amplitude, max_value) {
+ amplitude = Math.abs(amplitude);
+ if (amplitude < 0.0001) return 0; // noise floor
+ const dB_min = -40.0; // Noise floor
+ const dB_max = 40.0; // Peak (40 dB headroom)
+ const dB_scale = max_value * (1. / (dB_max - dB_min));
+ const dB = 20.0 * Math.log10(amplitude);
+ const normalized = (dB - dB_min) * dB_scale;
+ return Math.floor(Math.max(0, Math.min(max_value, normalized)));
+}
+
function drawSpectrumViewer() {
const viewer = document.getElementById('spectrumViewer');
const canvas = document.getElementById('spectrumCanvas');
@@ -1485,7 +1476,7 @@ function drawSpectrumViewer() {
frameIdx = state.mouseFrame;
}
- if (frameIdx < 0 || frameIdx >= (state.referenceNumFrames || 100)) return;
+ if (frameIdx < 0 || frameIdx >= state.referenceNumFrames) return;
// Clear canvas
ctx.fillStyle = '#1e1e1e';
@@ -1494,17 +1485,37 @@ function drawSpectrumViewer() {
const numBars = 100; // Downsample to 100 bars for performance
const barWidth = canvas.width / numBars;
+ // Draw spectrum bars (both reference and procedural overlaid)
+ function draw_spectrum(spectrum, is_ref_spectrum) {
+ if (!spectrum) return;
+ for (let i = 0; i < numBars; i++) {
+ const binIdx = Math.floor(i * state.referenceDctSize / numBars);
+ const height = amp_to_dB(spectrum[binIdx], canvas.height);
+ const gradient = ctx.createLinearGradient(0, canvas.height - height, 0, canvas.height);
+ if (is_ref_spectrum) {
+ // Draw reference spectrum (green, behind)
+ gradient.addColorStop(0, '#00ff00');
+ gradient.addColorStop(1, '#004400');
+ } else {
+ // Draw procedural spectrum (red, overlaid)
+ gradient.addColorStop(0, '#ff5555'); // Bright red
+ gradient.addColorStop(1, '#550000'); // Dark red
+ ctx.globalAlpha = 0.7;
+ }
+ ctx.fillStyle = gradient;
+ ctx.fillRect(i * barWidth, canvas.height - height, barWidth, height);
+ ctx.globalAlpha = 1.0;
+ }
+ }
+
+ const size = state.referenceDctSize;
+ const pos = frameIdx * size;
// Get reference spectrum (if available)
- let refSpectrum = null;
if (state.referenceSpectrogram && frameIdx < state.referenceNumFrames) {
- refSpectrum = new Float32Array(state.referenceDctSize);
- for (let bin = 0; bin < state.referenceDctSize; bin++) {
- refSpectrum[bin] = state.referenceSpectrogram[frameIdx * state.referenceDctSize + bin];
- }
+ draw_spectrum(state.referenceSpectrogram.subarray(pos, pos + size), true);
}
// Get procedural spectrum (if curves exist)
- let procSpectrum = null;
if (state.curves.length > 0) {
const numFrames = state.referenceNumFrames || 100;
const fullProcSpec = new Float32Array(state.referenceDctSize * numFrames);
@@ -1513,60 +1524,7 @@ function drawSpectrumViewer() {
});
// Extract just this frame
- procSpectrum = new Float32Array(state.referenceDctSize);
- for (let bin = 0; bin < state.referenceDctSize; bin++) {
- procSpectrum[bin] = fullProcSpec[frameIdx * state.referenceDctSize + bin];
- }
- }
-
- // Draw spectrum bars (both reference and procedural overlaid)
- for (let i = 0; i < numBars; i++) {
- const binIdx = Math.floor(i * state.referenceDctSize / numBars);
-
- // Draw reference spectrum (green, behind)
- if (refSpectrum) {
- const amplitude = Math.abs(refSpectrum[binIdx]);
- let height = 0;
- if (amplitude > 0.0001) {
- const dB = 20.0 * Math.log10(amplitude);
- const dB_min = -60.0;
- const dB_max = 40.0;
- const normalized = (dB - dB_min) / (dB_max - dB_min);
- height = Math.max(0, Math.min(canvas.height, normalized * canvas.height));
- }
-
- if (height > 0) {
- const gradient = ctx.createLinearGradient(0, canvas.height - height, 0, canvas.height);
- gradient.addColorStop(0, '#00ff00');
- gradient.addColorStop(1, '#004400');
- ctx.fillStyle = gradient;
- ctx.fillRect(i * barWidth, canvas.height - height, barWidth - 1, height);
- }
- }
-
- // Draw procedural spectrum (red, overlaid)
- if (procSpectrum) {
- const amplitude = Math.abs(procSpectrum[binIdx]);
- let height = 0;
- if (amplitude > 0.001) {
- const dB = 20.0 * Math.log10(amplitude);
- const dB_min = -40.0; // Same as procedural spectrogram rendering
- const dB_max = 40.0;
- const normalized = (dB - dB_min) / (dB_max - dB_min);
- height = Math.max(0, Math.min(canvas.height, normalized * canvas.height));
- }
-
- if (height > 0) {
- const gradient = ctx.createLinearGradient(0, canvas.height - height, 0, canvas.height);
- gradient.addColorStop(0, '#ff5555'); // Bright red
- gradient.addColorStop(1, '#550000'); // Dark red
- ctx.fillStyle = gradient;
- // Make it slightly transparent to see overlap
- ctx.globalAlpha = 0.7;
- ctx.fillRect(i * barWidth, canvas.height - height, barWidth - 1, height);
- ctx.globalAlpha = 1.0;
- }
- }
+ draw_spectrum(fullProcSpec.subarray(pos, pos + size), false);
}
// Draw frequency labels