美文网首页Go! Go! Python
python数据建模分析 - 语音识别

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

作者: 语落心生 | 来源:发表于2017-08-07 10:21 被阅读0次

    语音识别:

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

    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

    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:频谱的时间段划分似乎造成了误差,导致统计结果趋于集中

    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

    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采样的分布过于密集,不适合用频谱图进行统计


    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

    wave.png

    相关文章

      网友评论

        本文标题:python数据建模分析 - 语音识别

        本文链接:https://www.haomeiwen.com/subject/nitnlxtx.html