模拟信号和离散信号(数字信号)
在音频领域的模拟信号是指存在于自然界中的原始音频,有2个连续指标——时间连续、幅值连续。
数字信号是指对音频进行采样后,在计算机中通过离散信号来代表原始的模拟信号。关于数字信号、采样,数字信号和模拟信号的关系具体可参见以下这篇文章所表述的。
重采样介绍
音频的重采样一般可用于DSP等数字信号处理领域,也就是对数字信号进行处理。比如将原本的48k采样率的原信号,通过重采样(降采样)改为44.1k。即:将原本每1秒内48000个点的离散信号,改为44100个点的离散信号。
如下图,蓝色“□”表示原采样率时,在0~10时间里面输出11个点。
而如果在需要降低采样率时,如红色“○”,需要在0~6,总共7个点内,需要减少输出点数,实际只需输出6个点。所以需要步长>1的降采样。
同理升采样,如橙色“*”,在0~4,总共5个点内,需要增加输出点数,实际需要输出6个点。所以需要步长<1的升采样。
通过这样的方式改变一定时间内的输出点数(采样点),从而达到改变采样率的目的。
线性插值
按照以上所表述的改变一定时间内的输出点数,可通过线性插值的方式达到。以下介绍线性插值。
设原数据波形为{0, 1, 2, 3, 4, 5,……},现要根据步长=0.8进行升采样,此时,此时可在运用以下线性插值方式进行数据计算:
计算公式可以理解为:y=(y1-y0)*fre + y0;y1=第二个点,y0=第一个点,fre=(x-x0)/(x1-x0)。
通过计算输出,输出前5个点的数据依次为{0, 0.8, 1.6, 2.4, 3.2, 4……}在原4个点输出了5个点。
MATLAB验证
以下MATLAB代码,把一个立体声(一个L,一个R来存放,因为Usb Audio播放时,存放就是一L一R存放的)的原始wav通过重采样,计算出不同步长下的wav,可直接在matlab 2016a中运行验证。
可通过修改重采样步长查看验证结果:
clf;clear;
x=0:1:100; %源数据长度
step = 1.2; %冲采样步长
y=sin(2*pi*x/48); %生成正弦波1kHz
subplot(411);plot(x,y);xlabel('单声道正弦波——原波形');axis([0,100,-1.1,1.1]);
ySoc = zeros(1,length(x)*2);
for i=0:length(x)-1
ySoc(i*2+1) = y(i+1); %把一个正弦波改为立体声
ySoc(i*2+2) = y(i+1);
end
subplot(412);plot(ySoc);xlabel('一左、一右声道交替放置正弦波——原波形');axis([0,200,-1.1,1.1]);
yOutL = zeros(1,length(x));
yOutR = zeros(1,length(x));
readPt = 0;
yOutL_Count = 1;
yOutR_Count = 1;
yLs0 = ySoc(1);
yRs0 = ySoc(2);
%% 循环计算
for i=0:round(length(ySoc)/4)-2
%% s0,s1 calc.
yLs1 = ySoc(3+i*4);
yRs1 = ySoc(4+i*4);
readPt = readPt + step;
if((floor(readPt + step) - floor(readPt)) == 1)
%Nomal
yOutL(yOutL_Count) = (yLs1 - yLs0)*(readPt-floor(readPt)) + yLs0;
yOutL_Count = yOutL_Count+1;
yOutR(yOutR_Count) = (yRs1 - yRs0)*(readPt-floor(readPt)) + yRs0;
yOutR_Count = yOutR_Count+1;
elseif((floor(readPt + step) - floor(readPt)) == 0)
%step < 1. need repeat 1 Point socrice data.
yOutL(yOutL_Count) = (yLs1 - yLs0)*(readPt-floor(readPt)) + yLs0;
yOutL_Count = yOutL_Count+1;
yOutR(yOutR_Count) = (yRs1 - yRs0)*(readPt-floor(readPt)) + yRs0;
yOutR_Count = yOutR_Count+1;
readPt = readPt + step;
yOutL(yOutL_Count) = (yLs1 - yLs0)*(readPt-floor(readPt)) + yLs0;
yOutL_Count = yOutL_Count+1;
yOutR(yOutR_Count) = (yRs1 - yRs0)*(readPt-floor(readPt)) + yRs0;
yOutR_Count = yOutR_Count+1;
elseif((floor(readPt + step) - floor(readPt)) == 2)
%step > 1. need jump 1 piont source data.
end
%% s1,s2 calc.
yLs2 = ySoc(5+i*4);
yRs2 = ySoc(6+i*4);
readPt = readPt + step;
if((floor(readPt + step) - floor(readPt)) == 1)
%Nomal
yOutL(yOutL_Count) = (yLs2 - yLs1)*(readPt-floor(readPt)) + yLs1;
yOutL_Count = yOutL_Count+1;
yOutR(yOutR_Count) = (yRs2 - yRs1)*(readPt-floor(readPt)) + yRs1;
yOutR_Count = yOutR_Count+1;
elseif((floor(readPt + step) - floor(readPt)) == 0)
%step < 1. need repeat 1 Point socrice data.
yOutL(yOutL_Count) = (yLs2 - yLs1)*(readPt-floor(readPt)) + yLs1;
yOutL_Count = yOutL_Count+1;
yOutR(yOutR_Count) = (yRs2 - yRs1)*(readPt-floor(readPt)) + yRs1;
yOutR_Count = yOutR_Count+1;
readPt = readPt + step;
yOutL(yOutL_Count) = (yLs2 - yLs1)*(readPt-floor(readPt)) + yLs1;
yOutL_Count = yOutL_Count+1;
yOutR(yOutR_Count) = (yRs2 - yRs1)*(readPt-floor(readPt)) + yRs1;
yOutR_Count = yOutR_Count+1;
elseif((floor(readPt + step) - floor(readPt)) == 2)
%step > 1. need jump 1 piont source data.
end
yLs0=yLs2; %更新s0
yRs0=yRs2;
end
subplot(413);plot(yOutL);xlabel('冲采样后 L 声道正弦波');axis([0,100,-1.1,1.1]);
subplot(414);plot(yOutR);xlabel('冲采样后 R 声道正弦波');axis([0,100,-1.1,1.1]);