% 功能:语音信号分帧处理
% 日期: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
这里可以通过索引号以及形状,得到想要的结果。
% 功能:语音信号分帧处理
% 日期: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的语音信号,短时自相关函数没有明显的峰值。短时平均幅度差函数也没有明显的谷值。
% 功能:短时傅里叶变换
% 日期: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),:);
% ==============两种方法结果相同
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)); % 取1到1+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('语音信号波形');
% 功能:短时功率谱密度函数
% 原理:对信号分帧后再对每帧计算对应的短时功率谱密度
% 日期: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
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)']);