11#!/usr/bin/env python3
22
3+ import os
4+ import subprocess
5+ import time
36import traceback
47
58import rclpy
6- import sounddevice as sd
9+ import requests
10+ from ament_index_python import get_package_prefix
711from rcl_interfaces .msg import Parameter , SetParametersResult
812from rclpy .callback_groups import MutuallyExclusiveCallbackGroup
913from rclpy .executors import MultiThreadedExecutor
1014from rclpy .node import Node
1115from rclpy .publisher import Publisher
12- from TTS .api import TTS
1316
1417from bitbots_msgs .msg import Audio
1518
16- # pip install TTS needed
1719
18-
19- def speak (
20- text : str , publisher : Publisher , priority : int = 20 , speaking_active : bool = True
21- ) -> None : # Needed for what?
20+ def speak (text : str , publisher : Publisher , priority : int = 20 , speaking_active : bool = True ) -> None :
2221 """Utility method which can be used by other classes to easily publish a message."""
2322 if speaking_active :
2423 msg = Audio ()
@@ -27,6 +26,13 @@ def speak(
2726 publisher .publish (msg )
2827
2928
29+ def say (text : str ) -> None :
30+ """Start the shell `say.sh` script to output given text with mimic3. Beware: this is blocking."""
31+ script_path = os .path .join (get_package_prefix ("bitbots_tts" ), "lib/bitbots_tts/say.sh" )
32+ process = subprocess .Popen ((script_path , text ))
33+ process .wait ()
34+
35+
3036class Speaker (Node ):
3137 """
3238 Uses tts to say messages from the speak topic.
@@ -38,6 +44,9 @@ def __init__(self) -> None:
3844
3945 # Class Variables
4046 self .prio_queue : list [tuple [str , int ]] = []
47+ self .speak_enabled = None
48+ self .print_say = None
49+ self .message_level = None
4150
4251 # Initialize Parameters
4352 self .declare_parameter ("print" , True )
@@ -53,7 +62,16 @@ def __init__(self) -> None:
5362 # Subscribe to the speak topic
5463 self .create_subscription (Audio , "speak" , self .speak_cb , 10 , callback_group = MutuallyExclusiveCallbackGroup ())
5564
56- self .tts = TTS ("tts_models/en/ek1/tacotron2" ).to ("cpu" )
65+ # Wait for the mimic server to start
66+ while True :
67+ try :
68+ requests .get ("http://localhost:59125" )
69+ break
70+ except requests .exceptions .ConnectionError :
71+ # log once per second that the server is not yet available
72+ self .get_logger ().info ("Waiting for mimic server to start..." , throttle_duration_sec = 2.0 )
73+ time .sleep (0.5 )
74+ pass
5775
5876 # Start processing the queue
5977 self .create_timer (0.1 , self .run_speaker , callback_group = MutuallyExclusiveCallbackGroup ())
@@ -78,7 +96,7 @@ def run_speaker(self) -> None:
7896 # Get the next message and speak it
7997 text , _ = self .prio_queue .pop (0 )
8098 try :
81- self . say (text )
99+ say (text )
82100 except OSError :
83101 self .get_logger ().error (str (traceback .format_exc ()))
84102
@@ -99,10 +117,6 @@ def speak_cb(self, msg: Audio) -> None:
99117 self .prio_queue .append ((text , prio ))
100118 self .prio_queue .sort (key = lambda x : x [1 ], reverse = True )
101119
102- def say (self , text : str ) -> None :
103- sd .play (self .tts .tts_to_file (text , "test.wav" ))
104- sd .wait ()
105-
106120
107121def main (args = None ):
108122 rclpy .init (args = args )
0 commit comments