Skip to content

feat: stretch tracks shorter than the view to the spectrogram's width - #573

Merged
Tiaxi merged 2 commits into
mainfrom
feat/stretch-short-tracks
Nov 10, 2026
Merged

Tiaxi merged 2 commits into
mainfrom
feat/stretch-short-tracks

Conversation

@Tiaxi

@Tiaxi Tiaxi commented Nov 10, 2026

Copy link
Copy Markdown
Owner

Closes #546.

At the default zoom the spectrogram draws a column a pixel, about 45 pixels a second, so a one-second clip filled a sliver at the left of the plot. (The waveform editor's default zoom already shows the whole track, and the seek bar's waveform always does; neither changes.)

What changed

Setting. Preferences › Appearance › Default zoom: Stretch tracks shorter than the view to its full width, on by default. It's saved as stretch_short_tracks (on for settings files without the key), sent with command 77, and appended to the settings section (older backends' sections decode as on).

Default zoom. ferrous_ffi_spectrogram_fit_zoom (Rust, centered_view::fit_zoom) gives the deepest zoom at which every column a session decodes of the file (decoded_end_column) fits the view's width. It returns 1 when the track already fills the view at zoom 1 or its length is unknown. A 1 s file at 48 kHz in a 1000 px plot gets 44 samples a column: its 998 columns fill 998 of 1000 px, and the centered view shows the whole track wherever the playhead is.

  • The backend's zoom limit goes from 16 to 64 (a column every 16 samples at 44.1/48 kHz), so short clips can fill wide plots. The wheel still stops at 16, or at the stretched default where that is deeper. A track too short to fill the view even at 64 gets 64.
  • SpectrogramItem works out the default from the frames a chunk reports, or else from the clock's duration for the same track token (a rolling gapless continuation's chunks don't carry frames). It also uses its own width. It publishes the result as defaultZoomLevel.
  • Track changes reset the view to the default zoom, not to 1, in both reset paths: the centered metadata reset and the data reset. They ask the backend for that zoom directly. Nothing changes when the default is 1.
  • SpectrogramSurface stores the zoom the user chose, or 0 to follow the default. While it follows, a change of the default goes through the same setZoomLevel path as a wheel zoom, so fix: show older data instead of black while zooming #566's backdrop and reserve keep older data on screen until the new zoom's columns arrive. This covers the setting being toggled, the width changing (splitter, viewer) and the length arriving later. Reset Zoom and a middle click go back to following the default.
  • The zoom readout, the viewer's Reset button and View › Reset Zoom work relative to the default. At a short track's stretched default the readout shows nothing and Reset Zoom is disabled.

Transitions.

  • Duration unknown at the start: the default stays 1 until the length is known, from a later chunk or from the clock. Then the view zooms to the stretched default the way a wheel zoom does, with no reset and no black frame.
  • Gapless long → short, centered: the short track's chunks report its length, and the view resets to its default.
  • Gapless long → short, rolling: the view keeps its zoom through the transition, as it always has. Once the clock reports the new track's duration, a view that is following the default moves to the new one; a zoom the user chose stays. A kept zoom deeper than the new track allows is reset to the default.
  • Repeat-One of a stretched track: same default, data already at its hop, so no restart.

Time scale. Labels can now be 0.5, 0.2 or 0.1 s apart (0:00.5), so a stretched clip gets more than one label. This also affects deep wheel zooms: from about 10× in, labels are half a second apart. The crosshair readout already shows tenths, and the playhead uses the same column mapping as any zoom.

Tests

  • Rust: fit_zoom fills the view to within a hop's rounding at several rates, FFT sizes and widths. It returns 1 for tracks that fill the view, have no columns, or have unknown values, and reaches the deepest zoom for very short tracks. Also covered: the config round-trip and a missing key loading as on, command 77 and the settings section, the command handler, and the raised clamp.
  • Qt SpectrogramItem: a 1 s fixture fills the plot (995–1000 px) with the setting on and keeps today's 43 px with it off. A minute-long track is unaffected either way, in rolling and centered modes. Also covered: duration arriving later, gapless long → short in both modes (including the clock fallback), Repeat-One, the setting toggle and width changes, and sub-second time labels.
  • Qt SpectrogramSurface: follows the default when the length arrives, wheel limits around the default, a chosen zoom staying put, Reset Zoom, the toggle and a resize.
  • Main window: Reset Zoom is disabled at the stretched default and the relative zoom is right.
  • BridgeClient: decoding, command 77, defaults. Preferences: the check box is in the Appearance tab order, toggles, and resets to defaults.

Add Preferences > Appearance > "Stretch tracks shorter than the view to
its full width", saved as stretch_short_tracks (on by default and for
settings files without it), sent with command 77 and at the end of the
settings section.

With it on, the spectrogram's default zoom (a track's first view and
Reset Zoom) for a track shorter than the view is the deepest zoom at
which every column a session decodes of the file fits the plot
(ferrous_ffi_spectrogram_fit_zoom). The backend accepts zooms up to 64
for this; the wheel still stops at 16, or at a deeper default.

SpectrogramItem reckons the default from the chunk's frames, or else
the clock's duration for the same track, and from its width. Track
changes reset to it. SpectrogramSurface follows it until the user
zooms, through the wheel's zoom path, so a length that arrives later, a
resize or the setting toggled keep older data on screen until the new
zoom's arrives. The zoom readout and Reset Zoom are relative to the
default. Time scale labels go down to a tenth of a second apart.

Closes #546
A short clip's zoom deeper than 16x, kept through a rolling gapless
transition to a long track whose length only the clock tells, stayed
above the long track's 16x limit for good: the default dropped to 1
when the clock's duration arrived, but nothing checked the zoom kept.

Every change of the default now goes through publishDefaultZoomChange,
which brings a zoom deeper than the new limit back to it, whether the
length arrives, the setting goes off or the view narrows. The item
publishes the limit as maximumZoomLevel, and SpectrogramSurface clamps
the zoom it keeps to it, so the two agree. The rolling gapless path no
longer resets such a zoom itself, so the data and the clock clamp it
alike in either order.
@Tiaxi
Tiaxi merged commit b55d37f into main Nov 10, 2026
5 checks passed
@Tiaxi
Tiaxi deleted the feat/stretch-short-tracks branch November 10, 2026 09:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: stretch tracks shorter than the view to the visualization's full width

1 participant