File size: 3,576 Bytes
a22c748
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import numpy as np
import re

_FOLDINGS = {
    'AO': 'AA',
    'AX': 'AH',
    'AX-H': 'AH',
    'AXR': 'ER',
    'HV': 'HH',
    'IX': 'IH',
    'EL': 'L',
    'EM': 'M',
    'EN': 'N',
    'NX': 'N',
    'ENG': 'NG',
    'PCL': 'P',
    'TCL': 'T',
    'KCL': 'K',
    'BCL': 'B',
    'DCL': 'D',
    'GCL': 'G',
    'H#': 'SIL',
    'PAU': 'SIL',
    'EPI': 'SIL',
    'UX': 'UW'       
}

def align(seq, probs):

    def distance(r, c):
        category = seq[r]
        timestep = c
        return probs[timestep, category]
        
    nrow = len(seq)
    ncol = probs.shape[0] # time steps
    
    M = warp(range(nrow), range(ncol), distance)
    
    alignment = traceback(M[1:, 1:], seq)
    return alignment, M[1:, 1:]
    
def warp(S, T, d):
    
    nrow = len(S)
    ncol = len(T)
    
    M = np.full((nrow+1, ncol+1), np.inf)
    
    M[0, 0] = 0
    
    # Some optimization is being left on the table; this should be computable
    # for a whole column at a time
    for r in range(1, nrow+1):
        for c in range(1, ncol+1):
            # need -1 offset for cost because of extra first row
            # and first column in M that isn't present in the
            # `probs` matrix
            cost = d(r-1, c-1)
            M[r, c] = cost + min(M[r, c-1], M[r-1, c-1])
            
    return M
    
def traceback(M, sequence):

    nrow, ncol = M.shape
    seq = [nrow-1]
    
    r = nrow-1
    c = ncol-1
    
    curr_prob = M[r, c]
    
    while c >= 0:
        if r == 0:
            seq += [0 for x in range(ncol - len(seq))]
            break
        
        if M[r-1, c-1] > M[r, c-1]:
            seq.append(r)
            c -= 1
        else:
            c -= 1
            r -= 1
            
            seq.append(r)
            
    rs = seq[::-1]
    rs = [sequence[r] for r in rs]
    
    return rs
    
def collapse(s):
    a = [s[0]]
    for symbol in s[1:]:
        if a[-1] != symbol:
            a.append(symbol)
    return a
    
def fold_phone(p):

    stress_mark = re.findall(r'\d+', p)
    if stress_mark:
        stress_mark = stress_mark[0]
    else:
        stress_mark = ''
    phone_label = re.sub(r'\d', '', p)
    if phone_label in _FOLDINGS:
        phone_label = _FOLDINGS[phone_label]
    return phone_label + stress_mark

    
def load_dictionary(dname, cmuformat=True):

    

    mapping = dict()
    
    if cmuformat:

        with open(dname, 'r') as d:
            for line in d:
                line = line.strip()
                if not line or line.startswith(';;;'):
                    continue
                all_items = line.split()
                if len(all_items) < 2:
                    continue
                word = all_items[0]
                word = re.sub(r'\(\d*\)', '', word)
                # NLTK cmudict uses ``WORD <variant_index> PHONE ...``; skip the index.
                pron_start = 2 if len(all_items) >= 3 and all_items[1].isdigit() else 1
                pronunciation = all_items[pron_start:]
                pronunciation = [fold_phone(p) for p in pronunciation]
                if not pronunciation:
                    continue
                
                if word not in mapping:
                    mapping[word] = [pronunciation]
                else:
                    mapping[word].append(pronunciation)
                    
        mapping['sil'] = [['H#'], []]
        return mapping
        
    else:
        print('Custom dictionary formats not supported yet. Please format use CMU dict formatting and run again.')
        sys.exit(1)