语音信号处理:分帧、加窗

原文链接,在原文的基础上加入了自己的一些学习过程中遇到的一些问题而插入的其他链接的知识点,有错误请大家指正, 多多交流python对语音信号读取、分帧、加窗_YAOHAIPI的博客-CSDN博客用python做语音信号处理一、读入音频信号语音信号有三个重要的参数:声道数、取样频率和量化位数。声道数:单声道或者双声道采样频率:一秒钟对声音采样的次数,例如10000HZ代表一秒钟将信号分解为10000份,当采样数量非常高的时候,我们人眼看起来就是连续的。(实际是离散的)。采样频率越高声音的还原就越真实越自然。在当今的主流采集卡上,采样频率一般共分为22.05KHz、44.1K...https://blog.csdn.net/YAOHAIPI/article/details/102826051 

语音信号处理

        在语音信号处理当中,提取特征十分重要,在提取特征的过程当中,首先要进行分帧,在短时语音分析的过程当中,要一段一段进行分析,一段一段的提取其中的特征参数,这样的一段叫做一帧,帧长一般取10~30ms。那么分析整段语音的过程就变成了,分析从许多帧当中提取到参数从而组成的特征参数时间序列的分析。

提取参数的方法来对语音信号分析进行分类

        有时域分析、频域分析、倒频域分析以及其他方法。分析方法的不同,又可将语音信号分析分为模型分析方法和非模型分析方法两种。

        比如时域分析:简单、计算量小、物理意义明确等优点,语音信号最重要的感知特性反映在功率谱中,而相位变化只起着很小的作用,所以很多时候频域分析显得更为重要;

        模型分析方法:根据语音信号产生的数学模型,来分析和提取表征这些模型的特征参数。比如共振峰分析、声管分析(线性预测模型);(这里就先简单理解成为有无既有的语音数学模型来作为参考进行分析,如果有,那么可以视作模型分析方法;如果没有,那么可以看作是非模型分析法,也就是没有具体的数学模型,需要自行对语音的几种域进行分析)

        不按照模型来进行分析的方法自然也就叫做非模型分析法,包括上面提到的时域分析法、频域分析法及同态分析法(即倒频域分析法)等。;

        总之,不论使用什么方法,都需要在按帧进行分析、提取特征参数之前,需要进行一些常用的短时分析技术,比如信号的数字化、预加重、加窗分帧等,这些环节也不可以忽略。非常关键。

分帧

        在分帧过程中,需要在相邻的两帧之间有所重叠。因为语音信号是时变的,短时范围内的特征变化较小,所以可以视为稳态进行分析。但是如果超过这短时范围,那么信号就会产生变化,在这相邻的两帧之间,它们对应的基音可能就会发生变化,如果正好是两个音节的中间位置,或者正好是声母过渡向韵母等,这两帧的特征参数就可能变化较大,但是为了让特征参数变化的更加平滑,在两个不重叠的帧之间插入一些帧来提取特征参数,就形成了相邻帧之间的重叠部分。

        

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fGTdvtze-1572436638086)(C:\Users\jh\AppData\Roaming\Typora\typora-user-images\1572159439550.png)]

        wlen是帧长,inc是帧移,重叠部分即overlap=wlen-inc

        那么,整个信号对应的帧数fn就是下面的公式,其中N代表语音数据的长度

                fn=(N-overlap)/inc=(N-wlen)/inc+1

         每一帧对应的起点的时间位置是:

        startIndex=(0:(fn-1))*inc+1

这里+1的含义没明白

简单的处理

读取文件

import numpy as np
import wave
import matplotlib
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt

wlen=512
inc=128
f = wave.open(r"lantian.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
wave_data = np.fromstring(str_data, dtype=np.short)#str转化成short类型
wave_data = wave_data*1.0/(max(abs(wave_data)))
#进行归一化

  分帧

signal_length=len(wave_data) #这里是读取语音信号的总长度


if signal_length<=wlen: #若信号长度小于一个帧的长度,则帧数nf定义为1;其他变量wlen是帧信号的长度,inc是帧移
        nf=1
else:                 #否则,计算帧的总数,参考上面计算帧数的公式
        nf=int(np.ceil((1.0*signal_length-wlen+inc)/inc))

        但是np.ceil函数是向上取整的,所以导致结果当中的帧长度要大于信号本身的长度,所以,需要对原来的信号进行补0,这个和FFT变换里数据点不够需要补0来达到采样点的数量是类似的。

pad_length=int((nf-1)*inc+wlen) #所有帧加起来总的铺平后的长度
zeros=np.zeros((pad_length-signal_length,)) #不够的长度使用0填补,类似于FFT中的扩充数组操作
pad_signal=np.concatenate((wave_data,zeros)) #填补后的信号记为pad_signal
#np.concatenate()连接两个维度相同的矩阵
indices=np.tile(np.arange(0,wlen),(nf,1))+np.tile(np.arange(0,nf*inc,inc),(wlen,1)).T  
#相当于对所有帧的时间点进行抽取,得到nf*wlen长度的矩阵

np.title用法可参考该博客内容,可以认为是沿着x轴和y轴分别进行扩展复制                numpy.tile()_子衿_青青的博客-CSDN博客_np.tile()numpy.tile()是个什么函数呢,说白了,就是把数组沿各个方向复制比如 a = np.array([0,1,2]),    np.tile(a,(2,1))就是把a先沿x轴(就这样称呼吧)复制1倍,即没有复制,仍然是 [0,1,2]。 再把结果沿y方向复制2倍,即最终得到 array([[0,1,2],             [0,1,2]])同理:>>> b =https://blog.csdn.net/qq_18433441/article/details/54897250

         wlen=512,inc=128.因此上式形成的矩阵大致是这样的,一行当中有wlen个数据,每个数据和1~inc的数据进行相加,比如输出矩阵的前两行结果大致是这样的

print(indices[:2])
[[  0   1   2 ... 509 510 511]
 [128 129 130 ... 637 638 639]]

        要把语音信号转化成nf*wlen的形式,比如输出第30行的值

indices=np.array(indices,dtype=np.int32) #将indices转化为矩阵
frames=pad_signal[indices] #得到帧信号
a=frames[30:31]
print(a[0])

         因此,上面的内容总结下来就可以画出一帧信号的时域图像

       

import numpy as np
import wave
import matplotlib.pyplot as plt
wlen=512
inc=128
f = wave.open(r"lantian.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
wave_data = np.fromstring(str_data, dtype=np.short)
wave_data = wave_data*1.0/(max(abs(wave_data)))
print(wave_data[:10])
time = np.arange(0, wlen) * (1.0 / framerate)
signal_length=len(wave_data) #信号总长度
if signal_length<=wlen: #若信号长度小于一个帧的长度,则帧数定义为1
        nf=1
else: #否则,计算帧的总长度
        nf=int(np.ceil((1.0*signal_length-wlen+inc)/inc))
pad_length=int((nf-1)*inc+wlen) #所有帧加起来总的铺平后的长度
zeros=np.zeros((pad_length-signal_length,)) #不够的长度使用0填补,类似于FFT中的扩充数组操作
pad_signal=np.concatenate((wave_data,zeros)) #填补后的信号记为pad_signal
indices=np.tile(np.arange(0,wlen),(nf,1))+np.tile(np.arange(0,nf*inc,inc),(wlen,1)).T  #相当于对所有帧的时间点进行抽取,得到nf*nw长度的矩阵
print(indices[:2])
indices=np.array(indices,dtype=np.int32) #将indices转化为矩阵
frames=pad_signal[indices] #得到帧信号
a=frames[30:31]
print(a[0])
plt.figure(figsize=(10,4))
plt.plot(time,a[0],c="g")
plt.grid()
plt.show()

加窗

        常见的窗有矩形窗,海宁窗,汉明窗等

        窗长为L

矩形窗

w(n)=\begin{cases} 1,& \text{ if } 0<=n<L-1 \\ 0,& \text{ if } x= others \end{cases}

海宁窗

w(n)=\begin{cases} 0.5(1-cos(2*\pi*n/(L-1))), & \text{ if } 0<=n<L-1\\ 0 & \text{ if } n= others \end{cases}

汉明窗

w(n)=\begin{cases} 0.54-0.46cos(2\pi n/(L-1)), & \text{ if } 0<=n<L-1 \\0 & \text{ if } n=others \end{cases}

        通常对信号截断、分帧需要加窗,因为截断都有频域能量泄露,而窗函数可以减少截断带来的影响。窗函数在scipy.signal信号处理工具箱中,如hanning窗:(窗函数也叫做加权函数,可以让信号更好地满足FFT处理的周期性要求)

        何为频域能量泄露:什么是泄漏?泄漏是数字信号处理所遭遇的最严重的误差,因此,信号处理时应十分小心!https://mp.weixin.qq.com/s?__biz=MzI5NTM0MTQwNA==&mid=2247484164&idx=1&sn=fdaf2164306a9ca4166c2aa8713cacc5&scene=21#wechat_redirect

        比如添加汉宁窗

import matplotlib.pyplot as plt
import scipy.signal as signal
plt.figure(figsize=(6,2))
plt.plot(signal.hanning(512))
plt.grid()
plt.show()

        语音当中处理分帧,相当于乘以一个有限长的窗函数,以卷积的形式。

        可以理解为离散信号x经过一个单位冲激函数响应为w的FIR滤波器产生的输出。关于单位冲激函数响应的内容可以查看下面的:单位冲激函数和单位阶跃函数笔记总结 - 知乎在了解卷积积分之前,我们需要先来了解电路中两个比较重要的函数。 单位阶跃函数名词解释: (1)单位意味着变化值为1 (2)阶跃可以理解为一个阶段的跳跃(从一个阶段跳跃到另外一个阶段) 定义: 图像: 上述阶…https://zhuanlan.zhihu.com/p/368350900

y(n)=\sum_{x=-\infty}^{\infty}x(m)w(n-m)

        

mport numpy as np
import wave
import matplotlib.pyplot as plt
wlen=512
inc=128
f = wave.open(r"lantian.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
wave_data = np.fromstring(str_data, dtype=np.short)
wave_data = wave_data*1.0/(max(abs(wave_data)))
print(wave_data[:10])
time = np.arange(0, wlen) * (1.0 / framerate)
signal_length=len(wave_data) #信号总长度
if signal_length<=wlen: #若信号长度小于一个帧的长度,则帧数定义为1
        nf=1
else: #否则,计算帧的总长度
        nf=int(np.ceil((1.0*signal_length-wlen+inc)/inc))
pad_length=int((nf-1)*inc+wlen) #所有帧加起来总的铺平后的长度
zeros=np.zeros((pad_length-signal_length,)) #不够的长度使用0填补,类似于FFT中的扩充数组操作
pad_signal=np.concatenate((wave_data,zeros)) #填补后的信号记为pad_signal
indices=np.tile(np.arange(0,wlen),(nf,1))+np.tile(np.arange(0,nf*inc,inc),(wlen,1)).T  #相当于对所有帧的时间点进行抽取,得到nf*nw长度的矩阵
print(indices[:2])
indices=np.array(indices,dtype=np.int32) #将indices转化为矩阵
frames=pad_signal[indices] #得到帧信号
a=frames[30:31]
print(a[0])
windown=np.hanning(wlen)  #调用汉明窗
b=a[0]*windown
plt.figure(figsize=(10,4))
plt.plot(time,b,c="g")
plt.grid()
plt.show()

短时能量

        区分浊音段、轻音段,有声段,无声段,声母和韵母分解,还可以作为辅助的特征参数用在语音识别任务当中。语音短时能量就相当于,每一帧中所有语音信号的平方和

        关于短时能量,还可以参考

语音短时能量计算——Python实现_落队的飞行兵-CSDN博客_python 短时能量刚开始学习计算机视听觉,第一个实验是端点检测算法。这个算法实现起来还是比较简单的,主要是该算法利用到的两个数据——语音短时能量和短时过零率。今天先分享一下我计算短时能量的方法。语音短时能量,顾名思义就是计算较短时间内的语音能量。这里的较短时间,通常指的是一帧。也就是说,一帧时间内的语音能量就是短时能量,语音的短时能量就是将语音中每一帧的短时能量都计算出来,然后我们就可以利用这个短时能量数组做很多事情了。通常而言,短时能量用途有以下几个方面:https://blog.csdn.net/rocketeerLi/article/details/83271399

        计算第i帧的语音信号yi(n)的短时能量公式

y(n)=\sum_{n=0}^{L-1} y_{i}^{2},1<=i<=fn

import numpy as np
import wave
import matplotlib.pyplot as plt
wlen=512
inc=128
f = wave.open(r"lantian.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
#print(str_data[:10])
wave_data = np.fromstring(str_data, dtype=np.short)
#print(wave_data[:10])
wave_data = wave_data*1.0/(max(abs(wave_data)))
print(wave_data[:10])
time = np.arange(0, wlen) * (1.0 / framerate)
signal_length=len(wave_data) #信号总长度
if signal_length<=wlen: #若信号长度小于一个帧的长度,则帧数定义为1
        nf=1
else: #否则,计算帧的总长度
        nf=int(np.ceil((1.0*signal_length-wlen+inc)/inc))
pad_length=int((nf-1)*inc+wlen) #所有帧加起来总的铺平后的长度
zeros=np.zeros((pad_length-signal_length,)) #不够的长度使用0填补,类似于FFT中的扩充数组操作
pad_signal=np.concatenate((wave_data,zeros)) #填补后的信号记为pad_signal
indices=np.tile(np.arange(0,wlen),(nf,1))+np.tile(np.arange(0,nf*inc,inc),(wlen,1)).T  #相当于对所有帧的时间点进行抽取,得到nf*nw长度的矩阵
print(indices[:2])
indices=np.array(indices,dtype=np.int32) #将indices转化为矩阵
frames=pad_signal[indices] #得到帧信号
a=frames[30:31]
print(a[0])
windown=np.hanning(512)
b=a[0]*windown
c=np.square(b)
#短时能量
plt.figure(figsize=(10,4))
plt.plot(time,c,c="g")
plt.grid()
plt.show()

  • 5
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值