Whisper-style models expect 16 kHz audio. Most Mac audio hardware runs at 48 kHz. Somewhere between the two, a rate conversion has to happen, and if it happens without a filter it puts energy into the model input that your voice never produced.
TL;DR
- Decimating 48 kHz to 16 kHz without a low-pass filter folds everything above 8 kHz back into the speech band.
- Band-limited conversion low-passes at 8 kHz first, then decimates 3 to 1.
- Voice Type asks Core Audio for a 16 kHz input stream directly, so the conversion usually happens in the audio driver rather than in the app.
- When a device cannot deliver 16 kHz, the app converts with
AVAudioConverterat the end of each batch's conditioning chain.
The naive approach breaks things
Simple decimation, meaning drop two samples out of every three, creates aliasing. A 48 kHz capture carries content up to 24 kHz. Anything above 8 kHz has nowhere to go in a 16 kHz stream, so it reflects back down into the speech band. A 10 kHz tone lands at 6 kHz. The model hears a sound the speaker never made.
Cheap resampling optimises for speed, not stopband attenuation. That is fine for a ringtone. It is not fine for speech recognition, where the difference between "s", "f" and "th" lives in exactly the high-frequency region the filter is supposed to protect.
Band-limited resampling
Band-limited conversion means: apply a low-pass filter at the Nyquist frequency of the target rate (8 kHz for a 16 kHz output), then decimate. Frequencies that would alias are removed before they can fold.
The filter is where the engineering is. Too aggressive and you lose the high-frequency content that distinguishes the fricatives. Too gentle and aliasing sneaks through.
Where Voice Type does this
Not where you would guess, and not where an earlier version of this page said.
The app does not capture at 48 kHz and convert at the end. It asks the input Audio Queue for a 16 kHz stream up front, so Core Audio performs the rate conversion in the capture path before the app sees a sample. The voice activity detector, the batcher, and the recognizer all work at 16 kHz. There is no 48 kHz stage in the live pipeline.
Two places still do conversion in-app, because the request is a request and not a guarantee:
- If the selected device reports a rate other than 16 kHz, the voice activity detector's input is converted first, because the gate is built around 512-sample frames at 16 kHz and nothing else.
- Each flushed batch runs through a conditioning chain that ends in a resample to 16 kHz. When capture was already at 16 kHz this step is a no-op. When it was not,
AVAudioConverterhandles it, and the converter is treated as stateless per call so a short clip does not pick up priming artifacts from the previous one.
The conditioning chain around it, in order: remove the DC offset, apply the filters and loudness control, then resample. Filtering before the rate change is the right order, because the high-pass cutoff is specified in Hz and the filter coefficients depend on the sample rate it runs at.
Why not just record at 48 kHz and convert later?
You can, and plenty of pipelines do. Requesting the model's rate at the capture layer is cheaper in three ways: no buffer is three times larger than it needs to be, the voice activity detector does not need a conversion in front of it on the common path, and the conversion that does happen is done once by the audio driver rather than repeatedly by the app.
The trade-off is that you are trusting Core Audio's converter rather than your own. That is a trade we are happy with. A driver-level sample rate converter is a well-tested piece of code, and the alternative is maintaining our own to solve a problem Apple already solved.
What this means for you
Almost nothing, which is the point. Rate conversion is a correctness problem, not a tuning knob. Done right it is invisible, and done wrong it shows up as a transcript that gets consonants subtly wrong in a way no amount of model size fixes.
If you are building something similar: convert with a band-limited converter, do it once, and do it after your filters rather than before them.
Related: Voice activity detection · Technology overview
