Files
blind/audio/check_tts_env.sh
T
2026-05-18 18:39:49 +08:00

180 lines
5.0 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
set -euo pipefail
# VoxCPM TTS chain health check for blind/audio workflow.
# Usage:
# bash audio/check_tts_env.sh # quick check
# bash audio/check_tts_env.sh --full # quick check + test synthesis
HOST="${VOXCPM_HOST:-127.0.0.1}"
PORT="${VOXCPM_PORT:-8000}"
HEALTH_URL="http://${HOST}:${PORT}/health"
START_SCRIPT="${VOXCPM_START_SCRIPT:-/home/xsl/AutoVideo/start-voxcpm.sh}"
MODEL_DIR="${VOXCPM_MODEL_DIR:-/home/xsl/models/VoxCPM2}"
VENV_PYTHON="${VOXCPM_PYTHON:-/home/xsl/tts-server/.venv/bin/python}"
LOG_FILE="${VOXCPM_LOG_FILE:-/tmp/voxcpm-server.log}"
FULL_CHECK=0
if [[ "${1:-}" == "--full" ]]; then
FULL_CHECK=1
fi
ok() { echo "[OK] $*"; }
warn() { echo "[WARN] $*"; }
err() { echo "[ERROR] $*"; exit 1; }
require_cmd() {
local cmd="$1"
command -v "$cmd" >/dev/null 2>&1 || err "缺少命令: $cmd"
}
wait_health() {
local max_retry="${1:-30}"
local i=1
while [[ "$i" -le "$max_retry" ]]; do
if curl -fsS "$HEALTH_URL" >/dev/null 2>&1; then
return 0
fi
sleep 1
((i++))
done
return 1
}
print_startup_hint() {
[[ -f "$LOG_FILE" ]] || return 0
if grep -q "speech_zipenhancer_ans_multiloss_16k_base" "$LOG_FILE"; then
warn "检测到降噪模型下载失败(ModelScope),常见原因是 DNS/网络不可达。"
warn "建议:先确保可访问 modelscope,或在服务端改为可关闭 denoiser。"
elif grep -q "ConnectionError" "$LOG_FILE"; then
warn "检测到网络连接错误,请检查当前网络和 DNS。"
fi
}
start_server() {
local denoiser="${1:-1}"
VOXCPM_ENABLE_DENOISER="$denoiser" nohup "$START_SCRIPT" >"$LOG_FILE" 2>&1 &
local pid=$!
ok "已发起启动,PID=$pid,日志: $LOG_FILE (denoiser=$denoiser)"
if wait_health 90; then
ok "服务启动成功: $HEALTH_URL"
return 0
fi
if kill -0 "$pid" >/dev/null 2>&1; then
kill "$pid" >/dev/null 2>&1 || true
fi
return 1
}
is_port_busy() {
ss -lnt | awk '{print $4}' | grep -qE "(^|:)${PORT}$"
}
quick_checks() {
echo "=== VoxCPM 快速环境检查 ==="
require_cmd curl
require_cmd ffmpeg
require_cmd python3
require_cmd ss
[[ -f "$VENV_PYTHON" ]] || err "Python 虚拟环境不存在: $VENV_PYTHON"
[[ -x "$START_SCRIPT" ]] || warn "启动脚本不可执行,建议: chmod +x $START_SCRIPT"
[[ -f "$START_SCRIPT" ]] || err "启动脚本不存在: $START_SCRIPT"
[[ -d "$MODEL_DIR" ]] || err "模型目录不存在: $MODEL_DIR"
ffmpeg -version >/dev/null 2>&1 || err "FFmpeg 不可用"
ok "基础依赖可用(curl / ffmpeg / python3 / ss"
ok "模型目录存在: $MODEL_DIR"
ok "虚拟环境存在: $VENV_PYTHON"
}
ensure_server() {
if curl -fsS "$HEALTH_URL" >/dev/null 2>&1; then
ok "TTS 服务已运行: $HEALTH_URL"
return 0
fi
warn "服务未就绪,尝试启动: $START_SCRIPT"
if is_port_busy; then
warn "端口 $PORT 已被占用,但 /health 不可达,请检查占用进程"
fi
if start_server 1; then
return 0
fi
print_startup_hint
if grep -q "speech_zipenhancer_ans_multiloss_16k_base" "$LOG_FILE"; then
warn "检测到 denoiser 远端依赖失败,自动回退到 denoiser=0 重试。"
if start_server 0; then
warn "当前服务以 denoiser=0 运行(更稳,降噪关闭)。"
return 0
fi
fi
print_startup_hint
err "服务启动超时,查看日志: $LOG_FILE"
}
full_check() {
echo "=== VoxCPM 全链路测试(注册音色 + 合成) ==="
local ref_wav="/tmp/voxcpm_check_ref.wav"
local out_wav="/tmp/voxcpm_check_tts.wav"
ffmpeg -f lavfi -i "sine=frequency=220:duration=3" -ar 16000 -ac 1 -y "$ref_wav" >/dev/null 2>&1
ok "生成测试参考音频: $ref_wav"
local payload
payload="$(python3 - <<'PY'
import base64, json
with open("/tmp/voxcpm_check_ref.wav", "rb") as f:
print(json.dumps({"wav_base64": base64.b64encode(f.read()).decode("utf-8")}))
PY
)"
local voice_resp
voice_resp="$(curl -fsS -X POST "http://${HOST}:${PORT}/v1/voices" \
-H "Content-Type: application/json" \
-d "$payload")"
local voice_id
voice_id="$(VOICE_RESP="$voice_resp" python3 - <<'PY'
import json, os
resp = json.loads(os.environ["VOICE_RESP"])
print(resp.get("voice_id", ""))
PY
)"
[[ -n "$voice_id" ]] || err "注册音色失败: $voice_resp"
ok "注册测试音色成功: $voice_id"
local speech_payload
speech_payload="$(VOICE_ID="$voice_id" python3 - <<'PY'
import json, os
print(json.dumps({
"text": "这是 VoxCPM 环境自检音频。",
"voice_id": os.environ["VOICE_ID"],
"cfg_value": 2.0,
"inference_timesteps": 10
}))
PY
)"
local status_code
status_code="$(curl -sS -o "$out_wav" -w "%{http_code}" \
-X POST "http://${HOST}:${PORT}/v1/speech" \
-H "Content-Type: application/json" \
-d "$speech_payload")"
[[ "$status_code" == "200" ]] || err "语音合成失败,HTTP $status_code"
[[ -s "$out_wav" ]] || err "语音合成输出为空: $out_wav"
ok "语音合成成功: $out_wav"
}
quick_checks
ensure_server
if [[ "$FULL_CHECK" -eq 1 ]]; then
full_check
fi
echo "=== 检查完成:可开始生产 ==="