실험음성학 2019학년도 2학기 첫 강독회에서는 Praat가 가진 음향 자질 추출의 장점과 Python이 가진 텍스트 자질 추출의 장점을 함께 활용해 보는 시간을 가질 것입니다. Python을 활용하여 Praat의 여러 기능들을 통제할 수 있으면, Praat이 음성 분석에서 가지는 장점은 물론 범용 프로그래밍 언어어로서의 Python이 가지는 여러 장점도 함께 활용할 수 있어 아주 강력한 파워를 가질 수 있게 됩니다. 따라서 본 강독회에서는 Python에서 Praat을 조종할 수 있는 Parselmouth라는 패키지와 Praat의 TextGrid에 있는 정보를 활용할 수 있는 tgt라는 모듈을 활용하여 음향 자질과 텍스트 자질을 통합하여 음성분석을 할 수 있는 기반에 대해 Jupyter Notebook을 기반으로 하여 배울 예정입니다.
Python을 사용해 왔고, pip 등을 통해 패키지 등을 설치해 본 경험이 있으면 사용하던 컴퓨터를 가지고 와 활용할 수 있을 것입니다. Python을 많이 사용해 보지 않았거나 처음인 분들은 https://www.anaconda.com/downloads에서 자신의 OS에 맞는 anaconda 배포판을 설치해서 강독회에 참석해 주시면 되겠습니다. 예를 들어 window용 노트북을 사용하는 분들은 anaconda에서 Python 3.7 version에서 64-bit Graphical Installer를 눌러 설치하면 됩니다.
tgt (TextGridTools) github: https://github.com/hbuschme/TextGridTools
wavefile & textgrid: timit의 한 파일
import parselmouth
import IPython.display
sound=parselmouth.Sound("data/SX127.wav")
from IPython.display import Audio
Audio(data=sound.values, rate=sound.sampling_frequency)
import glob, re, tgt
for tg_file in glob.glob('data/*.TextGrid'):
# 텍스트그리드 파일을 읽어들임
print("processing {}...".format(tg_file))
# 디렉토리와 파일명이 /로 결합되어 있는 데, 이를 분리하고, 파일명 앞까지
# 분리된 디렉토리들을 결합하여 디렉토리명으로 지칭함.
dir_name = tg_file.split('/')[:-1]
dir_name = '/'.join(dir_name)
print("directory name... {}".format(dir_name))
# 마지막 [-1]로 된 것은 파일명이며, 텍스트그리드를 읽어들였으니 tg_name으로 명명함
tg_name = tg_file.split('/')[-1]
print('textgrid name... {}'.format(tg_name))
# wave 파일과 TextGrid 파일은 이름을 공유하고 있으므로 extension만 wav로 바꾸고
# wav_name으로 저장함.
wav_name = re.sub("TextGrid", "wav", tg_name)
print("wave file name...{}".format(wav_name))
# tgt를 이용해 textgrid 파일을 읽어들임.
tg = tgt.read_textgrid(dir_name + '/' + tg_name)
# tg의 파일명 알아보기
tgname = tg.filename
print("textgrid의 파일명...{}".format(tgname))
#textgrid tier name 알아보기
tg_tier_names = tg.get_tier_names()
print("textgrid tier names...{}".format(tg_tier_names))
# 찾아낸 tier명을 사용사여 tier들을 지정하기
phn_tier = tg.get_tier_by_name('phn')
wrd_tier = tg.get_tier_by_name('wrd')
#
overlap_tier = tgt.util.get_overlapping_intervals(phn_tier, wrd_tier)
#print('overlap_tier...{}'.format(overlap_tier))
print('idx','\t','start','\t','end','\t','dur','\t','phone','\t','word')
for i, t in enumerate(overlap_tier):
#print(i, t)
texts = t.text.split('+')
duration = (t.end_time-t.start_time)*1000 # miliseconds
print(format(i),'\t',
format(t.start_time, ".3f"),'\t',
format(t.end_time, ".3f"),'\t',
format(duration, ".2f"),'\t',
format(texts[0]),'\t',
format(texts[1]))
from parselmouth.praat import call
import glob, re, tgt
for tg_file in glob.glob('data/*.TextGrid'):
# 텍스트그리드 파일을 읽어들임
print("processing {}...".format(tg_file))
# 디렉토리와 파일명이 /로 결합되어 있는 데, 이를 분리하고, 파일명 앞까지
# 분리된 디렉토리들을 결합하여 디렉토리명으로 지칭함.
dir_name = tg_file.split('/')[:-1]
dir_name = '/'.join(dir_name)
print("directory name... {}".format(dir_name))
# 마지막 [-1]로 된 것은 파일명이며, 텍스트그리드를 읽어들였으니 tg_name으로 명명함
tg_name = tg_file.split('/')[-1]
print('textgrid name... {}'.format(tg_name))
# wave 파일과 TextGrid 파일은 이름을 공유하고 있으므로 extension만 wav로 바꾸고
# wav_name으로 저장함.
wav_name = re.sub("TextGrid", "wav", tg_name)
print("wave file name...{}".format(wav_name))
# tgt를 이용해 textgrid 파일을 읽어들임.
tg = tgt.read_textgrid(dir_name + '/' + tg_name)
# tg의 파일명 알아보기
tgname = tg.filename
print("textgrid의 파일명...{}".format(tgname))
#textgrid tier name 알아보기
tg_tier_names = tg.get_tier_names()
print("textgrid tier names...{}".format(tg_tier_names))
# 찾아낸 tier명을 사용사여 tier들을 지정하기
phn_tier = tg.get_tier_by_name('phn')
wrd_tier = tg.get_tier_by_name('wrd')
#
overlap_tier = tgt.util.get_overlapping_intervals(phn_tier, wrd_tier)
##################################################################
# Parselmouth에세 Praat을 통제하여 음성 자질 추출하기
###################################################################
wave_full_name = dir_name + '/' + wav_name
# 소리
sound = parselmouth.Sound(wave_full_name)
# Praat을 이용한 Pitch analysis
pitchobj = call(sound, "To Pitch (ac)", 0.0, 75, 15, False, 0.02, 0.45,
0.01, 0.35, 0.14, 450)
# Intensity Analysis
intensityobj = call(sound, "To Intensity", 100, 0, True)
# Formant
formantobj = call(sound, "To Formant (burg)", 0, 5, 5500, 0.005, 50)
#print('overlap_tier...{}'.format(overlap_tier))
print('idx','\t','start','\t','end','\t','dur','\t','f0','\t','db','\t',
'f1','\t','\f2','\t','\f3','\t',
'phone','\t','word')
for i, t in enumerate(overlap_tier):
#print(i, t)
texts = t.text.split('+')
duration = (t.end_time-t.start_time)*1000 # miliseconds
mid_time = t.start_time+(t.end_time-t.start_time)/2
pitchvalue = call(pitchobj, "Get value at time", mid_time , "Hertz", "Linear")
intensityvalue = call(intensityobj, "Get value at time", mid_time, "cubic")
f1value = call(formantobj, "Get value at time", 1, mid_time, "Hertz", "Linear")
f2value = call(formantobj, "Get value at time", 2, mid_time, "Hertz", "Linear")
f3value = call(formantobj, "Get value at time", 3, mid_time, "Hertz", "Linear")
print(format(i),'\t',
format(t.start_time, ".3f"),'\t',
format(t.end_time, ".3f"),'\t',
format(duration, ".2f"),'\t',
format(pitchvalue, ".2f"),'\t',
format(intensityvalue, ".2f"),'\t',
format(f1value, ".2f"),'\t',
format(f2value, ".2f"),'\t',
format(f3value, ".2f"),'\t',
format(texts[0]),'\t',
format(texts[1]))
import nltk
sentence = "the emperor had a mean temper"
text = nltk.word_tokenize(sentence)
pos = nltk.pos_tag(text)
print(pos)
for i, t in enumerate(pos):
print(i+1, t)
import collections
ord_dict = collections.OrderedDict()
for i, t in enumerate(pos):
ord_dict[t[0]] = (i, t[1])
for k, v in ord_dict.items():
print(k, v[0], v[1])
import glob, re, tgt
for tg_file in glob.glob('data/*.TextGrid'):
# 텍스트그리드 파일을 읽어들임
print("processing {}...".format(tg_file))
# 디렉토리와 파일명이 /로 결합되어 있는 데, 이를 분리하고, 파일명 앞까지
# 분리된 디렉토리들을 결합하여 디렉토리명으로 지칭함.
dir_name = tg_file.split('/')[:-1]
dir_name = '/'.join(dir_name)
print("directory name... {}".format(dir_name))
# 마지막 [-1]로 된 것은 파일명이며, 텍스트그리드를 읽어들였으니 tg_name으로 명명함
tg_name = tg_file.split('/')[-1]
print('textgrid name... {}'.format(tg_name))
# wave 파일과 TextGrid 파일은 이름을 공유하고 있으므로 extension만 wav로 바꾸고
# wav_name으로 저장함.
wav_name = re.sub("TextGrid", "wav", tg_name)
print("wave file name...{}".format(wav_name))
# tgt를 이용해 textgrid 파일을 읽어들임.
tg = tgt.read_textgrid(dir_name + '/' + tg_name)
# tg의 파일명 알아보기
tgname = tg.filename
print("textgrid의 파일명...{}".format(tgname))
#textgrid tier name 알아보기
tg_tier_names = tg.get_tier_names()
print("textgrid tier names...{}".format(tg_tier_names))
# 찾아낸 tier명을 사용사여 tier들을 지정하기
phn_tier = tg.get_tier_by_name('phn')
wrd_tier = tg.get_tier_by_name('wrd')
#
overlap_tier = tgt.util.get_overlapping_intervals(phn_tier, wrd_tier)
#print('overlap_tier...{}'.format(overlap_tier))
print('idx','\t','wordpos','\t','start','\t','end','\t',
'dur','\t','phone','\t','pos','\t','word')
for i, t in enumerate(overlap_tier):
#print(i, t)
texts = t.text.split('+')
duration = (t.end_time-t.start_time)*1000 # miliseconds
#print(ord_dict[texts[1]])
print(format(i),'\t',
format(ord_dict[texts[1]][0]+1),'\t',
format(t.start_time, ".3f"),'\t',
format(t.end_time, ".3f"),'\t',
format(duration, ".2f"),'\t',
format(texts[0]),'\t',
format(ord_dict[texts[1]][1]),'\t',
format(texts[1]))
Jadoul, Y., Thompson, B., & de Boer, B. (2018). Introducing Parselmouth: A Python interface to Praat. Journal of Phonetics, 71, 1-15. https://doi.org/10.1016/j.wocn.2018.07.001
Buschmeier, H. and Włodarczak, M. (2013). TextGridTools: A TextGrid Processing and Analysis Toolkit for Python. In P. Wagner (ed.) Tagungsband der 24. Konferenz zur Elektronischen Sprachsignalverarbeitung (ESSV 2013). Dresden: TUDpress, pp. 152-157. https://pub.uni-bielefeld.de/download/2561620/2563287