Package AEOutput :: Module Audio
[hide private]
[frames] | no frames]

Source Code for Module AEOutput.Audio

  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
38 - def __init__(self):
39 ''' 40 Initializes the empty listeners list. 41 ''' 42 Base.AEOutput.__init__(self) 43 self.listeners = []
44
45 - def getCapabilities(self):
46 ''' 47 @return: 'audio' as the only capability of this device. 48 @rtype: list of string 49 ''' 50 return ['audio']
51
52 - def addIndexListener(self, listener):
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
62 - def removeIndexListener(self, listener):
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
73 - def getProxy(self):
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 # spelling is anything but words, always add a space 108 if part: 109 part.append(', ') 110 # get the unicode character name 111 return word.getCharName(ch) or ch 112 elif sf == Style.FORMAT_SPELL: 113 # spelling is set to characters only, always add a space 114 if part: 115 part.append(', ') 116 try: 117 # try to find a spelling 118 return AEConstants.SPELLED_MAP[ch] 119 except KeyError: 120 # unicode hex value followed 121 return _('uni-code %s') % word.getCharValue(ch) 122 elif sf == Style.FORMAT_PHONETIC: 123 # spelling is set to NATO phonetic, always add a space 124 if part: 125 part.append(', ') 126 try: 127 # try to use a nato code word 128 return AEConstants.NATO_MAP[ch] 129 except KeyError: 130 # unicode hex value followed by the character classification 131 return _('uni-code %s %s') % (word.getCharValue(ch), 132 word.getCharDescription(ch)) 133 134 # only handle cap/num expand if spelling isn't enabled 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
142 - def parseString(self, text, style, por, sem):
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 # parse words 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 # handle individual characters by spelling them if we're treating them 173 # as words 174 try: 175 # try to find a spelling 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 # ignore initial blank words when there is more than one word in the 183 # group 184 continue 185 # fill in blank words 186 if sem != AEConstants.SEM_CHAR: 187 ns = style.Blank 188 189 if ns is None: 190 # leave non-blanks alone 191 ns = unicode(w) 192 if por is None: 193 # don't bother with PORs if we didn't start with one 194 por = None 195 else: 196 por = w.getPOR() 197 # ignore style changes for the time being 198 rv.append((ns, por, style)) 199 return rv
200
201 - def send(self, name, value, style=None):
202 ''' 203 Dispatches known commands to their appropriate handlers. 204 205 @param name: Descriptor of the data value sent 206 @type name: object 207 @param value: Content value 208 @type value: object 209 @param style: Style with which this value should be output 210 @type style: L{AEOutput.Style} 211 @return: Return value specific to the given command 212 @rtype: object 213 @raise NotImplementedError: When the handler for a common command is not 214 implemented by a subclass 215 ''' 216 if name == AEConstants.CMD_STOP: 217 self.sendStop(style) 218 elif name == AEConstants.CMD_TALK: 219 self.sendTalk(style) 220 elif name == AEConstants.CMD_STRING: 221 self.sendString(value, style) 222 elif name == AEConstants.CMD_STRING_SYNC: 223 self.sendStringSync(value, style) 224 elif name == AEConstants.CMD_FILENAME: 225 # may not be implemented 226 self.sendFilename(value, style) 227 elif name == AEConstants.CMD_INDEX: 228 # may not be implemented 229 return self.sendIndex(style) 230 elif name == AEConstants.CMD_GET_CLOSEST_LANG: 231 # may not be implemented 232 return self.sendGetClosestLang(value)
233
234 - def sendString(self, text, style):
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
247 - def sendFilename(self, fn, style):
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
266 - def sendStop(self, style=None):
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
277 - def sendTalk(self, style=None):
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
291 - def sendIndex(self, style):
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
307 - def sendStringSync(self, text, style):
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 # wait until done speaking; this blocks the main thread -- be careful 333 while self.isActive(): 334 time.sleep(1)
335
336 - def sendGetClosestLang(self, tag):
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