首先來認識一下什么是音頻信號
通過matlab我們可以直接讀入一個音頻文件,同時直接繪圖
但是此時的橫坐標和縱坐標是什么呢?
>> [y,fs] = audioread('5num.wav');
>> plot(y)
不斷放大信號:
得到了這樣一幅圖
這時候縱坐標我們可以理解成幅度,但是橫坐標其實什么也不是
或者說就是一系列點而已,我們可以對這些點進行一定的組合,比如每160個點作為一組,這就是分幀
那頻率呢?
matlab在讀取一個音頻的時候還會返回一個頻率呀,這個頻率,也就是那個fs,到底是干嘛的,有什么意義呢?
不妨來計算一下:
我的這段音頻是29s,
>> length(y)/fs
ans =
29.4400
此時用點的個數除以頻率fs,得到的就是時間!
這樣想,頻率的物理意義就是一秒鐘振動的次數,8khz就是一秒鐘要震動8000次,一個點震動一次,那么235520個點就要震動29.4400s!
同時可以知道這段音頻是16位量化的
可是我們老是說量化量化,量化究竟代表什么含義呢?
其實就理解成每一個點用16個bit來表示
那么,235520個點,每一個點都是16位的,相當于一個點就是2byte
那么就是 235520*2 字節
等價于460KB
查看文件大小
正好是460KB!!
現在理解這幾個參數的含義了吧
于是上面那幅圖我們需要改進一下
>>t = (0:length(y)-1)/fs;
>> plot(t,y);
>> xlabel('時間(.sec)')
>> ylabel('幅度')
這樣就能得到每一個時刻對應的幅度了
分幀?
到底什么是分幀呢?
首先需要確定分幀的長度:
比如采樣率是
11025
語音信號每20ms分成一段
怎么想,一秒鐘有11025個點震動,那么20ms內就是大概220個點了,于是幀長為220,幀移默認就是一半110
問:為什么分幀?
答:語音信號是瞬時變化的,但在10~20ms內是相對穩定的,即在一小段內是相對穩定的,分幀就是將語音分成一段一段的。
問:問什么要有幀移呢?
答:幀移后的每一幀信號都有上一幀的成分,防止兩幀之間的不連續。語音信號雖然短時可以認為平穩,但是由于人說話并不是間斷的,每幀之間都是相關的,加上幀移可以更好地與實際的語音相接近。
如果采樣率都是8000hz,并且20ms為一幀
相當于160個點為一個幀
但是調用enframe
函數返回的結果又是什么呢?
5885*160的矩陣
每一行,有160列
每一列,有5885行
你想啊,我是每160個點為一個幀,那么總共有多少個幀呢?
這個不難算
那么我可以單獨取出其中的一個幀來看
a = X(2000,:);
plot(a)
當然這里只是知道有160個點,但是對應的時間段卻是不知道的
也就是說每一行其實就是一個幀的數據了
之所以這張圖有點奇怪
橫坐標代表第多少幀,縱坐標代表振幅
因為有很多根曲線是重疊的
問題在于分幀之后,如何求自相關系數等
可不可以每一個幀求一次自相關系數呢?
補充一點,調用
enframe
函數如果不指定第三個參數那么幀之間就不會重疊的
如何求自相關系數?
首先試如何理解自相關系數
比如:
>> A = [1 2 3]
A =
1 2 3
>> xcorr(A)
ans =
3.0000 8.0000 14.0000 8.0000 3.0000
>>
這個口算應該沒問題的
就是求出A的自相關系數
但是這個呢?
>> xcorr(A,4)
ans =
1 至 7 列
0 0 3.0000 8.0000 14.0000 8.0000 3.0000
8 至 9 列
0 0
是不是可以理解成延展了
按照規律來說是沒問題的,xcorr(A,4) 相當于兩個5列的向量
得到的就是2*5 - 1= 9個
解讀代碼
先把代碼放上來吧,方便你們copy
[y,fs] = audioread('5num.wav');
N = length(y);
t = (1:N-1)/fs; %生成時間序列
win = hamming(N) %加窗
y = y.*win;
X = enframe(y,160); % 160個點為一幀,就沒要重疊了
% 然后就可以查看某個幀了
%%
% 求出自相關
num_frame = length(X);
ms2 = floor(fs/500);
ms20 = floor(fs/50);
F0 = zeros(num_frame,1);
% 直接對每一幀進行循環
for i=1:num_frame
% 每一幀都可以求出自相關的系數
r = xcorr(X(i,:), 160);
r = r(floor(length(r)/2):end);
[maxi,idx]=max(r(ms2:ms20));
F0(i) = fs/(ms2+idx-1);
end
figure(1);
plot(F0); %橫坐標代表幀
xlabel('幀')
ylabel('基音頻率')
% 經過探測我讀0時候的基音位于第210幀
%% 選出第210幀的數據
figure(2)
y210 = X(211,:);
subplot(311);
plot(y210);
xlabel('點');
ylabel('振幅');
title('210幀原始信號')
ai = lpc(y210, 10);
est_x=filter([0 -ai(2:end)],1,y210);%估計信號
subplot(312);
plot(est_x);
xlabel('點');
ylabel('振幅');
title('210幀估計信號');
err = y210-est_x;
subplot(313);
plot(err);
xlabel('點');
ylabel('振幅');
title('預測誤差');
%%
% 求預測增益
En=zeros(1,160);
for i=1:160
u=err(i);%取出一樣點
u2=u.*u;%求出能量
En(i)=sum(u2);%對每一樣點累加求和
end
%計算原始能量
En1=zeros(1,160);
for i=1:160
u=y210(i);%取出一樣點
u2=u.*u;%求出能量
En1(i)=sum(u2);%對每一樣點累加求和
end
CA=zeros(1,160);
for i=1:160
en1=En1(i);%取出一樣點
en=En(i);
CA(i)=abs(en1)/abs(en);
end
figure(3);
subplot(311);plot(En1);xlabel('取樣點數/個');ylabel('En');title('短時能量');title('E0');
subplot(312);plot(En);xlabel('取樣點數/個');ylabel('En');title('短時能量');title('Ep');
subplot(313);plot(CA);xlabel('取樣點數/個');ylabel('En');title('短時能量');title('預測增益');
1. 加窗分幀
[y,fs] = audioread('5num.wav');
N = length(y);
t = (1:N-1)/fs; %生成時間序列
win = hamming(N) %加窗
y = y.*win;
X = enframe(y,160); % 160個點為一幀,就沒要重疊了
其實加窗的過程也不是很難理解,就是生成一個函數然后去乘就好了
然后通過enframe
函數進行分幀處理
返回的是一個矩陣,行數代表了幀數,列數代表了每一幀有多少個點
2. 求自相關
num_frame = length(X);
ms2 = floor(fs/500);
ms20 = floor(fs/50);
F0 = zeros(num_frame,1);
% 直接對每一幀進行循環
for i=1:num_frame
% 每一幀都可以求出自相關的系數
r = xcorr(X(i,:), 160);
r = r(floor(length(r)/2):end);
[maxi,idx]=max(r(ms2:ms20));
F0(i) = fs/(ms2+idx-1);
end
這個過程也不是很難理解
得到的圖形是這樣子的
橫坐標就是每一個幀,縱坐標代表對應的基音頻率
其實這個圖確實有點奇怪,因為它是倒過來的
我后來仔細想了想,周圍沒有聲音的時候,基音頻率反而是最大的
然后又去翻書,發現人的聲音基音頻率大概也就是200~500hz左右,而且男聲本來就低,所以這個圖顯然是沒問題的
而且我總共只讀了5個音,0 1 2 3 4
基本正確地反映出來了
然后通過肉眼觀察,嗯,我讀0的那個幀就是210幀了
3. 通過LPC預測信號,同時計算增益
預測增益的方法就是LPC
增益系數我暫時不太清楚怎么求,就直接抄同學的了