admin 发表于 2018-12-17 11:43:37

python做语音信号处理

音频信号的读写、播放及录音标准的python已经支持WAV格式的书写,而实时的声音输入输出需要安装pyAudiio(http://people.csail.mit.edu/hubert/pyaudio)。最后我们还将使用pyMedia(http://pymedia.org)进行Mp3的解码和播放。  音频信号是模拟信号,我们需要将其保存为数字信号,才能对语音进行算法操作,WAV是Microsoft开发的一种声音文件格式,通常被用来保存未压缩的声音数据。语音信号有三个重要的参数:声道数、取样频率和量化位数。
[*]声道数:可以是单声道或者是双声道
[*]采样频率:一秒内对声音信号的采集次数,44100Hz采样频率意味着每秒钟信号被分解成44100份。换句话说,每隔<span class="MathJax" id="MathJax-Element-1-Frame" tabindex="0" data-mathml="144100Hz" role="presentation" style="display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border: 0px; position: relative;">144100Hz144100Hz<span id="MathJax-Element-1-Frame" class="MathJax" data-mathml="144100Hz" style="display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border: 0px;">就会存储一次,如果采样率高,那么媒体播放音频时会感觉信号是连续的。
[*]量化位数:用多少bit表达一次采样所采集的数据,通常有8bit、16bit、24bit和32bit等几种
例如CD中所储存的声音信号是双声道、44.1kHz、16bit。如果你需要自己录制和编辑声音文件,推荐使用Audacity(http://audacity.sourceforge.net),它是一款开源的、跨平台、多声道的录音编辑软件。在我的工作中经常使用Audacity进行声音信号的录制,然后再输出成WAV文件供Python程序处理。wave-读wav文件wava模块为WAV声音格式提供了方面的界面,他不支持压缩/解压,但支持单声道/立体声。Wave_read = wave.open(file,mode="rb")file通常为是字符串格式的文件名或者文件路径    例如voice.wav文件的路径C:\Users\Never\Desktop\code for the speech  则file有以下三种填写格式:    r"C:\Users\Never\Desktop\code for the speech\voice.wav"    "C:/Users/Never/Desktop/code for the speech/voice.wav"    "C:\\Users\\Never\\Desktop\\code for the speech\\voice.wav"  三者等价,右划线\为转意字符,如果要表达\则需要\\,引号前面加r表示原始字符串。mode是缺省参数,可以不填,也可以是"rb":只读模式;"wb":只写模式。注意不支持读/写格式。该open()函数可用于with声明中。当with块完成时,Wave_read.close()或Wave_write.close()方法被调用。Wave_read是读取的文件流。Wave_read.getparams()    一次性返回所有的音频参数,返回的是一个元组(声道数,量化位数(byte单位),采样频率,采样点数,压缩类型,压缩类型的描述)。(nchannels, sampwidth, framerate, nframes, comptype, compname)wave模块只支持非压缩的数据,因此可以忽略最后两个信息。str_data = Wave_read.readframes(nframes)  指定需要读取的长度(以取样点为单位),返回的是字符串类型的数据wave_data = np.fromstring(str_data, dtype=np.short)将读取的字符串数据转换为一维short类型的数组。通过np.fromstring函数将字符串转换为数组,通过其参数dtype指定转换后的数据格式(由于我们的声音格式是以两个字节表示一个取样值,因此采用short数据类型转换)现在的wave_data是一个一维的short类型的数组,但是因为我们的声音文件是双声道的,因此它由左右两个声道的取样交替构成:LRwave_data.shape = (-1, 2)  # -1的意思就是没有指定,根据另一个维度的数量进行分割,得到n行2列的数组。getnchannels, getsampwidth, getframerate, getnframes等方法可以单独返回WAV文件的特定的信息。Wave_read.close()  关闭文件流waveWave_read.getnchannels()  返回音频通道的数量(1对于单声道,2对于立体声)。Wave_read.getsampwidth()  以字节为单位返回样本宽度Wave_read.getframerate()  返回采样频率。Wave_read.getnframes()   返回音频帧数。Wave_read.rewind()      将文件指针倒回到音频流的开头。Wave_read.tell()      返回当前文件指针位置。 读取通道数为2的音频信号https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif 读取双通道波形并绘制波形图
效果图:https://img2018.cnblogs.com/blog/1433301/201812/1433301-20181210194719564-2059333061.png第二种读取文件的方式:from scipy.io import wavfilesampling_freq, audio = wavfile.read("***.wav")这里读取的audio直接是数组,不用像上面经过np.fromstring(str_data,dtype=np.short)类型转换。wave-写wav音频在写入第一帧数据时,先通过调用setnframes()设置好帧数,setnchannels()设置好声道数,setsampwidth()设置量化位数,setframerate()设置好采样频率,然后writeframes(wave.tostring())用于写入帧数据。Wave_write = wave.open(file,mode="wb")Wave_write是写文件流,Wave_write.setnchannels(n)  设置通道数。Wave_write.setsampwidth(n)  将样本宽度设置为n个字节,量化位数Wave_write.setframerate(n)  将采样频率设置为n。Wave_write.setnframes(n)  将帧数设置为nWave_write.setparams(tuple)  以元组形式设置所有参数(nchannels, sampwidth, framerate, nframes,comptype, compname)Wave_write.writeframes(data)  写入data个长度的音频,以采样点为单位Wave_write.tell()  返回文件中的当前位置https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif 写wav文件
https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif 写WAV文件方法2
第三种写文件的方法from scipy.io.wavfile import writewrite(output_filename, freq, audio)https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif 写WAV文件
合成有音调的音乐https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif 合成音调
https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif tone_freq_map
音频播放wav文件的播放用到的是pyaudio库p = pyaudio.PyAudio()stream = p.open(format = p.get_format_from_width(sampwidth),channels,rate,output = True)stream.write(data)  # 播放data数据以下列出pyaudio对象的open()方法的主要参数:rate - 取样频率channels - 声道数format - 取样值的量化格式 (paFloat32, paInt32, paInt24, paInt16, paInt8 ...)。在上面的例子中,使用get_format_from_width方法将wf.sampwidth()的返回值2转换为paInt16input - 输入流标志,如果为True的话则开启输入流output - 输出流标志,如果为True的话则开启输出流input_device_index - 输入流所使用的设备的编号,如果不指定的话,则使用系统的缺省设备output_device_index - 输出流所使用的设备的编号,如果不指定的话,则使用系统的缺省设备frames_per_buffer - 底层的缓存的块的大小,底层的缓存由N个同样大小的块组成start - 指定是否立即开启输入输出流,缺省值为Truehttps://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif 播放WAV音频
录音以SAMPLING_RATE为采样频率,每次读入一块有NUM_SAMPLES个采样的数据块,当读入的采样数据中有COUNT_NUM个值大于LEVEL的取样的时候,将数据保存进WAV文件,一旦开始保存数据,所保存的数据长度最短为SAVE_LENGTH个块。WAV文件以保存时的时刻作为文件名。从声卡读入的数据和从WAV文件读入的类似,都是二进制数据,由于我们用paInt16格式(16bit的short类型)保存采样值,因此将它自己转换为dtype为np.short的数组。录音https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif View Code
语音信号处理语音信号的产生和感知  我们要对语音进行分析,首先要提取能够表示该语音的特征参数,有了特征参数才可能利用这些参数进行有效的处理,在对语音信号处理的过程中,语音信号的质量不仅取决于处理方法,同时取决于时候选对了合适的特征参数。  语音信号是一个非平稳的时变信号,但语音信号是由声门的激励脉冲通过声道形成的,而声道(人的口腔、鼻腔)的肌肉运动是缓慢的,所以“短时间”(10~30ms)内可以认为语音信号是平稳时不变的。由此构成了语音信号的“短时分析技术”。  在短时分析中,将语音信号分为一段一段的语音帧,每一帧一般取10~30ms,我们的研究就建立在每一帧的语音特征分析上。  提取的不同的语音特征参数对应着不同的语音信号分析方法:时域分析、频域分析、倒谱域分析...由于语音信号最重要的感知特性反映在功率谱上,而相位变化只起到很小的作用,所有语音频域分析更加重要。信号加窗 1、矩形窗w(n)={100≤n≤L−1其他w(n)={10≤n≤L−10其他

2、汉明窗(Hamming)w(n)={12(1−cos(2πnL−1))00≤n≤L−1其他w(n)={12(1−cos(2πnL−1))0≤n≤L−10其他

3、海宁窗(Hanning)w(n)={0.54−0.46cos(2πnL−1)00≤n≤L−1其他w(n)={0.54−0.46cos(2πnL−1)0≤n≤L−10其他

通常对信号截断、分帧需要加窗,因为截断都有频域能量泄露,而窗函数可以减少截断带来的影响。窗函数在scipy.signal信号处理工具箱中,如hanning窗:https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif signal.hanning(winl)
https://img2018.cnblogs.com/blog/1433301/201812/1433301-20181210211241069-1343493967.png信号分帧在分帧中,相邻两帧之间会有一部分重叠,帧长(wlen) = 重叠(overlap)+帧移(inc),如果相邻两帧之间不重叠,那么由于窗函数的形状,截取到的语音帧边缘会出现损失,所以要设置重叠部分。inc为帧移,表示后一帧第前一帧的偏移量,fs表示采样率,fn表示一段语音信号的分帧数。N−overlapinc=N−wlen+incincN−overlapinc=N−wlen+incinc

信号分帧的理论依据,其中x是语音信号,w是窗函数:https://img2018.cnblogs.com/blog/1433301/201812/1433301-20181210212141283-1270468093.png加窗截断类似采样,为了保证相邻帧不至于差别过大,通常帧与帧之间有帧移,其实就是插值平滑的作用。给出示意图:https://img2018.cnblogs.com/blog/1433301/201812/1433301-20181210212227289-1447168358.png 这里主要用到numpy工具包,涉及的指令有:
[*]np.repeat:主要是直接重复

[*]np.tile:主要是周期性重复
对比一下:向量情况:https://images2015.cnblogs.com/blog/1085343/201705/1085343-20170504072335382-1109297397.png矩阵情况:对于数据:https://images2015.cnblogs.com/blog/1085343/201705/1085343-20170504073020695-597786520.pngrepeat操作:https://images2015.cnblogs.com/blog/1085343/201705/1085343-20170504073047757-590337536.pngtile操作:https://images2015.cnblogs.com/blog/1085343/201705/1085343-20170504073143382-1121353603.png对应结果:https://images2015.cnblogs.com/blog/1085343/201705/1085343-20170504073200726-1156485896.pnghttps://images2015.cnblogs.com/blog/1085343/201705/1085343-20170504073213492-895792872.png对应分帧的代码实现: 这是没有加窗的示例:https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif 没有加窗的语音分帧
https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif 加窗的语音分帧
语音信号的短时时域处理短时能量和短时平均幅度  短时能量和短时平均幅度的主要用途:
[*]区分浊音和清音段,因为浊音的短时能量<span class="MathJax" id="MathJax-Element-6-Frame" tabindex="0" data-mathml="E(i)" role="presentation" style="display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border: 0px; position: relative;">E(i)E(i)比清音大很多;
[*]区分声母和韵母的分界和无话段和有话段的分界
短时平均过零率  对于连续语音信号,过零率意味着时域波形通过时间轴,对于离散信号,如果相邻的取样值改变符号,则称为过零。作用:发浊音时由于声门波引起谱的高频跌落,所以语音信号能量约集中在3kHz以下发清音时多数能量集中在较高的频率上,因为高频意味着高的短时平均过零率,低频意味着低的短时平均过零率,所以浊音时具有较低的过零率,而清音时具有较高的过零率。利用短时平均过零率可以从背景噪声中找出语音信号,2、可以用于判断寂静无话段与有话段的起点和终止位置。3、在背景噪声较小的时候,用平均能量识别较为有效,在背景噪声较大的时候,用短时平均过零率识别较为有效。短时自相关函数短时自相关函数主要应用于端点检测和基音的提取,在韵母基因频率整数倍处将出现峰值特性,通常根据除R(0)外的第一峰值来估计基音,而在声母的短时自相关函数中看不到明显的峰值。短时平均幅度差函数用于检测基音周期,而且在计算上比短时自相关函数更加简单。语音信号的短时频域处理  在语音信号处理中,在语音信号处理中,信号在频域或其他变换域上的分析处理占重要的位置,在频域上研究语音可以使信号在时域上无法表现出来的某些特征变得十分明显,一个音频信号的本质是由其频率内容决定的,将时域信号转换为频域信号一般对语音进行短时傅里叶变换。fft_audio = np.fft.fft(audio)https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif 绘制语音信号的频谱图
https://img2018.cnblogs.com/blog/1433301/201812/1433301-20181211094554626-1731795666.png提取频域特征将信号转换为频域之后,还需要将其转换为有用的形式,梅尔频率倒谱系数(MFCC),MFCC首先计算信号的功率谱,然后用滤波器组和离散余弦变换的组合来提取特征。https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif 提取MFCC特征
语谱图  语谱图综合了时域和频域的特点,明显的显示出来了语音频率随时间的变化情况,语谱图的横轴为时间,纵轴为频率任意给定频率成分在给定时刻的强弱用颜色深浅表示。颜色深表示频谱值大,颜色浅表示频谱值小,语谱图上不同的黑白程度形成不同的纹路,称为声纹,不用讲话者的声纹是不一样的,可以用做声纹识别。其实得到了分帧信号,频域变换取幅值,就可以得到语谱图,如果仅仅是观察,matplotlib.pyplot有specgram指令:https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif 语谱图
https://img2018.cnblogs.com/blog/1433301/201812/1433301-20181210212725561-1426476463.png 语音识别https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif View Code
最后一个代码没有跑通,有很多原因,skearn-learn的库中minture库改动了。我看考下面的连接做了一些修改,但是还是跑不起来,应该就差一点点,后续我会把代码再调试。语音数据集在这里。




参考文献:网址:用python做科学计算 http://old.sebug.net/paper/books/scipydoc/index.html#python标准库wave模块https://docs.python.org/3.6/library/wave.html《python机器学习经典案例》美Prateek Joshi著傅里叶变换的介绍:http://www.thefouriertransform.com/各种音阶及其对应的频率 http://pages.mtu.edu/~suits/notefreqs.html这篇博客的代码https://github.com/LXP-Neve/Speech-signal-processing
页: [1]
查看完整版本: python做语音信号处理