hlrc / tts_bridge / mary / mary_tts_bridge / MaryTTSBridge.py @ f27f1860
History | View | Annotate | Download (6.089 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 | 38936fe1 | Robert Haschke | from .MaryTTSClient import * |
40 | try:
|
||
41 | from io import StringIO |
||
42 | except ImportError: |
||
43 | from cStringIO import StringIO |
||
44 | 0c15613f | Simon Schulz | |
45 | |||
46 | f27f1860 | Robert Haschke | class MaryTTSBridge(object): |
47 | # _feedback = ttsActionFeedback()
|
||
48 | # _result = ttsActionResult()
|
||
49 | 0c15613f | Simon Schulz | |
50 | def __init__(self, topic, voice="cmu-slt-hsmm", locale="en_GB", tts_host="127.0.0.1", tts_port=59125, loglevel=logging.WARNING): |
||
51 | f27f1860 | Robert Haschke | """initialise
|
52 | :param loglevel: optional log level
|
||
53 | """
|
||
54 | self.loglevel = loglevel
|
||
55 | self.logger = logging.getLogger(__name__)
|
||
56 | # create nice and actually usable formatter and add it to the handler
|
||
57 | self.config_logger(loglevel)
|
||
58 | self.logger.info("starting MaryTTSBridge on topic '"+topic+"'") |
||
59 | 0c15613f | Simon Schulz | |
60 | f27f1860 | Robert Haschke | self.tts_client = MaryTTSClient(voice, locale, tts_host, tts_port, loglevel)
|
61 | 0c15613f | Simon Schulz | |
62 | f27f1860 | Robert Haschke | rospy.init_node('MaryTTSBridge', anonymous=True) |
63 | 0c15613f | Simon Schulz | |
64 | f27f1860 | Robert Haschke | self._action_name = topic
|
65 | self._as = actionlib.SimpleActionServer(self._action_name, ttsAction, execute_cb = self.execute_cb, auto_start = False) |
||
66 | self._as.start()
|
||
67 | 0c15613f | Simon Schulz | |
68 | def __del__(self): |
||
69 | f27f1860 | Robert Haschke | """destructor
|
70 | """
|
||
71 | self.logger.debug("destructor of MaryTTSBridge called") |
||
72 | 0c15613f | Simon Schulz | |
73 | def config_logger(self, level): |
||
74 | f27f1860 | Robert Haschke | """initialise a nice logger formatting
|
75 | :param level: log level
|
||
76 | """
|
||
77 | formatter = logging.Formatter('%(asctime)s %(name)-30s %(levelname)-8s > %(message)s')
|
||
78 | ch = logging.StreamHandler() |
||
79 | #ch.setLevel(level)
|
||
80 | ch.setFormatter(formatter) |
||
81 | self.logger.setLevel(level)
|
||
82 | self.logger.addHandler(ch)
|
||
83 | 0c15613f | Simon Schulz | |
84 | def create_soundchunk(self, audio_data): |
||
85 | f27f1860 | Robert Haschke | #extract wave from data
|
86 | fio = BytesIO(audio_data) |
||
87 | wav = wave.open(fio) |
||
88 | |||
89 | s = soundchunk() |
||
90 | |||
91 | s.channels = wav.getnchannels() |
||
92 | s.data = audio_data |
||
93 | s.endianess = s.ENDIAN_LITTLE # guessed?!
|
||
94 | s.rate = wav.getframerate() |
||
95 | s.samplecount = wav.getnframes() |
||
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 | 0c15613f | Simon Schulz | |
113 | 38936fe1 | Robert Haschke | def create_phonemes(self, phoneme_bytes): |
114 | f27f1860 | Robert Haschke | last = 0.0
|
115 | plist = [] |
||
116 | 0c15613f | Simon Schulz | |
117 | f27f1860 | Robert Haschke | sio = StringIO(phoneme_bytes.decode('ascii'))
|
118 | for line in sio: |
||
119 | if (line[0] != '#'): |
||
120 | phoneme_list = line.split(" ")
|
||
121 | e21d7f2c | Simon Schulz | if (line == '\n'): |
122 | f27f1860 | Robert Haschke | # ignore empty lines
|
123 | e21d7f2c | Simon Schulz | continue
|
124 | elif (len(phoneme_list) != 3): |
||
125 | print("> could not split line '%s' during phoneme seperation\n" % (line))
|
||
126 | else:
|
||
127 | f27f1860 | Robert Haschke | symbol = phoneme_list[2]
|
128 | symbol = symbol.rstrip() |
||
129 | e21d7f2c | Simon Schulz | |
130 | f27f1860 | Robert Haschke | 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 | f27f1860 | Robert Haschke | self.logger.info("created phonemelist with " + str(len(plist)) + " elements") |
136 | 0c15613f | Simon Schulz | |
137 | f27f1860 | Robert Haschke | return plist
|
138 | 0c15613f | Simon Schulz | |
139 | def create_utterance(self, text, audio_data, phoneme_list): |
||
140 | f27f1860 | Robert Haschke | u = utterance() |
141 | u.text = text |
||
142 | u.audio = self.create_soundchunk(audio_data)
|
||
143 | u.phonemes = self.create_phonemes(phoneme_list)
|
||
144 | 0c15613f | Simon Schulz | |
145 | f27f1860 | Robert Haschke | self.logger.info("created utterance for 'phonemelist with '" + u.text + "'") |
146 | return u
|
||
147 | 0c15613f | Simon Schulz | |
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 | f27f1860 | Robert Haschke | self.logger.info("incoming utterance '" + goal.text + "'") |
155 | 0c15613f | Simon Schulz | |
156 | f27f1860 | Robert Haschke | success = True
|
157 | result = ttsResult() |
||
158 | 0c15613f | Simon Schulz | |
159 | f27f1860 | Robert Haschke | # 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 | 0c15613f | Simon Schulz | |
164 | f27f1860 | Robert Haschke | except:
|
165 | self.logger.error("failed to create utterance error = '" + str(sys.exc_info()[1]) + "'") |
||
166 | # try to open error message from file:
|
||
167 | e21d7f2c | Simon Schulz | success = True
|
168 | (audio, phonelist) = self.get_error_message()
|
||
169 | 0c15613f | Simon Schulz | |
170 | f27f1860 | Robert Haschke | 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 | 0c15613f | Simon Schulz | |
177 | def run(self): |
||
178 | f27f1860 | Robert Haschke | # run the main loop
|
179 | rospy.spin() |
||
180 | 0c15613f | Simon Schulz | |
181 | f27f1860 | Robert Haschke | # test code
|
182 | 163a7434 | Simon Schulz | def main(): |
183 | 0c15613f | Simon Schulz | if (len(sys.argv) != 2): |
184 | f27f1860 | Robert Haschke | print("> usage: "+sys.argv[0]+" <topic>\n\n") |
185 | sys.exit(1)
|
||
186 | 0c15613f | Simon Schulz | |
187 | bridge = MaryTTSBridge(topic=sys.argv[1], loglevel=logging.INFO)
|
||
188 | bridge.run() |
||
189 | |||
190 | 163a7434 | Simon Schulz | if __name__ == "__main__": |
191 | main() |