Package synthDrivers :: Module newfon
[hide private]
[frames] | no frames]

Source Code for Module synthDrivers.newfon

  1  #coding=UTF-8 
  2  #synthDrivers/newfon.py 
  3  #A part of NonVisual Desktop Access (NVDA) 
  4  #Copyright (C) 2006-2008 NVDA Contributors <http://www.nvda-project.org/> 
  5  #This file is covered by the GNU General Public License. 
  6  #See the file COPYING for more details. 
  7   
  8  from collections import OrderedDict 
  9  from synthDriverHandler import SynthDriver, VoiceInfo, SynthSetting, LanguageInfo, StringParameterInfo 
 10  from ctypes import * 
 11  import os 
 12  import config 
 13  import nvwave 
 14  import re 
 15  from logHandler import log 
 16   
 17  #config 
 18  abbreviationsLength = 4 
 19   
 20  isSpeaking = False 
 21  player = None 
 22  ProcessAudioCallback = WINFUNCTYPE(c_int, POINTER(c_char),POINTER(c_char),c_int) 
23 24 @ProcessAudioCallback 25 -def processAudio(udata, buffer,length):
26 global isSpeaking,player 27 if not isSpeaking: return 1 28 player.feed(string_at(buffer, length)) 29 return 0
30 31 re_words = re.compile(r"\b(\w+)\b",re.U) 32 re_englishLetters = re.compile(r"\b([a-zA-Z])\b") 33 re_abbreviations = re.compile(ur"\b([bcdfghjklmnpqrstvwxzбвгджзклмнпрстфхцчшщ]{2,})\b",re.U) 34 re_capAbbreviations = re.compile(ur"([bcdfghjklmnpqrstvwxzбвгджзклмнпрстфхцчшщ]{3,})",re.U|re.I) 35 re_afterNumber = re.compile(r"(\d+)([^\.\:\-\/\!\?\d])") 36 re_omittedCharacters = re.compile(r"[\(\)\*_\"]+") 37 re_zeros = re.compile(r"\b\a?\.?(0+)") 38 39 ukrainianRules = { 40 re.compile(u"\\b(й)\\s",re.U|re.I): U"й", 41 re.compile(u"\\b(з)\\s",re.U|re.I): U"з", 42 re.compile(u"\\s(ж)\\b",re.U|re.I): U"ж", 43 re.compile(u"\\s(б)\\b",re.U|re.I): U"б", 44 re.compile(ur"'([яюєї])",re.I|re.U): u"ьй\\1", 45 re.compile(u"ц([ьіяюєї])",re.U|re.I): U"тс\\1" 46 } 47 48 englishLetters = { 49 'a': u"эй", 50 'b' : u"би", 51 'c': u"си", 52 'd': u"ди", 53 'e': u"и", 54 'f': u"эф", 55 'g': u"джи", 56 'h': u"эйчь", 57 'i': u"ай", 58 'j': u"джей", 59 'k': u"кэй", 60 'l': u"эль", 61 'm': u"эм", 62 'n': u"эн", 63 'o': u"оу", 64 'p': u"пи", 65 'q': u"къю", 66 'r': u"ар", 67 's': u"эс", 68 't': u"ти", 69 'u': u"ю", 70 'v': u"ви", 71 'w': u"да+блъю", 72 'x': u"экс", 73 'y': u"вай", 74 'z': u"зи" 75 } 76 russianLetters = { 77 u"б": u"бэ", 78 u"в": u"вэ", 79 u"к": u"ка", 80 u"с": u"эс", 81 u"ь": u"мя", 82 u"ъ": u"твё" 83 } 84 85 englishPronunciation= { 86 'x': u"кс", 87 'e': u"э", 88 'y': u"ы", 89 'j': u"дж" 90 } 91 #ukrainian to russian character map 92 #ukrainian soft "g" is not supported, becouse synth does not contain this phonem :( 93 ukrainianPronunciation = { 94 u"и": u"ы", 95 u"і": u"и", 96 u"ї": u"ййи", 97 u"е": u"э", 98 u"є": u"е", 99 u"ґ": u"г" 100 } 101 ukrainianPronunciationOrder = [u"и",u"і", u"ї", u"е", u"є", u"ґ"] 102 103 ukrainianLetters = { 104 u"й": u"йот", 105 u"ґ": u"Твэрдэ+ гэ", 106 u"и": u"ы", 107 u"і": u"и", 108 u"ї": u"ййи", 109 u"е": u"э", 110 u"є": u"е" 111 } 112 letters = {} 113 letters.update(englishLetters) 114 letters.update(russianLetters) 115 russianZeros=[u"ноль ",u"ноля",u"нолей"] 116 ukrainianZeros=[u"нуль ",u"нулі ",u"нулів "]
117 118 -def subZeros(match,zeros):
119 l = len(match.group(1)) 120 if l == 1: return zeros[0] 121 text = " " + str(l) + " " 122 l = l%10 123 if l == 1: return text+ zeros[0] 124 elif l <5: return text+zeros[1] 125 else: return text+zeros[2]
126
127 -def expandAbbreviation(match):
128 loweredText = match.group(1).lower() 129 l = len(match.group(1)) 130 if (match.group(1).isupper() and (l <= abbreviationsLength and l > 1) and re_capAbbreviations.match(match.group(1))) or re_abbreviations.match(loweredText): 131 expandedText = "" 132 for letter in loweredText: 133 expandedText += letters[letter] if letters.has_key(letter) else letter 134 if letter.isalpha(): expandedText+=" " 135 return expandedText 136 return loweredText
137
138 -def subEnglishLetters(match):
139 letter = match.group(1).lower() 140 return englishLetters[letter]
141
142 -def preprocessEnglishText(text):
143 text = re_englishLetters.sub(subEnglishLetters, text) 144 for s in englishPronunciation: 145 text = text.replace(s, englishPronunciation[s]) 146 return text
147
148 -def preprocessUkrainianText(text):
149 for rule in ukrainianRules: 150 text = rule.sub(ukrainianRules[rule],text) 151 for s in ukrainianPronunciationOrder: 152 text = text.replace(s, ukrainianPronunciation[s]) 153 #stupid python! replace() does not have ignore case, reg exprs also sucks 154 text = text.replace(s.upper(), ukrainianPronunciation[s]) 155 return text
156
157 -def processText(text,language):
158 if len(text) == 1: 159 letter = text.lower() 160 if language == "uk" and ukrainianLetters.has_key(letter): return ukrainianLetters[letter] 161 elif letters.has_key(letter): return letters[letter] 162 else: return letter 163 text = re_omittedCharacters.sub(" ", text) 164 text = re_zeros.sub(lambda match: subZeros(match,russianZeros if language=="ru" else ukrainianZeros),text) 165 if language == "uk": 166 text = preprocessUkrainianText(text) 167 text = re_words.sub(expandAbbreviation,text) #this also lowers the text 168 text = preprocessEnglishText(text) 169 text = re_afterNumber.sub(r"\1-\2", text) 170 return text
171
172 -class SynthDriver(SynthDriver):
173 name="newfon" 174 description = "Newfon" 175 supportedSettings=( 176 SynthDriver.VoiceSetting(), 177 SynthDriver.LanguageSetting(), 178 SynthDriver.RateSetting(), 179 SynthSetting("accel",_("&Acceleration")), 180 SynthDriver.PitchSetting(), 181 SynthDriver.InflectionSetting(10), 182 SynthDriver.VolumeSetting(), 183 ) 184 _volume = 100 185 _language="ru" 186 _pitch = 50 187 _accel=0 188 _inflection=50 189 _rate=70 190 availableVoices = OrderedDict((str(index),VoiceInfo(str(index),name)) for index,name in enumerate([_("male 1"),_("female 1"),_("male 2"),_("female 2")])) 191 availableAccels=OrderedDict((str(x), StringParameterInfo(str(x),str(x))) for x in xrange(8)) 192 pitchTable=[(90,130),(190,330),(60,120),(220,340)] 193 availableLanguages = OrderedDict((("ru",LanguageInfo("ru")), ("uk",LanguageInfo("uk")))) 194 newfon_lib = None 195 sdrvxpdbDll = None 196 dictDll = None 197 198 @classmethod
199 - def check(cls):
200 return os.path.isfile('synthDrivers/newfon_nvda.dll')
201
202 - def calculateMinMaxPitch(self,pitch,inflection):
203 min,max=self.pitchTable[int(self.voice)] 204 i=max-min 205 i=int((i/50.0)*((inflection-50)/2)) 206 min-=i 207 max+=i 208 i=int((pitch-50)/1.3) 209 min+=i 210 max+=i 211 return min,max
212
213 - def __init__(self):
214 global player 215 player = nvwave.WavePlayer(channels=1, samplesPerSec=10000, bitsPerSample=8, outputDevice=config.conf["speech"]["outputDevice"]) 216 self.hasDictLib = os.path.isfile('synthDrivers/dict.dll') 217 if self.hasDictLib: 218 self.sdrvxpdb_lib = windll.LoadLibrary(r"synthDrivers\sdrvxpdb.dll") 219 self.dict_lib = windll.LoadLibrary(r"synthDrivers\dict.dll") 220 self.newfon_lib = windll.LoadLibrary(r"synthDrivers\newfon_nvda.dll") 221 self.newfon_lib.speakText.argtypes = [c_char_p, c_int] 222 if not self.newfon_lib.initialize(): raise Exception 223 self.newfon_lib.set_callback(processAudio) 224 self.newfon_lib.set_dictionary(1)
225
226 - def terminate(self):
227 self.cancel() 228 global player 229 player.close() 230 player=None 231 self.newfon_lib.terminate() 232 del self.newfon_lib 233 if self.hasDictLib: 234 del self.dict_lib 235 del self.sdrvxpdb_lib
236
237 - def speakText(self, text, index=None):
238 global isSpeaking 239 isSpeaking = True 240 text = processText(text, self._language) 241 if index is not None: 242 self.newfon_lib.speakText(text,index) 243 else: 244 self.newfon_lib.speakText(text,-1)
245
246 - def _get_lastIndex(self):
247 return self.newfon_lib.get_lastIndex()
248
249 - def cancel(self):
250 self.newfon_lib.cancel() 251 global isSpeaking,player 252 isSpeaking = False 253 player.stop()
254
255 - def _get_voice(self):
256 return str(self.newfon_lib.get_voice())
257
258 - def _set_voice(self, value):
259 self.newfon_lib.set_voice(int(value)) 260 self._set_pitch(self._pitch)
261
262 - def _get_volume(self):
263 return self._volume
264
265 - def _set_volume(self,value):
266 self.newfon_lib.set_volume(value) 267 self._volume = value
268
269 - def _get_rate(self):
270 return self._rate
271
272 - def _set_rate(self, value):
273 self.newfon_lib.set_rate(value) 274 self._rate = value
275
276 - def _set_pitch(self, value):
277 #if value <= 50: value = 50 278 #self.newfon_lib.set_accel(value/5 -10 ) 279 self._pitch = value 280 min,max=self.calculateMinMaxPitch(self._pitch,self._inflection) 281 self.newfon_lib.set_pitch_min(min) 282 self.newfon_lib.set_pitch_max(max)
283
284 - def _get_pitch(self):
285 return self._pitch
286
287 - def pause(self, switch):
288 global player 289 player.pause(switch)
290
291 - def _get_language(self):
292 return self._language
293
294 - def _set_language(self, language):
295 self._language = language 296 if not self.hasDictLib: return 297 if language == "ru": self.newfon_lib.set_dictionary(1) 298 else: self.newfon_lib.set_dictionary(0)
299
300 - def _set_inflection(self,inflection):
301 self._inflection=inflection 302 self._set_pitch(self._pitch)
303
304 - def _get_inflection(self):
305 return self._inflection
306
307 - def _set_accel(self,a):
308 self._accel=a 309 self.newfon_lib.set_accel(int(a))
310
311 - def _get_accel(self):
312 return self._accel
313