Python语音处理入门

参考资料:

腾讯云 Python Wave模块开发者手册

Python Wave:声音的输入输出

NumPy Matplotlib | 菜鸟教程

subplot

python音频处理用到的操作

Python中的X[:,0]和X[:,1]

Python语音短时能量计算

实验目的:

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:返回列表元素的个数。

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

energy = 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 zcr

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()

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()

发布了119 篇原创文章 · 获赞 10 · 访问量 8933

猜你喜欢

转载自blog.csdn.net/weixin_43673589/article/details/104940667