1. 基于VAD的静音段检测与丢弃
任务说明
在嵌入式语音前端处理中,静音段占据通话时长比例较高,若对静音帧继续做回声消除或降噪,会浪费算力并可能引入噪声。本案例实现一个轻量级VAD(语音活动检测)模块,基于短时能量与过零率双门限判决,对16kHz单声道PCM输入流逐帧检测,并将静音帧丢弃,仅输出语音帧。
算法设计
每帧长度20ms(320个采样点)。计算帧内短时能量E与过零率ZCR,判决规则如下:
- 若E > E_high 或 (E > E_low 且 ZCR < ZCR_max),判为语音帧;
- 否则判为静音帧并丢弃。
其中E_high=5000,E_low=1000,ZCR_max=0.3(归一化过零率)。该门限适用于16bit PCM、幅值范围[-32768,32767]的近距离语音场景。
代码实现
// vad_drop.c
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#define FRAME_SIZE 320 // 20ms @16kHz
#define E_HIGH 5000.0f
#define E_LOW 1000.0f
#define ZCR_MAX 0.3f
// 计算帧能量(均方和)
static float frame_energy(const int16_t *pcm, int n) {
float sum = 0.0f;
for (int i = 0; i < n; i++) {
sum += (float)pcm[i] * pcm[i];
}
return sum / n;
}
// 计算归一化过零率(每样本平均过零次数)
static float frame_zcr(const int16_t *pcm, int n) {
int crossings = 0;
for (int i = 1; i < n; i++) {
if ((pcm[i-1] >= 0 && pcm[i] < 0) || (pcm[i-1] < 0 && pcm[i] >= 0))
crossings++;
}
return (float)crossings / n;
}
// VAD判决:返回1=语音帧,0=静音帧
static int vad_decision(const int16_t *frame) {
float energy = frame_energy(frame, FRAME_SIZE);
float zcr = frame_zcr(frame, FRAME_SIZE);
if (energy > E_HIGH) return 1;
if (energy > E_LOW && zcr < ZCR_MAX) return 1;
return 0;
}
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage: %s input.pcm output.pcm\n", argv[0]);
return 1;
}
FILE *fin = fopen(argv[1], "rb");
FILE *fout = fopen(argv[2], "wb");
if (!fin || !fout) {
fprintf(stderr, "File open failed\n");
return 1;
}
int16_t frame[FRAME_SIZE];
size_t read_cnt;
int total_frames = 0, speech_frames = 0;
while ((read_cnt = fread(frame, sizeof(int16_t), FRAME_SIZE, fin)) == FRAME_SIZE) {
total_frames++;
if (vad_decision(frame)) {
fwrite(frame, sizeof(int16_t), FRAME_SIZE, fout);
speech_frames++;
}
}
printf("Total frames: %d, Speech frames: %d, Dropped: %d\n",
total_frames, speech_frames, total_frames - speech_frames);
fclose(fin);
fclose(fout);
return 0;
}
编译与运行
gcc -o vad_drop vad_drop.c -lm
./vad_drop input.pcm output.pcm
验证方法
准备一段包含语音与静音交替的16kHz、16bit单声道PCM文件(如录制“说话-停顿-说话”约5秒)。运行程序后观察终端输出:总帧数应等于PCM总时长除以20ms;语音帧数应明显小于总帧数。将输出PCM文件用音频工具(如Audacity)以16kHz/16bit/mono导入,可听到静音段被去除,语音段完整连续。判定标准:输出文件时长比输入短,且语音内容无丢失、无额外噪声插入。
验证观测点
上图展示了处理流程:输入PCM流按帧进入VAD判决模块,静音帧(红色虚线路径)被丢弃,语音帧(绿色实线路径)写入输出文件。终端打印的统计信息可直接验证丢弃效果。