diff options
| author | skal <pascal.massimino@gmail.com> | 2026-03-05 22:49:48 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-03-05 22:49:48 +0100 |
| commit | db6fbf8b8eae8b96d129ac673cbf11d67926996a (patch) | |
| tree | 97d28da56a77c17f5583a9342a77cb065b25f31f /src/tests/audio | |
| parent | b7fc4aa9a6bd15ce9780d46a425971d523c10b92 (diff) | |
fix(audio): correct OLA synthesis and extract shared ola_encode/ola_decode
- Remove erroneous Hann synthesis window from synth.cc (g_hann * tmp[j]).
Hann analysis at 50% overlap satisfies w[n]+w[n+H]=1, so rectangular
synthesis gives perfect reconstruction; applying Hann twice was wrong.
- Extract ola_encode()/ola_decode()/ola_num_frames() into src/audio/ola.h+cc.
spectool and test_wav_roundtrip now use the shared functions.
synth.cc lazy-decode path stays inlined (see TODO for future refactor).
- Drop dead <atomic> include and g_hann array from synth.cc.
- Drop dead window.h include from spectool.cc.
- Update PROJECT_CONTEXT.md, COMPLETED.md, TODO.md to reflect correct
analysis-only Hann window and new ola.h API.
handoff(Gemini): OLA synthesis bug fixed + ola.h factorized. synth.cc
lazy-decode still inline (TODO item added). 34/35 tests pass; WavDumpBackendTest
failure is pre-existing and unrelated.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'src/tests/audio')
| -rw-r--r-- | src/tests/audio/test_wav_roundtrip.cc | 60 |
1 files changed, 9 insertions, 51 deletions
diff --git a/src/tests/audio/test_wav_roundtrip.cc b/src/tests/audio/test_wav_roundtrip.cc index 6294d6d..79de6ad 100644 --- a/src/tests/audio/test_wav_roundtrip.cc +++ b/src/tests/audio/test_wav_roundtrip.cc @@ -1,9 +1,8 @@ // Tests the wav->spec->wav roundtrip SNR. -// Generates a sine wave, runs OLA-DCT analysis then IMDCT-OLA synthesis, +// Generates a sine wave, runs OLA encode then OLA decode, // and asserts the reconstruction SNR exceeds the threshold. -#include "audio/dct.h" -#include "audio/window.h" +#include "audio/ola.h" #include <assert.h> #include <cmath> #include <cstdio> @@ -12,49 +11,6 @@ static const int SAMPLE_RATE = 32000; static const float PI = 3.14159265358979323846f; -// Replicate analyze_audio OLA pass (Hann + FDCT, hop = OLA_HOP_SIZE) -static std::vector<float> ola_analyze(const std::vector<float>& pcm) { - float win[DCT_SIZE]; - hann_window_512(win); - - const int hop = OLA_HOP_SIZE; - const int n_pcm = (int)pcm.size(); - const int num_frames = (n_pcm > DCT_SIZE) ? (n_pcm - DCT_SIZE) / hop + 1 : 1; - - std::vector<float> spec(num_frames * DCT_SIZE); - float chunk[DCT_SIZE]; - - for (int f = 0; f < num_frames; ++f) { - const int start = f * hop; - const int avail = (start + DCT_SIZE <= n_pcm) ? DCT_SIZE : n_pcm - start; - for (int i = 0; i < avail; ++i) chunk[i] = pcm[start + i] * win[i]; - for (int i = avail; i < DCT_SIZE; ++i) chunk[i] = 0.0f; - - fdct_512(chunk, spec.data() + f * DCT_SIZE); - } - return spec; -} - -// IDCT + OLA synthesis (no synthesis window) matching decode_to_wav. -// Analysis used Hann; since Hann satisfies w[n]+w[n+H]=1 at 50% overlap, -// skipping the synthesis window gives perfect reconstruction. -static std::vector<float> ola_decode(const std::vector<float>& spec, - int num_frames) { - std::vector<float> pcm(num_frames * OLA_HOP_SIZE + OLA_OVERLAP, 0.0f); - float overlap[OLA_OVERLAP] = {}; - float tmp[DCT_SIZE]; - - for (int f = 0; f < num_frames; ++f) { - idct_512(spec.data() + f * DCT_SIZE, tmp); - for (int j = 0; j < OLA_HOP_SIZE; ++j) - pcm[f * OLA_HOP_SIZE + j] = tmp[j] + overlap[j]; - for (int j = 0; j < OLA_OVERLAP; ++j) - overlap[j] = tmp[OLA_HOP_SIZE + j]; - } - pcm.resize(num_frames * OLA_HOP_SIZE); - return pcm; -} - static float compute_snr_db(const std::vector<float>& ref, const std::vector<float>& out, int skip_samples) { @@ -78,12 +34,14 @@ int main() { for (int i = 0; i < n_samples; ++i) input[i] = 0.5f * sinf(2.0f * PI * 440.0f * i / SAMPLE_RATE); - // Analyze - std::vector<float> spec = ola_analyze(input); - const int num_frames = (int)(spec.size() / DCT_SIZE); + // Encode + const int num_frames = ola_num_frames(n_samples); + std::vector<float> spec(num_frames * DCT_SIZE); + ola_encode(input.data(), n_samples, spec.data(), num_frames); - // Decode with IDCT-OLA (no synthesis window) - std::vector<float> output = ola_decode(spec, num_frames); + // Decode + std::vector<float> output(num_frames * OLA_HOP_SIZE); + ola_decode(spec.data(), num_frames, output.data()); // SNR — skip first DCT_SIZE samples (ramp-up transient) const float snr = compute_snr_db(input, output, DCT_SIZE); |
