Mismatched sample rates are the kind of bug that only shows up as a faint pitch problem nobody can quite name. This guide converts an audio file to a chosen sample rate with FFmpeg, checks the resulting stream, and explains which resampler settings are actually changing the signal. Allow about 15 minutes for a first conversion, including a short inspection of the input and output. The examples use ordinary user permissions and write only to a temporary working directory.
This guide uses FFmpeg 8.0.1, with the ffmpeg-resampler(1) manual installed alongside it. The same manual describes the libswresample interface used by the aresample audio filter. Check your own executable and inspect the input before choosing settings:
$ ffmpeg -version | head -n 1
ffmpeg version 8.0.1 ...
$ ffprobe -v error -select_streams a:0 \
-show_entries stream=sample_rate,channels,sample_fmt \
-of default=noprint_wrappers=1 /path/to/input.wav
sample_fmt=s16
sample_rate=44100
channels=2
Your version and input values will differ. If ffprobe reports no audio stream, stop here and choose a file that contains audio. The filter works on audio streams, not on a filename extension alone.
Checkpoint: write down the input sample rate, channel count and sample format. You need them to tell a deliberate conversion from an accidental one.
Pass a filter graph with aresample=48000 to convert the audio to 48,000 Hz. The command below copies no video, encodes the audio as signed 16-bit PCM, and writes a WAV file:
$ ffmpeg -i /path/to/input.wav \
-map 0:a:0 -af 'aresample=48000' \
-c:a pcm_s16le /path/to/output-48000.wav
-map 0:a:0 selects the first audio stream, so a second stream in a more complicated input is not selected by accident. pcm_s16le is a practical uncompressed output choice for this test. Choose a codec that matches your final container when you are preparing a deliverable.
Verify the output instead of relying on FFmpeg's progress line:
$ ffprobe -v error -select_streams a:0 \
-show_entries stream=sample_rate,channels,sample_fmt \
-of default=noprint_wrappers=1 /path/to/output-48000.wav
sample_fmt=s16
sample_rate=48000
channels=2
The channel count remains two, because changing the sample rate does not by itself change the channel layout. A successful exit status confirms that FFmpeg completed; it does not confirm that you selected the intended stream or output format.
The manual supports the long option names as well as short names. The filter accepts a sample rate followed by colon-separated key=value options. This example selects the built-in swr engine and a Blackman Nuttall filter:
$ ffmpeg -i /path/to/input.wav \
-map 0:a:0 \
-af 'aresample=osr=48000:resampler=swr:filter_type=blackman_nuttall' \
-c:a pcm_s16le /path/to/output-filtered.wav
swr is the default engine. Its filter settings, including filter_type, apply only to that engine. The alternative soxr engine is available only when FFmpeg was built with it; its precision and cheby options belong to SoX Resampler operation. Check availability on the machine before building a script around it:
$ ffmpeg -hide_banner -h filter=aresample | grep -E 'resampler|soxr|filter_type|precision'
-resampler <int> set resampling Engine
soxr 1 select SoX Resampler
-filter_type <int> select swr filter type
-precision <double> set soxr resampling precision
Do not add quality options by habit. The installed help is the authority for the build you are running, and some options are ignored or inapplicable with the other engine.
The resampler can also convert sample formats and channel layouts. Make those changes explicit when a downstream tool requires them. For example, this requests 48 kHz stereo signed 16-bit audio in a WAV file:
$ ffmpeg -i /path/to/input.wav \
-map 0:a:0 \
-af 'aresample=osr=48000:ochl=stereo:osf=s16' \
-c:a pcm_s16le /path/to/output-stereo.wav
ochl sets the output channel layout and osf sets the output sample format inside the resampler. The encoder still matters: pcm_s16le is selected here so the file is not silently written with a different codec. Confirm all three properties:
$ ffprobe -v error -select_streams a:0 \
-show_entries stream=sample_rate,channel_layout,channels,sample_fmt \
-of default=noprint_wrappers=1 /path/to/output-stereo.wav
sample_fmt=s16
sample_rate=48000
channels=2
channel_layout=stereo
Ordinary resampling changes the number and spacing of samples to meet the output rate. It does not normally stretch or squeeze audio to correct timestamp drift. The manual's async, min_comp, min_hard_comp, comp_duration and max_soft_comp options control that timestamp-matching behaviour.
For a file where timestamps need gentle correction, an explicit filter might be:
$ ffmpeg -i /path/to/input.mkv \
-map 0:a:0 -af 'aresample=async=1000' \
-c:a pcm_s16le /path/to/output-corrected.wav
The value is a maximum amount of stretch or squeeze in samples per second. Do not use it merely because resampling is required: it changes timing, and it can fill or trim audio. Inspect the input timestamps and listen to the output before using this in a production pipeline.
FFmpeg opens the output for writing, so a destination with the same name as the source is a poor replacement strategy. The following pattern creates a new file first:
$ ffmpeg -i /path/to/input.wav \
-map 0:a:0 -af 'aresample=48000' \
-c:a pcm_s16le /path/to/input.resampled.wav
$ ffprobe -v error -select_streams a:0 \
-show_entries stream=sample_rate -of default=nw=1 \
/path/to/input.resampled.wav
Keep the original until the new file passes both metadata and listening checks. If the conversion fails, remove only the newly created output. Removing the source or replacing it in a blind batch is irreversible and is not needed for resampling.
aresample filter requests the intended output rate.ffprobe confirms the output metadata, including channel layout when relevant.