#!/bin/bash set -euo pipefail PROGPATH="$(realpath "$0")" PROGDIR="$(dirname "${PROGPATH}")" KNOWN_ARGS=( data-dir engine gpu no-gpu ) # shellcheck source=/dev/null source "${PROGDIR}/shell.functions" ENGINE="${ENGINE:-${POSITIONAL_ARGS[0]:-}}" case "${ENGINE}" in omnivoice|qwen3|moss) ;; *) echo "Usage: setup_modern_tts_envs --engine= [--data-dir=/data]" >&2 exit 2 ;; esac PYTHON_BIN="${MWW_TTS_PYTHON:-python3.12}" command -v "${PYTHON_BIN}" >/dev/null 2>&1 || PYTHON_BIN=python3 if [ -z "${GPU:-}" ] ; then GPU=false if [ -c /dev/nvidiactl ] || { command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi -L >/dev/null 2>&1 ; } ; then GPU=true fi fi TTS_ROOT="${DATA_DIR}/tts-envs" VENV="${TTS_ROOT}/${ENGINE}" STACK_VERSION="modern-tts-v1" MARKER="${VENV}/.stack-version" mkdir -p "${TTS_ROOT}" "${DATA_DIR}/.cache/huggingface" case "${ENGINE}" in omnivoice) TORCH_VERSION="2.8.0" TORCHAUDIO_VERSION="2.8.0" PACKAGE_SPEC="git+https://github.com/k2-fsa/OmniVoice.git@28bc0889d92110491d726a9c79f26a895db5a074" IMPORT_NAME="omnivoice" STACK_ID="${STACK_VERSION}:omnivoice-28bc088:torch-${TORCH_VERSION}" ;; qwen3) TORCH_VERSION="2.9.1" TORCHAUDIO_VERSION="2.9.1" PACKAGE_SPEC="qwen-tts==0.1.1" IMPORT_NAME="qwen_tts" STACK_ID="${STACK_VERSION}:qwen-tts-0.1.1:torch-${TORCH_VERSION}" ;; moss) TORCH_VERSION="2.7.0" TORCHAUDIO_VERSION="2.7.0" PACKAGE_SPEC="git+https://github.com/OpenMOSS/MOSS-TTS-Nano.git@cc7bdf19c7639c0870dab22045a33b442760f6be" IMPORT_NAME="moss_tts_nano" STACK_ID="${STACK_VERSION}:moss-cc7bdf1:torch-${TORCH_VERSION}" ;; esac environment_ready() { [ -x "${VENV}/bin/python" ] || return 1 [ -f "${MARKER}" ] || return 1 [ "$(cat "${MARKER}")" = "${STACK_ID}" ] || return 1 "${VENV}/bin/python" - "${IMPORT_NAME}" "${GPU}" <<'PY' >/dev/null 2>&1 import importlib import sys import torch importlib.import_module(sys.argv[1]) expect_cuda = sys.argv[2].lower() == "true" if expect_cuda and not torch.cuda.is_available(): raise SystemExit("NVIDIA GPU was detected but this environment cannot use CUDA") if torch.cuda.is_available(): torch.zeros(1, device="cuda") PY } if environment_ready ; then echo "✅ Reusing ${ENGINE} TTS environment: ${VENV}" exit 0 fi echo "===== Preparing isolated ${ENGINE} TTS environment =====" rm -rf "${VENV}" "${PYTHON_BIN}" -m venv "${VENV}" PY="${VENV}/bin/python" "${PY}" -m pip install -U pip setuptools wheel if ${GPU} ; then TORCH_INDEX="${MWW_TTS_TORCH_INDEX:-https://download.pytorch.org/whl/cu128}" echo "→ Installing CUDA torch ${TORCH_VERSION} from ${TORCH_INDEX}" "${PY}" -m pip install \ "torch==${TORCH_VERSION}" \ "torchaudio==${TORCHAUDIO_VERSION}" \ --index-url "${TORCH_INDEX}" else echo "→ Installing CPU torch ${TORCH_VERSION}" "${PY}" -m pip install \ "torch==${TORCH_VERSION}" \ "torchaudio==${TORCHAUDIO_VERSION}" fi echo "→ Installing ${PACKAGE_SPEC}" "${PY}" -m pip install "${PACKAGE_SPEC}" "huggingface_hub[hf_xet]" printf '%s\n' "${STACK_ID}" > "${MARKER}" if ! environment_ready ; then echo "❌ ${ENGINE} environment failed its import/CUDA check." >&2 exit 1 fi echo "✅ ${ENGINE} TTS environment ready: ${VENV}"