Commit Graph

5 Commits

Author SHA1 Message Date
tyler 26afc4b30e feat: frequency-aware selection stats (peak/center freq, occupied BW, SNR)
The selection readout was time-domain only — Energy/Peak/RMS/PAPR computed
from the whole-bandwidth waveform in the time span, ignoring the box's
frequency bounds entirely. The 2D box only measured one axis.

Add ComputeSpectralStats (stft.c): measures the boxed band from the STFT
magnitude (not the synchrosqueezed display buffer, which relocates energy)
and reports peak frequency, power-weighted centroid ("power center"),
occupied bandwidth (in-band span >3 dB over a median noise floor — robust
for both tones and noise-like bursts), and in-band SNR.

Also fold the two near-identical stats-panel blocks in render.c into one
DrawStatPanel + BuildSelectionStatLines helper so the live-drag and
committed-selection readouts can't drift.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 10:26:37 -07:00
tyler 487e3ad85b refactor: extract shared ComputeSegment for STFT passes
ComputeSTFTIncremental (overview, skipFactor-strided) and
ComputeNextHighResChunk (high-res fill) had ~50 lines of identical
per-segment code (windowing, FFT, bin fill for both the normal and
derivative spectra). Extract it into ComputeSegment() with a reusable
SegScratch buffer set (allocated once per pass, no per-segment malloc).
Each caller keeps only its own skip logic. Behavior-preserving — verified
the rendered spectrogram is unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 02:02:46 -07:00
tyler 3863ab8620 feat: dB scale modes (relative/absolute dBFS), about dialog, fix white-out
dB floor handling:
- Fix the white-out/inversion when the floor was cranked up: guard the
  (ceiling - floor) range in ColorizeSpectrogram so floor >= ceiling
  degrades to a hard threshold instead of dividing by ~0 (white) or a
  negative (inverted colors).
- Add two amplitude scale modes, toggled in the sidebar:
  * Relative: ceiling tracks the signal peak, floor sits N dB below it
    (slider = dynamic range, 10..100 dB). Floor can't cross the ceiling.
  * Absolute: fixed dBFS scale (0 dBFS = full scale), slider sets an
    absolute floor in dBFS. Brightness reflects real level.
  Mode + both slider values persist across loads. This replaces the
  amplitudeUserSet flag — storing the intent (range / absolute floor)
  preserves it across re-scales structurally.

About / Help dialog (no help menu existed):
- F1 or sidebar button; documents the scale modes and the caveats
  (dBFS not dBm — a WAV has no power reference; ~6 dB Hann window
  offset; pixels are reassigned energy, not raw bins) plus key list.
- Modal: underlying spectrogram input is gated while open; open/close
  handled in the main loop so the opening click can't self-close it and
  a dismissing click can't fall through into a selection.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 01:43:02 -07:00
tyler ddbbe2734c perf+ux: cache reassignment, keep manual dB floor, dedupe load paths
- Split GenerateSpectrogramTexture into ComputeSpectrogramReassignment
  (the expensive synchrosqueezing, cached in app.reassignBuffer) and
  ColorizeSpectrogram (cheap). dB-floor and colormap changes now only
  re-colorize instead of recomputing the whole reassignment every frame —
  the dB slider and colormap switching are smooth on large files.
- AutoScaleAmplitude no longer overwrites a dB floor the user set by hand
  (amplitudeUserSet flag, reset per file load).
- Extract ResetForNewSignal() used by all three load paths; removes the
  duplicated reset blocks and the double ComputeSTFTInit per load. Drag-drop
  now resets the selection like the browser already did.
- Remove the dead lastInteractedFrame field.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 01:26:51 -07:00
tyler 3a8f20b783 refactor: split spectrogram.c into per-concern modules
spectrogram.c was ~2950 lines holding everything. Break it into cohesive
translation units; spectrogram.c keeps only globals + the main frame loop.

New modules:
- spectrogram_types.h  shared types, constants, extern globals, inline math
- fft.c/.h             FFT, bit-reverse, twiddle (standalone, no app deps)
- stft.c/.h            STFT compute, adaptive resolution, FFT-size LRU cache
- audio.c/.h           WAV/ffmpeg load, FreeSignal, bandpass, playback
- render.c/.h          UI scaling, colormaps, texture gen, on-screen drawing
- ui.c/.h              file browser, sidebar, sliders, PNG export

Also:
- utils.c now includes utils.h instead of re-typedef'ing AudioSignal/
  SignalStats (they had to be hand-synced before).
- Remove dead code: ApplyHannWindow and ComputeSTFTHighResRange were never
  called (the live high-res path is ComputeNextHighResChunk).
- Delete the unused raylib-template main.c.
- rspektrum.make: build the new units. premake5.lua: glob src/**.c so a
  future regen stays correct.

Pure code movement otherwise; no behavior change. Builds clean (-Wshadow).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 01:15:51 -07:00