python数据建模分析 - 语音识别

语音识别:

Getting Started!首先,我们要知道语音的产生过程

python数据建模分析 - 语音识别_第1张图片
voice.png

状态:由肺产生向外的气流,完全放松时声带张开,就是平时的呼吸。如果声带一张一合(振动)形成周期性的脉冲气流。这个脉冲气流的周期称之为——基音周期(题主所言因音色不同导致的频率不同,事实上音色的大多是泛频上的差异,建立在基频之上,这个基频就是基音周期了,泛频可以忽略)。当然啦,这只是在发浊音(b,d,v...)时才会有,当发出清音(p,t,f...)时声带不振动,但是会处于紧绷状态,当气流涌出时会在声带产生湍流。清音和浊音是音素的两大类。接下来脉冲气流/湍流到达声道,由声道对气流进行调制,形成不同的音素。多个音素组成一个音节(就汉语而言是[声母]+韵母)。如果没学过信号系统那就想像一下平舌音和翘舌音,z和zh发声时肺和喉的状态都一样,只是舌头动作不一样,发出的声音也就不一样了,这就算是简单的调制。从而声音的波形会发生一些变化。这个波形,就是以后分析所需要的数据。

标识声音的图像有以下三种

  • 频谱图
  • 时谱图
  • 语谱图

以千里码 语音识别-1为例,将Mp3文件转换成wav,分析其频谱图
参照wav使用手册,让我们介绍一下wav文件

WAV是Microsoft开发的一种声音文件格式,虽然它支持多种压缩格式,不过它通常被用来保存未压缩的声音数据(PCM脉冲编码调制)。WAV有三个重要的参数:声道数、取样频率和量化位数。

  • 声道数:可以是单声道或者是双声道
  • 采样频率:一秒内对声音信号的采集次数,常用的有8kHz, 16kHz, 32kHz, 48kHz, 11.025kHz, 22.05kHz, 44.1kHz
  • 量化位数:用多少bit表达一次采样所采集的数据,通常有8bit、16bit、24bit和32bit等几种

1.读入二进制音频流数据流程

  • 对于一个音频实例wf而言,通过调用它的方法读取WAV文件的格式和数据:getnchannels, getsampwidth, getframerate, getnframes等方法可以单独返回WAV文件的特定的信息。
  • readframes:读取声音数据,传递一个参数指定需要读取的长度(以取样点为单位),readframes返回的是二进制数据

PS:注意需要使用"rb"(二进制模式)打开文件

import wave
import pyaudio
import numpy
from matplotlib import pylab


#打开wav文档,文件路径根据需要修改
wf = wave.open("F:\\work\\war.wav","rb")

#创建PyAudio对象
p = pyaudio.PyAudio()


class Audio(object):

    def __init__(self):
        self.channels = wf.getnchannels()
        # 返回音频通道数
        self.rate = wf.getframerate()
        # 返回采样频率。
        self.format = p.get_format_from_width(wf.getsampwidth())
        # 返回指定宽度的PortAudio格式常量。
        self.stream = p.open(format=self.format,channels=self.channels,rate=self.rate,output=True)
        # 使用所需的音频参数在所需设备上打开一个流
        self.nframes = wf.getnframes()
        # 返回音频帧数
        self.collect_point_num = 44100
        # 采样点数,修改采样点数和起始位置进行不同位置和长度的音频波形分析
        self.start = 0
        # 开始采样位置
    def read_data(self):
        self.str_data = wf.readframes(self.nframes)
        # 读取声音数据,传递一个参数指定需要读取的长度(以取样点为单位),readframes返回的是二进制数据(即bytes数组)
        # print(self.str_data)
        wf.close()
        #关闭媒体流
if __name__ == '__main__':
    aduio = Audio()
    aduio.read_data()

python output

python数据建模分析 - 语音识别_第2张图片
aduio.jpg

2.生成的流媒体字节数组计算出每个取样的时间

  • 将读取的二进制数据转换为一个可以计算的数组
  • 通过fromstring函数将字符串转换为数组,通过其参数dtype指定转换后的数据格式,由于我们的声音格式是以两个字节表示一个取样值,因此采用short数据类型转换。现在我们得到的wave_data是一个一维的short类型的数组,但是因为我们的声音文件是双声道的,因此它由左右两个声道的取样交替构成:LRLRLRLR....LR(L表示左声道的取样值,R表示右声道取样值)'
  • 由帧率的计算公式:
    采样率 = 每秒中的采样频率/每秒中的采样点数 帧率(fps) =1 /采样率
  • 将波形数据转换为数组
  • 通过取样点数和取样频率计算出每个取样的时间
    def convrt_data(self):
        self.df = self.rate / (self.collect_point_num - 1)
        # 根据总平均法使用全局帧数除以全局时间,以求出帧率
    def Data_collection(self):
        wave_data = numpy.fromstring(self.str_data,dtype=numpy.short)
        # 根据声道数和量化单位,将读取的二进制数据转换为一个可以计算的数组

        wave_data.shape = -1, 2
        # -1代表左声道,2代表右声道
        # 通过fromstring函数将字符串转换为数组,通过其参数dtype指定转换后的数据格式,由于我们的声音格式是以两个字节表示一个取样值,
        # 因此采用short数据类型转换。现在我们得到的wave_data是一个一维的short类型的数组,但是因为我们的声音文件是双声道的,
        # 因此它由左右两个声道的取样交替构成:LRLRLRLR....LR(L表示左声道的取样值,R表示右声道取样值)。修改wave_data的sharp之后:

        # 将wave_data数组改为2列,行数自动匹配。在修改shape的属性时,需使得数组的总长度不变。v
        wave_data = wave_data.T
        # 将波形数据转换为数组
        freq = [self.df*self.collect_point_num for n in range(0,self.collect_point_num)]
        # 通过取样点数和取样频率计算出每个取样的时间

3.划分采样位置,建立频谱图坐标系,根据采样时间标记采样点在频谱图上的位置

  • wave_data2保存声音字节数组转置后的结果,为列数为1存储的数组
  • 固定第一位,划分第二维区间从0一直扫描到行尾
  • 避免波形字节数组过长,利用numpy.fft.fft对压缩为1/2的波形字节数组进行快速傅里叶变换,常规显示采样频率一半的频谱
  • 设定如果每个取样点的取样时间大于4000ms,分隔单位为10的波形数组显示
 def Data_collection(self):
        wave_data = numpy.fromstring(self.str_data,dtype=numpy.short)
        # 根据声道数和量化单位,将读取的二进制数据转换为一个可以计算的数组

        wave_data.shape = -1.2
        # -1代表左声道,2代表右声道
        # 通过fromstring函数将字符串转换为数组,通过其参数dtype指定转换后的数据格式,由于我们的声音格式是以两个字节表示一个取样值,
        # 因此采用short数据类型转换。现在我们得到的wave_data是一个一维的short类型的数组,但是因为我们的声音文件是双声道的,
        # 因此它由左右两个声道的取样交替构成:LRLRLRLR....LR(L表示左声道的取样值,R表示右声道取样值)。修改wave_data的sharp之后:

        # 将wave_data数组改为2列,行数自动匹配。在修改shape的属性时,需使得数组的总长度不变。v
        wave_data = wave_data.T
        # 将波形数据转换为数组
        freq = [self.df*self.collect_point_num for n in range(0,self.collect_point_num)]
        # 通过取样点数和取样频率计算出每个取样的时间
        wave_data2 = wave_data[0][self.start:self.start+self.collect_point_num]
        c = numpy.fft.fft(wave_data2)*2/self.collect_point_num
        # 常规显示采样频率一半的频谱
        d = int(len(c)/2)
        while freq[0] > 4000:
            d -= 10
            pylab.plot(freq[:d-1],abs(c[:d-1]),"r")
            pylab.show()

python console:频谱的时间段划分似乎造成了误差,导致统计结果趋于集中

python数据建模分析 - 语音识别_第3张图片
fft.png

Test1:打印波形字节数组长度以及每个采样点采样花费的时间

 def Data_collection(self):
        wave_data = numpy.fromstring(self.str_data,dtype=numpy.short)
        # 根据声道数和量化单位,将读取的二进制数据转换为一个可以计算的数组

        wave_data.shape = -1, 2
        # -1代表左声道,2代表右声道
        # 通过fromstring函数将字符串转换为数组,通过其参数dtype指定转换后的数据格式,由于我们的声音格式是以两个字节表示一个取样值,
        # 因此采用short数据类型转换。现在我们得到的wave_data是一个一维的short类型的数组,但是因为我们的声音文件是双声道的,
        # 因此它由左右两个声道的取样交替构成:LRLRLRLR....LR(L表示左声道的取样值,R表示右声道取样值)。修改wave_data的sharp之后:

        # 将wave_data数组改为2列,行数自动匹配。在修改shape的属性时,需使得数组的总长度不变。v
        wave_data = wave_data.T
        # 将波形数据转换为数组
        freq = [self.df*self.collect_point_num for n in range(0,self.collect_point_num)]
        print(freq)
        # 通过取样点数和取样频率计算出每个取样的时间
        wave_data2 = wave_data[0][self.start:self.start+self.collect_point_num]
        c = numpy.fft.fft(wave_data2)*2/self.collect_point_num
        d = int(len(c)/2)
        print(d)

python console

python数据建模分析 - 语音识别_第4张图片
print.jpg

Test2: 原来是设定的采样时间过小,修改统计条件为 freq[0] > 44101

    def Data_collection(self):
        wave_data = numpy.fromstring(self.str_data,dtype=numpy.short)
        # 根据声道数和量化单位,将读取的二进制数据转换为一个可以计算的数组

        wave_data.shape = -1, 2
        # -1代表左声道,2代表右声道
        # 通过fromstring函数将字符串转换为数组,通过其参数dtype指定转换后的数据格式,由于我们的声音格式是以两个字节表示一个取样值,
        # 因此采用short数据类型转换。现在我们得到的wave_data是一个一维的short类型的数组,但是因为我们的声音文件是双声道的,
        # 因此它由左右两个声道的取样交替构成:LRLRLRLR....LR(L表示左声道的取样值,R表示右声道取样值)。修改wave_data的sharp之后:

        # 将wave_data数组改为2列,行数自动匹配。在修改shape的属性时,需使得数组的总长度不变。v
        wave_data = wave_data.T
        # 将波形数据转换为数组
        freq = [self.df*self.collect_point_num for n in range(0,self.collect_point_num)]
        print(freq)
        # 通过取样点数和取样频率计算出每个取样的时间
        wave_data2 = wave_data[0][self.start:self.start+self.collect_point_num]
        c = numpy.fft.fft(wave_data2)*2/self.collect_point_num
        d = int(len(c)/2)
        print(d)

        while freq[0] > 44101:
            d -= 0.1
            pylab.plot(freq[:d-1],abs(c[:d-1]),"r")
            pylab.show()

python console采样的分布过于密集,不适合用频谱图进行统计


python数据建模分析 - 语音识别_第5张图片
pinpu.png

Test3:使用波形图,分别用subplot211与subplot212标识左右声道的波形

import wave
import pyaudio
import numpy
from matplotlib import pylab


#打开wav文档,文件路径根据需要修改
wf = wave.open("F:\\work\\war.wav","rb")

#创建PyAudio对象
p = pyaudio.PyAudio()


class Audio(object):

    def __init__(self):
        self.channels = wf.getnchannels()
        # 返回音频通道数
        self.rate = wf.getframerate()
        # 返回采样频率。
        self.format = p.get_format_from_width(wf.getsampwidth())
        # 返回指定宽度的PortAudio格式常量。
        self.stream = p.open(format=self.format,channels=self.channels,rate=self.rate,output=True)
        # 使用所需的音频参数在所需设备上打开一个流
        self.nframes = wf.getnframes()
        # 返回音频帧数
        self.collect_point_num = 44100
        # 采样点数,修改采样点数和起始位置进行不同位置和长度的音频波形分析
        self.start = 0
        # 开始采样位置
    def read_data(self):
        self.str_data = wf.readframes(self.nframes)
        # 读取声音数据,传递一个参数指定需要读取的长度(以取样点为单位),readframes返回的是二进制数据(即bytes数组)
        # print(self.str_data)
        wf.close()

    def convert_data(self):
        self.df = self.rate / (self.collect_point_num - 1)
        # print(self.df)
        # 使用全局采样频率除以全局采样点数,以求出帧率
    def Data_collection(self):
        wave_data = numpy.fromstring(self.str_data,dtype=numpy.short)
        # 根据声道数和量化单位,将读取的二进制数据转换为一个可以计算的数组

        wave_data.shape = -1, 2
        # -1代表左声道,2代表右声道
        # 通过fromstring函数将字符串转换为数组,通过其参数dtype指定转换后的数据格式,由于我们的声音格式是以两个字节表示一个取样值,
        # 因此采用short数据类型转换。现在我们得到的wave_data是一个一维的short类型的数组,但是因为我们的声音文件是双声道的,
        # 因此它由左右两个声道的取样交替构成:LRLRLRLR....LR(L表示左声道的取样值,R表示右声道取样值)。修改wave_data的sharp之后:

        # 将wave_data数组改为2列,行数自动匹配。在修改shape的属性时,需使得数组的总长度不变。v
        wave_data = wave_data.T
        # 将波形数据转换为数组
        freq = [self.df*self.collect_point_num for n in range(0,self.collect_point_num)]
        # print(freq)
        # 通过取样点数和取样频率计算出每个取样的时间
        wave_data2 = wave_data[0][self.start:self.start+self.collect_point_num]
        c = numpy.fft.fft(wave_data2)*2/self.collect_point_num
        d = int(len(c)/2)
        # print(d)

        while freq[0] > 44101:
            d -= 20
            pylab.plot(freq[:d-1],c[:d-1],"r")
            pylab.show()


    def wavread(self):
        wavfile = wf
        params = wavfile.getparams()
        framesra, frameswav = params[2], params[3]
        datawav = wavfile.readframes(frameswav)
        wavfile.close()
        datause = numpy.fromstring(datawav, dtype=numpy.short)
        datause.shape = -1, 2
        datause = datause.T
        time = numpy.arange(0, frameswav) * (1.0 / framesra)
        return datause, time


    def work(self):
        self.read_data()
        self.convert_data()
        self.Data_collection()

if __name__ == '__main__':
    aduio = Audio()
    # aduio.work()

    wavdata, wavtime = aduio.wavread()
    pylab.title("Night.wav's Frames")
    pylab.subplot(211)
    pylab.plot(wavtime, wavdata[0], color='green')
    pylab.subplot(212)
    pylab.plot(wavtime, wavdata[1])
    pylab.show()

python console

python数据建模分析 - 语音识别_第6张图片
wave.png

你可能感兴趣的:(python数据建模分析 - 语音识别)