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