Module GSpeech
[hide private]
[frames] | no frames]

Source Code for Module GSpeech

  1  ''' 
  2  Contains the L{GSpeech} abstract base class which provides support for all 
  3  gnome-speech devices. Device definitions using gnome-speech should derive from 
  4  this class and override the L{AEOutput.USE_THREAD 
  5  <AccessEngine.AEDevice.AEOutput.Base.AEOutput.USE_THREAD>} and 
  6  L{AEOutput.COMMAND_CHARS 
  7  <AccessEngine.AEDevice.AEOutput.Base.AEOutput.COMMAND_CHARS>} 
  8  properties if desired. The subclass should also provide 
  9  L{GSpeech.createDistinctStyles} 
 10  and L{GSpeech._applyStyle} methods. This module should never be directly registered as 
 11  its own speech device with L{AccessEngine.AERegistrar} as the GSpeech class does not fully 
 12  implement the L{AEOutput.Base.AEOutput} interface. 
 13   
 14  @var MAX_BUFFER: Maximum number of characters to buffer before forcing a  
 15    L{GSpeech.sendTalk} call. 
 16  @type MAX_BUFFER: integer 
 17  @var SAY_WAIT_RETRY: Time to wait between calls to say if say fails 
 18  @type SAY_WAIT_RETRY: float 
 19   
 20  @author: Larry Weiss 
 21  @author: Peter Parente 
 22  @author: Brett Clippingdale 
 23  @organization: IBM Corporation 
 24  @copyright: Copyright (c) 2005, 2007 IBM Corporation 
 25   
 26  @author: Frank Zenker 
 27  @organization: IT Science Center Ruegen gGmbH, Germany 
 28  @copyright: Copyright (c) 2007, 2008 ITSC Ruegen 
 29   
 30  @license: I{The BSD License} 
 31  All rights reserved. This program and the accompanying materials are made 
 32  available under the terms of the BSD license which accompanies 
 33  this distribution, and is available at 
 34  U{http://www.opensource.org/licenses/bsd-license.php} 
 35  ''' 
 36  import time 
 37  from AccessEngine import AEOutput 
 38  import Audio 
 39  from Tools.i18n import _ 
 40   
 41  import ORBit, bonobo 
 42  # try to get the typelib 
 43  ORBit.load_typelib('GNOME_Speech') 
 44  import GNOME.Speech, GNOME__POA.Speech 
 45   
 46  # constants specific to GSpeech devices 
 47  SAY_WAIT_RETRY = 0.01 
 48  MAX_BUFFER = 80 
 49   
50 -class GSpeechStyle(Audio.AudioStyle):
51 ''' 52 Style object built dynamically as we discover what capabilities the active 53 gnome-speech driver supports. 54 '''
55 - def init(self, device):
56 ''' 57 Initializes the style object based on the capabilities reported by the 58 device. 59 60 @param device: Reference to the gnome speech device class 61 @type device: L{GSpeech} 62 ''' 63 voices = device.getVoices() 64 # compute all voice names 65 names = dict(((v.name, i) for i, v in enumerate(voices))) 66 # create the voice setting 67 self.newEnum('Voice', 0, _('Voice'), names, _('Name of the active voice')) 68 # create a speaker with the first voice, assumes all voices have roughly 69 # the same supported parameters 70 sp = device.createSpeaker(voices[0]) 71 for param in sp.getSupportedParameters(): 72 # iterate over the supported parameters and stick them on the style obj 73 # as setting; this may give us more than our known default set 74 # of settings, but it's OK because they'll just be ignored by SUE 75 cap_name = param.name.title() 76 val = sp.getParameterValue(param.name) 77 if param.enumerated: 78 pass 79 else: 80 # new relative range setting 81 self.newRelRange(cap_name, param.current, cap_name, param.min, 82 param.max, 0)
83
84 - def getGroups(self):
85 ''' 86 Gets configurable absolute settings affecting all output from this device. 87 88 @return: Group of all configurable settings 89 @rtype: L{AEState.Setting.Group} 90 ''' 91 g = self.newGroup() 92 s = g.newGroup(_('Speech')) 93 s.extend([name for name in ('Pitch', 'Rate', 'Volume') 94 if self.hasSetting(name)]) 95 if self.isDefault(): 96 # generate a group for standard word parsing settings on the default 97 # object only for now 98 self._newWordGroup(g) 99 return g
100
101 -class GSpeech(Audio.Audio):
102 ''' 103 Defines an abstract base class to send output from SUE to a speech device via 104 gnome-speech U{http://cvs.gnome.org/viewcvs/gnome-speech/}. A subclass must 105 override the L{DEVICE_IID} class variable to indicate which gnome-speech 106 server should be instantiated. If the specific speech server does not have 107 named parameters that map automatically to the style properties in 108 L{AEOutput.Style <AccessEngine.AEOutput.AEOutput.Style>}, 109 L{createDistinctStyles} and L{_applyStyle} should be overridden. If the 110 default style should be something other than an empty shell object to be 111 dynamically populated in L{createDistinctStyles}. 112 113 To be compliant with SUE requirements, this implements the interface defined 114 in the L{Audio <Audio.Audio>} class. 115 116 @ivar last_style: Last style object to be applied to output 117 @type last_style: L{AEOutput.Style <AccessEngine.AEOutput.AEOutput.Style>} 118 @ivar driver: Reference to a speech engine server 119 @type driver: GNOME.Speech.SynthesisDriver 120 @ivar speaker: Speaker that will synthesize speech from buffered text 121 @type speaker: GNOME.Speech.Speaker 122 @ivar voices: List of all gnome-speech voices 123 @type voices: list 124 @ivar buffer: Buffer of text and styles to be sent to the device 125 @type buffer: list 126 @cvar DEVICE_IID: Interface identifier for the gnome-speech device. Defaults 127 to None and should be overridden in a subclass. 128 @type DEVICE_IID: string 129 ''' 130 DEVICE_IID = None 131 STYLE = GSpeechStyle 132
133 - def _applyStyle(self, style):
134 ''' 135 Applies a given style to this output device. All output following this 136 call will be presented in the given style until this method is called again 137 with a different style. 138 139 @param style: Style to apply to future output 140 @type style: L{AEOutput.Style <AccessEngine.AEOutput.AEOutput.Style>} 141 ''' 142 sp = self.createSpeaker(self.voices[style.Voice]) 143 #if self.speaker is not None: 144 #self.speaker.unref() 145 146 for param in sp.getSupportedParameters(): 147 cap_name = param.name.title() 148 try: 149 val = style.getSettingVal(cap_name) 150 sp.setParameterValue(param.name, val) 151 except KeyError: 152 pass 153 self.speaker = sp
154
155 - def createSpeaker(self, voice):
156 ''' 157 Creates a new speaker object from the given voice. 158 159 @param voice: Voice object 160 @type voice: GNOME.Speech.VoiceInfo 161 @return: New speaker object 162 @rtype: GNOME.Speech.Speaker 163 ''' 164 return self.driver.createSpeaker(voice)
165
166 - def getVoices(self):
167 ''' 168 Gets the list of available voices 169 170 @return: List of all gnome-speech voice objects 171 @rtype: list 172 ''' 173 return self.voices
174
175 - def init(self):
176 ''' 177 Initializes the speech driver through gnome-speech. 178 179 @raise AEOutput.InitError: When the device can not be initialized 180 ''' 181 self.driver = None 182 self.speaker = None 183 self.voices = [] 184 self.buffer = [] 185 self.last_style = None 186 187 # initialize CORBA 188 ORBit.CORBA.ORB_init() 189 # try to activate Festival 190 self.driver = bonobo.activation.activate_from_id(self.DEVICE_IID, 0, False) 191 try: 192 # check if already initialized 193 bInit = self.driver.isInitialized() 194 except AttributeError: 195 # driver is None when the engine is not available 196 raise AEOutput.InitError 197 # could have been initialized by someone else 198 if not bInit: 199 try: 200 failure = not self.driver.driverInit() 201 except Exception: 202 failure = True 203 if failure: 204 # driverInit fails when engine is not in a "good state" 205 raise AEOutput.InitError 206 # store all voices for later reference 207 self.voices = self.driver.getAllVoices()
208
209 - def _say(self, text):
210 ''' 211 Loops until L{speaker}.say succeeds by returning a non-negative value. 212 213 @note: gnome-speech returns a negative value when there is a backlog of 214 events that must be processed by a GNOME.Speech.SpeechCallback before 215 say will succeed 216 @param text: Text to say 217 @type text: string 218 ''' 219 try: 220 text = text.encode('latin-1', 'ignore') 221 except (TypeError, UnicodeEncodeError): 222 return 223 while 1: 224 sid = self.speaker.say(text) 225 if sid >= 0: break 226 time.sleep(SAY_WAIT_RETRY)
227
228 - def close(self):
229 ''' 230 Stops and closes the speech device. 231 ''' 232 if self.speaker is not None: 233 self.speaker.stop() 234 del self.speaker 235 del self.driver
236
237 - def sendStop(self, style=None):
238 ''' 239 Stops speech immediately. 240 241 @param style: Ignored 242 @type style: L{AEOutput.Style <AccessEngine.AEOutput.AEOutput.Style>} 243 ''' 244 if self.speaker is not None: 245 self.speaker.stop() 246 self.buffer = []
247
248 - def sendTalk(self, style=None):
249 ''' 250 Begins synthesis of the buffered text. 251 252 @param style: Ignored 253 @type style: L{AEOutput.Style <AccessEngine.AEOutput.AEOutput.Style>} 254 ''' 255 stream = [] 256 for text, style in self.buffer: 257 if style.isDirty() or self.last_style != style: 258 self._applyStyle(style) 259 self._say(' '.join(stream)) 260 self.last_style = style 261 stream = [] 262 if text is not None: 263 stream.append(text) 264 if stream: 265 self._say(' '.join(stream)) 266 self.buffer = []
267
268 - def sendString(self, text, style):
269 ''' 270 Adds the given string to the text to be synthesized. The string is buffered 271 locally in this object since gnome-speech does not support buffering in 272 the speech engine driver, even if the engine does support it. 273 274 @param text: String to buffer on the device 275 @type text: string 276 @param style: Style with which this string should be output; None means no 277 style change should be applied 278 @type style: integer 279 ''' 280 # just buffer the text since gnome-speech will try speaking it right away 281 # actually send the text in sendTalk 282 self.buffer.append((text, style.copy()))
283
284 - def isActive(self):
285 ''' 286 Indicates whether the device is active meaning it is busy doing output or 287 has text waiting to be output. 288 289 @return: C{True} when the speech device is synthesizing, C{False} otherwise 290 @rtype: boolean 291 ''' 292 return len(self.buffer) != 0 or self.speaker.isSpeaking()
293
294 - def sendIndex(self, style):
295 ''' 296 Sends an index marker to the device driver. The driver should notify the 297 device when the marker has been reached during output. 298 299 @todo: PP: implement when index working 300 301 @param style: Style indicating channel in which the marker will be appended 302 @type style: L{AEOutput.Style <AccessEngine.AEOutput.AEOutput.Style>} 303 ''' 304 raise NotImplementedError
305
306 - def createDistinctStyles(self, num_groups, num_layers):
307 ''' 308 Creates distinct styles following the guidelines set forth in the base 309 class. Only distinguishes groups in terms of voice, not layers. Assumes 310 rate, pitch, and volume are exposed under those weakly specified, but 311 typically used names by gnome-speech. 312 313 @param num_groups: Number of sematic groups the requestor would like to 314 represent using distinct styles 315 @type num_groups: integer 316 @param num_layers: Number of content origins (e.g. output originating from 317 a background task versus the focus) the requestor would like to represent 318 using distinct styles 319 @type num_layers: integer 320 @return: Styles 321 @rtype: list of L{AEOutput.Style <AccessEngine.AEOutput.AEOutput.Style>} 322 ''' 323 styles = [] 324 for i in range(0, num_groups*num_layers): 325 # compute a valid voice number 326 v_num = i % len(self.voices) 327 # get that voice from the list of all voices 328 v = self.voices[v_num] 329 # create a speaker with that voice 330 sp = self.createSpeaker(v) 331 # create a style object with relative rate, pitch, and volume 332 s = GSpeechStyle(self.default_style) 333 # initialize the style 334 s.init(self) 335 # store the current voice number 336 s.Voice = v_num 337 # iterate over the supported parameters and stick them on the style obj 338 # as style attributes; this may give us more than our known default set 339 # of styles, but it's OK because they'll just be ignored by SUE 340 for param in sp.getSupportedParameters(): 341 cap_name = param.name.title() 342 s.setSettingVal(cap_name, param.current) 343 styles.append(s) 344 return styles
345