python提取时长2s以内的单词音频的韵母基频,以及单词词长信息

python提取时长2s以内的单词音频的韵母基频,以及单词词长信息

提取信息自动存入当前工作空间中的excel文件,包括文件名、前字时长、后字时长、两字总时长、前字韵母基频、后字韵母基频。(10个点,9段,采用自相关法)

音频源文件为.wav和.textgrid文件。如gong1_yuan2.wav、gong1_yuan2.textgrid,其中gong1_yuan2代表单词“公园”,1、2代表声平前字为1声,后字为2声。

使用代码时,文件夹格式建议如下,要有M001这个二级目录。(M为male性别,001代表说话人编号。)若你的代码无男女区别,则也建议新建一个M001子目录。
参考数据链接:
https://pan.baidu.com/s/1Sdymm3XYD9uXKGoD8UYoDg
提取码:d851
文件树格式

如需要多次处理音频,将旧音频文件删除,复制新的进去就行。

基本算法思想

根据textgrid文件标注的单词的韵头、韵母、以及中间silence段的时间序列,计算出每个韵头、韵母左右端点。在textgrid文件中,以gong1_yuan2.textgrid为例,前后字的Intervals为 ( 0.5,1.0,T1) 、( 1.,2,1.9,T12。 韵头为(0.5,0.6,g),韵母为(0.6,1.0,ong),其他依次类推。两字之间可能存在silence段:(1.0,1.2,sil)。

关于Textgrid文件格式的参考:https://blog.csdn.net/duxin_csdn/article/details/88966295

根据文件采样频率计算出离散时间序列横坐标,根据韵头韵母的左右端点,计算出时间序列横坐标区间,即为韵头or韵母所在的区间。 如时间序列为 0.1 0.2 0.3 0.4 0.5,韵母左端点标注为0.25,则取左区间端点为0.3.其他同理。

根据时间区间即可得到对应数字信号序列,将其输入自相关算法即可计算基频。

若需要提取韵头基频,根据算法修改即可。本文所建立的韵母、韵头表,未必对所有使用者的textgrid的标注都适用。

import textgrid
import xlwt
import os
import re
import wave
import numpy as np
import math
#gong1_yuan2.TextGrid
from matplotlib import pyplot as plt
import scipy.io.wavfile as wav


book = xlwt.Workbook()  # 创建Workbook,相当于创建Excel encoding='utf-8'
# 创建sheet,Sheet1为表的名字,cell_overwrite_ok为是否覆盖单元格
sheet1 = book.add_sheet('Sheet1')
D1=D2=D3=D4=0
def getparam(file_name):
    #此函数获取2个单字的时长、总长、第一个字韵母时间区间(D1,D2)、第二个字韵母时间区间 (D3,D4)
    #第一个单字时间总长T1 第二个单字时间总长T2 ,全长时间T3
    tg = textgrid.TextGrid()
    tg.read(file_name)  # 'file.TextGrid' 是文件名
    sylset = {'b', 'p', 'm', 'f', 'd', 't', 'n', 'l', 'g', 'k', 'h', 'j', 'q', 'x', 'zh', 'ch', 'sh', 'z', 'c', 's',
              'y', 'w', 'r','e'}
    sylmset={'a','ai','an','ang','ao','e','ei','en','eng','er','o','ong','ou','iu','i','ia','ian','iang','iao','ie','in','ing','iong','iou','u','un','ui','ua','uai','uan','uang','uei','uen','ueng','uo','v','van','ve','vn','R','iii','ii','eR'}
    #根据文件名处理标记
    twonum=re.findall("\d+", file_name)
    #'1'代表韵头,'0'代表韵母
    syltag=''
    num=1
    b = 1
    a = []  # 韵母区间
    c = []  # 韵母标记 注意可能出现3个韵母,则有6个区间数字,需要修改成4个
    for i in range(0, len(tg.tiers)):
        for j in range(0, len(tg.tiers[i])):
##############################################求韵母区间##############################################
            if (((tg.tiers[i][j].mark != 'None') & (tg.tiers[i][j].mark != 'sil') & (tg.tiers[i][j].mark != ''))):
                if (tg.tiers[i][j].mark in sylmset  ):
                    a.append(tg.tiers[i].intervals[j].minTime)
                    a.append(tg.tiers[i].intervals[j].maxTime)
                    c.append(tg.tiers[i].intervals[j].mark)
#############################################↑↑↑求韵母区间↑↑↑##############################################

##############################################求词时长##############################################

            if (twonum[0] != twonum[1]):
                  if ((tg.tiers[i][j].mark == 'T' + twonum[0])):
                        T1time = round(tg.tiers[i].intervals[j].maxTime - tg.tiers[i].intervals[j].minTime, 5)  # 第一个词时长
                        A1 = round(tg.tiers[i].intervals[j].minTime,5)
                  if ((tg.tiers[i][j].mark == 'T' + twonum[1])):
                        T2time = round(tg.tiers[i].intervals[j].maxTime - tg.tiers[i].intervals[j].minTime, 5)  # 第2个词时长
                        A2 = round(tg.tiers[i].intervals[j].maxTime,5)


            else:
                  if ((tg.tiers[i][j].mark == 'T' + twonum[0])):
                      if (b == 1):
                        T1time = round(tg.tiers[i].intervals[j].maxTime - tg.tiers[i].intervals[j].minTime, 5)  # 第一个词时长
                        A1=round(tg.tiers[i].intervals[j].minTime,5)
                        b = b + 1
                      else:
                           T2time = round(tg.tiers[i].intervals[j].maxTime - tg.tiers[i].intervals[j].minTime, 5)  # 第2个词时长
                           A2 = round(tg.tiers[i].intervals[j].maxTime,5)

##############################################↑↑↑求词时长↑↑↑##############################################
    if(len(a)>4 and c[0]=='e'):
        a.pop(0)
        a.pop(0)
    else:
        if(len(a)>4 and c[1]=='e'):
           a.pop(2)
           a.pop(2)
    D1 = a[0]
    D2 = a[1]
    D3 = a[2]
    D4 = a[3]
    T3time = A2 - A1
    print( str(T1time)+'---'+ str(T2time)+'---'+str(T3time)+'-/-/D1:'+str(D1)+'---D2:'+str(D2)+'--//-D3:'+str(D3)+'---D4:'+str(D4)  )
    return [T1time,T2time,T3time,D1,D2,D3,D4]



def write_file(file_name,file_name_wav,file_pre,j):
    #此函数将第j个文件写入excel第j行 (从0开始数
    par=getparam(file_name)
    #向表中添加数据

    sheet1.write(j, 0, file_pre[0]+'/'+file_name.split('.',1)[0]) #写入第一列文件名
    #sheet1.write(j, 8,file_pre )  # 写入第一列文件名
    for i in range(0,len(par)):
      sheet1.write(j, i+1, str(par[i]))    #写入时间数据。

    # 获取10个点的基频。根据time数据和wavedata数据.
    wav_data, time,fs = read_wav_data(file_name_wav)
        # 找到大于D1 小于D2的点。为前字区间。命名为L1,L2。
        # 找到大于D1的点。
    L1=0
    L2=0
    L3=0
    L4=0
    for i in range(0, len(wav_data), 1):
        if (time[i] >par[3]):#D1)
            L1 = i;
            break
            # 找到小于D2的点。
    for i in range(0, len(wav_data), 1):
        if (time[i] >par[4]):
            L2 = i - 1;
            break
            # 找到大于D3 小于D4的点。为前字区间。命名为L3,L4。
            # 找到大于D3的点。
    for i in range(0, len(wav_data), 1):
        if (time[i] >par[5]):
            L3 = i;
            break
            # 找到小于D2的点。
    for i in range(0, len(wav_data), 1):
        if (time[i] >par[6]):
            L4 = i - 1;
            break

        # 在L1--L2 L3--L4之间取10个点。
    if(L3>L2):
       global fre1
       fre1 = np.arange(0, 10, 1)
       global fre2
       fre2 = np.arange(0, 10, 1)
       m1=0;
       m2=0;
       for i in range(L1, L2, math.ceil((L2 - L1 + 1)/10)):
          k=i+math.ceil((L2 - L1 + 1)/10)
          fre1[m1]=find_pitch(wav_data[i:k],fs)
          m1=1+m1
          if(m1>=9):
              break

       for i in range(L3, L4, math.ceil((L4 - L3 + 1) / 10)):
          k = i + math.ceil((L4 - L3 + 1) / 10)
          fre2[m2] = find_pitch(wav_data[i:k],fs)
          m2 = 1 + m2
          if (m2 >= 9):
              break



            # 将基频写入

       for i in range(9, 18):
         sheet1.write(j, i, str(fre1[i-9]))
       for i in range(20, 29):
         sheet1.write(j, i, str(fre2[i-20]))
    print(file_pre[0] + '/' + file_name.split('.', 1)[0] + '->>>' + str(L1) + '---' + str(L2) + '---' + str(
        L3) + '---' + str(L4) +'->par5==' + str(par[3]) + '->par6==' + str(par[4]) +'->par5==' + str(par[5]) + '->par6==' + str(par[6]))
    #print(fre1)
    book.save('adata1.xls')


def file_name(file_dir):
    #此函数从路径下文件中获取所有textgrid文件的名字
    L=[]
    for root, dirs, files in os.walk(file_dir):
        for file in files:
            if os.path.splitext(file)[1] == '.TextGrid':
                L.append(os.path.join(root, file))
    for i in range(0,len(L)):
        L[i]=os.path.basename(L[i])
    return L


def file_name_wave(file_dir):
    # 此函数从路径下文件中获取所有wave文件的名字
    L=[]
    for root, dirs, files in os.walk(file_dir):
        for file in files:
            if os.path.splitext(file)[1] == '.wav':
                L.append(os.path.join(root, file))
    for i in range(0,len(L)):
        L[i]=os.path.basename(L[i])
    return L

def file_pre_name(file_dir):
    # 此函数从路径下文件中获取所有文件的上级目录名
    L=[]
    for root, dirs, files in os.walk(file_dir):
        for file in files:
            if os.path.splitext(file)[1] == '.wav':
                L.append(os.path.join(root, file))
    for i in range(0,len(L)):
        L[i]=re.findall("\w\d\d\d",L[i])
    return L






def read_wav_data(filename):
  fs, wavdata = wav.read(filename)
  time=np.arange(0,len(wavdata))/fs
  return  wavdata,time,fs

def find_pitch(x,fs):
    ms20 = int((fs / 50))
    ms2 = int(fs / 500)
    #print('--ms20='+str(ms20)+'--ms2='+str(ms2)+'--fs='+str(fs))
    x = [i / 32767 for i in x]
    y = plt.acorr(x, maxlags=110, normed=True)
    y = y[1]

    z = y[round(len(y) / 2):]
    z = z[ms2:ms20]
    zmax = max(z)
    index = np.where(z == zmax)
    index = index[0][0]
    pitch = fs / (ms2 + index + 2)
    #plt.show()
    return pitch


  # 下面是自己的文件夹地址
FileNameset = file_name(r'F:\PythonVirtualEnv\wuxiaoxin_para\音频')
FileNameset_wave = file_name_wave(r'F:\PythonVirtualEnv\wuxiaoxin_para\音频')
FileNameset_pre=file_pre_name(r'F:\PythonVirtualEnv\wuxiaoxin_para\音频')
  # excel第一行
markname = ['文件の名', '首字时长', '次字时长', '两字总时长', '首字韵母左端点', '首字韵母右端点', '次字韵母左端点', '次字韵母右端点']
for i in range(0, len(markname)):
    sheet1.write(0, i, markname[i])
sheet1.write(0, 9, '前字基频')
sheet1.write(0, 20,'后字基频')
# 将T1time至D4数据输入excel
for i in range(0, len(FileNameset)):
    write_file(FileNameset[i],FileNameset_wave[i],FileNameset_pre[i], i+1)
    if(i==len(FileNameset)-1):
        print('process finished')



#write_file('ban1_fa1.TextGrid','ban1_fa1.wav', 2)



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值