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>
This commit is contained in:
+13
-8
@@ -369,23 +369,28 @@ int ComputeSkipFactor(float signalDurationSec)
|
||||
// This replaces existing overview (skipFactor-strided) segments with
|
||||
|
||||
// ===== Amplitude auto-scaling =====
|
||||
// Derive the colorizer's floor/ceiling from the current scale mode and controls.
|
||||
// Called after the STFT changes (load, background completion, FFT-size change)
|
||||
// and when the user toggles the mode. Deriving the floor from dynRangeDb /
|
||||
// absoluteFloorDb here is what preserves the user's setting across re-scales.
|
||||
void AutoScaleAmplitude(StftResult* stft)
|
||||
{
|
||||
// Don't clobber a dB floor the user has set by hand. Reset on new file load
|
||||
// re-enables auto-scaling (see ResetForNewSignal).
|
||||
if (app.amplitudeUserSet) return;
|
||||
if (app.amplitudeMode == SCALE_ABSOLUTE) {
|
||||
// Fixed dBFS scale: 0 dBFS (full-scale) ceiling, user-set absolute floor.
|
||||
app.amplitudeCeilingDb = 0.0f;
|
||||
app.amplitudeFloorDb = app.absoluteFloorDb;
|
||||
return;
|
||||
}
|
||||
|
||||
// Relative: ceiling tracks the signal peak; floor sits dynRangeDb below it.
|
||||
float maxDb = -999.0f;
|
||||
float minDb = 0.0f;
|
||||
for (int seg = 0; seg < stft->numSegments; seg++) {
|
||||
for (int bin = 0; bin < stft->segments[seg].numBins; bin++) {
|
||||
float db = AmplitudeToDecibels(stft->segments[seg].spectrum[bin].amplitude);
|
||||
if (db > maxDb) maxDb = db;
|
||||
if (db < minDb) minDb = db;
|
||||
}
|
||||
}
|
||||
// Set ceiling at the max, floor 40dB below — enough range to see structure
|
||||
// but not so wide that the signal is drowned in black
|
||||
if (maxDb < -998.0f) maxDb = 0.0f; // no data yet — sane default
|
||||
app.amplitudeCeilingDb = maxDb;
|
||||
app.amplitudeFloorDb = maxDb - 40.0f;
|
||||
app.amplitudeFloorDb = maxDb - app.dynRangeDb;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user