piper icon indicating copy to clipboard operation
piper copied to clipboard

using piper binary under python on aarch64, and load model only one time !!

Open elpimous opened this issue 5 months ago • 0 comments

Hello the team, hello all, i'm under jetson orin nx 16, ubuntu 20.04, ros Noetic 1/ I made a node who publish the text to speak 2/ another node listens for the text and speak it with piper aarch64 binary.

the easy solution is : os.system("echo TEXT_TO_SPEAK | ./piper --model en_US-lessac-medium.onnx --output-raw | aplay -r 22050 -f S16_LE -t raw -")

But in that case, it loads model each call, around 1second or more

so I tried with a subprocess :

#!/usr/bin/env python3

import rospy
from std_msgs.msg import String
import subprocess
import time

# Global variables to hold subprocess references
global echo_proc, piper_proc, aplay_proc
echo_proc = None
piper_proc = None
aplay_proc = None

# Paths to the model and the piper binary
model = '/home/nvidia/catkin_ws/src/neo_talk/model/axel_piper_model/axel.onnx'
piper_binary = '/home/nvidia/catkin_ws/src/neo_talk/model/piper_arm64/piper/piper'
piper_cmd = [piper_binary, '--model', model, '--length_scale', '1.0', '--sentence_silence', '1.0', '--output-raw']

def start_piper():
    global piper_proc
    try:
        rospy.loginfo("Starting the Piper process")
        # Start the Piper subprocess
        piper_proc = subprocess.Popen(piper_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
        if piper_proc.poll() is not None:
            rospy.logerr("Piper did not start correctly.")
            stop_processes()
    except Exception as e:
        rospy.logerr(f"Error starting Piper: {e}")
        stop_processes()

def callback(data):
    global echo_proc, piper_proc, aplay_proc

    text_to_speak = data.data
    echo_cmd = ['echo', text_to_speak]
    aplay_cmd = ['aplay', '-r', '22050', '-f', 'S16_LE', '-t', 'raw', '-', '-B', '500000']

    try:
        # Start echo subprocess to read text
        echo_proc = subprocess.Popen(echo_cmd, stdout=subprocess.PIPE)
        
        # Write the echoed text to the Piper process
        piper_proc.stdin.write(echo_proc.stdout.read())
        piper_proc.stdin.flush()
        
        time.sleep(0.05)  # Short delay

        # Start aplay to play the audio output from Piper
        aplay_proc = subprocess.Popen(aplay_cmd, stdin=piper_proc.stdout)
        aplay_proc.wait()
        
        echo_proc.wait()  # Wait for echo to finish
    except Exception as e:
        rospy.logerr(f"Error executing command: {e}")
        stop_processes()

def stop_processes():
    global echo_proc, piper_proc, aplay_proc

    # Terminate any running processes
    if echo_proc and echo_proc.poll() is None:
        echo_proc.terminate()
    if piper_proc and piper_proc.poll() is None:
        piper_proc.terminate()
    if aplay_proc and aplay_proc.poll() is None:
        aplay_proc.terminate()

    # Wait for processes to complete
    if echo_proc:
        echo_proc.wait()
    if piper_proc:
        piper_proc.wait()
    if aplay_proc:
        aplay_proc.wait()

def listener():
    rospy.init_node('piper_tts_node', anonymous=True)
    rospy.loginfo("Piper TTS node is running")
    start_piper()
    # Subscribe to the 'text_to_speak' topic
    rospy.Subscriber('text_to_speak', String, callback)
    rospy.spin()

if __name__ == '__main__':
    try:
        listener()
    except rospy.ROSInterruptException:
        pass
    finally:
        stop_processes()

Here, ok, model is loaded only one time, but voice isn't good, lot of noise, 2 voices same time, incomplete sentence... Tried to change buffers values, same problem. Any idea, solution ?

Thanks the team, Vincent

elpimous avatar Sep 21 '24 22:09 elpimous