%% 按键音识别主程序
clc;
clear all;
close all;
%% 原始按键音的时域与频域分析
% [data,fs]=audioread('./作业按键音频/E.mp3');
% x0=data(:,1);
[data,fs]=audioread('合并后的按键音.mp3'); % 获取采样频率和音频数据
x0=data(:,1); % 提取单声道
N=length(x0); % 信号长度,采样点数
k=(0:N-1); %建立一个信号等长的序列;
T=1/fs; % 采样周期
t=k*T; %时间
f=fs*(k/N-1/2);% 得到以原点对称的真实频率序列
X0=fft(x0); % 快速傅里叶变换
figure(1)
subplot(2,1,1),plot(t,x0),grid on;
title('初始按键音的时域'),xlabel('时间'),ylabel('振幅');
subplot(2,1,2),plot(f,abs(fftshift(X0)));xlim([0,4000]),grid on; % fftshift的作用是将零频点移到频谱的中间
title('初始按键音的频域'),xlabel('f/Hz'),ylabel('幅度');
%% 设计带通数字滤波器进行滤波
% x1=DFLV(x0,fs);
Hd=elliplic; % 利用fdatool设计带通数字滤波器
x1=filter(Hd,x0); % 对原始音频进行滤波去噪
X1=fft(x1); %滤波后进行快速傅里叶变换
figure(2)
subplot(2,1,1),plot(t,x1);grid on
title('滤波后的按键音的时域'),xlabel('time'),ylabel('振幅');
subplot(2,1,2),plot(f,abs(fftshift(X1)));grid on
title('滤波后的按键音频域');
xlabel('频率/HZ'),ylabel('幅度');xlim([0,2000]);
%% 计算短时能量和过零率
framelen= floor(fs*40/1000);%floor向下取整,帧长
frameinc= floor(fs*10/1000);%帧移
framenum=fz(framelen,framelen-frameinc,x1);% 确定帧数
en=framenum.^2; % 每一帧的能量
EN=sum(en,2); %按行求和
zcr=zcro(framenum);%计算过零率
figure
plot(EN)
title('平均短时能量'),xlabel('帧编号'),ylabel('能量')
figure
plot(zcr)
title('过零率'),xlabel('帧编号'),ylabel('过零次数')
%% 端点检测 通过设置短时能量的阈值来确定语音段的起始和结尾
sign=EN;
threshold=0.05; %设置短时能量的阈值
sign(EN>threshold)=1; % 大于阈值为有效
sign(EN<threshold)=0; % 小于阈值为无效
eff_sign=[]; % 空列表存储有效的点
eff_sign(1)=0;
for i=1:length(sign)
for j=2:i
if sign(j-1)*sign(j)==0
eff_sign(i)=0;
else
eff_sign(i)=1;
end
end
end
for i=2:length(eff_sign)
if eff_sign(i)-eff_sign(i-1)==1 %由0到1为左端点
left_endpoint(i)=i;
elseif eff_sign(i)-eff_sign(i-1)==-1% 由1到0为右端点
right_endpoint(i)=i;
end
end
left_position = find(left_endpoint~=0);% 确定左端点对应的帧编号
right_position = find(right_endpoint~=0);% 确定右端点的帧编号
[left_end,leftend2]=reframe(left_position,framelen-frameinc,framelen); %将帧数恢复到时域中
[right_end,rightend2]=reframe(right_position,framelen-frameinc,framelen);
%在时域上添加划分线
figure
subplot(3,1,1),plot(x1)
title('滤波后按键音端点划分'),ylabel('幅度'),xlabel('t')
for i=1:length(left_end)
line([left_end(i) left_end(i)],[-0.5 0.5],'Color','m','LineWidth',1)
line([right_end(i) right_end(i)],[-0.5 0.5],'Color','g','LineWidth',1)
end
subplot(3,1,2),plot(EN),ylim([-0.6,0.6])
title('平均短时能量'),xlabel('帧编号'),ylabel('EN')
for i=1:length(left_position)
line([left_position(i) left_position(i)],[-1 5],'Color','m','LineWidth',1)
line([right_position(i) right_position(i)],[-1 5],'Color','g','LineWidth',1)
end
subplot(3,1,3),plot(zcr)
for i=1:length(left_position)
line([left_position(i) left_position(i)],[-200,200],'Color','m','LineWidth',1)
line([right_position(i) right_position(i)],[-200,200],'Color','g','LineWidth',1)
end
title('平均过零率'),xlabel('帧编号'),ylabel('过零次数')
%% 按键音的识别
number=' '; %设置一个空字符串
figure
title('频谱图');
for i= 1:length(left_end)
y=x1(left_end(i):right_end(i));
M=length(y);
n=0:M-1;%建立一个信号等长的序列;
F=fft(y,M);%N点傅里叶变换
amplitude=abs(F)/(M/2);% 还原真实的幅度值
f1=n*fs/M; %频率序列
f=f1(1:fix(M/2));mag=amplitude(1:fix(M/2));%取l/2作图
subplot(3,4,i)%做频谱图
plot(f,mag);
axis([600 1800 min(mag) max(mag)]);
xlabel('频率/Hz');%标注横坐标
ylabel('幅度');grid on;%打开网格线
a1=(697+770)/2;a2=(770+852)/2;a3=(852+941)/2;
a4=(1209+1336)/2;a5=(1336+1477)/2;a6=(1477+1633)/2;
if((f(mag==max(mag(f<=1000)))<a1)&&(f(mag==max(mag(f<=1000)))>(697*2-a1)))
if((f(mag==max(mag(f>1200)))<a4)&&(f(mag==max(mag(f>1200)))>(1209*2-a4)))
tel=1;
elseif((f(mag==max(mag(f>1200)))<a5)&&(f(mag==max(mag(f>1200)))>(1336*2-a5)))
tel=2;
elseif((f(mag==max(mag(f>1200)))<a6)&&(f(mag==max(mag(f>1200)))>(1477*2-a6)))
tel=3;
else
tel='A';
end
elseif((f(mag==max(mag(f<=1000)))<a2)&&(f(mag==max(mag(f<=1000)))>(770*2-a2)))
if((f(mag==max(mag(f>1100)))<a4)&&(f(mag==max(mag(f>1200)))>(1209*2-a4)))
tel=4;
elseif((f(mag==max(mag(f>1200)))<a5)&&(f(mag==max(mag(f>1200)))>(1336*2-a5)))
tel=5;
elseif((f(mag==max(mag(f>1200)))<a6)&&(f(mag==max(mag(f>1200)))>(1477*2-a6)))
tel=6;
else
tel='B';
end
elseif((f(mag==max(mag(f<=1000)))<a3)&&(f(mag==max(mag(f<=1000)))>(852*2-a3)))
if((f(mag==max(mag(f>1200)))<a4)&&(f(mag==max(mag(f>1200)))>(1209*2-a4)))
tel=7;
elseif((f(mag==max(mag(f>1200)))<a5)&&(f(mag==max(mag(f>1200)))>1300))
tel=8;
elseif((f(mag==max(mag(f>1200)))<a6)&&(f(mag==max(mag(f>1200)))>(1477*2-a6)))
tel=9;
else
tel='C';
end
else
if((f(mag==max(mag(f>1200)))<a4)&&(f(mag==max(mag(f>1200)))>(1209*2-a4)))
tel='*';
elseif((f(mag==max(mag(f>1200)))<a5)&&(f(mag==max(mag(f>1200)))>(1336*2-a5)))
tel=0;
elseif((f(mag==max(mag(f>1200)))<a6)&&(f(mag==max(mag(f>1200)))>(1477*2-a6)))
tel='#';
else
tel='D';
end
end
number=[number,num2str(tel)];
end
disp("识别的电话号码为:")
disp(number)
评论7