Fix gemma4 bit-equality: window construction and FFT precision
Two independent defects, both required to reach bit-exactness.
1. Wrong window helper. Gemma4's legacy extractor builds its window with
window_function(320).astype(np.float32) -- a float64 cosine stored as float32. It was
configured with hann_window_f32, which reproduces Gemma3n's inline construction instead:
np.arange(n, dtype=np.float32) fed through 0.5*(1-cos(...)). Under numpy scalar promotion
a float32 arange keeps the whole cosine in single precision, so the two windows differ by
2.4e-07. Both models legitimately need different windows, so this adds a second named
window, hann_window_f64_as_f32, rather than changing the existing one -- gemma3n is
correct as it stands.
2. FFT ran in complex64. np.fft.rfft always promotes to complex128 regardless of float32
input, so the legacy magnitudes are float64; fft_dtype was set to "native".
Neither fix alone helps -- window-only lands at 7.15e-06 and fft-only at 7.87e-06, both
worse than the 6.79e-06 starting point, which is why a config sweep never found it.
The named window is built through numpy in both backends, so the torch and numpy siblings
stay bit-identical (verified, maxdiff 0.0).
Not the cause, checked and discarded: the mel filter bank (making the filters bit-exact via
a numpy grid left the output diff unchanged), frequency_bin_mode, and the framing (frames
were already bit-identical).