From 31a909702ec8541dab0927caa672bbe703c4d4c5 Mon Sep 17 00:00:00 2001 From: Paul Bailey Date: Mon, 23 Feb 2026 00:04:46 +0000 Subject: [PATCH] Integrated professional-grade Piper TTS into installer and media tool --- install.sh | 40 +++++++++++++++++++++++++++++++++-- src/ollama_cli/tools/media.py | 24 +++++++++++++++------ 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/install.sh b/install.sh index 2e33b9e..515d9b8 100644 --- a/install.sh +++ b/install.sh @@ -100,7 +100,6 @@ if [[ $REPLY =~ ^[Yy]$ ]]; then fi # Update config to point to ComfyUI - # We use a python one-liner to update the json config safely python3 -c " import json import os @@ -113,12 +112,49 @@ else: config['comfy_url'] = 'http://127.0.0.1:8188' with open(config_path, 'w') as f: json.dump(config, f, indent=2) -print('✅ Configured ollama-cli to use ComfyUI at http://127.0.0.1:8188') " else echo "Skipping ComfyUI installation." fi +# 6. Install Piper TTS +echo "" +echo "-------------------------------------------" +read -p "Do you want to install Piper TTS (High-Quality Voice)? (y/n) " -n 1 -r +echo "" +if [[ $REPLY =~ ^[Yy]$ ]]; then + PIPER_DIR="$HOME/.ollama-cli/piper" + mkdir -p "$PIPER_DIR" + cd "$PIPER_DIR" + + if [ ! -f "piper" ]; then + echo "[*] Downloading Piper binary..." + # Detect architecture for Piper download + OS_TYPE=$(uname -s | tr '[:upper:]' '[:lower:]') + ARCH_TYPE=$(uname -m) + if [[ "$OS_TYPE" == "darwin" ]]; then + # macOS uses the apple silicon or intel build + curl -L "https://github.com/rhasspy/piper/releases/download/v1.2.0/piper_macos_x86_64.tar.gz" -o piper.tar.gz + else + curl -L "https://github.com/rhasspy/piper/releases/download/v1.2.0/piper_amd64.tar.gz" -o piper.tar.gz + fi + tar -xzf piper.tar.gz + # Move binary to top level + mv piper/piper . + mv piper/lib* . || true + rm -rf piper piper.tar.gz + fi + + if [ ! -f "voice.onnx" ]; then + echo "[*] Downloading High-Quality Voice Model (en_US-lessac-medium)..." + curl -L "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/medium/en_US-lessac-medium.onnx" -o voice.onnx + curl -L "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/medium/en_US-lessac-medium.onnx.json" -o voice.onnx.json + fi + echo "✅ Piper TTS installed." +else + echo "Skipping Piper installation." +fi + echo "" echo "===========================================" echo " Installation Complete! 🚀 " diff --git a/src/ollama_cli/tools/media.py b/src/ollama_cli/tools/media.py index e3fd2f4..b691167 100644 --- a/src/ollama_cli/tools/media.py +++ b/src/ollama_cli/tools/media.py @@ -89,23 +89,33 @@ def generate_image(prompt: str) -> str: @tool( name="speak_text", - description="Convert text to speech using Piper TTS", + description="Convert text to speech using Piper TTS (high quality)", parameters={ "text": {"type": "string", "description": "Text to speak"}, "output_file": {"type": "string", "description": "Output filename", "default": "output.wav"} } ) def speak_text(text: str, output_file: str = "output.wav") -> str: - """Uses local Piper installation if available""" - piper_bin = os.path.expanduser("~/piper/piper") - model_path = os.path.expanduser("~/piper/models/en_US-lessac-medium.onnx") # Guessing model path based on common setup + """Uses the high-quality Piper TTS installed by the installer""" + piper_dir = os.path.expanduser("~/.ollama-cli/piper") + piper_bin = os.path.join(piper_dir, "piper") + model_path = os.path.join(piper_dir, "voice.onnx") - # Fallback to simple 'say' command on macOS if piper is tricky to locate without scanning + if os.path.exists(piper_bin) and os.path.exists(model_path): + try: + # Piper reads from stdin and writes to file + cmd = f'echo "{text}" | {piper_bin} --model {model_path} --output_file {output_file}' + subprocess.run(cmd, shell=True, check=True, capture_output=True) + return f"Audio saved to {output_file} (using high-quality Piper TTS)" + except Exception as e: + return f"Error using Piper: {e}. Falling back to system TTS..." + + # Fallback to simple 'say' command on macOS if sys.platform == "darwin": try: subprocess.run(["say", "-o", output_file, text], check=True) - return f"Audio saved to {output_file} (using macOS 'say')" + return f"Audio saved to {output_file} (using system 'say' fallback)" except Exception as e: return f"Error using 'say': {e}" - return "TTS not configured for this platform yet." + return "TTS engine not found. Please run the installer to set up Piper."