improve: bandpass skirt width in Hz, not bins (consistent across selections)

The playback bandpass transition was freqPerBin*10 wide, and freqPerBin
depends on the FFT size, which is derived from the selection length — so
the same frequency band got a sharper or softer filter depending on how
long a region you selected. Set the skirt to ~20% of the passband (capped
at 100 Hz), floored at 3 FFT bins so it stays smooth at coarse resolution.
Now a given band sounds the same regardless of selection duration.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-25 10:31:23 -07:00
parent ee9eac786c
commit 9d21dbe82b
+11 -2
View File
@@ -136,14 +136,23 @@ static void ApplyBandpassFilterFFT(float* samples, int numSamples, int sampleRat
FFT(fftInput, fftOutput, fftSize, false);
float freqPerBin = (float)sampleRate / fftSize;
// Transition (skirt) width in Hz: ~20% of the passband, capped at 100 Hz,
// with a floor of 3 FFT bins so it stays smooth at coarse resolution.
// Previously the skirt was tied to bin width alone (freqPerBin * 10), so the
// FFT size — and thus the filter's sharpness — changed with the selection
// length: the same band sounded different depending on how much you boxed.
float bandwidthHz = freqHigh - freqLow;
float transitionHz = fmaxf(fminf(bandwidthHz * 0.2f, 100.0f), freqPerBin * 3.0f);
for (int bin = 0; bin < fftSize / 2 + 1; bin++) {
float frequency = bin * freqPerBin;
float attenuation = 1.0f;
if (frequency < freqLow) {
float dist = (freqLow - frequency) / (freqPerBin * 10.0f);
float dist = (freqLow - frequency) / transitionHz;
attenuation = 1.0f / (1.0f + dist * dist * dist);
} else if (frequency > freqHigh) {
float dist = (frequency - freqHigh) / (freqPerBin * 10.0f);
float dist = (frequency - freqHigh) / transitionHz;
attenuation = 1.0f / (1.0f + dist * dist * dist);
}
fftOutput[bin] *= attenuation;