Statistics
| Branch: | Tag: | Revision:

hlrc / tts_bridge / mary / mary_tts_bridge / MaryTTSBridge.py @ e3691d2d

History | View | Annotate | Download (5.358 KB)

1 0c15613f Simon Schulz
#!/usr/bin/python
2
"""
3
This file is part of hlrc
4

5
Copyright(c) sschulz <AT> techfak.uni-bielefeld.de
6
http://opensource.cit-ec.de/projects/hlrc
7

8
This file may be licensed under the terms of the
9
GNU General Public License Version 3 (the ``GPL''),
10
or (at your option) any later version.
11

12
Software distributed under the License is distributed
13
on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14
express or implied. See the GPL for the specific language
15
governing rights and limitations.
16

17
You should have received a copy of the GPL along with this
18
program. If not, go to http://www.gnu.org/licenses/gpl.html
19
or write to the Free Software Foundation, Inc.,
20
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21

22
The development of this software was supported by the
23
Excellence Cluster EXC 277 Cognitive Interaction Technology.
24
The Excellence Cluster EXC 277 is a grant of the Deutsche
25
Forschungsgemeinschaft (DFG) in the context of the German
26
Excellence Initiative.
27
"""
28
29
import logging
30
import rospy
31
from hlrc_server.msg import *
32
import time
33
import sys
34
import actionlib
35
from io import BytesIO
36
import wave
37 e21d7f2c Simon Schulz
import os
38
import pkgutil
39 0c15613f Simon Schulz
from MaryTTSClient import *
40
from cStringIO import StringIO
41
42
class MaryTTSBridge(object):
43
    #_feedback = ttsActionFeedback()
44
    #_result   = ttsActionResult()
45
46
47
    def __init__(self, topic, voice="cmu-slt-hsmm", locale="en_GB", tts_host="127.0.0.1", tts_port=59125, loglevel=logging.WARNING):
48
        """initialise
49
        :param  loglevel: optional log level
50
        """
51
        self.loglevel = loglevel
52
        self.logger = logging.getLogger(__name__)
53
        # create nice and actually usable formatter and add it to the handler
54
        self.config_logger(loglevel)
55 163a7434 Simon Schulz
        self.logger.info("starting MaryTTSBridge on topic '"+topic+"'")
56 0c15613f Simon Schulz
57
        self.tts_client = MaryTTSClient(voice, locale, tts_host, tts_port, loglevel)
58
59
        rospy.init_node('MaryTTSBridge')
60
61
        self._action_name = topic
62
        self._as = actionlib.SimpleActionServer(self._action_name, ttsAction, execute_cb = self.execute_cb, auto_start = False)
63
        self._as.start()
64
65
66
67
    def __del__(self):
68
        """destructor
69
        """
70
        self.logger.debug("destructor of MaryTTSBridge called")
71
72
    def config_logger(self, level):
73
        """initialise a nice logger formatting
74
        :param  level: log level
75
        """
76
        formatter = logging.Formatter('%(asctime)s %(name)-30s %(levelname)-8s > %(message)s')
77
        ch = logging.StreamHandler()
78
        #ch.setLevel(level)
79
        ch.setFormatter(formatter)
80
        self.logger.setLevel(level)
81
        self.logger.addHandler(ch)
82
83
    def create_soundchunk(self, audio_data):
84
        #extract wave from data
85
        fio = BytesIO(audio_data)
86
        wav = wave.open(fio)
87
88
        s = soundchunk()
89
90
        s.channels = wav.getnchannels()
91
        s.data = audio_data
92
        s.endianess = s.ENDIAN_LITTLE #guessed?!
93
        s.rate = wav.getframerate()
94
        s.samplecount = wav.getnframes()
95
96
97
        #sample format:
98
        sample_width = wav.getsampwidth()
99
        if (sample_width == 1):
100
            s.sample_type = s.SAMPLE_U8
101
        elif (sample_width == 2):
102
            s.sample_type = s.SAMPLE_U16
103
        elif (sample_width == 3):
104
            s.sample_type = s.SAMPLE_U24
105
        else:
106
            self.logger.error("ERROR: invalid sample width "+str(sample_width) + " detected")
107
            s = soundchunk()
108
109
        self.logger.info("created soundchunk with "+str(s.samplecount)+" samples")
110
111
        return s
112
113
    def create_phonemes(self, phoneme_str):
114
        last = 0.0
115
        plist = []
116
117
        sio = StringIO(phoneme_str)
118
        for line in sio:
119
            if (line[0] != '#'):
120
                phoneme_list = line.split(" ")
121 e21d7f2c Simon Schulz
                if (line == '\n'):
122
                    #ignore empty lines
123
                    continue
124
                elif (len(phoneme_list) != 3):
125
                    print("> could not split line '%s' during phoneme seperation\n" % (line))
126
                else:
127
                    symbol = phoneme_list[2]
128
                        symbol = symbol.rstrip()
129
130
                       now = float(phoneme_list[0])
131
                    duration = (now - last)*1000
132
                    last = now
133
                    plist.append(phoneme(symbol, int(duration)))
134 0c15613f Simon Schulz
135
        self.logger.info("created phonemelist with " + str(len(plist)) + " elements")
136
137
        return plist
138
139
    def create_utterance(self, text, audio_data, phoneme_list):
140
        u = utterance()
141
        u.text  = text
142
        u.audio    = self.create_soundchunk(audio_data)
143
        u.phonemes = self.create_phonemes(phoneme_list)
144
145
        self.logger.info("created utterance for 'phonemelist with '" + u.text + "'")
146
        return u
147
148 e21d7f2c Simon Schulz
    def get_error_message(self):
149
        data_wav = pkgutil.get_data('mary_tts_bridge', 'data/connection_failed.wav')
150
        data_phonemes = pkgutil.get_data('mary_tts_bridge', 'data/connection_failed.phonemes')
151
        return (data_wav, data_phonemes)
152
153 0c15613f Simon Schulz
    def execute_cb(self, goal):
154
        self.logger.info("incoming utterance '" + goal.text + "'")
155
156
        success = True
157
        result   = ttsResult()
158
159
        #incoming msg, ask mary tts for data:
160
        try:
161
            audio     = self.tts_client.generate_audio(goal.text)
162
            phonelist = self.tts_client.generate_phonemes(goal.text)
163
164
        except:
165
            self.logger.error("failed to create utterance error = '" + str(sys.exc_info()[1]) + "'")
166 e21d7f2c Simon Schulz
            #try to open error message from file:
167
            success = True
168
            (audio, phonelist) = self.get_error_message()
169 0c15613f Simon Schulz
170
        if success:
171
            #build soundchunk
172
            result.utterance = self.create_utterance(goal.text, audio, phonelist)
173
            self._as.set_succeeded(result)
174
        else:
175
            self._as.set_aborted(result)
176
177
    def run(self):
178
        #run the main loop
179
        rospy.spin()
180
181
#test code
182 163a7434 Simon Schulz
def main():
183 0c15613f Simon Schulz
    if (len(sys.argv) != 2):
184
        print("> usage: "+sys.argv[0]+" <topic>\n\n")
185
        sys.exit(1)
186
187
    bridge = MaryTTSBridge(topic=sys.argv[1], loglevel=logging.INFO)
188
    bridge.run()
189
190 163a7434 Simon Schulz
if __name__ == "__main__":
191
    main()