Skip to content

ASR

Note

在展馆等多人声环境中,建议通过物理方式(如定向麦克风)或音频预处理(如降噪、波束成形)减少环境干扰后再送入ASR服务

from funasr import AutoModel

model_dir = r"C:\Users\wjn\yx\hello-xiaoxin\models\asr\SenseVoiceSmall"
wav_path="test.wav"
wav_path = r"C:\Users\wjn\github\xiaozhi-esp32-server\main\xiaozhi-server\models\SenseVoiceSmall\example\zh.mp3"
model = AutoModel(
    model=model_dir,
    vad_model="fsmn-vad",
    vad_kwargs={"max_single_segment_time": 30000},
    device="cpu",
)
res = model.generate(input=[wav_path], cache={}, batch_size_s=0)
text = res[0]["text"]
print(text)

asr & tts

import os
import sys
import pyttsx3
from funasr import AutoModel

# ================= 配置区域 =================
# 1. ASR 模型路径 (SenseVoiceSmall)
# 请确保该路径下包含 model.pt, config.yaml, tokens.json 等文件
ASR_MODEL_DIR = r"C:\Users\wjn\yx\hello-xiaoxin\models\asr\SenseVoiceSmall"

# 2. 测试音频路径
# 优先使用你提供的 mp3 路径,如果不存在则 fallback 到 test.wav
AUDIO_FILE = r"C:\Users\wjn\github\xiaozhi-esp32-server\main\xiaozhi-server\models\SenseVoiceSmall\example\zh.mp3"
if not os.path.exists(AUDIO_FILE):
    AUDIO_FILE = "test.wav"

# 3. TTS 配置
TTS_RATE = 150  # 语速 (默认约 200,调低更清晰)
TTS_VOLUME = 1.0  # 音量 (0.0 - 1.0)
TTS_VOICE_ID = None  # 留空则使用默认,可设置为特定中文语音 ID


# ===========================================

def init_tts():
    """初始化 TTS 引擎并配置中文声音"""
    print("🔊 正在初始化 TTS 引擎...")
    engine = pyttsx3.init()

    # 配置语速和音量
    engine.setProperty('rate', TTS_RATE)
    engine.setProperty('volume', TTS_VOLUME)

    # 尝试自动选择中文声音
    voices = engine.getProperty('voices')
    selected_voice = None

    # 优先寻找中文女声 (通常包含 'zh' 或 'xiaoxiao' 等关键字)
    for voice in voices:
        if 'zh' in voice.id.lower() or 'chinese' in voice.name.lower():
            selected_voice = voice.id
            print(f"✅ 找到中文语音: {voice.name}")
            break

    if selected_voice:
        engine.setProperty('voice', selected_voice)
    else:
        print("⚠️ 未找到专用中文语音,使用系统默认语音。")
        # 可选:打印所有可用声音以便调试
        # for v in voices: print(v.name)

    return engine


def speak(engine, text):
    """执行朗读"""
    if not text:
        print("⚠️ 没有文本可朗读。")
        return

    print(f"🗣️  正在朗读: {text}")
    engine.say(text)
    engine.runAndWait()  # 阻塞直到朗读完成


def run_asr(model, audio_path):
    """执行语音识别"""
    if not os.path.exists(audio_path):
        raise FileNotFoundError(f"❌ 音频文件不存在: {audio_path}")

    print(f"🎤 正在识别音频: {os.path.basename(audio_path)} ...")

    try:
        # FunASR 的 generate 方法
        # cache={} 用于清空上下文,确保每次都是独立识别
        res = model.generate(input=[audio_path], cache={}, batch_size_s=0)

        if res and len(res) > 0:
            text = res[0].get("text", "")
            # SenseVoice 有时会返回带标点或不带标点的结果,视模型训练而定
            return text.strip()
        else:
            return ""
    except Exception as e:
        print(f"❌ ASR 识别出错: {e}")
        return None


def main():
    # 1. 初始化 TTS
    tts_engine = init_tts()

    # 2. 加载 ASR 模型
    print(f"🧠 正在加载 ASR 模型 (SenseVoiceSmall)... 路径: {ASR_MODEL_DIR}")
    print("(首次加载可能需要几分钟,请耐心等待...)")

    try:
        asr_model = AutoModel(
            model=ASR_MODEL_DIR,
            vad_model="fsmn-vad",  # 语音活动检测,自动切除静音
            vad_kwargs={"max_single_segment_time": 30000},  # 最大切片长度 30s
            device="cpu",  # 强制使用 CPU (如果没有 GPU)
            # disable_update=True       # 可选:禁止检查模型更新
        )
        print("✅ ASR 模型加载成功!")
    except Exception as e:
        print(f"❌ 模型加载失败: {e}")
        print("💡 请检查路径是否正确,以及是否安装了 torchaudio/torch。")
        sys.exit(1)

    # 3. 执行流程:听 -> 说
    recognized_text = run_asr(asr_model, AUDIO_FILE)

    if recognized_text is None:
        speak(tts_engine, "抱歉,识别过程中出错了。")
    elif recognized_text == "":
        speak(tts_engine, "我没听清,请再说一遍。")
    else:
        print(f"✨ 识别结果: {recognized_text}")
        # 这里可以加入简单的逻辑回复,或者直接复述
        reply = f"我听到了:{recognized_text}"
        speak(tts_engine, reply)


if __name__ == "__main__":
    main()