diff --git a/bitbots_misc/bitbots_tts/bitbots_tts/tts.py b/bitbots_misc/bitbots_tts/bitbots_tts/tts.py index 647dcd147..7ab748224 100755 --- a/bitbots_misc/bitbots_tts/bitbots_tts/tts.py +++ b/bitbots_misc/bitbots_tts/bitbots_tts/tts.py @@ -8,6 +8,7 @@ import rclpy import requests +from ament_index_python import get_package_prefix from rcl_interfaces.msg import Parameter, SetParametersResult from rclpy.node import Node from rclpy.publisher import Publisher @@ -24,6 +25,13 @@ def speak(text: str, publisher: Publisher, priority: int = 20, speaking_active: publisher.publish(msg) +def say(text: str) -> None: + """Start the shell `say.sh` script to output given text with mimic3. Beware: this is blocking.""" + script_path = os.path.join(get_package_prefix("bitbots_tts"), "lib/bitbots_tts/say.sh") + process = subprocess.Popen((script_path, text)) + process.wait() + + class Speaker(Node): """ Uses tts to say messages from the speak topic. @@ -50,17 +58,6 @@ def __init__(self) -> None: # Callback for parameter changes self.add_on_set_parameters_callback(self.on_set_parameters) - # Mapping from robot name to voice name - self.robot_voice_mapping = { - "amy": "en_US/vctk_low", - "donna": "en_US/vctk_low", - "jack": "en_UK/apope_low", - "melody": "en_US/vctk_low", - "rory": "en_UK/apope_low", - } - - self.robot_speed_mapping = {"amy": 2.2, "donna": 2.2, "jack": 1.0, "melody": 2.2, "rory": 1.0} - # Subscribe to the speak topic self.create_subscription(Audio, "speak", self.speak_cb, 10) @@ -92,30 +89,15 @@ def on_set_parameters(self, parameters: List[Parameter]) -> SetParametersResult: return SetParametersResult(successful=True) def run_speaker(self) -> None: - """Continously checks the queue and speaks the next message.""" + """Continuously checks the queue and speaks the next message.""" # Check if there is a message in the queue if len(self.prio_queue) > 0: # Get the next message and speak it text, _ = self.prio_queue.pop(0) - self.say(text) - - def say(self, text: str) -> None: - """Speak this specific text.""" - # Get the voice name from the environment variable ROBOT_NAME or use the default voice if it's not set - voice = self.robot_voice_mapping.get(os.getenv("ROBOT_NAME"), "en_US/vctk_low") - # Get the speed for the given robot or use the default speed if no robot name is set - speed = self.robot_speed_mapping.get(os.getenv("ROBOT_NAME"), 2.2) - try: - # Generate the speech with mimic - mimic_subprocess = subprocess.Popen( - ("mimic3", "--remote", "--voice", voice, "--length-scale", str(speed), text), stdout=subprocess.PIPE - ) - # Play the audio from the previous process with aplay - aplay_subprocess = subprocess.Popen(("aplay", "-"), stdin=mimic_subprocess.stdout, stdout=subprocess.PIPE) - # Wait for the process to finish - aplay_subprocess.wait() - except OSError: - self.get_logger().error(str(traceback.format_exc())) + try: + say(text) + except OSError: + self.get_logger().error(str(traceback.format_exc())) def speak_cb(self, msg: Audio) -> None: """Handles incoming msg on speak topic.""" diff --git a/bitbots_misc/bitbots_tts/launch/tts.launch b/bitbots_misc/bitbots_tts/launch/tts.launch index 5f3f78bb5..04cadcbb5 100644 --- a/bitbots_misc/bitbots_tts/launch/tts.launch +++ b/bitbots_misc/bitbots_tts/launch/tts.launch @@ -1,5 +1,7 @@ - + + + diff --git a/bitbots_misc/bitbots_tts/scripts/say.sh b/bitbots_misc/bitbots_tts/scripts/say.sh new file mode 100755 index 000000000..0209913e9 --- /dev/null +++ b/bitbots_misc/bitbots_tts/scripts/say.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -eEuo pipefail + +ROBOT_NAME="${ROBOT_NAME:-}" + +# Mapping robot name to voice and speed +case "$ROBOT_NAME" in + "jack"|"rory") + voice="en_UK/apope_low" + speed=1.0 + ;; + "amy"|"donna"|"melody"|"rose") + voice="en_US/vctk_low" + speed=1.7 + ;; + *) + echo "Unknown robot: '$ROBOT_NAME', using default female voice" + voice="en_US/vctk_low" + speed=1.7 + ;; +esac + +text="$1" +if [ -z "$text" ]; then + echo "No text provided!" + exit 1 +fi + +# Generate the speech with mimic and play it with alsa +mimic3 --remote --voice "$voice" --length-scale "$speed" "$text" | aplay -q - diff --git a/bitbots_misc/bitbots_tts/scripts/speak_ip.py b/bitbots_misc/bitbots_tts/scripts/speak_ip.py new file mode 100755 index 000000000..5424d2376 --- /dev/null +++ b/bitbots_misc/bitbots_tts/scripts/speak_ip.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 + +import os +from socket import gethostname + +from bitbots_tts.tts import say +from netifaces import AF_INET, ifaddresses, interfaces + +ip_address = "not set" + +wifi_interface = next(filter(lambda i: i.startswith("wlp"), interfaces()), None) +if wifi_interface and AF_INET in ifaddresses(wifi_interface): + ip_address = ifaddresses(wifi_interface)[AF_INET][0]["addr"] + +ip = " dot ".join(ip_address.split(".")) + +robot_name = os.getenv("ROBOT_NAME") or gethostname() +msg = f"Startup complete: {robot_name}. My wifi IP is {ip}." +say(msg) diff --git a/bitbots_misc/bitbots_tts/setup.py b/bitbots_misc/bitbots_tts/setup.py index 301de3cc5..29cedacd8 100644 --- a/bitbots_misc/bitbots_tts/setup.py +++ b/bitbots_misc/bitbots_tts/setup.py @@ -16,7 +16,7 @@ install_requires=[ "setuptools", ], - scripts=["scripts/send_text.py"], + scripts=glob.glob("scripts/*"), entry_points={ "console_scripts": [ "tts = bitbots_tts.tts:main", diff --git a/bitbots_misc/bitbots_utils/scripts/speak_ip.py b/bitbots_misc/bitbots_utils/scripts/speak_ip.py deleted file mode 100755 index ca2d0c670..000000000 --- a/bitbots_misc/bitbots_utils/scripts/speak_ip.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python3 -import os -import socket - -import pyttsx3 -from netifaces import AF_INET, ifaddresses, interfaces - -ip_address = "not set" -for iface_name in interfaces(): - addresses = [i["addr"] for i in ifaddresses(iface_name).setdefault(AF_INET, [{"addr": "No IP addr"}])] - if iface_name == "wlp3s0": - ip_address = " ".join(addresses) - -hostname = socket.gethostname() -ip_parts = ip_address.split(".") -ip = ip_parts[0] + " point " + ip_parts[1] + " point " + ip_parts[2] + " point " + ip_parts[3] - -engine = pyttsx3.init() -engine.setProperty("rate", 175) -robot_name = os.getenv("ROBOT_NAME") -engine.say("Startup complete") -engine.runAndWait() -engine.setProperty("rate", 175) -engine.say(f"{robot_name}") -engine.say(f"My IP adress is {ip}") -engine.runAndWait()