采样率转换是通过插值重建信号再按新速率采样,而非简单丢点或插零;libsamplerate库用sinc滤波器实现高质量重采样,需正确初始化SRC_STATE并预留输出缓冲余量。

什么是采样率转换,为什么不能直接复制数据
采样率转换不是简单地丢点或插零——那样会产生严重失真或混叠。比如把 44.1kHz 音频转成 48kHz,每秒要多出约 3900 个样本,硬插零会让高频突变,听感刺耳;等距丢点则会漏掉细节,还可能引入周期性噪声。真正可行的方式是重采样(resampling),本质是用插值重建信号,再按新速率采样。
用 libsamplerate 实现高质量重采样
libsamplerate 是 C++ 项目中最常用、最可靠的开源重采样库,支持多种质量模式(SRC_SINC_BEST_QUALITY 到 SRC_LINEAR),底层用 sinc 滤波器加窗口,能有效抑制混叠和镜像。它不依赖 FFT,适合实时或嵌入式场景。
实操建议:
- 安装:Ubuntu 上
sudo apt install libsamplerate-dev;macOS 用brew install libsamplerate - 链接时加
-lsamplerate,头文件只需#include <samplerate.h> - 关键结构体是
SRC_STATE *,必须用src_new()初始化,用完调src_delete() - 输入缓冲区长度建议 ≥ 256 样本,输出缓冲区要预留足够空间:若输入
n点、比例ratio = out_rate / in_rate,输出至少需ceil(n * ratio) + 16(留滤波器延迟余量)
示例核心调用:
立即学习“C++免费学习笔记(深入)”;
int error; SRC_STATE *src = src_new(SRC_SINC_FASTEST, 1, &error); // 单声道 SRC_DATA data; data.data_in = input_samples; data.data_out = output_samples; data.input_frames = input_len; data.output_frames = output_capacity; data.src_ratio = 48000.0 / 44100.0; src_process(src, &data); int actual_out = data.output_frames_gen;
自己写线性插值只适用于低要求场景
如果只是测试、调试或对音质无要求(比如 UI 音效预览),可以用双线性插值快速实现,但仅限于整数倍或接近整数倍的转换(如 44.1k → 22.05k),且必须先做抗混叠低通滤波——而手写滤波器极易出错。
常见错误现象:
- 没做预滤波 → 高频“嘶嘶”声明显,尤其在转高采样率时
- 插值索引越界 →
input[i]和input[i+1]中i+1超出数组长度 - 浮点累积误差导致采样点漂移 → 几秒后相位偏移,出现可闻的抖动
简化版线性逻辑(仅示意,不可用于生产):
float ratio = (float)out_rate / in_rate;
for (int i = 0; i < out_len; ++i) {
float src_pos = i / ratio;
int idx = (int)floor(src_pos);
float frac = src_pos - idx;
if (idx + 1 < in_len) {
out[i] = in[idx] * (1 - frac) + in[idx + 1] * frac;
} else {
out[i] = in[idx]; // 边界处理
}
}输入/输出格式与内存布局要注意什么
libsamplerate 默认处理 float 类型样本(范围 [-1.0, 1.0]),不支持 int16_t 直接传入。常见踩坑点:
- 从 WAV 读出的
int16_t必须先归一化:(float)sample / 32768.0f(注意是有符号,不是 32767) - 输出仍是
float,写回 WAV 前需反量化:(int16_t)roundf(sample * 32767.0f),并 clamp 到 [-32768, 32767] - 多声道要拆成单声道分别处理,
libsamplerate不支持 interleaved 数据(即 LRLR 格式),必须是 planar(LL... RR...) - 采样率转换本身不改变位深度或声道数,这些需单独处理
容易被忽略的是:重采样前后音频时长并不严格等于 len × in_rate / out_rate,因为滤波器引入了前导/尾部延迟(通常几毫秒),src_process() 返回的 output_frames_gen 才是真实有效样本数。


















