Source code for naplib.features.alignment_extras

import os
from os.path import isfile, join
import re
import numpy as np

[docs] def get_phoneme_label_vector(phn_file, length, fs, befaft, mode='phonemes', return_label_lists=False): ''' Creates a time-series vector of phoneme labels based on the output of the Penn-Phonetic Alignment procedure. Returns label vector as numpy array of shape (time, ) containing categorical labels. Assumes that the .phn files were made on wav files that did not have a befaft zero period. This function can be used instead of the ``get_label_vecs_from_files`` method inside the ``Aligner`` class if you only want a single file's label vector. Parameters ---------- phn_file : string, path to .phn file length : int, length of stimulus (in samples) including befaft periods fs : int, sampling rate befaft : array or list of length 2, containing befaft time periods in seconds mode : string, either 'phonemes', or 'manner'. Any empty or 'sp' periods are labeled -1. If 'manner': labels correspond to ['plosive','fricative','nasal','sonorant'] return_label_lists : bool, if True, returns a 2-tuple containing the labels as well as the list of possible labels, so that the index of a label in the list is the integer label assigned to it. Returns ------- labels : np.array, shape (time,) Integer labels over time. Full length is befaft[0]*fs+length+befaft[1]*fs item_list : list List of phonemes (if mode=='phonemes') or manners of articulation (if mode=='manner') which were used. The index of a phoneme indicates the label it is assigned. ''' pattern = r'[0-9]' phoneme_list = ['EH','K','S','L','AH','M','EY','SH','N','P','OY','T','OW','Z','W','D','B','V','IH','AA','R','AY','ER','AE','AO','NG','G','TH','IY','F','DH','HH','UH','CH','UW','AW','JH','Y','ZH'] manner_list = ['plosive','fricative','nasal','sonorant'] manner_dict = {'EH':'sonorant', 'K':'plosive', 'S':'fricative', 'L':'sonorant', 'AH':'sonorant', 'M':'nasal', 'EY':'sonorant', 'SH':'fricative', 'N':'nasal', 'P':'plosive', 'OY':'sonorant', 'T':'plosive', 'OW':'sonorant', 'Z':'fricative', 'W':'sonorant', 'D':'plosive', 'B':'plosive', 'V':'fricative', 'IH':'sonorant', 'AA':'sonorant', 'R':'sonorant', 'AY':'sonorant', 'ER':'sonorant', 'AE':'sonorant', 'AO':'sonorant', 'NG':'nasal', 'G':'plosive', 'TH':'fricative', 'IY':'sonorant', 'F':'fricative', 'DH':'fricative', 'HH':'fricative', 'UH':'sonorant', 'CH':'velar', 'UW':'sonorant', 'AW':'sonorant', 'JH':'velar', 'Y':'sonorant', 'ZH':'sonorant', 'sp':'none'} f = open(phn_file, 'r') Lines = [line.strip() for line in f.readlines()] f.close() labels = -1 * np.ones((length,)) for line in Lines: phn = re.sub(pattern, '', line.split(' ')[-1]) start_time, end_time = line.split(' ')[:2] start_time, end_time = float(start_time), float(end_time) start_time += befaft[0] end_time += befaft[0] if mode == 'phonemes': labels[round(start_time*fs):round(end_time*fs)] = phoneme_list.index(phn) if phn in phoneme_list else -1.0 elif mode == 'manner': labels[round(start_time*fs):round(end_time*fs)] = manner_list.index(manner_dict[phn]) if manner_dict[phn] in manner_list else -1.0 else: raise Exception(f"mode parameter must be either 'phonemes' or 'manner', but found {mode}") if return_label_lists: if mode == 'phonemes': return labels, phoneme_list elif mode == 'manner': return labels, manner_list else: raise Exception(f"mode parameter must be either 'phonemes' or 'manner', but found {mode}") return labels
[docs] def get_word_label_vector(wrd_file, length, fs, befaft, wrd_dict=None, wrd_files_dir=None, return_wrd_dict=False): ''' Returns label vector as numpy array of shape (time, ) containing categorical labels. Assumes that the .wrd files were made on wav files that did not have a befaft zero period. This function can be used instead of the ``get_label_vecs_from_files`` method inside the ``Aligner`` class if you only want a single file's label vector. Parameters ---------- wrd_file : string, path to .wrd file length : int length of stimulus (in samples) including befaft periods fs : int, sampling rate befaft : array or list of length 2 containing befaft time periods in seconds wrd_dict : dict keys are words (capitalized) and values are integers which become the labels for each word wrd_files_dir : string Path to directory containing all the .wrd files to be included in the dictionary. This is ignored if wrd_dict is supplied, but otherwise, a new wrd_dict is created using the .wrd files in this directory. By default, this will make 'sp' (space) have a label of -1. return_wrd_dict : bool Whether or not to return the wrd_dict along with the labels as a tuple Returns ------- labels : np.array, shape (time,) Integer labels over time. Full length is befaft[0]*fs+length+befaft[1]*fs wrd_dict : dict Word dictionary (word to integer) used to create the labels. Only returned if return_wrd_dict==True ''' pattern = r'[0-9]' if wrd_dict is None and wrd_files_dir is None: raise Exception('Must provide either wrd_dict or wrd_files_dir') elif wrd_dict is None: wrd_dict = create_wrd_dict(wrd_files_dir) f = open(wrd_file, 'r') Lines = [line.strip() for line in f.readlines()] f.close() labels = -1 * np.ones((length,)) for line in Lines: wrd = re.sub(pattern, '', line.split(' ')[-1]) start_time, end_time = line.split(' ')[:2] start_time, end_time = float(start_time), float(end_time) start_time += befaft[0] end_time += befaft[0] try: labels[round(start_time*fs):round(end_time*fs)] = wrd_dict[wrd] except KeyError: raise KeyError(f"{wrd} is not a valid key in wrd_dict") if return_wrd_dict: return labels, wrd_dict return labels
[docs] def create_wrd_dict(wrd_files_dir, list_to_skip=[]): ''' Create a new word to label dictionary, which can be passed to get_word_label_vector. Parameters ---------- wrd_files_dir : string Path to directory containing all the .wrd files to be used list_to_skip : list of strings, default=[] Words which will not be added to the dictionary. Returns ------- wrd_dict : dict Dictionary of word:int (key:value) pairs for all the words in the corpus of files in the directory. ''' pattern = r'[0-9]' wrd_files = sorted([f for f in os.listdir(wrd_files_dir) if isfile(join(wrd_files_dir, f)) and f.endswith('.wrd')]) wrd_dict = {} for wrd_file in wrd_files: f = open(join(wrd_files_dir, wrd_file), 'r') Lines = [line.strip() for line in f.readlines()] f.close() for line in Lines: wrd = re.sub(pattern, '', line.split(' ')[-1]) if wrd == 'sp': wrd_dict[wrd] = -1 elif wrd not in wrd_dict and wrd not in list_to_skip: if 'sp' in wrd_dict: wrd_dict[wrd] = len(wrd_dict) else: wrd_dict[wrd] = len(wrd_dict)+1 return wrd_dict