summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-06 16:44:18 +0100
committerskal <pascal.massimino@gmail.com>2026-02-06 16:44:18 +0100
commit6ed5952afe5c7a03f82ea02d261c3be2d56bd6a1 (patch)
treef37a9b230ece3e101d83d88a6d95b6523eed5488 /tools
parenta9f0174f95b577cc7f8d67023eb37d83d050c0fd (diff)
fix(editor): Apply window to spectrum before IDCT, not after
Fixed comb-like pattern in web editor playback by matching the C++ synth windowing strategy. Root Cause: - C++ synth (synth.cc): Applies window to SPECTRUM before IDCT - JavaScript editors: Applied window to TIME DOMAIN after IDCT - This mismatch caused phase/amplitude distortion (comb pattern) Solution: - Updated spectral_editor/script.js: Window spectrum before IDCT - Updated editor/script.js: Window spectrum before IDCT - Removed redundant time-domain windowing after IDCT - JavaScript now matches C++ approach exactly Result: - Clean frequency spectrum (no comb pattern) - Correct audio playback matching C++ synth output - Generated Gaussian curves sound proper Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/editor/script.js13
-rw-r--r--tools/spectral_editor/script.js8
2 files changed, 14 insertions, 7 deletions
diff --git a/tools/editor/script.js b/tools/editor/script.js
index abfd4f4..ebc543a 100644
--- a/tools/editor/script.js
+++ b/tools/editor/script.js
@@ -631,11 +631,18 @@ async function playSpectrogramData(specData) {
// 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
+ // Apply window to spectrum before IDCT (matches C++ synth.cc)
+ const windowedSpectral = new Float32Array(dctSize);
for (let i = 0; i < dctSize; i++) {
- audioData[frameIndex * dctSize + i] = timeDomainFrame[i] * hanningWindowArray[i];
+ windowedSpectral[i] = spectralFrame[i] * hanningWindowArray[i];
+ }
+
+ const timeDomainFrame = javascript_idct_512(windowedSpectral);
+
+ // Direct output (no additional windowing)
+ for (let i = 0; i < dctSize; i++) {
+ audioData[frameIndex * dctSize + i] = timeDomainFrame[i];
}
}
diff --git a/tools/spectral_editor/script.js b/tools/spectral_editor/script.js
index 4053aef..677a823 100644
--- a/tools/spectral_editor/script.js
+++ b/tools/spectral_editor/script.js
@@ -1543,20 +1543,20 @@ function spectrogramToAudio(spectrogram, dctSize, numFrames) {
const window = hanningWindowArray;
for (let frameIdx = 0; frameIdx < numFrames; frameIdx++) {
- // Extract frame
+ // Extract frame and apply window to spectrum (matches C++ synth.cc)
const frame = new Float32Array(dctSize);
for (let b = 0; b < dctSize; b++) {
- frame[b] = spectrogram[frameIdx * dctSize + b];
+ frame[b] = spectrogram[frameIdx * dctSize + b] * window[b];
}
// IDCT
const timeFrame = javascript_idct_512(frame);
- // Apply window and overlap-add
+ // Overlap-add (no additional windowing - window already applied to spectrum)
const frameStart = frameIdx * hopSize;
for (let i = 0; i < dctSize; i++) {
if (frameStart + i < audioLength) {
- audioData[frameStart + i] += timeFrame[i] * window[i];
+ audioData[frameStart + i] += timeFrame[i];
}
}
}