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
43 ORBit.load_typelib('GNOME_Speech')
44 import GNOME.Speech, GNOME__POA.Speech
45
46
47 SAY_WAIT_RETRY = 0.01
48 MAX_BUFFER = 80
49
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
65 names = dict(((v.name, i) for i, v in enumerate(voices)))
66
67 self.newEnum('Voice', 0, _('Voice'), names, _('Name of the active voice'))
68
69
70 sp = device.createSpeaker(voices[0])
71 for param in sp.getSupportedParameters():
72
73
74
75 cap_name = param.name.title()
76 val = sp.getParameterValue(param.name)
77 if param.enumerated:
78 pass
79 else:
80
81 self.newRelRange(cap_name, param.current, cap_name, param.min,
82 param.max, 0)
83
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
97
98 self._newWordGroup(g)
99 return g
100
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
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
144
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
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
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
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
188 ORBit.CORBA.ORB_init()
189
190 self.driver = bonobo.activation.activate_from_id(self.DEVICE_IID, 0, False)
191 try:
192
193 bInit = self.driver.isInitialized()
194 except AttributeError:
195
196 raise AEOutput.InitError
197
198 if not bInit:
199 try:
200 failure = not self.driver.driverInit()
201 except Exception:
202 failure = True
203 if failure:
204
205 raise AEOutput.InitError
206
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
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
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
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
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
281
282 self.buffer.append((text, style.copy()))
283
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
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
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
326 v_num = i % len(self.voices)
327
328 v = self.voices[v_num]
329
330 sp = self.createSpeaker(v)
331
332 s = GSpeechStyle(self.default_style)
333
334 s.init(self)
335
336 s.Voice = v_num
337
338
339
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