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

Source Code for Module synthDrivers._espeak

  1  #synthDrivers/_espeak.py 
  2  #A part of NonVisual Desktop Access (NVDA) 
  3  #Copyright (C) 2006-2007 NVDA Contributors <http://www.nvda-project.org/> 
  4  #This file is covered by the GNU General Public License. 
  5  #See the file COPYING for more details. 
  6   
  7  import time 
  8  import nvwave 
  9  import threading 
 10  import Queue 
 11  from ctypes import * 
 12  import config 
 13  import globalVars 
 14  from logHandler import log 
 15  import os 
 16  import codecs 
 17   
 18  isSpeaking = False 
 19  lastIndex = None 
 20  bgThread=None 
 21  bgQueue = None 
 22  player = None 
 23  espeakDLL=None 
 24   
 25  #Parameter bounds 
 26  minRate=80 
 27  maxRate=450 
 28  minPitch=0 
 29  maxPitch=99 
 30   
 31  #event types 
 32  espeakEVENT_LIST_TERMINATED=0 
 33  espeakEVENT_WORD=1 
 34  espeakEVENT_SENTENCE=2 
 35  espeakEVENT_MARK=3 
 36  espeakEVENT_PLAY=4 
 37  espeakEVENT_END=5 
 38  espeakEVENT_MSG_TERMINATED=6 
 39  espeakEVENT_PHONEME=7 
 40   
 41  #position types 
 42  POS_CHARACTER=1 
 43  POS_WORD=2 
 44  POS_SENTENCE=3 
 45   
 46  #output types 
 47  AUDIO_OUTPUT_PLAYBACK=0 
 48  AUDIO_OUTPUT_RETRIEVAL=1 
 49  AUDIO_OUTPUT_SYNCHRONOUS=2 
 50  AUDIO_OUTPUT_SYNCH_PLAYBACK=3 
 51   
 52  #synth flags 
 53  espeakCHARS_AUTO=0 
 54  espeakCHARS_UTF8=1 
 55  espeakCHARS_8BIT=2 
 56  espeakCHARS_WCHAR=3 
 57  espeakSSML=0x10 
 58  espeakPHONEMES=0x100 
 59  espeakENDPAUSE=0x1000 
 60  espeakKEEP_NAMEDATA=0x2000 
 61   
 62  #speech parameters 
 63  espeakSILENCE=0 
 64  espeakRATE=1 
 65  espeakVOLUME=2 
 66  espeakPITCH=3 
 67  espeakRANGE=4 
 68  espeakPUNCTUATION=5 
 69  espeakCAPITALS=6 
 70  espeakWORDGAP=7 
 71  espeakOPTIONS=8   # reserved for misc. options.  not yet used 
 72  espeakINTONATION=9 
 73  espeakRESERVED1=10 
 74  espeakRESERVED2=11 
 75   
 76  #error codes 
 77  EE_OK=0 
78 #EE_INTERNAL_ERROR=-1 79 #EE_BUFFER_FULL=1 80 #EE_NOT_FOUND=2 81 82 -class espeak_EVENT_id(Union):
83 _fields_=[ 84 ('number',c_int), 85 ('name',c_char_p), 86 ]
87
88 -class espeak_EVENT(Structure):
89 _fields_=[ 90 ('type',c_int), 91 ('unique_identifier',c_uint), 92 ('text_position',c_int), 93 ('length',c_int), 94 ('audio_position',c_int), 95 ('sample',c_int), 96 ('user_data',c_void_p), 97 ('id',espeak_EVENT_id), 98 ]
99
100 -class espeak_VOICE(Structure):
101 _fields_=[ 102 ('name',c_char_p), 103 ('languages',c_char_p), 104 ('identifier',c_char_p), 105 ('gender',c_byte), 106 ('age',c_byte), 107 ('variant',c_byte), 108 ('xx1',c_byte), 109 ('score',c_int), 110 ('spare',c_void_p), 111 ] 112
113 - def __eq__(self, other):
114 return isinstance(other, type(self)) and addressof(self) == addressof(other)
115 116 t_espeak_callback=CFUNCTYPE(c_int,POINTER(c_short),c_int,POINTER(espeak_EVENT))
117 118 @t_espeak_callback 119 -def callback(wav,numsamples,event):
120 try: 121 global player, isSpeaking, lastIndex 122 if not isSpeaking: 123 return 1 124 for e in event: 125 if e.type==espeakEVENT_MARK: 126 lastIndex=int(e.id.name) 127 elif e.type==espeakEVENT_LIST_TERMINATED: 128 break 129 if not wav: 130 player.idle() 131 isSpeaking = False 132 return 0 133 if numsamples > 0: 134 try: 135 player.feed(string_at(wav, numsamples * sizeof(c_short))) 136 except: 137 log.debugWarning("Error feeding audio to nvWave",exc_info=True) 138 return 0 139 except: 140 log.error("callback", exc_info=True)
141
142 -class BgThread(threading.Thread):
143 - def __init__(self):
144 threading.Thread.__init__(self) 145 self.setDaemon(True)
146
147 - def run(self):
148 global isSpeaking 149 while True: 150 func, args, kwargs = bgQueue.get() 151 if not func: 152 break 153 try: 154 func(*args, **kwargs) 155 except: 156 log.error("Error running function from queue", exc_info=True) 157 bgQueue.task_done()
158
159 -def _execWhenDone(func, *args, **kwargs):
160 global bgQueue 161 # This can't be a kwarg in the function definition because it will consume the first non-keywor dargument which is meant for func. 162 mustBeAsync = kwargs.pop("mustBeAsync", False) 163 if mustBeAsync or bgQueue.unfinished_tasks != 0: 164 # Either this operation must be asynchronous or There is still an operation in progress. 165 # Therefore, run this asynchronously in the background thread. 166 bgQueue.put((func, args, kwargs)) 167 else: 168 func(*args, **kwargs)
169
170 -def _speak(text):
171 global isSpeaking 172 uniqueID=c_int() 173 isSpeaking = True 174 flags = espeakCHARS_WCHAR | espeakSSML 175 return espeakDLL.espeak_Synth(text,0,0,0,0,flags,byref(uniqueID),0)
176
177 -def speak(text):
178 global bgQueue 179 _execWhenDone(_speak, text, mustBeAsync=True)
180
181 -def stop():
182 global isSpeaking, bgQueue, lastIndex 183 # Kill all speech from now. 184 # We still want parameter changes to occur, so requeue them. 185 params = [] 186 try: 187 while True: 188 item = bgQueue.get_nowait() 189 if item[0] != _speak: 190 params.append(item) 191 bgQueue.task_done() 192 except Queue.Empty: 193 # Let the exception break us out of this loop, as queue.empty() is not reliable anyway. 194 pass 195 for item in params: 196 bgQueue.put(item) 197 isSpeaking = False 198 player.stop() 199 lastIndex=None
200
201 -def pause(switch):
202 global player 203 player.pause(switch)
204
205 -def setParameter(param,value,relative):
206 _execWhenDone(espeakDLL.espeak_SetParameter,param,value,relative)
207
208 -def getParameter(param,current):
209 return espeakDLL.espeak_GetParameter(param,current)
210
211 -def getVoiceList():
212 voices=espeakDLL.espeak_ListVoices(None) 213 voiceList=[] 214 for voice in voices: 215 if not voice: break 216 voiceList.append(voice.contents) 217 return voiceList
218
219 -def getCurrentVoice():
220 voice = espeakDLL.espeak_GetCurrentVoice() 221 if voice: 222 return voice.contents 223 else: 224 return None
225
226 -def setVoice(voice):
227 # For some weird reason, espeak_EspeakSetVoiceByProperties throws an integer divide by zero exception. 228 setVoiceByName(voice.identifier)
229
230 -def setVoiceByName(name):
231 _execWhenDone(espeakDLL.espeak_SetVoiceByName,name)
232
233 -def _setVoiceAndVariant(voice=None, variant=None):
234 res = getCurrentVoice().identifier.split("+") 235 if not voice: 236 voice = res[0] 237 if not variant: 238 if len(res) == 2: 239 variant = res[1] 240 else: 241 variant = "none" 242 if variant == "none": 243 espeakDLL.espeak_SetVoiceByName(voice) 244 else: 245 try: 246 espeakDLL.espeak_SetVoiceByName("%s+%s" % (voice, variant)) 247 except: 248 espeakDLL.espeak_SetVoiceByName(voice)
249
250 -def setVoiceAndVariant(voice=None, variant=None):
251 _execWhenDone(_setVoiceAndVariant, voice=voice, variant=variant)
252
253 -def _setVoiceByLanguage(lang):
254 v=espeak_VOICE() 255 lang=lang.replace('_','-') 256 v.languages=lang 257 try: 258 espeakDLL.espeak_SetVoiceByProperties(byref(v)) 259 except: 260 v.languages="en" 261 espeakDLL.espeak_SetVoiceByProperties(byref(v))
262
263 -def setVoiceByLanguage(lang):
264 _execWhenDone(_setVoiceByLanguage, lang)
265
266 -def espeak_errcheck(res, func, args):
267 if res != EE_OK: 268 raise RuntimeError("%s: code %d" % (func.__name__, res)) 269 return res
270
271 -def initialize():
272 global espeakDLL, bgThread, bgQueue, player 273 espeakDLL=cdll.LoadLibrary(r"synthDrivers\espeak.dll") 274 espeakDLL.espeak_Info.restype=c_char_p 275 espeakDLL.espeak_Synth.errcheck=espeak_errcheck 276 espeakDLL.espeak_SetVoiceByName.errcheck=espeak_errcheck 277 espeakDLL.espeak_SetVoiceByProperties.errcheck=espeak_errcheck 278 espeakDLL.espeak_SetParameter.errcheck=espeak_errcheck 279 espeakDLL.espeak_Terminate.errcheck=espeak_errcheck 280 espeakDLL.espeak_ListVoices.restype=POINTER(POINTER(espeak_VOICE)) 281 espeakDLL.espeak_GetCurrentVoice.restype=POINTER(espeak_VOICE) 282 espeakDLL.espeak_SetVoiceByName.argtypes=(c_char_p,) 283 sampleRate=espeakDLL.espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS,300,"synthDrivers",0) 284 if sampleRate<0: 285 raise OSError("espeak_Initialize %d"%sampleRate) 286 player = nvwave.WavePlayer(channels=1, samplesPerSec=sampleRate, bitsPerSample=16, outputDevice=config.conf["speech"]["outputDevice"]) 287 espeakDLL.espeak_SetSynthCallback(callback) 288 bgQueue = Queue.Queue() 289 bgThread=BgThread() 290 bgThread.start()
291
292 -def terminate():
293 global bgThread, bgQueue, player, espeakDLL 294 stop() 295 bgQueue.put((None, None, None)) 296 bgThread.join() 297 espeakDLL.espeak_Terminate() 298 bgThread=None 299 bgQueue=None 300 player.close() 301 player=None 302 espeakDLL=None
303
304 -def info():
305 return espeakDLL.espeak_Info()
306
307 -def getVariantDict():
308 dir='synthDrivers\\espeak-data\\voices\\!v' 309 variantDict={"none":_("none")} 310 for fileName in os.listdir(dir): 311 if os.path.isfile("%s\\%s"%(dir,fileName)): 312 file=codecs.open("%s\\%s"%(dir,fileName)) 313 for line in file: 314 if line.startswith('name '): 315 temp=line.split(" ") 316 if len(temp) ==2: 317 name=temp[1].rstrip() 318 break 319 name=None 320 file.close() 321 if name is not None: 322 variantDict[fileName]=name 323 return variantDict
324