diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-06 16:44:18 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-06 16:44:18 +0100 |
| commit | 6ed5952afe5c7a03f82ea02d261c3be2d56bd6a1 (patch) | |
| tree | f37a9b230ece3e101d83d88a6d95b6523eed5488 /tools | |
| parent | a9f0174f95b577cc7f8d67023eb37d83d050c0fd (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.js | 13 | ||||
| -rw-r--r-- | tools/spectral_editor/script.js | 8 |
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]; } } } |
