基于MATLAB的语音信号处理

基于MATLAB的语音信号处理

分帧

语音信号的两种分帧方法

% 功能:语音信号分帧处理
% 日期:2019,6,2

clear;
clc;
close all;
% ======================= input signal ==========================
y = [1,5,3,7,2,6,1];
N = length(y);  % 数据长度
wlen = 4;    % 帧长
overlap = 2; % 重叠
inc = wlen - overlap;        % 帧移
fn = floor((N - wlen)/inc) + 1;  % 帧数

% ============== 法一:通过循环来完成分帧数组 =====================
% yseg = zeros(wlen,fn);    % 数据初始化,行数为帧长,列数为帧数
% for i = 1:fn
%     startindex = (i-1)*inc +1;
%     endindex = startindex + wlen-1;
%     yseg(:,i) = y(startindex : endindex);
% end
% ============== 法二:分帧位置分配 ==============================
indf = ((0:(fn - 1))*inc)'; % 每一帧在数据y中开始位置的指针
inds = 1:wlen;              % 每一帧的数据位置为1至wlen
% 将indf扩展乘fn*wlen的矩阵,每一列的数值都和原indf一样
indf_k = indf(:,ones(1,wlen));  % 相当于repmat(indf,1,wlen)
% 将inds扩展乘fn*wlen的矩阵,每一行的数值都和原inds一样
inds_k = inds (ones(fn,1), : );    % 相当于repmat(inds,fn,1)
yseg = y(indf_k + inds_k);

法二中:

indf_k =
0 0 0 0
2 2 2 2

inds_k =
1 2 3 4
1 2 3 4

indf_k + inds_k =
1 2 3 4
3 4 5 6
y(indf_k + inds_k) =
1 5 3 7
3 7 2 6
这里可以通过索引号以及形状,得到想要的结果。

根据分帧法二,将语音信号按帧长和帧移进行分帧

执行文件 exert
% 功能:语音信号分帧处理
% 日期:2019,6,2

clear;
clc;
close all;
% ======================= input signal ==========================
[y,fs] = audioread('a.wav');
N_win = fs/40;   % 窗长
wlen = N_win;    % 帧长,这里帧长等于窗长
inc = 800;        % 帧移
win = boxcar(N_win);        % 给定窗函数,或直接给定长度
% =========================== 调用函数 ==============================
y_data = enframe(y,win,inc);  % 分帧后的数组(帧数×帧长)
% ============================设置观察时间参数
N = length(y);
time = (0:N - 1)/fs;
n = 20; % 观察第几帧信号
data_start = inc * (n-1);         % 每一帧开始的数据索引
data_end = time_start + wlen-1;   % 每一帧结束的数据索引
time_n = (data_start : data_end)./fs;    % 第几帧信号对应的时间段
% ====================plot
subplot(2,1,1);plot(time,y)  % 总信号
xlabel('时间/s','FontSize',12);
ylabel('语音信号','FontSize',15)

subplot(2,1,2);plot(time_n,y_data(n,:))  % 第n帧信号
xlabel('时间/s','FontSize',12);
str = num2str(n);   % strnum()来将数字转换为字符类型
sum=strcat('语音信号第',str,'帧');
ylabel(sum,'FontSize',15)
调用分帧函数
function [y_data ] = enframe(x,win,inc)
% in% x:语音信号
    % win:窗函数或者帧长
    % inc:帧移
% out:
    % 分帧后的数组(帧数×帧长)
% ===================================================
 L = length(x(:)); % 数据的长度
 nwin = length(win); % 取窗长, 数字的长度是1
 if (nwin == 1)  % 判断有无窗函数,若为1,表示没有窗函数
     wlen = win;  % 没有,帧长等于win
 else
     wlen = nwin; % 有窗函数,帧长等于窗长
 end
 
 if (nargin <3)  % 如果只有两个参数,inc = 帧长
    inc = len;
 end
 
 fn = floor((L - wlen)/inc) + 1;  % 帧数
 
 y_data = zeros(fn,wlen); % 初始化,fn行,wlen列
 indf = ((0:(fn - 1))*inc)'; % 每一帧在数据y中开始位置的指针
 inds = 1:wlen;              % 每一帧的数据位置为1至wlen
 indf_k = indf(:,ones(1,wlen));  % 将indf扩展乘fn*wlen的矩阵,每一列的数值都和原indf一样
 inds_k = inds (ones(fn,1), : ); % 将inds扩展乘fn*wlen的矩阵,每一行的数值都和原inds一样
 y_data(:) = x(indf_k + inds_k);
 
 if (nwin >1)  % 若参数中有窗函数,把每帧乘以窗函数
     w = win(:)';
     y_data = y_data.*w(ones(fn,1),:);
end

在这里插入图片描述

短时能量,短时平均幅度

原理:将信号分帧加窗,每一帧求幅值的平方再求和
数据点数 = 帧数
每帧对应的时间取这一帧数据中间位置的时间

% 功能:
%     短时能量 E
%     短时平均幅度 M:不会因二次方而造成较大差异
% 两者用途:
%     区分浊音段和清音段(浊音段的E比清音大的多)
%     区分声母与韵母的边界
%     区分无话段与有话段的边界
% 日期:2019,6,2

clear;
clc;
close all;
% ======================= input signal ==========================
filedir = [];              % 设置路径
filename = 'a.wav';        % 设置文件名
fle = [filedir filename];  % 构成完整的路径和文件名
[x, fs] = audioread(fle);  % 读入文件

wlen = 200;                % 帧长
inc = 80;                  % 帧移
win = hanning(wlen);
N = length(x);
X = enframe(x,win,inc)';   %  分帧后的数组(帧长×帧数)
fn = size(X,2); % 求出帧数
time = (0:N - 1)/fs;
for i = 1:fn
    u = X(:,i);            % 取出一帧
    u2 = u.^2;             % 求出能量
    M(i) = sum(abs(u));         % 幅值累加求和
    E(i) = sum(u2);        % 能量累加求和
end
subplot 311;
plot(time,x,'k')
xlabel('时间/s','FontSize',12);
title('语音波形')

subplot 312;
frametime = frame2time(fn,wlen,inc,fs); % 求出每帧对应的时间
plot(frametime, E, 'k')
xlabel('时间/s','FontSize',12);
title('短时能量')

subplot 313;
plot(frametime, M, 'k')
xlabel('时间/s','FontSize',12);
title('短时平均幅度')

function [frameTime] = frame2time(fn,wlen,inc,fs)
    % 求出每帧对应的时间,即取这一帧数据中间位置的时间
         frameTime=(((1:fn)-1)*inc+wlen/2)/fs;
end

在这里插入图片描述

短时平均过零率

% 功能:
%     短时平均过零率
% 发现:
%     高频意味着高的短时过零率
%     低频。。。低的。。。。
%     浊音由于声门波引起谱的高频跌落,语音能量约集中在3khz以下
%     清音多数能量在较高频率上
% 用途    
%     1. 从背景噪声中找出语音信号
%     2. 判断清音和辅音
%     3. 判断寂静无话端与有话段的起点和终点位置
%     4. 背景噪声小时,用平均能量识别有效;
%     5. 背景噪声大时,用短时平均过零率识别有效;

% 日期:2019,6,2
clear;
clc;
close all;
% ======================= input signal ==========================
filedir = ['C:\Users\Administrator\Desktop\语音信号处理\'];              % 设置路径
filename = '蓝天白云.wav';        % 设置文件名
fle = [filedir filename];  % 构成完整的路径和文件名
[xx, fs] = audioread(fle); % 读入文件
x = xx - mean(xx);         % 消除直流分量,确保短时过零率的正确估算

wlen = 200;                % 帧长
inc = 80;                  % 帧移
win = hanning(wlen);
N = length(x);
X = enframe(x,win,inc)';   % 分帧后的数组(帧长×帧数)
fn = size(X,2);            % 求出帧数

zcr = zeros(1,fn);        % 过零率初始化
for i = 1:fn
    z = X(:,i);            % 取得一帧数据
    for j = 1:(wlen - 1)   % 在一帧内寻找过零点,从第一个数到倒数第二个数
        if z(j)*z(j+1)<0   % 判断是否过零点
            zcr(i) = zcr(i)+1;  % 是过零点,记录一次
        end
    end     
end

time = (0:N - 1)/fs;
frametime = frame2time(fn,wlen,inc,fs); % 求出每帧对应的时间

subplot 211;
plot(time,x,'k')
xlabel('时间/s','FontSize',12);
title('语音波形')

subplot 212;
plot(frametime, zcr, 'k')
xlabel('时间/s','FontSize',12);
title('短时平均过零率')

function [frameTime] = frame2time(fn,wlen,inc,fs)
    % 求出每帧对应的时间,即取这一帧数据中间位置的时间
         frameTime=(((1:fn)-1)*inc+wlen/2)/fs;
end

在这里插入图片描述

短时自相关函数函数,短时平均幅度差函数

在这里插入图片描述

% 功能1%      短时自相关函数
% 用途:用于端点检测和基音的提取
%      韵母基因频率整倍数处出现峰值特性,而声母不明显
%      浊音的基音周期T = 自相关第一个峰值的位置/fs 

% 功能2%      短时平均幅度差函数 average magnitude difference function
% 用途:基音周期检测,计算比短时自相关更简单,只有加减法
%       韵母基因频率整倍数处出现谷值特性,而声母不明显
%       浊音的基音周期T = 自相关第一个谷值的位置/fs
% 日期:2019,6,3
clear;
clc;
close all;
% ======================= input signal ==========================
filedir = ['C:\Users\Administrator\Desktop\语音信号处理\'];  % 设置路径
filename = 'sh.wav';        % 设置文件名
fle = [filedir filename];  % 构成完整的路径和文件名
[x, fs] = audioread(fle);  % 读入文件

wlen = 300;                % 帧长
inc = 100;                  % 帧移
win = boxcar(wlen);
N = length(x);
X = enframe(x,win,inc)';   %  分帧后的数组(帧长×帧数)
fn = size(X,2);        % 求出帧数
time = (0:N - 1)/fs;
i = 20;                 % 第几帧信号
u = X(:,i);            % 取出一帧
% ==============求一帧自相关
tic
Rxx = xcorr(u);        % 长度2*N-1
toc
Rxx = Rxx(wlen:end);   % 只取正值的自相关函数,长度为帧长N
% ==============求一帧平均幅度差函数
tic
for k = 1:wlen
     amdfvec(k) = sum(abs(u(k:end)-u(1:end-k+1)));  % 求每个样点的幅度差再累加
end
toc

% ======================= figure
subplot 411;
plot(x./max(x),'k')
xlabel('样点','FontSize',12);
ylabel('归一化幅值','FontSize',12);
title('语音波形')

subplot 412;
plot(u./max(u), 'k')
xlabel('样点','FontSize',12);
ylabel('归一化幅值','FontSize',12);
title('一帧语音信号')

subplot 413;
plot(Rxx, 'k')
xlabel('样点','FontSize',12);
ylabel('幅值','FontSize',12);
title('一帧语音信号的自相关函数')

subplot 414;
plot(amdfvec, 'k')
xlabel('样点','FontSize',12);
ylabel('幅值','FontSize',12);
title('一帧语音信号的平均幅度差函数')

在这里插入图片描述
上图给出了声母为sh的语音信号,短时自相关函数没有明显的峰值。短时平均幅度差函数也没有明显的谷值。

短时傅里叶变换

在这里插入图片描述
在这里插入图片描述

exert

% 功能:短时傅里叶变换    
% 日期:2019,6,3
clear;
clc;
close all;
% ======================= input signal ==========================
filedir = ['C:\Users\Administrator\Desktop\语音信号处理\'];  % 设置路径
filename = 'sh.wav';        % 设置文件名
fle = [filedir filename];   % 构成完整的路径和文件名
[x, fs] = audioread(fle);   % 读入文件
% ===============参数设置
inc = 100;                  % 帧移
% 将帧长,窗长,nfft点数设置成相同。
% nfft: fft的长度,可以和FFT的长度不同,以获得更高的频率分辨率,但必须大>=win的长度
wlen = 300;                 % 帧长
win = boxcar(wlen);         % 窗类型
nfft = wlen;                % nfft的点数
% ======================= 第一种方法,每帧加窗求fft
d = stftms(x,win,nfft,inc); % 每一列是一帧STFT的数组,复数,保留1~nfft/2+1个频率分量
% ======================= 第二种方法,分帧加窗数组后统一fft
 y = enframe(x,win,inc)';
 Y = fft(y);                % nfft默认是y的长度
 Y = Y((1:1+nfft/2),:);
 % ==============两种方法结果相同

stftms函数

function d = stftms(x,win,nfft,inc)
% 实现STFT短时傅里叶变换
% input: 
    % x: 输入信号
    % win: 窗函数,也可以是帧长,当设置为帧长时,函数中自动设置hanning窗,
    % nfft: fft的长度,可以和FFT的长度不同,以获得更高的频率分辨率,但必须大>=win的长度
    % Inc:帧长
% output:
    % d:每一列是一帧STFT的数组,复数,保留1~nfft/2+1个频率分量
    if length(win) == 1          % 判断有无设置窗函数
        wlen = win;              % 否,设置帧长
        win = hanning(wlen);     % 设置窗函数
    else
        wlen = length(win);
    end
    x = x(:);                   % 变为列数组
    win = win(:);               % 变为列数组
    L = length(x);
    c = 1;
    fn = fix((L - wlen)/inc) + 1;  % 帧数
    d = zeros((1+nfft/2),fn );     % 初始化输出数组, 1+nfft/2行,fn列 
    for b = 0:inc:(L-wlen)                          % 循环
        start_ind = b+1;
        end_ind = b+wlen;
        u = win.*x(start_ind : end_ind);            % 取来一帧数据加窗
        y = fft(u,nfft);
        d(:,c)= y(1:(1+nfft/2));                    %11+nfft/2之间的谱值
        c = c+1;                                    % 改变帧数,求下一帧
    end
end

语谱图(短时傅里叶变换)

% 功能:短时傅里叶变换    
% 日期:2019,6,3
clear;
clc;
close all;
% ======================= input signal ==========================
filedir = ['C:\Users\Administrator\Desktop\语音信号处理\'];  % 设置路径
filename = '蓝天白云.wav';        % 设置文件名
fle = [filedir filename];   % 构成完整的路径和文件名
[x, fs] = audioread(fle);   % 读入文件
% ==================== 参数设置 ===================================
inc = 80;                   % 帧移
wlen = 200;                 % 帧长
win = hanning(wlen);        % 窗类型
nfft = wlen;                % nfft的点数
N = length(x);
time = (0: N -1)/fs;
y = enframe(x,win,inc)';   % 帧长*帧数
fn = size(y,2);            % 帧数
frameTime=(((1:fn)-1)*inc+wlen/2)/fs;    % 求出每帧对应的时间,即取这一帧数据中间位置的时间
n2 = 1:wlen/2+1;           % 由于共轭对称,取数据的一半
freq = (n2 - 1)*fs/wlen;   % 计算fft后的频率刻度
Y = fft(y);                % 短时傅里叶变换
clf                        % 初始化图形
% ======================= 语谱图  ===============================
set(gcf, 'Position',[20 100 600 500]);
axes('Position',[0.1 0.1 0.85 0.5]);
imagesc(frameTime, freq,abs(Y(n2,:)));
axis xy;
ylabel('频率/Hz');
xlabel('时间/s');
title('语谱图');
LightYellow = [0.6 0.6 0.6];
MidRed = [0 0 0 ];
black = [0.5 0.7 1];
colors = [LightYellow; MidRed; black];
m = 64;
colormap(SpecColorMap(m, colors));  % m指在着色分配中设置多少等级
% ======================= 语谱图  ===============================
axes('Position',[0.07 0.72 0.9 0.22]);
plot(time,x, 'k')
xlim([0 max(time)]);
xlabel('时间/s');
ylabel('幅值');
title('语音信号波形');

在这里插入图片描述

短时功率谱密度函数

exert

% 功能:短时功率谱密度函数   
% 原理:对信号分帧后再对每帧计算对应的短时功率谱密度
% 日期:2019,6,4
clear;
clc;
close all;
% ======================= input signal ==========================
filedir = ['C:\Users\Administrator\Desktop\语音信号处理\'];  % 设置路径
filename = '蓝天白云.wav';                % 设置文件名
fle=[filedir filename];                   % 构成完整的路径和文件名
[wavin0,fs] = audioread(fle);             % 读入数据文件
nwind = 240;                              % 设置帧长为240,
noverlap = 160;                           % 重叠为160
inc = nwind - noverlap;                   % 帧移为80
w_nwind = hanning(200);                   % 段长为200
w_noverlap = 195;                         % 段重叠为195
nfft = 200;                               % FFT长度为200
% 对每帧用pwelch_2计算短时功率谱密度
[Pxx] = pwelch_2(wavin0, nwind, noverlap, w_nwind, w_noverlap, nfft);
fn = size(Pxx,2);                          % 取来帧数
frameTime = frame2time(fn,nfft,inc,fs);    % 计算每帧对应的时间
freq=(0:nfft/2)*fs/nfft;                   % 计算频率刻度
% 作图
imagesc(frameTime,freq,Pxx);
axis xy           
ylabel('频率/Hz');
xlabel('时间/s');
title('短时功率谱密度函数')
m = 256; 
LightYellow = [0.6 0.6 0.6];
MidRed = [0 0 0]; 
Black = [0.5 0.7 1];
Colors = [LightYellow; MidRed; Black];
colormap(SpecColorMap(m,Colors));

function frameTime=frame2time(frameNum,framelen,inc,fs)
% 分帧后计算每帧对应的时间
frameTime=(((1:frameNum)-1)*inc+framelen/2)/fs;
end

pwelch_2函数

function [Pxx] = pwelch_2(x, nwind, noverlap, w_nwind, w_noverlap, nfft)
% 计算短时功率谱密度函数
% x是信号,
% nwind是每帧长度,
% noverlap是每帧重叠的样点数
% w_nwind是每段的窗函数,或相应的段长,
% w_noverlap是每段之间的重叠的样点数,nfft是FFT的长度

x = x(:);                   % 转换成列
inc = nwind-noverlap;       % 计算帧移
X = enframe(x,nwind,inc)';  % 分帧
frameNum = size(X,2);       % 计算帧数
%用pwelch函数对每帧计算功率谱密度函数
for k = 1 : frameNum
    Pxx(:,k) = pwelch(X(:,k),w_nwind,w_noverlap,nfft);  
end

在这里插入图片描述

倒谱

在这里插入图片描述


% 功能:
%     倒谱:信号频谱模的自然对数的逆傅里叶变换
%     在倒频谱中横轴是倒频率,英文名词称为quefrency,量纲是时间
%     倒谱的窗选择:一般取渐变窗,如hamming窗,
%     浊音,倒谱的第一非零点(基音尖峰)与原点的距离(以样点计)为基音周期。

%     日期:2019,6,5
clear;
clc;
close all;
% ======================= input signal ==========================
y = load('su1.txt');                    % 读入数据
fs = 16000;                             % 采样频率
nfft=1024;                              % FFT的长度
time=(0:nfft-1)/fs;                     % 时间刻度

figure(1)
subplot 211; 
plot(time,y,'k');                       % 画出信号波形
title('信号波形');
axis([0 max(time) -0.7 0.7]);
ylabel('幅值'); 
xlabel(['时间/s' 10 '(a)']); 
grid;

figure(2)
nn = 1:nfft/2; 
ff = (nn-1)*fs/nfft;                    % 计算频率刻度
Y = log(abs(fft(y)));                   % 按式(3-1-8)取实数部分
subplot 211; 
plot(ff,Y(nn),'k');
hold on;                                % 画出信号的频谱图
quefrency = ifft(Y);                    % 按式(3-1-8)求取倒谱

figure(1)
subplot 212;
plot(time,quefrency,'k');               % 画出倒谱图
title('信号倒谱图');
axis([0 time(512) -0.2 0.2]);
grid; 
ylabel('幅值');
xlabel(['倒频率/s' 10 '(b)']);

% 分离声门激励脉冲和声道冲激响应
figure(2),                                    
mcep = 29;                % 为什么是29                               
zy = quefrency(1 : mcep+1);
zy = [zy' zeros(1,1024 - 2*mcep - 1) zy(end:-1:2)'];     % 构建声道冲激响应的倒谱序列
ZY = fft(zy);                                            % 计算声道冲激响应的频谱
line(ff,real(ZY(nn)),'color',[.6 .6 .6],'linewidth',3);  % 画出声道冲激响应的频谱,用灰线表示
grid;
hold off;
ylim([-4 5]);
title('信号频谱(黑线)和声道冲激响频谱(灰线)')
ylabel('幅值'); 
xlabel(['频率/Hz' 10 '(a)']); 

ft=[zeros(1,mcep+1) quefrency(mcep+2:end-mcep)' zeros(1,mcep)]; % 构建声门激励脉冲的倒谱序列
FT=fft(ft);                                                     % 计算声门激励脉冲的频谱
subplot 212; 
plot(ff,real(FT(nn)),'k');                                      % 画出声门激励脉冲的频谱
grid;                                    
title('声门激励脉冲频谱')
ylabel('幅值'); 
xlabel(['频率/Hz' 10 '(b)']); 

在这里插入图片描述

  • 22
    点赞
  • 130
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值