1# Orca
2#
3# Copyright 2004-2009 Sun Microsystems Inc.
4#
5# This library is free software; you can redistribute it and/or
6# modify it under the terms of the GNU Lesser General Public
7# License as published by the Free Software Foundation; either
8# version 2.1 of the License, or (at your option) any later version.
9#
10# This library is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13# Lesser General Public License for more details.
14#
15# You should have received a copy of the GNU Lesser General Public
16# License along with this library; if not, write to the
17# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
18# Boston MA  02110-1301 USA.
19
20"""Manages the default speech server for orca.  A script can use this
21as its speech server, or it can feel free to create one of its own."""
22
23__id__        = "$Id$"
24__version__   = "$Revision$"
25__date__      = "$Date$"
26__copyright__ = "Copyright (c) 2005-2009 Sun Microsystems Inc."
27__license__   = "LGPL"
28
29import importlib
30import time
31
32from . import debug
33from . import logger
34from . import orca_state
35from . import settings
36from . import speech_generator
37from .speechserver import VoiceFamily
38
39from .acss import ACSS
40
41_logger = logger.getLogger()
42log = _logger.newLog("speech")
43
44# The speech server to use for all speech operations.
45#
46_speechserver = None
47
48# The last time something was spoken.
49_timestamp = 0
50
51def getSpeechServerFactories():
52    """Imports all known SpeechServer factory modules.  Returns a list
53    of modules that implement the getSpeechServers method, which
54    returns a list of speechserver.SpeechServer instances.
55    """
56
57    factories = []
58
59    moduleNames = settings.speechFactoryModules
60    for moduleName in moduleNames:
61        try:
62            module = importlib.import_module('orca.%s' % moduleName)
63            factories.append(module)
64        except:
65            debug.printException(debug.LEVEL_CONFIGURATION)
66
67    return factories
68
69def _initSpeechServer(moduleName, speechServerInfo):
70
71    global _speechserver
72
73    if not moduleName:
74        return
75
76    factory = None
77    try:
78        factory = importlib.import_module('orca.%s' % moduleName)
79    except:
80        try:
81            factory = importlib.import_module(moduleName)
82        except:
83            debug.printException(debug.LEVEL_SEVERE)
84
85    # Now, get the speech server we care about.
86    #
87    speechServerInfo = settings.speechServerInfo
88    if speechServerInfo:
89        _speechserver = factory.SpeechServer.getSpeechServer(speechServerInfo)
90
91    if not _speechserver:
92        _speechserver = factory.SpeechServer.getSpeechServer()
93        if speechServerInfo:
94            msg = 'SPEECH: Invalid speechServerInfo: %s' % speechServerInfo
95            debug.println(debug.LEVEL_INFO, msg, True)
96
97    if not _speechserver:
98        raise Exception("ERROR: No speech server for factory: %s" % moduleName)
99
100def init():
101    debug.println(debug.LEVEL_INFO, 'SPEECH: Initializing', True)
102    if _speechserver:
103        debug.println(debug.LEVEL_INFO, 'SPEECH: Already initialized', True)
104        return
105
106    try:
107        moduleName = settings.speechServerFactory
108        _initSpeechServer(moduleName,
109                          settings.speechServerInfo)
110    except:
111        moduleNames = settings.speechFactoryModules
112        for moduleName in moduleNames:
113            if moduleName != settings.speechServerFactory:
114                try:
115                    _initSpeechServer(moduleName, None)
116                    if _speechserver:
117                        break
118                except:
119                    debug.printException(debug.LEVEL_SEVERE)
120
121    if _speechserver:
122        msg = 'SPEECH: Using speech server factory: %s' % moduleName
123        debug.println(debug.LEVEL_INFO, msg, True)
124    else:
125        msg = 'SPEECH: Not available'
126        debug.println(debug.LEVEL_INFO, msg, True)
127
128    debug.println(debug.LEVEL_INFO, 'SPEECH: Initialized', True)
129
130def checkSpeechSetting():
131    msg = "SPEECH: Checking speech setting."
132    debug.println(debug.LEVEL_INFO, msg, True)
133
134    if not settings.enableSpeech:
135        shutdown()
136    else:
137        init()
138
139def __resolveACSS(acss=None):
140    if isinstance(acss, ACSS):
141        family = acss.get(acss.FAMILY)
142        try:
143            family = VoiceFamily(family)
144        except:
145            family = VoiceFamily({})
146        acss[acss.FAMILY] = family
147        return acss
148    elif isinstance(acss, list) and len(acss) == 1:
149        return ACSS(acss[0])
150    else:
151        voices = settings.voices
152        return ACSS(voices[settings.DEFAULT_VOICE])
153
154def sayAll(utteranceIterator, progressCallback):
155    if settings.silenceSpeech:
156        return
157    if _speechserver:
158        _speechserver.sayAll(utteranceIterator, progressCallback)
159    else:
160        for [context, acss] in utteranceIterator:
161            logLine = "SPEECH OUTPUT: '" + context.utterance + "'"
162            debug.println(debug.LEVEL_INFO, logLine, True)
163            log.info(logLine)
164
165def _speak(text, acss, interrupt):
166    """Speaks the individual string using the given ACSS."""
167
168    logLine = "SPEECH OUTPUT: '" + text + "'"
169    extraDebug = ""
170    if acss in list(settings.voices.values()):
171        for key in settings.voices:
172            if acss == settings.voices[key]:
173                if key != settings.DEFAULT_VOICE:
174                    extraDebug = " voice=%s" % key
175                break
176
177    debug.println(debug.LEVEL_INFO, logLine + extraDebug + str(acss), True)
178    log.info(logLine + extraDebug)
179
180    if _speechserver:
181        voice = ACSS(settings.voices.get(settings.DEFAULT_VOICE))
182        try:
183            voice.update(__resolveACSS(acss))
184        except:
185            pass
186        _speechserver.speak(text, __resolveACSS(voice), interrupt)
187
188def speak(content, acss=None, interrupt=True):
189    """Speaks the given content.  The content can be either a simple
190    string or an array of arrays of objects returned by a speech
191    generator."""
192
193    if settings.silenceSpeech:
194        return
195
196    validTypes = (str, list, speech_generator.Pause,
197                  speech_generator.LineBreak, ACSS)
198    error = "SPEECH: bad content sent to speak(): '%s'"
199    if not isinstance(content, validTypes):
200        debug.printStack(debug.LEVEL_WARNING)
201        debug.println(debug.LEVEL_WARNING, error % content, True)
202        return
203
204    global _timestamp
205    if _timestamp:
206        msg = "SPEECH: Last spoke %.4f seconds ago" % (time.time() - _timestamp)
207        debug.println(debug.LEVEL_INFO, msg, True)
208    _timestamp = time.time()
209
210    if isinstance(content, str):
211        _speak(content, acss, interrupt)
212    if not isinstance(content, list):
213        return
214
215    toSpeak = []
216    activeVoice = ACSS(acss)
217    for element in content:
218        if not isinstance(element, validTypes):
219            debug.println(debug.LEVEL_WARNING, error % element, True)
220        elif isinstance(element, list):
221            speak(element, acss, interrupt)
222        elif isinstance(element, str):
223            if len(element):
224                toSpeak.append(element)
225        elif toSpeak:
226            newVoice = ACSS(acss)
227            newItemsToSpeak = []
228            if isinstance(element, speech_generator.Pause):
229                if toSpeak[-1] and toSpeak[-1][-1].isalnum():
230                    toSpeak[-1] += '.'
231            elif isinstance(element, ACSS):
232                newVoice.update(element)
233                if newVoice == activeVoice:
234                    continue
235                newItemsToSpeak.append(toSpeak.pop())
236
237            if toSpeak:
238                string = " ".join(toSpeak)
239                _speak(string, activeVoice, interrupt)
240            activeVoice = newVoice
241            toSpeak = newItemsToSpeak
242
243    if toSpeak:
244        string = " ".join(toSpeak)
245        _speak(string, activeVoice, interrupt)
246
247def speakKeyEvent(event, acss=None):
248    """Speaks a key event immediately.
249
250    Arguments:
251    - event: input_event.KeyboardEvent to speak.
252    """
253
254    if settings.silenceSpeech:
255        return
256
257    keyname = event.getKeyName()
258    lockingStateString = event.getLockingStateString()
259    acss = __resolveACSS(acss)
260    msg = "%s %s" % (keyname, lockingStateString)
261    logLine = "SPEECH OUTPUT: '%s' %s" % (msg, acss)
262    debug.println(debug.LEVEL_INFO, logLine, True)
263    log.info(logLine)
264
265    if _speechserver:
266        _speechserver.speakKeyEvent(event, acss)
267
268def speakCharacter(character, acss=None):
269    """Speaks a single character immediately.
270
271    Arguments:
272    - character: text to be spoken
273    - acss:      acss.ACSS instance; if None,
274                 the default voice settings will be used.
275                 Otherwise, the acss settings will be
276                 used to augment/override the default
277                 voice settings.
278    """
279    if settings.silenceSpeech:
280        return
281
282    acss = __resolveACSS(acss)
283    msg = "SPEECH OUTPUT: '" + character + "' " + str(acss)
284    debug.println(debug.LEVEL_INFO, msg, True)
285    log.info("SPEECH OUTPUT: '%s'" % character)
286
287    if _speechserver:
288        _speechserver.speakCharacter(character, acss=acss)
289
290def isSpeaking():
291    """Returns True if the system is currently speaking."""
292    if _speechserver:
293        return _speechserver.isSpeaking()
294    else:
295        return False
296
297def getInfo():
298    info = None
299    if _speechserver:
300        info = _speechserver.getInfo()
301
302    return info
303
304def stop():
305    if _speechserver:
306        _speechserver.stop()
307
308def updateCapitalizationStyle(script=None, inputEvent=None):
309    if _speechserver:
310        _speechserver.updateCapitalizationStyle()
311
312    return True
313
314def updatePunctuationLevel(script=None, inputEvent=None):
315    """ Punctuation level changed, inform this speechServer. """
316
317    if _speechserver:
318        _speechserver.updatePunctuationLevel()
319
320    return True
321
322def increaseSpeechRate(script=None, inputEvent=None):
323    if _speechserver:
324        _speechserver.increaseSpeechRate()
325
326    return True
327
328def decreaseSpeechRate(script=None, inputEvent=None):
329    if _speechserver:
330        _speechserver.decreaseSpeechRate()
331    else:
332        logLine = "SPEECH OUTPUT: 'slower'"
333        debug.println(debug.LEVEL_INFO, logLine)
334        log.info(logLine)
335
336    return True
337
338def increaseSpeechPitch(script=None, inputEvent=None):
339    if _speechserver:
340        _speechserver.increaseSpeechPitch()
341
342    return True
343
344def decreaseSpeechPitch(script=None, inputEvent=None):
345    if _speechserver:
346        _speechserver.decreaseSpeechPitch()
347
348    return True
349
350def increaseSpeechVolume(script=None, inputEvent=None):
351    if _speechserver:
352        _speechserver.increaseSpeechVolume()
353    return True
354
355def decreaseSpeechVolume(script=None, inputEvent=None):
356    if _speechserver:
357        _speechserver.decreaseSpeechVolume()
358    return True
359
360def shutdown():
361    debug.println(debug.LEVEL_INFO, 'SPEECH: Shutting down', True)
362    global _speechserver
363    if _speechserver:
364        _speechserver.shutdownActiveServers()
365        _speechserver = None
366
367def reset(text=None, acss=None):
368    if _speechserver:
369        _speechserver.reset(text, acss)
370
371def testNoSettingsInit():
372    init()
373    speak("testing")
374    speak("this is higher", ACSS({'average-pitch' : 7}))
375    speak("this is slower", ACSS({'rate' : 3}))
376    speak("this is faster", ACSS({'rate' : 80}))
377    speak("this is quiet",  ACSS({'gain' : 2}))
378    speak("this is loud",   ACSS({'gain' : 10}))
379    speak("this is normal")
380
381def test():
382    from . import speechserver
383    factories = getSpeechServerFactories()
384    for factory in factories:
385        print(factory.__name__)
386        servers = factory.SpeechServer.getSpeechServers()
387        for server in servers:
388            try:
389                print("    ", server.getInfo())
390                for family in server.getVoiceFamilies():
391                    name = family[speechserver.VoiceFamily.NAME]
392                    print("      ", name)
393                    acss = ACSS({ACSS.FAMILY : family})
394                    server.speak(name, acss)
395                    server.speak("testing")
396                server.shutdown()
397            except:
398                debug.printException(debug.LEVEL_OFF)
399