Files
rspektrum/known_bugs.md
T
tyler 5c3c88dc22 feat: scale time zoom by duration, decouple the two axes
The minimum visible time window was a flat 2% of the file, but view.start
and view.end are normalized to the whole signal — so the achievable time
resolution scaled with file length. A 30-minute recording could never show
less than a 36-second span, while a 30-second one reached 0.6 s. Long
captures were effectively unreadable at the sample level no matter how far
you scrolled.

Derive the floor from the STFT hop instead (MinTimeViewWidth): segments sit
fftSize/HOP_RATIO samples apart, so the real limit is the point where only
a handful of segments span the viewport and further zoom would interpolate
rather than reveal. The floor is now a constant ~43 ms at 48 kHz/1024
regardless of duration — an 844x improvement on a 30-minute file, and it
tightens further with a smaller FFT. Guards cover the unloaded (sampleRate
0) and shorter-than-the-floor cases.

Zooming is also no longer forced to move both axes together. The bare wheel
keeps the existing coupled behaviour; Shift+wheel is time-only and
Ctrl+wheel frequency-only, so a long capture can be stretched along time
without collapsing the frequency range to match.

Time-axis labels now pick their precision from the span between adjacent
ticks (1 to 4 decimals, and m:ss.sss past a minute). At the spans this
change makes reachable the old fixed "%.1fs" printed the same value in
every slot, which read as a frozen axis.

Adds a `wheel X Y N [mod]` action to shot_input.sh for exercising zoom
headlessly, and known_bugs.md for behaviour that is unspecified rather
than broken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V8ZWfr5XZyyDttvkhJUgHN
2026-08-12 00:19:36 -07:00

73 lines
3.4 KiB
Markdown

# Known bugs & rough edges
Behaviour that is unspecified, awkward, or known-imperfect — as distinct from
outright breakage. Each entry says what happens, why, and what a real fix would
need to decide.
---
## Playhead vs. a selection edited mid-playback
**Status:** partially addressed; underlying semantics still undefined.
Playback hands a *snapshot* of the selected region to the audio device — the
samples are copied, bandpassed, and normalised up front, so the sound coming out
of the speakers is fixed the moment **Space** is pressed. The selection box,
however, stays live and editable while that audio plays.
Previously the playhead marker was drawn against the *live* `app.sel`, so moving
or resizing the selection during playback made the marker jump, run off the end,
or scale to a region that had nothing to do with what was audible. The playhead
is now measured against `playSelStart` / `playSelEnd` / `playDuration`, captured
at `PlaySelectedRegion()` time, so it tracks the audio that is actually playing.
What remains undefined is the *product* question, not the drawing math:
- If the user drags the selection somewhere else mid-playback, should the audio
follow (restart / re-seek against the new region), or should playback keep
going with the old buffer and the marker stay where it is (current behaviour)?
- Should editing the selection during playback simply stop playback?
- Should the playhead remain visible when the region it refers to is scrolled
off-screen, or has been replaced by a selection elsewhere in the file?
Current behaviour is the conservative reading: **the sound wins**. The marker
always describes real audio, and a mid-playback edit is treated as staging the
*next* thing to play rather than modifying the current one. That is defensible
but was never explicitly chosen, and the UI gives no feedback that the box on
screen and the audio in flight have diverged.
Related: a sub-threshold click *inside* an existing selection deliberately does
not clear it (`hoverInsideSelection` in `spectrogram.c`), because silently
clearing changes what **Space** would play. A click on empty space still resets
to full range.
**Touches:** `audio.c` (`PlaySelectedRegion`), `spectrogram.c` (playhead
advance, scope cursor), `render.c` (`DrawPlayhead`), `spectrogram_types.h`
(`playSelStart` / `playSelEnd` / `playDuration`).
---
## Long-file zoom sharpness lags the zoom gesture
**Status:** working as designed, but reads as a bug.
`ComputeSkipFactor()` (`stft.c`) strides the initial STFT pass for long files —
every 8th segment past 10 minutes — so the overview loads promptly. The missing
segments are filled at full resolution afterwards: the visible range first, then
a background sweep of the whole file.
The practical effect is that a hard zoom into a 30-minute file can look blocky
for a moment before the foreground fill catches up and it sharpens. The fill is
gated on `view.end - view.start <= 0.25f`, so it only runs once reasonably zoomed
in. If a view stays blocky indefinitely, the fill is not reaching that range and
that *is* a real bug worth chasing.
---
## Load time on long files is unbounded and unreported
A 30-minute 48 kHz file spends a long time in `Processing…` before the UI is
usable, and the percentage indicator advances non-linearly (the strided overview
completes fast, the high-res fill does not). There is no cancel. Headless/scripted
runs must wait this out; see `AGENTS.md`.