电子科技大学数字信号处理DSP课程设计钢琴音符识别Word文档下载推荐.docx
《电子科技大学数字信号处理DSP课程设计钢琴音符识别Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《电子科技大学数字信号处理DSP课程设计钢琴音符识别Word文档下载推荐.docx(21页珍藏版)》请在冰点文库上搜索。
2.计算每一帧的谱能量;
3.计算出每一帧中每个样本点的概率密度函数
;
4.计算出每一帧的谱熵值
(由信息论知识知道,熵值在自变量服从均匀分布的时候,熵值达到最大值,所以噪声的熵值是比较大的,而钢琴音符的熵值是比较小的,由此区别了噪声和音符);
5.设置判决门限;
6.根据各帧的谱熵值进行端点检测。
在实验过程中发现:
依然存在当语音信号频率分布较广时,阀值不太好控制的问题。
因此对该方法进行改进,引入,能熵比的概念:
谱熵值类似于过零率,能熵比的表示为
。
由于噪声和信号的能熵比差别很大。
因此在能熵比的图像中,每一个“尖刺”就代表了一个特定频率的语言信号。
图2:
能熵比图中的“尖刺”
在检测过程中,依然不能通过简单的设置阀值的办法来进行端点检测,原因是语音频率分布较广时,每个音符的能熵比变化范围差别较大,如下图所示,有的“尖刺”完全在门限之上,而有的则完全在门限之下。
图3:
88阶全音的能熵比图
因此,采用检测能熵比中的“低谷点”(该点比左右两边的一定数目的点的能熵比都小)的方法。
语音信号一定位于两个低谷点之间的部分,再对低谷点进行适当的左右移动作为语音信号的起止点。
如下图所示:
图4:
标记起止点的能熵比图
(绿色为起始点,红色为截止点)
(3)设计框架和流程:
1.用audioread函数读入钢琴音乐,并用sound函数播放;
2.为了方便处理,对信号以11.025kHz的频率进行重新采样,并统一转换成单声道的信号;
3.因为语言信号可以在短时间内认为是平稳的,因此对语音信号进行分帧的处理,设置帧长320,为了减小误差,两帧之间设置重叠部分,因此帧移取80;
4.计算每一帧的能熵比;
5.找到能熵比中的“低谷点”(该点比左右两边的一定数目的点的能熵比都小);
6.如果两个低谷点之间的距离大于miniL(认为持续长度超过一定长度的为音符,最小长度miniL可自行设置)。
则低谷点右移sr(即shiftright,数值可自行调节)帧作为一段信号的起始点,将低谷点左移sl(即shiftleft,数值可自行调节)帧作为截止点[注:
采用该方法的优点是通过调节相关参数能适应多种情况,缺点是检测环境发生较大变化时,需要重新设置参数];
7.将找到的语音段转换成未分帧时对应坐标的语音段,并对每段做快速傅里叶变换;
8.找到每段快速傅里叶变换中的最大值以及最大值所对应的横坐标(fft点),将横坐标转换成相应的频率,得到的频率即为该段音符的频率;
9.利用比值法进行频率的校正,窗函数选择矩形窗;
10.根据检测到的频率确定音符,计算公式为:
,
为第几个按键,再通过查表得到对应音符;
11.分析结果。
三.具体设计过程:
(1)部分代码(测试部分缺省):
主函数部分:
[x,fs]=audioread('
钢琴音频.WAV'
);
formatshort;
wlen=320;
inc=80;
%分帧的帧长和帧移
overlap=wlen-inc;
%帧之间的重叠部分
sound(x,fs);
%播放音乐
x=calsample(x,fs);
%为了方便处理,重新以11025Hz的频率采样,并转换成单声道
x=x-mean(x);
%消去直流分量
x=x/max(abs(x));
%幅值归一化
y=Enframe(wlen,inc,x)'
;
%分帧
fn=size(y,2);
%取得帧数
time=(0:
length(x)-1)/11025;
%计算时间坐标
frameTime=frame2time(fn,wlen,inc,11025);
%计算各帧对应的时间坐标
sr=2;
sl=13;
miniL=33;
%配置左右移动的帧数和要求的最短帧数
[voicesegment,vos,Ef]=get_segment(y,fn,sr,sl,miniL);
%获得语音段
[real_f,ft,ax]=get_f(x,voicesegment,vos,wlen,inc);
%检测频率的结果
fori=1:
length(real_f)
real_f(i)=roundn(real_f(i),-4);
end
real_node(i)=get_node(real_f(i))
end
%**********************************绘图部分********************************
subplot211;
stem(real_f);
title('
频率检测结果'
xlabel('
音符/个'
ylabel('
频率/Hz'
subplot212;
stem(real_node,'
r'
音符检测结果'
对应按键'
figure
(2);
plot(Ef);
能熵比图及语音起止点'
帧数/个'
能熵比'
length(voicesegment)
text(voicesegment(i).begin,Ef(voicesegment(i).begin),'
o'
'
color'
g'
)
text(voicesegment(i).end,Ef(voicesegment(i).end),'
subplot212,plot(time,x,'
k'
title('
语音信号端点检测结果'
axis([0max(time)-11]);
ylabel('
幅值'
fork=1:
vos%标出有话段
nx1=voicesegment(k).begin;
nx2=voicesegment(k).end;
nxl=voicesegment(k).duration;
fprintf('
%4d%4d%4d%4d\n'
k,nx1,nx2,nxl);
subplot212
line([frameTime(nx1)frameTime(nx1)],[-11],'
linestyle'
-'
line([frameTime(nx2)frameTime(nx2)],[-11],'
--'
其中的用到的子函数:
1.calsample.m(调整采样率和声道)
functionsample=calsample(sampledata,FS)
temp_sample=resample(sampledata,1,FS/11025);
%调整采样频率
[~,n]=size(temp_sample);
if(n==2)%转换成单声道
sample=temp_sample(:
1);
else
sample=temp_sample;
2.Enframe.m(分帧函数)
functionf=Enframe(len,inc,x)%对读入的语音进行分帧,len为帧长,
%inc为帧重叠样点数,x为输入语音数据
fh=fix(((size(x,1)-len)/inc)+1);
%计算帧数
f=zeros(fh,len);
%设置一个零矩阵,行为帧数,列为帧长
i=1;
n=1;
whilei<
=fh%帧间循环
j=1;
whilej<
=len%帧内循环
f(i,j)=x(n);
j=j+1;
n=n+1;
end
n=n-len+inc;
%下一帧开始位置
i=i+1;
3.frame2time.m(坐标刻度转换)
functionframeTime=frame2time(frameNum,framelen,inc,fs)
frameTime=(((1:
frameNum)-1)*inc+framelen/2)/fs;
%求对应的时间坐标
4.get_segment.m(端点检测,确定音符段)
function[voicesegment,vos,Ef]=get_segment(y,fn,sr,sl,miniL)
ifsize(y,2)~=fn,y=y'
end%把y转换为每列数据表示一帧语音信号
wlen=size(y,1);
%取得帧长
fn
Sp=abs(fft(y(:
i)));
%FFT取幅值
Sp=Sp(1:
wlen/2+1);
%只取正频率部分
Esum(i)=sum(Sp.*Sp);
%计算能量值
prob=Sp/(sum(Sp));
%计算概率
H(i)=-sum(prob.*log(prob+eps));
%求谱熵值
hindex=find(H<
0.1);
H(hindex)=max(H);
Ef=sqrt(1+abs(Esum./H));
%计算能熵比
Ef=Ef/max(Ef);
%归一化
[x1,y1]=get_max(Ef);
%找到能熵比中的“高峰点”
[x2,y2]=get_min(Ef);
%找到能熵比中的“低估点”
voicesegment
(1).begin=x1
(1)-8;
%由于仅仅靠低谷点无法检测出第一个音符起始位置,因此将高峰点的第一个值左移8帧作为第一个音符的起始点
voicesegment
(1).end=x2
(1)-sl;
%将第一个低谷点作为第一个音符的截止点
voicesegment
(1).duration=voicesegment
(1).end-voicesegment
(1).begin+1;
%一个音符的持续帧数
j=1;
fork=2:
length(x2)%将找到的低谷点作为音符的起止点
ifx2(k)-x2(k-1)>
=miniL%剔除持续帧长度小于miniL的音符段
temp1=x2(k-1)+sr;
%将低谷点右移sr个帧,作为一个音符的起始点
voicesegment(j).begin=temp1;
temp2=x2(k)-sl;
%将低谷点左移sl个帧,作为一个音符的截止点
voicesegment(j).end=temp2;
voicesegment(j).duration=voicesegment(j).end-voicesegment(j).begin+1;
%音符持续帧数
vos=length(voicesegment);
%返回音符个数
5.get_max.m(找到高峰点)
function[max_x,max]=get_max(x)
l=length(x);
%获得数组的长度
max=[];
max_x=[];
fori=20:
l-15%找到“峰值点”
if(x(i)>
x(i-1))&
&
(x(i)>
x(i-2))&
x(i-3))&
x(i-4))&
x(i-5))...
&
x(i-6))&
x(i-7))&
x(i-8))&
x(i-9))&
x(i-10))...
x(i-11))&
x(i-12))&
x(i-13))&
x(i-14))...
x(i-15))&
x(i-16))&
x(i-17))...
x(i+1))&
x(i+2))&
x(i+3))&
x(i+4))&
x(i+5))...
x(i+6))&
x(i+7))&
x(i+8))&
x(i+9))&
x(i+10))...
x(i+11))&
x(i+12))&
x(i+13))&
x(i+14))...
x(i+15))&
x(i+16))...
0.1)
max_x(j)=i;
%找到后赋值给返回参数
max(j)=x(i);
else
6.get_min.m(找到低谷点)
function[min_x,min]=get_min(x)
min=[];
min_x=[];
fori=100:
l-10%寻找低谷点
if(x(i)<
(x(i)<
x(i-17))&
x(i-18))&
x(i-19))...
x(i-20))&
x(i-21))&
x(i-22))&
x(i-23))&
x(i-24))...
x(i-25))&
x(i-26))&
x(i-27))&
x(i-28))&
x(i-29))...
x(i-30))&
x(i-31))&
x(i-32))&
x(i-33))&
x(i-34))......
min_x(j)=i;
%找到后赋值给返回值
min(j)=x(i);
7.get_f.m(确定频率)
function[real_f,ft,ax]=get_f(x,voicesegment,vos,wlen,inc)
vos%横坐标转化,将起止点坐标的单位由帧转换成点
ax(i).begin=(voicesegment(i).begin-1)*inc+1;
ax(i).end=(voicesegment(i).end-1)*inc+wlen;
ax(i).duration=ax(i).end-ax(i).begin+1;
temp=x(ax(i).begin:
ax(i).end);
%获得语音段
ft{i}=fft(temp);
%做快速傅里叶变换,并将结果保存在元胞矩阵中
ft{i}=abs(ft{i});
%将得到的fft去模值
[fm(i),fm_x(i)]=max(ft{i});
%找到每个语音段对应的fft中的最大值及最大值对应的横坐标
real_f1(i)=fm_x(i)*11025./ax(i).duration;
%进行频率转换,将横坐标乘以分辨率得到真实频率
real_f(i)=Specorrm(x(ax(i).begin:
ax(i).end),11025,ax(i).duration,real_f1(i)-10,real_f1(i)+10);
%利用比值法进行频率修正,窗函数选择矩形窗
8.Specorrm.m(频率校正)
functionZ=Specorrm(x,fs,N,nx1,nx2)
%x是被测信号,fs是采样频率N为FFT的长度,nx1和nx2被测信号频率的区间,nx2>
nx1
[nx,mx]=size(x);
ifmx==1,x=x'
end%转换成行矩阵
M=fix(N/2)+mod(N,2);
xf=fft(x);
%xf=xf(1:
M)*2/N;
ddf=fs/N;
%频率分辨率
n1=fix(nx1/ddf);
%将频率转换成fft对应的点
n2=round(nx2/ddf);
A=abs(xf);
%取fft的模值
[Amax,index]=max(A(n1:
n2));
%找到fft点n1到n2之间幅值的最大值
index=index+n1-1;
%移动到n1和n2中间
%比值法
%加矩形窗
indsecL=A(index-1)>
A(index+1);
%满足条件则为1,不满足则为0
df=indsecL.*A(index-1)./(Amax+A(index-1))-(1-indsecL).*A(index+1)./(Amax+A(index+1));
Z=(index-1-df)*ddf;
%修正后的频率
9.get_node.m(确定音符)
functionnode=get_node(f)
node=12*log2(f/440)+49;
node=round(node);
%四舍五入确定对应按键
(2)生成的图与数据等
端点检测结果
序号
起始帧
截止帧
持续帧数
1
40
97
58
2
112
166
55
3
181
235
4
250
303
54
5
318
372
6
387
441
7
456
510
8
525
579
9
594
648
10
663
714
52
11
729
786
12
801
854
13
869
923
14
938
993
56
15
1008
1051
44
16
1066
1122
57
17
1137
1176
18
1191
1267
77
19
1282
1337
20
1352
1391
21
1406
1475
70
22
1490
1513
24
23
1528
1588
61
1603
1666
64
25
1681
1730
50
26
1760
1808
49
27
1823
1873
51
28
1903
1944
42
29
1959
2017
59
30
2032
2090
31
2105
2136
32
2177
2225
33
2240
2278
39
34
2317
2340
35
2380
2429
对数据的分析:
真实按键
真实音符
真实频率/Hz
检测频率/Hz
对应按键
检测音符
绝对误差/Hz
相对误差
G♯7/A♭7
523.251
524.8973
1.6463
0.31%
53
G7
554.365
555.0437
0.6787
0.12%
F♯7/G♭7
587.33
589.8768
2.5468
0.43%
F7
622.254
623.1585
0.9045
0.15%
E7
659.255
659.7079
0.4529
0.07%
D♯7/E♭7
698.456
699.2968
0.8408
D7
739.989
741.8503
1.8613