Synteza mowy III: Finał

20.07.2010 - Krzysztof Kercz
TrudnośćTrudność

Slowo.py

Pokaż/ukryj kod źródłowy

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
151
152
153
154
#!/usr/bin/python
# -*- coding: utf-8 -*-
 
import Gloska
import akcenty
 
gloski_file = 'data/rules.txt'
rules = map(lambda line: (linesplit('->')[0].strip().decode('utf-8'),
    line.split('->')[1].decode('utf-8').strip().split(',')), 
    open(gloski_file))
rules = filter(lambda (a, b): not a.startswith(u'#'), rules)
maxlen = max(map(lambda (g,f): len(g.strip("^$")), rules))
rules = dict(rules)
 
def main(word):
    gloski, i = [], 0
    
    while i < len(word):
        for j in range(maxlen):
            take = maxlen - j
            part = word[i:i+take]
            if i+take == len(word):
                if part+'$' in rules:
                    key = part+'$'
                    gloski.extend(map(lambda gl: Gloska.Gloska(gl),
                        rules[key]))
                    break
            if i == 0:
                if '^'+part in rules:
                    key = '^'+part
                    gloski.extend(map(lambda gl: Gloska.Gloska(gl),
                        rules[key]))
                    break
            if part in rules:
                key = part
                gloski.extend(map(lambda gl: Gloska.Gloska(gl),
                    rules[key]))
                break
        i += take
        
        gloski = reduce(lambda a, b: a[:-1] + [b] if (a[-2].name in 
        ['ci', 'si', 'ni', 'zi', 'dzi'] 
            and a[-1].name == u'i' and b.isVowel())
            else a + [b], gloski[2:], gloski[:2])
    return gloski
 
def backward(phones):
    
'''przechodzi przez głoski i uskutecznia upodobnienie wsteczne'''
    
    for i in range(len(phones)-1, 0, -1):
        if not ((phones[i].isVowel() or phones[i-1].isVowel() or 
            phones[i].stopien_zblizenia in [u'półsamogłoskowa',
                u'drżąca', u'nosowa', u'boczna']) 
            and phones[i].dzwieczna != phones[i-1].dzwieczna):
            phones[i-1].zmien_dzwiecznosc()
            
    return phones
 
 
def forward(phones):
    
    ''' przechodzi przez głoski i uskutecznia upodobnienie postępowe
        przy okazji zmienia i niesylabotwórcze na j '''
    
    for i in range(1, len(phones)):
        if (phones[i].name in [u'w', u'rz', u'r']
            and phones[i].dzwieczna
            and not phones[i-1].dzwieczna
            and phones[i-1].stopien_zblizenia not in [u'nosowa',
                u'boczna', u'półsamogłoskowa'] 
            and not phones[i-1].isVowel()):
                phones[i].ubezdzwiecznij()
        
        if (phones[i].name == u'i' and i+1 < len(phones) 
            and phones[i+1].isVowel()):
            phones[i]=Gloska.Gloska(u'j')
                
    return phones
 
def unosowienie(phones):
    change = []
    for i in range(0, len(phones)-2):
        if (phones[i].name in [u'o', u'e']
            and phones[i+1].name in [u'n']
            and phones[i+2].stopien_zblizenia == u'szczelinowa':
            change.append(i)
    for i in range(len(change)):
        [phones[change[i]-i].nazalizuj()]
        phones = phones[:change[i]-i+1] + phones[change[i]+2-i:]
        
    return phones
 
def graphem2phonem(word):
    phonem = main(word)
    try: phonem[-1].ubezdzwiecznij()
    except: pass
    phonem = (forward(phonem) if not graphem.endswith(u'że') else
        (forward(phonem[:-2]) + phonem[-2:]) if len(phonem[:-2])>2 
        else phonem)
    phonem = backward(phonem)
    phonem = unosowienie(phonem)
    if phonem[-1].name == u'en': phonem[-1] = Gloska.Gloska(u'e')
    return phonem
 
class Slowo:
    
    def __init__(self, word):
        self.orginal = word
        self.gloski = graphem2phonem(word)
        self.akcent_sylaba = akcenty.akcent(word)
        self.akcent_gloska = self._akcent()
        if self.akcent_gloska is not None:
            self.gloski[self.akcent_gloska].zaakcentuj()
        self.bytes = ""
        
    def get_bytes(self):
        for G in self:
            self.bytes += (G.get_bytes())
        
        return self.bytes
    
    def _akcent(self):
        a, place = 0, 0
        #print self.akcent_sylaba
        for i in range(len(self.gloski)-1, -1, -1):
            #print i,a,self.akcent_sylaba, self[i]
            if self.gloski[i].isVowel(): 
                a += 1
                place = i
                #print a
            if a == self.akcent_sylaba:
                return place
        
        if a > 1:
            return place
        
        return None
        
    def __iter__(self):
        return self.gloski.__iter__()
        
    def __getitem__(self, i):
        return self.gloski[i]
    
    def __setitem__(self, i, item):
        self.gloski[i] = item
        
    def __repr__(self):
        return '< %s>' % self.gloski
        
    def __str__(self):
        return '-'.join(map(lambda G: G.name.upper() 
            if G.akcent else G.name, self.gloski))    

Mamy już klasę reprezentującą głoski, ale wiemy przecież, że głoski składają się w słowa. Stwórzmy więc również klasę Slowo!

W module tym umieściliśmy funkcję dokonującą fonemizacji - graphem2phonem(word). Nakreśliliśmy już wcześniej, jak powinna ona wyglądać, tutaj mamy implementację - przyjrzyjmy się jej.

Pierwszym i najważniejszym etapem fonemizacji jest zastosowanie reguł z pliku rules.txt. Wczytujemy je już w trakcie ładowania modułu (linie 8-13). W zmiennej maxlen zapamiętujemy, ile liter ze słowa czyta najdłuższa reguła. To dlatego, że najpierw stosować będziemy te reguły, które pokrywają największą część słowa. Łatwo zobaczyć, dlaczego chcemy tak robić - powiedzmy, że mamy cztery reguły:

s -> s
i -> i
t -> t
si -> si, i
Dostajemy słowo sit. Jeśli stosować będziemy reguły dla każdej literki, otrzymamy ciąg fonemów /s i t/, czyli nie to, co byśmy chcieli. Dlatego musimy sprawdzić, jaki najdłuższy ciąg dopasuje nam się do reguły i tę regułę zastosować: si zamienimy na /si i/ a pozostałe t na /t/ i otrzymamy /si i t/ czyli prawidłowy zapis fonetyczny.

Gdy w słowniku rules mamy już reguły, możemy łatwo zamienić słowo na ciąg fonemów. Dokonuje tego funkcja main(word). Warto skomentować linie 41-44: usuwamy tutaj głoskę /i/ występującą po głoskach miękkich i przed samogłoską. Na przykład, jeśli po zastosowaniu reguł otrzymaliśmy ciąg fonemów /ci i a ch o/, to ostatecznie zwrócimy /ci a ch o/. Po zastosowaniu reguł musimy jeszcze dokonać upodobnień wstecznych (backward(phones)), postępowych (forward(phones)) oraz unosowienia samogłosek przed grupą /n/+spółgłoska szczelinowa. Wszystko to już wiemy.

Przejdźmy teraz do omówienia klasy Slowo. Jej konstrukcja jest w miarę oczywista, jedynego wyjaśnienia wymaga być może funkcja _akcent: w zmiennej Slowo.akcent_sylaba mamy liczbę mówiącą o tym, która sylaba jest akcentowana w słowie. Na tej podstawie znajdujemy samogłoskę (pamiętając o tym, że samogłoski są ośrodkiem sylaby, pisaliśmy o tym tutaj), którą akcentujemy wywołując na niej funkcję Gloska.zaakcentuj().

5
Twoja ocena: Brak Ocena: 5 (1 ocena)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com