What's Changed
This release lands wide-range Master Tempo — a second, honest-latency keylock profile covering the engine's full 0.25–2.0 tempo range (ROADMAP Stage 11) — and consolidates per-track analysis into a single .tsa container owned by the library: timestretch 0.11.0 on crates.io.
Breaking Changes
timestretch-cli analyzenow writes the binary.tsaanalysis container by default (<input>.tsa) instead of<input>.tsanalysis.json. An explicit-o out.jsonkeeps the legacy JSON format, and--pre-analysissniffs magic bytes and accepts either.EngineProfilegains theWideKeylockvariant — external exhaustivematches over the enum will need a new arm.- Desktop: tracks converge to one
.tsasidecar. Valid legacy.tsanalysis.jsonartifacts are absorbed into the container on load, and both legacy sidecars (.tsanalysis.json,.tspeaks) are deleted once the on-disk container supersedes them.
Wide-Range Master Tempo (Stage 11)
EngineProfile::WideKeylock: a full-spectrum FFT-2048/hop-256 identity-locked phase-vocoder corrector with artifact-driven per-band phase resets, keylocking across tempo rates 0.25–2.0 with no correction fade. Constant pipeline delay of 2144 frames (48.6 ms at 44.1 kHz) — its own honest latency contract alongside the untouched 12.7 ms standard keylock chain.- Built on a falsification experiment first: a ±30/±50/±100/−75% listening matrix against varispeed, the shipped wide phase vocoder, and RubberBand references identified the coherence-blend taper as the cause of robotic wide slowdowns (now held at 0.20, settled by live A/B) and 75% overlap as the −75% click/level blowup (hop = FFT/8 eliminates it).
- Gate suite: strict WCET p99.9 of 0.328 vs the 0.5 budget at 64-frame callbacks, zero-alloc steady state, click-free wide rides and snaps, an exact-constant latency contract gate (≤55 ms), and seven wide A/B matrix gates (steady-state pitch error 0.09–0.50 cents p95).
- Supporting engine API:
PhaseVocoder::set_wide_ratio_coherence_blend,StreamingSincResampler::set_step_anchor(uniform chunks consumed uniformly) andStreamingSincResampler::flush_into(drains the sinc lookahead tail without losing the final half-window), andStage::warm_start_settle_framesfor per-stage warm-start preroll.
.tsa Analysis Container
io::tsa: one content-bound chunked binary file per track — a 28-byte identity header (sample rate, mono length, content hash) plus versionedARTF(pre-analysis artifact) andPEAK(3-band waveform peaks) chunks. Unknown tags and versions skip forward-compatibly; readers reject-don't-panic on hostile input.- Two API layers: bytes (
AnalysisFile::to_bytes/from_bytes/from_bytes_validated) for apps that store analysis blobs in their own database keyed bycontent_hash, and sidecar file wrappers (read_analysis_file,read_analysis_file_validated,write_analysis_file,analysis_file_path) with atomic writes. analysis::waveform: the desktop app's 3-band waveform peaks pyramid moved into the library (BandPeaks,PeakLevel,NUM_BANDS) so any frontend gets display peaks without reimplementing the analyzer.read_preanalysis_json/write_preanalysis_jsonare deprecated (still working) in favor of the container;PreAnalysisArtifact::matches_identityadded for callers that already hold the (rate, length, hash) identity.
Desktop
- Range selector (Standard | Wide) with a seek-priced engine rebuild that preserves the playhead, and a live pipeline-latency readout next to it.
- Track loads no longer freeze the UI: decode, peaks, and pre-analysis run on a background worker, and analyzed tracks load near-instantly on relaunch via the persisted peaks cache (now stored in the
.tsacontainer).
Full Changelog: v0.10.0...v0.11.0