Coverage Report - orca.espeechfactory

ModuleCoverage %
orca.espeechfactory
32%
1
# Orca
2
#
3
# Copyright 2005-2006 Google Inc.
4
#
5
# This library is free software; you can redistribute it and/or
6
# modify it under the terms of the GNU Library General Public
7
# License as published by the Free Software Foundation; either
8
# version 2 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
# Library General Public License for more details.
14
#
15
# You should have received a copy of the GNU Library General Public
16
# License along with this library; if not, write to the
17
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18
# Boston, MA 02111-1307, USA.
19
#
20
"""Python wrapper for Emacspeak speech servers.
21
22
The emacspeak TTS server provides a simple but powerful and
23
well-tested speech-server abstraction. That server is implemented
24
as an external program (typically in TCL).  This wrapper class
25
provides Python access to available Emacspeak speech servers.
26
27
Initially, this class will provide Python access to the TTS
28
server commands.  Over time, this module may also implement
29
functionality present in Emacspeak's Lisp layer ---specifically,
30
higher level TTS functionality provided by the following
31
emacspeak modules:
32
33
0)  dtk-speak.el
34
35
1)  emacspeak-pronounce.el
36
37
2)  accs-structure.el
38
39 1
"""
40
41 1
__id__ = "$Id: espeechfactory.py 1680 2006-11-09 18:43:48Z wwalker $"
42 1
__author__ = "T. V. Raman"
43 1
__version__ = "$Revision: 1680 $"
44 1
__date__ = "$Date: 2006-11-09 10:43:48 -0800 (Thu, 09 Nov 2006) $"
45 1
__copyright__ = "Copyright (c) 2005 Google Inc."
46 1
__license__ = "LGPL"
47 1
__all__=['Speaker']
48
49 1
import os
50
51 1
import debug
52 1
import settings
53 1
import speechserver
54
55 1
from orca_i18n import _           # for gettext support
56
57 2
class SpeechServer(speechserver.SpeechServer):
58
59
    """Provides speech server abstraction.
60
61
    Class Variables:
62
63
        location -- specifies directory where Emacspeak
64
        speech servers are installed.
65
66
        config --  dictionary of default settings.
67
68
    Speaker objects can be initialized with the following parameters:
69
70
        engine -- TTS server to instantiate. Default: outloud
71
        host -- Host that runs   server. Default: localhost
72
        settings -- Dictionary of default settings.
73
74
    """
75
76 1
    location="/usr/share/emacs/site-lisp/emacspeak/servers"
77
78 1
    config = {'splitcaps' : 1,
79
              'rate' : 70,
80
    'capitalize' : 0,
81
    'allcaps' : 0,
82
    'punctuations' : 'all'
83
    }
84
85
    # Dictionary of known running servers.  The key is the name and
86
    # the value is the SpeechServer instance.  We do this to enforce a
87
    # singleton instance of any given server.
88
    #
89 1
    __activeServers = {}
90 1
    __getSpeechServersCalled = False
91
92 1
    def getFactoryName():
93
        """Returns a localized name describing this factory."""
94 0
        return _("Emacspeak Speech Services")
95
96 1
    getFactoryName = staticmethod(getFactoryName)
97
98 1
    def getSpeechServers():
99
        """Enumerate available speech servers.
100
101
        Returns a list of [name, id] values identifying the available
102
        speech servers.  The name is a human consumable string and the
103
        id is an object that can be used to create a speech server
104
        via the getSpeechServer method.
105
        """
106
107 1
        if SpeechServer.__getSpeechServersCalled:
108 0
            return SpeechServer.__activeServers.values()
109
        else:
110 1
            SpeechServer.__getSpeechServersCalled = True
111
112 1
        f = open(os.path.join(SpeechServer.location, '.servers'))
113 0
        for line in f:
114 0
            if line[0] == '#' or line.strip() == '': continue
115 0
            name = line.strip()
116 0
            if not SpeechServer.__activeServers.has_key(name):
117 0
                try:
118 0
                    SpeechServer.__activeServers[name] = SpeechServer(name)
119 0
                except:
120 0
                    pass
121 0
        f.close()
122
123 0
        return SpeechServer.__activeServers.values()
124
125 1
    getSpeechServers = staticmethod(getSpeechServers)
126
127 1
    def getSpeechServer(info=['outloud','outloud']):
128
        """Gets a given SpeechServer based upon the info.
129
        See SpeechServer.getInfo() for more info.
130
        """
131 0
        if SpeechServer.__activeServers.has_key(info[0]):
132 0
            return SpeechServer.__activeServers[info[0]]
133
        else:
134 0
            try:
135 0
                return SpeechServer(info[0])
136 0
            except:
137 0
                debug.printException(debug.LEVEL_SEVERE)
138 0
            return None
139
140 1
    getSpeechServer = staticmethod(getSpeechServer)
141
142 1
    def shutdownActiveServers():
143
        """Cleans up and shuts down this factory.
144
        """
145 0
        for key in SpeechServer.__activeServers.keys():
146 0
            server = SpeechServer.__activeServers[key]
147 0
            server.shutdown()
148
149 1
    shutdownActiveServers = staticmethod(shutdownActiveServers)
150
151 1
    def __init__ (self,
152
                  engine='outloud',
153
                  host='localhost',
154
                  initial=config):
155
        """Launches speech engine."""
156
157 0
        speechserver.SpeechServer.__init__(self)
158
159 0
        self._engine = engine
160 0
        e = __import__(_getcodes(engine),
161 0
                       globals(),
162 0
                       locals(),
163 0
                       [''])
164 0
        self.getvoice     = e.getvoice
165 0
        self.getrate      = e.getrate
166 0
        self.getvoicelist = e.getvoicelist
167 0
        if host == 'localhost':
168 0
            self._server = os.path.join(SpeechServer.location, self._engine)
169
        else:
170 0
            self._server = os.path.join(SpeechServer.location,
171 0
                                         "ssh-%s" % self._engine)
172 0
        cmd = '{ ' + self._server + '; } 2>&1'
173
        #print "Command = ", cmd
174
        #self._output = os.popen(cmd, "w", 1)
175 0
        [self._output, stdout, stderr] = os.popen3(cmd, "w", 1)
176 0
        self._settings ={}
177 0
        if initial:
178 0
            self._settings.update(initial)
179 0
            self.configure(self._settings)
180
181 1
    def configure(self, settings):
182
        """Configure engine with settings."""
183 0
        for k in settings.keys():
184 0
            if hasattr(self, k) and callable(getattr(self,k)):
185 0
                getattr(self,k)(settings[k])
186
187 1
    def settings(self): return self._settings
188
189 1
    def getInfo(self):
190
        """Returns [driverName, serverId]
191
        """
192 0
        return [self._engine, self._engine]
193
194 1
    def getVoiceFamilies(self):
195
        """Returns a list of speechserver.VoiceFamily instances
196
        representing all the voice families known by the speech server.
197
        """
198
199 0
        families = []
200 0
        try:
201 0
            for voice in self.getvoicelist():
202 0
                props = {
203
                    speechserver.VoiceFamily.NAME   : voice
204
                }
205 0
                families.append(speechserver.VoiceFamily(props))
206 0
        except:
207 0
            debug.printException(debug.LEVEL_SEVERE)
208 0
            pass
209
210 0
        return families
211
212 1
    def queueText(self, text="", acss=None):
213
        """Queue text to be spoken.
214
        Output is produced by next call to say() or speak()."""
215 0
        if acss:
216 0
            code =self.getvoice(acss)
217 0
            self._output.write("q {%s %s %s}\n" %(code[0], text,
218
        code[1]))
219
        else:
220 0
            self._output.write("q {%s}\n" %text)
221
222 1
    def queueTone(self, pitch=440, duration=50):
223
        """Queue specified tone."""
224 0
        self._output.write("t %s %s\n " % (pitch, duration))
225
226 1
    def queueSilence( self, duration=50):
227
        """Queue specified silence."""
228 0
        self._output.write("sh  %s" %  duration)
229
230 1
    def speakCharacter(self, character, acss=None):
231
        """Speak single character."""
232 0
        self._output.write("l {%s}\n" % character)
233
234 1
    def speakUtterances(self, list, acss=None, interrupt=True):
235
        """Speak list of utterances."""
236 0
        if acss:
237 0
            code =self.getvoice(acss)
238 0
            for t in list:
239 0
                self._output.write("q { %s %s %s }\n" %(code[0], str(t), code[1]))
240
        else:
241 0
            for t in list:
242 0
                self._output.write("q { %s }\n" % str(t))
243 0
        self._output.write("d\n")
244
245 1
    def speak(self, text="", acss=None, interrupt=True):
246
        """Speaks specified text. All queued text is spoken immediately."""
247
248
        # If the user has speech turned off, just return.
249
        #
250 0
        if not settings.enableSpeech:
251 0
            return
252
253 0
        if acss:
254 0
            code =self.getvoice(acss)
255 0
            self._output.write("q {%s %s %s}\nd\n" %(code[0], text, code[1]))
256
        else:
257 0
            self._output.write("q {%s}\nd\n" %text)
258
259 1
    def increaseSpeechPitch(self, step=0.5):
260
        """Increase speech pitch."""
261 0
        self._settings['average-pitch'] += step
262
263 1
    def decreaseSpeechPitch(self, step=0.5):
264
        """Decrease speech pitch."""
265 0
        self._settings['average-pitch'] -= step
266
267 1
    def increaseSpeechRate(self, step=5):
268
        """Set speech rate."""
269 0
        self._settings['rate'] += step
270 0
        self._output.write("tts_set_speech_rate %s\n" \
271
                            % self.getrate(self._settings['rate']))
272
273 1
    def decreaseSpeechRate(self, step=5):
274
        """Set speech rate."""
275 0
        self._settings['rate'] -= step
276 0
        self._output.write("tts_set_speech_rate %s\n" \
277
                            % self.getrate(self._settings['rate']))
278
279 1
    def stop(self):
280
        """Silence ongoing speech."""
281 0
        self._output.write("s\n")
282
283 1
    def shutdown(self):
284
        """Shutdown speech engine."""
285 0
        if SpeechServer.__activeServers.has_key(self._engine):
286 0
            self._output.close()
287 0
            del SpeechServer.__activeServers[self._engine]
288
289 1
    def reset(self, text=None, acss=None):
290
        """Reset TTS engine."""
291 0
        self._output.write("tts_reset\n")
292
293 1
    def version(self):
294
        """Speak TTS version info."""
295 0
        self._output.write("version\n")
296
297 1
    def punctuations(self, mode):
298
        """Set punctuation mode."""
299 0
        if mode in ['all', 'some', 'none']:
300 0
            self._settings['punctuations'] = mode
301 0
            self._output.write("tts_set_punctuations %s\n" % mode)
302
303 1
    def rate(self, r):
304
        """Set speech rate."""
305 0
        self._settings['rate'] = r
306 0
        self._output.write("tts_set_speech_rate %s\n" % self.getrate(r))
307
308 1
    def splitcaps(self, flag):
309
        """Set splitcaps mode. 1  turns on, 0 turns off"""
310 0
        flag = bool(flag) and 1 or 0
311 0
        self._settings['splitcaps'] = flag
312 0
        self._output.write("tts_split_caps %s\n" % flag)
313
314 1
    def capitalize(self, flag):
315
        """Set capitalization  mode. 1  turns on, 0 turns off"""
316 0
        flag = bool(flag) and 1 or 0
317 0
        self._settings['capitalize'] = flag
318 0
        self._output.write("tts_capitalize %s\n" % flag)
319
320 1
    def allcaps(self, flag):
321
        """Set allcaps  mode. 1  turns on, 0 turns off"""
322 0
        flag = bool(flag) and 1 or 0
323 0
        self._settings['allcaps'] = flag
324 0
        self._output.write("tts_allcaps_beep %s\n" % flag)
325
326 1
    def __del__(self):
327
        "Shutdown speech engine."
328 0
        if hasattr(self, "_output") \
329
           and not self._output.closed:
330 0
            self.shutdown()
331
332 1
def _getcodes(engine):
333
    """Helper function that fetches synthesizer codes for a
334
    specified engine."""
335 0
    if engine not in _codeTable:
336 0
        raise Exception("No code table for %s" % engine)
337 0
    return _codeTable[engine]
338
339 1
_codeTable = {
340
    'dtk-exp' : 'dectalk',
341
    'dtk-mv' : 'dectalk',
342
    'dtk-soft' : 'dectalk',
343
    'outloud' : 'outloud',
344
    }
345
346 1
def _test():
347
    """Self test."""
348 0
    import time
349 0
    import acss
350 0
    s=SpeechServer()
351 0
    a=acss.ACSS()
352 0
    s.punctuations('some')
353 0
    s.queueText("This is an initial test.");
354 0
    s.queueText("Next, we'll test audio formatted output.")
355 0
    for d in ['average-pitch', 'pitch-range',
356
              'richness', 'stress']:
357 0
        for i in range(0,10,2):
358 0
            a[d] = i
359 0
            s.queueText("Set %s to %i. " % (d, i), a)
360 0
        del a[d]
361 0
        s.queueText("Reset %s." % d, a)
362 0
    s.speak()
363 0
    print "sleeping  while waiting for speech to complete."
364 0
    time.sleep(40)
365 0
    s.shutdown()
366
367
368 1
if __name__=="__main__": _test()