赞
踩
参考资料:
实验目的:
1、 读取音频数据
2、 绘制单通道及双通道音频波形
3、 计算语音信号短时能量与短时过零率
4、 绘制语谱图并观察语谱图中音频的基音周期、频率与共振峰
准备工作:
首先,我们需要 import 几个工具包,一个是 python 标准库中的 wave 模块,用于音频处理操作,另外两个是 numpy 和 matplot,提供数据处理函数,这两个工具包的安装请参考 Python图像处理入门中的相关介绍。
一:读取本地音频数据
处理音频第一步是需要从让计算机“听到”声音,这里我们使用 python 标准库中自带的 wave模块进行音频参数的获取。
(1) 导入 wave 模块
(2) 使用 wave 中的函数 open 打开音频文件,wave.open(file,mode)函数带有两个参数, 第一个 file 是所需要打开的文件名及路径,使用字符串表示;第二个 mode 是打开的模式,也是用字符串表示 (’rb’或’wb’)
(3) 打开音频后使用 getparams() 获取音频基本的相关参数
getparams:
一次性返回所有的WAV文件的格式信息,它返回的是一个组元(tuple):声道数, 量化位数(byte单位), 采样频率, 采样点数, 压缩类型, 压缩类型的描述。wave模块只支持非压缩的数据,因此可以忽略最后两个信息。
nchannels:声道数;
sampwidth:量化位数或量化深度(byte);
framerate:采样频率;
nframes:采样点数
- # 导入 wave 模块
- import wave
- # 用于绘制波形图
- import matplotlib.pyplot as plt
- # 用于计算波形数据
- import numpy as np
- # 用于系统处理,如读取本地音频文件
- import os
-
- # 打开WAV文档
- f = wave.open(r"2.wav",'rb' )
- # 读取格式信息
- params = f.getparams ()
- nchannels,sampwidth, framerate, nframes = params [:4]
- print(framerate)
仅支持特定格式的wav,手机录音的wav不经过处理无法使用
网络下载的wav素材测试通过
二:读取单通道音频,并绘制波形图(常见音频为左右2个声道)
(1) 通过第一步,可以继续读取音频数据本身,保存为字符串格式
readframes:
读取声音数据,传递一个参数指定需要读取的长度(以取样点为单位),readframes返回的是二进制数据(一大堆bytes),在Python中用字符串表示二进制数据。
strData = f.readframes(nframes)
(2) 如果需要绘制波形图,则需要将字符串格式的音频数据转化为 int 类型
fromstring:
根据声道数和量化单位,将读取的二进制数据转换为一个可以计算的数组。
通过fromstring函数将字符串转换为数组,通过其参数dtype指定转换后的数据格式。
waveData = np.fromstring(strData,dtype=np.int16)
此处需要使用到 numpy 进行数据格式的转化
(3) 将幅值归一化
把数据变成(0,1)之间的小数。主要是为了数据处理方便提出来的,把数据映射到0~1范围之内处理,更加便捷快速。
waveData = waveData*1.0/(max(abs(waveData)))
这一步去掉也可画出波形图,大家可以尝试不用此步,找出波形图的不同
(4) 绘制图像
通过取样点数和取样频率计算出取样的时间:
time = np.arange(0,nframes)*(1.0 / framerate)
plot:
1、plot(y)
以y的分量为纵坐标,以元素序号为横坐标,用直线依次连接数据点。
2、plot(x,y)
以x为横坐标,y为纵坐标。
3、plot(x1,y1,x2,y2,……)
在此格式中,每对x,y必须符合plot(x,y)中的要求,不同对之间没有影响,命令将对每一对x,y绘制曲线。
subplot:
subplot(m,n,p)
m 代表行,n 代表列,将数字窗口分成m×n的网格,p 代表的待绘图形的位置序号。
- import wave
- # 导入 wave 模块
- import matplotlib.pyplot as plt
- # 用于绘制波形图
- import numpy as np
- # 用于计算波形数据
- import os
- # 用于系统处理,如读取本地音频文件
-
- f = wave.open(r"di.wav",'rb' )
- params = f.getparams ()
- nchannels,sampwidth, framerate, nframes = params [:4]
- print(framerate)
-
- # 读取波形数据
- strData = f.readframes(nframes)
- # 将字符串转换为16位整数
- waveData = np.fromstring(strData,dtype=np.int16)
- # 幅值归一化
- waveData = waveData*1.0/(max(abs(waveData)))
- #计算音频的时间
- time = np.arange(0,nframes)*(1.0 / framerate)
-
- plt.plot(time,waveData)
- plt.xlabel("Time(s)")
- plt.ylabel("Amplitude")
- plt.title("Single channel wavedata")
- plt.show()
自己录的还是不行,下载的可以
如图
三: 多通道语音数据读取
(1) 唯一不一样的地方就是 waveData 是一个多维的矩阵了。
plt.plot(time,waveData[:,n])
#绘制waveData数据的第n列,即音频第n个通道的数据
reshape:
b = reshape(a,m,n)
更改矩阵的行列数,返回一个m*n的矩阵b, b中元素是按列从a中得到的。
如果a中元素个数没有m*n个, 则会报错。
waveData[:,0]
表示对waveData这个二维数组,取第一维中的所有数据,取第二维中第0(或1)个数据
waveData[:,0] 取所有行的第0个数据,
waveData[:,1] 取所有行的第1个数据。
- import wave
- # 导入 wave 模块
- import matplotlib.pyplot as plt
- # 用于绘制波形图
- import numpy as np
- # 用于计算波形数据
- import os
- # 用于系统处理,如读取本地音频文件
-
- f = wave.open(r"3.wav",'rb' )
- params = f.getparams ()
- nchannels,sampwidth, framerate, nframes = params [:4]#从0~ 4之前的数
- print(nchannels,sampwidth,framerate,nframes)
-
- # 读取波形数据
- strData = f.readframes(nframes)
- # 将波形数据转换为数组
- waveData = np.fromstring(strData,dtype=np.int16)
- waveData = waveData*1.0/(max(abs(waveData)))
- time = np.arange(0,nframes)*(1.0 / framerate)#计算音频的时间
-
- waveData = np.reshape(waveData,[nframes,nchannels])
- f.close()
-
- # 绘制波形
- plt.figure()
- plt.subplot(5,1,1)
- plt.plot(time,waveData[:,0])
- plt.xlabel("Time(s)")
- plt.ylabel("Amplitude")
- plt.title("Ch-1 wavedata")
- plt.subplot(5,1,3)
- plt.plot(time,waveData[:,1])
- plt.xlabel("Time(s)")
- plt.ylabel("Amplitude")
- plt.title("Ch-2 wavedata")
- plt.show()
这里用的双通道wav
四:语音信号短时能量
计算较短时间内的语音能量。这里短时指的是一帧(256个采样点)。
len:返回列表元素的个数。
energy = [] 新建空列表
energy.append(sum) 列表末尾增加元素
aList.sort() 列表内元素排序
def calEnergy(wave_data) :
energy = []
sum = 0
frameSize = 256
for i in range(len(wave_data)) :#计算每一帧的数据和
sum = sum + (wave_data[i] * wave_data[i])#采样点数据平方
if (i + 1) % frameSize == 0 :
energy.append(sum)
sum = 0
elif i == len(wave_data) - 1 :
energy.append(sum)
return energyenergy = calEnergy(waveData[:,0])
time2 = np.arange(0, len(energy)) * (len(waveData[:,0])/len(energy) / framerate)
- import wave
- # 导入 wave 模块
- import matplotlib.pyplot as plt
- # 用于绘制波形图
- import numpy as np
- # 用于计算波形数据
- import os
- # 用于系统处理,如读取本地音频文件
-
- def calEnergy(wave_data) :
- energy = []
- sum = 0
- frameSize = 256
- for i in range(len(wave_data)) :#计算每一帧的数据和
- sum = sum + (wave_data[i] * wave_data[i])#采样点数据平方
- if (i + 1) % frameSize == 0 :
- energy.append(sum)
- sum = 0
- elif i == len(wave_data) - 1 :
- energy.append(sum)
- return energy
-
- f = wave.open(r"3.wav",'rb' )
- params = f.getparams ()
- nchannels,sampwidth, framerate, nframes = params [:4]#从0~ 4之前的数
- print(nchannels,sampwidth,framerate,nframes)
-
- strData = f.readframes(nframes)
- waveData = np.fromstring(strData,dtype=np.int16)
- waveData = waveData*1.0/(max(abs(waveData)))
- time = np.arange(0,nframes)*(1.0 / framerate)#计算音频的时间
-
- waveData = np.reshape(waveData,[nframes,nchannels])
- f.close()
-
- plt.figure()
- plt.subplot(5,1,1)
- plt.plot(time,waveData[:,0])
- plt.xlabel("Time(s)")
- plt.ylabel("Amplitude")
- plt.title("Ch-1 wavedata")
- plt.subplot(5,1,3)
- plt.plot(time,waveData[:,1])
- plt.xlabel("Time(s)")
- plt.ylabel("Amplitude")
- plt.title("Ch-2 wavedata")
- plt.show()
-
- energy = calEnergy(waveData[:,0])
- time2 = np.arange(0, len(energy)) * (len(waveData[:,0])/len(energy) / framerate)
-
- plt.plot(time2, energy)
- plt.ylabel("short energy")
- plt.xlabel("time (seconds)")
- plt.show()
五:语音信号短时过零率
在每帧中(256个采样点),语音信号通过零点(从正变为负或从负变为正)的次数。
overLap 部分重叠
切片
[ 0 ] 读取第一个值
[ -1 ] 取最后一个元素
[1: ] 去掉列表中第一个元素(下标为0),去后面的元素进行操作
[ :-1] 除了最后一个取全部
[ :-2] 从位置0到位置-2之前的数
[ ::-1] 取从后向前(相反)的元素
[2::-1] 取从下标为2的元素翻转读取
b = a[ i : j ] 表示复制 a[ i ] 到 a[ j -1],以生成新的list对象。
def ZeroCR(wave_data,frameSize,overLap):
wlen = len(wave_data)
step = frameSize - overLap
frameNum = math.ceil(wlen/step)#帧的数量
zcr = np.zeros((frameNum,1))
for i in range(frameNum):#每帧过零的次数计算
curFrame = wave_data[np.arange(i*step,min(i*step+frameSize,wlen))]
curFrame = curFrame - np.mean(curFrame)
zcr[i] = sum(curFrame[0:-1]*curFrame[1::]<=0)
return zcrframeSize = 256
overLap = 0
zcr = ZeroCR(waveData[:,0],frameSize,overLap)
time3 = np.arange(0, len(zcr)) * (len(waveData[:,0])/len(zcr) / framerate)plt.plot(time3, zcr)
plt.ylabel("ZCR")
plt.xlabel("time (seconds)")
plt.show()
- import wave
- # 导入 wave 模块
- import matplotlib.pyplot as plt
- # 用于绘制波形图
- import numpy as np
- # 用于计算波形数据
- import os
- # 用于系统处理,如读取本地音频文件
- import math
-
- def calEnergy(wave_data) :
- energy = []
- sum = 0
- frameSize = 256
- for i in range(len(wave_data)) :#计算每一帧的数据和
- sum = sum + (wave_data[i] * wave_data[i])#采样点数据平方
- if (i + 1) % frameSize == 0 :
- energy.append(sum)
- sum = 0
- elif i == len(wave_data) - 1 :
- energy.append(sum)
- return energy
-
-
- def ZeroCR(wave_data,frameSize,overLap):
- wlen = len(wave_data)
- step = frameSize - overLap
- frameNum = math.ceil(wlen/step)#帧的数量
- zcr = np.zeros((frameNum,1))
- for i in range(frameNum):#每帧过零的次数计算
- curFrame = wave_data[np.arange(i*step,min(i*step+frameSize,wlen))]
- curFrame = curFrame - np.mean(curFrame)
- zcr[i] = sum(curFrame[0:-1]*curFrame[1::]<=0)
- return zcr
-
- f = wave.open(r"3.wav",'rb' )
- params = f.getparams ()
- nchannels,sampwidth, framerate, nframes = params [:4]#从0~ 4之前的数
- print(nchannels,sampwidth,framerate,nframes)
-
- strData = f.readframes(nframes)
- waveData = np.fromstring(strData,dtype=np.int16)
- waveData = waveData*1.0/(max(abs(waveData)))
- time = np.arange(0,nframes)*(1.0 / framerate)#计算音频的时间
-
- waveData = np.reshape(waveData,[nframes,nchannels])
- f.close()
-
- plt.figure()
- plt.subplot(5,1,1)
- plt.plot(time,waveData[:,0])
- plt.xlabel("Time(s)")
- plt.ylabel("Amplitude")
- plt.title("Ch-1 wavedata")
- plt.subplot(5,1,3)
- plt.plot(time,waveData[:,1])
- plt.xlabel("Time(s)")
- plt.ylabel("Amplitude")
- plt.title("Ch-2 wavedata")
- plt.show()
-
- energy = calEnergy(waveData[:,0])
- time2 = np.arange(0, len(energy)) * (len(waveData[:,0])/len(energy) / framerate)
-
- plt.plot(time2, energy)
- plt.ylabel("short energy")
- plt.xlabel("time (seconds)")
- plt.show()
-
- frameSize = 256
- overLap = 0
- zcr = ZeroCR(waveData[:,0],frameSize,overLap)
- time3 = np.arange(0, len(zcr)) * (len(waveData[:,0])/len(zcr) / framerate)
-
- plt.plot(time3, zcr)
- plt.ylabel("ZCR")
- plt.xlabel("time (seconds)")
- plt.show()
六:绘制语谱图
1. 绘制宽带语谱图
(1)设置帧长,包含采样点数,必须为2^n,n取小(如32,画出的图为宽带频谱图),n取大(如2048,画出的图为窄带频谱图)
framesize = 32
(2)计算离散傅里叶变换的点数,NFFT必须与时域的点数framsize相等,即不补零的FFT
NFFT = framesize
(3)设置帧与帧重叠部分采样点数,overlapSize约为每帧点数的1/3~1/2
overlapSize = 1.0 / 3 * framesize
overlapSize = int(round(overlapSize)) # 取整
(4)绘制频谱图
plt.specgram(waveData[:,0],NFFT=NFFT,Fs=framerate,window=np.hanning(M=framesize), noverlap=overlapSize)
(5)绘制
- import wave
- # 导入 wave 模块
- import matplotlib.pyplot as plt
- # 用于绘制波形图
- import numpy as np
- # 用于计算波形数据
- import os
- # 用于系统处理,如读取本地音频文件
- import math
-
- def calEnergy(wave_data) :
- energy = []
- sum = 0
- frameSize = 256
- for i in range(len(wave_data)) :#计算每一帧的数据和
- sum = sum + (wave_data[i] * wave_data[i])#采样点数据平方
- if (i + 1) % frameSize == 0 :
- energy.append(sum)
- sum = 0
- elif i == len(wave_data) - 1 :
- energy.append(sum)
- return energy
-
-
- def ZeroCR(wave_data,frameSize,overLap):
- wlen = len(wave_data)
- step = frameSize - overLap
- frameNum = math.ceil(wlen/step)#帧的数量
- zcr = np.zeros((frameNum,1))
- for i in range(frameNum):#每帧过零的次数计算
- curFrame = wave_data[np.arange(i*step,min(i*step+frameSize,wlen))]
- curFrame = curFrame - np.mean(curFrame)
- zcr[i] = sum(curFrame[0:-1]*curFrame[1::]<=0)
- return zcr
-
- f = wave.open(r"3.wav",'rb' )
- params = f.getparams ()
- nchannels,sampwidth, framerate, nframes = params [:4]#从0~ 4之前的数
- print(nchannels,sampwidth,framerate,nframes)
-
- strData = f.readframes(nframes)
- waveData = np.fromstring(strData,dtype=np.int16)
- waveData = waveData*1.0/(max(abs(waveData)))
- time = np.arange(0,nframes)*(1.0 / framerate)#计算音频的时间
-
- waveData = np.reshape(waveData,[nframes,nchannels])
- f.close()
-
- plt.figure()
- plt.subplot(5,1,1)
- plt.plot(time,waveData[:,0])
- plt.xlabel("Time(s)")
- plt.ylabel("Amplitude")
- plt.title("Ch-1 wavedata")
- plt.subplot(5,1,3)
- plt.plot(time,waveData[:,1])
- plt.xlabel("Time(s)")
- plt.ylabel("Amplitude")
- plt.title("Ch-2 wavedata")
- plt.show()
-
- energy = calEnergy(waveData[:,0])
- time2 = np.arange(0, len(energy)) * (len(waveData[:,0])/len(energy) / framerate)
-
- plt.plot(time2, energy)
- plt.ylabel("short energy")
- plt.xlabel("time (seconds)")
- plt.show()
-
- frameSize = 256
- overLap = 0
- zcr = ZeroCR(waveData[:,0],frameSize,overLap)
- time3 = np.arange(0, len(zcr)) * (len(waveData[:,0])/len(zcr) / framerate)
-
- plt.plot(time3, zcr)
- plt.ylabel("ZCR")
- plt.xlabel("time (seconds)")
- plt.show()
-
- framesize = 32
- NFFT = framesize
- overlapSize = 1.0 / 3 * framesize
- overlapSize = int(round(overlapSize)) # 取整
- plt.specgram(waveData[:,0], NFFT=NFFT, Fs=framerate,
- window=np.hanning(M=framesize), noverlap=overlapSize)
-
- plt.ylabel('Frequency')
- plt.xlabel('Time')
- plt.ylim(0, 6000)
- plt.title("Wide Band Spectrum")
- plt.show()
2.绘制窄带语谱图
- import wave
- # 导入 wave 模块
- import matplotlib.pyplot as plt
- # 用于绘制波形图
- import numpy as np
- # 用于计算波形数据
- import os
- # 用于系统处理,如读取本地音频文件
- import math
-
- def calEnergy(wave_data) :
- energy = []
- sum = 0
- frameSize = 256
- for i in range(len(wave_data)) :#计算每一帧的数据和
- sum = sum + (wave_data[i] * wave_data[i])#采样点数据平方
- if (i + 1) % frameSize == 0 :
- energy.append(sum)
- sum = 0
- elif i == len(wave_data) - 1 :
- energy.append(sum)
- return energy
-
-
- def ZeroCR(wave_data,frameSize,overLap):
- wlen = len(wave_data)
- step = frameSize - overLap
- frameNum = math.ceil(wlen/step)#帧的数量
- zcr = np.zeros((frameNum,1))
- for i in range(frameNum):#每帧过零的次数计算
- curFrame = wave_data[np.arange(i*step,min(i*step+frameSize,wlen))]
- curFrame = curFrame - np.mean(curFrame)
- zcr[i] = sum(curFrame[0:-1]*curFrame[1::]<=0)
- return zcr
-
- f = wave.open(r"3.wav",'rb' )
- params = f.getparams ()
- nchannels,sampwidth, framerate, nframes = params [:4]#从0~ 4之前的数
- print(nchannels,sampwidth,framerate,nframes)
-
- strData = f.readframes(nframes)
- waveData = np.fromstring(strData,dtype=np.int16)
- waveData = waveData*1.0/(max(abs(waveData)))
- time = np.arange(0,nframes)*(1.0 / framerate)#计算音频的时间
-
- waveData = np.reshape(waveData,[nframes,nchannels])
- f.close()
-
- plt.figure()
- plt.subplot(5,1,1)
- plt.plot(time,waveData[:,0])
- plt.xlabel("Time(s)")
- plt.ylabel("Amplitude")
- plt.title("Ch-1 wavedata")
- plt.subplot(5,1,3)
- plt.plot(time,waveData[:,1])
- plt.xlabel("Time(s)")
- plt.ylabel("Amplitude")
- plt.title("Ch-2 wavedata")
- plt.show()
-
- energy = calEnergy(waveData[:,0])
- time2 = np.arange(0, len(energy)) * (len(waveData[:,0])/len(energy) / framerate)
-
- plt.plot(time2, energy)
- plt.ylabel("short energy")
- plt.xlabel("time (seconds)")
- plt.show()
-
- frameSize = 256
- overLap = 0
- zcr = ZeroCR(waveData[:,0],frameSize,overLap)
- time3 = np.arange(0, len(zcr)) * (len(waveData[:,0])/len(zcr) / framerate)
-
- plt.plot(time3, zcr)
- plt.ylabel("ZCR")
- plt.xlabel("time (seconds)")
- plt.show()
-
- framesize = 32
- NFFT = framesize
- overlapSize = 1.0 / 3 * framesize
- overlapSize = int(round(overlapSize)) # 取整
- plt.specgram(waveData[:,0], NFFT=NFFT, Fs=framerate,
- window=np.hanning(M=framesize), noverlap=overlapSize)
-
- plt.ylabel('Frequency')
- plt.xlabel('Time')
- plt.ylim(0, 6000)
- plt.title("Wide Band Spectrum")
- plt.show()
-
- framesize = 2048
- framelength = framesize / framerate
- NFFT = framesize
- overlapSize = 1.0 / 3 * framesize
- overlapSize = int(round(overlapSize))
- plt.specgram(waveData[:,0], NFFT=NFFT,Fs=framerate,
- window=np.hanning(M=framesize),noverlap=overlapSize)
-
- plt.ylabel('Frequency')
- plt.xlabel('Time')
- plt.ylim(0, 6000)
- plt.title("Narrow Band Spectrum")
- plt.show()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。