B.1 升采样:从低速到高速
一段语音用 8 kHz 采样保存,每秒 8000 个样本。播放设备要求 16 kHz,每秒要读 16000 个样本。语音内容没变,问题是样本点不够——设备一秒钟想要 16000 个数,文件里只有 8000 个。
最容易想到的办法,是在两个旧样本之间补一个新样本。真正的问题也在这里:补什么?
先试最省事的办法:补零。
B.1.1 插零操作
Section titled “B.1.1 插零操作”假设原始序列只有 8 个样本:
做 2 倍升采样,在每两个样本之间插一个零:
这个操作叫插零(zero-stuffing)。序列长度变成原来的 2 倍,采样间隔从 变成了 。如果用一般的 倍升采样表示,就是
对 ,按 记下标,原来的样本落在 这些偶数位置,新插入的点在中间。MATLAB 下标从 1 开始,写程序时对应到第 1、3、5、… 个元素。

插零确实把采样率翻倍了,但结果还没法用。把插零后的序列当波形看,每个原始样本后面都突然掉到 0,再从 0 跳到下一个样本。放在语音里,多出来一些高频毛刺;放在图像里,类比放大后边缘很生硬。插零只是把新采样点的位置排出来了,新位置上应该是什么合理的数值,它还没给。
B.1.2 插零后的频谱镜像
Section titled “B.1.2 插零后的频谱镜像”时域里的跳变,对应频域里的高频成分。把插零前后的序列做 DTFT,可以看到:原来的频谱被压到低频区域,同时高频区域出现了一份额外的复制品。
对 2 倍升采样,频谱关系是
把插零定义代入 DTFT, 来自下标换元:
第二个等号只保留偶数位置。奇数位置上的 都是 0,对求和没有贡献;偶数位置写成 ,并且 。指数项随之变成 ,于是得到 。
关键在自变量 。当 从 走到 时, 正好走完整个 ,原来占满 的频谱就被压缩到了 。离散时间频谱以 为周期,压缩后相邻周期的副本也会落进新的 范围内。这个副本就叫镜像频谱(image)。

还是拿 8 kHz 语音升到 16 kHz 来看。原信号在 8 kHz 采样率下无歧义频率范围是 ,升到 16 kHz 后新的无歧义范围变成 。原有内容仍然只占低频那一块;插零带来的镜像,会落到新的高频区域。
时域里的毛刺和频域里的高频镜像说的是同一件事。要让波形平滑,就要把这些镜像去掉。
B.1.3 低通滤波去除镜像
Section titled “B.1.3 低通滤波去除镜像”去除镜像用低通滤波器。它保留低频里的原始信息,压掉高频里的镜像。
对 2 倍升采样,原始频谱被压缩到 ,镜像从 附近开始出现。理想低通滤波器的截止频率取
倍升采样时,原始频谱压缩到 ,截止频率对应变成

滤波器通带增益要取 。用常数序列就能看出来为什么。原来全是 1 的序列,2 倍插零后变成
平均值变成了原来的一半。低通滤波把它平滑成常数时,如果通带增益还是 1,结果会接近 ;要恢复到原来的幅度,就要乘以 2。 倍升采样的道理一样,插值低通的通带增益取 。
经过低通滤波后,插入位置就不再是零了,而是由邻近样本共同决定的平滑值。时域上是在做插值,频域上是在去镜像,两种说法指的是同一个操作。

表B.1-1:不同升采样倍数对应的滤波器参数
| 升采样倍数 | 截止频率 | 通带增益 |
|---|---|---|
| 2 | 2 | |
| 3 | 3 | |
| 4 | 4 |
截止频率不是越高越好,它要卡在原始频谱和镜像之间。放得太高,镜像会漏出来;放得太低,原始信号本来有用的高频内容也会被削掉。
B.1.4 完整升采样系统
Section titled “B.1.4 完整升采样系统”把 8 kHz 语音升到 16 kHz,完整流程是

升采样倍数为
先在每两个样本之间插一个零:
| 原始 | 插零后 |
|---|---|
[x[0], x[1], x[2], x[3], ...] | [x[0], 0, x[1], 0, x[2], 0, x[3], 0, ...] |
再设计低通滤波器:截止频率 ,通带增益 2。滤波后,新插入位置得到合理的中间值,输出序列的采样率就是 16 kHz。
下面是一个 2 倍升采样的 MATLAB 实现。fir1 里的截止频率按 Nyquist 频率归一化,cutoff = 1/L 对应数字频率 。函数假设输入是单通道序列;如果输入是列向量,输出也保持为列向量。
function y = upsample_2x(x) L = 2; wasColumn = iscolumn(x);
x = x(:).'; x_upsampled = zeros(1, L * length(x)); x_upsampled(1:L:end) = x;
numtaps = 51; cutoff = 1 / L; h = fir1(numtaps - 1, cutoff); h = L * h;
y = conv(x_upsampled, h, 'same');
if wasColumn y = y.'; endend两个步骤对应代码里的两行:x_upsampled(1:L:end) = x 完成插零,conv(x_upsampled, h, 'same') 完成低通滤波。取 51 点 FIR 是为了得到奇数长度的线性相位滤波器,群延迟是 个样本。numtaps 越大,过渡带越窄,镜像压制越好,计算量和延迟也跟着涨。'same' 让输出长度保持为插零后的长度。实际处理音频时还要补偿群延迟,开头和结尾的边界样本也要留意。
工程里也可以用 interp 或 resample。这里手写是为了把两个动作拆开看清楚:插零把采样位置扩展出来,低通滤波去掉镜像并补出中间值。频域上去掉的是镜像,时域上得到的是插值后的平滑波形。