fix: playhead drift, replay, and cursor flicker; add selection transport
**Playhead ran ahead of the audio.** It was dead-reckoned by summing GetFrameTime() every frame, so every frame's *render* work counted as playback time and the error compounded — by seconds over a long region, and worst when zoomed out where each frame does the most work. That is why it tracked fine zoomed in. It is now derived from a wall-clock instant captured when the buffer is handed to the device, so it cannot drift from the audio regardless of frame timing. **Replay after a natural finish left the marker stuck at the end.** The rail's play button never cleared playbackFinished, so the stale playheadT > 1.0 persisted and DrawPlayhead early-returned while the audio played from the top. The Space path already handled this; the button did not. **No way to replay a subrange.** The playhead is now drawn while stopped (with a grab tab) and can be dragged to set where the next play starts *within* the selection, leaving the region itself intact. Stopping parks the marker where it stopped rather than snapping to the start. playheadT is a fraction of the *played* span, which stops being the selection once a scrub offset exists — so every conversion goes through absolute file time, the only frame the two share. Getting this wrong made stop-after-scrub jump backwards. **Selection transport bar.** Rewind / play-pause / stop / loop, attached above the selection box. Pause resumes where it left off; loop restarts from the top of the region rather than repeating whatever tail the last play started from. **Cursor flicker.** Twelve unconditional SetMouseCursor calls ran per frame and the last one won. Two blocks in particular both ran every frame: one set a cursor regardless of mouse position, and a second overrode it only when the mouse was inside the spectrogram — so when a capture guard had parked mousePos off-screen, the first block's stale choice stuck. That is the fight between finger/pointer, resize/pointer and crosshair/pointer. Handlers now record a prioritised request (active drag > hover hint > default) and it is applied once at the end of the frame. The divider hint is RESIZE_NS rather than the 4-way arrow while here. The rewind glyph drew its bar and triangle with a gap between them and read as a lone vertical bar; verified the fix by rendering it offscreen and dumping pixels rather than eyeballing the geometry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8ZWfr5XZyyDttvkhJUgHN
This commit is contained in:
@@ -44,10 +44,15 @@ format.
|
||||
- **mLnL annotation overlay** — labelled boxes from the WAV's embedded annotation
|
||||
chunk; hover a box (or its region on the scope) for per-frame detail (sequence,
|
||||
channel, rate, scheduling offset…).
|
||||
- **Zoom & pan** the time/frequency view.
|
||||
- **Zoom & pan** the time/frequency view, with a **minimap** for navigating a
|
||||
long capture without zooming out and back in.
|
||||
- **Collision detection** — finds transmissions that genuinely overlap in *both*
|
||||
time and frequency, and jumps between them.
|
||||
- **Region selection** — box a time *and* frequency range with the mouse.
|
||||
- **Filtered playback** — play just the selected region, band-limited to the
|
||||
selected frequency box via an FFT bandpass. What you hear is what you'd export.
|
||||
Transport controls (play/pause, rewind, loop) attach to the selection, and the
|
||||
playhead can be dragged to replay a subrange.
|
||||
- **Waveform scope** — toggleable time-domain view beneath the spectrum.
|
||||
- **Marker / ruler** and a **spectrum slice (PSD)** readout.
|
||||
- **Export** — save the view as a PNG, or the selected region as a WAV.
|
||||
@@ -141,6 +146,7 @@ Middle-drag always pans.
|
||||
| **Ctrl+wheel** | Zoom the frequency axis only |
|
||||
| **Wheel on a scrollbar** | Pan that axis (**Shift** to zoom it) |
|
||||
| **Space** | Play / stop the selected region |
|
||||
| **Drag the playhead** | (while stopped) set where the next play starts |
|
||||
| **Hover an annotation** | Tooltip with that frame's mLnL detail; lists **every** overlapping frame under the cursor |
|
||||
| **N** / **Shift+N** | Jump to the next / previous collision |
|
||||
| **O** | Open file browser |
|
||||
@@ -180,6 +186,17 @@ texture and only rebuilt when its *content* changes (new file, colormap,
|
||||
overlays toggled); panning and zooming just move the rectangle drawn on top, so
|
||||
navigation costs nothing.
|
||||
|
||||
### Playback
|
||||
|
||||
**Space** plays the selected region, band-limited to the selected frequency box.
|
||||
Selecting a region also brings up a small transport bar above it — rewind,
|
||||
play/pause, stop, and loop. Pause resumes where it left off; loop repeats the
|
||||
whole region.
|
||||
|
||||
While stopped, the playhead stays where it is and can be dragged: that sets
|
||||
where the next play begins *within* the selection, so a subrange can be replayed
|
||||
without redrawing the region. Clearing or redrawing the selection resets it.
|
||||
|
||||
### Inspecting overlapping transmissions
|
||||
|
||||
When several stations are on the air at once their annotation boxes stack, and
|
||||
@@ -255,25 +272,10 @@ Annotation kinds: `tx_frame`, `tx_burst`, `control`, `channel_up`,
|
||||
|
||||
## Driving the GUI headlessly (agents / CI)
|
||||
|
||||
The app can be run, screenshotted, and clicked on a virtual X display with no
|
||||
monitor or GPU (Mesa software GL under Xvfb). The full playbook lives in
|
||||
[`AGENTS.md`](AGENTS.md); the working reference implementation is
|
||||
[`shot_input.sh`](shot_input.sh).
|
||||
|
||||
The loop in one breath:
|
||||
|
||||
```bash
|
||||
Xvfb :99 -screen 0 1280x800x24 >/tmp/xvfb.log 2>&1 & # 1. fake screen
|
||||
DISPLAY=:99 ./bin/Debug/rspektrum mlnl_samples.wav \
|
||||
>/tmp/app.log 2>&1 & # 2. run on it
|
||||
sleep 2 # 3. reach a steady frame
|
||||
DISPLAY=:99 import -window root /tmp/shot.png # 4. grab the frame
|
||||
```
|
||||
|
||||
Prerequisites (Debian/Ubuntu): `sudo apt-get install xvfb imagemagick xdotool`
|
||||
(plus `libgl1-mesa-dri` and `LIBGL_ALWAYS_SOFTWARE=1` if GL fails / frames are
|
||||
black). Synthesize input with `xdotool` against `DISPLAY=:99` to exercise UI
|
||||
paths.
|
||||
The app runs, screenshots, and takes synthetic input on a virtual X display with
|
||||
no monitor or GPU (Mesa software GL under Xvfb). The playbook is in
|
||||
[`AGENTS.md`](AGENTS.md); [`shot_input.sh`](shot_input.sh) is the working
|
||||
reference implementation.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user