python编程实现语音数据分帧及分帧还原

主要功能

本文主要实现使用python编程实现对我上一个博客写的python编程读取wav文件数据得到的语音数据进行分帧,同时也编程实现将分帧后的数据还原为原始语音数据。
第一部分主要实现用python编程实现对语音数据进行分帧。
第二部分实现将分帧后的数据还原成原始语音数据,语音数据为单声道的。
输入输出数据文件均从命令行输入,分帧的帧长和帧移也从命令行输入。
附件包含两个程序,一个是语音数据分帧,另一个是分帧数据还原,其中分帧程序中English.txt为分帧前的语音数据,Englishframe1.txt为帧长为4,帧移为2进行分帧得到的结果。Englishframe2.txt为为帧长为8,帧移为4进行分帧得到的结果。每个数据之间采用空格分隔,一行一帧数据。English.wav为原始语音文件。

第一部分 分帧

对输入的语音数据进行分帧,语音数据文件,分帧数据文件(分帧后的数据保存在文件中),帧长,帧移均采用命令行方式输入。

程序

程序主要由两部分组成,一部分为main子函数部分,判断命令行参数,并读入数据文件,同时调用分帧函数,返回分帧后的数据,保存到文件中。另一部分为enframe分帧函数部分,主要对main函数部分传过来的语音数据进行分帧,返回分帧后的矩阵。

#将txt语音数据进行分帧
# -*- coding: utf-8 -*-
import numpy as np
import sys
import wave   #语音文件处理包
import getopt

def enframe(data, wlen, inc):   #data为语音数据,wlen为帧长,inc为帧移
    data_length = len(data)  # 信号总长度
    if data_length <= wlen:  # 若信号长度小于一个帧的长度,则帧数定义为1
        nf = 1
    else:  # 否则,计算帧数
        nf = int(np.ceil((1.0 * data_length - wlen + inc) / inc))
        #np.ceil计算大于等于改值的最小整数,将小数点后部分删除
    pad_length = int((nf - 1) * inc + wlen)  # 所有帧加起来总的铺平后的长度
    #zeros = np.zeros((pad_length - data_length,))
    #pad_signal = np.concatenate((data, zeros))  # 补充完整的语音数据
    pad_signal = np.pad(data, (0, pad_length - data_length), 'constant')  # 用0填充最后不足一帧的数据
    indices = np.tile(np.arange(0, wlen), (nf, 1)) + np.tile(np.arange(0, nf * inc, inc), (wlen, 1)).T  #每帧的索引,将原矩阵横向、纵向地复制展开
    #tile() 函数,就是将原矩阵横向、纵向地复制展开
    indices = np.array(indices, dtype=np.int32)  # 将indices转化为矩阵,数值类型为32位整型
    frames = pad_signal[indices]  # 得到帧信号, 用索引拿数据

    return frames  #返回分帧后的语音数据矩阵

def main(argv):  #定义一个函数
    try:  #首先执行try后的程序,如果输入格式不对,则执行except getopt.GetoptError:后的程序
        opts, args = getopt.getopt(argv, "i:o:-f:-l:h", ["input", "output","framelength=","overlap=","help"])  #命令行输入参数
    except getopt.GetoptError:
        print('输入参数错误,输入格式为:python wavtxtframe.py -i English.txt -o Englishframe.txt -f 4 -l 2,\n其中wavtxtframe.py为程序文件名称,English.txt为语音数据文件,Englishframe.txt为分帧后的语音数据文件,\n-f为分帧的帧长,-l为帧移')
        sys.exit()
    #global file
    for opt, arg in opts:
        if opt in ("-h", "--help"):   #打印帮助
            #test.wav为单声道语音文件,test2.wav为双声道语音文件
            print('输入格式为:')
            print('python wavtxtframe.py -i English.txt -o Englishframe.txt -f 4 -l 2')
            print('或者:python wavtxtframe.py -i English.txt -o Englishframe.txt --framelength 4 --overlap 2')
            print('其中wavtxtframe.py为程序文件名称,English.txt为语音数据文件,Englishframe.txt为分帧后的语音数据文件')
            print('-f/-framenlength为分帧的帧长,-l/overlap为帧移' )
            sys.exit()
        elif opt in ("-i", "--input"):
            input = arg   #取命令行参数,即输入
            file1 = open(input, 'rb')  # 打开语音数据文件
        elif opt in ("-o", "--output"):
            output = arg   #取命令行参数,即输出
            file2 = open(output, 'w+')  # 打开分帧后的数据保存的txt文件
        elif opt in ("-f", "--framelength"):
            framelength = arg   #取命令行framelength后的参数,即帧长
            framelength = int(framelength)
        elif opt in ("-l", "--overlap"):
            overlap = arg  #取命令行overlap后的参数,即帧移
            overlap = int(overlap)

            line = file1.readline()  # 每次读出txt文件中的一行内容
            data = []  #初始化一个空矩阵
            while line:   #当未读到文件最后时
                # 把切分出的列表的每个值, 把它们转成np.short型, 并返回迭代器
                num = list(map(np.short, line.split()))  # np.short
                # 用list函数把map函数返回的迭代器遍历展开成一个列表
                data.append(num)
                line = file1.readline()
            file1.close()
            wavdata = np.array(data)  # 将列表数据转换成数组
            signal = wavdata.T  #将列矩阵转为行矩阵
            Frame = enframe(signal[0], framelength, overlap)  # 调用分帧函数
            #for i in Frame:

            np.savetxt(file2, Frame, fmt='%d', delimiter=' ')
            print(Frame)
            file2.close()

    '''
    lines = f.readlines()
    long = len(lines)
    for line in lines:
        
        for i in range(4):
            line1 = line
            file.write(line1)
            print("\t")
        print("\n")
        
        data = np.reshape(line, [long, 4])
    file.close()  # 关闭输出文件
    f.close()  # 关闭输入的语音文件
    exit()
    '''
if __name__ == "__main__":
    # sys.argv[1:]为要处理的参数列表,sys.argv[0]为脚本名,所以用sys.argv[1:]过滤掉脚本名。
    main(sys.argv[1:])  #调用函数


#https://blog.csdn.net/luolinll1212/article/details/98940838
#python wavtxtframe.py -i English.txt -o Englishframe1.txt -f 4 -l 2
#python wavtxtframe.py -i English.txt -o Englishframe1.txt --framelength 4 --overlap 2
#python wavtxtframe.py -i English.txt -o Englishframe2.txt --framelength 8 --overlap 4

结果

1 查看帮助文档

python wavtxtframe.py -h

运行结果:

python编程实现语音数据分帧及分帧还原_第1张图片

2 分帧,帧长为4,帧移为2

python wavtxtframe.py -i English.txt -o Englishframe1.txt -f 4 -l 2

运行结果:

python编程实现语音数据分帧及分帧还原_第2张图片

另一种运行方式:

python wavtxtframe.py -i English.txt -o Englishframe1.txt --framelength 4 --overlap 2

运行结果:

python编程实现语音数据分帧及分帧还原_第3张图片

分帧后,数据保存到文件中的结果:

python编程实现语音数据分帧及分帧还原_第4张图片

3 分帧,帧长为8,帧移为4

python wavtxtframe.py -i English.txt -o Englishframe2.txt --framelength 8 --overlap 4
#或者:
python wavtxtframe.py -i English.txt -o Englishframe2.txt -f 8 -l 4

运行结果:

python编程实现语音数据分帧及分帧还原_第5张图片

分帧后,数据保存文件结果:

python编程实现语音数据分帧及分帧还原_第6张图片

查看文件中分帧后的数据,可以发现,分帧程序可以按照命令行输入的帧长和帧移进行分帧,且结果正确。

第二部分 分帧数据还原

程序

程序主要将分帧后的数据还原成原始语音数据。

#将分帧之后的语音数据还原为分帧之前的语音数据
import numpy as np
import sys
import wave   #语音文件处理包
import getopt
import re

def main(argv):  # 定义一个函数
    try:  # 首先执行try后的程序,如果输入格式不对,则执行except getopt.GetoptError:后的程序
        opts, args = getopt.getopt(argv, "i:o:-f:-l:h",["input", "output", "framelength=", "overlap=", "help"])  # 命令行输入参数
    except getopt.GetoptError:
        print('输入参数错误,输入格式为:python frametxtwav.py -i Englishframe2.txt -o Englishframetxtwav.txt --framelength 8 --overlap 4,\n其中frametxtwav.py为程序文件名称,程序功能是通过分帧后的数据得到原始语音数据,Englishframe2.txt为分帧后的数据文件, Englishframetxtwav.txt为还原的语音数据文件,\n --framelength为分帧的帧长,--overlap为帧移')
        sys.exit()
    # global file
    for opt, arg in opts:
        if opt in ("-h", "--help"):  # 打印帮助
            # test.wav为单声道语音文件,test2.wav为双声道语音文件
            print('输入格式为:')
            print('python framtxtwave.py -i Englishframe1.txt -o Englishframetxtwav1.txt --framelength 4 --overlap 2')
            print('或者:python framtxtwave.py -i Englishframe1.txt -o Englishframetxtwav1.txt -f 4 -l 2')
            print('其中frametxtwav.py为程序文件名称,程序功能是通过分帧后的数据得到原始语音数据,Englishframe2.txt为分帧后的数据文件, Englishframetxtwav.txt为还原的语音数据文件')
            print('-f/-framenlength为分帧的帧长,-l/overlap为帧移')
            sys.exit()
        elif opt in ("-i", "--input"):
            input = arg  # 取命令行参数,即输入
            file1 = open(input, 'rb')  # 打开分帧后的语音数据文件
        elif opt in ("-o", "--output"):
            output = arg  # 取命令行参数,即输出
            file2 = open(output, 'w+')  # 打开用于保存分帧前的数据的txt文件
        elif opt in ("-f", "--framelength"):
            framelength = arg  # 取命令行framelength后的参数,即帧长
            framelength = int(framelength)
        elif opt in ("-l", "--overlap"):
            overlap = arg  # 取命令行overlap后的参数,即帧移
            overlap = int(overlap)
            x = overlap  #帧移,即每行中原始语音数据的长度
            line1 = file1.readline()  # 每次读出txt文件中的一行内容
            #data = []  # 初始化一个空矩阵
            print('txt文件中的数据的数据类型为:\n', type(line1))  #返回从txt文件中得到的数据的数据类型
            line = line1.decode(encoding='utf-8')  #按utf-8的方式解码,解码成字符串,因为存入txt文件的数据为bytes型,要转为str
            while line:   #当未读到文件末尾
            #for line in lines:

                #x = re.split(r' ', line)[0]  # 正则化以空格分割一行数据,取分割出来的第一个数据
                #y = re.split(r' ', line)[1]
                #line = str(x) + ' ' + str(y) + ' ' +  '\n'  # 重新写行数据
                for i in range(x):   #循环读取一行中的前x个数据
                    #content = str(re.split(r' ', line)[i]) + ' '
                    content = str(re.split(r' ', line)[i]) + '\n'    #读取文件中的前overlap列数据,即未分帧前的数据,去掉了重复的数据
                    file2.write(content)   #将数据写入另一个txt文件中
                    #content = str(re.split(r' ', line)[0]) + ' '+str(re.split(r' ', line)[1]) + ' ' +str(re.split(r' ', line)[i-1])+'\n'
                #line = str(x) + ' ' + '\n'  # 重新写行数据
                #file2.write(line)
                #file2.write(content)
                #content1 = '\n'
                #file2.write(content1)
                line1 = file1.readline()  # 每次读出txt文件中的一行内容
                line = line1.decode(encoding='utf-8')  #按utf-8的方式解码,解码成字符串,因为存入txt文件的数据为bytes型,要转为str
            file1.close()

            #获取分帧数据文件的最后一帧的framelength-overlap长度的数据,并存入文件中
            file1 = open(input, 'rb')  # 打开分帧后的语音数据文件
            line1 = file1.readlines()
            long = len(line1)
            print('文件行数为:',long)
            print('txt文件中的数据的数据类型为:\n', type(line1))  # 返回从txt文件中得到的数据的数据类型
            last_line = line1[-1].decode('utf-8')
            #line = last_line.decode(encoding='utf-8')  # 按utf-8的方式解码,解码成字符串,因为存入txt文件的数据为bytes型,要转为str
            for i in range(x,framelength):  # 循环读取最后一行(即最后一帧)数据的framelength-overlap长度的数据
                # content = str(re.split(r' ', line)[i]) + ' '
                content = str(re.split(r' ', last_line)[i]) + '\n'  # 读取文件中的前overlap列数据,即未分帧前的数据,去掉了重复的数据
                file2.write(content)  # 将数据写入另一个txt文件中

            file1.close()
            file2.close()
            #with open(output, 'w+') as mon:
                 #   mon.write(line)
        '''  
          while line:  # 当未读到文件最后时
                # 把切分出的列表的每个值, 把它们转成np.short型, 并返回迭代器
                num = list(map(np.short, line.split()))  # np.short
                # 用list函数把map函数返回的迭代器遍历展开成一个列表
                data.append(num)
                line = file1.readline()
            

           # file2.close()
        '''
    '''
    lines = f.readlines()
    long = len(lines)
    for line in lines:

        for i in range(4):
            line1 = line
            file.write(line1)
            print("\t")
        print("\n")

        data = np.reshape(line, [long, 4])
    file.close()  # 关闭输出文件
    f.close()  # 关闭输入的语音文件
    exit()
    '''


if __name__ == "__main__":
    # sys.argv[1:]为要处理的参数列表,sys.argv[0]为脚本名,所以用sys.argv[1:]过滤掉脚本名。
    main(sys.argv[1:])  # 调用函数

#命令行运行的命令
#python frametxtwav.py -i Englishframe1.txt -o Englishframetxtwav1.txt --framelength 4 --overlap 2
#python frametxtwav.py -i Englishframe2.txt -o Englishframetxtwav2.txt --framelength 8 --overlap 4
#python frametxtwav.py -i Englishframe1.txt -o Englishframetxtwav1.txt -f 4 -l 2
#python frametxtwav.py -i Englishframe2.txt -o Englishframetxtwav2.txt -f 8 -l 4
#python frametxtwav.py -h

1 帮助文档

python frametxtwav.py -h

结果:

python编程实现语音数据分帧及分帧还原_第7张图片

C:\Users\CL\Desktop\学习\python\wav>python frametxtwav.py -h
输入格式为:
python framtxtwave.py -i Englishframe1.txt -o Englishframetxtwav1.txt --framelength 4 --overlap 2
或者:python framtxtwave.py -i Englishframe1.txt -o Englishframetxtwav1.txt -f 4 -l 2
其中frametxtwav.py为程序文件名称,程序功能是通过分帧后的数据得到原始语音数据,Englishframe2.txt为分帧后的数据文件, Englishframetxtwav.txt为还原的语音数据文件
-f/-framenlength为分帧的帧长,-l/overlap为帧移

2 分帧数据还原,帧长为4,帧移为2

python frametxtwav.py -i Englishframe1.txt -o Englishframetxtwav1.txt --framelength 4 --overlap 2

结果:

python编程实现语音数据分帧及分帧还原_第8张图片

C:\Users\CL\Desktop\学习\python\wav>python frametxtwav.py -i Englishframe1.txt -o Englishframetxtwav1.txt --framelength 4 --overlap 2
txt文件中的数据的数据类型为:
 <class 'bytes'>
文件行数为: 265651
txt文件中的数据的数据类型为:
 <class 'list'>

还原的语音数据结果:

python编程实现语音数据分帧及分帧还原_第9张图片

原语音数据:

python编程实现语音数据分帧及分帧还原_第10张图片

对还原后的语音数据和原来的语音数据进行对比,结果一样(还原的语音数据最后多了一行0是因为分帧时最后不足一帧的补0所致,但0并不影响还原语音结果)。最后一帧数据还原正确。

3 分帧数据还原,帧长为8,帧移为4

python frametxtwav.py -i Englishframe2.txt -o Englishframetxtwav2.txt --framelength 8 --overlap 4

结果:

python编程实现语音数据分帧及分帧还原_第11张图片

还原的语音数据结果:

python编程实现语音数据分帧及分帧还原_第12张图片

原语音数据:

python编程实现语音数据分帧及分帧还原_第13张图片
还原的语音数据最后多了一行0是因为分帧时最后不足一帧的补0所致,但0并不影响还原语音结果)。最后一帧数据还原正确。

附件:
本文章所有程序及文件:
链接:https://pan.baidu.com/s/1q_iFdQN94QHKuz773zPGVA
提取码:9k69

你可能感兴趣的:(python编程)