1 '''
2 Defines the base class for all audio output devices.
3
4 @author: Brett Clippingdale
5 @author: Peter Parente
6 @organization: IBM Corporation
7 @copyright: Copyright (c) 2005, 2007 IBM Corporation
8 @license: The BSD License
9
10 All rights reserved. This program and the accompanying materials are made
11 available under the terms of the BSD license which accompanies
12 this distribution, and is available at
13 U{http://www.opensource.org/licenses/bsd-license.php}
14 '''
15 import time
16 import Base, Style
17 import Word, AEConstants
18 from i18n import _
19 from ThreadProxy import *
20
21 -class Audio(Base.AEOutput):
22 '''
23 Defines the base class for all audio output devices. Provides default
24 implementations of L{sendStringSync} and L{parseString} specific to audio
25 devices.
26
27 Some methods defined here raise NotImplementedError to ensure that
28 derived classes create appropriate implementions. Implementing
29 L{sendIndex} is optional. The L{DeviceManager} catches the not implemented
30 exception in this case.
31
32 @ivar listeners: List of callables that should be notified when a marker
33 inserted with L{sendIndex} is encountered
34 @type listeners: list
35 '''
36 STYLE = Style.AudioStyle
37
39 '''
40 Initializes the empty listeners list.
41 '''
42 Base.AEOutput.__init__(self)
43 self.listeners = []
44
46 '''
47 @return: 'audio' as the only capability of this device.
48 @rtype: list of string
49 '''
50 return ['audio']
51
53 '''
54 Adds a listener that should be notified when speech has progressed to the
55 point where a marker was inserted with L{sendIndex}.
56
57 @param listener: The method that should be called when markers received.
58 @type listener: callable
59 '''
60 self.listeners.append(listener)
61
63 '''
64 Removes the specified listener.
65
66 @param listener: The method that should no longer be called when markers
67 received.
68 @type listener: callable
69 @raise ValueError: When the given listener is not already registered
70 '''
71 self.listeners.remove(listener)
72
74 '''
75 Looks at the L{USE_THREAD} flag to see if the device implementing this
76 interface wants a thread proxy or not.
77
78 @return: self or L{ThreadProxy.AudioThreadProxy}
79 @rtype: L{AEOutput}
80 '''
81 if self.USE_THREAD == True:
82 return AudioThreadProxy(self)
83 elif self.USE_THREAD == False:
84 return self
85 else:
86 raise NotImplementedError('USE_THREAD not specified')
87
88 - def _parseMain(self, word, style, ch, part):
89 '''
90 Adds support for the L{AEOutput.Style.AudioStyle} settings of
91 CapExpand and NumExpand. This method is notified during parsing of the main
92 part of a L{Word}.
93
94 @param word: Word currently parsing its source text
95 @type word: L{Word}
96 @param style: Style used to configure the parsing process
97 @type style: L{AEOutput.Style.AudioStyle}
98 @param ch: Character in the word
99 @type ch: string
100 @param part: List of characters already processed in this part of the word
101 @type part: list
102 @return: Character(s) to be appended to the list
103 @rtype: string
104 '''
105 sf = style.SpellFormat
106 if sf and (word.isPunctuation(ch) or word.isSymbol(ch)):
107
108 if part:
109 part.append(', ')
110
111 return word.getCharName(ch) or ch
112 elif sf == Style.FORMAT_SPELL:
113
114 if part:
115 part.append(', ')
116 try:
117
118 return AEConstants.SPELLED_MAP[ch]
119 except KeyError:
120
121 return _('uni-code %s') % word.getCharValue(ch)
122 elif sf == Style.FORMAT_PHONETIC:
123
124 if part:
125 part.append(', ')
126 try:
127
128 return AEConstants.NATO_MAP[ch]
129 except KeyError:
130
131 return _('uni-code %s %s') % (word.getCharValue(ch),
132 word.getCharDescription(ch))
133
134
135 if style.CapExpand and word.isCap(ch) and word.getMainLength():
136 part.append(' ')
137 elif style.NumExpand and word.isNumeric(ch) and word.getMainLength():
138 part.append(' ')
139
140 return ch
141
143 '''
144 Provides a default implementation of parsing that formats words for audio
145 devices supporting speech output. The base L{Word} class is used plus some
146 additional processing for:
147
148 - blank words
149 - expanded caps
150 - expanded numbers
151 - spelling format
152 - individual characters
153
154 @param text: Text to be parsed
155 @type text: string
156 @param style: Style object defining how the text should be parsed
157 @type style: L{AEOutput.Style}
158 @param por: Point of regard for the first character in the text, or None if
159 the text is not associated with a POR
160 @type por: L{POR}
161 @param sem: Semantic tag for the text to aid parsing
162 @type sem: integer
163 @return: Parsed words
164 @rtype: 3-tuple of lists of string, L{POR}, L{AEOutput.Style}
165 '''
166 rv = []
167
168 words = Word.buildWordsFromString(text, por, style, self._parseMain)
169 for i, w in enumerate(words):
170 ns = None
171 if len(text) == 1 and style.SpellFormat == AEConstants.FORMAT_TEXT:
172
173
174 try:
175
176 ns = AEConstants.SPELLED_MAP[text]
177 except KeyError:
178 ns = text
179
180 if w.isAllBlank():
181 if i == 0 and len(words) > 1:
182
183
184 continue
185
186 if sem != AEConstants.SEM_CHAR:
187 ns = style.Blank
188
189 if ns is None:
190
191 ns = unicode(w)
192 if por is None:
193
194 por = None
195 else:
196 por = w.getPOR()
197
198 rv.append((ns, por, style))
199 return rv
200
201 - def send(self, name, value, style=None):
233
235 '''
236 Sends a string of one or more characters to the device. The style object
237 is used by the device in deciding how the given text should be presented.
238
239 @param text: Text to send to the device
240 @type text: string
241 @param style: Style with which this text should be output
242 @type style: L{AEOutput.Style}
243 @raise NotImplementedError: When not overridden in a subclass
244 '''
245 raise NotImplementedError
246
248 '''
249 Sends a string filename to the device, the contents of which should be
250 output. The style object is used by the device in decided how the given
251 text should be presented.
252
253 Typically, this method should be implemented by an audio device that
254 supports playback of waveform or sequencer files. It might also be used
255 by devices as a way of synthesizing the entire contents of a file without
256 having to pass all of the contents through the rest of the system.
257
258 @param fn: Absolute filename
259 @type fn: string
260 @param style: Style with which this text should be output
261 @type style: L{AEOutput.Style}
262 @raise NotImplementedError: When not overridden in a subclass
263 '''
264 raise NotImplementedError
265
267 '''
268 Purges buffered text and styles, and interrupts on-going output.
269
270 @param style: Style indicating which channel on which the stop should be
271 performed; None indicates stop on all channels
272 @type style: L{AEOutput.Style}
273 @raise NotImplementedError: When not overridden in a subclass
274 '''
275 raise NotImplementedError
276
278 '''
279 Indicates all text buffered by L{sendString} should now be output.
280 For devices that do the buffering in the driver, this action may mean
281 simply sending the command. For devices that do not buffer, this action
282 means sending text and styles buffered in the LSR device definition.
283
284 @param style: Style indicating which channel on which the talk should be
285 performed; None indicates talk on all channels
286 @type style: L{AEOutput.Style}
287 @raise NotImplementedError: When not overridden in a subclass
288 '''
289 raise NotImplementedError
290
292 '''
293 Inserts a marker in the output stream. The device should notify all
294 listeners when the marker is reached. The marker is typically a
295 monotonically increase integer number mod the maximum integer value
296 supported by the device.
297
298 @param style: Style indicating which channel on which the marker should be
299 inserted
300 @type style: L{AEOutput.Style}
301 @return: Unique marker identifying the index inserted
302 @rtype: integer
303 @raise NotImplementedError: When not overridden in a subclass
304 '''
305 raise NotImplementedError
306
308 '''
309 Buffers a complete string to send to the device synchronously.
310
311 This should B{not} be used in place of L{sendString} since this will not
312 return until the string is finished being output. This is provided B{only}
313 for the convenience of utility writers. Uses L{sendStop}, L{sendString},
314 L{sendTalk}, and then sleeps until L{isActive} returns False.
315
316 This method sends the stop, string, and talk commands directly to the
317 device, bypassing any thread proxy. This could be problematic if the audio
318 library in question cannot be used in more than one thread.
319
320 @param text: String to send to the device
321 @type text: string
322 @param style: Style on which this string should be output; None implies
323 some reasonable default should be used
324 @type style: L{AEOutput.Style}
325 '''
326 if style is None:
327 style = self.default_style
328 self.send(AEConstants.CMD_STOP, None, None)
329 self.send(AEConstants.CMD_STRING, text, style)
330 self.send(AEConstants.CMD_TALK, None, None)
331
332
333 while self.isActive():
334 time.sleep(1)
335
337 '''
338 Maps a language tag to the closest one possible as supported by this
339 device. The result may be an exact match or only a partial match. If
340 absolutely nothing matches the requested tag, not even starting at the
341 major language, None should be returned.
342
343 @param tag: IANA language tag, lower case
344 @type tag: string
345 @return: IANA language tag or None
346 @rtype: string
347 '''
348 raise NotImplementedError
349