一、核心原理与架构
1.1 什么是音频重采样?
音频重采样是将音频信号从**源采样率(Fs_in)转换为目标采样率(Fs_out)**的过程。例如:将48kHz麦克风采集的语音转换为16kHz供语音识别算法使用。
1.2 重采样的数学本质
原始信号 x[n] (Fs_in) → 插值/抽取 → 新信号 y[m] (Fs_out)
1.3 两种重采样方式
| 类型 | 公式 | 特点 | 应用场景 |
|---|---|---|---|
| 整数倍重采样 | Fs_out = L × Fs_in | 简单高效 | MCU实时处理 |
| 分数倍重采样 | Fs_out = (L/M) × Fs_in | 灵活通用 | PC/嵌入式通用 |
二、STM32 嵌入式整数倍重采样(推荐)
2.1 系统架构
麦克风/音频输入
↓
抗混叠滤波器(FIR)
↓
插值(补零)
↓
低通滤波(FIR)
↓
抽取
↓
输出到DAC/算法
2.2 完整源码实现
(1)头文件(audio_resample.h)
c
/**
* @file audio_resample.h
* @brief STM32音频重采样库(整数倍)
*/
#ifndef __AUDIO_RESAMPLE_H
#define __AUDIO_RESAMPLE_H
#include <stdint.h>
#include <stdbool.h>
/* 重采样配置 */
#define RESAMPLE_MAX_FACTOR 8 // 最大插值因子
#define FIR_FILTER_TAPS 33 // FIR滤波器阶数(奇数)
#define AUDIO_BUFFER_SIZE 256 // 音频缓冲区大小
/* 重采样状态结构 */
typedef struct {
uint8_t interpolation; // 插值因子 L
uint8_t decimation; // 抽取因子 M
int16_t fir_coeff[FIR_FILTER_TAPS]; // FIR滤波器系数
int32_t history[FIR_FILTER_TAPS]; // 历史样本缓存
uint8_t history_index; // 历史缓存索引
uint32_t input_samples; // 输入样本计数
uint32_t output_samples; // 输出样本计数
bool initialized; // 初始化标志
} ResampleState;
/* 函数声明 */
void Resample_Init(ResampleState* rs, uint8_t L, uint8_t M);
int16_t Resample_Process(ResampleState* rs, int16_t input_sample);
void Resample_ProcessBlock(ResampleState* rs,
int16_t* input, uint16_t input_len,
int16_t* output, uint16_t* output_len);
void Resample_Reset(ResampleState* rs);
#endif /* __AUDIO_RESAMPLE_H */
(2)FIR滤波器系数生成(audio_resample.c)
c
/**
* @file audio_resample.c
* @brief 音频重采样实现(基于FIR滤波)
*/
#include "audio_resample.h"
#include <string.h>
#include <math.h>
/* 生成低通FIR滤波器系数(Hamming窗) */
static void GenerateFIRCoeff(int16_t* coeff, uint8_t taps, float cutoff)
{
float sum = 0.0f;
for (int i = 0; i < taps; i++) {
float n = (float)i - (taps - 1) / 2.0f;
float sinc = (n == 0) ? 1.0f : sinf(2.0f * M_PI * cutoff * n) / (2.0f * M_PI * cutoff * n);
float window = 0.54f - 0.46f * cosf(2.0f * M_PI * i / (taps - 1)); // Hamming窗
coeff[i] = (int16_t)(sinc * window * 32767.0f);
sum += coeff[i];
}
// 归一化
for (int i = 0; i < taps; i++) {
coeff[i] = (int16_t)((float)coeff[i] * 32767.0f / sum);
}
}
/* 初始化重采样器 */
void Resample_Init(ResampleState* rs, uint8_t L, uint8_t M)
{
if (rs == NULL || L == 0 || M == 0) return;
rs->interpolation = L;
rs->decimation = M;
// 生成FIR滤波器系数
float cutoff = 0.5f / (L > M ? L : M); // 截止频率
GenerateFIRCoeff(rs->fir_coeff, FIR_FILTER_TAPS, cutoff);
// 初始化历史缓存
memset(rs->history, 0, sizeof(rs->history));
rs->history_index = 0;
rs->input_samples = 0;
rs->output_samples = 0;
rs->initialized = true;
}
/* 单样本重采样处理 */
int16_t Resample_Process(ResampleState* rs, int16_t input_sample)
{
if (!rs->initialized) return 0;
int32_t accumulator = 0;
int16_t output_sample = 0;
// 1. 将输入样本存入历史缓存
rs->history[rs->history_index] = input_sample;
rs->history_index = (rs->history_index + 1) % FIR_FILTER_TAPS;
// 2. 插值(补零)
static uint8_t phase = 0;
if (phase == 0) {
// 3. FIR滤波
uint8_t hist_idx = rs->history_index;
for (int i = 0; i < FIR_FILTER_TAPS; i++) {
hist_idx = (hist_idx == 0) ? FIR_FILTER_TAPS - 1 : hist_idx - 1;
accumulator += (int32_t)rs->history[hist_idx] * rs->fir_coeff[i];
}
output_sample = (int16_t)(accumulator >> 15); // 右移15位恢复缩放
}
// 4. 抽取
phase++;
if (phase >= rs->interpolation) {
phase = 0;
}
rs->input_samples++;
if (phase == 0) {
rs->output_samples++;
return output_sample;
}
return 0; // 非输出时刻返回0
}
/* 块处理重采样 */
void Resample_ProcessBlock(ResampleState* rs,
int16_t* input, uint16_t input_len,
int16_t* output, uint16_t* output_len)
{
uint16_t out_idx = 0;
for (uint16_t i = 0; i < input_len; i++) {
int16_t sample = Resample_Process(rs, input[i]);
if (sample != 0) { // 非零表示有效输出
if (out_idx < *output_len) {
output[out_idx++] = sample;
}
}
}
*output_len = out_idx;
}
/* 重置重采样器状态 */
void Resample_Reset(ResampleState* rs)
{
memset(rs->history, 0, sizeof(rs->history));
rs->history_index = 0;
rs->input_samples = 0;
rs->output_samples = 0;
}
(3)主程序示例(main.c)
c
/**
* @file main.c
* @brief STM32音频重采样示例
*/
#include "stm32f10x.h"
#include "audio_resample.h"
#include "i2s.h"
#include "dma.h"
/* 全局变量 */
ResampleState resampler;
int16_t audio_input[AUDIO_BUFFER_SIZE];
int16_t audio_output[AUDIO_BUFFER_SIZE * 4]; // 4倍缓冲区
uint16_t output_len = 0;
int main(void)
{
/* 1. 系统初始化 */
System_Init();
I2S_Init();
DMA_Init();
/* 2. 初始化重采样器(48kHz → 16kHz,L=1, M=3) */
Resample_Init(&resampler, 1, 3); // 48/3 = 16kHz
/* 3. 主循环 */
while (1) {
/* 3.1 等待I2S接收完成 */
if (I2S_ReceiveComplete()) {
/* 3.2 获取音频数据 */
I2S_GetData(audio_input, AUDIO_BUFFER_SIZE);
/* 3.3 重采样处理 */
uint16_t max_output = AUDIO_BUFFER_SIZE * 4;
Resample_ProcessBlock(&resampler,
audio_input, AUDIO_BUFFER_SIZE,
audio_output, &max_output);
output_len = max_output;
/* 3.4 发送处理后的数据到语音识别算法 */
Speech_Process(audio_output, output_len);
}
}
}
三、PC/Linux 分数倍重采样(通用)
3.1 基于线性插值的轻量级实现
适合资源受限的系统,音质尚可。
c
/**
* @file linear_resample.c
* @brief 线性插值重采样(分数倍)
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
/* 线性插值重采样 */
uint32_t Linear_Resample(int16_t* input, uint32_t input_len,
int16_t* output, uint32_t output_capacity,
uint32_t fs_in, uint32_t fs_out)
{
if (fs_in == fs_out) {
uint32_t copy_len = (input_len < output_capacity) ? input_len : output_capacity;
memcpy(output, input, copy_len * sizeof(int16_t));
return copy_len;
}
double ratio = (double)fs_out / fs_in;
uint32_t output_len = (uint32_t)(input_len * ratio);
if (output_len > output_capacity) {
output_len = output_capacity;
}
for (uint32_t i = 0; i < output_len; i++) {
double src_index = i / ratio;
uint32_t index_low = (uint32_t)src_index;
uint32_t index_high = index_low + 1;
double frac = src_index - index_low;
if (index_high >= input_len) {
output[i] = input[index_low];
} else {
// 线性插值: y = y0 + (y1 - y0) * frac
output[i] = (int16_t)(input[index_low] +
(input[index_high] - input[index_low]) * frac);
}
}
return output_len;
}
/* 测试示例 */
int main()
{
int16_t input[10] = {0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000};
int16_t output[20];
uint32_t out_len = Linear_Resample(input, 10, output, 20, 48000, 16000);
printf("Resampled Output (%d samples):\n", out_len);
for (uint32_t i = 0; i < out_len; i++) {
printf("%d ", output[i]);
}
printf("\n");
return 0;
}
3.2 基于SpeexDSP的高质量重采样
这是工业界常用的高质量重采样库。
c
/**
* @file speex_resample.c
* @brief 使用SpeexDSP库的重采样示例
*/
#include <speex/speex_resampler.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FRAME_SIZE 160
int main()
{
SpeexResamplerState *resampler;
int err;
spx_uint32_t in_frames = FRAME_SIZE;
spx_uint32_t out_frames = FRAME_SIZE;
spx_uint32_t in_frames_used, out_frames_generated;
short in_buf[FRAME_SIZE];
short out_buf[FRAME_SIZE * 2]; // 缓冲区放大
// 1. 初始化重采样器(单声道,48kHz → 16kHz)
resampler = speex_resampler_init(1, 48000, 16000, 10, &err);
if (err != 0) {
printf("Resampler init failed: %d\n", err);
return -1;
}
// 2. 设置重采样质量(0-10,越高越好)
speex_resampler_set_quality(resampler, 10);
// 3. 处理音频数据
while (1) {
// 读取一帧音频数据
if (read_audio_frame(in_buf, FRAME_SIZE) <= 0) break;
// 执行重采样
speex_resampler_process_int(resampler, 0,
in_buf, &in_frames,
out_buf, &out_frames);
// 输出重采样后的数据
write_audio_frame(out_buf, out_frames);
}
// 4. 清理资源
speex_resampler_destroy(resampler);
return 0;
}
四、性能优化建议
4.1 算法优化
c
/* 定点运算优化(避免浮点) */
typedef struct {
int32_t fir_coeff_fixed[FIR_FILTER_TAPS]; // Q15格式
int64_t accumulator; // 64位累加器
} FastResampler;
/* SIMD指令优化(ARM NEON) */
#ifdef __ARM_NEON
#include <arm_neon.h>
void FIR_Filter_NEON(int16_t* input, int16_t* coeff, int16_t* output, int len)
{
int16x8_t in_vec, coeff_vec;
int32x4_t acc_low, acc_high;
for (int i = 0; i < len; i += 8) {
in_vec = vld1q_s16(input + i);
coeff_vec = vld1q_s16(coeff);
acc_low = vmull_s16(vget_low_s16(in_vec), vget_low_s16(coeff_vec));
acc_high = vmull_s16(vget_high_s16(in_vec), vget_high_s16(coeff_vec));
// ... 累加处理
}
}
#endif
4.2 内存优化
c
/* 环形缓冲区替代线性缓冲区 */
typedef struct {
int16_t buffer[AUDIO_BUFFER_SIZE];
uint16_t write_ptr;
uint16_t read_ptr;
uint16_t count;
} CircularBuffer;
/* 原地重采样(避免额外拷贝) */
void InPlace_Resample(int16_t* buffer, uint32_t len, uint32_t factor)
{
// 从后向前处理,避免覆盖未处理的数据
for (int i = len - 1; i >= 0; i--) {
buffer[i * factor] = buffer[i];
for (int j = 1; j < factor; j++) {
buffer[i * factor + j] = 0; // 插值零点
}
}
}
参考代码 音频重采样 www.youwenfan.com/contentcsu/56438.html
五、测试与验证
5.1 测试代码
c
/* 重采样质量测试 */
void Test_ResampleQuality(void)
{
int16_t sine_wave[1000];
int16_t resampled[3000];
uint32_t out_len;
// 生成1kHz正弦波(48kHz采样率)
for (int i = 0; i < 1000; i++) {
sine_wave[i] = (int16_t)(32767 * sin(2 * M_PI * 1000 * i / 48000.0));
}
// 重采样到16kHz
ResampleState rs;
Resample_Init(&rs, 1, 3); // 48kHz → 16kHz
Resample_ProcessBlock(&rs, sine_wave, 1000, resampled, &out_len);
printf("Input samples: 1000, Output samples: %d\n", out_len);
// 验证频率是否正确
// 理论上输出应该是333个样本的1kHz正弦波
}
5.2 性能基准
| 算法 | 复杂度 | 延迟 | 音质 | 适用场景 |
|---|---|---|---|---|
| 最近邻 | O(n) | 0 | 差 | 快速原型 |
| 线性插值 | O(n) | 低 | 中 | 嵌入式实时 |
| FIR滤波 | O(n×taps) | 中 | 好 | 通用音频 |
| SpeexDSP | O(n×log n) | 高 | 优 | 专业音频 |
六、常见问题解决
6.1 抗混叠处理
c
/* 重采样前必须加抗混叠滤波器 */
void AntiAlias_Filter(int16_t* input, uint32_t len, uint32_t fs_out)
{
// 截止频率设为 fs_out/2
float cutoff = fs_out / 2.0f;
// 使用FIR或IIR滤波器
}
6.2 相位失真
c
/* 使用线性相位FIR滤波器避免相位失真 */
#define FIR_PHASE_LINEAR 1
#define FIR_PHASE_MINIMUM 0
6.3 缓冲区溢出
c
/* 计算安全的输出缓冲区大小 */
uint32_t Safe_Output_Size(uint32_t input_len, uint32_t fs_in, uint32_t fs_out)
{
return (uint32_t)ceil((double)input_len * fs_out / fs_in) + 10;
}