feat: render bounded effect tails and reset internal history on transport changes

This commit is contained in:
Johannes Findeisen 2026-11-05 23:55:56 +01:00
commit 836cdedc6b
22 changed files with 284 additions and 18 deletions

View file

@ -152,3 +152,39 @@ energy, bounded extreme feedback/diffusion and graph/direct stereo output
(including the engine's subnormal-to-zero policy). GUI tests save/reopen both
identities; callback guards and ASan/UBSan cover processing. `make builtin-preview`
also writes files 5/6 for repeatable listening; human listening remains pending.
## Phase 44 history and tails
Internal effects reset on a transport discontinuity (seek/loop wrap), stopped
backend, or newly prepared plan. Pause uses backend Stop and therefore also
resets history. Continuous playback retains tails. Reset invalidates delay
history with a valid-frame counter and clears only fixed-size filter/voice state;
it neither allocates nor clears a large delay buffer in the callback. External
plugins retain their existing history contract. MIDI discontinuities continue
to chase notes with fresh envelopes.
**File → Export WAV** renders the song plus the Preferences / transport
`tail_ms` duration (0..60000, default 3000). Output is stereo float32 WAV at the
offline sample rate. The destination must be a new absolute path. Rendering
prepares an independent snapshot, then advances in 256-frame control-thread
steps with a Cancel button; the project and playback instance stay independent.
Only a complete synced file is published, without replacing an existing file.
Cancellation/errors remove the temporary. Missing media rejects export.
RIFF's 32-bit size limit is enforced before rendering. Rendering does not apply
loop preferences, normalize, automatically detect silence or stop infinite
oscillator/feedback sources early: the configured endpoint always bounds work.
The public `xtc_export_create/step/progress/destroy` API supports incremental
rendering and cancellation by destruction. A failed step must be followed by
destruction. `xtc_project_render_length` returns the exact sample endpoint.
Set `stop_after_tail=1` in Preferences to stop ordinary playback after the song
and configured tail. Looping and recording disable this automatic stop. Playback
stop is serviced by the GUI refresh timer and normal stop fade, so it can exceed
the endpoint by a refresh interval plus fade; export has the exact sample length.
Uncheck the setting to retain indefinite playback. Stop always remains available.
Tests compare stereo export against direct rendering in 31-frame blocks, verify
nonzero tails, exact length, cancellation, existing-file retention, tail bounds,
history reset and callback-safe reset. Preview generation and manual listening
procedures remain available; no human listening result is claimed.